int main(int argc, char *argv[]) { // this should be checked, especially in larger projects if(!ttvfs::checkCompat()) { std::cout << "HUH? ttvfs was compiled with different options than this file!" << std::endl; return 1; } // Note: Here, it is really important that all files & subdirs are loaded recursively. // In the merge step below, only those files that already exist in the tree will be // accessible in their new location. vfs.LoadFileSysRoot(true); vfs.Prepare(); PrintFile("myfile.txt"); // this is the default file PrintFile("patches/myfile.txt"); // this is the patch file std::cout << "-- Mounting 'patches' -> ''" << std::endl; // merge "patches" into root dir //vfs.MountExternalPath("patches"); // this works, but recreates parts of the tree // that are already existing - possibly error prone! vfs.Mount("patches", ""); // <-- this is the better way. // all files and subdirs that were in "patches" are now mirrored in "" as well. PrintFile("myfile.txt"); // Access the file as before -> it got replaced. std::cout << "-- Before mounting 'more/even_more/deep' -> 'far' (should error)" << std::endl; PrintFile("far/file.txt"); // not found! // remount a directory under a different name vfs.Mount("more/even_more/deep", "far"); std::cout << "-- After mounting 'more/even_more/deep' -> 'far'" << std::endl; PrintFile("far/file.txt"); // ... and access this file normally // mount an external directory (this could be ~/.MyApp or anything) vfs.MountExternalPath("../ttvfs", "ext"); ttvfs::VFSDir *ext = vfs.GetDir("ext"); if(ext) { //VFS_GUARD(ext); // in case this would be called from multiple threads, lock this directory. std::cout << "Listing files in 'ext' subdir ..." << std::endl; unsigned int c = 0; ext->forEachFile(FileCallback, &c); std::cout << c << " files in total!" << std::endl; } return 0; }
VFILE *vfopen(const char *fn, const char *mode) { if (strchr(mode, 'w')) { fprintf(stderr, "FileAPI.h: File writing via VFS not yet supported!"); return NULL; } VFILE *vf = vfs.GetFile(fn); if (!vf || !vf->open(mode)) return NULL; ++(vf->ref); // keep the file alive until closed. return vf; }
bool InStream::open(const char *fn) { ttvfs::VFSFile *vf = vfs.GetFile(fn); if(vf) { vf->open("r"); str((char*)vf->getBuf()); // stringstream will always make a copy vf->close(); vf->dropBuf(true); return true; } setstate(std::ios_base::failbit); return false; }
static void PrintFile(const char *fn) { ttvfs::VFSFile *vf = vfs.GetFile(fn); std::cout << "Open file " << fn << ": "; if(vf) { vf->open("r"); // force text mode std::cout << (const char*)vf->getBuf() << std::endl; vf->dropBuf(true); // no longer needed vf->close(); } else { std::cout << "FILE OPEN ERROR!" << std::endl; } }
//Main program entry point int main(int argc, char** argv) { g_bProgressOverwrite = false; g_iNumThreads = 0; DWORD iTicks = GetTickCount(); //Store the starting number of milliseconds vfs.Prepare(); //read in the resource names to unpack initResMap(); initSoundManifest(); parseCmdLine(argc,argv); if(argc < 2) { cout << "Usage: liDecompress [filename1] [filename2] ... [filenameN]" << endl; return 0; } for(int iArg = 1; iArg < argc; iArg++) { if(argv[iArg][0] == '-') //Skip over commandline switches continue; cout << endl << "Unpacking resource blob file " << argv[iArg] << endl; FILE* f = fopen(argv[iArg], "rb"); if(f == NULL) { cout << "Unable to open file " << argv[iArg] << endl; continue; } blobHeader bH; if(fread((void*)&bH, 1, sizeof(blobHeader), f) != sizeof(blobHeader)) { cout << "Error reading number of resources in file " << argv[iArg] << endl; fclose(f); continue; } list<resourceHeader> lResourceHeaders; for(int i = 0; i < bH.numItems; i++) { resourceHeader rH; size_t sizeRead = fread((void*)&rH, 1, sizeof(resourceHeader), f); if(sizeRead != sizeof(resourceHeader)) { cout << "Read " << sizeRead << " bytes, which differs from resource header size " << sizeof(resourceHeader) << endl; fclose(f); continue; } lResourceHeaders.push_back(rH); } //Create list file with all the files that were in this .pak string sPakListFilename = ""; for(int i = strlen(argv[iArg])-1; i >= 0; i--) { if(argv[iArg][i] == '\\' || argv[iArg][i] == '/') break; sPakListFilename.insert(sPakListFilename.begin(), argv[iArg][i]); } sPakListFilename += ".filelist.txt"; ofstream oPakList(sPakListFilename.c_str()); //Iterate through these items, splitting them out of the file and creating new files out of each cout << "Extracting files..." << endl; for(list<resourceHeader>::iterator i = lResourceHeaders.begin(); i != lResourceHeaders.end(); i++) { ThreadConvertHelper dh; makeFolder(i->id); const wchar_t* cName = getName(i->id); oPakList << ws2s(cName) << endl; fseek(f, i->offset, SEEK_SET); dh.sFilename = cName; if(i->flags == FLAG_ZLIBCOMPRESSED) { compressedHeader cH; if(fread((void*)&cH, 1, sizeof(compressedHeader), f) != sizeof(compressedHeader)) { cout << "Error reading compressed header." << endl; fclose(f); continue; } uint32_t size = cH.compressedSizeBytes; uint8_t* buf = (uint8_t*)malloc(size); size_t sizeRead = fread((void*)buf, 1, size, f); if(sizeRead != size) { cout << "Error reading compressed data. Size: " << size << " read: " << sizeRead << endl; fclose(f); free(buf); continue; } dh.data.data = buf; dh.data.compressedSize = cH.compressedSizeBytes; dh.data.decompressedSize = cH.uncompressedSizeBytes; dh.bCompressed = true; } else if(i->flags == FLAG_NOCOMPRESSION) { uint8_t* buf = (uint8_t*)malloc(i->size); if(fread((void*)buf, 1, i->size, f) != i->size) { cout << "Error reading non-compressed data." << endl; fclose(f); free(buf); continue; } dh.data.data = buf; dh.data.compressedSize = dh.data.decompressedSize = i->size; dh.bCompressed = false; } else cout << "Invalid resource flag " << i->flags << endl; g_lThreadedResources.push_back(dh); } threadedDecompress(); fclose(f); oPakList.close(); } cout << "\rDone. " << endl; iTicks = GetTickCount() - iTicks; float iSeconds = (float)iTicks / 1000.0; //Get seconds elapsed int iMinutes = iSeconds / 60; iSeconds -= iMinutes * 60; cout << "Time elapsed: " << iMinutes << " min, " << iSeconds << " sec" << endl; //system("PAUSE"); return 0; }
//Main program entry point int main(int argc, char** argv) { g_bProgressOverwrite = false; g_iNumThreads = 0; g_iCompressAmount = Z_DEFAULT_COMPRESSION; DWORD iTicks = GetTickCount(); vfs.Prepare(); //read in the resource names to pack initResMap(); initSoundManifest(); parseCmdLine(argc, argv); if(argc < 2) { cout << "Usage: liCompress [pakfile1] [pakfile2] ... [pakfileN]" << endl; return 0; } for(int iArg = 1; iArg < argc; iArg++) { if(argv[iArg][0] == '-') //Skip over commandline switches continue; list<wstring> lFilenames; //Files in this pakfile that we're going to compress wstring sArg = s2ws(argv[iArg]); size_t pos = sArg.find(TEXT(".filelist.txt")); if(pos != wstring::npos) sArg.erase(pos, wstring::npos); //Erase a .filelist.pak extension if there is one cout << endl << "Packing resource blob file " << ws2s(sArg) << endl; //Determine what files to pack into this .pak file wstring sInfilename = sArg; sInfilename += TEXT(".filelist.txt"); ifstream infile(ws2s(sInfilename).c_str()); if(infile.fail()) { cout << "Cannot open " << ws2s(sInfilename) << " to pack " << ws2s(sArg) << " Skipping..." << endl; continue; } while(!infile.fail() && !infile.eof()) //Pull in all the lines out of this file { string ss; getline(infile, ss); wstring s = s2ws(ttvfs::FixSlashes(ss)); if(!s.length() || s == TEXT("")) continue; //Ignore blank lines lFilenames.push_back(s); //Add this to our list of files to package } threadedCompress(lFilenames); //Compress everything //Ok, now we have all the compressed files in RAM. Stick them in the .pak file and call it a day if(g_pakHelping.size() != lFilenames.size()) //These should be the same { cout << "Error: size of file list: " << g_pakHelping.size() << " differs from size of files to pak: " << lFilenames.size() << endl; continue; } //Create a mini residmap.dat file and stick it into g_pakHelping to compress if we need to //See what IDs are known and unknown map<wstring, u32> mResIDs; map<u32, wstring> mUnknownIDs; for(list<wstring>::iterator i = lFilenames.begin(); i != lFilenames.end(); i++) { u32 id = getKnownResID(*i); if(!id) //The ID mapping is unknown { id = hash(*i); mUnknownIDs[id] = toBackslashes(*i); //Make sure we use backslashes inside a residmap.dat } mResIDs[*i] = id; } //If there are any unknown mappings, make our residmap.dat if(mUnknownIDs.size()) { lFilenames.push_front(TEXT(RESIDMAP_NAME)); //Add this to the front, so it'll decompress first so we'll have all the filenames mResIDs[TEXT(RESIDMAP_NAME)] = RESIDMAP_ID; //Add this to the IDs we'll compress createMiniResidMap(&mUnknownIDs); //And create the file in memory } //Open our output pakfile for writing FILE* f = _wfopen(sArg.c_str(), TEXT("wb")); if(f == NULL) { cout << "Unable to open file " << ws2s(sArg) << " for writing. Skipping..." << endl; continue; } //Add the header blobHeader bh; bh.pakVersion = 0x01; bh.numItems = lFilenames.size(); fwrite(&bh, 1, sizeof(blobHeader), f); //Get the starting file pos for where we (should) be writing objects to size_t offsetPos = sizeof(blobHeader) + (lFilenames.size() * sizeof(resourceHeader)); //Add the table of contents cout << "\rAdding table of contents... " << endl; for(list<wstring>::iterator i = lFilenames.begin(); i != lFilenames.end(); i++) { resourceHeader rH; rH.id = mResIDs[*i]; rH.flags = 0x01; rH.offset = offsetPos; //Offset rH.size = g_pakHelping[*i].dataSz; if(g_pakHelping[*i].bCompressed) rH.size += sizeof(compressedHeader); //Compressed files have a compression header also else rH.flags = 0x00; //Set the flags to uncompressed fwrite(&rH, 1, sizeof(resourceHeader), f); //Write this to the file offsetPos += rH.size; //Add this size to our running tally of where we are } //Add actual resource data cout << "Adding compressed files..." << endl; for(list<wstring>::iterator i = lFilenames.begin(); i != lFilenames.end(); i++) { pakHelper pH = g_pakHelping[*i]; //Write the compressed header only if compressed if(pH.bCompressed) fwrite(&(pH.cH), 1, sizeof(compressedHeader), f); fwrite(pH.data, 1, pH.dataSz, f); //One pass to write this file. Simple enough. //Don't free the memory here, in case there's more than one .pak file with this data in it. } fclose(f); //Done packing this .pak file //Clear memory for(map<wstring, pakHelper>::iterator i = g_pakHelping.begin(); i != g_pakHelping.end(); i++) free(i->second.data); g_pakHelping.clear(); } cout << "Done." << endl; iTicks = GetTickCount() - iTicks; float iSeconds = (float)iTicks / 1000.0; //Get seconds elapsed int iMinutes = iSeconds / 60; iSeconds -= iMinutes * 60; cout << "Time elapsed: " << iMinutes << " min, " << iSeconds << " sec" << endl; return 0; }