675 lines
19 KiB
Plaintext
Executable File
675 lines
19 KiB
Plaintext
Executable File
#include "gcminfo.h"
|
|
#include <unistd.h>
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
int needByteSwap = 0;
|
|
|
|
// these are functions that the user really doesn't need to see,
|
|
// so they're declared here away from the external API. They're
|
|
// used mainly for populating the internal FST data structure.
|
|
// All the user-necessary functions are listed out in gcminfo.h.
|
|
|
|
int gcmPopulateFST(GCM *data, FILE *file);
|
|
void *gcmWhichData(int whichdata, GCM *data);
|
|
int gcmGetData(int whichdata, GCM *data, FILE *file);
|
|
int gcmGetFile(GCM *data, GCM_FSTEntry *entry, int fnum, FILE *file);
|
|
int gcmCheckMagic(FILE *file);
|
|
|
|
// checks the magic number of the DVD file, and sets needByteSwap
|
|
// returns the final value of needByteSwap. Returns -1 if the magic
|
|
// word doesn't match either way and needByteSwap couldn't be set.
|
|
int gcmCheckMagic(FILE *file)
|
|
{
|
|
int magic = 0;
|
|
fseek(file, GCMPieces[GCM_DHEAD_DVDMAGIC].offset, 0);
|
|
fread(&magic, GCMPieces[GCM_DHEAD_DVDMAGIC].size, 1, file);
|
|
|
|
if ( magic == GCNDVD_MAGICNUM ) {
|
|
needByteSwap = 0;
|
|
} else {
|
|
magic = (((magic&0x000000FF)<<24)+((magic&0x0000FF00)<<8)+
|
|
((magic&0x00FF0000)>>8)+((magic&0xFF000000)>>24));
|
|
if ( magic == GCNDVD_MAGICNUM ) {
|
|
needByteSwap = 1;
|
|
}
|
|
else needByteSwap = -1;
|
|
}
|
|
|
|
return needByteSwap;
|
|
}
|
|
|
|
// swap from big to little endian (only needed on intel machines)
|
|
// only works if needByteSwap is > -1
|
|
int byteSwap (int nLongNumber)
|
|
{
|
|
if ( needByteSwap ) return (((nLongNumber&0x000000FF)<<24)+((nLongNumber&0x0000FF00)<<8)+
|
|
((nLongNumber&0x00FF0000)>>8)+((nLongNumber&0xFF000000)>>24));
|
|
else if ( needByteSwap == -1 ) return 0; // refuse to work
|
|
}
|
|
|
|
// return the type (0=file 1=directory) for the given FST entry
|
|
int gcmGetFSTEntryType(GCM_FSTEntry *entry)
|
|
{
|
|
return entry->fname_offset >> 24;
|
|
}
|
|
|
|
// uses one of the GCM_* definitions to return a pointer to
|
|
// the appropriate point of the GCM struct to read data into.
|
|
void *gcmWhichData(int whichdata, GCM *data)
|
|
{
|
|
void *toPrint = NULL;
|
|
|
|
switch (whichdata) {
|
|
case GCM_DHEAD_GAMECODE:
|
|
toPrint = &data->head.gamecode;
|
|
break;
|
|
case GCM_DHEAD_GAMEMAKER:
|
|
toPrint = &data->head.gamemaker;
|
|
break;
|
|
case GCM_DHEAD_DVDMAGIC:
|
|
toPrint = &data->head.magicword;
|
|
break;
|
|
case GCM_DHEAD_GAMENAME:
|
|
toPrint = &data->head.gamename;
|
|
break;
|
|
case GCM_DHEAD_DEBUGMON:
|
|
toPrint = &data->head.debugmon;
|
|
break;
|
|
case GCM_DHEAD_DEBUGMONADDR:
|
|
toPrint = &data->head.debugmonaddr;
|
|
break;
|
|
case GCM_DHEAD_BOOTFILE:
|
|
toPrint = &data->head.bootfile;
|
|
break;
|
|
case GCM_DHEAD_FST:
|
|
toPrint = &data->fst.offset;
|
|
break;
|
|
case GCM_DHEAD_FSTSIZE:
|
|
toPrint = &data->fst.size;
|
|
break;
|
|
case GCM_DHEAD_FSTMAXSIZE:
|
|
toPrint = &data->fst.maxsize;
|
|
break;
|
|
case GCM_DHEAD_USERPOS:
|
|
toPrint = &data->head.userpos;
|
|
break;
|
|
case GCM_DHEAD_USERLEN:
|
|
toPrint = &data->head.userlen;
|
|
break;
|
|
case GCM_DHEADINF_DEBUGMONSIZE:
|
|
toPrint = &data->headInf.debugmonsize;
|
|
break;
|
|
case GCM_DHEADINF_SIMMEMSIZE:
|
|
toPrint = &data->headInf.simmemsize;
|
|
break;
|
|
case GCM_DHEADINF_ARGOFFSET:
|
|
toPrint = &data->headInf.simmemsize;
|
|
break;
|
|
case GCM_DHEADINF_DEBUGFLAG:
|
|
toPrint = &data->headInf.debugflag;
|
|
break;
|
|
case GCM_DHEADINF_TRACKLOCATION:
|
|
toPrint = &data->headInf.tracklocation;
|
|
break;
|
|
case GCM_DHEADINF_TRACKSIZE:
|
|
toPrint = &data->headInf.tracksize;
|
|
break;
|
|
case GCM_DHEADINF_COUNTRYCODE:
|
|
toPrint = &data->headInf.countrycode;
|
|
break;
|
|
case GCM_APPLOADER_VERSION:
|
|
toPrint = &data->apploader.datever;
|
|
break;
|
|
case GCM_APPLOADER_ENTRY:
|
|
toPrint = &data->apploader.entry;
|
|
break;
|
|
case GCM_APPLOADER_SIZE:
|
|
toPrint = &data->apploader.size;
|
|
break;
|
|
case GCM_APPLOADER_TRAILERSIZE:
|
|
toPrint = &data->apploader.trailersize;
|
|
break;
|
|
case GCM_APPLOADER_BINARY:
|
|
toPrint = &data->apploader.apploader;
|
|
break;
|
|
case GCM_FST_ROOT:
|
|
toPrint = (void *)"Not implemented yet";
|
|
break;
|
|
case GCM_FST_STRINGTABLEOFF:
|
|
toPrint = &data->fst.stringtable_offset;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
return toPrint;
|
|
}
|
|
|
|
// print the info in data as indicated by the GCM_*
|
|
// passed to whichdata
|
|
int gcmPrintData(int whichdata, GCM *data)
|
|
{
|
|
void *ptr;
|
|
if ( GCMPieces[whichdata].type == 1) {
|
|
char *theData = (char *)gcmWhichData(whichdata,data);
|
|
printf("%-38s:\t %s\n", GCMPieces[whichdata].name, theData);
|
|
}
|
|
else {
|
|
int *theData = (int *)gcmWhichData(whichdata,data);
|
|
if ( theData ) {
|
|
printf("%-38s:\t %x\n", GCMPieces[whichdata].name, *theData);
|
|
}
|
|
else {
|
|
printf("%-38s:\t refusing to print NULL pointer...\n",
|
|
"(untested function/invalid data)"); // this means you're using a feature that doesn't work (yet)
|
|
}
|
|
}
|
|
}
|
|
|
|
// print the information for a given file in data (by entry num)
|
|
int gcmPrintFile(GCM *data, int fnum)
|
|
{
|
|
GCM_FSTEntry f;
|
|
char *fname = NULL;
|
|
|
|
// printf("DEBUG : retrieving file data for number %d\n", fnum);
|
|
f = data->fst.entries.at(fnum);
|
|
|
|
// see gcmPrint for how this is laid out...
|
|
if ( fnum >= data->fst.entries.size() )
|
|
fname = "(out_of_bounds string)" ;
|
|
else
|
|
fname = (char *)data->fst.stringtable.at(fnum)->c_str();
|
|
|
|
printf("%-20s%-6d%-6x%-12x%-20d\n",
|
|
fname,
|
|
fnum,
|
|
gcmGetFSTEntryType(&f),
|
|
f.file_offset,
|
|
f.file_length);
|
|
}
|
|
|
|
// reads data into dest and returns the number of bytes read
|
|
// (only used internally by the other gcm functions, this should
|
|
// be transparent outside the API guts)
|
|
int gcmGetData(int whichdata, GCM *data, FILE *file)
|
|
{
|
|
int offset = 0;
|
|
void *dest = gcmWhichData(whichdata, data);
|
|
if ( whichdata > GCM_MAX_PIECES || dest == NULL || file == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
switch ( GCMPieces[whichdata].area ) {
|
|
case 0:
|
|
offset = GCMPieces[whichdata].offset;
|
|
break;
|
|
case 1:
|
|
offset = GCMPieces[GCM_DHEAD].offset + GCMPieces[whichdata].offset;
|
|
break;
|
|
case 2:
|
|
offset = GCMPieces[GCM_DHEADINF].offset + GCMPieces[whichdata].offset;
|
|
break;
|
|
case 3:
|
|
offset = GCMPieces[GCM_APPLOADER].offset + GCMPieces[whichdata].offset;
|
|
break;
|
|
case 4:
|
|
offset = data->fst.offset + GCMPieces[whichdata].offset;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
fseek(file, offset, 0);
|
|
if ( GCMPieces[whichdata].type == 1 ) {
|
|
fgets((char *)dest, GCMPieces[whichdata].size, file) == NULL;
|
|
}
|
|
else {
|
|
fread(dest, 1, GCMPieces[whichdata].size, file);
|
|
int *tmp = (int *)dest;
|
|
*tmp = byteSwap(*tmp);
|
|
}
|
|
rewind(file);
|
|
return GCMPieces[whichdata].size;
|
|
}
|
|
|
|
// get the file at fnum from file for the GCM data and store it
|
|
// in entry
|
|
int gcmGetFile(GCM *data, GCM_FSTEntry *entry, int fnum, FILE *file)
|
|
{
|
|
if ( fnum > -1 && file != NULL ) {
|
|
rewind(file);
|
|
fseek(file, data->fst.offset + (12 * fnum), 0);
|
|
|
|
if ( !feof(file) ) {
|
|
fread(&entry->fname_offset, 4, 1, file);
|
|
entry->fname_offset = byteSwap(entry->fname_offset);
|
|
fread(&entry->file_offset, 4, 1, file);
|
|
entry->file_offset = byteSwap(entry->file_offset);
|
|
fread(&entry->file_length, 4, 1, file);
|
|
entry->file_length = byteSwap(entry->file_length);
|
|
data->fst.entries.push_back(*entry);
|
|
}
|
|
}
|
|
else return -1;
|
|
}
|
|
|
|
// populate the FST of the given GCM from file
|
|
int gcmPopulateFST(GCM *data, FILE *file)
|
|
{
|
|
GCM_FSTEntry *newEntry;
|
|
GCM_FSTEntry root;
|
|
int i = 0;
|
|
int j = 0;
|
|
int oldpos = 0;
|
|
int maxentries = 0;
|
|
char *tmpbuff = new char[GCNDVD_FNAME_LENGTH];
|
|
std::string * strdest = new std::string("");
|
|
char tmp = 0xFF;
|
|
int f = 0;
|
|
int *appSize = NULL;
|
|
|
|
if ( !tmpbuff ) {
|
|
return -1;
|
|
}
|
|
|
|
fseek(file, data->fst.offset, 0);
|
|
|
|
// now just loop until there are no more entries...
|
|
gcmGetFile(data, &root, 0,file);
|
|
root = data->fst.entries.front();
|
|
// this is where we inject the filesystem entries for bootloader.bin, apploader.bin,
|
|
// and game.dol ... these aren't technically part of the filesystem, but we
|
|
// do this so they can be viewed/extracted like other files.
|
|
/*
|
|
appSize = (int *)gcmWhichData(GCM_APPLOADER_SIZE, data);
|
|
newEntry = new GCM_FSTEntry;
|
|
newEntry->fname_offset = 0;
|
|
newEntry->file_offset = GCM_APPLOADER_CODE_LOC;
|
|
newEntry->file_length = *appSize;
|
|
newEntry->stringtable_off = 1;
|
|
data->fst.entries.push_back(*newEntry);
|
|
delete newEntry;*/
|
|
for ( int i = 1; i < root.file_length ; i++) {
|
|
newEntry = new GCM_FSTEntry;
|
|
gcmGetFile(data, newEntry, i, file);
|
|
delete newEntry;
|
|
}
|
|
|
|
|
|
// we're past the FST proper; now the string table...
|
|
data->fst.stringtable_offset = data->fst.offset + (root.file_length * 12);
|
|
fseek(file, data->fst.stringtable_offset, 0);
|
|
//printf("FST string table offset calculated to 0x\%x...\n", data->fst.stringtable_offset);
|
|
data->fst.stringtable.clear();
|
|
data->fst.stringtable.push_back(&std::string("/\0"));
|
|
//data->fst.stringtable.push_back(&std::string("apploader.bin\0"));
|
|
memset(tmpbuff, GCNDVD_FNAME_LENGTH, 0x00);
|
|
while ( ftell(file) < (data->fst.offset + data->fst.size) && !feof(file)) {
|
|
i = 0;
|
|
while ( 1 ) {
|
|
tmp = fgetc(file);
|
|
if ( tmp == 0x00 ) {
|
|
break;
|
|
}
|
|
tmpbuff[i] = tmp;
|
|
i++;
|
|
}
|
|
tmpbuff[i] = '\0';
|
|
strdest->clear();
|
|
strdest->reserve(i);
|
|
strdest->assign(tmpbuff);
|
|
data->fst.stringtable.push_back(strdest);
|
|
// printf("DEBUG : FILENAME WAS %s ... added %s\n", strdest,
|
|
// data->fst.stringtable.at(f+1)->c_str());
|
|
memset(tmpbuff, GCNDVD_FNAME_LENGTH, 0x00);
|
|
strdest = new std::string("");
|
|
f++;
|
|
}
|
|
delete tmpbuff;
|
|
return 0;
|
|
}
|
|
|
|
// called inside of gcmInit to make sure everything inside is sane
|
|
// (only needed internally)
|
|
int gcmZeroOut(GCM *data)
|
|
{
|
|
data->apploader.entry = 0;
|
|
data->apploader.size = 0;
|
|
data->apploader.trailersize = 0;
|
|
data->fst.maxsize = 0;
|
|
data->fst.offset = 0;
|
|
data->fst.size = 0;
|
|
data->head.bootfile = 0;
|
|
data->head.debugmon = 0;
|
|
data->head.debugmonaddr = 0;
|
|
data->head.gamecode = 0;
|
|
data->head.gamemaker = 0;
|
|
data->head.magicword = 0;
|
|
data->head.userlen = 0;
|
|
data->head.userpos = 0;
|
|
data->headInf.argoff = 0;
|
|
data->headInf.countrycode = 0;
|
|
data->headInf.debugflag = 0;
|
|
data->headInf.debugmonsize = 0;
|
|
data->headInf.simmemsize = 0;
|
|
data->headInf.tracklocation = 0;
|
|
data->headInf.tracksize = 0;
|
|
}
|
|
|
|
// reads all info from the GCM in gcmname and puts it in data
|
|
int gcmInit(char *gcmname, GCM *data)
|
|
{
|
|
FILE *gcm = fopen(gcmname, "r+b");
|
|
int i = 0;
|
|
|
|
if (!gcm) {
|
|
return -1;
|
|
}
|
|
|
|
gcmZeroOut(data);
|
|
|
|
switch ( gcmCheckMagic(gcm) ) {
|
|
case 0:
|
|
needByteSwap = 1;
|
|
break;
|
|
case 1:
|
|
break;
|
|
case -1:
|
|
//printf("DEBUG: DVD Magic number on GCM image was incorrect or couldn't be understood!\n");
|
|
//printf("DEBUG: Aborting in GCMInit! (filename %s)\n", gcmname);
|
|
fclose(gcm);
|
|
return -1;
|
|
}
|
|
|
|
for ( i = 3; i < GCM_MAX_PIECES ; i++) {
|
|
gcmGetData(i,data, gcm);
|
|
}
|
|
|
|
gcmPopulateFST(data, gcm);
|
|
|
|
fclose(gcm);
|
|
|
|
data->fname = new char[strlen(gcmname)+1];
|
|
if ( !data->fname ) {
|
|
printf("couldn't store GCM filename for later retrieval. Aborting.\n");
|
|
gcmFreeStringTable(data);
|
|
exit(-1);
|
|
}
|
|
else strcpy(data->fname, gcmname);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// print all information about the GCM in a columnar format,
|
|
// including the FST.
|
|
int gcmPrint(GCM *data)
|
|
{
|
|
int ml = 0;
|
|
int i = 0;
|
|
|
|
for (i = 3; i < GCM_MAX_PIECES; i++ ) {
|
|
gcmPrintData(i,data);
|
|
}
|
|
|
|
GCM_FSTEntry root = data->fst.entries.front();
|
|
|
|
printf("%-38s:\t%d\n", "Total files in the FST", root.file_length);
|
|
printf("%-38s:\t%d\n", "Total entries in the FST string table", data->fst.stringtable.size());
|
|
return 0;
|
|
}
|
|
|
|
int gcmPrintFST(GCM *data)
|
|
{
|
|
int ml = 0;
|
|
int i = 0;
|
|
char *tbuff = new char[GCNDVD_FNAME_LENGTH];
|
|
if (!tbuff) {
|
|
printf("Couldn't get temporary buffer for full path printing. Aborting.\n");
|
|
return -1;
|
|
} else {
|
|
memset(tbuff, 0x00, GCNDVD_FNAME_LENGTH);
|
|
}
|
|
|
|
GCM_FSTEntry root = data->fst.entries.front();
|
|
printf("%-38s:\t%d\n", "Total files in the FST", root.file_length);
|
|
printf("%-38s:\t%d\n", "Total entries in the FST string table", data->fst.stringtable.size());
|
|
printf("\n\n\n%30s\n", "(FST CONTENTS)");
|
|
printf("%-16s %-6s %-6s %s %-12s\n",
|
|
"(name)", "(fnum)", "(type)", "(offset)", "(length)");
|
|
for (i = 0; i < data->fst.entries.size() - 1 ; i++) {
|
|
//printf("DEBUG: retrieving file data for number %d\n", i);
|
|
gcmPrintFile(data, i);
|
|
}
|
|
|
|
printf("\n\t\t\t(PRINTING FILES WITH FULL PATH)\n");
|
|
for (i = 0; i < data->fst.entries.size() - 1; i++ ) {
|
|
printf("File %d:", i);
|
|
gcmGetFullFileName(data, i, tbuff);
|
|
printf(" %s\n", tbuff);
|
|
memset(tbuff, 0x00, GCNDVD_FNAME_LENGTH);
|
|
}
|
|
|
|
delete tbuff;
|
|
return 0;
|
|
}
|
|
|
|
// frees memory used by the string table. MUST be called before zeroing
|
|
// the gcm for new reading (gcmInit) or quitting the application.
|
|
// This should be accompanied by clearing the entries vector, though the
|
|
// destructor in that member should take care of cleanup for you.
|
|
// Thou shalt not leak memory!
|
|
|
|
int gcmFreeStringTable(GCM *data)
|
|
{
|
|
std::string *string = NULL;
|
|
int i = 0;
|
|
|
|
//printf("freeing memory used by FST string table... ");
|
|
|
|
for (i = 0; i < (data->fst.stringtable.size()) ; i++ ) {
|
|
string = (std::string *)data->fst.stringtable.at(i);
|
|
// we create the "/" entry for 0 manually, and it's not alloc'ed
|
|
// so don't try freeing it!
|
|
if ( string != NULL && i > 0 ) {
|
|
delete string;
|
|
}
|
|
string = NULL;
|
|
}
|
|
data->fst.stringtable.clear();
|
|
delete data->fname;
|
|
|
|
//printf(" ...done.\n");
|
|
}
|
|
|
|
GCM_FSTEntry gcmGetFileByFnum(GCM *data, int fnum)
|
|
{
|
|
return data->fst.entries.at(fnum);
|
|
}
|
|
|
|
int gcmGetFileByOffset(GCM *data, int offset)
|
|
{
|
|
GCM_FSTEntry curr;
|
|
bool done = false;
|
|
|
|
for (int i = 0; i < data->fst.entries.size() -1; i++) {
|
|
curr = data->fst.entries.at(i);
|
|
if (curr.file_offset == offset) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// takes in a full pathname and retrieves the fnum
|
|
// for the appropriate file in the FST
|
|
unsigned int gcmGetFileByName(GCM *data, char *name)
|
|
{
|
|
GCM_FSTEntry curr;
|
|
bool done = false;
|
|
int nextStart = 0;
|
|
int i = 0;
|
|
char *tmp2 = (char *) new char[GCNDVD_FNAME_LENGTH];
|
|
if ( !tmp2 ) {
|
|
fprintf(stderr, "Failed to allocate temporary string storage\n");
|
|
return -1;
|
|
}
|
|
for ( i = 0; i < data->fst.stringtable.size() ; i++ ) {
|
|
gcmGetFullFileName(data, i, tmp2);
|
|
//printf("DEBUG : comparing filename %s to original %s...\n", name, tmp2);
|
|
if ( !strcmp(name, tmp2) ) {
|
|
delete tmp2;
|
|
//printf("DEBUG : found filename %s at number %d", name, i);
|
|
return i;
|
|
}
|
|
}
|
|
delete tmp2;
|
|
return -1;
|
|
}
|
|
|
|
// extracts a file into outfile from the GCM given the fnum.
|
|
// FIXME : allow the user to specify a different output filename?
|
|
unsigned int gcmExtractFile(GCM *data, int fnum, char *outfname)
|
|
{
|
|
FILE *out = NULL;
|
|
FILE *gcm = NULL;
|
|
int pos = 0;
|
|
int endpos = 0;
|
|
int b = 0;
|
|
GCM_FSTEntry curr;
|
|
std::string execstring = "";
|
|
char *outfile = NULL;
|
|
std::string tmpStr = "";
|
|
|
|
// check that the fnum is in range
|
|
|
|
if ( fnum < data->fst.entries.size() - 1) {
|
|
curr = data->fst.entries.at(fnum);
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
|
|
// if it's a file, open the outfile, open the GCM, and perform the copy - clean up and exit
|
|
if ( gcmGetFSTEntryType(&curr) == 0 ) {
|
|
if ( outfname ) {
|
|
outfile = outfname;
|
|
tmpStr = "";
|
|
} else {
|
|
outfile = new char[GCNDVD_FNAME_LENGTH];
|
|
if ( !outfile ) return -1;
|
|
else gcmGetFullFileName(data, fnum, outfile);
|
|
tmpStr = ".";
|
|
}
|
|
tmpStr.append(outfile);
|
|
gcm = fopen(data->fname, "rb");
|
|
out = fopen(tmpStr.c_str(), "wb");
|
|
if ( !gcm || !out ) {
|
|
gcm ? fclose(gcm) : printf("Couldn't read from gcm file\n");
|
|
out ? fclose(out) : printf("Couldn't write to output file\n");
|
|
return -1;
|
|
}
|
|
fseek(gcm, curr.file_offset, 0);
|
|
endpos = curr.file_offset + curr.file_length;
|
|
if ( ftell(gcm) != curr.file_offset || !out || !gcm) {
|
|
delete outfile;
|
|
fclose(gcm);
|
|
fclose(out);
|
|
return -1;
|
|
}
|
|
|
|
while ( !feof(gcm) && ftell(gcm) <= endpos) {
|
|
fread(&b, 1, 1, gcm);
|
|
if ( needByteSwap == 1 ) b = byteSwap(b); // swap the bytes for the host
|
|
fwrite(&b, 1, 1, out);
|
|
}
|
|
|
|
if ( outfile && !outfname )delete outfile;
|
|
fclose(gcm);
|
|
fclose(out);
|
|
printf("exploded %s\n", tmpStr.c_str());
|
|
return 0;
|
|
}
|
|
// if it's a dir, construct the mkdir command, do it, clean up and exit.
|
|
else {
|
|
if ( outfname )
|
|
outfile = outfname;
|
|
else {
|
|
outfile = new char[GCNDVD_FNAME_LENGTH];
|
|
if (!outfile ) {
|
|
return -1;
|
|
}
|
|
}
|
|
gcmGetFullFileName(data, fnum, outfile);
|
|
if ( !outfname )
|
|
execstring.append("mkdir -p .");
|
|
else
|
|
execstring.append("mkdir -p ");
|
|
execstring.append(outfile);
|
|
int retcode = system(execstring.c_str());
|
|
if ( outfile && !outfname ) delete outfile;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
// explodes the FST onto the host filesystem
|
|
int gcmExplode(GCM *data)
|
|
{
|
|
for ( int i = 0; i <= (data->fst.entries.size() - 1) ; i++ ) {
|
|
if ( gcmExtractFile(data, i, NULL) < 0 ) return -1;
|
|
}
|
|
|
|
}
|
|
|
|
// retrieves the full filename for fnum with full path
|
|
int gcmGetFullFileName(GCM *data, int fnum, char *dest)
|
|
{
|
|
bool finished = false;
|
|
GCM_FSTEntry curr;
|
|
std::vector<int> fnames;
|
|
int fn = fnum;
|
|
int depth = 0;
|
|
std::string work = "";
|
|
|
|
curr = data->fst.entries.at(fn);
|
|
|
|
while (!finished) {
|
|
depth++;
|
|
if ( depth > data->fst.entries.size() - 1) {
|
|
printf("went too far. aborting.\n");
|
|
exit(-1);
|
|
}
|
|
if ( fn == 0 ) {
|
|
fnames.push_back(0);
|
|
finished = true;
|
|
break;
|
|
}
|
|
if ( gcmGetFSTEntryType(&curr) == 0 ) {
|
|
// file
|
|
fnames.push_back(fn);
|
|
while (gcmGetFSTEntryType(&curr) == 0) {
|
|
// search up for the parent directory....
|
|
curr = data->fst.entries.at(fn - 1);
|
|
fn -= 1;
|
|
}
|
|
}
|
|
else if ( gcmGetFSTEntryType(&curr) == 1) {
|
|
// directory
|
|
fnames.push_back(fn);
|
|
fn = curr.file_offset;
|
|
curr = data->fst.entries.at(fn);
|
|
}
|
|
}
|
|
|
|
if ( fnum != 0 ) {
|
|
for (int i = fnames.size() -1 ; i >= 0; i--) {
|
|
if ( i < fnames.size()-1 ) {
|
|
work.append("/");
|
|
}
|
|
if ( fnames.at(i) != 0 ) work.append(data->fst.stringtable.at(fnames.at(i))->c_str());
|
|
}
|
|
} else work = "/";
|
|
strcpy(dest, work.c_str());
|
|
return 0;
|
|
}
|