code to do xbox mapfile compression

Post here about Halo modding and related editing. For help go to the Help Desk forum above.
Post Reply
Grenadiac




Socialist Golden Age Magic Era Eureka
Tsunami Scorched Earth Articulatist 250

Posts: 390
Joined: Fri Sep 05, 2003 1:36 pm
Location: Tucson, AZ USA
Contact:

code to do xbox mapfile compression

Post by Grenadiac »

Ok, UltraMagnus asked me for the code to do mapfile compression to help someone make a tool that can automate NMP construction, so here it is. Credit goes to PfhorSlayer for helping me with the buffer alignments many moons ago. To use this, you need the zlib compression library

http://www.zlib.net/zlib121-dll.zip

You need to know the structure of the mapfile header:

Code: Select all

typedef struct STRUCT_MAPFILE_HDR
{
  int  id;                    // 'head'
  int  Version;               // 5
  int  decomp_len;            // Actual len of decompressed data. Halo sticks garbage on the end so that the file is one of several fixed sizes (35, etc).
  int  Unknown1;
  int  TagIndexOffset;
  int  TagIndexMetaLength;
  int  Reserved1[2];          // both always 0x0
  char Name[32];
  char BuildDate[32];         // Year.Month.Day.Build - I guess they use this to make sure that a certain build will only open that build's map files, because this string is in the app too
  int  MapType;               // 0 = singleplayer, 1 = multiplayer, 2 = ui - this also determines the size of the cache file. UI = 35MB, multiplayer = 47MB, and singleplayer = 270MB
  int  Unknown4;
  int  Reserved2[485];
  int  Footer;                // 'foot'
}MAPFILE_HDR; /* header_t */ 
The compression code (m_InputFile is uncompressed map file):

Code: Select all

BOOL CHaloMapFile::CompressMapFile(CString output_file)
{
  BYTE *pInBuf = NULL;
  BYTE *pOutBuf = NULL;
  HANDLE hInFile, hOutFile, hInSection, hOutSection;
  MAPFILE_HDR *pHeader = NULL;
  CString str, cachepath;
  int result = Z_MEM_ERROR;
  unsigned long out_len;
  int actual_file_size = 0;
  int desired_file_size = 0;
  int pad_bytes = 0;


  //Close open file so we can open it in memory-mapping mode
  
  out_len = m_InputFile.GetLength() - 2048;
  m_InputFile.Close();


  str.Format("Saving to compressed file %s\n", output_file);
  g_pOutput->PostText(str, LOG_BLUE);

  str.Format("Compressing %s\n", m_InputFilename);
  g_pOutput->PostText(str, LOG_BLUE);
  g_pOutput->PostText("Please wait...\n", LOG_BLUE);
  g_pOutput->RedrawWindow();

  int k = sizeof(MAPFILE_HDR);
  // Use Filemapping to make the input file "look" like a normal buffer
  hInFile = CreateFile(m_InputFilename,
                       GENERIC_READ,
                       0,             // do not share
                       0,
                       OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL,
                       0);

  hInSection = CreateFileMapping(hInFile,
                                 0,
                                 PAGE_READONLY,
                                 0,   //this is not a large file
                                 0,   //map the entire file
                                 0);  //it does not need a name - no interprocess comm here :)

  pInBuf = (BYTE*)MapViewOfFile(hInSection,
                                FILE_MAP_READ,
                                0,
                                0,
                                0);

  if(pInBuf)
  {
    cachepath = m_WorkingDir + "\\cache.tmp";
    // Do the same for the output "buffer" (make an output file)
    hOutFile = CreateFile(output_file,
                          GENERIC_READ|GENERIC_WRITE,
                          0,             // do not share
                          0,
                          CREATE_ALWAYS,
                          FILE_ATTRIBUTE_NORMAL,
                          0);

    pHeader = (MAPFILE_HDR*)pInBuf;

    hOutSection = CreateFileMapping(hOutFile,
                                    0,
                                    PAGE_READWRITE,
                                    0,   //this is not a large file
                                    pHeader->decomp_len+2048,   //the decompressed length of the output file
                                    0);  //it does not need a name - no interprocess comm here :)

    pOutBuf = (BYTE*)MapViewOfFile(hOutSection,
                                   FILE_MAP_WRITE,
                                   0,
                                   0,
                                   0);

    if(!pOutBuf)
    {
      g_pOutput->PostText("Could not create output file.\r\nThere may not be enough hard drive space.\n", LOG_BLUE);
      AfxMessageBox("Could not create output file.  There may not be enough hard drive space.\n");
      UnmapViewOfFile(pOutBuf);
      CloseHandle(hOutSection);
    }
    else
    {
      CWaitCursor wait;

      //write header
      memcpy(pOutBuf, &m_MapfileHdr, sizeof(m_MapfileHdr));

      //compress file data
      int ret;

      ret = compress(pOutBuf+2048,
                     &out_len,
                     pInBuf+2048,
                     pHeader->decomp_len-2048);

      TRACE("outlen = %08X\n", out_len);

      int actual_file_size = out_len + 2048;
      int desired_file_size;
      int pad_bytes;

      pad_bytes = (0x800 - (actual_file_size % 0x800));
      desired_file_size = actual_file_size + pad_bytes;

      //truncate file to desired_file_size
      UnmapViewOfFile(pOutBuf);
      CloseHandle(hOutSection);
      SetFilePointer(hOutFile, desired_file_size, 0, FILE_BEGIN);      
      SetEndOfFile(hOutFile);

		  if(ret != Z_OK)
		  {
        str.Format("Compress returned %i!\n", ret);
        g_pOutput->PostText(str, LOG_RED);
        AfxMessageBox("Compress Map Failed.");
		  }
    }
    
    CloseHandle(hOutFile);
  }

  UnmapViewOfFile(pInBuf);
  CloseHandle(hInSection);
  CloseHandle(hInFile);

  return(FALSE);
}
Talin64




Socialist

Posts: 213
Joined: Mon Jan 26, 2004 12:24 pm

Post by Talin64 »

I always love to see more code.
Thanks Talin
dom





Posts: 215
Joined: Mon Mar 01, 2004 3:58 pm

Post by dom »

ummm... unassigned code... :lol:
Grenadiac




Socialist Golden Age Magic Era Eureka
Tsunami Scorched Earth Articulatist 250

Posts: 390
Joined: Fri Sep 05, 2003 1:36 pm
Location: Tucson, AZ USA
Contact:

Post by Grenadiac »

Someone asked for this, so here is the corresponding decompress code:

Code: Select all

  BYTE *pInBuf = NULL;
  BYTE *pOutBuf = NULL;
  HANDLE hInFile, hOutFile, hInSection, hOutSection;
  MAPFILE_HDR *pHeader = NULL;
  DWORD InFileLen, InFileLenHigh, DecompLen;
  CString str, cachepath;
  int result = Z_MEM_ERROR;

  str.Format("Decompressing %s\n", path);
  g_pOutput->PostText(str, LOG_BLUE);
  g_pOutput->PostText("Please wait...\n", LOG_BLUE);
  g_pOutput->RedrawWindow();

  int k = sizeof(MAPFILE_HDR);
  // Use Filemapping to make the input file "look" like a normal buffer
  hInFile = CreateFile(path,
                       GENERIC_READ,
                       0,             // do not share
                       0,
                       OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL,
                       0);

  hInSection = CreateFileMapping(hInFile,
                                 0,
                                 PAGE_READONLY,
                                 0,   //this is not a large file
                                 0,   //map the entire file
                                 0);  //it does not need a name - no interprocess comm here :)

  pInBuf = (BYTE*)MapViewOfFile(hInSection,
                                FILE_MAP_READ,
                                0,
                                0,
                                0);

  if(pInBuf)
  {
    cachepath = m_WorkingDir + "\\cache.tmp";
    // Do the same for the output "buffer" (make an output file)
    hOutFile = CreateFile(cachepath,
                          GENERIC_READ|GENERIC_WRITE,
                          0,             // do not share
                          0,
                          CREATE_ALWAYS,
                          FILE_ATTRIBUTE_NORMAL,
                          0);

    pHeader = (MAPFILE_HDR*)pInBuf;

    hOutSection = CreateFileMapping(hOutFile,
                                    0,
                                    PAGE_READWRITE,
                                    0,   //this is not a large file
                                    pHeader->decomp_len+2048,   //the decompressed length of the output file
                                    0);  //it does not need a name - no interprocess comm here :)

    pOutBuf = (BYTE*)MapViewOfFile(hOutSection,
                                   FILE_MAP_WRITE,
                                   0,
                                   0,
                                   0);

    if(!pOutBuf)
    {
      g_pOutput->PostText("Could not expand cache.tmp.\r\nThere may not be enough hard drive space.\n", LOG_BLUE);
      AfxMessageBox("Could not expand cache.tmp.  There may not be enough hard drive space.\n");
    }
    else
    {
      CWaitCursor wait;

      InFileLen = GetFileSize(hInFile, &InFileLenHigh);
      DecompLen = pHeader->decomp_len;

      memcpy(pOutBuf, pHeader, sizeof(MAPFILE_HDR));

      result = uncompress(pOutBuf+2048,
                              &DecompLen,
                              pInBuf+2048,
                              InFileLen-2048);//InFileLen-2048);

      switch(result)
      {
      case Z_DATA_ERROR:
        g_pOutput->PostText("Failed to decompress - the file was corrupt.\n", LOG_RED);
        AfxMessageBox("Failed to decompress - the file was corrupt.");
        break;

      case Z_MEM_ERROR:
        g_pOutput->PostText("Failed to decompress - not enough memory.\n", LOG_RED);
        AfxMessageBox("Failed to decompress - not enough memory.");
        break;

      case Z_BUF_ERROR:
        g_pOutput->PostText("Failed to decompress - output buffer was not large enough.\n", LOG_RED);
        AfxMessageBox("Failed to decompress - output buffer was not large enough.");
        break;

      case Z_OK:
        g_pOutput->PostText("Decompression complete.\n", LOG_BLUE);
        AfxMessageBox("Successfully decompressed the Halo map file.");
        break;
      }
    }
    
    UnmapViewOfFile(pOutBuf);
    CloseHandle(hOutSection);
    CloseHandle(hOutFile);
  }

  UnmapViewOfFile(pInBuf);
  CloseHandle(hInSection);
  CloseHandle(hInFile);

  if(result == Z_OK)
    OpenMapFile(cachepath);
Post Reply