aboutsummaryrefslogtreecommitdiff
path: root/tools/lzpack
diff options
context:
space:
mode:
authorJohn Wilbert M. Villamor <lameguy64@gmail.com>2019-04-06 10:11:07 +0800
committerJohn Wilbert M. Villamor <lameguy64@gmail.com>2019-04-06 10:11:07 +0800
commitf3e040230772f978540a71aea43dfde200992922 (patch)
treebd8ca31b72dd01e24980b073854e263589530f56 /tools/lzpack
downloadpsn00bsdk-f3e040230772f978540a71aea43dfde200992922.tar.gz
First commit
Diffstat (limited to 'tools/lzpack')
-rw-r--r--tools/lzpack/filelist.cpp79
-rw-r--r--tools/lzpack/filelist.h43
-rw-r--r--tools/lzpack/lzp/lzconfig.h68
-rw-r--r--tools/lzpack/lzp/makefile30
-rw-r--r--tools/lzpack/main.cpp615
-rw-r--r--tools/lzpack/makefile46
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