New farextract naming logic and a rewritten UTalk decompressor

This commit is contained in:
Fatbag 2013-03-07 16:17:18 -06:00
parent 558726a9f1
commit 66ce473514
11 changed files with 584 additions and 352 deletions

View file

@ -20,9 +20,31 @@
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <time.h>
#include <sys/stat.h>
#include <errno.h>
#include "far.h"
#ifndef read_uint32
#define read_uint32(x) (unsigned)(((x)[0]<<(8*0)) | ((x)[1]<<(8*1)) | ((x)[2]<<(8*2)) | ((x)[3]<<(8*3)))
#endif
#ifdef _WIN32
#define mkdir(path, x) mkdir(path)
#endif
static int mkpath(char * path){
char * p;
for(p = strpbrk(path+1, "/\\"); p; p = strpbrk(p+1, "/\\")){
char c = *p;
int value;
*p = '\0'; value = mkdir(path, 0644); *p = c;
if(value != 0 && errno != EEXIST)
return -1;
}
return 0;
}
enum {
profile_ts1 = 1,
profile_tso,
@ -32,6 +54,122 @@ enum {
profile_ts3
};
typedef struct {
uint32_t GroupID;
const char * Directory;
} GroupMap_t;
typedef struct {
uint32_t TypeID;
const char * Extension;
} TypeMap_t;
typedef struct {
uint8_t Size;
const char * Header;
const char * Extension;
} HeaderMap_t;
static const GroupMap_t GroupMap[] = {
{0x0A3C55C7, "Music/Stations/Horror/"},
{0x0A3C55CE, "Music/Stations/OldWorld/"},
{0x0A3C55D3, "Music/Stations/SciFi/"},
{0x1D6962CF, "SoundData/HitLabUI/"},
{0x1D8A8B4F, "SoundData/HitLabTestSamples/"},
{0x29D9359D, "SoundData/CustomTrks/"},
{0x29DAA4A6, "SoundData/Custom/"},
{0x29DD0888, "SoundData/Multiplayer/"},
{0x69C6C943, "SoundData/tsov2/"},
{0x8A6FCC30, "SoundData/EP5Samps/"},
{0x9DBDBF74, "SoundData/HitLists/"},
{0x9DBDBF89, "SoundData/Samples/"},
{0x9DF26DAD, "Music/Stations/Country/"},
{0x9DF26DAE, "Music/Stations/CountryD/"},
{0x9DF26DB1, "Music/Stations/Latin/"},
{0x9DF26DB3, "Music/Stations/Rap/"},
{0x9DF26DB6, "Music/Stations/Rock/"},
{0xA9C6C89A, "SoundData/Tracks/"},
{0xBD6E5937, "SoundData/HitLabTest/"},
{0xBDF26DB0, "Music/Stations/Disco/"},
{0xC9C6C9B3, "SoundData/HitListsTemp/"},
{0xDDBDBF8C, "SoundData/Stings/"},
{0xDDE8F5C6, "SoundData/EP2/"},
{0xDDF26DA9, "Music/Stations/Beach/"},
{0xDDF26DB4, "Music/Stations/Rave/"},
{0xFDBDBF87, "SoundData/TrackDefs/"},
{0xFDF26DAB, "Music/Stations/Classica/"}
};
static const TypeMap_t TypeMap[] = {
{0, ".dat"},
{1, ".bmp"},
{2, ".tga"},
{5, ".skel"},
{7, ".anim"},
{9, ".mesh"},
{11, ".bnd"},
{12, ".apr"},
{13, ".oft"},
{14, ".png"},
{15, ".po"},
{16, ".col"},
{18, ".hag"},
{20, ".jpg"},
{24, ".png"},
{0x0A8B0E70, ".mad"},
{0x1B6B9806, ".utk"},
{0x1D07EB4B, ".xa"},
{0x1D968538, ".xa"},
{0x2026960B, ".dat"},
{0x3CEC2B47, ".mp3"},
{0x3D968536, ".mp3"},
{0x5D73A611, ".trk"},
{0x7B1ACFCD, ".hls"},
{0x856DDBAC, ".bmp"},
{0x9D796DB4, ".tlo"},
{0x9D96853A, ".wav"},
{0xA3CD96CF, ".tkd"},
{0xBB7051F5, ".wav"}
};
static const HeaderMap_t AudioHeaders[] = {
{2, "XA", ".xa"},
{4, "RIFF", ".wav"},
{4, "UTM0", ".utk"},
{4, "\xFF\xFB\x90\x40", ".mp3"},
{24, "# Generated by UI editor", ".scr"}
};
static const char * groupid_to_dir(uint32_t GroupID){
size_t i;
for(i=0; i<sizeof(GroupMap)/sizeof(GroupMap_t); i++){
if(GroupMap[i].GroupID == GroupID)
return GroupMap[i].Directory;
}
fprintf(stderr, "%sUnrecognized Group ID 0x%08X.\n", "farextract: warning: ", GroupID);
return "./";
}
static const char * typeid_to_ext(uint32_t TypeID){
size_t i;
for(i=0; i<sizeof(TypeMap)/sizeof(TypeMap_t); i++){
if(TypeMap[i].TypeID == TypeID)
return TypeMap[i].Extension;
}
fprintf(stderr, "%sUnrecognized Type ID 0x%08X.\n", "farextract: warning: ", TypeID);
return ".dat";
}
static const char * header_to_ext(const uint8_t * Buffer, size_t Size){
size_t i;
for(i=0; i<sizeof(AudioHeaders)/sizeof(HeaderMap_t); i++){
if(Size >= AudioHeaders[i].Size && !memcmp(Buffer, AudioHeaders[i].Header, AudioHeaders[i].Size))
return AudioHeaders[i].Extension;
}
return ".str";
}
int main(int argc, char *argv[]){
int profile = 0, overwrite = 0;
const char * InFile = "", * OutDirectory;
@ -62,7 +200,7 @@ int main(int argc, char *argv[]){
"\n"
"Report bugs to <X-Fi6@phppoll.org>.\n"
"farextract and libfar are maintained by the Niotso project.\n"
"Home page: <http://www.niotso.org/>");
"Home page: <http://www.niotso.org/>\n");
return 0;
}
@ -141,6 +279,11 @@ int main(int argc, char *argv[]){
return -1;
}
if(chdir(OutDirectory) != 0){
fprintf(stderr, "%sOutput directory '%s' does not exist.", "farextract: error: ", OutDirectory);
return -1;
}
if(ArchiveType != FAR_TYPE_PERSIST){
FAREntryNode * EntryNode;
unsigned file = 0, filescount;
@ -177,28 +320,35 @@ int main(int argc, char *argv[]){
** Extract each entry
*/
for(EntryNode = FARFileInfo->FirstEntry; EntryNode; EntryNode = EntryNode->NextEntry){
char destination[256];
char destbuffer[256], * destination = destbuffer;
file++;
if(EntryNode->Entry.Filename)
sprintf(destination, "%s/%s", OutDirectory, EntryNode->Entry.Filename);
destination = EntryNode->Entry.Filename;
else
sprintf(destination, "%s/%08x-%08x-%08x.dat", OutDirectory,
EntryNode->Entry.TypeID, EntryNode->Entry.GroupID, EntryNode->Entry.FileID);
sprintf(destbuffer, "%s%08x%s", groupid_to_dir(EntryNode->Entry.GroupID),
EntryNode->Entry.FileID, typeid_to_ext(EntryNode->Entry.TypeID));
if(!far_read_entry_data(FARFileInfo, &(EntryNode->Entry), ArchiveData)){
printf(" (%u/%u) Skipped (%s): %s\n", file, filescount,
"entry data is corrupt", EntryNode->Entry.Filename);
if(far_read_entry_data(FARFileInfo, &(EntryNode->Entry), ArchiveData)){
/* Decompression, if any, was successful */
if(!EntryNode->Entry.Filename && EntryNode->Entry.TypeID == 0x2026960B)
sprintf(destbuffer, "%s%08x%s", groupid_to_dir(EntryNode->Entry.GroupID), EntryNode->Entry.FileID,
header_to_ext(EntryNode->Entry.DecompressedData, EntryNode->Entry.DecompressedSize));
}else{
printf(" (%u/%u) Skipped (%s): %s\n", file, filescount, "entry data is corrupt", destination);
continue;
}
/* Decompression, if any, was successful */
if(mkpath(destination) != 0){
fprintf(stderr, "%sCould not create path '%s'.", "farextract: error: ", destination);
return -1;
}
if(!overwrite){
hFile = fopen(destination, "rb");
if(hFile != NULL){
/* File exists */
fclose(hFile);
printf(" (%u/%u) Skipped (%s): %s\n", file, filescount, "could not open", EntryNode->Entry.Filename);
printf(" (%u/%u) Skipped (%s): %s\n", file, filescount, "file exists", destination);
if(EntryNode->Entry.DecompressedData != EntryNode->Entry.CompressedData)
libfar_free(EntryNode->Entry.DecompressedData);
continue;
@ -206,19 +356,15 @@ int main(int argc, char *argv[]){
}
hFile = fopen(destination, "wb");
if(hFile == NULL){
printf(" (%u/%u) Skipped (%s): %s\n", file, filescount, "could not open", EntryNode->Entry.Filename);
printf(" (%u/%u) Skipped (%s): %s\n", file, filescount, "could not open", destination);
if(EntryNode->Entry.DecompressedData != EntryNode->Entry.CompressedData)
libfar_free(EntryNode->Entry.DecompressedData);
continue;
}
if(EntryNode->Entry.Filename)
printf(" (%u/%u) %s (%u bytes)\n", file, filescount,
EntryNode->Entry.Filename, EntryNode->Entry.DecompressedSize);
else
printf(" (%u/%u) %08x-%08x-%08x (%u bytes)\n", file, filescount,
EntryNode->Entry.TypeID, EntryNode->Entry.GroupID, EntryNode->Entry.FileID,
EntryNode->Entry.DecompressedSize);
printf(" (%u/%u) %s (Group ID: 0x%08X, File ID: 0x%08X, Type ID: 0x%08X) (%u bytes)\n", file, filescount,
destination, EntryNode->Entry.GroupID, EntryNode->Entry.FileID, EntryNode->Entry.TypeID,
EntryNode->Entry.DecompressedSize);
fwrite(EntryNode->Entry.DecompressedData, 1, EntryNode->Entry.DecompressedSize, hFile);
fclose(hFile);
@ -232,8 +378,6 @@ int main(int argc, char *argv[]){
}else{
/* Persist file */
PersistFile * PersistInfo;
char destination[256];
sprintf(destination, "%s/%s.out", OutDirectory, InFile);
/****
** Load header information
@ -261,7 +405,7 @@ int main(int argc, char *argv[]){
EndingTime = clock();
if(!overwrite){
hFile = fopen(destination, "rb");
hFile = fopen(InFile, "rb");
if(hFile != NULL){
/* File exists */
fclose(hFile);
@ -270,7 +414,7 @@ int main(int argc, char *argv[]){
return -1;
}
}
hFile = fopen(destination, "wb");
hFile = fopen(InFile, "wb");
if(hFile == NULL){
fprintf(stderr, "%sCould not open.", "farextract: error: ");
libfar_free(PersistInfo->DecompressedData);