#include // Windows only probably #include #include "DAT1.h" using namespace std; DAT1::DAT1() { directoryCount = 0; unknown1 = 0; unknown2 = 0; unknown3 = 0; directoryBlockSize = 0; directories = NULL; } DAT1::~DAT1() { if (directories != NULL) { for (unsigned int i = 0; i < directoryCount; i++) { delete[] directories[i].files; } } DAT1Stream.close(); } void DAT1::loadDAT1File(string filename) { DAT1Stream.open(filename.c_str(),ios::in | ios::binary); // Read the unknown values out of the header DAT1Stream.read((char *)&directoryCount,sizeof(directoryCount)); DAT1Stream.read((char *)&unknown1,sizeof(unknown1)); DAT1Stream.read((char *)&unknown2,sizeof(unknown2)); // Should be 0, not putting in a die check unless usefulness is shown DAT1Stream.read((char *)&unknown3,sizeof(unknown3)); // Swap out endian order (should check endian order perhaps) directoryCount = (directoryCount>>24) | ((directoryCount<<8) & 0x00FF0000) | ((directoryCount>>8) & 0x0000FF00) | (directoryCount<<24); unknown1 = (unknown1>>24) | ((unknown1<<8) & 0x00FF0000) | ((unknown1>>8) & 0x0000FF00) | (unknown1<<24);;; unknown2 = (unknown2>>24) | ((unknown2<<8) & 0x00FF0000) | ((unknown2>>8) & 0x0000FF00) | (unknown2<<24); unknown3 = (unknown3>>24) | ((unknown3<<8) & 0x00FF0000) | ((unknown3>>8) & 0x0000FF00) | (unknown3<<24); // Allocate an array of Directories directories = new DAT1Directory[directoryCount]; // Now we're at the directory name blocks // There is a single byte containing each // Directory name length char ctmpstr[256]; // Max possible chars per directory for (unsigned int i=0; i < directoryCount;i++) { // Don't need to manually NULL terminate C string memset(ctmpstr,0,sizeof(ctmpstr)); DAT1Stream.read((char *)&directories[i].size,sizeof(directories[i].size)); DAT1Stream.read((char *)&ctmpstr,directories[i].size); directories[i].name = ctmpstr; } for (unsigned int i=0; i < directoryCount; i++) { // Get the directory header DAT1Stream.read((char *)&directories[i].numFiles,sizeof(directories[i].numFiles)); DAT1Stream.read((char *)&directories[i].unknown1,sizeof(directories[i].unknown1)); DAT1Stream.read((char *)&directories[i].unknown2,sizeof(directories[i].unknown2)); DAT1Stream.read((char *)&directories[i].unknown3,sizeof(directories[i].unknown3)); // Fix endianness (should be conditional for big endian systems) directories[i].numFiles = (directories[i].numFiles>>24) | ((directories[i].numFiles<<8) & 0x00FF0000) | ((directories[i].numFiles>>8) & 0x0000FF00) | (directories[i].numFiles<<24); directories[i].unknown1 = (directories[i].unknown1>>24) | ((directories[i].unknown1<<8) & 0x00FF0000) | ((directories[i].unknown1>>8) & 0x0000FF00) | (directories[i].unknown1<<24); directories[i].unknown2 = (directories[i].unknown2>>24) | ((directories[i].unknown2<<8) & 0x00FF0000) | ((directories[i].unknown2>>8) & 0x0000FF00) | (directories[i].unknown2<<24); directories[i].unknown3 = (directories[i].unknown3>>24) | ((directories[i].unknown3<<8) & 0x00FF0000) | ((directories[i].unknown3>>8) & 0x0000FF00) | (directories[i].unknown3<<24); // Allocate enough file classes for each file directories[i].files = new DAT1File[directories[i].numFiles]; // Now enumerate the files in the directory for (unsigned int j = 0; j < directories[i].numFiles; j++) { // Don't need to manually NULL terminate C string memset(ctmpstr,0,sizeof(ctmpstr)); // Get filename length and filename, store it in tmp str DAT1Stream.read((char *)&directories[i].files[j].size,sizeof(directories[i].files[j].size)); DAT1Stream.read((char *)&ctmpstr,directories[i].files[j].size); // Strick it in the class directories[i].files[j].filename = ctmpstr; // Escape the name while ((pos = directories[i].name.find("\\",oldpos)) != string::npos) { static string::size_type pos = 0; static cout << "pos = " << pos << " oldpos = " << oldpos << " Substring = " << directories[i].name.substr(pos,(pos-oldpos)) << endl; oldpos = pos; oldpos += 2; cout << "oldpos = " << oldpos << endl; } /* while (((pos = directories[i].name.find("\\",oldpos)) != string::npos)) { cout << "pos = " << pos << " oldpos = " << oldpos << " Substring = " << directories[i].name.substr(pos,(pos-oldpos)) << endl; directories[i].escapedName.append(directories[i].name.substr(pos,(pos-oldpos))); directories[i].escapedName.append("\\"); oldpos = pos; oldpos += 1; } */ // Get the integers DAT1Stream.read((char *)&directories[i].files[j].attributes,sizeof(directories[i].files[j].attributes)); DAT1Stream.read((char *)&directories[i].files[j].offset,sizeof(directories[i].files[j].offset)); DAT1Stream.read((char *)&directories[i].files[j].uncompressedSize,sizeof(directories[i].files[j].uncompressedSize)); DAT1Stream.read((char *)&directories[i].files[j].compressedSize,sizeof(directories[i].files[j].compressedSize)); // Swap endian order directories[i].files[j].attributes = (directories[i].files[j].attributes>>24) | ((directories[i].files[j].attributes<<8) & 0x00FF0000) | ((directories[i].files[j].attributes>>8) & 0x0000FF00) | (directories[i].files[j].attributes<<24); directories[i].files[j].offset = (directories[i].files[j].offset>>24) | ((directories[i].files[j].offset<<8) & 0x00FF0000) | ((directories[i].files[j].offset>>8) & 0x0000FF00) | (directories[i].files[j].offset<<24); directories[i].files[j].uncompressedSize = (directories[i].files[j].uncompressedSize>>24) | ((directories[i].files[j].uncompressedSize<<8) & 0x00FF0000) | ((directories[i].files[j].uncompressedSize>>8) & 0x0000FF00) | (directories[i].files[j].uncompressedSize<<24);; directories[i].files[j].compressedSize = (directories[i].files[j].compressedSize>>24) | ((directories[i].files[j].compressedSize<<8) & 0x00FF0000) | ((directories[i].files[j].compressedSize>>8) & 0x0000FF00) | (directories[i].files[j].compressedSize<<24);; } } return; } void DAT1::decompressFile(string inFile, string outFile) { ofstream fout(outFile.c_str(),ios::out | ios::binary); DAT1File *datFile = NULL; for (unsigned int i=0; i < directoryCount; i++) { for (unsigned int j=0; j < directories[i].numFiles; j++) { if (directories[i].files[j].filename == inFile) { datFile = &directories[i].files[j]; break; } } } if (datFile == NULL) { cout << "Could not find file " << inFile << endl; return; } DAT1Stream.seekg(datFile->offset); int filesize = (datFile->compressedSize) ? datFile->compressedSize : datFile->uncompressedSize; //printf("Offset: 0x%0x Size: %0d\n",datFile->offset,filesize); if (!datFile->compressedSize) { char *buf = new char[filesize]; DAT1Stream.read((char *)buf,filesize); fout.write((char *)buf, filesize); fout.close(); return; } // We need to decompress the LZSS compression int windowSize = 4096; char *dict = new char[4096]; // Fill dictionary with spaces memset(dict,'\0x20',4096); int offset = 0; unsigned int O = 0; unsigned char L = 0; unsigned char FLAGS = 0; unsigned char byte = 0; short N = 0; // 0. Set dictionary offset to D-18 offset = windowSize - 18; // 1. If at the end of file, exit. while(filesize > 0) { // 2. Read N (2 bytes == word) from input. // The absolute value of N is how many bytes of data to read (if N=0, exit) DAT1Stream.read((char *)&N,sizeof(N)); filesize-=sizeof(N); // Flip endianness N = (N>>8) | (N<<8); if (N == 0) return; // 2.1. If N < 0, read the absolute value // of N bytes from input and write to output (do not put into dictionary). Go to 1. if (N < 0) { char *output = new char[N*-1]; DAT1Stream.read((char *)output,N*-1); filesize -= N*-1; fout.write((char *)output,N*-1); continue; } // 2.2. If N > 0, clear dictionary, repeat below until N bytes have been read from input, // and then go to 1. if (N > 0) { // Clear dictionary (with spaces, not null) memset(dict,'\0x20',4096); while (N > 0) { if (filesize == 0) break; // 2.2.1. Read FLAGS (1 byte) from input. Repeat below 8 times. DAT1Stream.read((char *)&FLAGS,sizeof(FLAGS)); filesize -= sizeof(FLAGS); N-=sizeof(FLAGS); // Repeat 8 times for (int i=0;i<8 && N > 0;i++,FLAGS = FLAGS>>1) { // 2.2.1.1. If FLAGS *is not* a modulus of 2 (& 1 != 0), //read 1 byte from input, write to output, then go to 2.2.1.3. if ((FLAGS & 1) != 0) { DAT1Stream.read((char *)&byte, sizeof(byte)); filesize--; N--; fout << byte; dict[offset] = byte; if (offset == (windowSize-1)) offset = 0; else offset++; } else { // 2.2.1.2. If FLAGS *is* a modulus of 2 (& 1 == 0) O = L = 0; // 2.2.1.2.1. Read O (1 byte) and L (1 byte) from input. DAT1Stream.read((char *)&O,1); DAT1Stream.read((char *)&L,1); filesize -= 2; N-=2; // 2.2.1.2.2. Take away the High-nibble (first 4 bits) from L and prepend it to O; O = (unsigned int)((((unsigned int)L>>4)<<8) | (unsigned int)O); L <<= 4; L >>= 4; //fout << endl << "O = " << (int)O << " L = " << (int)L << " offset[" << (int)O << "] = \"" << (char)dict[O] << "\" offset = " << (int)offset << endl; for (int m=0;m<(L+3);m++) { dict[offset] = dict[O]; if (offset == (windowSize-1)) offset = 0; else offset++; fout << dict[O]; if (O == (windowSize-1)) O = 0; else O++; } } } } } } fout.close(); } void DAT1::decompress() { ofstream fout; int filesize = 0; for (unsigned int i=0; i < directoryCount; i++) { if (directories[i].name != ".") { cout << "Creating: " << directories[i].escapedName << endl; _mkdir(directories[i].escapedName.c_str()); } for (unsigned int j=0; j < directories[i].numFiles; j++) { cout << "Extracting " << directories[i].escapedName + "\\" + directories[i].files[j].filename << endl; decompressFile(directories[i].files[j].filename,"DATA\\" + directories[i].files[j].filename); /* char *buf = new char[filesize]; DAT1Stream.read((char *)buf,filesize); fout.write((char *)buf, filesize); fout.close(); delete[] buf; */ } } } void DAT1::printDAT1Info(string inFilename) { ofstream Boo(inFilename.c_str(),ios::out); Boo << "[Header Information]" << endl; Boo << "Directory Count: " << directoryCount << endl; Boo << "Unknown #1: " << unknown1 << endl; Boo << "Unknown #2: " << unknown2 << endl; Boo << "Unknown #3: " << unknown3 << endl; Boo << "Directory Block Size: " << directoryBlockSize << endl; Boo << endl; Boo << "[Directories]" << endl; for (unsigned int i=0; i < directoryCount; i++) { Boo << "Directory Name: " << directories[i].name << endl; Boo << "Number of files: " << directories[i].numFiles << endl; for (unsigned int j=0; j < directories[i].numFiles; j++) { Boo << "File Name: " << directories[i].files[j].filename << endl; } } Boo.close(); }