/*********************************************************************************************** * * Copyright © DreamWorks Interactive, 1998. * * Contents: * Implementation of ImageLoader.hpp * * Bugs: * * To do: * *********************************************************************************************** * * $Log:: /JP2_PC/Source/Lib/Loader/ImageLoader.cpp $ * * 28 10/05/98 6:17a Agrant * better? calculation of swap file space management * * 27 10/02/98 9:49p Rwyatt * Chnaged swpspace registry key to SwapSpaceMb * * 26 9/25/98 7:21p Rwyatt * Image loader no longer sets the VM to use blocking loads * * 25 9/16/98 9:18p Agrant * Image loader succeeds unless overridden elsewhere. * * 24 98.09.14 12:23p Mmouni * Out of date for bad local swap files are now deleted before the image loader tries to do its * thing. * * 23 9/04/98 4:40p Mmouni * Added preliminary assembly version of de-compression (currently disabled). * Added support for handling write errors. * * 22 9/03/98 4:30p Shernd * Checking for out of disk space * * 21 98.09.02 10:02p Mmouni * Added progress counters to new de-compression. * * 20 98.09.01 8:58p Mmouni * Added switch to enable/disable loading of new swap file compressed format. * * 19 98.09.01 8:43p Mmouni * Added the first version (optimized C) of the new image file decompression. * * 18 8/31/98 6:46p Mmouni * Added optional warning message for no swap file. * * 17 8/27/98 1:33p Shernd * Improved callback updates * * 16 8/25/98 2:13p Rwyatt * Made the load heap 16Mb maximum * * 15 8/23/98 4:30p Rwyatt * Added an assert for zero length PID files * * 14 8/23/98 2:45a Agrant * Don't wait for the swap thread if we already have a swap file. * * 13 8/23/98 12:24a Shernd * While waiting for the local Copy to finish we now use an Even instead of a CritialSection. * * 12 8/19/98 1:38p Rwyatt * New allocation functions to load from the Image Loader fast heap * * 11 98.08.13 4:26p Mmouni * Changes for VC++ 5.0sp3 compatibility. * * 10 8/03/98 6:34p Shernd * Changes for Progress Loading Bar * * 9 6/24/98 3:22p Rwyatt * Use synchronous VM for the first few frames so image cahces and terrain textures are at full * resolution. * * 8 6/12/98 2:33p Rwyatt * If Trespasser is not installed the image loader will default to a non compressed swap file if * it can find one. It will assert if a SWP file cannot be found. * * 7 6/10/98 4:52p Rwyatt * Now loads compressed swap files (.spp). If one of these files cannot be found then the * existing swap file is loaded in the usual way, * * 6 6/02/98 11:17p Rwyatt * Fixed the problem of finding and deleting old swap files if they are read only * * 5 4/23/98 7:58p Rwyatt * Copy local of swap files now deletes old swap file once the specified quota is reached. The * quota is obtained from the registry. * Commit all is now idsabled by default. * * 4 4/22/98 2:54p Rwyatt * The VM thread is only resumed if we do not have the commit all flag set * * 3 4/21/98 3:10p Rwyatt * New format swap files (110). * These support a pageable and non-pageable section. * * 2 2/25/98 3:16p Rwyatt * Added an extentsion to the swp and pid file so multiples can exist for different versions of * the GUIApp * * 1 2/18/98 1:08p Rwyatt * Moved from TextureManager.cpp * **********************************************************************************************/ #include "Common.hpp" #include "stdio.h" #include "stdlib.h" #include "io.h" #include "Lib/W95/WinInclude.hpp" #include "ImageLoader.hpp" #include "Lib/Sys/VirtualMem.hpp" #include "TextureManager.hpp" #include "Loader.hpp" #include "Lib/Sys/MemoryLog.hpp" #include "Lib/Sys/DebugConsole.hpp" #include "Lib/Sys/FileEx.hpp" #include "Lib/sys/Reg.h" #include "Lib/EntityDBase/RenderDB.hpp" #include "Lib/EntityDBase/WorldDBase.hpp" #include "Lib/Sys/Profile.hpp" //********************************************************************************************* #define THREADED_COPY (1) #define NEW_SWAP_COMPRESSION (1) #if !defined(WARN_ON_NO_SWAP) #define WARN_ON_NO_SWAP (0) #endif //********************************************************************************************* // Load Image Directory Static Variables // CLoadImageDirectory* CLoadImageDirectory::plidImageDir = NULL; bool CLoadImageDirectory::bValidImage = false; bool CLoadImageDirectory::bImageLoader = true; bool CLoadImageDirectory::bAutoCommit = false; HANDLE CLoadImageDirectory::hCopyLocal = NULL; bool CLoadImageDirectory::bCopyResult = false; bool CLoadImageDirectory::bCompressed = false; bool CLoadImageDirectory::bNewCompression = false; void* CLoadImageDirectory::pvThreadHandle = NULL; CFastHeap CLoadImageDirectory::fhImageLoad(1 << 24); // 16Mb //********************************************************************************************* // // A Function that reads the size from one of our newly compressed files. // uint32 u4OriginalFileSize(const char *str_filename); // //************************************* //********************************************************************************************* // Image Directory Loading // // This will create all of the structures for relocating rasters and will start the VM system // if all goes well. // CLoadImageDirectory::CLoadImageDirectory ( const char* str_grf_name, // name of the GRF file being loaded PFNWORLDLOADNOTIFY pfnInWorldNotify, // Notification callback uint32 u4InNotifyParam // Notificaiton parameter ) //************************************* { char str_dir_name[MAX_PATH]; HANDLE h_load; uint32 u4_len; uint32 u4_load_bytes; // Assume success unless set elsewhere. i4Error = 0; Assert(plidImageDir==NULL); // there can be only one. hCopyLocal = CreateEvent(NULL, TRUE, FALSE, NULL); pfnWorldNotify = pfnInWorldNotify; u4NotifyParam = u4InNotifyParam; strcpy(str_dir_name,str_grf_name); strcpy(str_dir_name + strlen(str_dir_name) - 4, strPID_FILE_EXTENTSION); plidImageDir = this; // set the global class pointer to this bValidImage = false; pdfhDirHeader = NULL; // // if the image loader is disbaled then return without doing anything, // if the virtual memory manager is already image based we must use the conventional loader, // if the virtual memory manager has allocated any memory we must use the conventional loader, // if exit path is taken and something is allocated then we cannot create a swap file later, this // happens when two GRF files are loaded. Also, if this exit is taken then make sure that there // is no small texture manager. Small textures should be forced to use the normal allocator. // if ((!bImageLoader) || (gtxmTexMan.pvmeTextures->bFileBased()) || (gtxmTexMan.pvmeTextures->bAnythingAllocated())) { gtxmTexMan.DestroySmallTextureAllocator(); bImageLoader = false; SetEvent(hCopyLocal); return; } // There must not be a sub allocator at this point or something is really wrong. Assert(gtxmTexMan.pvmsaSmallTextures == NULL); // process the local swap file. if ( bProcessLocalSwapFile ( str_grf_name // name of the GRF file to find a swap file for ) == false ) { // We need to create the small texture allocator before we continue with the conventional // load. gtxmTexMan.CreateSmallTextureAllocator(); // cannot process the swap file, probably because it does not exist. bImageLoader = false; return; } // // open the directory file.... // h_load=CreateFile(str_dir_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY|FILE_FLAG_SEQUENTIAL_SCAN,NULL); if (h_load == INVALID_HANDLE_VALUE) { return; } // size of the file u4_len = GetFileSize(h_load, NULL); Assert(u4_len > 0); pdfhDirHeader = (SDirectoryFileHeader*) new char[u4_len]; if (ReadFile(h_load, pdfhDirHeader , u4_len ,(DWORD*)&u4_load_bytes, NULL) == false) { CloseHandle(h_load); delete pdfhDirHeader; pdfhDirHeader = NULL; return; } // make sure we got the whole file, if not fail on the image file so it is not used. if (u4_load_bytes != u4_len) { CloseHandle(h_load); delete pdfhDirHeader; pdfhDirHeader = NULL; return; } CloseHandle(h_load); // We must have the correct version number AlwaysAssert(pdfhDirHeader->u4Version == u4DIRECTORY_FILE_VERSION); // // check if the bump maps in this file match the bit depth of the current build... if (pdfhDirHeader->u4BumpMapBitDepth != iBUMPMAP_RESOLUTION) { delete pdfhDirHeader; pdfhDirHeader = NULL; return; } // // Construct an array of CPals // uint32 u4_pal_count = pdfhDirHeader->u4PaletteCount; SDirectoryPaletteChunk* pdpc = (SDirectoryPaletteChunk*)( ((uint8*)pdfhDirHeader) + pdfhDirHeader->u4PaletteOffset); uint32 u4_pal = 0; // get the actual palette data from the directory file while (u4_pal_count) { // create a new palette of the correct size and set its hash value to the stored value CPal* ppal = new CPal(pdpc->u4ColoursUsed); ppal->SetHashValue(pdpc->u4HashValue); // push the palettes into an STL vector. CLoadImageDirectory::plidImageDir->appal.push_back(ppal); // copy the data across memcpy(&ppal->aclrPalette[0], &pdpc->clr[0], pdpc->u4ColoursUsed * sizeof(CColour) ); pdpc = (SDirectoryPaletteChunk*) ((char*)pdpc + pdpc->u4Size); u4_pal_count--; u4_pal++; } // // add the raster elements to the map for easy locating // uint32 u4_chunks = pdfhDirHeader->u4RasterChunkCount; SDirectoryFileChunk* pdfc = (SDirectoryFileChunk*)( ((uint8*)pdfhDirHeader) + pdfhDirHeader->u4RasterChunkOffset); // pdfc points to the first of the nodes... while (u4_chunks) { // check that this ID is not already in directory map. Assert(mapChunk[pdfc->u8HashValue] == NULL); mapChunk[pdfc->u8HashValue] = pdfc; pdfc = (SDirectoryFileChunk*) ((char*)pdfc + pdfc->u4Size); u4_chunks--; } // Reserve enough virtual memory for the image file gtxmTexMan.pvmeTextures->SetPagingRegion(u4LocalImageSize); bValidImage = true; } //********************************************************************************************* // CLoadImageDirectory::~CLoadImageDirectory ( ) //************************************* { dprintf("Waiting for local copy..\n"); // Spin and wait for mutext to finish DWORD dw; do { dw = WaitForSingleObject(hCopyLocal, 250); if (dw == WAIT_TIMEOUT && pfnWorldNotify) { (pfnWorldNotify)(u4NotifyParam, 0, 0, 0); } } while (dw != WAIT_OBJECT_0); CloseHandle(hCopyLocal); CloseHandle((HANDLE)pvThreadHandle); if (bValidImage) { if (!bCopyResult) { // what should we do because the copy has failed.... AlwaysAssert(0); } // Start the VM system... if (!gtxmTexMan.pvmeTextures->bBeginPaging(strLocalImage, pdfhDirHeader->u4PageableOffset,pdfhDirHeader->u4NonPageableCount,false)) { // failed to start the VM system. Assert(0); } // If the auto commit flag is set then load the whole swap file, otherwise // start the loader thread so we can begin loading the required data. if (bAutoCommit) { gtxmTexMan.pvmeTextures->CommitAll(); } else { // Start the thread gtxmTexMan.pvmeTextures->ResumeVMLoadThread(); } } // go through all the palettes in the STL vector and delete them. This is safe because no // raster should reference one of these palettes. All rasters should have created their // own palettes. for (std::vector::iterator i = appal.begin(); i0); Assert(u8SourceImageTime!=0xffffffff); // // The GRF file has a swap file so check if we have a local version of // the same swp file. // // This is the name of the local swap file if one exists.. // strcpy(strLocalImage, str_local_swap_dir); strcat(strLocalImage, "\\"); strcat(strLocalImage, strLeafName(strSourceImage) ); // Make sure the local image ends in SWP strcpy(strLocalImage + strlen(strLocalImage) - strlen(strSWP_FILE_EXTENTSION), strSWP_FILE_EXTENTSION); if (bFileExists(strLocalImage)) { // There is a local version of this swap file, does it have the same last accessed date // and the same size as the version with the GRF file?? uint64 u8_local_time = u8FileTimeLastWritten(strLocalImage); if ((u8_local_time == u8SourceImageTime) && (u4LocalImageSize == u4FileSize(strLocalImage))) { // We have a match for the swap file so copy the name of the file to load. dprintf("Using local swap file..\n"); if (pfnWorldNotify) { (pfnWorldNotify)(u4NotifyParam, 2, 99, 0); } SetEvent(hCopyLocal); bCopyResult = true; return true; } else { // Local swap file is out of date or bad, delete it. SetFileAttributes(strLocalImage, FILE_ATTRIBUTE_NORMAL); DeleteFile(strLocalImage); } } // // We cannot create a temp file called the same as local swap file so we must assume // that the copy will fail, or the local directory path is NULL. // In this case we can use the swap file that is with the GRF file.. // if ((*str_local_swap_dir == 0) || !bCanCreateFile(strLocalImage)) { dprintf("Cannot create local swap file - Is Trespasser installed??\n"); if (bCompressed) { // We have found a compressed swap file and this cannot be used so we need to look for // a uncompressed version or fail the load and force it to use the conventional loader. strcpy(strSourceImage + strlen(strSourceImage) - strlen(strCOMP_FILE_EXTENTSION), strSWP_FILE_EXTENTSION); if (!bFileExists(strSourceImage)) { // // There is only a compressed swap file and this machine does not have Trespasser installed so // no registry key can be found, hence we have no idea where to decompress the file to. If the // uncompressed swap file had been present it would have been used in place without being copied // locally. Compressed swap files must be copied locally in order to decompress them and therefore // Trespasser must be installed. // dprintf("No uncompressed swap file with GRF file. Using conventional loader\n", strSourceImage); AlwaysAssert(0); SetEvent(hCopyLocal); return false; } } dprintf("Using GRF swap file - Cannot create local.\n"); strcpy(strLocalImage,strSourceImage); bCopyResult = true; SetEvent(hCopyLocal); return true; } // // If we manage to get to here we need to create a local swap file.... // // Before we copy local ensure that we have not reached our quota for used disk space char str_wildcard[MAX_PATH]; _finddata_t fnd; std::vector vdsf; strcpy(str_wildcard,str_local_swap_dir); strcat(str_wildcard,"\\*.swp"); int32 i4_ffhandle = _findfirst(str_wildcard, &fnd); // // If FindFirst reports an error there is probably no files that match so copy the // swap file without any other regards. // if (i4_ffhandle!=-1) { uint32 u4_count = 0; uint32 u4_totalsize = 0; int32 i4_res = 0; while (i4_res==0) { SDiskSwapFile dsf; strcpy(dsf.strFilename,str_local_swap_dir); strcat(dsf.strFilename,"\\"); strcat(dsf.strFilename,fnd.name); // FAT system do not support a last access time stamp, if this time is -1 then use // the write time stamp. This effectively changes the caching system from an LRU to // FIFO. if (fnd.time_access == -1) { dsf.u4Time = fnd.time_write; } else { dsf.u4Time = fnd.time_access; } dsf.u4Size = fnd.size; u4_totalsize += fnd.size; dsf.bDeleted = false; vdsf.push_back(dsf); u4_count++; // find the next matching file i4_res = _findnext(i4_ffhandle, &fnd); } _findclose(i4_ffhandle); // If the SwpSpace registry key is not present then assume the max size is 0, // This is effect means that there will only ever be a single swap file locally. uint32 u4_swap_disk_space = (uint32)GetRegValue("SwapSpaceMb", 0) * 1024 * 1024; int32 i4_space_free = int(u4_swap_disk_space) - int(u4_totalsize); // Have a guess at the decompressed size int32 i4_space_required = int(u4LocalImageSize) - i4_space_free; if (i4_space_required>0) { uint32 u4_files = u4_count; uint32 u4_oldest = 0xffffffff; uint32 u4_oldtime = 0xffffffff; // we need to clean out some old swap files to make our new one fit. dprintf("Cleaning out old swap files.\n"); // If we have any swap files left in the directory and we still need to find more room // go and delete another file. while ((u4_files>0) && (i4_space_required>0)) { for (int i=0; istrSourceImage, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hFileSrc == INVALID_HANDLE_VALUE) { goto ThreadError; } // Open the destination file. hFileDest = CreateFile((char*)plid->strLocalImage, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFileDest == INVALID_HANDLE_VALUE) { CloseHandle(hFileSrc); goto ThreadError; } // Initialize progress bar. if (plid->pfnWorldNotify) { (plid->pfnWorldNotify)(plid->u4NotifyParam, 3, 0, 100); } iLastPercent = 0; // Read the un-compressed size from the file. bRet = ReadFile(hFileSrc, &u4_file_size, sizeof(u4_file_size), &dwRead, NULL); Assert(bRet); // Clear the ring buffer. for (i = 0; i < N - F; i++) text_buf[i] = '\0'; r = N - F; i_dest_start = r; flags = 0; #if (0 && VER_ASM && TARGET_PROCESSOR == PROCESSOR_PENTIUM) // This doesn't seem to be any faster than the non-asm version. __asm { mov eax,[flags] // eax = flags mov esi,[i_srccnt] // esi = i_srccnt mov ecx,[r] // ecx = r mov ebx,[src_base] // ebx = src_base lea edi,[text_buf] // edi = text_buf FOREVER_LOOP: shl ah,1 // mask & count jnz KEEP_DECODING // Check if we need to read. cmp esi,0 jne NO_READ1 // Read. call READ_BUFFER test esi,esi jnz NO_READ1 // EOF. mov [r],ecx // ecx = r jmp DONE NO_READ1: // Load new flags. mov al,[ebx+esi] // src_base[i_srccnt] inc esi // i_srccnt++ mov ah,1 // use ah as bit mask and to count 8. KEEP_DECODING: test al,ah // test encoding bit. jz ENCODED // Not encoded. // Check if we need to read. cmp esi,0 jne NO_READ2 // Read. call READ_BUFFER test esi,esi jnz NO_READ2 // EOF. mov [r],ecx // ecx = r jmp DONE NO_READ2: // Copy character. mov dl,[ebx+esi] inc esi mov [edi+ecx],dl inc ecx // Check if we need to write. cmp ecx,4096 jne FOREVER_LOOP // Write. call WRITE_BUFFER jmp FOREVER_LOOP ENCODED: // Check if we have 2 bytes. cmp esi,-2 jle HAVE_TWO // Check if we need to read. cmp esi,0 jne NO_READ3 // Read. call READ_BUFFER test esi,esi jnz NO_READ3 // EOF. mov [r],ecx // ecx = r jmp DONE NO_READ3: mov dl,[ebx+esi] // lower 8 bits of index inc esi // Check if we need to read. cmp esi,0 jne NO_READ4 push edx // Read. call READ_BUFFER pop edx test esi,esi jnz NO_READ4 // EOF. mov [r],ecx // ecx = r jmp DONE NO_READ4: mov bl,[ebx+esi] // upper 4 bits of index + count inc esi jmp DONE_WITH_READ HAVE_TWO: mov dl,[ebx+esi] // lower 8 bits of index mov bl,[ebx+esi+1] // upper 4 bits of index + count add esi,2 // i_srccnt += 2 DONE_WITH_READ: mov dh,bl // copy count and bl,0x0f // mask bits of count shr dh,4 // shift bits of index down add bl,3 // add THRESHOLD+1 and edx,4095 // and with N-1 DECODE_LOOP: mov bh,[edi+edx] // read byte from ring buffer. inc edx mov [edi+ecx],bh // write byte to ring buffer. inc ecx // Check if we need to write. cmp ecx,4096 jne NO_WRITE push edx push ebx // Write. call WRITE_BUFFER pop ebx pop edx NO_WRITE: and edx,4095 // and with N-1 dec bl jnz DECODE_LOOP mov ebx,[src_base] jmp FOREVER_LOOP } // Write a block of data to the output file. WRITE_BUFFER: __asm { mov [flags],eax // eax = flags mov [i_srccnt],esi // esi = i_srccnt } { // Write from ring buffer. bRet = WriteFile(hFileDest, text_buf+i_dest_start, N-i_dest_start, &dwWritten, NULL); if (!bRet || (iRead != dwWritten)) { plid->i4Error = -2; CloseHandle(hFileSrc); CloseHandle(hFileDest); // TODO: return error code here. } // Show progress. u4_bytes_written += N-i_dest_start; iPercent = (u4_bytes_written * 100 / u4_file_size); if (iPercent > iLastPercent) { if (plid->pfnWorldNotify) { (plid->pfnWorldNotify)(plid->u4NotifyParam, 2, iPercent, 0); } iLastPercent = iPercent; } r = 0; i_dest_start = 0; } __asm { mov eax,[flags] // eax = flags mov esi,[i_srccnt] // esi = i_srccnt mov ecx,[r] // ecx = r mov ebx,[src_base] // ebx = src_base lea edi,[text_buf] // edi = text_buf ret } // Read a block of data from the input file. READ_BUFFER: __asm { mov [flags],eax // eax = flags mov [r],ecx // ecx = r } { // Read buffer. bRet = ReadFile(hFileSrc, src_buf, i_bufsize, &dwRead, NULL); Assert(bRet); // EOF if (dwRead == 0). src_base = src_buf + dwRead; i_srccnt = -dwRead; } __asm { mov eax,[flags] // eax = flags mov esi,[i_srccnt] // esi = i_srccnt mov ecx,[r] // ecx = r mov ebx,[src_base] // ebx = src_base lea edi,[text_buf] // edi = text_buf ret } DONE: #else int j, k; for (;;) { // Shift encoding flags & decrement count. flags >>= 1; // Check count to see if we need new flags. if ((flags & 256) == 0) { // Read a character. if (i_srccnt == 0) { // Read buffer. bRet = ReadFile(hFileSrc, src_buf, i_bufsize, &dwRead, NULL); Assert(bRet); // EOF. if (dwRead == 0) break; src_base = src_buf + dwRead; i_srccnt = -dwRead; } flags = src_base[i_srccnt++]; flags |= 0xff00; // Uses higher byte cleverly to count eight. } // Check encoding flags. if (flags & 1) { // Read a character. if (i_srccnt == 0) { // Read buffer. bRet = ReadFile(hFileSrc, src_buf, i_bufsize, &dwRead, NULL); Assert(bRet); // EOF. if (dwRead == 0) break; src_base = src_buf + dwRead; i_srccnt = -dwRead; } // Copy character. text_buf[r++] = src_base[i_srccnt++]; // See if we need to write. if (r == N) { // Write from ring buffer. bRet = WriteFile(hFileDest, text_buf+i_dest_start, r-i_dest_start, &dwWritten, NULL); if (!bRet || (r-i_dest_start != dwWritten)) { plid->i4Error = -2; CloseHandle(hFileSrc); CloseHandle(hFileDest); goto ThreadError; } // Show progress. u4_bytes_written += r-i_dest_start; iPercent = (u4_bytes_written * 100 / u4_file_size); if (iPercent > iLastPercent) { if (plid->pfnWorldNotify) { (plid->pfnWorldNotify)(plid->u4NotifyParam, 2, iPercent, 0); } iLastPercent = iPercent; } r = 0; i_dest_start = 0; } } else { // Read a 2 characters. if (i_srccnt > -2) { // Read buffer. if (i_srccnt == 0) { bRet = ReadFile(hFileSrc, src_buf, i_bufsize, &dwRead, NULL); Assert(bRet); // EOF. if (dwRead == 0) break; src_base = src_buf + dwRead; i_srccnt = -dwRead; } i = src_base[i_srccnt++]; // Read a character. if (i_srccnt == 0) { // Read buffer. bRet = ReadFile(hFileSrc, src_buf, i_bufsize, &dwRead, NULL); Assert(bRet); // EOF. if (dwRead == 0) break; src_base = src_buf + dwRead; i_srccnt = -dwRead; } j = src_base[i_srccnt++]; } else { i = src_base[i_srccnt++]; j = src_base[i_srccnt++]; } // Convert to index & count. i |= ((j & 0xf0) << 4); j = (j & 0x0f) + THRESHOLD; for (k = i; k <= i+j; k++) { // Store in ring buffer. text_buf[r++] = text_buf[k & (N - 1)]; // See if we need to write. if (r == N) { // Write from ring buffer. bRet = WriteFile(hFileDest, text_buf+i_dest_start, r-i_dest_start, &dwWritten, NULL); if (!bRet || (r-i_dest_start != dwWritten)) { plid->i4Error = -2; CloseHandle(hFileSrc); CloseHandle(hFileDest); goto ThreadError; } // Show progress. u4_bytes_written += r-i_dest_start; iPercent = (u4_bytes_written * 100 / u4_file_size); if (iPercent > iLastPercent) { if (plid->pfnWorldNotify) { (plid->pfnWorldNotify)(plid->u4NotifyParam, 2, iPercent, 0); } iLastPercent = iPercent; } r = 0; i_dest_start = 0; } } } } #endif // Flush buffer. bRet = WriteFile(hFileDest, text_buf+i_dest_start, r-i_dest_start, &dwWritten, NULL); if (!bRet || (r-i_dest_start != dwWritten)) { plid->i4Error = -2; CloseHandle(hFileSrc); CloseHandle(hFileDest); goto ThreadError; } // Show progress as done. if (plid->pfnWorldNotify) { (plid->pfnWorldNotify)(plid->u4NotifyParam, 2, 100, 0); } // Close the files. CloseHandle(hFileSrc); CloseHandle(hFileDest); } else { int i_file_s; OFSTRUCT ofs_s; BYTE ab[4096]; int iRead; HANDLE hFile; DWORD dwWritten; BOOL bRet; int iLen; int iChunk; int iPercent; int iLastPercent; // Open the source file. i_file_s = LZOpenFile((char*)plid->strSourceImage,&ofs_s,OF_READ); if (i_file_s < 0) { goto ThreadError; } // Open the destination file. hFile = CreateFile((char*)plid->strLocalImage, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { LZClose(i_file_s); goto ThreadError; } iLen = LZSeek(i_file_s, 0, 2); LZSeek(i_file_s, 0, 0); iLen /= 4096; iChunk = 0; iLastPercent = 0; if (plid->pfnWorldNotify) { (plid->pfnWorldNotify)(plid->u4NotifyParam, 3, 0, 100); } while ((iRead = LZRead(i_file_s, (LPSTR)&ab, sizeof(ab))) > 0) { iChunk++; iPercent = (iChunk * 100 / iLen); if (iPercent > iLastPercent) { if (plid->pfnWorldNotify) { (plid->pfnWorldNotify)(plid->u4NotifyParam, 2, iPercent, 0); } iLastPercent = iPercent; } bRet = WriteFile(hFile, ab, iRead, &dwWritten, NULL); if (!bRet || (iRead != dwWritten)) { plid->i4Error = -2; LZClose(i_file_s); CloseHandle(hFile); goto ThreadError; } } if (plid->pfnWorldNotify) { (plid->pfnWorldNotify)(plid->u4NotifyParam, 2, 100, 0); } // close the files and clean up LZClose(i_file_s); CloseHandle(hFile); } dprintf("done creating local swap file...\n"); dprintf("in %f seconds.\n", (double)ctmr() * ctmr.fSecondsPerCycle()); bCopyResult = true; // Now set the time stamp of the swap file to be the same as the swap file source bFileSetTimeStamp(plid->strLocalImage, plid->u8SourceImageTime); // Signal that the CopyLocal event is complete SetEvent(hCopyLocal); return 1; //****************************************************************************************** ThreadError: dprintf("Failed to create local swap file..\n"); AlwaysAssert(0); bCopyResult = false; SetEvent(hCopyLocal); return 0; }