diff options
| author | John Wilbert M. Villamor <lameguy64@gmail.com> | 2019-04-06 10:11:07 +0800 |
|---|---|---|
| committer | John Wilbert M. Villamor <lameguy64@gmail.com> | 2019-04-06 10:11:07 +0800 |
| commit | f3e040230772f978540a71aea43dfde200992922 (patch) | |
| tree | bd8ca31b72dd01e24980b073854e263589530f56 /tools/lzpack | |
| download | psn00bsdk-f3e040230772f978540a71aea43dfde200992922.tar.gz | |
First commit
Diffstat (limited to 'tools/lzpack')
| -rw-r--r-- | tools/lzpack/filelist.cpp | 79 | ||||
| -rw-r--r-- | tools/lzpack/filelist.h | 43 | ||||
| -rw-r--r-- | tools/lzpack/lzp/lzconfig.h | 68 | ||||
| -rw-r--r-- | tools/lzpack/lzp/makefile | 30 | ||||
| -rw-r--r-- | tools/lzpack/main.cpp | 615 | ||||
| -rw-r--r-- | tools/lzpack/makefile | 46 |
6 files changed, 881 insertions, 0 deletions
diff --git a/tools/lzpack/filelist.cpp b/tools/lzpack/filelist.cpp new file mode 100644 index 0000000..8554ae0 --- /dev/null +++ b/tools/lzpack/filelist.cpp @@ -0,0 +1,79 @@ +#include "filelist.h" + +FileListClass::FileListClass() { + + NumFiles = 0; + AllocFiles = 1; + + FileList = (FileListEntry*)malloc(sizeof(FileListEntry)); + memset(FileList, 0x00, sizeof(FileListEntry)); + +} + +FileListClass::~FileListClass() { + + for(int i=NumFiles-1; i>=0; i--) { + + if (FileList[i].fileName != NULL) + free(FileList[i].fileName); + + if (FileList[i].aliasName != NULL) + free(FileList[i].aliasName); + + } + + free(FileList); + +} + +void FileListClass::AddFileEntry(const char* fileName, const char* aliasName, short windowSize, short hash1Size, short hash2Size) { + + if (NumFiles >= AllocFiles) { + + FileList = (FileListEntry*)realloc(FileList, sizeof(FileListEntry)*(AllocFiles+1)); + memset(&FileList[AllocFiles], 0x00, sizeof(FileListEntry)); + + AllocFiles++; + + } + + if (aliasName == NULL) + FileList[NumFiles].aliasName = NULL; + else + FileList[NumFiles].aliasName = strdup(aliasName); + + FileList[NumFiles].fileName = strdup(fileName); + FileList[NumFiles].windowSize = windowSize; + FileList[NumFiles].hash1Size = hash1Size; + FileList[NumFiles].hash2Size = hash2Size; + NumFiles++; + +} + +const FileListEntry* FileListClass::Entry(int index) { + + return(&FileList[index]); + +} + +int FileListClass::EntryCount() { + + return(NumFiles); + +} + +void FileListClass::PrintEntries() { + + for(int i=0; i<NumFiles; i++) { + + printf("FL FILE:%s", FileList[i].fileName); + + if (FileList[i].aliasName != NULL) { + printf(" ALIAS:%s\n", FileList[i].aliasName); + } else { + printf("\n"); + } + + } + +} diff --git a/tools/lzpack/filelist.h b/tools/lzpack/filelist.h new file mode 100644 index 0000000..5351335 --- /dev/null +++ b/tools/lzpack/filelist.h @@ -0,0 +1,43 @@ +#ifndef _FILELIST_H +#define _FILELIST_H + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> + +#ifndef MAX_PATH +#define MAX_PATH PATH_MAX +#endif + +typedef struct { + char* fileName; + char* aliasName; + int windowSize; + int hash1Size; + int hash2Size; +} FileListEntry; + +class FileListClass { + + int NumFiles; + int AllocFiles; + FileListEntry* FileList; + +public: + + FileListClass(); + virtual ~FileListClass(); + + void AddFileEntry(const char* fileName, const char* aliasName, short windowSize, short hash1Size, short hash2Size); + + const FileListEntry* Entry(int index); + int EntryCount(); + + void PrintEntries(); + +}; + + +#endif diff --git a/tools/lzpack/lzp/lzconfig.h b/tools/lzpack/lzp/lzconfig.h new file mode 100644 index 0000000..65e623c --- /dev/null +++ b/tools/lzpack/lzp/lzconfig.h @@ -0,0 +1,68 @@ +/*! \file lzconfig.h + * \brief Library configuration header + * \details Define settings will only take effect when you recompile the library. + */ + +#ifndef _LZP_CONFIG_H +#define _LZP_CONFIG_H + + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + + +/* Set to TRUE to compile without data compression routines useful if you + * plan to use this library on a program that does not require said routines + * especially on a platform with limited memory (such as the PlayStation). + * + * This define will rule out lzCompress(), lzSetHashSizes() and + * lzResetHashSizes() functions and their associated functions. + */ +#define LZP_NO_COMPRESS FALSE + + +/* Set to TRUE to make default compression table sizes to maximum and works best + * when compressing large amounts of data. LZP_USE_MALLOC must be set to TRUE to + * prevent stack overflow errors. + * + * Do not enable this if you plan to compile for a platform with limited memory + * otherwise, the library will consume all memory and crash the system. + * + * This define only affects lzCompress(). + */ +#define LZP_MAX_COMPRESS TRUE + + +/* Uncomment to make the library use malloc() instead of array initializers to + * allocate hash tables. Enabling this is a must if you plan to use large hash + * and window table sizes. + */ +#define LZP_USE_MALLOC TRUE + + +/* Hash table sizes (in power-of-two multiple units) + * + * These define only affect lzCompress(). + */ +#if LZP_MAX_COMPRESS == TRUE + +// Minimal defaults +#define LZP_WINDOW_SIZE 17 +#define LZP_HASH1_SIZE 8 +#define LZP_HASH2_SIZE 10 + +#else + +// Maximum defaults +#define LZP_WINDOW_SIZE 17 +#define LZP_HASH1_SIZE 22 +#define LZP_HASH2_SIZE 24 + +#endif + + +#endif // _LZP_CONFIG_H diff --git a/tools/lzpack/lzp/makefile b/tools/lzpack/lzp/makefile new file mode 100644 index 0000000..83da67d --- /dev/null +++ b/tools/lzpack/lzp/makefile @@ -0,0 +1,30 @@ +# This LZP library builds off the lzp sources in libpsn00b/lzp. The only +# difference is this is built with compression enabled specified in the +# lzconfig.h file and the library is built for the host platform. + +TARGET = liblzp.a + +CFILES = $(wildcard ../../../libpsn00b/lzp/*.c) +OFILES = $(addprefix build/, $(notdir $(CFILES:.c=.o))) + +INCLUDE = -I../include -I. + +CFLAGS = -g -O2 + +CC = $(PREFIX)gcc +AR = $(PREFIX)ar +RANLIB = $(PREFIX)ranlib + +all: $(TARGET) + +$(TARGET): $(OFILES) + $(AR) cr $(TARGET) $(OFILES) + $(RANLIB) $(TARGET) + +# Dunno if there's a better way to do this but it works at least +build/%.o: ../../../libpsn00b/lzp/%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +clean: + rm -Rf build $(TARGET)
\ No newline at end of file diff --git a/tools/lzpack/main.cpp b/tools/lzpack/main.cpp new file mode 100644 index 0000000..ed650d2 --- /dev/null +++ b/tools/lzpack/main.cpp @@ -0,0 +1,615 @@ +#include <stdio.h> +#include <tinyxml2.h> + +#include "lzp/lzconfig.h" +#include "lzp/lzp.h" +#include "filelist.h" + + +#define BUFF_SIZE 4096 + + +typedef struct { + char id[3]; + u_char numFiles; +} QLP_HEAD; + +typedef struct { + char fileName[16]; + u_long fileSize; + u_long offset; +} QLP_FILE; + + +typedef struct { + char name[16]; + int size; + int offset; // In 2048 byte sector units +} PCK_FILE; + +typedef struct { + char id[3]; + u_char numFiles; + PCK_FILE file[85]; // File entries + int lba; // LBA of the PCK file (in 2048 byte sector units) +} PCK_TOC; + + +namespace param { + + bool AlwaysOverwrite = false; + char ScriptFile[MAX_PATH]= { 0 }; + +} + + +int ParseCreateElement(tinyxml2::XMLElement* element); + +char* lcase(char* str); +const char* TrimPathName(const char* path); + + +int main(int argc, const char* argv[]) { + + printf("LZPack v0.60b - File Compression and Packing Utility\n"); + printf("2016-2019 Meido-Tek Productions (Lameguy64)\n\n"); + + if (argc <= 1) { + + printf("Parameters:\n"); + printf(" lzpack [-y] <scriptFile>\n\n"); + printf(" -y - Always overwrite existing files.\n"); + printf(" <scriptFile> - Script file to parse (in XML format, see readme.txt).\n"); + + exit(0); + + } + + + // Parse arguments + for(int i=1; i<argc; i++) { + + if (strcmp("-y", argv[i]) == 0) { + + param::AlwaysOverwrite = true; + + } else if ((argv[i][0] == '-') || (argv[i][0] == '/')) { + + printf("Unknown parameter: %s\n", argv[i]); + + } else { + + strcpy(param::ScriptFile, argv[i]); + + } + + } + + if (strlen(param::ScriptFile) == 0) { + printf("ERROR: No script file specified.\n"); + exit(EXIT_FAILURE); + } + + + tinyxml2::XMLDocument document; + + switch(document.LoadFile(param::ScriptFile)) { + case tinyxml2::XML_SUCCESS: + + break; + + case tinyxml2::XML_ERROR_FILE_NOT_FOUND: + + printf("ERROR: Could not find file: %s\n", param::ScriptFile); + exit(EXIT_FAILURE); + + case tinyxml2::XML_ERROR_FILE_COULD_NOT_BE_OPENED: + case tinyxml2::XML_ERROR_FILE_READ_ERROR: + + printf("ERROR: Could not load file: %s\n", param::ScriptFile); + exit(EXIT_FAILURE); + + case tinyxml2::XML_ERROR_EMPTY_DOCUMENT: + + printf("ERROR: %s is empty.\n", param::ScriptFile); + exit(EXIT_FAILURE); + + default: + + printf("ERROR: Unknown error when loading %s\n", param::ScriptFile); + exit(EXIT_FAILURE); + + } + + + tinyxml2::XMLElement* element = document.FirstChildElement("lzp_project"); + + if (element == NULL) { + + printf("ERROR: <lzp_project> element not found.\n"); + exit(EXIT_FAILURE); + + } + + + tinyxml2::XMLElement* createElement = element->FirstChildElement("create"); + + while(createElement != NULL) { + + ParseCreateElement(createElement); + + createElement = createElement->NextSiblingElement(); + + } + + + return(0); + +} + + +int CreateLZPfile(const char* packFile, FileListClass* fileList) { + + FILE* packp; + LZP_FILE entry[fileList->EntryCount()]; + int overallSize=0; + int overallPackedSize=0; + + + packp = fopen(packFile, "wb"); + + fseek(packp, sizeof(LZP_HEAD)+(sizeof(LZP_FILE)*fileList->EntryCount()), SEEK_SET); + + for(int i=0; i<fileList->EntryCount(); i++) { + + const char* name; + + if (fileList->Entry(i)->aliasName == NULL) { + + name = TrimPathName(fileList->Entry(i)->fileName); + + } else { + + name = fileList->Entry(i)->aliasName; + + } + + if (strlen(name) > 15) { + + printf("ERROR: Entry '%s' has more than 15 characters.\n", name); + fclose(packp); + unlink(packFile); + + return(0); + + } + + strcpy(entry[i].fileName, name); + + if (fileList->Entry(i)->aliasName == NULL) { + printf(" Packing %s... ", fileList->Entry(i)->fileName); + } else { + printf(" Packing %s as %s... ", fileList->Entry(i)->fileName, fileList->Entry(i)->aliasName); + } + + + FILE* fp = fopen(fileList->Entry(i)->fileName, "rb"); + + fseek(fp, 0, SEEK_END); + int fileSize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + void* fileBuff = malloc(fileSize); + fread(fileBuff, fileSize, 1, fp); + + fclose(fp); + + + void* compBuff = malloc(fileSize+16384); + int compSize = lzCompress(compBuff, fileBuff, fileSize, 2); + + + entry[i].crc = lzCRC32(compBuff, compSize, LZP_CRC32_REMAINDER); + entry[i].fileSize = fileSize; + entry[i].packedSize = compSize; + entry[i].offset = ftell(packp); + + fwrite(compBuff, compSize, 1, packp); + + free(compBuff); + free(fileBuff); + + printf("Ok. (%.02f%%)\n", 100.f*((float)compSize/fileSize)); + + overallSize += fileSize; + overallPackedSize += compSize; + + } + + + LZP_HEAD head; + + strncpy(head.id, "LZP", 4); + head.numFiles = fileList->EntryCount(); + + fseek(packp, 0, SEEK_SET); + fwrite(&head, sizeof(LZP_HEAD), 1, packp); + + fwrite(entry, sizeof(LZP_FILE), fileList->EntryCount(), packp); + + fclose(packp); + + + printf("Packed %d file(s) totaling %d bytes (%.02f%% compression ratio).\n", + fileList->EntryCount(), + overallPackedSize, + 100.f*((float)overallPackedSize/overallSize) + ); + + + return(true); + +} + +int CreateQLPfile(const char* packFile, FileListClass* fileList) { + + FILE* packp; + QLP_HEAD head; + QLP_FILE fileEntry[fileList->EntryCount()]; + + strncpy(head.id, "QLP", 3); + head.numFiles = fileList->EntryCount(); + + packp = fopen(packFile, "wb"); + + fseek(packp, sizeof(QLP_HEAD)+(sizeof(QLP_FILE)*head.numFiles), SEEK_SET); + + for(int i=0; i<head.numFiles; i++) { + + // Get name for the file entry either from its source file name or specified alias + const char* name; + + if (fileList->Entry(i)->aliasName == NULL) { + + name = TrimPathName(fileList->Entry(i)->fileName); + + } else { + + name = fileList->Entry(i)->aliasName; + + } + + // Make sure entry name does not exceed 15 characters (16 with null terminator byte) + if (strlen(name) > 15) { + + printf("ERROR: Entry '%s' has more than 15 characters.\n", name); + fclose(packp); + unlink(packFile); + + return(0); + + } + + strcpy(fileEntry[i].fileName, name); + + if (fileList->Entry(i)->aliasName == NULL) { + + printf(" Packing %s... ", fileList->Entry(i)->fileName); + + } else { + + printf(" Packing %s as %s... ", fileList->Entry(i)->fileName, fileList->Entry(i)->aliasName); + + } + + // Make sure written data is aligned in multiples of 4 bytes + if ((4*((ftell(packp)+3)/4)) != ftell(packp)) + fseek(packp, (4*((ftell(packp)+3)/4)), SEEK_SET); + + // Set name and offset of file entry + memset(fileEntry[i].fileName, 0x00, 16); + strcpy(fileEntry[i].fileName, name); + fileEntry[i].offset = ftell(packp); + + // Open file and copy its contents to the pack file + FILE* fp = fopen(fileList->Entry(i)->fileName, "rb"); + + int bytesCopied = 0; + void* copyBuff = malloc(BUFF_SIZE); + + while(!feof(fp)) { + + int bytesRead = fread(copyBuff, 1, BUFF_SIZE, fp); + + fwrite(copyBuff, bytesRead, 1, packp); + + bytesCopied += bytesRead; + + } + + free(copyBuff); + fclose(fp); + + fileEntry[i].fileSize = bytesCopied; + + printf("Done.\n"); + + } + + printf("Packed %d file(s) totaling %d bytes.\n", head.numFiles, (int)ftell(packp)); + + fseek(packp, 0, SEEK_SET); + fwrite(&head, sizeof(QLP_HEAD), 1, packp); + + fwrite(fileEntry, sizeof(QLP_FILE), head.numFiles, packp); + + fclose(packp); + + return(true); + +} + +int CreatePCKfile(const char* packFile, FileListClass* fileList) { + + FILE* packp; + PCK_TOC toc; + + memset(&toc, 0x00, sizeof(PCK_TOC)); + + toc.numFiles = fileList->EntryCount(); + + packp = fopen(packFile, "wb"); + + fseek(packp, 2048, SEEK_SET); + + for(int i=0; i<toc.numFiles; i++) { + + // Get name for the file entry either from its source file name or specified alias + const char* name; + + if (fileList->Entry(i)->aliasName == NULL) { + + name = TrimPathName(fileList->Entry(i)->fileName); + + } else { + + name = fileList->Entry(i)->aliasName; + + } + + // Make sure entry name does not exceed 15 characters (16 with null terminator byte) + if (strlen(name) > 15) { + + printf("ERROR: Entry '%s' has more than 15 characters.\n", name); + fclose(packp); + unlink(packFile); + + return(0); + + } + + strcpy(toc.file[i].name, name); + toc.file[i].offset = ftell(packp)/2048; + + if (fileList->Entry(i)->aliasName == NULL) { + + printf(" Packing %s... ", fileList->Entry(i)->fileName); + + } else { + + printf(" Packing %s as %s... ", fileList->Entry(i)->fileName, fileList->Entry(i)->aliasName); + + } + + FILE* fp = fopen(fileList->Entry(i)->fileName, "rb"); + void* buff = malloc(BUFF_SIZE); + + int bytesTotal = 0; + + while(!feof(fp)) { + + int bytesRead = fread(buff, 1, BUFF_SIZE, fp); + fwrite(buff, 1, bytesRead, packp); + bytesTotal += bytesRead; + + } + + fclose(fp); + free(buff); + + toc.file[i].size = bytesTotal; + + if ((2048*((ftell(packp)+2047)/2048)) != ftell(packp)) { + + int pad = (2048*(((ftell(packp)%2048)+2047)/2048))-(ftell(packp)%2048); + char padding[pad]; + + memset(padding, 0x00, pad); + fwrite(padding, pad, 1, packp); + + } + + printf("Done.\n"); + + } + + printf("Packed %d file(s) totaling %d bytes.\n", toc.numFiles, (int)ftell(packp)); + + strncpy(toc.id, "PCK", 3); + + fseek(packp, 0, SEEK_SET); + fwrite(&toc, sizeof(PCK_TOC), 1, packp); + + fclose(packp); + + return(true); + +} + +int ParseCreateElement(tinyxml2::XMLElement* element) { + + + const char* packName = element->Attribute("packname"); + + if (packName == NULL) { + printf("ERROR: No 'packname' attribute found in <create> element.\n"); + return(false); + } + + + int packFormat; + + { + + char* packType = (char*)element->Attribute("format"); + + if (packType == NULL) { + + packType = strdup("lzp"); + + } else { + + packType = strdup(packType); + lcase(packType); + + } + + if (strcmp("lzp", packType) == 0) { + packFormat = 0; + } else if (strcmp("qlp", packType) == 0) { + packFormat = 1; + } else if (strcmp("pck", packType) == 0) { + packFormat = 2; + } else { + + printf("ERROR: Unknown pack format: %s\n", packType); + free(packType); + return(false); + + } + + free(packType); + + } + + + printf("Creating %s in ", packName); + switch(packFormat) { + case 0: + + printf("LZP"); + break; + + case 1: + + printf("QLP"); + break; + + case 2: + + printf("PCK"); + break; + + } + printf(" format...\n"); + + + tinyxml2::XMLElement* fileElement = element->FirstChildElement("file"); + + if (fileElement == NULL) { + printf("ERROR: No file element(s) found.\n"); + return(false); + } + + + FileListClass fileList; + + while(1) { + + bool valid = true; + + int entryWindowSize = LZP_WINDOW_SIZE; + int entryHash1Size = LZP_HASH1_SIZE; + int entryHash2Size = LZP_HASH2_SIZE; + + if (fileElement->GetText() == NULL) { + printf("WARNING: <file> element not containing text found.\n"); + valid = false; + } + + + FILE* fp = fopen(fileElement->GetText(), "rb"); + if (!fp) { + printf("WARNING: File '%s' either does not exist or it cannot be opened.\n", fileElement->GetText()); + valid = false; + } + fclose(fp); + + + if (valid) { + fileList.AddFileEntry( + fileElement->GetText(), + fileElement->Attribute("alias"), + entryWindowSize, + entryHash1Size, + entryHash2Size + ); + } + + + fileElement = fileElement->NextSiblingElement(); + + if (fileElement == NULL) + break; + + } + + + if (fileList.EntryCount() == 0) { + printf("No file(s) to pack.\n"); + return(true); + } + + switch(packFormat) { + case 0: // Create LZP + CreateLZPfile(packName, &fileList); + break; + case 1: // Create QLP + CreateQLPfile(packName, &fileList); + break; + case 2: // Create PCK + CreatePCKfile(packName, &fileList); + break; + } + + + return(true); + +} + + +const char* TrimPathName(const char* path) { + + if ((strrchr(path, '\\') == NULL) && (strrchr(path, '/') == NULL)) { + + return(path); + + } else { + + if (strrchr(path, '\\') == NULL) + return(strrchr(path, '/')+1); + + return(strrchr(path, '\\')+1); + + } + +} + +char* lcase(char* str) { + + for(int i=0; str[i]!=0x00; i++) + str[i] = tolower(str[i]); + + return(str); + +} diff --git a/tools/lzpack/makefile b/tools/lzpack/makefile new file mode 100644 index 0000000..e11f5c7 --- /dev/null +++ b/tools/lzpack/makefile @@ -0,0 +1,46 @@ +TARGET := lzpack + +CPPFILES = main.cpp filelist.cpp +CFLAGS = -O2 +LDFLAGS = -s + +LIBS = -ltinyxml2 -llzp + +CC = gcc +CXX = g++ + +OFILES = $(addprefix build/,$(CPPFILES:.cpp=.o)) + +ifeq "$(OS)" "Windows_NT" + +# Config for Windows +INCLUDE = -I/c/tinyxml2 +LIBDIRS = -L/c/tinyxml2 +TARGET := $(TARGET).exe + +else + +# Config for anything else that isn't Linux + +endif + +INCLUDE += -I../../libpsn00b +LIBDIRS += -Llzp + +build/%.o: %.cpp + @mkdir -p $(dir $@) + $(CXX) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +all: $(OFILES) + $(MAKE) -C lzp + $(CXX) $(CFLAGS) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) + +install: + mkdir -p ../bin + cp $(TARGET) ../bin/$(TARGET) + +clean: + $(MAKE) -C lzp clean + rm -Rf build $(TARGET) + +cleanall: clean |
