////////////////////////////////////////////////////////////////////////////////
// Win32/MappedFile.cpp -- this file is part of the Emulator Developers Kit
// available at http://ourworld.compuserve.com/homepages/pc64/develop.htm


////////////////////////////////////////////////////////////////////////////////
// close the file

global void MappedFile::Close() {
  if (pbStart != NULL) {

    // adjust end
    if (pbPosition > pbEnd) {
      pbEnd = pbPosition;
    }

    // save file size
    int iNewSize = pbEnd - pbStart;

    // unmap the file from memory
    win(UnmapViewOfFile(pbStart));
    pbStart = NULL;
    pbPosition = NULL;
    pbEnd = NULL;
    pbReserved = NULL;
    win(CloseHandle(hMapping));
    hMapping = NULL;

    // change size of file on disk
    File::SetSize(iNewSize);

  // an error occured in SetName
  } else {
    if (hMapping != NULL) {
      CloseHandle(hMapping);
      hMapping = NULL;
    }
  }
}


////////////////////////////////////////////////////////////////////////////////
// set name and map the file into memory

global void MappedFile::SetName(const char* pcNewName) {

  // close old file
  Close();
  bReadOnly = false;

  // set name and open the file
  File::SetName(pcNewName);

  // save the size because CreateFileMapping may change it
  int iSize = File::GetSize();
  int iReserved = iSize;

  // map a read only file into memory
  if (IsReadOnly()) {
    win(hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL));
    win(pbStart = (byte*)MapViewOfFileEx(hMapping, FILE_MAP_READ, 0, 0, 0, pbInitialStart));
  } else {

    // calculate expand granularity
    SYSTEM_INFO sinf;
    GetSystemInfo(&sinf);
    iExpandGranularity = (iExpandGranularity + sinf.dwAllocationGranularity - 1) / sinf.dwAllocationGranularity * sinf.dwAllocationGranularity;
    if (iExpandGranularity == 0) {
      iExpandGranularity = sinf.dwAllocationGranularity;
    }

    // calculate size of reserved memory
    if (iInitialSize > iReserved) {
      iReserved = iInitialSize;
    }
    iReserved = (iReserved + iExpandGranularity - 1) / iExpandGranularity * iExpandGranularity;
    if (iReserved == 0) {
      iReserved = iExpandGranularity;
    }

    // map the file with read/write permission
    win(hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, iReserved, NULL));
    win(pbStart = (byte*)MapViewOfFileEx(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0, pbInitialStart));
  }

  // initialize memory pointers
  pbPosition = pbStart;
  pbEnd = pbStart + iSize;
  pbReserved = pbStart + iReserved;
}


////////////////////////////////////////////////////////////////////////////////
// change size of the mapped file in memory

global void MappedFile::SetReserved(const byte* pbNewReserved) {

  // check parameters and conditions
  assert(hMapping != NULL);
  assert(pbStart != NULL);
  assert(pbNewReserved > pbReserved);
  verify(!IsReadOnly());

  // save properties
  byte* pbOldStart = pbStart;
  int iPosition = pbPosition - pbStart;
  int iSize = pbEnd - pbStart;

  // unmap the file from memory
  win(UnmapViewOfFile(pbStart));
  pbStart = NULL;
  pbPosition = NULL;
  pbEnd = NULL;
  pbReserved = NULL;
  win(CloseHandle(hMapping));
  hMapping = NULL;

  // map the file into memory again with new size
  int iReserved = (pbNewReserved - pbOldStart + iExpandGranularity - 1) / iExpandGranularity * iExpandGranularity;
  win(hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, iReserved, NULL));
  pbStart = (byte*)MapViewOfFileEx(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0, pbOldStart);
  if (pbStart == NULL) {
    win(pbStart = (byte*)MapViewOfFileEx(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL));
  }

  // restore memory pointers
  pbPosition = pbStart + iPosition;
  pbEnd = pbStart + iSize;
  pbReserved = pbStart + iReserved;

  // call notify function if start has changed
  if (pbStart != pbOldStart) {
    if (pfniOnMove != NULL) {
      pfniOnMove(pbStart - pbOldStart);
    }
  }
}


////////////////////////////////////////////////////////////////////////////////
// read after end of file error

global void MappedFile::ReadAfterEndOfFile() {
  error("Read after end of file %s", GetName());
}


////////////////////////////////////////////////////////////////////////////////
// replace a part of the file and return pointer difference if start has moved

int MappedFile::Replace(byte* pbOld, int iOldSize, const byte* pbNew, int iNewSize) {
  assert(pbOld >= pbStart);
  assert(pbOld + iOldSize <= pbEnd);
  byte* pbOldStart = pbStart;
  int iDelta = iNewSize - iOldSize;
  if (iDelta > 0) {
    if (pbEnd + iDelta > pbReserved) {
      SetReserved(pbEnd + iDelta);
    }
  }
  memmove(pbOld + iNewSize, pbOld + iOldSize, pbEnd - pbOld - iOldSize);
  pbEnd += iDelta;
  memcpy(pbOld, pbNew, iNewSize);
  return pbStart - pbOldStart;
}
