// pakMake May 10, 2012. Baker. // License public domain except for BSD string functions strlcpy and strlcat // Provided "as-is". #include #include #include #include #include // MSVC #include #include #define MAX_PATH_IS_1024 1024 // Byte ordering #ifdef __BIG_ENDIAN__ long LittleLong (long myLong) { byte_1 = (myLong >> 0) & 255; byte_2 = (myLong >> 8) & 255; byte_3 = (myLong >> 16) & 255; byte_4 = (myLong >> 24) & 255; return ((long) byte_1 << 24) + ((long) byte_2 << 16) + ((long) byte_3 << 8 ) + byte_4; } #else #define LittleLong(value) (value) #endif // // MSVC has a different name for several standard functions // #ifdef _WIN32 #define strcasecmp _stricmp #define strncasecmp _strnicmp #define SNPrintf _snprintf #else #define SNPrintf snprintf #endif // Not missing, just desired #define CLAMP(min, x, max) ((x) < (min) ? (min) : (x) > (max) ? (max) : (x)) // // Data Types // typedef unsigned char byte; typedef enum { False = 0, True = 1 } fbool; #define Host_FatalError printf #define Console_Printf printf /////////////////////////////////////////////////////////////////////////////// // Simple memory /////////////////////////////////////////////////////////////////////////////// void* Memory_free (void* ptr) { if (ptr == NULL) Host_FatalError ("Memory_free failed: \"%s\"", "Attempted to free NULL pointer"); free (ptr); ptr = NULL; return ptr; // We set ptr to NULL (our local copy) and return ptr for consistency. } void* Memory_malloc (const size_t size, const char* Reason) { void* ptr = malloc (size); if (ptr == NULL) Host_FatalError ("Memory_malloc failed: \"%s\" on %i bytes", Reason, size); return ptr; } void* Memory_calloc (const size_t n, const size_t size, const char* Reason) { void* ptr = calloc (n, size); const int allocsize = (int)(n * size); if (ptr == NULL) Host_FatalError ("Memory_calloc: \"%s\" on %i bytes", Reason, allocsize); return ptr; } // // strlcpy and strlcat // //======================================================== // strlcat and strlcpy, from OpenBSD /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HAVE_STRLCAT size_t strlcat(char *dst, const char *src, size_t siz) { register char *d = dst; register const char *s = src; register size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } #endif // #ifndef HAVE_STRLCAT #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { do { if ((*d++ = *s++) == 0) break; } while (--n != 0); } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } #endif // #ifndef HAVE_STRLCPY // // String functions // #define StringMatch(s1, s2) !strcmp(s1, s2) #define StringMatchCaseless(s1, s2) !strcasecmp(s1, s2) #define StringMatchNCaseless(s1, s2, s3) !strncasecmp(s1, s2, s3) #define StringNOTMatch(s1, s2) (strcmp(s1, s2) !=0 ) #define StringNOTMatchCaseless(s1, s2) (strcasecmp(s1, s2) !=0 ) #define StringStartsWith(s1, s2) !strncmp(s1, s2, strlen(s2)) #define StringStartsWithCaseLess(s1, s2) !strncasecmp(s1, s2, strlen(s2)) #define StringLCopy(dest, source) strlcpy(dest, source, sizeof(dest)) #define StringLCat(dest, stringToAppend) strlcat(dest, stringToAppend, sizeof(dest)) #define StringInStr(bigString, findWhat) (strstr(bigString, findWhat) != NULL) void String_Edit_SlashesForward_Like_Unix (char *WindowsStylePath) { size_t i; // Translate "\" to "/" for (i = 0 ; i < strlen(WindowsStylePath) ; i ++) if (WindowsStylePath[i] == '\\') WindowsStylePath[i] = '/'; } void String_Edit_StripExtension (char* myEditString) { size_t cursor = strlen(myEditString) - 1; for ( ; cursor > 0; cursor --) if (myEditString[cursor] == '.') { myEditString[cursor] = 0; // NULL terminate there break; } } char* String_Edit_Append_To_Name (char* filename, const char* appendToName) { char tempbuffer[MAX_PATH_IS_1024]; // We need this for string work char* extension = strrchr (filename, '.'); // Locate extension (NULL if none) StringLCopy (tempbuffer, filename); if (extension) String_Edit_StripExtension (tempbuffer); StringLCat (tempbuffer, "_copy"); if (extension) StringLCat (tempbuffer, extension); // Tack the extension back on // Move the modified string back into the filename, this is a String_Edit function you know ... strcpy (filename, tempbuffer); // Can't StringLCopy, length of filename is "unknown" here return filename; } int File_HasDotExtension (const char *filename, const char *extension) { // Check to ensure strlen is equal or exceeds strlen extension if (strlen(filename) < strlen(extension)) // This allows for stupid "situations like filename is '.dem' return 0; // Now check for match // Go to end of the file and move back length of extension vs. extension if (StringMatchCaseless (filename + strlen(filename) - strlen(extension), extension)) return 1; // Match! // No Match return 0; } #include void printTime (void) { // http://www.cplusplus.com/forum/windows/18617/ // http://www.cplusplus.com/reference/clibrary/ctime/localtime/ time_t rawtime; struct tm * timeinfo; time ( &rawtime ); timeinfo = localtime ( &rawtime ); printf ( "Current local time and date: %s\n", asctime (timeinfo) ); } /* ================= CheckParm Checks for the given parameter in the program's command line arguments Returns the argument number (1 to argc-1) or 0 if not present ================= */ // set these before calling CheckParm int commandLineArgumentsCount; char **commandLineArguments; int CheckParm (char *check) { int i; for (i = 1; i < commandLineArgumentsCount; i++) { if ( StringMatchCaseless(check, commandLineArguments[i]) ) return i; } return 0; } int System_GetCurrentWorkingDirectory (char* buffer) { // Get the current working directory: if ( _getcwd( buffer, MAX_PATH_IS_1024 ) == NULL ) { perror( "_getcwd error" ); printf ("Unable to get current directory\n"); return 0; } String_Edit_SlashesForward_Like_Unix (buffer); return 1; } #include //unsigned attrib; //__time64_t time_create; /* -1 for FAT file systems */ //__time64_t time_access; /* -1 for FAT file systems */ //__time64_t time_write; //_fsize_t size; //char name[260]; // If successful, _findfirst returns a unique search handle identifying the file or group of files that // match the filespec specification, which can be used in a subsequent call to _findnext or to _findclose. // Otherwise, _findfirst returns –1 and sets errno to one of the following values. //#define _A_NORMAL 0x00 /* Normal file - No read/write restrictions */ //#define _A_RDONLY 0x01 /* Read only file */ //#define _A_HIDDEN 0x02 /* Hidden file */ //#define _A_SYSTEM 0x04 /* System file */ //#define _A_SUBDIR 0x10 /* Subdirectory */ //#define _A_ARCH 0x20 /* Archive file */ #define isFolder(x) (!!(x.attrib & _A_SUBDIR )) int _FileMatch_Count (const char* dirString, const char* filter, fbool doFolders) { char queryString[MAX_PATH_IS_1024]; int count = 0; SNPrintf (queryString, sizeof(queryString), "%s/%s", dirString, filter); do { struct _finddata_t fileinfo; int handle = _findfirst (queryString, &fileinfo); int foundFile = 1; if (handle == -1) break; // No matches for ( ; foundFile != -1; foundFile = _findnext( handle, &fileinfo )) if ( isFolder (fileinfo) == doFolders ) if (doFolders == False /* unconditionally*/ || (StringNOTMatch (fileinfo.name, ".") && StringNOTMatch (fileinfo.name, "..")) /* no special dirs*/) count ++; _findclose (handle); } while (0); return count; } char* String_SkipPath (const char *pathName) { char* lastSlash = strrchr(pathName, '/'); if (lastSlash == NULL) return (char*)pathName; // No slash found, return whole file name return lastSlash + 1; // Return first character after the slash } typedef struct { char text[1024]; } text1024_t; void _FileMatch_Populate (const char* dirString, const char* filter, text1024_t* myFiles, int countMyFiles, fbool doFolders) { char queryString[MAX_PATH_IS_1024]; int count = 0; SNPrintf (queryString, sizeof(queryString), "%s/%s", dirString, filter); do { struct _finddata_t fileinfo; int handle = _findfirst (queryString, &fileinfo); int foundFile = 1; if (handle == -1) break; // No matches for ( ; foundFile != -1 && count < countMyFiles; foundFile = _findnext( handle, &fileinfo )) if ( isFolder (fileinfo) == doFolders ) if (doFolders == False /* unconditionally*/ || (StringNOTMatch (fileinfo.name, ".") && StringNOTMatch (fileinfo.name, "..")) /* no special dirs*/) SNPrintf (myFiles[count++].text, 1024, "%s/%s", dirString, fileinfo.name); _findclose (handle); } while (0); if (count > countMyFiles) printf ("Warning: Count of %i exceeded expected count of %i", count, countMyFiles); // return count; } void sFileMatch_Populate (const char* dirString, const char* filter, text1024_t* myFiles, int countMyFiles) { _FileMatch_Populate (dirString, filter, myFiles, countMyFiles, False /* do files */ ); } void sFolderMatch_Populate (const char* dirString, const char* filter, text1024_t* myFiles, int countMyFiles) { _FileMatch_Populate (dirString, filter, myFiles, countMyFiles, True /* do folders */ ); } int sFileMatch_Count (const char* dirString, const char* filter) { return _FileMatch_Count (dirString, filter, False /* don't do folders */); } int sFolderMatch_Count (const char* dirString, const char* filter) { return _FileMatch_Count (dirString, filter, True /* do folders */); } text1024_t* FileMatch_Populate_Alloc (const char* dirString, const char* filter, int* count) { // 1. Find out number of matches. 2. Allocate space if there are matches otherwise set fileSpace to NULL int matches = sFileMatch_Count (dirString, filter); text1024_t* fileSpace = matches ? Memory_calloc (matches, sizeof(text1024_t), "filename space") : NULL; if (matches) sFileMatch_Populate (dirString, filter, fileSpace, matches); // Set count and return memory address *count = matches; return fileSpace; } text1024_t* FolderMatch_Populate_Alloc (const char* dirString, const char* filter, int* count) { // 1. Find out number of matches. 2. Allocate space if there are matches otherwise set folderSpace to NULL int matches = sFolderMatch_Count (dirString, filter); text1024_t* folderSpace = matches ? Memory_calloc (matches, sizeof(text1024_t), "filename space") : NULL; if (matches) sFolderMatch_Populate (dirString, filter, folderSpace, matches); // Set count and return memory address *count = matches; return folderSpace; } /* ============ main ============ */ int PakFile_Create_From_List (char* outputPakName, text1024_t* listOfFiles, int numFiles); int main (int argc, char **argv) { char currentWorkingDirectory[MAX_PATH_IS_1024]; int numFolders; int numFiles; text1024_t* folderNames = NULL; text1024_t* fileNames = NULL; // int i; printTime (); // Get current directory if ( System_GetCurrentWorkingDirectory (currentWorkingDirectory) == 0) return 1; // Error printf ("Current working directory: %s\n", currentWorkingDirectory); // Set ugly globals commandLineArgumentsCount = argc; commandLineArguments = argv; // Get folders list. Get file list. folderNames = FolderMatch_Populate_Alloc (currentWorkingDirectory, "*", &numFolders); fileNames = FileMatch_Populate_Alloc (currentWorkingDirectory, "*", &numFiles); // Maybe print stuff // if (CheckParm ("-pak")) { PakFile_Create_From_List ("pak_x.pak", fileNames, numFiles); } #ifndef _DEBUG // Always do the pause thing in debug mode if (!CheckParm ("-pause")) #endif { printf ("\nPress enter to continue"); getchar(); } return 0; // 0 = success of application } // Pak file structure // 1. Header // 2. File bytes one after another // 3. Directory typedef struct { char name[56]; int filepos, filelen; } packfile_t; typedef struct { char id[4]; int dirofs; int dirlen; } packheader_t; int File_Length_ByHandle (FILE* fileToCheck) { int lengthOfFile; fseek (fileToCheck, 0, SEEK_END); // Go to end lengthOfFile = ftell(fileToCheck); // Read position, that is file length fseek (fileToCheck, 0, SEEK_SET); // Set to start return lengthOfFile; } int File_Append_BinaryFile (FILE* writeHandle, const char* fileToRead) { int bytesWritten = 0; FILE* readHandle = fopen (fileToRead, "rb"); if (readHandle) { char buf[4096]; int bufsize = sizeof(buf); int fileReadLength = File_Length_ByHandle (readHandle); int remaining = fileReadLength; printf ("Appending %i bytes from %s\n", fileReadLength, fileToRead); //CreatePath (dest); while (remaining) { int bytesThisPass = remaining < bufsize ? remaining : bufsize; fread (buf, bytesThisPass, 1, readHandle); // Read 4096 bytes in (or less if near EOF) fwrite (buf, bytesThisPass, 1, writeHandle); // Write 4096 bytes out remaining -= bytesThisPass; bytesWritten += bytesThisPass; } fclose (readHandle); } return bytesWritten; } int File_GetCursor (FILE* fileHandle) { int position = ftell (fileHandle); return position; } void File_SetCursor_ToStart (FILE* fileHandle) { fseek (fileHandle, 0, SEEK_SET); } int PakFile_Create_From_List (char* outputPakName, text1024_t* listOfFiles, int numFiles) { int i; packheader_t pakHeader; packfile_t pakDirectory[4096]; int pakHeaderSize = sizeof(pakHeader); int dataBytesWritten = 0, fileSize, markSpot; FILE* pakHandle = fopen (outputPakName, "wb"); if (!pakHandle) { printf ("Error opening file for write '%s'\n", outputPakName); return 0; } memset (&pakHeader, 0, sizeof(pakHeader)); memset (pakDirectory, 0, sizeof(pakDirectory)); // Write the header with invalid data. Don't worry we will be updating this later. fwrite (&pakHeader, pakHeaderSize, 1, pakHandle); // Write the files for (i = 0; i < numFiles ; i ++) { char* curFile = listOfFiles[i].text; markSpot = File_GetCursor (pakHandle); // LittleLong for Endian correctness dataBytesWritten += (fileSize = File_Append_BinaryFile (pakHandle, curFile) ); // Get file size, increment dataBytes pakDirectory[i].filepos = LittleLong (markSpot); pakDirectory[i].filelen = LittleLong (fileSize); StringLCopy (pakDirectory[i].name, String_SkipPath(curFile)); } { // Store the offset then write the directory. Data at pakDirectory with entry size of entry 0 times filecount int direntSize = sizeof(pakDirectory[0]); int directoryStart = File_GetCursor (pakHandle); int directoryByteLength = direntSize * numFiles; fwrite (pakDirectory, directoryByteLength, 1, pakHandle); // Construct final header pakHeader.id[0] = 'P'; pakHeader.id[1] = 'A'; pakHeader.id[2] = 'C'; pakHeader.id[3] = 'K'; pakHeader.dirofs = LittleLong (directoryStart); pakHeader.dirlen = LittleLong (directoryByteLength); // Go back to the beginning and rewrite the header File_SetCursor_ToStart (pakHandle); fwrite (&pakHeader, pakHeaderSize, 1, pakHandle); } // close the file fclose (pakHandle); return 1; }