/* AMX Mod X * * by the AMX Mod X Development Team * originally developed by OLO * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * In addition, as a special exception, the author gives permission to * link the code of this program with the Half-Life Game Engine ("HL * Engine") and Modified Game Libraries ("MODs") developed by Valve, * L.L.C ("Valve"). You must obey the GNU General Public License in all * respects for all of the code used other than the HL Engine and MODs * from Valve. If you modify this file, you may extend this exception * to your version of the file, but you are not obligated to do so. If * you do not wish to do so, delete this exception statement from your * version. */ #include "amxmodx.h" #include "amxxfile.h" #include "zlib/zlib.h" /********************** ****** AMXXFILE ****** **********************/ #if defined __GNUC__ #define PACKED __attribute__((packed)) #else #define PACKED #endif #if defined __linux__ #pragma pack(1) /* structures must be packed (byte-aligned) */ #else #pragma pack(1) /* structures must be packed (byte-aligned) */ #if defined __TURBOC__ #pragma option -a- /* "pack" pragma for older Borland compilers */ #endif #endif struct TableEntry { mint8_t cellSize; mint32_t origSize PACKED; // contains AMX_HEADER->stp mint32_t offset PACKED; }; #define DATAREAD(addr, itemsize, itemcount) \ if (fread((addr), (itemsize), (itemcount), (m_pFile)) != (itemcount)) \ { \ if (feof(m_pFile)) \ m_Status = Err_FileInvalid; \ else \ m_Status = Err_FileRead; \ fclose(m_pFile); \ m_pFile = NULL; \ return; \ } CAmxxReader::CAmxxReader(const char *filename, int cellsize) { m_Bh.plugins = NULL; m_AmxxFile = false; if (!filename) { m_Status = Err_InvalidParam; return; } m_Status = Err_None; m_CellSize = cellsize; m_pFile = fopen(filename, "rb"); if (!m_pFile) { m_Status = Err_FileOpen; return; } mint32_t magic; DATAREAD(&magic, sizeof(magic), 1); m_OldFile = false; if (magic == 0x524C4542) { //we have an invalid, old, RLEB file m_Status = Err_OldFile; fclose(m_pFile); m_pFile = NULL; return; } else if (magic == MAGIC_HEADER2) { DATAREAD(&m_Bh.version, sizeof(int16_t), 1); if (m_Bh.version > MAGIC_VERSION) { m_Status = Err_OldFile; fclose(m_pFile); m_pFile = NULL; return; } m_AmxxFile = true; DATAREAD(&m_Bh.numPlugins, sizeof(mint8_t), 1); m_Bh.plugins = new PluginEntry[m_Bh.numPlugins]; PluginEntry *pe; m_SectionHdrOffset = 0; m_Entry = -1; for (mint8_t i = 0; i < m_Bh.numPlugins; i++) { pe = &(m_Bh.plugins[i]); DATAREAD(&pe->cellsize, sizeof(mint8_t), 1); DATAREAD(&pe->disksize, sizeof(int32_t), 1); DATAREAD(&pe->imagesize, sizeof(int32_t), 1); DATAREAD(&pe->memsize, sizeof(int32_t), 1); DATAREAD(&pe->offs, sizeof(int32_t), 1); } for (mint8_t i = 0; i < m_Bh.numPlugins; i++) { pe = &(m_Bh.plugins[i]); if (pe->cellsize == m_CellSize) { m_Entry = i; break; } } if (m_Entry == -1) { m_Status = Err_SectionNotFound; fclose(m_pFile); m_pFile = NULL; return; } pe = &(m_Bh.plugins[m_Entry]); m_SectionLength = pe->disksize; } else if (magic == MAGIC_HEADER) { // try to find the section mint8_t numOfPlugins; DATAREAD(&numOfPlugins, sizeof(numOfPlugins), 1); TableEntry entry; m_SectionHdrOffset = 0; int i = 0; for (i = 0; i < static_cast(numOfPlugins); ++i) { DATAREAD(&entry, sizeof(entry), 1); if (entry.cellSize == m_CellSize) { m_SectionHdrOffset = ftell(m_pFile) - sizeof(entry); break; } } if (!m_SectionHdrOffset) { m_Status = Err_SectionNotFound; fclose(m_pFile); m_pFile = NULL; return; } // compute section length if ((i + 1) < static_cast(numOfPlugins)) { // there is a next section TableEntry nextEntry; DATAREAD(&nextEntry, sizeof(nextEntry), 1); m_SectionLength = nextEntry.offset - entry.offset; } else { fseek(m_pFile, 0, SEEK_END); m_SectionLength = ftell(m_pFile) - (long)entry.offset; } } else { // check for old file AMX_HEADER hdr; rewind(m_pFile); fread(&hdr, sizeof(hdr), 1, m_pFile); amx_Align16(&hdr.magic); if (hdr.magic == AMX_MAGIC) { if (cellsize != 4) { m_Status = Err_SectionNotFound; fclose(m_pFile); m_pFile = NULL; return; } m_OldFile = true; return; } else { // no known file format m_Status = Err_FileInvalid; fclose(m_pFile); m_pFile = NULL; return; } } } CAmxxReader::~CAmxxReader() { if (m_pFile) { fclose(m_pFile); m_pFile = NULL; } if (m_Bh.plugins) { delete [] m_Bh.plugins; m_Bh.plugins = NULL; } } CAmxxReader::Error CAmxxReader::GetStatus() { return m_Status; } #undef DATAREAD #define DATAREAD(addr, itemsize, itemcount) \ if (fread(addr, itemsize, itemcount, m_pFile) != itemcount) \ { \ if (feof(m_pFile)) \ m_Status = Err_FileInvalid; \ else \ m_Status = Err_FileRead; \ fclose(m_pFile); \ m_pFile = NULL; \ return 0; \ } size_t CAmxxReader::GetBufferSize() { if (!m_pFile) return 0; long save = ftell(m_pFile); if (m_OldFile) { rewind(m_pFile); AMX_HEADER hdr; DATAREAD(&hdr, sizeof(hdr), 1); fseek(m_pFile, save, SEEK_SET); return hdr.stp; } else if (m_AmxxFile) { PluginEntry *pe = &(m_Bh.plugins[m_Entry]); if (pe->imagesize > pe->memsize) return pe->imagesize + 1; return pe->memsize + 1; } fseek(m_pFile, m_SectionHdrOffset, SEEK_SET); TableEntry entry; DATAREAD(&entry, sizeof(entry), 1); fseek(m_pFile, save, SEEK_SET); return entry.origSize + 1; // +1 : safe } #undef DATAREAD #define DATAREAD(addr, itemsize, itemcount) \ if (fread(addr, itemsize, itemcount, m_pFile) != static_cast(itemcount)) \ { \ if (feof(m_pFile)) \ m_Status = Err_FileInvalid; \ else \ m_Status = Err_FileRead; \ fclose(m_pFile); \ m_pFile = NULL; \ return m_Status; \ } CAmxxReader::Error CAmxxReader::GetSection(void *buffer) { if (!m_pFile) return m_Status; if (m_OldFile) { // get file size fseek(m_pFile, 0, SEEK_END); long filesize = ftell(m_pFile); rewind(m_pFile); DATAREAD(buffer, 1, filesize); m_Status = Err_None; return m_Status; } else if (m_AmxxFile) { PluginEntry *pe = &(m_Bh.plugins[m_Entry]); char *tempBuffer = new char[m_SectionLength + 1]; fseek(m_pFile, pe->offs, SEEK_SET); DATAREAD((void *)tempBuffer, 1, m_SectionLength); uLongf destLen = GetBufferSize(); int result = uncompress((Bytef *)buffer, &destLen, (Bytef *)tempBuffer, m_SectionLength); delete [] tempBuffer; if (result != Z_OK) { AMXXLOG_Log("[AMXX] Zlib error encountered: %d(%d)", result, m_SectionLength); m_Status = Err_Decompress; return Err_Decompress; } return Err_None; } else { // new file type: go to the section table entry fseek(m_pFile, m_SectionHdrOffset, SEEK_SET); // go to the offset TableEntry entry; DATAREAD(&entry, sizeof(entry), 1); fseek(m_pFile, entry.offset, SEEK_SET); uLongf destLen = GetBufferSize(); // read the data to a temporary buffer char *tempBuffer = new char[m_SectionLength + 1]; //fread(tempBuffer, sizeof(char), m_SectionLength, m_pFile); DATAREAD((void*)tempBuffer, 1, m_SectionLength); // decompress int result = uncompress((Bytef *)buffer, &destLen, (Bytef *)tempBuffer, m_SectionLength); delete [] tempBuffer; if (result != Z_OK) { AMXXLOG_Log("[AMXX] Zlib error encountered: %d(%d)", result, m_SectionLength); m_Status = Err_Decompress; return Err_Decompress; } return Err_None; } }