From: ShadowFlare Date: Wed, 16 Jan 2013 06:26:20 +0000 (-0700) Subject: Partial rewrite to make easier to read and remove Windows-specific code X-Git-Url: https://sfsrealm.hopto.org/projects/gitweb.cgi?p=CwadLib.git;a=commitdiff_plain;h=cfca19c6007280db08e672c38131c49f93739327 Partial rewrite to make easier to read and remove Windows-specific code --- diff --git a/CwadLib.cpp b/CwadLib.cpp new file mode 100644 index 0000000..8238495 --- /dev/null +++ b/CwadLib.cpp @@ -0,0 +1,447 @@ +#include "CwadLib.h" +#include "SFTypes.h" +#include +#include +#include +#ifndef __SYS_ZLIB +#include "zlib/zlib.h" +#else +#include +#endif + +const UInt32 ID_CWAD = *(UInt32 *)"CWAD"; +const UInt32 ID_SEP = *(UInt32 *)"SEP\0"; + +typedef struct _CWADFILEHEADER { + UInt32 dwPackedSize; + UInt32 dwNameLength; + UInt32 dwFullSize; + UInt32 dwFlags; +// char szFileName[dwNameLength]; +// UInt8 byData[dwPackedSize]; +} CWADFILEHEADER; + +/* struct CWAD { + char IDTag[4] = "CWAD"; + CWADFILEHEADER Files[]; +}; */ + +typedef struct _CWADFILE CWADFILE; + +typedef struct _CWADARCHIVE { + TCHAR *pszFileName; + FILE *pFile; + unsigned long nStart; + unsigned long nEnd; + unsigned long nFiles; + CWADFILE *pFiles; + char *pmszFileList; + unsigned long nFileListSize; + bool bHasFileNameBlank; + long nReferences; + bool bOpen; +} CWADARCHIVE; + +typedef struct _CWADFILE { + char *pszFileName; + CWADARCHIVE *pParentArc; + UInt32 dwPackedSize; + UInt32 dwFullSize; + UInt32 dwFlags; + unsigned long nOffset; + UInt8 *pBuffer; + long nReferences; +} CWADFILE; + +typedef struct _CWADFILEHANDLE { + CWADFILE *f; + UInt32 dwFilePointer; +} CWADFILEHANDLE; + +void CWadListFilesInternal(void *hCWAD); +unsigned long CWadFindHeaderAndSize(FILE *hFile, unsigned long *pnCwadEnd); +void CWadDecryptData(UInt8 *pBuffer, unsigned long nBufferLength); + +void * CWadOpenArchive(const TCHAR *pszFileName) +{ + if (!pszFileName) + return 0; + + unsigned long flen; + FILE *pFile; + pFile = _tfopen(pszFileName, _T("rb")); + if (pFile) + { + unsigned long nCwadStart = CWadFindHeaderAndSize(pFile, &flen); + if (nCwadStart != -1) { + CWADARCHIVE *cOpenArc = (CWADARCHIVE *)malloc(sizeof(CWADARCHIVE)); + if (cOpenArc) { + cOpenArc->pFile = pFile; + cOpenArc->nStart = nCwadStart; + cOpenArc->nEnd = flen; + cOpenArc->pmszFileList = 0; + cOpenArc->bHasFileNameBlank = false; + cOpenArc->pFiles = 0; + CWadListFilesInternal(cOpenArc); + if (cOpenArc->pmszFileList) { + cOpenArc->pszFileName = _tcsdup(pszFileName); + cOpenArc->nReferences = 0; + cOpenArc->bOpen = true; + return cOpenArc; + } + + free(cOpenArc); + } + } + + fclose(pFile); + } + + return 0; +} + +bool CWadCloseArchive(void *hCWAD) +{ + if (!hCWAD) + return false; + + CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; + if (cOpenArc->nReferences > 0) { + cOpenArc->bOpen = false; + return false; + } + fclose(cOpenArc->pFile); + if (cOpenArc->pFiles) free(cOpenArc->pFiles); + if (cOpenArc->pmszFileList) free(cOpenArc->pmszFileList); + if (cOpenArc->pszFileName) free(cOpenArc->pszFileName); + free(cOpenArc); + return true; +} + +unsigned long CWadGetArchiveInfo(void *hCWAD, int nInfoType) +{ + if (!hCWAD) + return -1; + + CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; + switch (nInfoType) { + case CWAD_INFO_NUM_FILES: + return cOpenArc->nFiles; + case CWAD_INFO_SIZE: + return cOpenArc->nEnd - cOpenArc->nStart; + default: + return -1; + } +} + +unsigned long CWadListFiles(void *hCWAD, char *pmszBuffer, unsigned long nBufferLength) +{ + if (!hCWAD) + return 0; + + CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; + if (pmszBuffer && nBufferLength >= cOpenArc->nFileListSize) + memcpy(pmszBuffer, cOpenArc->pmszFileList, cOpenArc->nFileListSize); + + return cOpenArc->nFileListSize; +} + +void CWadListFilesInternal(void *hCWAD) +{ + if (!hCWAD) + return; + + CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; + CWADFILEHEADER CwadFile; + unsigned long nReqSize = 0; + cOpenArc->nFiles = 0; + if (cOpenArc->bHasFileNameBlank && cOpenArc->pmszFileList) { + *cOpenArc->pmszFileList = 0; + nReqSize++; + } + fseek(cOpenArc->pFile, cOpenArc->nStart + 4, SEEK_SET); + for (long fpos = ftell(cOpenArc->pFile); fpos != -1 && fpos + sizeof(CWADFILEHEADER) <= cOpenArc->nEnd; fpos = ftell(cOpenArc->pFile)) { + if (fread(&CwadFile, sizeof(CWADFILEHEADER), 1, cOpenArc->pFile) == 1) { + if (cOpenArc->pmszFileList) + { + if (CwadFile.dwNameLength > 0) { + if (fread(cOpenArc->pmszFileList + nReqSize, CwadFile.dwNameLength, 1, cOpenArc->pFile) == 1) + CWadDecryptData((UInt8 *)cOpenArc->pmszFileList + nReqSize, CwadFile.dwNameLength); + cOpenArc->pFiles[cOpenArc->nFiles].pszFileName = cOpenArc->pmszFileList + nReqSize; + } + else + cOpenArc->pFiles[cOpenArc->nFiles].pszFileName = cOpenArc->pmszFileList; + cOpenArc->pFiles[cOpenArc->nFiles].pParentArc = cOpenArc; + cOpenArc->pFiles[cOpenArc->nFiles].dwPackedSize = CwadFile.dwPackedSize; + cOpenArc->pFiles[cOpenArc->nFiles].dwFullSize = CwadFile.dwFullSize; + cOpenArc->pFiles[cOpenArc->nFiles].dwFlags = CwadFile.dwFlags; + cOpenArc->pFiles[cOpenArc->nFiles].nOffset = fpos + sizeof(CWADFILEHEADER) + CwadFile.dwNameLength; + cOpenArc->pFiles[cOpenArc->nFiles].pBuffer = 0; + cOpenArc->pFiles[cOpenArc->nFiles].nReferences = 0; + } + else { + fseek(cOpenArc->pFile, CwadFile.dwNameLength, SEEK_CUR); + } + nReqSize += CwadFile.dwNameLength; + if (CwadFile.dwNameLength > 0) { + if (cOpenArc->pmszFileList) + cOpenArc->pmszFileList[nReqSize] = 0; + nReqSize++; + } + else if (!cOpenArc->bHasFileNameBlank) { + cOpenArc->bHasFileNameBlank = true; + nReqSize++; + } + fseek(cOpenArc->pFile, CwadFile.dwPackedSize, SEEK_CUR); + cOpenArc->nFiles++; + } + else break; + } + if (cOpenArc->pmszFileList) + cOpenArc->pmszFileList[nReqSize] = 0; + nReqSize++; + if (nReqSize == 1) + { + if (cOpenArc->pmszFileList) + cOpenArc->pmszFileList[nReqSize] = 0; + nReqSize++; + } + if (!cOpenArc->pmszFileList) { + cOpenArc->pmszFileList = (char *)malloc(nReqSize); + cOpenArc->nFileListSize = nReqSize; + if (cOpenArc->nFiles > 0) + cOpenArc->pFiles = (CWADFILE *)calloc(cOpenArc->nFiles, sizeof(CWADFILE)); + if (cOpenArc->pmszFileList && (cOpenArc->pFiles || cOpenArc->nFiles == 0)) + CWadListFilesInternal(hCWAD); + else { + if (cOpenArc->pmszFileList) { + free(cOpenArc->pmszFileList); + cOpenArc->pmszFileList = 0; + } + if (cOpenArc->pFiles) + free(cOpenArc->pFiles); + } + } +} + +void * CWadOpenFile(void *hCWAD, const char *pszFileName) +{ + if (!hCWAD || !pszFileName) + return 0; + + CWADARCHIVE *cOpenArc = (CWADARCHIVE *)hCWAD; + unsigned long nFile; + for (nFile = 0; nFile < cOpenArc->nFiles; nFile++) { + if (strcmp(pszFileName, cOpenArc->pFiles[nFile].pszFileName) == 0) { + CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)malloc(sizeof(CWADFILEHANDLE)); + if (cOpenFile) { + cOpenFile->f = &cOpenArc->pFiles[nFile]; + cOpenFile->dwFilePointer = 0; + cOpenFile->f->nReferences++; + cOpenArc->nReferences++; + return cOpenFile; + } + } + } + + return 0; +} + +bool CWadCloseFile(void *hFile) +{ + if (!hFile) + return false; + + CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; + cOpenFile->f->nReferences--; + cOpenFile->f->pParentArc->nReferences--; + if (cOpenFile->f->nReferences < 1 && cOpenFile->f->pBuffer) { + free(cOpenFile->f->pBuffer); + cOpenFile->f->pBuffer = 0; + } + if (cOpenFile->f->pParentArc->nReferences < 1 && !cOpenFile->f->pParentArc->bOpen) + CWadCloseArchive(cOpenFile->f->pParentArc); + free(cOpenFile); + + return true; +} + +unsigned long CWadGetFileSize(void *hFile) +{ + if (!hFile) + return -1; + + CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; + return cOpenFile->f->dwFullSize; +} + +unsigned long CWadGetFileInfo(void *hFile, int nInfoType) +{ + if (!hFile) + return -1; + + CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; + switch (nInfoType) { + case CWAD_INFO_NUM_FILES: + return cOpenFile->f->pParentArc->nFiles; + case CWAD_INFO_SIZE: + return cOpenFile->f->dwFullSize; + case CWAD_INFO_COMPRESSED_SIZE: + return cOpenFile->f->dwPackedSize; + case CWAD_INFO_FLAGS: + return cOpenFile->f->dwFlags; + case CWAD_INFO_PARENT: + return (unsigned long)cOpenFile->f->pParentArc; + case CWAD_INFO_POSITION: + return cOpenFile->dwFilePointer; + default: + return -1; + } +} + +unsigned long CWadSetFilePointer(void *hFile, long nDistanceToMove, int nMoveMethod) +{ + if (!hFile) + return -1; + + CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; + long fsz = cOpenFile->f->dwFullSize; + long cpos = cOpenFile->dwFilePointer; + switch (nMoveMethod) { + case CWAD_FILE_CURRENT: + if (cpos + nDistanceToMove < 0 || cpos + nDistanceToMove > fsz) return -1; + cOpenFile->dwFilePointer += nDistanceToMove; + break; + case CWAD_FILE_END: + if (fsz + nDistanceToMove < 0 || nDistanceToMove > 0) return -1; + cOpenFile->dwFilePointer = fsz + nDistanceToMove; + break; + case CWAD_FILE_BEGIN: + default: + if (nDistanceToMove < 0 || nDistanceToMove > fsz) return -1; + cOpenFile->dwFilePointer = nDistanceToMove; + } + + return cOpenFile->dwFilePointer; +} + +unsigned long CWadReadFile(void *hFile, void *pBuffer, unsigned long nNumberOfBytesToRead) +{ + if (!hFile || !pBuffer || nNumberOfBytesToRead == 0) + return 0; + + CWADFILEHANDLE *cOpenFile = (CWADFILEHANDLE *)hFile; + CWADARCHIVE *cOpenArc = (CWADARCHIVE *)cOpenFile->f->pParentArc; + if (cOpenFile->dwFilePointer >= cOpenFile->f->dwFullSize) + return 0; + if (cOpenFile->dwFilePointer + nNumberOfBytesToRead > cOpenFile->f->dwFullSize) + nNumberOfBytesToRead = cOpenFile->f->dwFullSize - cOpenFile->dwFilePointer; + unsigned long nBytesRead = nNumberOfBytesToRead; + if (cOpenFile->f->dwFlags & 1) { + if (!cOpenFile->f->pBuffer) { + cOpenFile->f->pBuffer = (UInt8 *)malloc(cOpenFile->f->dwFullSize); + UInt8 *compbuffer = (UInt8 *)malloc(cOpenFile->f->dwPackedSize); + bool bReadOK = false; + if (cOpenFile->f->pBuffer && compbuffer) { + fseek(cOpenArc->pFile, cOpenFile->f->nOffset, SEEK_SET); + if (fread(compbuffer, 1, cOpenFile->f->dwPackedSize, cOpenArc->pFile) == cOpenFile->f->dwPackedSize) + if (uncompress(cOpenFile->f->pBuffer, &nBytesRead, compbuffer, cOpenFile->f->dwPackedSize) == Z_OK) + bReadOK = true; + } + if (!bReadOK && cOpenFile->f->pBuffer) { + free(cOpenFile->f->pBuffer); + cOpenFile->f->pBuffer = 0; + nBytesRead = 0; + } + if (compbuffer) + free(compbuffer); + } + if (cOpenFile->f->pBuffer) + memcpy(pBuffer, cOpenFile->f->pBuffer + cOpenFile->dwFilePointer, nNumberOfBytesToRead); + } + else { + fseek(cOpenArc->pFile, cOpenFile->f->nOffset + cOpenFile->dwFilePointer, SEEK_SET); + nBytesRead = fread(pBuffer, 1, nNumberOfBytesToRead, cOpenArc->pFile); + } + + cOpenFile->dwFilePointer += nBytesRead; + return nBytesRead; +} + +unsigned long CWadFindHeader(FILE *hFile) +{ + return CWadFindHeaderAndSize(hFile, 0); +} + +unsigned long CWadFindHeaderAndSize(FILE *pFile, unsigned long *pnCwadEnd) +{ + if (!pFile) return -1; + + if (fseek(pFile, 0, SEEK_END)) return -1; + long fsz = ftell(pFile); + UInt32 buffer[2]; + long sep, i, offset; + + if (pnCwadEnd) *pnCwadEnd = fsz; + + fseek(pFile, 0, SEEK_SET); + if (fread(buffer, sizeof(UInt32), 1, pFile) < 1) return -1; + if (buffer[0] == ID_CWAD) + return 0; + + if (fsz < 12) + return -1; + + if (pnCwadEnd) *pnCwadEnd = fsz - 8; + + fseek(pFile, -8, SEEK_END); + if (fread(buffer, sizeof(UInt32), 2, pFile) < 2) return -1; + if (buffer[0] == ID_CWAD) { + fseek(pFile, buffer[1], SEEK_SET); + if (fread(buffer, sizeof(UInt32), 1, pFile) < 1) return -1; + if (buffer[0] == ID_CWAD) + return buffer[1]; + } + + if (fsz < 132) + return -1; + + for (sep = fsz - 12; sep >= fsz - 132; sep -= 8) { + fseek(pFile, sep, SEEK_SET); + if (fread(buffer, sizeof(UInt32), 1, pFile) < 1) return -1; + + if (buffer[0] == ID_SEP) { + for (i = sep + 4; i < fsz; i += 8) { + fseek(pFile, i, SEEK_SET); + if (fread(buffer, sizeof(UInt32), 2, pFile) < 2) return -1; + + offset = buffer[0]; + if (pnCwadEnd) *pnCwadEnd = offset + buffer[1] - 8; + fseek(pFile, offset + buffer[1] - 8, SEEK_SET); + if (fread(buffer, sizeof(UInt32), 2, pFile) < 2) return -1; + if (buffer[0] == ID_CWAD) { + fseek(pFile, offset + buffer[1], SEEK_SET); + if (fread(buffer, sizeof(UInt32), 1, pFile) < 1) return -1; + if (buffer[0] == ID_CWAD) + return offset + buffer[1]; + } + } + + break; + } + } + + return -1; +} + +void CWadDecryptData(UInt8 *pBuffer, unsigned long nBufferLength) +{ + if (!pBuffer || nBufferLength == 0) return; + pBuffer += nBufferLength - 1; + UInt8 byCWadKey; + byCWadKey = (UInt8)(66 - (nBufferLength << 1)); + for (unsigned long i = 0; i < nBufferLength; i++) { + pBuffer[0] ^= (UInt8)(byCWadKey + (i << 1)); + pBuffer--; + } +} diff --git a/CwadLib.h b/CwadLib.h new file mode 100644 index 0000000..60e34d7 --- /dev/null +++ b/CwadLib.h @@ -0,0 +1,29 @@ +#ifndef CWADLIB_INCLUDED +#define CWADLIB_INCLUDED + +#include + +#define CWAD_INFO_NUM_FILES 0x03 //Number of files in CWAD +#define CWAD_INFO_SIZE 0x05 //Size of CWAD or uncompressed file +#define CWAD_INFO_COMPRESSED_SIZE 0x06 //Size of compressed file +#define CWAD_INFO_FLAGS 0x07 //File flags (compressed, etc.) +#define CWAD_INFO_PARENT 0x08 //Handle of CWAD that file is in +#define CWAD_INFO_POSITION 0x09 //Position of file pointer in files + +#define CWAD_FILE_BEGIN 0 +#define CWAD_FILE_CURRENT 1 +#define CWAD_FILE_END 2 + +void * CWadOpenArchive(const TCHAR *pszFileName); +bool CWadCloseArchive(void *hCWAD); +unsigned long CWadGetArchiveInfo(void *hCWAD, int nInfoType); +unsigned long CWadListFiles(void *hCWAD, char *pmszBuffer, unsigned long nBufferLength); // Returns required buffer size. Strings are in multi string form. (null-terminated strings with an extra null after the last string) +void * CWadOpenFile(void *hCWAD, const char *pszFileName); +bool CWadCloseFile(void *hFile); +unsigned long CWadGetFileSize(void *hFile); +unsigned long CWadGetFileInfo(void *hFile, int nInfoType); +unsigned long CWadSetFilePointer(void *hFile, long nDistanceToMove, int nMoveMethod); +unsigned long CWadReadFile(void *hFile, void *pBuffer, unsigned long nNumberOfBytesToRead); +unsigned long CWadFindHeader(FILE *pFile); + +#endif diff --git a/Cwad_specs.txt b/Cwad_specs.txt new file mode 100644 index 0000000..96f4090 --- /dev/null +++ b/Cwad_specs.txt @@ -0,0 +1,69 @@ +These structs are to demonstrate the layout of a CWAD file. Note that they are not actual working structs. + +In C++: + + struct CWAD { + char IDTag[4] = "CWAD"; + CWADFILE Files[]; + }; + + struct CWADFILE { + DWORD dwPackedSize; + DWORD dwNameLength; + DWORD dwFullSize; + DWORD dwFlags; + char lpFileName[dwNameLength]; + BYTE lpData[dwPackedSize]; + }; + +In VB: + + Type CWAD + IDTag As String * 4 = "CWAD" + Files() as CWADFILE + End Type + + Type CWADFILE + dwPackedSize As Long + dwNameLength As Long + dwFullSize As Long + dwFlags As Long + lpFileName As String * dwNameLength + lpData(dwPackedSize) As Byte + End Type + +At the beginning, there are 4 bytes, the string "CWAD". After that there is a CWADFILE struct for each file until the end of the CWAD file. After the 16 bytes for the header for each file is the filename, then the actual file data, then the next file header. + +If dwFlags is 0, the file is uncompressed. +If it is 1, the file is compressed. + +Filenames are encrypted with a fairly simple encryption method. Here is some code for decrypting them: + +In C++: + +void DecryptData(BYTE *lpBuffer, DWORD dwBufferLength) +{ + if (lpBuffer==0 || dwBufferLength==0) return; + lpBuffer += dwBufferLength - 1; + BYTE byCWadKey; + byCWadKey = (BYTE)(66 - (dwBufferLength << 1)); + for (DWORD i=0;i + +typedef int8_t Int8; +typedef int16_t Int16; +typedef int32_t Int32; +typedef int64_t Int64; +typedef intptr_t IntPtr; + +typedef uint8_t UInt8; +typedef uint16_t UInt16; +typedef uint32_t UInt32; +typedef uint64_t UInt64; +typedef uintptr_t UIntPtr; + +#endif + +union IntConv { + Int8 i8[8]; + UInt8 ui8[8]; + Int16 i16[4]; + UInt16 ui16[4]; + Int32 i32[2]; + UInt32 ui32[2]; + Int64 i64; + UInt64 ui64; +}; + +#endif // #ifndef SFTYPES_INCLUDED