mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-07-16 02:56:44 -04:00
New farextract naming logic and a rewritten UTalk decompressor
This commit is contained in:
parent
558726a9f1
commit
66ce473514
11 changed files with 584 additions and 352 deletions
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue