From 6eacfc3cb5923327d19a7c9fe568cf39a97af2be Mon Sep 17 00:00:00 2001 From: Youness Alaoui Date: Sun, 9 Jan 2011 06:51:46 -0500 Subject: [PATCH] Dos2unix all the files, no other changes than that --- configscan.c | 664 +++++------ configscan.h | 26 +- general.c | 428 +++---- general.h | 174 +-- globdefs.c | 96 +- main.c | 2166 +++++++++++++++++----------------- rcodump.c | 1296 ++++++++++----------- rcodump.h | 48 +- rcofile.h | 514 ++++----- rcomain.c | 832 ++++++------- rcomain.h | 1178 +++++++++---------- rcoreader.c | 2166 +++++++++++++++++----------------- rcowriter.c | 2098 ++++++++++++++++----------------- rlzpack.c | 1090 ++++++++--------- rlzpack.h | 8 +- strfuncs.h | 14 +- vaghandler.c | 934 +++++++-------- vaghandler.h | 6 +- vsmx.c | 3146 +++++++++++++++++++++++++------------------------- vsmx.h | 114 +- xml.h | 74 +- xmlread.c | 3016 +++++++++++++++++++++++------------------------ xmlwrite.c | 1094 +++++++++--------- 23 files changed, 10591 insertions(+), 10591 deletions(-) diff --git a/configscan.c b/configscan.c index 0328635..383a67c 100644 --- a/configscan.c +++ b/configscan.c @@ -1,332 +1,332 @@ - - -#include -#include -#include -#include "general.h" -#include "rcomain.h" -#include "xml.h" - - -#define INI_LINE_BUF_LEN 2048 - -char* configDir = NULL; - -int get_ini_line(FILE* fp, char* buf, char** out1, char** out2); - - -// always try to load the INI files from the same dir as the executable -#ifdef WIN32 -#include -#else -#include -#endif -FILE* fopen_local(char* fn, char* mode) { - char path[512]; - int bytes = 0; - char *p = NULL; - - - if(configDir) { - strcpy(path, configDir); - if(path[strlen(path)-1] != '/' && path[strlen(path)-1] != '\\') { - char sepAdd[2] = {DIR_SEPARATOR, '\0'}; - strcat(path, sepAdd); - } - strcat(path, fn); - } else { - // solution to get current path from http://stackoverflow.com/questions/143174/c-c-how-to-obtain-the-full-path-of-current-directory -#ifdef WIN32 - bytes = GetModuleFileName(NULL, path, 512); - if(bytes) p = strrchr(path, '\\'); -#else - char szTmp[32]; - sprintf(szTmp, "/proc/%d/exe", getpid()); - bytes = readlink(szTmp, path, 512); - if(bytes > 511) bytes = 511; - if(bytes >= 0) path[bytes] = 0; - else bytes = 0; - if(bytes) p = strrchr(path, '/'); -#endif - - if(p) strcpy(p+1, fn); - else strcpy(path, fn); - } - return fopen(path, mode); -} - - -void configLoadTagmap(void) { - FILE* fp = fopen_local("tagmap.ini", "r"); - if(fp) { - char buf[INI_LINE_BUF_LEN]; - char *key, *val; - int type; - int id = -1, id2 = -1; - while((type = get_ini_line(fp, buf, &key, &val))) { - if(type == 1) { - id++; - RCOXML_TABLE_TAGS = (RcoTagMap)realloc(RCOXML_TABLE_TAGS, sizeof(*RCOXML_TABLE_TAGS) * (id+2)); - RCOXML_TABLE_TAGS[id][0][0] = 0; - id2 = -1; - RCOXML_TABLE_NAMES = (RcoTableMap)realloc(RCOXML_TABLE_NAMES, sizeof(*RCOXML_TABLE_NAMES) * (id+1)); - strcpy(RCOXML_TABLE_NAMES[id], key); - } else { - if(id==-1) continue; - id2++; - if(id2 >= RCO_TAGMAP_SIZE-2) { - error("Too many types."); - exit(1); - } - strcpy(RCOXML_TABLE_TAGS[id][id2], key); - RCOXML_TABLE_TAGS[id][id2+1][0] = 0; - } - } - fclose(fp); - RCOXML_TABLE_TAGS_NUM = id+1; - } else { - error("Could not open map file."); - exit(1); - } -} -void configLoadMiscmap(void) { - FILE* fp = fopen_local("miscmap.ini", "r"); - if(fp) { - char buf[INI_LINE_BUF_LEN]; - char *key, *val; - int type; - int id = -1; - RcoTableMap* map = NULL; - while((type = get_ini_line(fp, buf, &key, &val))) { - if(type == 1) { - if(!strcasecmp(key, "compression")) { - map = &RCOXML_TABLE_DATA_COMPRESSION; - } else if(!strcasecmp(key, "languages")) { - map = &RCOXML_TABLE_TEXT_LANG; - } else if(!strcasecmp(key, "textformats")) { - map = &RCOXML_TABLE_TEXT_FMT; - } else if(!strcasecmp(key, "imageformats")) { - map = &RCOXML_TABLE_IMG_FMT; - } else if(!strcasecmp(key, "soundformats")) { - map = &RCOXML_TABLE_SOUND_FMT; - } else if(!strcasecmp(key, "modelformats")) { - map = &RCOXML_TABLE_MODEL_FMT; - } else { - error("Unknown key %s", key); - exit(1); - } - *map = (RcoTableMap)malloc(sizeof(**map)); - (*map)[0][0] = 0; - id = -1; - } else { - if(!map) continue; - id++; - (*map) = (RcoTableMap)realloc(*map, sizeof(**map) * (id+2)); - strcpy((*map)[id], key); - (*map)[id+1][0] = 0; - } - } - fclose(fp); - #define CONFIG_LOAD_MISC_CHECK(v, s) if(!v) { error("Couldn't load " s " constants from config file!"); exit(1); } - CONFIG_LOAD_MISC_CHECK(RCOXML_TABLE_DATA_COMPRESSION, "compression"); - CONFIG_LOAD_MISC_CHECK(RCOXML_TABLE_TEXT_LANG, "language"); - CONFIG_LOAD_MISC_CHECK(RCOXML_TABLE_TEXT_FMT, "textformat"); - CONFIG_LOAD_MISC_CHECK(RCOXML_TABLE_IMG_FMT, "imageformat"); - CONFIG_LOAD_MISC_CHECK(RCOXML_TABLE_SOUND_FMT, "soundformat"); - CONFIG_LOAD_MISC_CHECK(RCOXML_TABLE_MODEL_FMT, "modelformat"); - } else { - error("Could not open map file."); - exit(1); - } -} -void configLoadObjmap(Bool ps3) { - char from[30] = "objattribdef-psp.ini"; - if(ps3) strcpy(from, "objattribdef-ps3.ini"); - FILE* fp = fopen_local(from, "r"); - if(fp) { - char buf[INI_LINE_BUF_LEN]; - char *key, *val; - int type; - int id = -1, id2 = -1, lenAdd=0; - while((type = get_ini_line(fp, buf, &key, &val))) { - if(type == 1) { - id++; - RCO_OBJ_EXTRA_LEN = (int*)realloc(RCO_OBJ_EXTRA_LEN, sizeof(int) * (id+1)); - //RCO_OBJ_EXTRA_LEN[id] = id2 + lenAdd; - RCO_OBJ_EXTRA_LEN[id] = 0; - RCO_OBJ_EXTRA_NAMES = (RcoObjMap)realloc(RCO_OBJ_EXTRA_NAMES, sizeof(*RCO_OBJ_EXTRA_NAMES) * (id+1)); - RCO_OBJ_EXTRA_NAMES[id][0][0] = 0; - RCO_OBJ_EXTRA_TYPES = (RcoObjTypes)realloc(RCO_OBJ_EXTRA_TYPES, sizeof(*RCO_OBJ_EXTRA_TYPES) * (id+1)); - RCO_OBJ_EXTRA_TYPES[id][0] = 0; - id2 = -1; - lenAdd = 0; - } else { - id2++; - RCO_OBJ_EXTRA_LEN[id]++; - if(id2 >= RCO_OBJMAP_SIZE-1) { - error("Too many types."); - exit(1); - } - strcpy(RCO_OBJ_EXTRA_NAMES[id][id2], key); - if(!strcasecmp(val, "int")) - RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_INT; - else if(!strcasecmp(val, "float")) - RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_FLOAT; - else if(!strcasecmp(val, "ref")) - RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_REF; - else if(!strcasecmp(val, "event")) - RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_EVENT; - else if(!strcasecmp(val, "image")) - RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_IMG; - else if(!strcasecmp(val, "object")) - RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_OBJ; - else if(!strcasecmp(val, "text")) - RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_TEXT; - else if(!strcasecmp(val, "model")) - RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_MODEL; - else if(!strcasecmp(val, "font")) - RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_FONT; - else if(!strcasecmp(val, "unk")) - RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_UNK; - else { - error("Unknown type '%s'", val); - exit(1); - } - if(RCO_OBJ_IS_REF(id, id2)) RCO_OBJ_EXTRA_LEN[id]++; - } - } - //RCO_OBJ_EXTRA_LEN[id+1] = id2 + lenAdd; - fclose(fp); - RCO_OBJ_EXTRA_LEN_NUM = id+1; - } else { - error("Could not open map file."); - exit(1); - } -} - -void configLoadAnimmap(Bool ps3) { - char from[30] = "animattribdef-psp.ini"; - if(ps3) strcpy(from, "animattribdef-ps3.ini"); - FILE* fp = fopen_local(from, "r"); - if(fp) { - char buf[INI_LINE_BUF_LEN]; - char *key, *val; - int type; - int id = -1, id2 = -1, lenAdd=0; - while((type = get_ini_line(fp, buf, &key, &val))) { - if(type == 1) { - id++; - RCO_ANIM_EXTRA_LEN = (int*)realloc(RCO_ANIM_EXTRA_LEN, sizeof(int) * (id+1)); - RCO_ANIM_EXTRA_LEN[id] = 0; - RCO_ANIM_EXTRA_NAMES = (RcoObjMap)realloc(RCO_ANIM_EXTRA_NAMES, sizeof(*RCO_ANIM_EXTRA_NAMES) * (id+1)); - RCO_ANIM_EXTRA_NAMES[id][0][0] = 0; - RCO_ANIM_EXTRA_TYPES = (RcoObjTypes)realloc(RCO_ANIM_EXTRA_TYPES, sizeof(*RCO_ANIM_EXTRA_TYPES) * (id+1)); - RCO_ANIM_EXTRA_TYPES[id][0] = 0; - id2 = -1; - lenAdd = 0; - } else { - id2++; - RCO_ANIM_EXTRA_LEN[id]++; - if(id2 >= RCO_OBJMAP_SIZE-1) { - error("Too many types."); - exit(1); - } - strcpy(RCO_ANIM_EXTRA_NAMES[id][id2], key); - if(!strcasecmp(val, "int")) - RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_INT; - else if(!strcasecmp(val, "float")) - RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_FLOAT; - else if(!strcasecmp(val, "ref")) - RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_REF; - else if(!strcasecmp(val, "event")) - RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_EVENT; - else if(!strcasecmp(val, "image")) - RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_IMG; - else if(!strcasecmp(val, "object")) - RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_OBJ; - else if(!strcasecmp(val, "text")) - RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_TEXT; - else if(!strcasecmp(val, "model")) - RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_MODEL; - else if(!strcasecmp(val, "font")) - RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_FONT; - else if(!strcasecmp(val, "unk")) - RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_UNK; - else { - error("Unknown type '%s'", val); - exit(1); - } - if(RCO_ANIM_IS_REF(id, id2)) RCO_ANIM_EXTRA_LEN[id]++; - } - } - fclose(fp); - RCO_ANIM_EXTRA_LEN_NUM = id+1; - } else { - error("Could not open map file."); - exit(1); - } -} - - - - - - -int get_ini_line(FILE* fp, char* buf, char** out1, char** out2) { - while(fgets(buf, INI_LINE_BUF_LEN, fp)) { - char *op = buf, *arg = NULL, *tmp; - unsigned int lineLen; - // trim line - #define IS_WHITESPACE(x) (x == '\t' || x == ' ' || x == '\n' || x == '\r') - while(*op && IS_WHITESPACE(*op)) - op++; - // don't forget to remove comments! - tmp = op; - while(*tmp) { - if(*tmp == ';') { - *tmp = '\0'; - break; - } - tmp++; - } - lineLen = strlen(op); - while(lineLen && IS_WHITESPACE(op[lineLen-1])) - lineLen--; - op[lineLen] = '\0'; - - if(lineLen == 0) continue; - if(op[0] == L';') continue; // comment line - - - if(op[0] == '[' && op[strlen(op)-1] == ']') { // [line] - op[strlen(op)-1] = '\0'; - op++; - *out1 = op; - *out2 = NULL; - return 1; - } - - // find =, if any - tmp = op; - while(*(++tmp)) - if(*tmp == '=') { - arg = tmp+1; - *tmp = '\0'; - while(*arg && IS_WHITESPACE(*arg)) - arg++; - - lineLen = strlen(op); - while(lineLen && IS_WHITESPACE(op[lineLen-1])) - lineLen--; - op[lineLen] = '\0'; - - break; - } - - *out1 = op; - *out2 = arg; - - return 2; - } - return 0; -} + + +#include +#include +#include +#include "general.h" +#include "rcomain.h" +#include "xml.h" + + +#define INI_LINE_BUF_LEN 2048 + +char* configDir = NULL; + +int get_ini_line(FILE* fp, char* buf, char** out1, char** out2); + + +// always try to load the INI files from the same dir as the executable +#ifdef WIN32 +#include +#else +#include +#endif +FILE* fopen_local(char* fn, char* mode) { + char path[512]; + int bytes = 0; + char *p = NULL; + + + if(configDir) { + strcpy(path, configDir); + if(path[strlen(path)-1] != '/' && path[strlen(path)-1] != '\\') { + char sepAdd[2] = {DIR_SEPARATOR, '\0'}; + strcat(path, sepAdd); + } + strcat(path, fn); + } else { + // solution to get current path from http://stackoverflow.com/questions/143174/c-c-how-to-obtain-the-full-path-of-current-directory +#ifdef WIN32 + bytes = GetModuleFileName(NULL, path, 512); + if(bytes) p = strrchr(path, '\\'); +#else + char szTmp[32]; + sprintf(szTmp, "/proc/%d/exe", getpid()); + bytes = readlink(szTmp, path, 512); + if(bytes > 511) bytes = 511; + if(bytes >= 0) path[bytes] = 0; + else bytes = 0; + if(bytes) p = strrchr(path, '/'); +#endif + + if(p) strcpy(p+1, fn); + else strcpy(path, fn); + } + return fopen(path, mode); +} + + +void configLoadTagmap(void) { + FILE* fp = fopen_local("tagmap.ini", "r"); + if(fp) { + char buf[INI_LINE_BUF_LEN]; + char *key, *val; + int type; + int id = -1, id2 = -1; + while((type = get_ini_line(fp, buf, &key, &val))) { + if(type == 1) { + id++; + RCOXML_TABLE_TAGS = (RcoTagMap)realloc(RCOXML_TABLE_TAGS, sizeof(*RCOXML_TABLE_TAGS) * (id+2)); + RCOXML_TABLE_TAGS[id][0][0] = 0; + id2 = -1; + RCOXML_TABLE_NAMES = (RcoTableMap)realloc(RCOXML_TABLE_NAMES, sizeof(*RCOXML_TABLE_NAMES) * (id+1)); + strcpy(RCOXML_TABLE_NAMES[id], key); + } else { + if(id==-1) continue; + id2++; + if(id2 >= RCO_TAGMAP_SIZE-2) { + error("Too many types."); + exit(1); + } + strcpy(RCOXML_TABLE_TAGS[id][id2], key); + RCOXML_TABLE_TAGS[id][id2+1][0] = 0; + } + } + fclose(fp); + RCOXML_TABLE_TAGS_NUM = id+1; + } else { + error("Could not open map file."); + exit(1); + } +} +void configLoadMiscmap(void) { + FILE* fp = fopen_local("miscmap.ini", "r"); + if(fp) { + char buf[INI_LINE_BUF_LEN]; + char *key, *val; + int type; + int id = -1; + RcoTableMap* map = NULL; + while((type = get_ini_line(fp, buf, &key, &val))) { + if(type == 1) { + if(!strcasecmp(key, "compression")) { + map = &RCOXML_TABLE_DATA_COMPRESSION; + } else if(!strcasecmp(key, "languages")) { + map = &RCOXML_TABLE_TEXT_LANG; + } else if(!strcasecmp(key, "textformats")) { + map = &RCOXML_TABLE_TEXT_FMT; + } else if(!strcasecmp(key, "imageformats")) { + map = &RCOXML_TABLE_IMG_FMT; + } else if(!strcasecmp(key, "soundformats")) { + map = &RCOXML_TABLE_SOUND_FMT; + } else if(!strcasecmp(key, "modelformats")) { + map = &RCOXML_TABLE_MODEL_FMT; + } else { + error("Unknown key %s", key); + exit(1); + } + *map = (RcoTableMap)malloc(sizeof(**map)); + (*map)[0][0] = 0; + id = -1; + } else { + if(!map) continue; + id++; + (*map) = (RcoTableMap)realloc(*map, sizeof(**map) * (id+2)); + strcpy((*map)[id], key); + (*map)[id+1][0] = 0; + } + } + fclose(fp); + #define CONFIG_LOAD_MISC_CHECK(v, s) if(!v) { error("Couldn't load " s " constants from config file!"); exit(1); } + CONFIG_LOAD_MISC_CHECK(RCOXML_TABLE_DATA_COMPRESSION, "compression"); + CONFIG_LOAD_MISC_CHECK(RCOXML_TABLE_TEXT_LANG, "language"); + CONFIG_LOAD_MISC_CHECK(RCOXML_TABLE_TEXT_FMT, "textformat"); + CONFIG_LOAD_MISC_CHECK(RCOXML_TABLE_IMG_FMT, "imageformat"); + CONFIG_LOAD_MISC_CHECK(RCOXML_TABLE_SOUND_FMT, "soundformat"); + CONFIG_LOAD_MISC_CHECK(RCOXML_TABLE_MODEL_FMT, "modelformat"); + } else { + error("Could not open map file."); + exit(1); + } +} +void configLoadObjmap(Bool ps3) { + char from[30] = "objattribdef-psp.ini"; + if(ps3) strcpy(from, "objattribdef-ps3.ini"); + FILE* fp = fopen_local(from, "r"); + if(fp) { + char buf[INI_LINE_BUF_LEN]; + char *key, *val; + int type; + int id = -1, id2 = -1, lenAdd=0; + while((type = get_ini_line(fp, buf, &key, &val))) { + if(type == 1) { + id++; + RCO_OBJ_EXTRA_LEN = (int*)realloc(RCO_OBJ_EXTRA_LEN, sizeof(int) * (id+1)); + //RCO_OBJ_EXTRA_LEN[id] = id2 + lenAdd; + RCO_OBJ_EXTRA_LEN[id] = 0; + RCO_OBJ_EXTRA_NAMES = (RcoObjMap)realloc(RCO_OBJ_EXTRA_NAMES, sizeof(*RCO_OBJ_EXTRA_NAMES) * (id+1)); + RCO_OBJ_EXTRA_NAMES[id][0][0] = 0; + RCO_OBJ_EXTRA_TYPES = (RcoObjTypes)realloc(RCO_OBJ_EXTRA_TYPES, sizeof(*RCO_OBJ_EXTRA_TYPES) * (id+1)); + RCO_OBJ_EXTRA_TYPES[id][0] = 0; + id2 = -1; + lenAdd = 0; + } else { + id2++; + RCO_OBJ_EXTRA_LEN[id]++; + if(id2 >= RCO_OBJMAP_SIZE-1) { + error("Too many types."); + exit(1); + } + strcpy(RCO_OBJ_EXTRA_NAMES[id][id2], key); + if(!strcasecmp(val, "int")) + RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_INT; + else if(!strcasecmp(val, "float")) + RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_FLOAT; + else if(!strcasecmp(val, "ref")) + RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_REF; + else if(!strcasecmp(val, "event")) + RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_EVENT; + else if(!strcasecmp(val, "image")) + RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_IMG; + else if(!strcasecmp(val, "object")) + RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_OBJ; + else if(!strcasecmp(val, "text")) + RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_TEXT; + else if(!strcasecmp(val, "model")) + RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_MODEL; + else if(!strcasecmp(val, "font")) + RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_FONT; + else if(!strcasecmp(val, "unk")) + RCO_OBJ_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_UNK; + else { + error("Unknown type '%s'", val); + exit(1); + } + if(RCO_OBJ_IS_REF(id, id2)) RCO_OBJ_EXTRA_LEN[id]++; + } + } + //RCO_OBJ_EXTRA_LEN[id+1] = id2 + lenAdd; + fclose(fp); + RCO_OBJ_EXTRA_LEN_NUM = id+1; + } else { + error("Could not open map file."); + exit(1); + } +} + +void configLoadAnimmap(Bool ps3) { + char from[30] = "animattribdef-psp.ini"; + if(ps3) strcpy(from, "animattribdef-ps3.ini"); + FILE* fp = fopen_local(from, "r"); + if(fp) { + char buf[INI_LINE_BUF_LEN]; + char *key, *val; + int type; + int id = -1, id2 = -1, lenAdd=0; + while((type = get_ini_line(fp, buf, &key, &val))) { + if(type == 1) { + id++; + RCO_ANIM_EXTRA_LEN = (int*)realloc(RCO_ANIM_EXTRA_LEN, sizeof(int) * (id+1)); + RCO_ANIM_EXTRA_LEN[id] = 0; + RCO_ANIM_EXTRA_NAMES = (RcoObjMap)realloc(RCO_ANIM_EXTRA_NAMES, sizeof(*RCO_ANIM_EXTRA_NAMES) * (id+1)); + RCO_ANIM_EXTRA_NAMES[id][0][0] = 0; + RCO_ANIM_EXTRA_TYPES = (RcoObjTypes)realloc(RCO_ANIM_EXTRA_TYPES, sizeof(*RCO_ANIM_EXTRA_TYPES) * (id+1)); + RCO_ANIM_EXTRA_TYPES[id][0] = 0; + id2 = -1; + lenAdd = 0; + } else { + id2++; + RCO_ANIM_EXTRA_LEN[id]++; + if(id2 >= RCO_OBJMAP_SIZE-1) { + error("Too many types."); + exit(1); + } + strcpy(RCO_ANIM_EXTRA_NAMES[id][id2], key); + if(!strcasecmp(val, "int")) + RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_INT; + else if(!strcasecmp(val, "float")) + RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_FLOAT; + else if(!strcasecmp(val, "ref")) + RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_REF; + else if(!strcasecmp(val, "event")) + RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_EVENT; + else if(!strcasecmp(val, "image")) + RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_IMG; + else if(!strcasecmp(val, "object")) + RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_OBJ; + else if(!strcasecmp(val, "text")) + RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_TEXT; + else if(!strcasecmp(val, "model")) + RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_MODEL; + else if(!strcasecmp(val, "font")) + RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_FONT; + else if(!strcasecmp(val, "unk")) + RCO_ANIM_EXTRA_TYPES[id][id2] = RCO_OBJ_EXTRA_TYPE_UNK; + else { + error("Unknown type '%s'", val); + exit(1); + } + if(RCO_ANIM_IS_REF(id, id2)) RCO_ANIM_EXTRA_LEN[id]++; + } + } + fclose(fp); + RCO_ANIM_EXTRA_LEN_NUM = id+1; + } else { + error("Could not open map file."); + exit(1); + } +} + + + + + + +int get_ini_line(FILE* fp, char* buf, char** out1, char** out2) { + while(fgets(buf, INI_LINE_BUF_LEN, fp)) { + char *op = buf, *arg = NULL, *tmp; + unsigned int lineLen; + // trim line + #define IS_WHITESPACE(x) (x == '\t' || x == ' ' || x == '\n' || x == '\r') + while(*op && IS_WHITESPACE(*op)) + op++; + // don't forget to remove comments! + tmp = op; + while(*tmp) { + if(*tmp == ';') { + *tmp = '\0'; + break; + } + tmp++; + } + lineLen = strlen(op); + while(lineLen && IS_WHITESPACE(op[lineLen-1])) + lineLen--; + op[lineLen] = '\0'; + + if(lineLen == 0) continue; + if(op[0] == L';') continue; // comment line + + + if(op[0] == '[' && op[strlen(op)-1] == ']') { // [line] + op[strlen(op)-1] = '\0'; + op++; + *out1 = op; + *out2 = NULL; + return 1; + } + + // find =, if any + tmp = op; + while(*(++tmp)) + if(*tmp == '=') { + arg = tmp+1; + *tmp = '\0'; + while(*arg && IS_WHITESPACE(*arg)) + arg++; + + lineLen = strlen(op); + while(lineLen && IS_WHITESPACE(op[lineLen-1])) + lineLen--; + op[lineLen] = '\0'; + + break; + } + + *out1 = op; + *out2 = arg; + + return 2; + } + return 0; +} diff --git a/configscan.h b/configscan.h index ed000ec..8e3c41b 100644 --- a/configscan.h +++ b/configscan.h @@ -1,13 +1,13 @@ - -#ifndef __CONFIGSCAN_H__ -#define __CONFIGSCAN_H__ -#include "general.h" - -extern char* configDir; - -void configLoadTagmap(void); -void configLoadMiscmap(void); -void configLoadObjmap(Bool ps3); -void configLoadAnimmap(Bool ps3); - -#endif + +#ifndef __CONFIGSCAN_H__ +#define __CONFIGSCAN_H__ +#include "general.h" + +extern char* configDir; + +void configLoadTagmap(void); +void configLoadMiscmap(void); +void configLoadObjmap(Bool ps3); +void configLoadAnimmap(Bool ps3); + +#endif diff --git a/general.c b/general.c index 1d7c03d..8f7dd7c 100644 --- a/general.c +++ b/general.c @@ -1,214 +1,214 @@ - -#include // memset() -#include -#include // sqrt -#include // free() - -#include -#include "general.h" -#include "rlzpack.h" -#include "7z/7z.h" - -uint zlib_compress(void* src, uint srcLen, void* dest, uint destLen, int level, int strat) { - if(strat == Z_USE_7Z) { - // compress with 7z - if(destLen < 6) return 0; - - unsigned int outSize = destLen - 6; // = 2 for sig + 4 for checksum - unsigned char* destPtr = (unsigned char*)dest; - *(uint16*)destPtr = 0xDA78; // zlib signature - destPtr += 2; - - int passes=1, fastbytes=64; - switch(level) { - case 1: - passes = 1; - fastbytes = 64; - break; - case 2: - passes = 3; - fastbytes = 128; - break; - case 3: - passes = 5; - fastbytes = 255; - break; - case 4: // overkill - passes = 15; // max is 255 - fastbytes = 255; // seems to work better than 256 or 257 - break; - } - // limits: passes [1-255], fastbytes [3-257(kMatchMaxLen)] - if(!compress_deflate_7z((unsigned char*)src, srcLen, destPtr, &outSize, passes, fastbytes)) - return 0; - - if(outSize+6 > destLen) return 0; - - uLong adler = adler32(adler32(0L, Z_NULL, 0), (Bytef*)src, srcLen); - #define ENDIAN_SWAP_32(x) (((x) & 0xFF) << 24 | ((x) & 0xFF00) << 8 | ((x) & 0xFF0000) >> 8 | ((x) & 0xFF000000) >> 24) - *(uint32*)(destPtr + outSize) = ENDIAN_SWAP_32(adler); - - return outSize + 6; - } else { - // compress with zlib - z_stream s; - memset(&s, 0, sizeof(s)); - if(deflateInit2(&s, level, Z_DEFLATED, 15, 8, strat)) return 0; - - s.next_in = (Bytef*)src; - s.avail_in = srcLen; - s.next_out = (Bytef*)dest; - s.avail_out = destLen; - - if(deflate(&s, Z_FINISH) < 0) return 0; - - deflateEnd(&s); - return s.total_out; - } -} - -int zlib_uncompress(void* dest, unsigned int destLen, const void* src, unsigned int srcLen) { - z_stream zs; - int ret; - - zs.next_in = (Bytef*)src; - zs.avail_in = srcLen; - zs.next_out = (Bytef*)dest; - zs.avail_out = destLen; - zs.zalloc = Z_NULL; - zs.zfree = Z_NULL; - zs.opaque = 0; - - ret = inflateInit(&zs); - if(ret == Z_OK) { - ret = inflate(&zs, Z_FINISH); - if(ret == Z_STREAM_END) ret = Z_OK; - if(inflateEnd(&zs) == Z_DATA_ERROR) ret = Z_DATA_ERROR; - } - if(ret == Z_BUF_ERROR) ret = Z_DATA_ERROR; - return ret; -} - -uint zlib_unpacked_size(void* src, uint srcLen) { - z_stream s; - memset(&s, 0, sizeof(s)); - - #define ZLIB_TESTUNPACK_BUFSIZE 65536 - char buffer[ZLIB_TESTUNPACK_BUFSIZE]; - - s.next_in = (Bytef*)src; - s.avail_in = srcLen; - s.next_out = (Bytef*)buffer; - s.avail_out = ZLIB_TESTUNPACK_BUFSIZE; - - if(inflateInit(&s) != Z_OK) return 0xFFFFFFFF; - - uint size = 0; - int ret; - while((ret = inflate(&s, Z_NO_FLUSH)) == Z_OK) { - size += ZLIB_TESTUNPACK_BUFSIZE - s.avail_out; - if(!s.avail_in) - break; - s.next_out = (Bytef*)buffer; - s.avail_out = ZLIB_TESTUNPACK_BUFSIZE; - } - - if(ret == Z_STREAM_END) { - size += ZLIB_TESTUNPACK_BUFSIZE - s.avail_out; - } - - inflateEnd(&s); - if(s.avail_in || (ret != Z_STREAM_END && ret != Z_OK)) return 0xFFFFFFFF; - - return size; -} - -uint rlz_compress(void* src, uint srcLen, void* dest, uint destLen, int mode) { - if(mode == -1) { - // theme creator compatible mode - // we'll be just as bad as the theme creator and run _4_ compression passes >_> - - int size5, size6, size7; - size5 = rlzcompress(dest, srcLen, src, 5); - size6 = rlzcompress(dest, srcLen, src, 6); - size7 = rlzcompress(dest, srcLen, src, 7); - - if((size5 == -1) && (size6 == -1) && (size7 == -1)) return 0; //all failed, lol - - if(size7 != -1 && (size7 < size6 || size6 != -1) && (size7 < size5 || size5 != -1)) - return size7; // okay, we're _slightly_ more optimised than the theme creator :P - if(size6 != -1 && (size6 < size5 || size5 != -1)) - return rlzcompress(dest, srcLen, src, 6); - if(size5 != -1) - return rlzcompress(dest, srcLen, src, 5); - - return 0; // should never occur - - } else { - int size = rlzcompress(dest, srcLen, src, mode); - if(size == -1) return 0; - else return size; - } -} - -Bool file_exists(char* fn) { - // our deetection routine is weird - just tries to open the file - FILE* fp; - - if((fp = fopen(fn, "r"))) { - // file exists - fclose(fp); - return TRUE; - } - return FALSE; -} - -uint filesize(const char* fn) { - FILE* fp = fopen(fn, "rb"); - if(!fp) return 0; - fseek(fp, 0, SEEK_END); - uint f = ftell(fp); - fclose(fp); - - return f; -} - -// finds the smallest prime number which is >= in -uint find_larger_prime(uint in) { - if(in <= 2) return 2; - - if(!(in & 1)) in++; // in is an odd number - - while(TRUE) { - if(is_prime(in)) return in; - in+=2; - } -} -Bool is_prime(uint in) { - if(in < 12) { // need this buffer as "lim" (below) may underflow - return (in <= 3 || in == 5 || in == 7 || in == 11); - } - - // filter out half the results by checking if the number is even - if((in ^ 1) & 1) return FALSE; - - uint i; - uint lim = (uint)floor(sqrt((float)in)); - for(i=3; i<=lim; i+=2) - if(in % i == 0) - return FALSE; - - return TRUE; -} - -void get_temp_fname(char* out, const char* ext) { - char* tmp = tempnam(NULL, "rcomage_tmp"); - if(tmp) { - strcpy(out, tmp); - free(tmp); - } else - strcpy(out, "rcomage_tmp"); - if(ext) - sprintf(out + strlen(out), ".%s", ext); -} - + +#include // memset() +#include +#include // sqrt +#include // free() + +#include +#include "general.h" +#include "rlzpack.h" +#include "7z/7z.h" + +uint zlib_compress(void* src, uint srcLen, void* dest, uint destLen, int level, int strat) { + if(strat == Z_USE_7Z) { + // compress with 7z + if(destLen < 6) return 0; + + unsigned int outSize = destLen - 6; // = 2 for sig + 4 for checksum + unsigned char* destPtr = (unsigned char*)dest; + *(uint16*)destPtr = 0xDA78; // zlib signature + destPtr += 2; + + int passes=1, fastbytes=64; + switch(level) { + case 1: + passes = 1; + fastbytes = 64; + break; + case 2: + passes = 3; + fastbytes = 128; + break; + case 3: + passes = 5; + fastbytes = 255; + break; + case 4: // overkill + passes = 15; // max is 255 + fastbytes = 255; // seems to work better than 256 or 257 + break; + } + // limits: passes [1-255], fastbytes [3-257(kMatchMaxLen)] + if(!compress_deflate_7z((unsigned char*)src, srcLen, destPtr, &outSize, passes, fastbytes)) + return 0; + + if(outSize+6 > destLen) return 0; + + uLong adler = adler32(adler32(0L, Z_NULL, 0), (Bytef*)src, srcLen); + #define ENDIAN_SWAP_32(x) (((x) & 0xFF) << 24 | ((x) & 0xFF00) << 8 | ((x) & 0xFF0000) >> 8 | ((x) & 0xFF000000) >> 24) + *(uint32*)(destPtr + outSize) = ENDIAN_SWAP_32(adler); + + return outSize + 6; + } else { + // compress with zlib + z_stream s; + memset(&s, 0, sizeof(s)); + if(deflateInit2(&s, level, Z_DEFLATED, 15, 8, strat)) return 0; + + s.next_in = (Bytef*)src; + s.avail_in = srcLen; + s.next_out = (Bytef*)dest; + s.avail_out = destLen; + + if(deflate(&s, Z_FINISH) < 0) return 0; + + deflateEnd(&s); + return s.total_out; + } +} + +int zlib_uncompress(void* dest, unsigned int destLen, const void* src, unsigned int srcLen) { + z_stream zs; + int ret; + + zs.next_in = (Bytef*)src; + zs.avail_in = srcLen; + zs.next_out = (Bytef*)dest; + zs.avail_out = destLen; + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + zs.opaque = 0; + + ret = inflateInit(&zs); + if(ret == Z_OK) { + ret = inflate(&zs, Z_FINISH); + if(ret == Z_STREAM_END) ret = Z_OK; + if(inflateEnd(&zs) == Z_DATA_ERROR) ret = Z_DATA_ERROR; + } + if(ret == Z_BUF_ERROR) ret = Z_DATA_ERROR; + return ret; +} + +uint zlib_unpacked_size(void* src, uint srcLen) { + z_stream s; + memset(&s, 0, sizeof(s)); + + #define ZLIB_TESTUNPACK_BUFSIZE 65536 + char buffer[ZLIB_TESTUNPACK_BUFSIZE]; + + s.next_in = (Bytef*)src; + s.avail_in = srcLen; + s.next_out = (Bytef*)buffer; + s.avail_out = ZLIB_TESTUNPACK_BUFSIZE; + + if(inflateInit(&s) != Z_OK) return 0xFFFFFFFF; + + uint size = 0; + int ret; + while((ret = inflate(&s, Z_NO_FLUSH)) == Z_OK) { + size += ZLIB_TESTUNPACK_BUFSIZE - s.avail_out; + if(!s.avail_in) + break; + s.next_out = (Bytef*)buffer; + s.avail_out = ZLIB_TESTUNPACK_BUFSIZE; + } + + if(ret == Z_STREAM_END) { + size += ZLIB_TESTUNPACK_BUFSIZE - s.avail_out; + } + + inflateEnd(&s); + if(s.avail_in || (ret != Z_STREAM_END && ret != Z_OK)) return 0xFFFFFFFF; + + return size; +} + +uint rlz_compress(void* src, uint srcLen, void* dest, uint destLen, int mode) { + if(mode == -1) { + // theme creator compatible mode + // we'll be just as bad as the theme creator and run _4_ compression passes >_> + + int size5, size6, size7; + size5 = rlzcompress(dest, srcLen, src, 5); + size6 = rlzcompress(dest, srcLen, src, 6); + size7 = rlzcompress(dest, srcLen, src, 7); + + if((size5 == -1) && (size6 == -1) && (size7 == -1)) return 0; //all failed, lol + + if(size7 != -1 && (size7 < size6 || size6 != -1) && (size7 < size5 || size5 != -1)) + return size7; // okay, we're _slightly_ more optimised than the theme creator :P + if(size6 != -1 && (size6 < size5 || size5 != -1)) + return rlzcompress(dest, srcLen, src, 6); + if(size5 != -1) + return rlzcompress(dest, srcLen, src, 5); + + return 0; // should never occur + + } else { + int size = rlzcompress(dest, srcLen, src, mode); + if(size == -1) return 0; + else return size; + } +} + +Bool file_exists(char* fn) { + // our deetection routine is weird - just tries to open the file + FILE* fp; + + if((fp = fopen(fn, "r"))) { + // file exists + fclose(fp); + return TRUE; + } + return FALSE; +} + +uint filesize(const char* fn) { + FILE* fp = fopen(fn, "rb"); + if(!fp) return 0; + fseek(fp, 0, SEEK_END); + uint f = ftell(fp); + fclose(fp); + + return f; +} + +// finds the smallest prime number which is >= in +uint find_larger_prime(uint in) { + if(in <= 2) return 2; + + if(!(in & 1)) in++; // in is an odd number + + while(TRUE) { + if(is_prime(in)) return in; + in+=2; + } +} +Bool is_prime(uint in) { + if(in < 12) { // need this buffer as "lim" (below) may underflow + return (in <= 3 || in == 5 || in == 7 || in == 11); + } + + // filter out half the results by checking if the number is even + if((in ^ 1) & 1) return FALSE; + + uint i; + uint lim = (uint)floor(sqrt((float)in)); + for(i=3; i<=lim; i+=2) + if(in % i == 0) + return FALSE; + + return TRUE; +} + +void get_temp_fname(char* out, const char* ext) { + char* tmp = tempnam(NULL, "rcomage_tmp"); + if(tmp) { + strcpy(out, tmp); + free(tmp); + } else + strcpy(out, "rcomage_tmp"); + if(ext) + sprintf(out + strlen(out), ".%s", ext); +} + diff --git a/general.h b/general.h index d1457a0..f5d4f95 100644 --- a/general.h +++ b/general.h @@ -1,87 +1,87 @@ - -#ifndef __RCOGENERAL_H__ -#define __RCOGENERAL_H__ - -//#define DISABLE_RLZ - -#include - -#define APPNAME "Rcomage" -#define APPNAME_VER "Rcomage v1.1.1" -#define APPXMLVER 1.10f -#define APPVER 1.11f - -typedef unsigned int uint; // since "unsigned int" is a pain in the @$$ to type - -typedef int Bool; -#ifndef TRUE -#define TRUE (Bool)1 -#endif -#ifndef FALSE -#define FALSE (Bool)0 -#endif - -#define ENDIAN_SWAP_16(x) (((x) & 0xFF) << 8 | ((x) & 0xFF00) >> 8) -#define ENDIAN_SWAP_32(x) (((x) & 0xFF) << 24 | ((x) & 0xFF00) << 8 | ((x) & 0xFF0000) >> 8 | ((x) & 0xFF000000) >> 24) -#define ENDIAN_SWAP(x) (sizeof(x) == 2 ? ENDIAN_SWAP_16(x) : ENDIAN_SWAP_32(x)) - - -#include - -extern Bool quietMode; -#define info(...) if(!quietMode) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } -#define error(...) { fprintf(stderr, "Error: "); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); } -#define warning(...) { fprintf(stderr, "Warning: "); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); } -// flush stderr to ensure that stuff gets written if app ends up crashing; can probably remove it later when the likelihood of rcomage crashing is lessened - - -#define fileread(fp, buf, len) fread(buf, len, 1, fp) -#define filewrite(fp, buf, len) fwrite(buf, len, 1, fp) -#define openread(fn) ((fn)[0] == '-' && (fn)[1] == 0 ? stdin : fopen(fn, "rb")) -#define openwrite(fn) ((fn)[0] == '-' && (fn)[1] == 0 ? stdout : fopen(fn, "wb")) - -// note, "n % 4" can also be written as "n & 3" (faster version as this does not invoke division) -//#define ALIGN_TO_4(n) (((n)&3) ? ((n) + (4-((n)&3))) : (n)) -//#define ALIGN_TO_4(n) (((n)&3) ? (((n) >> 2 + 1) << 2) : (n)) -//#define ALIGN_TO_4(n) (((n) + 3) & 0xFFFFFFFC) -//#define ALIGN_TO_4(n) (((n) + 3) & ((~0) ^ 3)) -#define ALIGN_TO_4(n) (((n) + 3) & (~3)) -//#define ALIGN_TO_4(n) (((n) + 3) >>2 <<2) // problem: upper 2 bits gets wiped too - -typedef uint8_t uint8; -typedef int8_t int8; -typedef uint16_t uint16; -typedef int16_t int16; -typedef uint32_t uint32; -typedef int32_t int32; - - -#define UTF8_BOM ((uint32) 0xBFBBEF) -#define UTF16_BOM ((uint16) 0xFEFF) -#define UTF32_BOM ((uint32) 0x0000FEFF) - -#define PACK_STRUCT(nam, struc) typedef struct struc __attribute__ ((packed)) nam - - -uint zlib_compress(void* src, uint srcLen, void* dest, uint destLen, int level, int strat); -int zlib_uncompress(void* dest, unsigned int destLen, const void* src, unsigned int srcLen); -uint zlib_unpacked_size(void* src, uint srcLen); -uint rlz_compress(void* src, uint srcLen, void* dest, uint destLen, int mode); -Bool file_exists(char* fn); - -uint filesize(const char* fn); - -uint find_larger_prime(uint in); -Bool is_prime(uint in); - -void get_temp_fname(char* out, const char* ext); - -#define Z_USE_7Z 10 - -#ifdef WIN32 -#define DIR_SEPARATOR '\\' -#else -#define DIR_SEPARATOR '/' -#endif - -#endif + +#ifndef __RCOGENERAL_H__ +#define __RCOGENERAL_H__ + +//#define DISABLE_RLZ + +#include + +#define APPNAME "Rcomage" +#define APPNAME_VER "Rcomage v1.1.1" +#define APPXMLVER 1.10f +#define APPVER 1.11f + +typedef unsigned int uint; // since "unsigned int" is a pain in the @$$ to type + +typedef int Bool; +#ifndef TRUE +#define TRUE (Bool)1 +#endif +#ifndef FALSE +#define FALSE (Bool)0 +#endif + +#define ENDIAN_SWAP_16(x) (((x) & 0xFF) << 8 | ((x) & 0xFF00) >> 8) +#define ENDIAN_SWAP_32(x) (((x) & 0xFF) << 24 | ((x) & 0xFF00) << 8 | ((x) & 0xFF0000) >> 8 | ((x) & 0xFF000000) >> 24) +#define ENDIAN_SWAP(x) (sizeof(x) == 2 ? ENDIAN_SWAP_16(x) : ENDIAN_SWAP_32(x)) + + +#include + +extern Bool quietMode; +#define info(...) if(!quietMode) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } +#define error(...) { fprintf(stderr, "Error: "); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); } +#define warning(...) { fprintf(stderr, "Warning: "); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); } +// flush stderr to ensure that stuff gets written if app ends up crashing; can probably remove it later when the likelihood of rcomage crashing is lessened + + +#define fileread(fp, buf, len) fread(buf, len, 1, fp) +#define filewrite(fp, buf, len) fwrite(buf, len, 1, fp) +#define openread(fn) ((fn)[0] == '-' && (fn)[1] == 0 ? stdin : fopen(fn, "rb")) +#define openwrite(fn) ((fn)[0] == '-' && (fn)[1] == 0 ? stdout : fopen(fn, "wb")) + +// note, "n % 4" can also be written as "n & 3" (faster version as this does not invoke division) +//#define ALIGN_TO_4(n) (((n)&3) ? ((n) + (4-((n)&3))) : (n)) +//#define ALIGN_TO_4(n) (((n)&3) ? (((n) >> 2 + 1) << 2) : (n)) +//#define ALIGN_TO_4(n) (((n) + 3) & 0xFFFFFFFC) +//#define ALIGN_TO_4(n) (((n) + 3) & ((~0) ^ 3)) +#define ALIGN_TO_4(n) (((n) + 3) & (~3)) +//#define ALIGN_TO_4(n) (((n) + 3) >>2 <<2) // problem: upper 2 bits gets wiped too + +typedef uint8_t uint8; +typedef int8_t int8; +typedef uint16_t uint16; +typedef int16_t int16; +typedef uint32_t uint32; +typedef int32_t int32; + + +#define UTF8_BOM ((uint32) 0xBFBBEF) +#define UTF16_BOM ((uint16) 0xFEFF) +#define UTF32_BOM ((uint32) 0x0000FEFF) + +#define PACK_STRUCT(nam, struc) typedef struct struc __attribute__ ((packed)) nam + + +uint zlib_compress(void* src, uint srcLen, void* dest, uint destLen, int level, int strat); +int zlib_uncompress(void* dest, unsigned int destLen, const void* src, unsigned int srcLen); +uint zlib_unpacked_size(void* src, uint srcLen); +uint rlz_compress(void* src, uint srcLen, void* dest, uint destLen, int mode); +Bool file_exists(char* fn); + +uint filesize(const char* fn); + +uint find_larger_prime(uint in); +Bool is_prime(uint in); + +void get_temp_fname(char* out, const char* ext); + +#define Z_USE_7Z 10 + +#ifdef WIN32 +#define DIR_SEPARATOR '\\' +#else +#define DIR_SEPARATOR '/' +#endif + +#endif diff --git a/globdefs.c b/globdefs.c index 4311138..a28eade 100644 --- a/globdefs.c +++ b/globdefs.c @@ -1,48 +1,48 @@ - -/*** For rcomain.h ***/ -#include "rcomain.h" - -// 0x1F object type only found in PS3 RCOs and may not be valid for PSP!!! -int* RCO_OBJ_EXTRA_LEN = NULL; -uint RCO_OBJ_EXTRA_LEN_NUM; - -// this doesn't include position info -RcoObjMap RCO_OBJ_EXTRA_NAMES = NULL; - - -RcoObjTypes RCO_OBJ_EXTRA_TYPES = NULL; - -int* RCO_ANIM_EXTRA_LEN = NULL; -uint RCO_ANIM_EXTRA_LEN_NUM; -const Bool RCO_ANIM_EXTRA_REFS[] = {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE}; - - -// this doesn't include references -RcoObjMap RCO_ANIM_EXTRA_NAMES = NULL; - -// we'll use the RCO_OBJ_EXTRA_TYPE_* constants here -RcoObjTypes RCO_ANIM_EXTRA_TYPES = NULL; - - - -/*** For xml.h ***/ -#include "xml.h" - - -RcoTableMap RCOXML_TABLE_DATA_COMPRESSION = NULL; - -RcoTableMap RCOXML_TABLE_TEXT_LANG = NULL; -RcoTableMap RCOXML_TABLE_TEXT_FMT = NULL; -RcoTableMap RCOXML_TABLE_IMG_FMT = NULL; -RcoTableMap RCOXML_TABLE_MODEL_FMT = NULL; -RcoTableMap RCOXML_TABLE_SOUND_FMT = NULL; -//const RcoTableMap RCOXML_TABLE_REFTYPE = {"event", "text"}; // TODO: - - -RcoTagMap RCOXML_TABLE_TAGS = NULL; - -uint RCOXML_TABLE_TAGS_NUM; - -RcoTableMap RCOXML_TABLE_NAMES = NULL; - - + +/*** For rcomain.h ***/ +#include "rcomain.h" + +// 0x1F object type only found in PS3 RCOs and may not be valid for PSP!!! +int* RCO_OBJ_EXTRA_LEN = NULL; +uint RCO_OBJ_EXTRA_LEN_NUM; + +// this doesn't include position info +RcoObjMap RCO_OBJ_EXTRA_NAMES = NULL; + + +RcoObjTypes RCO_OBJ_EXTRA_TYPES = NULL; + +int* RCO_ANIM_EXTRA_LEN = NULL; +uint RCO_ANIM_EXTRA_LEN_NUM; +const Bool RCO_ANIM_EXTRA_REFS[] = {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE}; + + +// this doesn't include references +RcoObjMap RCO_ANIM_EXTRA_NAMES = NULL; + +// we'll use the RCO_OBJ_EXTRA_TYPE_* constants here +RcoObjTypes RCO_ANIM_EXTRA_TYPES = NULL; + + + +/*** For xml.h ***/ +#include "xml.h" + + +RcoTableMap RCOXML_TABLE_DATA_COMPRESSION = NULL; + +RcoTableMap RCOXML_TABLE_TEXT_LANG = NULL; +RcoTableMap RCOXML_TABLE_TEXT_FMT = NULL; +RcoTableMap RCOXML_TABLE_IMG_FMT = NULL; +RcoTableMap RCOXML_TABLE_MODEL_FMT = NULL; +RcoTableMap RCOXML_TABLE_SOUND_FMT = NULL; +//const RcoTableMap RCOXML_TABLE_REFTYPE = {"event", "text"}; // TODO: + + +RcoTagMap RCOXML_TABLE_TAGS = NULL; + +uint RCOXML_TABLE_TAGS_NUM; + +RcoTableMap RCOXML_TABLE_NAMES = NULL; + + diff --git a/main.c b/main.c index 4186905..b246a3a 100644 --- a/main.c +++ b/main.c @@ -1,1083 +1,1083 @@ - -#include -#include -#include -#include "strfuncs.h" -#include "general.h" -#include "rcomain.h" -#include "xml.h" -#include "rcodump.h" -#include "vaghandler.h" -#include "vsmx.h" -#include "configscan.h" - -#define MAIN_INV_CMD_SYNTAX { \ - error("Invalid command syntax. See '%s help %s' for help.", app_argv[0], app_argv[1]); \ - return RETERR_SYNTAX; \ -} - - -// "1" is reserved -#define RETERR_SYNTAX 2 -#define RETERR_READRCO 3 -#define RETERR_WRITEXML 4 -#define RETERR_READXML 5 -#define RETERR_WRITERCO 6 -#define RETERR_GENERIC_FAILED 10 - -int main_help(void); -int main_dump(void); -int main_compile(void); - -int main_rebuild(void); - -int main_extract(void); -//int main_replace(void); -int main_vagdec(void); -int main_vagenc(void); -int main_vsmxdec(void); -int main_vsmxenc(void); - - -void retrieve_from_opts(Bool warnUnk, int num, ...); - -Bool quietMode; -int app_argc; -char** app_argv; - -extern Bool suppressDecompWarnings; - -int main(int argc, char** argv) { - app_argc = argc; - app_argv = argv; - - if(argc < 2 || !strcasecmp(app_argv[1], "help")) { - int ret = main_help(); - // we'll assume this is executed not from the cmd line so we'll call a pause - //system("pause"); - return ret; - } - - int (*func)(void) = NULL; - - if(!strcasecmp(app_argv[1], "dump")) - func = main_dump; - else if(!strcasecmp(app_argv[1], "compile")) - func = main_compile; - else if(!strcasecmp(app_argv[1], "rebuild")) - func = main_rebuild; - else if(!strcasecmp(app_argv[1], "extract")) - func = main_extract; - //else if(!strcasecmp(app_argv[1], "replace")) - // func = main_replace; - else if(!strcasecmp(app_argv[1], "vagdec")) - func = main_vagdec; - else if(!strcasecmp(app_argv[1], "vagenc")) - func = main_vagenc; - else if(!strcasecmp(app_argv[1], "vsmxdec")) - func = main_vsmxdec; - else if(!strcasecmp(app_argv[1], "vsmxenc")) - func = main_vsmxenc; - else { - error("Unknown function '%s'. Use '%s help' to see available functions.", app_argv[1], app_argv[0]); - return RETERR_SYNTAX; - } - - retrieve_from_opts(FALSE, 2, - "--quiet", "bool", &quietMode, - "--ini-dir", "string", &configDir - ); - - info("%s, written by ZiNgA BuRgA\nA general purpose RCO creation and manipulation command-line tool.\n", APPNAME_VER); - - configLoadTagmap(); - configLoadMiscmap(); - - return func(); -} - - -int main_help() { - printf("%s, written by ZiNgA BuRgA\nA general purpose RCO creation and manipulation command-line tool.\n\n", APPNAME_VER); - - - #ifdef DISABLE_RLZ - #define PRINT_PACK_OPTS \ - printf( \ - " For the following --pack-* functions, values can be 'none' or 'zlib'.\n" \ - " --pack-res and --pack-cmp, if specified, override values stored in XML.\n" \ - " --pack-hdr How to compress the RCO header. [none]\n" \ - " --pack-res How to compress RCO resources (BMP, GIM & GMO).\n" \ - " --pack-cmp Compression used on already compressed resources. [none]\n" \ - " This can be used to force additional compression on PNG,\n" \ - " JPEG, TIFF and GIF resources. 'none' is recommended.\n" \ - " --zlib-method \n" \ - " Zlib compression method/strategy [7z]\n" \ - " Can be default, filtered, huffman, rle, fixed or 7z\n" \ - " '7z' will use 7-Zip's deflate instead of zlib\n" \ - " --zlib-level Zlib compression level [3]\n" \ - " Values can be 0-9, or 1-4 for '--zlib-method 7z'\n" \ - " Defaults to 9 if not using 7z\n" \ - ) - #else - #define PRINT_PACK_OPTS \ - printf( \ - " For the following --pack-* functions, values can be 'none', 'zlib' or 'rlz'.\n" \ - " --pack-res and --pack-cmp, if specified, override values stored in XML.\n" \ - " Note: RLZ compression is EXPERIMENTAL!\n" \ - " --pack-hdr How to compress the RCO header. [none]\n" \ - " --pack-res How to compress RCO resources (BMP, GIM & GMO).\n" \ - " --pack-cmp Compression used on already compressed resources. [none]\n" \ - " This can be used to force additional compression on PNG,\n" \ - " JPEG, TIFF and GIF resources. 'none' is recommended.\n" \ - " --zlib-method \n" \ - " Zlib compression method/strategy [7z]\n" \ - " Can be default, filtered, huffman, rle, fixed or 7z\n" \ - " '7z' will use 7-Zip's deflate instead of zlib\n" \ - " --zlib-level Zlib compression level [3]\n" \ - " Values can be 0-9, or 1-4 for '--zlib-method 7z'\n" \ - " Defaults to 9 if not using 7z\n" \ - " --rlz-mode RLZ compression mode [-1]\n" \ - " Values can be 0-31, or -1\n" \ - " -1 tries modes 5, 6 & 7 and selects optimal output (default\n" \ - " Sony behaviour)\n" \ - ) - #endif - - - if(app_argc > 2) { - if(!strcasecmp(app_argv[2], "dump")) { - printf("Syntax: %s dump [] [options]\n", app_argv[0]); - printf( - " Dumps the structure of in an XML format to .\n" - " can be '-' which means stdout.\n" - "\n" - "\nOptions:\n" - " --resdir Folder to dump resources into, or '-' for no dumping.\n" - " Resources will be dumped to , but you can have custom\n" - " directories for different resources with the following:\n" - " --images \n" - " --sounds \n" - " --models \n" - " --text \n" - " --vsmx \n" - " You can also use '-' as for the above to disable\n" - " dumping resources of that type.\n" - " --output-txt Output separate .txt files for every text string. Each is\n" - " prepended with the appropriate UCS BOM.\n" - " --conv-gim \n" - " Send GIM images through gimconv (Windows only) and\n" - " convert to type with specified extension (ie png, bmp etc)\n" - " --gimconv-cmd \n" - " gimconv command to execute; defaults to 'gimconv'.\n" - " --gimconv-flags \n" - " Additional flags to pass to gimconv.\n" - " --conv-vag Convert VAG files to WAV.\n" - " --decode-vsmx Decode VSMX/JSX files to textual format.\n" - " --no-decomp-warn\n" - " Suppress decompression warnings.\n" - "\n" - "\nNote: for resource dumping, directories are NOT automatically created. If the\n" - " specified directorie(s) don't exist, the dumping will fail. However,\n" - " directories for text languages with the '--output-txt' option will be\n" - " automatically created if necessary.\n" - ); - return 0; - } - else if(!strcasecmp(app_argv[2], "compile")) { - printf("Syntax: %s compile [options]\n", app_argv[0]); - printf( - " Compiles an RCO using structure defined in .\n" - " can be '-' which means stdin.\n" - " Note that the XML file may have linked resources which need to be present for\n" - " the compilation process to succeed.\n" - "\n" - "\nOptions:\n" - ); - PRINT_PACK_OPTS; - printf("\n" - " --no-convgim Don't automatically run images marked as format=gim\n" - " through gimconv if extension is not '.gim'.\n" - " --gimconv-cmd \n" - " gimconv command to execute; defaults to 'gimconv'.\n" - " --gimconv-flags \n" - " Additional flags to pass to gimconv.\n" - " --no-convvag Don't automatically convert WAV sounds to VAG format\n" - " (based on extension). Note WAV->VAG conversion is lossy!\n" - " --no-encvsmx Don't automatically encode text files to VSMX\n" - " (based on extension).\n" - ); - return 0; - } - else if(!strcasecmp(app_argv[2], "rebuild")) { - printf("Syntax: %s rebuild [options]\n", app_argv[0]); - printf( - " Simply rebuilds an RCO , writing out to ; useful for\n" - " changing compression used.\n" - "\n" - "\nOptions:\n" - ); - PRINT_PACK_OPTS; - return 0; - } - - else if(!strcasecmp(app_argv[2], "extract")) { - printf("Syntax: %s extract [] [options]\n", app_argv[0]); - printf(" Extracts a single resource (image/sound/model/VSMX/text) with label\n" - " and saves it to . If is not specified, will default\n" - " to using the label as the filename, with no extension. can be '-'\n" - " meaning stdout.\n" - " * You should supply the '--lang' option when extracting text resources.\n" - " * You should supply the '--channel' option when extracting sound resources.\n" - "\n" - "\nOptions:\n" - " The following options only apply for extracting text resources.\n" - " --lang Language of text to extract. [English]\n" - " You can use a language ID or one of the following:\n"); - uint i=0; - while(RCOXML_TABLE_TEXT_LANG[i][0]) { - printf(" - %s (ID=%d)\n", RCOXML_TABLE_TEXT_LANG[i], i); - i++; - } - printf(" --no-txt-hdr Don't write UCS BOM.\n" - " The following option only applies for extracting sound resources.\n" - " --channel Extract sound channel . [1]\n" - " ** Note, --channel has currently not been implemented; will dump all channels.\n"); - return 0; - } - /* else if(!strcasecmp(app_argv[2], "replace")) { - printf("Syntax: %s replace [] [options]\n", app_argv[0]); - printf( - " Replaces a single resource (image/sound/model/VSMX/text) in with\n" - " label with that stored in , and rebuilds RCO as .\n" - " If is not supplied, will overwrite with this rebuilt RCO.\n" - " * You should supply the '--lang' option when replacing text resources.\n" - " * You should supply the '--channel' option when replacing sound resources.\n" - "\n" - "\nOptions:\n" - " --lang Language of text to replace. [English]\n" - " See '%s help extract' for valid values.\n" - " --channel Replace sound channel . [1]\n" - " --format Format of imported resource. Only applies to image,\n" - " sound and model resources. You can use a format ID, or\n" - " the following:\n"); - // TODO: - printf( - " --pack How to compress this resource.\n" - #ifdef DISABLE_RLZ - " Valid values are 'none' and 'zlib'.\n" - #else - " Valid values are 'none', 'zlib' and 'rlz'.\n" - #endif - " Only applies to images and models. By default, will use\n" - " 'zlib' unless format is PNG/JPG/TIF/GIF where 'none' will\n" - " be used.\n"); - PRINT_PACK_OPTS; - return 0; - } */ - /* - else if(!strcasecmp(app_argv[2], "list")) { - printf("Syntax: %s list [options]\n", app_argv[0]); - printf(" List resources in an easy to parse format.\n"); - printf("\n"); - printf("\nOptions:\n"); - printf(" --type Only list resources of a certain type.\n"); - printf(" Types can be 'image', 'sound', 'model', \n"); - printf(" --no-recurse Don't write UCS BOM.\n"); - printf(" The following option only applies for extracting sound resources.\n"); - printf(" --channel Extract sound channel . [1]\n"); - return 0; - } - */ - - else if(!strcasecmp(app_argv[2], "vagdec")) { - printf("Syntax: %s vagdec [ [ ...]] \n", app_argv[0]); - printf( - " Converts input VAG files to WAV file , where each input file is a\n" - " separate channel. Can use '-' for VAG or WAV files which mean stdin or\n" - " stdout respectively.\n" - ); - return 0; - } - else if(!strcasecmp(app_argv[2], "vagenc")) { - printf("Syntax: %s vagenc [ [ ...]]\n", app_argv[0]); - printf( - " Converts input WAV file to VAG. Output will be a separate file for each\n" - " channel. If only one output VAG is specified and input WAV has multiple\n" - " channels, will automatically append extensions. Input/outputs can be '-'\n" - " which mean stdin/stdout." - ); - return 0; - } - else if(!strcasecmp(app_argv[2], "vsmxdec")) { - printf("Syntax: %s vsmxdec [--decompile] \n", app_argv[0]); - printf( - " Decodes the input to , which can be '-' meaning stdout.\n" - " If --decompile option is specified, will activate experimental decompiler\n" - " and will output decompiled Javascript.\n" - ); - return 0; - } - else if(!strcasecmp(app_argv[2], "vsmxenc")) { - printf("Syntax: %s vsmxenc \n", app_argv[0]); - printf( - " Encodes input to . can be '-', meaning stdin.\n" - " Note that this cannot compile Javascript, only decoded text files.\n" - ); - return 0; - } - } - - printf("Syntax: %s [options]\n", app_argv[0]); - printf(" Use '%s help ' for help on a specific function\n", app_argv[0]); - - printf("\n"); - printf("\nAvailable functions:\n"); - printf("\n"); - //printf(" info Display info on an RCO file or a resource contained within.\n"); - //printf(" list List contents/resources in an RCO file.\n"); - printf(" extract Extract a resource from an RCO file.\n"); - printf(" dump Dumps structure and contents of an RCO file.\n"); - printf("\n"); - printf(" compile Compiles a new RCO file from a dump.\n"); - printf(" rebuild Reads an RCO, then writes it out; in other words, it just\n"); - printf(" copies it (and doesn't do it perfectly) - incredibly useful\n"); - printf(" eh? ^^\n"); - //printf(" replace Replace a resource in an RCO file.\n"); - printf("\n"); - printf(" vagdec Converts VAG to WAV.\n"); - printf(" vagenc Converts WAV to VAG.\n"); - printf("\n"); - printf(" vsmxdec Decodes a VSMX/JSX file into a text file.\n"); - printf(" vsmxenc Encodes a (VSMX) text file into a VSMX/JSX file.\n"); - printf("\n"); - printf(" help This help screen.\n"); - printf("\n"); - printf("\nAvailable options:\n"); - printf("\n"); - printf(" --quiet Only display warnings/errors.\n"); - printf(" --ini-dir Specify directory containing Rcomage INI files.\n"); - - /* - printf("\n"); - printf("\nGeneral options:\n"); - printf("\n"); - printf(" --pack \n"); - */ - - //edit/show page/anim data - - - return 0; -} - -int main_dump(void) { - if(app_argc < 4) MAIN_INV_CMD_SYNTAX; - - char* sRcoFile = NULL; - char* sXmlFile = NULL; - Bool sTextOutput = FALSE; - //char curPath[] = "."; - char* sResDir = NULL; - char* sResImgDir = NULL; - char* sResSndDir = NULL; - char* sResMdlDir = NULL; - char* sResVsmxDir = NULL; - char* sResTxtDir = NULL; - - //char* sConvGim = NULL; - RcoDumpGimconvOpts gimconvOpts; - Bool sConvVag = FALSE; - Bool sConvVsmx = FALSE; - - int i; - gimconvOpts.ext = gimconvOpts.cmd = gimconvOpts.extFlags = NULL; - // parse options - retrieve_from_opts(TRUE, 13, - "--output-txt", "bool", &sTextOutput, - "--conv-gim", "text", &gimconvOpts.ext, - "--gimconv-cmd", "text", &gimconvOpts.cmd, - "--gimconv-flags", "text", &gimconvOpts.extFlags, - "--conv-vag", "bool", &sConvVag, - "--decode-vsmx", "bool", &sConvVsmx, - "--resdir", "text", &sResDir, - "--images", "text", &sResImgDir, - "--sounds", "text", &sResSndDir, - "--models", "text", &sResMdlDir, - "--vsmx", "text", &sResVsmxDir, - "--text", "text", &sResTxtDir, - "--no-decomp-warn", "bool", &suppressDecompWarnings - ); - -#ifndef WIN32 - if(gimconvOpts.ext) { - warning("gimconv support is only available on Windows, as stuff only gets compiled for that platform and Sony doesn't give away source code (duh)."); - gimconvOpts.ext = NULL; - } -#endif - - for(i=2; i 5) { - gimconvOpts.ext[5] = '\0'; // prevent buffer overflow - if(gimconvOpts.cmd && strlen(gimconvOpts.cmd) > 255) { - gimconvOpts.cmd[255] = '\0'; // prevent buffer overflow - } - if(gimconvOpts.extFlags && strlen(gimconvOpts.extFlags) > 512) { - gimconvOpts.extFlags[512] = '\0'; // prevent buffer overflow - } - } - - rRCOFile* rco = read_rco(sRcoFile); - if(!rco) { - return RETERR_READRCO; - } - - - // dump resources - char textPathPrefix[MAX_FILENAME_LEN] = "\0"; - Bool sndDumped = FALSE; - if((!sResDir || strcmp(sResDir, "-")) && (rco->tblImage || rco->tblSound || rco->tblModel || rco->tblVSMX || rco->tblText)) { - char pathPrefix[MAX_FILENAME_LEN] = "\0"; - #define RCO_PRINT_PATH(p, c) \ - if(sResDir && c) \ - sprintf(p, "%s%c%s%c", sResDir, DIR_SEPARATOR, c, DIR_SEPARATOR); \ - else if(sResDir) \ - sprintf(p, "%s%c", sResDir, DIR_SEPARATOR); \ - else if(c) \ - sprintf(p, "%s%c", c, DIR_SEPARATOR); \ - else \ - p[0] = '\0'; - - if(!sResImgDir || strcmp(sResImgDir, "-")) { - void* arg = (void*)&gimconvOpts; - RCO_PRINT_PATH(pathPrefix, sResImgDir); - if(!gimconvOpts.ext) arg = NULL; - dump_resources(rco->labels, rco->tblImage, RCOXML_TABLE_IMG_FMT, pathPrefix, arg); - } - if(!sResSndDir || strcmp(sResSndDir, "-")) { - RCO_PRINT_PATH(pathPrefix, sResSndDir); - dump_resources(rco->labels, rco->tblSound, RCOXML_TABLE_SOUND_FMT, pathPrefix, (void*)sConvVag); - sndDumped = TRUE; - } - if(!sResMdlDir || strcmp(sResMdlDir, "-")) { - RCO_PRINT_PATH(pathPrefix, sResMdlDir); - dump_resources(rco->labels, rco->tblModel, RCOXML_TABLE_MODEL_FMT, pathPrefix, NULL); - } - - - if(!sResTxtDir || strcmp(sResTxtDir, "-")) { - RCO_PRINT_PATH(textPathPrefix, sResTxtDir); - dump_text_resources(rco->labels, rco->tblText, TRUE, textPathPrefix, !sTextOutput); - } - - - if((!sResVsmxDir || strcmp(sResVsmxDir, "-")) && rco->tblVSMX) { - rRCOEntry* rcoNode; - for(rcoNode=rco->tblMain.firstChild; rcoNode; rcoNode=rcoNode->next) { - if(rcoNode->id == RCO_TABLE_VSMX) { - char defName[] = "vsmx"; - char* label = defName; - if(rcoNode->labelOffset) - label = rco->labels + rcoNode->labelOffset; - RCO_PRINT_PATH(pathPrefix, sResVsmxDir); - sprintf(pathPrefix + strlen(pathPrefix), (sConvVsmx ? "%s.txt" : "%s.vsmx"), label); - info("Dumping VSMX resource '%s'...", label); - if(!dump_resource(pathPrefix, rcoNode, (sConvVsmx ? dump_output_vsmxdec : dump_output_data), NULL)) { - if(rcoNode->labelOffset) { - warning("Unable to dump VSMX resource '%s'.", label); - } else { - warning("Unable to dump VSMX resource."); - } - } - - strcpy(rcoNode->srcFile, pathPrefix); - rcoNode->srcAddr = 0; - rcoNode->srcCompression = RCO_DATA_COMPRESSION_NONE; - } - } - } - } - - // hack for text resources if dumping is disabled - if((sResDir && !strcmp(sResDir, "-")) || (sResTxtDir && !strcmp(sResTxtDir, "-"))) - sTextOutput = TRUE; - - // shouldn't need to check whether file exists cause we're not doing a binary write? - - FILE* fp; - if(!strcmp(sXmlFile, "-")) - fp = stdout; - else if(!(fp = fopen(sXmlFile, "w"))) { - error("Unable to open file %s", sXmlFile); - return RETERR_WRITEXML; - } - - int sndDumpVar = (sndDumped ? 1:0); - if(sndDumped && sConvVag) sndDumpVar = 2; - if(!write_xml(rco, fp, (textPathPrefix[0] ? textPathPrefix : NULL), !sTextOutput, sndDumpVar, sConvVsmx)) - return RETERR_WRITEXML; - - return 0; -} - -int main_extract(void) { - if(app_argc < 4) MAIN_INV_CMD_SYNTAX; - - char* sRcoFile = NULL; - char* sResLabel = NULL; - char* sOutFile = NULL; - char* stTextLang = NULL; - uint sTextLang = 1; - Bool sTextNoHdr = FALSE; - int sSndChannel = 1; - - int i; - // parse options - retrieve_from_opts(TRUE, 3, - "--lang", "text", &stTextLang, - "--no-txt-hdr", "bool", &sTextNoHdr, - "--channel", "int", &sSndChannel - ); - - for(i=2; itblMain, sResLabel); - if(entry) { - /* if(entry->id == RCO_TABLE_SOUND && entry->type == 1) { - // handle sound channel stuff - - } */ - if(stTextLang) warning("Resource '%s' is not text.", sResLabel); - if(!dump_resource(sOutFile, entry, dump_output_data, NULL)) { - error("Unable to dump resource '%s'.", sResLabel); - return RETERR_GENERIC_FAILED; - } - return 0; - } else if(rco->tblText) { - // try text - if(!rcoxml_text_to_int(stTextLang, RCOXML_TABLE_TEXT_LANG, &sTextLang)) { - if(!sscanf(stTextLang, "%i", &sTextLang)) - warning("Unknown language '%s', defaulting to English.", stTextLang); - } - rRCOEntry* rcoNode; - for(rcoNode = rco->tblText->firstChild; rcoNode; rcoNode = rcoNode->next) - if(((rRCOTextEntry*)rcoNode->extra)->lang == sTextLang) - break; - - if(!rcoNode) { - error("Unable to find specified language '%s'.", stTextLang); - return RETERR_GENERIC_FAILED; - } - - rRCOTextEntry* rte = (rRCOTextEntry*)rcoNode->extra; - - int textIdx = find_text_from_label(rco->labels, rte, sResLabel); - if(textIdx != -1) { - // dump it - FILE* fp = openwrite(sOutFile); - if(fp) { - if(!sTextNoHdr) { - if(rte->format == RCO_TEXT_FMT_UTF32) { - uint32 bom = UTF32_BOM; - if(rco->eSwap) bom = ENDIAN_SWAP(bom); - filewrite(fp, &bom, sizeof(bom)); - } else if(rte->format == RCO_TEXT_FMT_UTF8) { - uint32 bom = UTF8_BOM; - filewrite(fp, &bom, 3); - } else { - uint16 bom = UTF16_BOM; - if(rco->eSwap) bom = ENDIAN_SWAP(bom); - filewrite(fp, &bom, sizeof(bom)); - } - } - filewrite(fp, rco->labels + rte->indexes[textIdx].labelOffset, rte->indexes[textIdx].length); - fclose(fp); - return 0; - } else { - error("Unable to open file '%s'.", sOutFile); - return RETERR_GENERIC_FAILED; - } - } - } - - // not found - error("Unable to find resource named '%s'.", sResLabel); - return RETERR_GENERIC_FAILED; -} - - -void get_pack_opts(writerco_options* opts) { - char *packHdr, *packRes, *packCmp; - char *zlibMethod; - int zlibLevel, rlzMode; - - packHdr = packRes = packCmp = NULL; - zlibMethod = NULL; - zlibLevel = rlzMode = -1; - - opts->packHeader = opts->packText = RCO_DATA_COMPRESSION_NONE; - opts->packImg = opts->packModel = -1; - opts->packImgCompr = -1; - - retrieve_from_opts(FALSE, 5, - "--pack-hdr", "text", &packHdr, - "--pack-res", "text", &packRes, - "--pack-cmp", "text", &packCmp, - "--zlib-method", "text", &zlibMethod, - "--zlib-level", "int", &zlibLevel, - "--rlz-mode", "int", &rlzMode - ); - -#ifdef DISABLE_RLZ - #define READ_PACK_VAL(a, v) { \ - if(!strcasecmp(a, "none")) v = RCO_DATA_COMPRESSION_NONE; \ - else if(!strcasecmp(a, "zlib")) v = RCO_DATA_COMPRESSION_ZLIB; \ - else { \ - error("Unknown compression type '%s'.", a); \ - exit(RETERR_SYNTAX); \ - } \ - } -#else - #define READ_PACK_VAL(a, v) { \ - if(!strcasecmp(a, "none")) v = RCO_DATA_COMPRESSION_NONE; \ - else if(!strcasecmp(a, "zlib")) v = RCO_DATA_COMPRESSION_ZLIB; \ - else if(!strcasecmp(a, "rlz")) v = RCO_DATA_COMPRESSION_RLZ; \ - else { \ - error("Unknown compression type '%s'.", a); \ - exit(RETERR_SYNTAX); \ - } \ - } -#endif - if(packHdr) { - READ_PACK_VAL(packHdr, opts->packHeader); - opts->packText = opts->packHeader; - } - if(packRes) { - READ_PACK_VAL(packRes, opts->packImg); - opts->packModel = opts->packImg; - } - if(packCmp) { - READ_PACK_VAL(packCmp, opts->packImgCompr); - } - - opts->zlibMethod = WRITERCO_ZLIB_METHOD_7Z; - opts->zlibLevel = 3; - if(zlibMethod) { - if(!strcasecmp(zlibMethod, "default")) opts->zlibMethod = WRITERCO_ZLIB_METHOD_ZDEFAULT; - else if(!strcasecmp(zlibMethod, "filtered")) opts->zlibMethod = WRITERCO_ZLIB_METHOD_ZFILTERED; - else if(!strcasecmp(zlibMethod, "huffman")) opts->zlibMethod = WRITERCO_ZLIB_METHOD_ZHUFFMAN; - else if(!strcasecmp(zlibMethod, "rle")) opts->zlibMethod = WRITERCO_ZLIB_METHOD_ZRLE; - else if(!strcasecmp(zlibMethod, "fixed")) opts->zlibMethod = WRITERCO_ZLIB_METHOD_ZFIXED; - else if(!strcasecmp(zlibMethod, "7z")) opts->zlibMethod = WRITERCO_ZLIB_METHOD_7Z; - else { - error("Unknown zlib method '%s'.", zlibMethod); - exit(RETERR_SYNTAX); - } - } - // stick in default compression level if using zlib - if(opts->zlibMethod >= WRITERCO_ZLIB_METHOD_ZDEFAULT && opts->zlibMethod <= WRITERCO_ZLIB_METHOD_ZRLE) - opts->zlibLevel = 9; - if(zlibLevel != -1) { - if(opts->zlibMethod >= WRITERCO_ZLIB_METHOD_ZDEFAULT && opts->zlibMethod <= WRITERCO_ZLIB_METHOD_ZRLE) { - if(zlibLevel < 0 || zlibLevel > 9) { - error("zlib compression level must be between 0 and 9."); - exit(RETERR_SYNTAX); - } - } else if(opts->zlibMethod == WRITERCO_ZLIB_METHOD_7Z) { - if(zlibLevel < 1 || zlibLevel > 4) { - error("zlib-7z compression level must be between 1 and 4."); - exit(RETERR_SYNTAX); - } - } - - opts->zlibLevel = zlibLevel; - } - - opts->rlzMode = -1; - if(rlzMode != -1) { - if(rlzMode < 0 || rlzMode > 31) { - error("RLZ compression mode must be between 0 and 31."); - exit(RETERR_SYNTAX); - } - opts->rlzMode = rlzMode; - } -} - -int main_compile(void) { - if(app_argc < 4) MAIN_INV_CMD_SYNTAX; - - writerco_options opts; - - char* sXmlFile = NULL; - char* sRcoFile = NULL; - - Bool sNoConvGim = FALSE; - Bool sNoConvVag = FALSE; - Bool sNoConvVsmx = FALSE; - RcoDumpGimconvOpts gimconvOpts; - - gimconvOpts.cmd = gimconvOpts.extFlags = NULL; - - int i; - // parse options - get_pack_opts(&opts); - retrieve_from_opts(TRUE, 5, - "--no-convgim", "bool", &sNoConvGim, - "--no-convvag", "bool", &sNoConvVag, - "--no-encvsmx", "bool", &sNoConvVsmx, - "--gimconv-cmd", "text", &gimconvOpts.cmd, - "--gimconv-flags", "text", &gimconvOpts.extFlags - ); - - for(i=2; i 1) { - if((int)channels > app_argc-3) { - warning("WAV file contains %d channels, which is more than VAG files specified - will only output the first %d channels.", channels, app_argc-3); - channels = app_argc-3; - } else if((int)channels < app_argc-3) { - warning("WAV file contains %d channels, which is less than VAG files specified.", channels); - } - } - - // trim .vag extension if autogenerating that - if(app_argc-3 == 1 && channels > 1) { - uint nl = strlen(app_argv[3]); - if(nl > MAX_FILENAME_LEN) - app_argv[3][MAX_FILENAME_LEN] = '\0'; - else if(!strcasecmp(app_argv[3] + nl - 4, ".vag")) - app_argv[3][nl-4] = '\0'; - } - - Bool writeStdout = !strcmp(app_argv[3], "-"); - for(i=0; i 4) - warning("Extra arguments ignored."); - - - info("Reading and encoding input..."); - VsmxMem* vm = VsmxEncode(fin); - fclose(fin); - if(vm) { - info("Writing VSMX..."); - writeVSMX(fout, vm); - freeVsmxMem(vm); - fclose(fout); - return 0; - } else { - error("Failed to encode VSMX."); - return RETERR_GENERIC_FAILED; - } -} - - -void retrieve_from_opts(Bool warnUnk, int num, ...) { - int i,j; - va_list ap; - - for(i=2; i +#include +#include +#include "strfuncs.h" +#include "general.h" +#include "rcomain.h" +#include "xml.h" +#include "rcodump.h" +#include "vaghandler.h" +#include "vsmx.h" +#include "configscan.h" + +#define MAIN_INV_CMD_SYNTAX { \ + error("Invalid command syntax. See '%s help %s' for help.", app_argv[0], app_argv[1]); \ + return RETERR_SYNTAX; \ +} + + +// "1" is reserved +#define RETERR_SYNTAX 2 +#define RETERR_READRCO 3 +#define RETERR_WRITEXML 4 +#define RETERR_READXML 5 +#define RETERR_WRITERCO 6 +#define RETERR_GENERIC_FAILED 10 + +int main_help(void); +int main_dump(void); +int main_compile(void); + +int main_rebuild(void); + +int main_extract(void); +//int main_replace(void); +int main_vagdec(void); +int main_vagenc(void); +int main_vsmxdec(void); +int main_vsmxenc(void); + + +void retrieve_from_opts(Bool warnUnk, int num, ...); + +Bool quietMode; +int app_argc; +char** app_argv; + +extern Bool suppressDecompWarnings; + +int main(int argc, char** argv) { + app_argc = argc; + app_argv = argv; + + if(argc < 2 || !strcasecmp(app_argv[1], "help")) { + int ret = main_help(); + // we'll assume this is executed not from the cmd line so we'll call a pause + //system("pause"); + return ret; + } + + int (*func)(void) = NULL; + + if(!strcasecmp(app_argv[1], "dump")) + func = main_dump; + else if(!strcasecmp(app_argv[1], "compile")) + func = main_compile; + else if(!strcasecmp(app_argv[1], "rebuild")) + func = main_rebuild; + else if(!strcasecmp(app_argv[1], "extract")) + func = main_extract; + //else if(!strcasecmp(app_argv[1], "replace")) + // func = main_replace; + else if(!strcasecmp(app_argv[1], "vagdec")) + func = main_vagdec; + else if(!strcasecmp(app_argv[1], "vagenc")) + func = main_vagenc; + else if(!strcasecmp(app_argv[1], "vsmxdec")) + func = main_vsmxdec; + else if(!strcasecmp(app_argv[1], "vsmxenc")) + func = main_vsmxenc; + else { + error("Unknown function '%s'. Use '%s help' to see available functions.", app_argv[1], app_argv[0]); + return RETERR_SYNTAX; + } + + retrieve_from_opts(FALSE, 2, + "--quiet", "bool", &quietMode, + "--ini-dir", "string", &configDir + ); + + info("%s, written by ZiNgA BuRgA\nA general purpose RCO creation and manipulation command-line tool.\n", APPNAME_VER); + + configLoadTagmap(); + configLoadMiscmap(); + + return func(); +} + + +int main_help() { + printf("%s, written by ZiNgA BuRgA\nA general purpose RCO creation and manipulation command-line tool.\n\n", APPNAME_VER); + + + #ifdef DISABLE_RLZ + #define PRINT_PACK_OPTS \ + printf( \ + " For the following --pack-* functions, values can be 'none' or 'zlib'.\n" \ + " --pack-res and --pack-cmp, if specified, override values stored in XML.\n" \ + " --pack-hdr How to compress the RCO header. [none]\n" \ + " --pack-res How to compress RCO resources (BMP, GIM & GMO).\n" \ + " --pack-cmp Compression used on already compressed resources. [none]\n" \ + " This can be used to force additional compression on PNG,\n" \ + " JPEG, TIFF and GIF resources. 'none' is recommended.\n" \ + " --zlib-method \n" \ + " Zlib compression method/strategy [7z]\n" \ + " Can be default, filtered, huffman, rle, fixed or 7z\n" \ + " '7z' will use 7-Zip's deflate instead of zlib\n" \ + " --zlib-level Zlib compression level [3]\n" \ + " Values can be 0-9, or 1-4 for '--zlib-method 7z'\n" \ + " Defaults to 9 if not using 7z\n" \ + ) + #else + #define PRINT_PACK_OPTS \ + printf( \ + " For the following --pack-* functions, values can be 'none', 'zlib' or 'rlz'.\n" \ + " --pack-res and --pack-cmp, if specified, override values stored in XML.\n" \ + " Note: RLZ compression is EXPERIMENTAL!\n" \ + " --pack-hdr How to compress the RCO header. [none]\n" \ + " --pack-res How to compress RCO resources (BMP, GIM & GMO).\n" \ + " --pack-cmp Compression used on already compressed resources. [none]\n" \ + " This can be used to force additional compression on PNG,\n" \ + " JPEG, TIFF and GIF resources. 'none' is recommended.\n" \ + " --zlib-method \n" \ + " Zlib compression method/strategy [7z]\n" \ + " Can be default, filtered, huffman, rle, fixed or 7z\n" \ + " '7z' will use 7-Zip's deflate instead of zlib\n" \ + " --zlib-level Zlib compression level [3]\n" \ + " Values can be 0-9, or 1-4 for '--zlib-method 7z'\n" \ + " Defaults to 9 if not using 7z\n" \ + " --rlz-mode RLZ compression mode [-1]\n" \ + " Values can be 0-31, or -1\n" \ + " -1 tries modes 5, 6 & 7 and selects optimal output (default\n" \ + " Sony behaviour)\n" \ + ) + #endif + + + if(app_argc > 2) { + if(!strcasecmp(app_argv[2], "dump")) { + printf("Syntax: %s dump [] [options]\n", app_argv[0]); + printf( + " Dumps the structure of in an XML format to .\n" + " can be '-' which means stdout.\n" + "\n" + "\nOptions:\n" + " --resdir Folder to dump resources into, or '-' for no dumping.\n" + " Resources will be dumped to , but you can have custom\n" + " directories for different resources with the following:\n" + " --images \n" + " --sounds \n" + " --models \n" + " --text \n" + " --vsmx \n" + " You can also use '-' as for the above to disable\n" + " dumping resources of that type.\n" + " --output-txt Output separate .txt files for every text string. Each is\n" + " prepended with the appropriate UCS BOM.\n" + " --conv-gim \n" + " Send GIM images through gimconv (Windows only) and\n" + " convert to type with specified extension (ie png, bmp etc)\n" + " --gimconv-cmd \n" + " gimconv command to execute; defaults to 'gimconv'.\n" + " --gimconv-flags \n" + " Additional flags to pass to gimconv.\n" + " --conv-vag Convert VAG files to WAV.\n" + " --decode-vsmx Decode VSMX/JSX files to textual format.\n" + " --no-decomp-warn\n" + " Suppress decompression warnings.\n" + "\n" + "\nNote: for resource dumping, directories are NOT automatically created. If the\n" + " specified directorie(s) don't exist, the dumping will fail. However,\n" + " directories for text languages with the '--output-txt' option will be\n" + " automatically created if necessary.\n" + ); + return 0; + } + else if(!strcasecmp(app_argv[2], "compile")) { + printf("Syntax: %s compile [options]\n", app_argv[0]); + printf( + " Compiles an RCO using structure defined in .\n" + " can be '-' which means stdin.\n" + " Note that the XML file may have linked resources which need to be present for\n" + " the compilation process to succeed.\n" + "\n" + "\nOptions:\n" + ); + PRINT_PACK_OPTS; + printf("\n" + " --no-convgim Don't automatically run images marked as format=gim\n" + " through gimconv if extension is not '.gim'.\n" + " --gimconv-cmd \n" + " gimconv command to execute; defaults to 'gimconv'.\n" + " --gimconv-flags \n" + " Additional flags to pass to gimconv.\n" + " --no-convvag Don't automatically convert WAV sounds to VAG format\n" + " (based on extension). Note WAV->VAG conversion is lossy!\n" + " --no-encvsmx Don't automatically encode text files to VSMX\n" + " (based on extension).\n" + ); + return 0; + } + else if(!strcasecmp(app_argv[2], "rebuild")) { + printf("Syntax: %s rebuild [options]\n", app_argv[0]); + printf( + " Simply rebuilds an RCO , writing out to ; useful for\n" + " changing compression used.\n" + "\n" + "\nOptions:\n" + ); + PRINT_PACK_OPTS; + return 0; + } + + else if(!strcasecmp(app_argv[2], "extract")) { + printf("Syntax: %s extract [] [options]\n", app_argv[0]); + printf(" Extracts a single resource (image/sound/model/VSMX/text) with label\n" + " and saves it to . If is not specified, will default\n" + " to using the label as the filename, with no extension. can be '-'\n" + " meaning stdout.\n" + " * You should supply the '--lang' option when extracting text resources.\n" + " * You should supply the '--channel' option when extracting sound resources.\n" + "\n" + "\nOptions:\n" + " The following options only apply for extracting text resources.\n" + " --lang Language of text to extract. [English]\n" + " You can use a language ID or one of the following:\n"); + uint i=0; + while(RCOXML_TABLE_TEXT_LANG[i][0]) { + printf(" - %s (ID=%d)\n", RCOXML_TABLE_TEXT_LANG[i], i); + i++; + } + printf(" --no-txt-hdr Don't write UCS BOM.\n" + " The following option only applies for extracting sound resources.\n" + " --channel Extract sound channel . [1]\n" + " ** Note, --channel has currently not been implemented; will dump all channels.\n"); + return 0; + } + /* else if(!strcasecmp(app_argv[2], "replace")) { + printf("Syntax: %s replace [] [options]\n", app_argv[0]); + printf( + " Replaces a single resource (image/sound/model/VSMX/text) in with\n" + " label with that stored in , and rebuilds RCO as .\n" + " If is not supplied, will overwrite with this rebuilt RCO.\n" + " * You should supply the '--lang' option when replacing text resources.\n" + " * You should supply the '--channel' option when replacing sound resources.\n" + "\n" + "\nOptions:\n" + " --lang Language of text to replace. [English]\n" + " See '%s help extract' for valid values.\n" + " --channel Replace sound channel . [1]\n" + " --format Format of imported resource. Only applies to image,\n" + " sound and model resources. You can use a format ID, or\n" + " the following:\n"); + // TODO: + printf( + " --pack How to compress this resource.\n" + #ifdef DISABLE_RLZ + " Valid values are 'none' and 'zlib'.\n" + #else + " Valid values are 'none', 'zlib' and 'rlz'.\n" + #endif + " Only applies to images and models. By default, will use\n" + " 'zlib' unless format is PNG/JPG/TIF/GIF where 'none' will\n" + " be used.\n"); + PRINT_PACK_OPTS; + return 0; + } */ + /* + else if(!strcasecmp(app_argv[2], "list")) { + printf("Syntax: %s list [options]\n", app_argv[0]); + printf(" List resources in an easy to parse format.\n"); + printf("\n"); + printf("\nOptions:\n"); + printf(" --type Only list resources of a certain type.\n"); + printf(" Types can be 'image', 'sound', 'model', \n"); + printf(" --no-recurse Don't write UCS BOM.\n"); + printf(" The following option only applies for extracting sound resources.\n"); + printf(" --channel Extract sound channel . [1]\n"); + return 0; + } + */ + + else if(!strcasecmp(app_argv[2], "vagdec")) { + printf("Syntax: %s vagdec [ [ ...]] \n", app_argv[0]); + printf( + " Converts input VAG files to WAV file , where each input file is a\n" + " separate channel. Can use '-' for VAG or WAV files which mean stdin or\n" + " stdout respectively.\n" + ); + return 0; + } + else if(!strcasecmp(app_argv[2], "vagenc")) { + printf("Syntax: %s vagenc [ [ ...]]\n", app_argv[0]); + printf( + " Converts input WAV file to VAG. Output will be a separate file for each\n" + " channel. If only one output VAG is specified and input WAV has multiple\n" + " channels, will automatically append extensions. Input/outputs can be '-'\n" + " which mean stdin/stdout." + ); + return 0; + } + else if(!strcasecmp(app_argv[2], "vsmxdec")) { + printf("Syntax: %s vsmxdec [--decompile] \n", app_argv[0]); + printf( + " Decodes the input to , which can be '-' meaning stdout.\n" + " If --decompile option is specified, will activate experimental decompiler\n" + " and will output decompiled Javascript.\n" + ); + return 0; + } + else if(!strcasecmp(app_argv[2], "vsmxenc")) { + printf("Syntax: %s vsmxenc \n", app_argv[0]); + printf( + " Encodes input to . can be '-', meaning stdin.\n" + " Note that this cannot compile Javascript, only decoded text files.\n" + ); + return 0; + } + } + + printf("Syntax: %s [options]\n", app_argv[0]); + printf(" Use '%s help ' for help on a specific function\n", app_argv[0]); + + printf("\n"); + printf("\nAvailable functions:\n"); + printf("\n"); + //printf(" info Display info on an RCO file or a resource contained within.\n"); + //printf(" list List contents/resources in an RCO file.\n"); + printf(" extract Extract a resource from an RCO file.\n"); + printf(" dump Dumps structure and contents of an RCO file.\n"); + printf("\n"); + printf(" compile Compiles a new RCO file from a dump.\n"); + printf(" rebuild Reads an RCO, then writes it out; in other words, it just\n"); + printf(" copies it (and doesn't do it perfectly) - incredibly useful\n"); + printf(" eh? ^^\n"); + //printf(" replace Replace a resource in an RCO file.\n"); + printf("\n"); + printf(" vagdec Converts VAG to WAV.\n"); + printf(" vagenc Converts WAV to VAG.\n"); + printf("\n"); + printf(" vsmxdec Decodes a VSMX/JSX file into a text file.\n"); + printf(" vsmxenc Encodes a (VSMX) text file into a VSMX/JSX file.\n"); + printf("\n"); + printf(" help This help screen.\n"); + printf("\n"); + printf("\nAvailable options:\n"); + printf("\n"); + printf(" --quiet Only display warnings/errors.\n"); + printf(" --ini-dir Specify directory containing Rcomage INI files.\n"); + + /* + printf("\n"); + printf("\nGeneral options:\n"); + printf("\n"); + printf(" --pack \n"); + */ + + //edit/show page/anim data + + + return 0; +} + +int main_dump(void) { + if(app_argc < 4) MAIN_INV_CMD_SYNTAX; + + char* sRcoFile = NULL; + char* sXmlFile = NULL; + Bool sTextOutput = FALSE; + //char curPath[] = "."; + char* sResDir = NULL; + char* sResImgDir = NULL; + char* sResSndDir = NULL; + char* sResMdlDir = NULL; + char* sResVsmxDir = NULL; + char* sResTxtDir = NULL; + + //char* sConvGim = NULL; + RcoDumpGimconvOpts gimconvOpts; + Bool sConvVag = FALSE; + Bool sConvVsmx = FALSE; + + int i; + gimconvOpts.ext = gimconvOpts.cmd = gimconvOpts.extFlags = NULL; + // parse options + retrieve_from_opts(TRUE, 13, + "--output-txt", "bool", &sTextOutput, + "--conv-gim", "text", &gimconvOpts.ext, + "--gimconv-cmd", "text", &gimconvOpts.cmd, + "--gimconv-flags", "text", &gimconvOpts.extFlags, + "--conv-vag", "bool", &sConvVag, + "--decode-vsmx", "bool", &sConvVsmx, + "--resdir", "text", &sResDir, + "--images", "text", &sResImgDir, + "--sounds", "text", &sResSndDir, + "--models", "text", &sResMdlDir, + "--vsmx", "text", &sResVsmxDir, + "--text", "text", &sResTxtDir, + "--no-decomp-warn", "bool", &suppressDecompWarnings + ); + +#ifndef WIN32 + if(gimconvOpts.ext) { + warning("gimconv support is only available on Windows, as stuff only gets compiled for that platform and Sony doesn't give away source code (duh)."); + gimconvOpts.ext = NULL; + } +#endif + + for(i=2; i 5) { + gimconvOpts.ext[5] = '\0'; // prevent buffer overflow + if(gimconvOpts.cmd && strlen(gimconvOpts.cmd) > 255) { + gimconvOpts.cmd[255] = '\0'; // prevent buffer overflow + } + if(gimconvOpts.extFlags && strlen(gimconvOpts.extFlags) > 512) { + gimconvOpts.extFlags[512] = '\0'; // prevent buffer overflow + } + } + + rRCOFile* rco = read_rco(sRcoFile); + if(!rco) { + return RETERR_READRCO; + } + + + // dump resources + char textPathPrefix[MAX_FILENAME_LEN] = "\0"; + Bool sndDumped = FALSE; + if((!sResDir || strcmp(sResDir, "-")) && (rco->tblImage || rco->tblSound || rco->tblModel || rco->tblVSMX || rco->tblText)) { + char pathPrefix[MAX_FILENAME_LEN] = "\0"; + #define RCO_PRINT_PATH(p, c) \ + if(sResDir && c) \ + sprintf(p, "%s%c%s%c", sResDir, DIR_SEPARATOR, c, DIR_SEPARATOR); \ + else if(sResDir) \ + sprintf(p, "%s%c", sResDir, DIR_SEPARATOR); \ + else if(c) \ + sprintf(p, "%s%c", c, DIR_SEPARATOR); \ + else \ + p[0] = '\0'; + + if(!sResImgDir || strcmp(sResImgDir, "-")) { + void* arg = (void*)&gimconvOpts; + RCO_PRINT_PATH(pathPrefix, sResImgDir); + if(!gimconvOpts.ext) arg = NULL; + dump_resources(rco->labels, rco->tblImage, RCOXML_TABLE_IMG_FMT, pathPrefix, arg); + } + if(!sResSndDir || strcmp(sResSndDir, "-")) { + RCO_PRINT_PATH(pathPrefix, sResSndDir); + dump_resources(rco->labels, rco->tblSound, RCOXML_TABLE_SOUND_FMT, pathPrefix, (void*)sConvVag); + sndDumped = TRUE; + } + if(!sResMdlDir || strcmp(sResMdlDir, "-")) { + RCO_PRINT_PATH(pathPrefix, sResMdlDir); + dump_resources(rco->labels, rco->tblModel, RCOXML_TABLE_MODEL_FMT, pathPrefix, NULL); + } + + + if(!sResTxtDir || strcmp(sResTxtDir, "-")) { + RCO_PRINT_PATH(textPathPrefix, sResTxtDir); + dump_text_resources(rco->labels, rco->tblText, TRUE, textPathPrefix, !sTextOutput); + } + + + if((!sResVsmxDir || strcmp(sResVsmxDir, "-")) && rco->tblVSMX) { + rRCOEntry* rcoNode; + for(rcoNode=rco->tblMain.firstChild; rcoNode; rcoNode=rcoNode->next) { + if(rcoNode->id == RCO_TABLE_VSMX) { + char defName[] = "vsmx"; + char* label = defName; + if(rcoNode->labelOffset) + label = rco->labels + rcoNode->labelOffset; + RCO_PRINT_PATH(pathPrefix, sResVsmxDir); + sprintf(pathPrefix + strlen(pathPrefix), (sConvVsmx ? "%s.txt" : "%s.vsmx"), label); + info("Dumping VSMX resource '%s'...", label); + if(!dump_resource(pathPrefix, rcoNode, (sConvVsmx ? dump_output_vsmxdec : dump_output_data), NULL)) { + if(rcoNode->labelOffset) { + warning("Unable to dump VSMX resource '%s'.", label); + } else { + warning("Unable to dump VSMX resource."); + } + } + + strcpy(rcoNode->srcFile, pathPrefix); + rcoNode->srcAddr = 0; + rcoNode->srcCompression = RCO_DATA_COMPRESSION_NONE; + } + } + } + } + + // hack for text resources if dumping is disabled + if((sResDir && !strcmp(sResDir, "-")) || (sResTxtDir && !strcmp(sResTxtDir, "-"))) + sTextOutput = TRUE; + + // shouldn't need to check whether file exists cause we're not doing a binary write? + + FILE* fp; + if(!strcmp(sXmlFile, "-")) + fp = stdout; + else if(!(fp = fopen(sXmlFile, "w"))) { + error("Unable to open file %s", sXmlFile); + return RETERR_WRITEXML; + } + + int sndDumpVar = (sndDumped ? 1:0); + if(sndDumped && sConvVag) sndDumpVar = 2; + if(!write_xml(rco, fp, (textPathPrefix[0] ? textPathPrefix : NULL), !sTextOutput, sndDumpVar, sConvVsmx)) + return RETERR_WRITEXML; + + return 0; +} + +int main_extract(void) { + if(app_argc < 4) MAIN_INV_CMD_SYNTAX; + + char* sRcoFile = NULL; + char* sResLabel = NULL; + char* sOutFile = NULL; + char* stTextLang = NULL; + uint sTextLang = 1; + Bool sTextNoHdr = FALSE; + int sSndChannel = 1; + + int i; + // parse options + retrieve_from_opts(TRUE, 3, + "--lang", "text", &stTextLang, + "--no-txt-hdr", "bool", &sTextNoHdr, + "--channel", "int", &sSndChannel + ); + + for(i=2; itblMain, sResLabel); + if(entry) { + /* if(entry->id == RCO_TABLE_SOUND && entry->type == 1) { + // handle sound channel stuff + + } */ + if(stTextLang) warning("Resource '%s' is not text.", sResLabel); + if(!dump_resource(sOutFile, entry, dump_output_data, NULL)) { + error("Unable to dump resource '%s'.", sResLabel); + return RETERR_GENERIC_FAILED; + } + return 0; + } else if(rco->tblText) { + // try text + if(!rcoxml_text_to_int(stTextLang, RCOXML_TABLE_TEXT_LANG, &sTextLang)) { + if(!sscanf(stTextLang, "%i", &sTextLang)) + warning("Unknown language '%s', defaulting to English.", stTextLang); + } + rRCOEntry* rcoNode; + for(rcoNode = rco->tblText->firstChild; rcoNode; rcoNode = rcoNode->next) + if(((rRCOTextEntry*)rcoNode->extra)->lang == sTextLang) + break; + + if(!rcoNode) { + error("Unable to find specified language '%s'.", stTextLang); + return RETERR_GENERIC_FAILED; + } + + rRCOTextEntry* rte = (rRCOTextEntry*)rcoNode->extra; + + int textIdx = find_text_from_label(rco->labels, rte, sResLabel); + if(textIdx != -1) { + // dump it + FILE* fp = openwrite(sOutFile); + if(fp) { + if(!sTextNoHdr) { + if(rte->format == RCO_TEXT_FMT_UTF32) { + uint32 bom = UTF32_BOM; + if(rco->eSwap) bom = ENDIAN_SWAP(bom); + filewrite(fp, &bom, sizeof(bom)); + } else if(rte->format == RCO_TEXT_FMT_UTF8) { + uint32 bom = UTF8_BOM; + filewrite(fp, &bom, 3); + } else { + uint16 bom = UTF16_BOM; + if(rco->eSwap) bom = ENDIAN_SWAP(bom); + filewrite(fp, &bom, sizeof(bom)); + } + } + filewrite(fp, rco->labels + rte->indexes[textIdx].labelOffset, rte->indexes[textIdx].length); + fclose(fp); + return 0; + } else { + error("Unable to open file '%s'.", sOutFile); + return RETERR_GENERIC_FAILED; + } + } + } + + // not found + error("Unable to find resource named '%s'.", sResLabel); + return RETERR_GENERIC_FAILED; +} + + +void get_pack_opts(writerco_options* opts) { + char *packHdr, *packRes, *packCmp; + char *zlibMethod; + int zlibLevel, rlzMode; + + packHdr = packRes = packCmp = NULL; + zlibMethod = NULL; + zlibLevel = rlzMode = -1; + + opts->packHeader = opts->packText = RCO_DATA_COMPRESSION_NONE; + opts->packImg = opts->packModel = -1; + opts->packImgCompr = -1; + + retrieve_from_opts(FALSE, 5, + "--pack-hdr", "text", &packHdr, + "--pack-res", "text", &packRes, + "--pack-cmp", "text", &packCmp, + "--zlib-method", "text", &zlibMethod, + "--zlib-level", "int", &zlibLevel, + "--rlz-mode", "int", &rlzMode + ); + +#ifdef DISABLE_RLZ + #define READ_PACK_VAL(a, v) { \ + if(!strcasecmp(a, "none")) v = RCO_DATA_COMPRESSION_NONE; \ + else if(!strcasecmp(a, "zlib")) v = RCO_DATA_COMPRESSION_ZLIB; \ + else { \ + error("Unknown compression type '%s'.", a); \ + exit(RETERR_SYNTAX); \ + } \ + } +#else + #define READ_PACK_VAL(a, v) { \ + if(!strcasecmp(a, "none")) v = RCO_DATA_COMPRESSION_NONE; \ + else if(!strcasecmp(a, "zlib")) v = RCO_DATA_COMPRESSION_ZLIB; \ + else if(!strcasecmp(a, "rlz")) v = RCO_DATA_COMPRESSION_RLZ; \ + else { \ + error("Unknown compression type '%s'.", a); \ + exit(RETERR_SYNTAX); \ + } \ + } +#endif + if(packHdr) { + READ_PACK_VAL(packHdr, opts->packHeader); + opts->packText = opts->packHeader; + } + if(packRes) { + READ_PACK_VAL(packRes, opts->packImg); + opts->packModel = opts->packImg; + } + if(packCmp) { + READ_PACK_VAL(packCmp, opts->packImgCompr); + } + + opts->zlibMethod = WRITERCO_ZLIB_METHOD_7Z; + opts->zlibLevel = 3; + if(zlibMethod) { + if(!strcasecmp(zlibMethod, "default")) opts->zlibMethod = WRITERCO_ZLIB_METHOD_ZDEFAULT; + else if(!strcasecmp(zlibMethod, "filtered")) opts->zlibMethod = WRITERCO_ZLIB_METHOD_ZFILTERED; + else if(!strcasecmp(zlibMethod, "huffman")) opts->zlibMethod = WRITERCO_ZLIB_METHOD_ZHUFFMAN; + else if(!strcasecmp(zlibMethod, "rle")) opts->zlibMethod = WRITERCO_ZLIB_METHOD_ZRLE; + else if(!strcasecmp(zlibMethod, "fixed")) opts->zlibMethod = WRITERCO_ZLIB_METHOD_ZFIXED; + else if(!strcasecmp(zlibMethod, "7z")) opts->zlibMethod = WRITERCO_ZLIB_METHOD_7Z; + else { + error("Unknown zlib method '%s'.", zlibMethod); + exit(RETERR_SYNTAX); + } + } + // stick in default compression level if using zlib + if(opts->zlibMethod >= WRITERCO_ZLIB_METHOD_ZDEFAULT && opts->zlibMethod <= WRITERCO_ZLIB_METHOD_ZRLE) + opts->zlibLevel = 9; + if(zlibLevel != -1) { + if(opts->zlibMethod >= WRITERCO_ZLIB_METHOD_ZDEFAULT && opts->zlibMethod <= WRITERCO_ZLIB_METHOD_ZRLE) { + if(zlibLevel < 0 || zlibLevel > 9) { + error("zlib compression level must be between 0 and 9."); + exit(RETERR_SYNTAX); + } + } else if(opts->zlibMethod == WRITERCO_ZLIB_METHOD_7Z) { + if(zlibLevel < 1 || zlibLevel > 4) { + error("zlib-7z compression level must be between 1 and 4."); + exit(RETERR_SYNTAX); + } + } + + opts->zlibLevel = zlibLevel; + } + + opts->rlzMode = -1; + if(rlzMode != -1) { + if(rlzMode < 0 || rlzMode > 31) { + error("RLZ compression mode must be between 0 and 31."); + exit(RETERR_SYNTAX); + } + opts->rlzMode = rlzMode; + } +} + +int main_compile(void) { + if(app_argc < 4) MAIN_INV_CMD_SYNTAX; + + writerco_options opts; + + char* sXmlFile = NULL; + char* sRcoFile = NULL; + + Bool sNoConvGim = FALSE; + Bool sNoConvVag = FALSE; + Bool sNoConvVsmx = FALSE; + RcoDumpGimconvOpts gimconvOpts; + + gimconvOpts.cmd = gimconvOpts.extFlags = NULL; + + int i; + // parse options + get_pack_opts(&opts); + retrieve_from_opts(TRUE, 5, + "--no-convgim", "bool", &sNoConvGim, + "--no-convvag", "bool", &sNoConvVag, + "--no-encvsmx", "bool", &sNoConvVsmx, + "--gimconv-cmd", "text", &gimconvOpts.cmd, + "--gimconv-flags", "text", &gimconvOpts.extFlags + ); + + for(i=2; i 1) { + if((int)channels > app_argc-3) { + warning("WAV file contains %d channels, which is more than VAG files specified - will only output the first %d channels.", channels, app_argc-3); + channels = app_argc-3; + } else if((int)channels < app_argc-3) { + warning("WAV file contains %d channels, which is less than VAG files specified.", channels); + } + } + + // trim .vag extension if autogenerating that + if(app_argc-3 == 1 && channels > 1) { + uint nl = strlen(app_argv[3]); + if(nl > MAX_FILENAME_LEN) + app_argv[3][MAX_FILENAME_LEN] = '\0'; + else if(!strcasecmp(app_argv[3] + nl - 4, ".vag")) + app_argv[3][nl-4] = '\0'; + } + + Bool writeStdout = !strcmp(app_argv[3], "-"); + for(i=0; i 4) + warning("Extra arguments ignored."); + + + info("Reading and encoding input..."); + VsmxMem* vm = VsmxEncode(fin); + fclose(fin); + if(vm) { + info("Writing VSMX..."); + writeVSMX(fout, vm); + freeVsmxMem(vm); + fclose(fout); + return 0; + } else { + error("Failed to encode VSMX."); + return RETERR_GENERIC_FAILED; + } +} + + +void retrieve_from_opts(Bool warnUnk, int num, ...) { + int i,j; + va_list ap; + + for(i=2; i -#include -#include -#include - -// chdir, mkdir etc -#ifdef WIN32 /* windows */ -#include -#include -#define makedir _mkdir -#else /* linux */ -#include -#include -#include -#define makedir(s) mkdir(s, 0777); -#endif - - -#include "general.h" -#include "rcomain.h" -#include "xml.h" -#include "strfuncs.h" - -#include "rcodump.h" -#include "vaghandler.h" -#include "vsmx.h" - -Bool exec_gimconv(char* cmd, char* src, char* dest, char* extFlags); - - -Bool dump_resource(char* dest, rRCOEntry* entry, OutputDumpFunc outputfunc, void* outputfuncArg) { - - if(file_exists(dest)) { - warning("File '%s' already exists.", dest); - return FALSE; - } - - uint len=0; - uint8* bufferMid = (uint8*)read_resource(entry, &len); - if(!bufferMid) return FALSE; - - if(!len) { - free(bufferMid); - return FALSE; - } - - if(len != entry->srcLenUnpacked) - warning("Extracted resource size for does not match specified length."); - - Bool ret = outputfunc(dest, (void*)bufferMid, entry, outputfuncArg); - free(bufferMid); - return ret; -} - -Bool dump_output_data(char* dest, void* buf, rRCOEntry* entry, void* arg) { - FILE* fp = openwrite(dest); - if(fp) { - filewrite(fp, buf, entry->srcLenUnpacked); - fclose(fp); - return TRUE; - } - warning("Unable to open to file '%s'.", dest); - return FALSE; -} - -Bool dump_output_wav(char* dest, void* buf, rRCOEntry* entry, void* arg) { - int i; - rRCOSoundEntry* rse = ((rRCOSoundEntry*)entry->extra); - - void** vagData = (void**)malloc(rse->channels * sizeof(void*)); - int* vagLen = (int*)malloc(rse->channels * sizeof(int)); - - Bool ret; - - for(i=0; ichannels; i++) { - vagLen[i] = rse->channelData[i*2]; - vagData[i] = (void*)((char*)buf + rse->channelData[i*2+1]); - } - - ret = vag2wav(dest, rse->channels, vagLen, vagData); - free(vagData); - free(vagLen); - return ret; -} - -Bool dump_output_gimconv(char* dest, void* buf, rRCOEntry* entry, void* arg) { -#ifdef WIN32 - char tmpName[255]; - static uint successes = 0, failures = 0; - RcoDumpGimconvOpts* opts = (RcoDumpGimconvOpts*)arg; - Bool ret; - - if(failures <= 5 || successes > 0) { - - get_temp_fname(tmpName, "gim"); - - FILE* fp = fopen(tmpName, "wb"); - if(!fp) return FALSE; - - // try to apply gim patch - if(entry->srcLenUnpacked > 0x28) { - // dirty code - we'll leave here, since the internal converter will have nicer code :P - uint32* i32; - Bool es = FALSE; - i32 = (uint32*)buf; - if(*i32 == 0x4D49472E) es = TRUE; // .GIM - - if(*i32 == 0x2E47494D || *i32 == 0x4D49472E) { - uint16 i, i2; - i = *(uint16*)((char*)buf + 0x10); - i2 = *(uint16*)((char*)buf + 0x20); - if(es) { - i = ENDIAN_SWAP(i); - i2 = ENDIAN_SWAP(i2); - } - if(i == 2 && i2 == 3) { - uint32 sz = *(uint32*)((char*)buf + 0x14), sz2; - i32 = (uint32*)((char*)buf + 0x24); - sz2 = *i32; - if(es) { - sz = ENDIAN_SWAP(sz); - sz2 = ENDIAN_SWAP(sz2); - } - if(sz-0x10 != sz2) { - info("Note: Applied GIM patch when using GimConv to dump '%s'.", dest); - sz2 = sz-0x10; - if(es) sz2 = ENDIAN_SWAP(sz2); - *i32 = sz2; - } - } - } - } - - filewrite(fp, buf, entry->srcLenUnpacked); - fclose(fp); - - - ret = exec_gimconv(opts->cmd, tmpName, dest, opts->extFlags); - remove(tmpName); - - if(!ret) { - warning("gimconv failed to process GIM. Resource being dumped as GIM instead."); - failures++; - if(failures > 5 && successes == 0) { - warning("GimConv failed too many times without success - disabling GimConv (GIMs will not be converted)."); - } - } - } - else - ret = FALSE; - - if(!ret) { - char tmpDest[260]; - strcpy(tmpDest, dest); - char* dot = strrchr(tmpDest, '.'); - if(!dot) dot = tmpDest + strlen(tmpDest); - strcpy(dot, ".gim"); - - return dump_output_data(tmpDest, buf, entry, NULL); - } else - successes++; - return ret; -#else - return FALSE; -#endif -} - -Bool dump_output_vsmxdec(char* dest, void* buf, rRCOEntry* entry, void* arg) { - VsmxMem* vm = readVSMXMem(buf); - FILE* fp = openwrite(dest); - if(fp) { - if(VsmxDecode(vm, fp)) { - warning("Unable to decode VSMX. Dumping as data instead."); - freeVsmxMem(vm); - fclose(fp); - return dump_output_data(dest, buf, entry, arg); - } - freeVsmxMem(vm); - fclose(fp); - return TRUE; - } - warning("Unable to open to file '%s'.", dest); - return FALSE; -} - -void dump_resources(char* labels, rRCOEntry* parent, const RcoTableMap extMap, char* pathPrefix, void* outputFilterArg) { - // TODO: remove text crap from this function - if(!parent || !parent->numSubentries) return; - - char fullOutName[MAX_FILENAME_LEN]; - uint extMapLen = 0; - char dat[5] = "dat"; - - strcpy(fullOutName, pathPrefix); - char* outName = fullOutName + strlen(pathPrefix); - - while(extMap[extMapLen][0]) extMapLen++; - - #define MAX_LABEL_LEN 216 - uint i; - rRCOEntry* entry; - for(entry=parent->firstChild; entry; entry=entry->next) { - char* ext = (char*)dat; - if(entry->id == RCO_TABLE_IMG || entry->id == RCO_TABLE_MODEL) { - uint fmt = ((rRCOImgModelEntry*)entry->extra)->format; - if(fmt == RCO_IMG_GIM && outputFilterArg) { - ext = ((RcoDumpGimconvOpts*)outputFilterArg)->ext; - } - else if(fmt <= extMapLen) - ext = (char*)extMap[fmt]; - } else if(entry->id == RCO_TABLE_SOUND) { - rRCOSoundEntry* rse = (rRCOSoundEntry*)entry->extra; - if(rse->format == RCO_SOUND_VAG) { - if(outputFilterArg) - strcpy(ext, "wav"); - else - strcpy(ext, "vag"); - } - } - - - char* label = get_label_from_offset(labels, entry->labelOffset); - - uint len = strlen(label); - if(len > MAX_LABEL_LEN) len = MAX_LABEL_LEN; - memcpy(outName, label, len); - outName[len] = '.'; - - OutputDumpFunc of = dump_output_data; - if(outputFilterArg) { - if(entry->id == RCO_TABLE_SOUND && ((rRCOSoundEntry*)entry->extra)->format == RCO_SOUND_VAG) - of = dump_output_wav; - else if(entry->id == RCO_TABLE_IMG && ((rRCOImgModelEntry*)entry->extra)->format == RCO_IMG_GIM) - of = dump_output_gimconv; - } - - info("Dumping resource '%s'...", label); - - if(entry->id == RCO_TABLE_SOUND && !outputFilterArg) { - rRCOSoundEntry* rse = (rRCOSoundEntry*)entry->extra; - char soundSetSrc[MAX_FILENAME_LEN] = "\0"; - for(i=0; ichannels; i++) { - outName[len+1] = '\0'; - if(!soundSetSrc[0]) { - strcpy(soundSetSrc, fullOutName); - strcpy(soundSetSrc + strlen(soundSetSrc), "ch*.vag"); - } - - sprintf(outName + len +1, "ch%d.", i); - //len = strlen(outName) -1; - - strcpy(outName + strlen(outName), ext); - - uint origAddr = entry->srcAddr, origLen = entry->srcLen, origLenUnpacked = entry->srcLenUnpacked; - entry->srcLen = entry->srcLenUnpacked = ((rRCOSoundEntry*)entry->extra)->channelData[i*2]; - entry->srcAddr += ((rRCOSoundEntry*)entry->extra)->channelData[i*2+1]; - if(!dump_resource(fullOutName, entry, of, outputFilterArg)) - warning("Unable to dump resource '%s'.", label); - entry->srcAddr = origAddr; - entry->srcLen = origLen; - entry->srcLenUnpacked = origLenUnpacked; - } - strcpy(entry->srcFile, soundSetSrc); - } else { - strcpy(outName + len +1, ext); - if(!dump_resource(fullOutName, entry, of, outputFilterArg)) - warning("Unable to dump resource '%s'.", label); - - // *new* fix entry here as well - strcpy(entry->srcFile, fullOutName); - entry->srcLenUnpacked = filesize(fullOutName); // get around issues of conversions changing filesize :P (dirty, but works!) - } - - /* - for(j=0; jid == RCO_TABLE_SOUND) { - // do something dirty - hack the srcFile here... - outName[len+1] = '\0'; - strcpy(soundSetSrc, fullOutName); - strcpy(soundSetSrc + strlen(soundSetSrc), "ch*.vag"); - - sprintf(outName + len +1, "ch%d.", j); - len = strlen(outName) -1; - } - strcpy(outName + len +1, ext); - - // trick dump_resource() into doing what we want it to by fiddling with stuff - uint origAddr = entry->srcAddr, origLen = entry->srcLen, origLenUnpacked = entry->srcLenUnpacked; - if(entry->id == RCO_TABLE_SOUND) { - entry->srcLen = entry->srcLenUnpacked = ((rRCOSoundEntry*)entry->extra)->channelData[j*2]; - entry->srcAddr += ((rRCOSoundEntry*)entry->extra)->channelData[j*2+1]; - } - - if((fp = fopen(fullOutName, "wb"))) { - if(!dump_resource(fp, entry)) - warning("Unable to dump resource '%s'.", labels + entry->labelOffset); - fclose(fp); - } else - warning("Unable to write to file '%s'.", fullOutName); - - entry->srcAddr = origAddr; - entry->srcLen = origLen; - entry->srcLenUnpacked = origLenUnpacked; - - // *new* fix entry here as well - if(entry->id != RCO_TABLE_SOUND) - strcpy(entry->srcFile, fullOutName); - } - */ - entry->srcAddr = 0; - entry->srcLen = entry->srcLenUnpacked; - entry->srcCompression = RCO_DATA_COMPRESSION_NONE; - } -} - -void dump_text_resources(char* labels, rRCOEntry* parent, Bool writeHeader, char* pathPrefix, Bool bWriteXML) { - if(!parent || !parent->numSubentries) return; - - FILE* fp; - char fullOutName[MAX_FILENAME_LEN]; - - - #define MAX_LABEL_LEN 216 - uint i; - rRCOEntry* entry; - for(entry=parent->firstChild; entry; entry=entry->next) { - // read & decompress text data - char* textBuffer; - rRCOTextEntry* textEntry = ((rRCOTextEntry*)entry->extra); - if(!(fp = fopen(entry->srcFile, "rb"))) return; - if(entry->srcAddr) fseek(fp, entry->srcAddr, SEEK_SET); - char* inBuffer = (char*)malloc(entry->srcLen); - fileread(fp, inBuffer, entry->srcLen); - fclose(fp); - - strcpy(fullOutName, pathPrefix); - char* outName = fullOutName + strlen(pathPrefix); - rcoxml_int_to_text(textEntry->lang, RCOXML_TABLE_TEXT_LANG, outName); - // dirty, but since stuff is here conveniently, update srcFile - info("Dumping %s text entries...", outName); - if(bWriteXML) { - sprintf(entry->srcFile, "%s.xml", fullOutName); - outName += strlen(outName); - strcpy(outName, ".xml"); - } - else { - makedir(fullOutName); - sprintf(entry->srcFile, "%s%c*.txt", fullOutName, DIR_SEPARATOR); - outName += strlen(outName); - outName[0] = DIR_SEPARATOR; - outName++; - } - - if(entry->srcCompression) { - textBuffer = (char*)malloc(entry->srcLenUnpacked); - if(entry->srcCompression == RCO_DATA_COMPRESSION_ZLIB) { - int uRet = zlib_uncompress(textBuffer, entry->srcLenUnpacked, inBuffer, entry->srcLen); - if(uRet != Z_OK && uRet != Z_DATA_ERROR) return; - if(uRet == Z_DATA_ERROR) - warning("Encountered 'data error' when decompressing text resource."); - } - free(inBuffer); - } else - textBuffer = inBuffer; - - iconv_t ic = NULL; // prevent gcc warning - if(bWriteXML) { - if(!(fp = fopen(fullOutName, "wb"))) { - warning("Unable to write to file '%s'.", fullOutName); - continue; - } - // put UTF-8 header - fputc(0xEF, fp); - fputc(0xBB, fp); - fputc(0xBF, fp); - fputs("\n", fp); - fprintf(fp, "\n", APPNAME_VER); - fputs("\n", fp); - - // set up iconv - char icSrctype[8]; - make_iconv_charset(icSrctype, textEntry->format, parent->rco->eSwap); - ic = iconv_open("utf-8", icSrctype); - } - - for(i=0; inumIndexes; i++) { - RCOTextIndex* idx = &(textEntry->indexes[i]); - uint len = strlen(get_label_from_offset(labels, idx->labelOffset)); - uint dataLen = 0; - if(len > MAX_LABEL_LEN) len = MAX_LABEL_LEN; - - if(idx->length) { - uint charWidth = RCO_TEXT_FMT_CHARWIDTH(textEntry->format); - dataLen = idx->length; - if(idx->length >= charWidth) { - int j; - for(j=0; j<(int)charWidth; j++) { - if(textBuffer[idx->offset + idx->length - (j+1)]) break; - } - if(j == (int)charWidth) - dataLen -= charWidth; - } - } - - if(!bWriteXML) { - memcpy(outName, get_label_from_offset(labels, idx->labelOffset), len); - strcpy(outName + len, ".txt"); - - if(!(fp = fopen(fullOutName, "wb"))) { - warning("Unable to write to file '%s'.", fullOutName); - continue; - } - if(writeHeader) { - if(textEntry->format == RCO_TEXT_FMT_UTF32) { - uint32 bom = UTF32_BOM; - if(parent->rco->eSwap) bom = ENDIAN_SWAP(bom); - filewrite(fp, &bom, sizeof(bom)); - } else if(textEntry->format == RCO_TEXT_FMT_UTF8) { - uint32 bom = UTF8_BOM; - filewrite(fp, &bom, 3); - } else { - uint16 bom = UTF16_BOM; - if(parent->rco->eSwap) bom = ENDIAN_SWAP(bom); - filewrite(fp, &bom, sizeof(bom)); - } - } - if(dataLen) - filewrite(fp, textBuffer + idx->offset, dataLen); - - fclose(fp); - } - else { - char* bufIn = textBuffer + idx->offset; - Bool useCdata = (memchr(bufIn, '<', dataLen) || memchr(bufIn, '>', dataLen) || memchr(bufIn, '&', dataLen)); - fprintf(fp, "\t", get_label_from_offset(labels, idx->labelOffset)); - if(useCdata) - fputs("format == RCO_TEXT_FMT_UTF32) { - uint32 bom = UTF32_BOM; - if(parent->rco->eSwap) bom = ENDIAN_SWAP(bom); - number = sizeof(bom); - unicodePtr = (char*)&bom; - iconv(ic, (const char**)(&unicodePtr), (size_t*)(&number), &bufOut, (size_t*)(&outBufLen)); - } else if(textEntry->format == RCO_TEXT_FMT_UTF8) { - uint32 bom = UTF8_BOM; - number = 3; - unicodePtr = (char*)&bom; - iconv(ic, (const char**)(&unicodePtr), (size_t*)(&number), &bufOut, (size_t*)(&outBufLen)); - } else { - uint16 bom = UTF16_BOM; - if(parent->rco->eSwap) bom = ENDIAN_SWAP(bom); - number = sizeof(bom); - unicodePtr = (char*)&bom; - iconv(ic, (const char**)(&unicodePtr), (size_t*)(&number), &bufOut, (size_t*)(&outBufLen)); - } - } */ - uint nullsStripped = 0; - while(dataLen) { - iconv(ic, (&bufIn), (size_t*)(&dataLen), &bufOut, (size_t*)(&outBufLen)); - if(buf == bufOut) { - warning("iconv failed when converting resource '%s'.", get_label_from_offset(labels, idx->labelOffset)); - break; - } - // strip null characters - UTF-8's encoding scheme doesn't contain null bytes in itself, so this simple solution works nicely :) - char* bufTmp; - for(bufTmp=buf; bufTmplabelOffset)); - - if(useCdata) - fputs("]]>", fp); - fputs("\n", fp); - } - - } - if(bWriteXML) { - fputs("\n", fp); - fclose(fp); - iconv_close(ic); - } - free(textBuffer); - } -} - -void compile_gimconv_map(rRCOFile* rco, rRCOEntry* entry, void* arg) { - if(entry->id != RCO_TABLE_IMG || entry->type != 1 || ((rRCOImgModelEntry*)entry->extra)->format != RCO_IMG_GIM) return; - - if(entry->srcFile[0] && entry->srcAddr == 0 && entry->srcLen == filesize(entry->srcFile) && entry->srcCompression == RCO_DATA_COMPRESSION_NONE && strcasecmp(entry->srcFile + strlen(entry->srcFile) - 4, ".gim")) { - info("Converting '%s' to GIM...", get_label_from_offset(rco->labels, entry->labelOffset)); - RcoDumpGimconvOpts* opts = (RcoDumpGimconvOpts*)arg; - char tmpName[255]; - get_temp_fname(tmpName, "gim"); - char gimconvSwpCmd[255] = "-ps3"; - char* extFlags = opts->extFlags; - if(rco->eSwap) { - extFlags = gimconvSwpCmd; - if(opts->extFlags) { - strcat(extFlags, " "); - strcat(extFlags, gimconvSwpCmd); - } - } - if(exec_gimconv(opts->cmd, entry->srcFile, tmpName, extFlags)) { - FILE* fp = fopen(tmpName, "rb"); - if(fp) { - entry->srcLen = entry->srcLenUnpacked = filesize(tmpName); - entry->srcFile[0] = '\0'; - entry->srcBuffer = malloc(entry->srcLen); - entry->srcCompression = RCO_DATA_COMPRESSION_NONE; - fileread(fp, entry->srcBuffer, entry->srcLen); - fclose(fp); - remove(tmpName); - } - else - warning("Open '%s' failed.", tmpName); - } else - warning("Failed to convert resource '%s' to GIM.", get_label_from_offset(rco->labels, entry->labelOffset)); - } -} - -void compile_vagconv_map(rRCOFile* rco, rRCOEntry* entry, void* arg) { - rRCOSoundEntry* rse = (rRCOSoundEntry*)entry->extra; - if(entry->id != RCO_TABLE_SOUND || entry->type != 1 || rse->format != RCO_SOUND_VAG) return; - - if(entry->srcFile[0] && entry->srcAddr == 0 && entry->srcCompression == RCO_DATA_COMPRESSION_NONE && !strcasecmp(entry->srcFile + strlen(entry->srcFile) - 4, ".wav")) { - uint len; - int i; - - rse->channels = wav2vag(entry->srcFile, &len, &entry->srcBuffer, ""); - if(!rse->channels) { - error("WAV->VAG conversion failed for '%s'", get_label_from_offset(rco->labels, entry->labelOffset)); - return; - } - entry->srcFile[0] = '\0'; - entry->srcLen = entry->srcLenUnpacked = len * rse->channels; - entry->srcCompression = RCO_DATA_COMPRESSION_NONE; - - if(!rse->channelData) - rse->channelData = (uint32*)malloc(rse->channels * sizeof(uint32)*2); - for(i=0; ichannels; i++) { - rse->channelData[i*2] = len; - rse->channelData[i*2+1] = len*i; - } - } -} - -void compile_vsmxconv_map(rRCOFile* rco, rRCOEntry* entry, void* arg) { - if(entry->id != RCO_TABLE_VSMX || entry->type != 1) return; - if(entry->srcFile[0] && entry->srcAddr == 0 && entry->srcCompression == RCO_DATA_COMPRESSION_NONE && !strcasecmp(entry->srcFile + strlen(entry->srcFile) - 4, ".txt")) { - FILE* fin = fopen(entry->srcFile, "rb"); - if(fin) { - VsmxMem* vm = VsmxEncode(fin); - if(vm) { - entry->srcBuffer = writeVSMXMem(&entry->srcLen, vm); - entry->srcLenUnpacked = entry->srcLen; - entry->srcFile[0] = '\0'; - entry->srcCompression = RCO_DATA_COMPRESSION_NONE; - freeVsmxMem(vm); - } else - error("Could not encode VSMX!"); - fclose(fin); - } - } -} - -void compile_wavcheck_map(rRCOFile* rco, rRCOEntry* entry, void* arg) { - if(entry->id == RCO_TABLE_SOUND && entry->type == 1 && ((rRCOSoundEntry*)entry->extra)->channels == 0) - warning("Unable to determine channel data for sound '%s'.", get_label_from_offset(rco->labels, entry->labelOffset)); -} - -Bool exec_gimconv(char* cmd, char* src, char* dest, char* extFlags) { -#ifdef WIN32 - char gimconvCmd[1200]; - int ret; - - if(cmd) - sprintf(gimconvCmd, "\"%s\"", cmd); - else - strcpy(gimconvCmd, "gimconv"); - - if(extFlags) { - sprintf(gimconvCmd + strlen(gimconvCmd), " %s", extFlags); - } - - // gimconv is screwy and sometimes prepends a '/' to our destination file - // so we do our feeble attempt to determine if it is a relative path - if(dest[0] == '\\' || dest[0] == '/' || dest[0] == '.' || dest[1] == ':') // should handle "\file", "\\network\blah" and "C:\file" notations; gimconv seems to also escape if first char == '.' - sprintf(gimconvCmd + strlen(gimconvCmd), " \"%s\" -o \"%s\"", src, dest); - else - sprintf(gimconvCmd + strlen(gimconvCmd), " \"%s\" -o \"./%s\"", src, dest); - - { - STARTUPINFO si; - PROCESS_INFORMATION pi; - DWORD exitCode; - HANDLE hNull = CreateFileA("NUL", FILE_APPEND_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - si.dwFlags = STARTF_USESTDHANDLES; - si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - si.hStdError = GetStdHandle(STD_ERROR_HANDLE); - si.hStdOutput = hNull; - ZeroMemory(&pi, sizeof(pi)); - - if(!CreateProcessA(NULL, gimconvCmd, NULL, NULL, FALSE, 0, NULL, NULL, (LPSTARTUPINFOA)&si, &pi)) - return FALSE; - - if(WaitForSingleObject(pi.hProcess, 5000) == WAIT_TIMEOUT) { - TerminateProcess(pi.hProcess, 1); - exitCode = 1; //failed - warning("GimConv wait time exceeded - process automatically terminated."); - } else - GetExitCodeProcess(pi.hProcess, (LPDWORD)&exitCode); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - CloseHandle(hNull); - ret = (int)exitCode; - } - - //ret = system(gimconvCmd); - return (ret == 0); -#else - return FALSE; -#endif -} - + +#include +#include +#include +#include + +// chdir, mkdir etc +#ifdef WIN32 /* windows */ +#include +#include +#define makedir _mkdir +#else /* linux */ +#include +#include +#include +#define makedir(s) mkdir(s, 0777); +#endif + + +#include "general.h" +#include "rcomain.h" +#include "xml.h" +#include "strfuncs.h" + +#include "rcodump.h" +#include "vaghandler.h" +#include "vsmx.h" + +Bool exec_gimconv(char* cmd, char* src, char* dest, char* extFlags); + + +Bool dump_resource(char* dest, rRCOEntry* entry, OutputDumpFunc outputfunc, void* outputfuncArg) { + + if(file_exists(dest)) { + warning("File '%s' already exists.", dest); + return FALSE; + } + + uint len=0; + uint8* bufferMid = (uint8*)read_resource(entry, &len); + if(!bufferMid) return FALSE; + + if(!len) { + free(bufferMid); + return FALSE; + } + + if(len != entry->srcLenUnpacked) + warning("Extracted resource size for does not match specified length."); + + Bool ret = outputfunc(dest, (void*)bufferMid, entry, outputfuncArg); + free(bufferMid); + return ret; +} + +Bool dump_output_data(char* dest, void* buf, rRCOEntry* entry, void* arg) { + FILE* fp = openwrite(dest); + if(fp) { + filewrite(fp, buf, entry->srcLenUnpacked); + fclose(fp); + return TRUE; + } + warning("Unable to open to file '%s'.", dest); + return FALSE; +} + +Bool dump_output_wav(char* dest, void* buf, rRCOEntry* entry, void* arg) { + int i; + rRCOSoundEntry* rse = ((rRCOSoundEntry*)entry->extra); + + void** vagData = (void**)malloc(rse->channels * sizeof(void*)); + int* vagLen = (int*)malloc(rse->channels * sizeof(int)); + + Bool ret; + + for(i=0; ichannels; i++) { + vagLen[i] = rse->channelData[i*2]; + vagData[i] = (void*)((char*)buf + rse->channelData[i*2+1]); + } + + ret = vag2wav(dest, rse->channels, vagLen, vagData); + free(vagData); + free(vagLen); + return ret; +} + +Bool dump_output_gimconv(char* dest, void* buf, rRCOEntry* entry, void* arg) { +#ifdef WIN32 + char tmpName[255]; + static uint successes = 0, failures = 0; + RcoDumpGimconvOpts* opts = (RcoDumpGimconvOpts*)arg; + Bool ret; + + if(failures <= 5 || successes > 0) { + + get_temp_fname(tmpName, "gim"); + + FILE* fp = fopen(tmpName, "wb"); + if(!fp) return FALSE; + + // try to apply gim patch + if(entry->srcLenUnpacked > 0x28) { + // dirty code - we'll leave here, since the internal converter will have nicer code :P + uint32* i32; + Bool es = FALSE; + i32 = (uint32*)buf; + if(*i32 == 0x4D49472E) es = TRUE; // .GIM + + if(*i32 == 0x2E47494D || *i32 == 0x4D49472E) { + uint16 i, i2; + i = *(uint16*)((char*)buf + 0x10); + i2 = *(uint16*)((char*)buf + 0x20); + if(es) { + i = ENDIAN_SWAP(i); + i2 = ENDIAN_SWAP(i2); + } + if(i == 2 && i2 == 3) { + uint32 sz = *(uint32*)((char*)buf + 0x14), sz2; + i32 = (uint32*)((char*)buf + 0x24); + sz2 = *i32; + if(es) { + sz = ENDIAN_SWAP(sz); + sz2 = ENDIAN_SWAP(sz2); + } + if(sz-0x10 != sz2) { + info("Note: Applied GIM patch when using GimConv to dump '%s'.", dest); + sz2 = sz-0x10; + if(es) sz2 = ENDIAN_SWAP(sz2); + *i32 = sz2; + } + } + } + } + + filewrite(fp, buf, entry->srcLenUnpacked); + fclose(fp); + + + ret = exec_gimconv(opts->cmd, tmpName, dest, opts->extFlags); + remove(tmpName); + + if(!ret) { + warning("gimconv failed to process GIM. Resource being dumped as GIM instead."); + failures++; + if(failures > 5 && successes == 0) { + warning("GimConv failed too many times without success - disabling GimConv (GIMs will not be converted)."); + } + } + } + else + ret = FALSE; + + if(!ret) { + char tmpDest[260]; + strcpy(tmpDest, dest); + char* dot = strrchr(tmpDest, '.'); + if(!dot) dot = tmpDest + strlen(tmpDest); + strcpy(dot, ".gim"); + + return dump_output_data(tmpDest, buf, entry, NULL); + } else + successes++; + return ret; +#else + return FALSE; +#endif +} + +Bool dump_output_vsmxdec(char* dest, void* buf, rRCOEntry* entry, void* arg) { + VsmxMem* vm = readVSMXMem(buf); + FILE* fp = openwrite(dest); + if(fp) { + if(VsmxDecode(vm, fp)) { + warning("Unable to decode VSMX. Dumping as data instead."); + freeVsmxMem(vm); + fclose(fp); + return dump_output_data(dest, buf, entry, arg); + } + freeVsmxMem(vm); + fclose(fp); + return TRUE; + } + warning("Unable to open to file '%s'.", dest); + return FALSE; +} + +void dump_resources(char* labels, rRCOEntry* parent, const RcoTableMap extMap, char* pathPrefix, void* outputFilterArg) { + // TODO: remove text crap from this function + if(!parent || !parent->numSubentries) return; + + char fullOutName[MAX_FILENAME_LEN]; + uint extMapLen = 0; + char dat[5] = "dat"; + + strcpy(fullOutName, pathPrefix); + char* outName = fullOutName + strlen(pathPrefix); + + while(extMap[extMapLen][0]) extMapLen++; + + #define MAX_LABEL_LEN 216 + uint i; + rRCOEntry* entry; + for(entry=parent->firstChild; entry; entry=entry->next) { + char* ext = (char*)dat; + if(entry->id == RCO_TABLE_IMG || entry->id == RCO_TABLE_MODEL) { + uint fmt = ((rRCOImgModelEntry*)entry->extra)->format; + if(fmt == RCO_IMG_GIM && outputFilterArg) { + ext = ((RcoDumpGimconvOpts*)outputFilterArg)->ext; + } + else if(fmt <= extMapLen) + ext = (char*)extMap[fmt]; + } else if(entry->id == RCO_TABLE_SOUND) { + rRCOSoundEntry* rse = (rRCOSoundEntry*)entry->extra; + if(rse->format == RCO_SOUND_VAG) { + if(outputFilterArg) + strcpy(ext, "wav"); + else + strcpy(ext, "vag"); + } + } + + + char* label = get_label_from_offset(labels, entry->labelOffset); + + uint len = strlen(label); + if(len > MAX_LABEL_LEN) len = MAX_LABEL_LEN; + memcpy(outName, label, len); + outName[len] = '.'; + + OutputDumpFunc of = dump_output_data; + if(outputFilterArg) { + if(entry->id == RCO_TABLE_SOUND && ((rRCOSoundEntry*)entry->extra)->format == RCO_SOUND_VAG) + of = dump_output_wav; + else if(entry->id == RCO_TABLE_IMG && ((rRCOImgModelEntry*)entry->extra)->format == RCO_IMG_GIM) + of = dump_output_gimconv; + } + + info("Dumping resource '%s'...", label); + + if(entry->id == RCO_TABLE_SOUND && !outputFilterArg) { + rRCOSoundEntry* rse = (rRCOSoundEntry*)entry->extra; + char soundSetSrc[MAX_FILENAME_LEN] = "\0"; + for(i=0; ichannels; i++) { + outName[len+1] = '\0'; + if(!soundSetSrc[0]) { + strcpy(soundSetSrc, fullOutName); + strcpy(soundSetSrc + strlen(soundSetSrc), "ch*.vag"); + } + + sprintf(outName + len +1, "ch%d.", i); + //len = strlen(outName) -1; + + strcpy(outName + strlen(outName), ext); + + uint origAddr = entry->srcAddr, origLen = entry->srcLen, origLenUnpacked = entry->srcLenUnpacked; + entry->srcLen = entry->srcLenUnpacked = ((rRCOSoundEntry*)entry->extra)->channelData[i*2]; + entry->srcAddr += ((rRCOSoundEntry*)entry->extra)->channelData[i*2+1]; + if(!dump_resource(fullOutName, entry, of, outputFilterArg)) + warning("Unable to dump resource '%s'.", label); + entry->srcAddr = origAddr; + entry->srcLen = origLen; + entry->srcLenUnpacked = origLenUnpacked; + } + strcpy(entry->srcFile, soundSetSrc); + } else { + strcpy(outName + len +1, ext); + if(!dump_resource(fullOutName, entry, of, outputFilterArg)) + warning("Unable to dump resource '%s'.", label); + + // *new* fix entry here as well + strcpy(entry->srcFile, fullOutName); + entry->srcLenUnpacked = filesize(fullOutName); // get around issues of conversions changing filesize :P (dirty, but works!) + } + + /* + for(j=0; jid == RCO_TABLE_SOUND) { + // do something dirty - hack the srcFile here... + outName[len+1] = '\0'; + strcpy(soundSetSrc, fullOutName); + strcpy(soundSetSrc + strlen(soundSetSrc), "ch*.vag"); + + sprintf(outName + len +1, "ch%d.", j); + len = strlen(outName) -1; + } + strcpy(outName + len +1, ext); + + // trick dump_resource() into doing what we want it to by fiddling with stuff + uint origAddr = entry->srcAddr, origLen = entry->srcLen, origLenUnpacked = entry->srcLenUnpacked; + if(entry->id == RCO_TABLE_SOUND) { + entry->srcLen = entry->srcLenUnpacked = ((rRCOSoundEntry*)entry->extra)->channelData[j*2]; + entry->srcAddr += ((rRCOSoundEntry*)entry->extra)->channelData[j*2+1]; + } + + if((fp = fopen(fullOutName, "wb"))) { + if(!dump_resource(fp, entry)) + warning("Unable to dump resource '%s'.", labels + entry->labelOffset); + fclose(fp); + } else + warning("Unable to write to file '%s'.", fullOutName); + + entry->srcAddr = origAddr; + entry->srcLen = origLen; + entry->srcLenUnpacked = origLenUnpacked; + + // *new* fix entry here as well + if(entry->id != RCO_TABLE_SOUND) + strcpy(entry->srcFile, fullOutName); + } + */ + entry->srcAddr = 0; + entry->srcLen = entry->srcLenUnpacked; + entry->srcCompression = RCO_DATA_COMPRESSION_NONE; + } +} + +void dump_text_resources(char* labels, rRCOEntry* parent, Bool writeHeader, char* pathPrefix, Bool bWriteXML) { + if(!parent || !parent->numSubentries) return; + + FILE* fp; + char fullOutName[MAX_FILENAME_LEN]; + + + #define MAX_LABEL_LEN 216 + uint i; + rRCOEntry* entry; + for(entry=parent->firstChild; entry; entry=entry->next) { + // read & decompress text data + char* textBuffer; + rRCOTextEntry* textEntry = ((rRCOTextEntry*)entry->extra); + if(!(fp = fopen(entry->srcFile, "rb"))) return; + if(entry->srcAddr) fseek(fp, entry->srcAddr, SEEK_SET); + char* inBuffer = (char*)malloc(entry->srcLen); + fileread(fp, inBuffer, entry->srcLen); + fclose(fp); + + strcpy(fullOutName, pathPrefix); + char* outName = fullOutName + strlen(pathPrefix); + rcoxml_int_to_text(textEntry->lang, RCOXML_TABLE_TEXT_LANG, outName); + // dirty, but since stuff is here conveniently, update srcFile + info("Dumping %s text entries...", outName); + if(bWriteXML) { + sprintf(entry->srcFile, "%s.xml", fullOutName); + outName += strlen(outName); + strcpy(outName, ".xml"); + } + else { + makedir(fullOutName); + sprintf(entry->srcFile, "%s%c*.txt", fullOutName, DIR_SEPARATOR); + outName += strlen(outName); + outName[0] = DIR_SEPARATOR; + outName++; + } + + if(entry->srcCompression) { + textBuffer = (char*)malloc(entry->srcLenUnpacked); + if(entry->srcCompression == RCO_DATA_COMPRESSION_ZLIB) { + int uRet = zlib_uncompress(textBuffer, entry->srcLenUnpacked, inBuffer, entry->srcLen); + if(uRet != Z_OK && uRet != Z_DATA_ERROR) return; + if(uRet == Z_DATA_ERROR) + warning("Encountered 'data error' when decompressing text resource."); + } + free(inBuffer); + } else + textBuffer = inBuffer; + + iconv_t ic = NULL; // prevent gcc warning + if(bWriteXML) { + if(!(fp = fopen(fullOutName, "wb"))) { + warning("Unable to write to file '%s'.", fullOutName); + continue; + } + // put UTF-8 header + fputc(0xEF, fp); + fputc(0xBB, fp); + fputc(0xBF, fp); + fputs("\n", fp); + fprintf(fp, "\n", APPNAME_VER); + fputs("\n", fp); + + // set up iconv + char icSrctype[8]; + make_iconv_charset(icSrctype, textEntry->format, parent->rco->eSwap); + ic = iconv_open("utf-8", icSrctype); + } + + for(i=0; inumIndexes; i++) { + RCOTextIndex* idx = &(textEntry->indexes[i]); + uint len = strlen(get_label_from_offset(labels, idx->labelOffset)); + uint dataLen = 0; + if(len > MAX_LABEL_LEN) len = MAX_LABEL_LEN; + + if(idx->length) { + uint charWidth = RCO_TEXT_FMT_CHARWIDTH(textEntry->format); + dataLen = idx->length; + if(idx->length >= charWidth) { + int j; + for(j=0; j<(int)charWidth; j++) { + if(textBuffer[idx->offset + idx->length - (j+1)]) break; + } + if(j == (int)charWidth) + dataLen -= charWidth; + } + } + + if(!bWriteXML) { + memcpy(outName, get_label_from_offset(labels, idx->labelOffset), len); + strcpy(outName + len, ".txt"); + + if(!(fp = fopen(fullOutName, "wb"))) { + warning("Unable to write to file '%s'.", fullOutName); + continue; + } + if(writeHeader) { + if(textEntry->format == RCO_TEXT_FMT_UTF32) { + uint32 bom = UTF32_BOM; + if(parent->rco->eSwap) bom = ENDIAN_SWAP(bom); + filewrite(fp, &bom, sizeof(bom)); + } else if(textEntry->format == RCO_TEXT_FMT_UTF8) { + uint32 bom = UTF8_BOM; + filewrite(fp, &bom, 3); + } else { + uint16 bom = UTF16_BOM; + if(parent->rco->eSwap) bom = ENDIAN_SWAP(bom); + filewrite(fp, &bom, sizeof(bom)); + } + } + if(dataLen) + filewrite(fp, textBuffer + idx->offset, dataLen); + + fclose(fp); + } + else { + char* bufIn = textBuffer + idx->offset; + Bool useCdata = (memchr(bufIn, '<', dataLen) || memchr(bufIn, '>', dataLen) || memchr(bufIn, '&', dataLen)); + fprintf(fp, "\t", get_label_from_offset(labels, idx->labelOffset)); + if(useCdata) + fputs("format == RCO_TEXT_FMT_UTF32) { + uint32 bom = UTF32_BOM; + if(parent->rco->eSwap) bom = ENDIAN_SWAP(bom); + number = sizeof(bom); + unicodePtr = (char*)&bom; + iconv(ic, (const char**)(&unicodePtr), (size_t*)(&number), &bufOut, (size_t*)(&outBufLen)); + } else if(textEntry->format == RCO_TEXT_FMT_UTF8) { + uint32 bom = UTF8_BOM; + number = 3; + unicodePtr = (char*)&bom; + iconv(ic, (const char**)(&unicodePtr), (size_t*)(&number), &bufOut, (size_t*)(&outBufLen)); + } else { + uint16 bom = UTF16_BOM; + if(parent->rco->eSwap) bom = ENDIAN_SWAP(bom); + number = sizeof(bom); + unicodePtr = (char*)&bom; + iconv(ic, (const char**)(&unicodePtr), (size_t*)(&number), &bufOut, (size_t*)(&outBufLen)); + } + } */ + uint nullsStripped = 0; + while(dataLen) { + iconv(ic, (&bufIn), (size_t*)(&dataLen), &bufOut, (size_t*)(&outBufLen)); + if(buf == bufOut) { + warning("iconv failed when converting resource '%s'.", get_label_from_offset(labels, idx->labelOffset)); + break; + } + // strip null characters - UTF-8's encoding scheme doesn't contain null bytes in itself, so this simple solution works nicely :) + char* bufTmp; + for(bufTmp=buf; bufTmplabelOffset)); + + if(useCdata) + fputs("]]>", fp); + fputs("\n", fp); + } + + } + if(bWriteXML) { + fputs("\n", fp); + fclose(fp); + iconv_close(ic); + } + free(textBuffer); + } +} + +void compile_gimconv_map(rRCOFile* rco, rRCOEntry* entry, void* arg) { + if(entry->id != RCO_TABLE_IMG || entry->type != 1 || ((rRCOImgModelEntry*)entry->extra)->format != RCO_IMG_GIM) return; + + if(entry->srcFile[0] && entry->srcAddr == 0 && entry->srcLen == filesize(entry->srcFile) && entry->srcCompression == RCO_DATA_COMPRESSION_NONE && strcasecmp(entry->srcFile + strlen(entry->srcFile) - 4, ".gim")) { + info("Converting '%s' to GIM...", get_label_from_offset(rco->labels, entry->labelOffset)); + RcoDumpGimconvOpts* opts = (RcoDumpGimconvOpts*)arg; + char tmpName[255]; + get_temp_fname(tmpName, "gim"); + char gimconvSwpCmd[255] = "-ps3"; + char* extFlags = opts->extFlags; + if(rco->eSwap) { + extFlags = gimconvSwpCmd; + if(opts->extFlags) { + strcat(extFlags, " "); + strcat(extFlags, gimconvSwpCmd); + } + } + if(exec_gimconv(opts->cmd, entry->srcFile, tmpName, extFlags)) { + FILE* fp = fopen(tmpName, "rb"); + if(fp) { + entry->srcLen = entry->srcLenUnpacked = filesize(tmpName); + entry->srcFile[0] = '\0'; + entry->srcBuffer = malloc(entry->srcLen); + entry->srcCompression = RCO_DATA_COMPRESSION_NONE; + fileread(fp, entry->srcBuffer, entry->srcLen); + fclose(fp); + remove(tmpName); + } + else + warning("Open '%s' failed.", tmpName); + } else + warning("Failed to convert resource '%s' to GIM.", get_label_from_offset(rco->labels, entry->labelOffset)); + } +} + +void compile_vagconv_map(rRCOFile* rco, rRCOEntry* entry, void* arg) { + rRCOSoundEntry* rse = (rRCOSoundEntry*)entry->extra; + if(entry->id != RCO_TABLE_SOUND || entry->type != 1 || rse->format != RCO_SOUND_VAG) return; + + if(entry->srcFile[0] && entry->srcAddr == 0 && entry->srcCompression == RCO_DATA_COMPRESSION_NONE && !strcasecmp(entry->srcFile + strlen(entry->srcFile) - 4, ".wav")) { + uint len; + int i; + + rse->channels = wav2vag(entry->srcFile, &len, &entry->srcBuffer, ""); + if(!rse->channels) { + error("WAV->VAG conversion failed for '%s'", get_label_from_offset(rco->labels, entry->labelOffset)); + return; + } + entry->srcFile[0] = '\0'; + entry->srcLen = entry->srcLenUnpacked = len * rse->channels; + entry->srcCompression = RCO_DATA_COMPRESSION_NONE; + + if(!rse->channelData) + rse->channelData = (uint32*)malloc(rse->channels * sizeof(uint32)*2); + for(i=0; ichannels; i++) { + rse->channelData[i*2] = len; + rse->channelData[i*2+1] = len*i; + } + } +} + +void compile_vsmxconv_map(rRCOFile* rco, rRCOEntry* entry, void* arg) { + if(entry->id != RCO_TABLE_VSMX || entry->type != 1) return; + if(entry->srcFile[0] && entry->srcAddr == 0 && entry->srcCompression == RCO_DATA_COMPRESSION_NONE && !strcasecmp(entry->srcFile + strlen(entry->srcFile) - 4, ".txt")) { + FILE* fin = fopen(entry->srcFile, "rb"); + if(fin) { + VsmxMem* vm = VsmxEncode(fin); + if(vm) { + entry->srcBuffer = writeVSMXMem(&entry->srcLen, vm); + entry->srcLenUnpacked = entry->srcLen; + entry->srcFile[0] = '\0'; + entry->srcCompression = RCO_DATA_COMPRESSION_NONE; + freeVsmxMem(vm); + } else + error("Could not encode VSMX!"); + fclose(fin); + } + } +} + +void compile_wavcheck_map(rRCOFile* rco, rRCOEntry* entry, void* arg) { + if(entry->id == RCO_TABLE_SOUND && entry->type == 1 && ((rRCOSoundEntry*)entry->extra)->channels == 0) + warning("Unable to determine channel data for sound '%s'.", get_label_from_offset(rco->labels, entry->labelOffset)); +} + +Bool exec_gimconv(char* cmd, char* src, char* dest, char* extFlags) { +#ifdef WIN32 + char gimconvCmd[1200]; + int ret; + + if(cmd) + sprintf(gimconvCmd, "\"%s\"", cmd); + else + strcpy(gimconvCmd, "gimconv"); + + if(extFlags) { + sprintf(gimconvCmd + strlen(gimconvCmd), " %s", extFlags); + } + + // gimconv is screwy and sometimes prepends a '/' to our destination file + // so we do our feeble attempt to determine if it is a relative path + if(dest[0] == '\\' || dest[0] == '/' || dest[0] == '.' || dest[1] == ':') // should handle "\file", "\\network\blah" and "C:\file" notations; gimconv seems to also escape if first char == '.' + sprintf(gimconvCmd + strlen(gimconvCmd), " \"%s\" -o \"%s\"", src, dest); + else + sprintf(gimconvCmd + strlen(gimconvCmd), " \"%s\" -o \"./%s\"", src, dest); + + { + STARTUPINFO si; + PROCESS_INFORMATION pi; + DWORD exitCode; + HANDLE hNull = CreateFileA("NUL", FILE_APPEND_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + si.hStdOutput = hNull; + ZeroMemory(&pi, sizeof(pi)); + + if(!CreateProcessA(NULL, gimconvCmd, NULL, NULL, FALSE, 0, NULL, NULL, (LPSTARTUPINFOA)&si, &pi)) + return FALSE; + + if(WaitForSingleObject(pi.hProcess, 5000) == WAIT_TIMEOUT) { + TerminateProcess(pi.hProcess, 1); + exitCode = 1; //failed + warning("GimConv wait time exceeded - process automatically terminated."); + } else + GetExitCodeProcess(pi.hProcess, (LPDWORD)&exitCode); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + CloseHandle(hNull); + ret = (int)exitCode; + } + + //ret = system(gimconvCmd); + return (ret == 0); +#else + return FALSE; +#endif +} + diff --git a/rcodump.h b/rcodump.h index cf570f3..cb7a494 100644 --- a/rcodump.h +++ b/rcodump.h @@ -1,24 +1,24 @@ - -#ifndef __RCODUMP_H__ -#define __RCODUMP_H__ - -typedef Bool(*OutputDumpFunc)(char*,void*,rRCOEntry*,void*); - -typedef struct { - char* cmd; - char* ext; - char* extFlags; -} RcoDumpGimconvOpts; - -Bool dump_resource(char* dest, rRCOEntry* entry, OutputDumpFunc outputfunc, void* outputfuncArg); -void dump_resources(char* labels, rRCOEntry* parent, const RcoTableMap extMap, char* pathPrefix, void* outputFilterArg); -void dump_text_resources(char* labels, rRCOEntry* parent, Bool writeHeader, char* pathPrefix, Bool bWriteXML); -Bool dump_output_data(char* dest, void* buf, rRCOEntry* entry, void* arg); -Bool dump_output_vsmxdec(char* dest, void* buf, rRCOEntry* entry, void* arg); - -void compile_gimconv_map(rRCOFile* rco, rRCOEntry* entry, void* arg); -void compile_vagconv_map(rRCOFile* rco, rRCOEntry* entry, void* arg); -void compile_vsmxconv_map(rRCOFile* rco, rRCOEntry* entry, void* arg); -void compile_wavcheck_map(rRCOFile* rco, rRCOEntry* entry, void* arg); - -#endif + +#ifndef __RCODUMP_H__ +#define __RCODUMP_H__ + +typedef Bool(*OutputDumpFunc)(char*,void*,rRCOEntry*,void*); + +typedef struct { + char* cmd; + char* ext; + char* extFlags; +} RcoDumpGimconvOpts; + +Bool dump_resource(char* dest, rRCOEntry* entry, OutputDumpFunc outputfunc, void* outputfuncArg); +void dump_resources(char* labels, rRCOEntry* parent, const RcoTableMap extMap, char* pathPrefix, void* outputFilterArg); +void dump_text_resources(char* labels, rRCOEntry* parent, Bool writeHeader, char* pathPrefix, Bool bWriteXML); +Bool dump_output_data(char* dest, void* buf, rRCOEntry* entry, void* arg); +Bool dump_output_vsmxdec(char* dest, void* buf, rRCOEntry* entry, void* arg); + +void compile_gimconv_map(rRCOFile* rco, rRCOEntry* entry, void* arg); +void compile_vagconv_map(rRCOFile* rco, rRCOEntry* entry, void* arg); +void compile_vsmxconv_map(rRCOFile* rco, rRCOEntry* entry, void* arg); +void compile_wavcheck_map(rRCOFile* rco, rRCOEntry* entry, void* arg); + +#endif diff --git a/rcofile.h b/rcofile.h index fb9b97f..4c72a6b 100644 --- a/rcofile.h +++ b/rcofile.h @@ -1,257 +1,257 @@ - -#ifndef __RCOFILE_H__ -#define __RCOFILE_H__ - -#include "general.h" - -#define RCO_NULL_PTR 0xFFFFFFFF - -#define RCO_TABLE_MAIN 1 -#define RCO_TABLE_VSMX 2 -#define RCO_TABLE_TEXT 3 -#define RCO_TABLE_IMG 4 -#define RCO_TABLE_MODEL 5 -#define RCO_TABLE_SOUND 6 -#define RCO_TABLE_FONT 7 -#define RCO_TABLE_OBJ 8 -#define RCO_TABLE_ANIM 9 - -#define RCO_SIGNATURE 0x46525000 //.PRF (PSP Resource File?) -PACK_STRUCT(PRFHeader, { - uint32 signature; // RCO_SIGNATURE - uint32 version; - // 0x70 - UMD RCOs (video?), FW1.00 - // 0x71 - UMD RCOs (audio?), FW1.50, FW2.50 - // 0x90 - FW2.60 - // 0x95 - FW2.70, FW2.71 - // 0x96 - FW2.80, FW2.82, FW3.00, FW3.03, FW3.10, FW3.30, FW3.40 - // 0x100 - FW3.50, FW3.52, FW5.00, FW5.55 - uint32 null; - uint32 compression; // upper nibble = compression, lower nibble: 0=flash0 RCOs, 1=UMD RCOs???? - /* - #define RCO_COMPRESSION_NONE 0x00 - // entries for 0x01 ?? - #define RCO_COMPRESSION_ZLIB 0x10 - #define RCO_COMPRESSION_RLZ 0x20 - */ - - // main table pointers - uint32 pMainTable; // type 1 - uint32 pVSMXTable; // type 2 - uint32 pTextTable; // type 3 - uint32 pSoundTable; // type 5 - uint32 pModelTable; // type 6 - uint32 pImgTable; // type 4 - uint32 pUnknown; // always 0xFFFFFFFF - uint32 pFontTable; // type 7 - uint32 pObjTable; // type 8 - uint32 pAnimTable; // type 9 - - // text stuff - uint32 pTextData; // NOTE: this may == pLabelData if lTextData == 0 - uint32 lTextData; - uint32 pLabelData; - uint32 lLabelData; - uint32 pEventData; - uint32 lEventData; - - // pointer data - uint32 pTextPtrs; - uint32 lTextPtrs; - uint32 pImgPtrs; - uint32 lImgPtrs; - uint32 pModelPtrs; - uint32 lModelPtrs; - uint32 pSoundPtrs; - uint32 lSoundPtrs; - uint32 pObjPtrs; - uint32 lObjPtrs; - uint32 pAnimPtrs; - uint32 lAnimPtrs; - - // attached data - uint32 pImgData; - uint32 lImgData; - uint32 pSoundData; - uint32 lSoundData; - uint32 pModelData; - uint32 lModelData; - - // always 0xFFFFFFFF - uint32 unknown[3]; -}); - -PACK_STRUCT(RCOEntry, { - //uint8 type; // main table uses 0x01; may be used as a current entry depth value - //uint8 id; - uint16 typeId; - uint16 blank; - uint32 labelOffset; - uint32 eHeadSize; // = sizeof(RCOEntry) = 0x28 [ only used for entries with extra info (ie not "main" entries) ] - uint32 entrySize; // main tables (main/img etc) uses 0x28 here, or is this the length of current entry (not including subentries)? - // 0x10 - uint32 numSubentries; - uint32 nextEntryOffset; - uint32 prevEntryOffset; // this is usually 0x0 however (does make writing RCOs easier though :P I guess Sony's tools do something similar...) - uint32 parentTblOffset; // offset of this entry from parent table - // 0x20 - uint32 blanks[2]; - -}); - -PACK_STRUCT(RCOVSMXEntry, { - uint32 offset; // always 0x0, so I assume this is an offset - uint32 length; // length of VSMX file -}); - -#define RCO_LANG_JAPANESE 0x0 -#define RCO_LANG_ENGLISH 0x1 -#define RCO_LANG_FRENCH 0x2 -#define RCO_LANG_SPANISH 0x3 -#define RCO_LANG_GERMAN 0x4 -#define RCO_LANG_ITALIAN 0x5 -#define RCO_LANG_DUTCH 0x6 -#define RCO_LANG_PORTUGESE 0x7 -#define RCO_LANG_RUSSIAN 0x8 -#define RCO_LANG_KOREAN 0x9 -#define RCO_LANG_CHINESE_TRADITIOAL 0xA -#define RCO_LANG_CHINESE_SIMPLIFIED 0xB -// the following are just guestimated from the p3tcompiler readme -#define RCO_LANG_FINNISH 0xC -#define RCO_LANG_SWEDISH 0xD -#define RCO_LANG_DANISH 0xE -#define RCO_LANG_NORWEGIAN 0xF -#define RCO_TEXT_FMT_UTF8 0x0 -#define RCO_TEXT_FMT_UTF16 0x1 -#define RCO_TEXT_FMT_UTF32 0x2 -PACK_STRUCT(RCOTextEntry, { - uint16 lang; - uint16 format; - uint32 numIndexes; -}); - -PACK_STRUCT(RCOTextIndex, { - uint32 labelOffset; - uint32 length; - uint32 offset; -}); - -#define RCO_DATA_COMPRESSION_NONE 0x0 -#define RCO_DATA_COMPRESSION_ZLIB 0x1 -#define RCO_DATA_COMPRESSION_RLZ 0x2 - -#define RCO_IMG_PNG 0x0 -#define RCO_IMG_JPEG 0x1 -#define RCO_IMG_TIFF 0x2 -#define RCO_IMG_GIF 0x3 -#define RCO_IMG_BMP 0x4 -#define RCO_IMG_GIM 0x5 -#define RCO_MODEL_GMO 0x0 -PACK_STRUCT(RCOImgModelEntry, { - uint16 format; - uint16 compression; // RCO_DATA_COMPRESSION_* constants - uint32 sizePacked; - uint32 offset; - uint32 sizeUnpacked; // this value doesn't exist if entry isn't compressed -}); -// note, some image/model entries which aren't compressed, don't have the last member -PACK_STRUCT(RCOPS3ImgModelEntry, { // PS3 version of the above - uint16 format; - uint16 compression; // RCO_DATA_COMPRESSION_* constants - uint32 sizePacked; - uint32 offset; - uint32 something; // PS3 RCOs seem to have this extra element - probably something to do with planes/frames?? usually 0x1 - uint32 sizeUnpacked; // this value doesn't exist if entry isn't compressed -}); - - -#define RCO_SOUND_VAG 0x1 -PACK_STRUCT(RCOSoundEntry, { - uint16 format; // 0x01 = VAG - uint16 channels; // 1 or 2 channels - uint32 sizeTotal; - uint32 offset; - // now pairs of size/offset for each channel -}); - - -PACK_STRUCT(RCOFontEntry, { - uint16 format; // 1 - uint16 compression; // 0 - uint32 unknown; // 0 - uint32 unknown2; -}); - -#define RCO_OBJ_TYPE_PAGE 0x1 -#define RCO_OBJ_TYPE_PLANE 0x2 -#define RCO_OBJ_TYPE_BUTTON 0x3 -#define RCO_OBJ_TYPE_XMENU 0x4 -#define RCO_OBJ_TYPE_XMLIST 0x5 -#define RCO_OBJ_TYPE_XLIST 0x6 -#define RCO_OBJ_TYPE_PROGRESS 0x7 -#define RCO_OBJ_TYPE_SCROLL 0x8 -#define RCO_OBJ_TYPE_MLIST 0x9 -#define RCO_OBJ_TYPE_MITEM 0xA - //#define RCO_OBJ_TYPE_0B = 0xB -#define RCO_OBJ_TYPE_XITEM 0xC -#define RCO_OBJ_TYPE_TEXT 0xD -#define RCO_OBJ_TYPE_MODEL 0xE -#define RCO_OBJ_TYPE_SPIN 0xF -#define RCO_OBJ_TYPE_ACTION 0x10 -#define RCO_OBJ_TYPE_ITEMSPIN 0x11 -#define RCO_OBJ_TYPE_GROUP 0x12 -#define RCO_OBJ_TYPE_LLIST 0x13 -#define RCO_OBJ_TYPE_LITEM 0x14 -#define RCO_OBJ_TYPE_EDIT 0x15 -#define RCO_OBJ_TYPE_CLOCK 0x16 -#define RCO_OBJ_TYPE_ILIST 0x17 -#define RCO_OBJ_TYPE_IITEM 0x18 -#define RCO_OBJ_TYPE_ICON 0x19 -#define RCO_OBJ_TYPE_UBUTTON 0x1A - -#define RCO_REF_EVENT 0x400 // relative -#define RCO_REF_TEXT 0x401 // # (0-based) -#define RCO_REF_IMG 0x402 // absolute -#define RCO_REF_MODEL 0x403 // absolute -#define RCO_REF_FONT 0x405 // absolute -#define RCO_REF_OBJ2 0x407 // same as 0x409?? -#define RCO_REF_ANIM 0x408 // absolute -#define RCO_REF_OBJ 0x409 // absolute -#define RCO_REF_NONE 0xFFFF -PACK_STRUCT(RCOReference, { - uint32 type; - uint32 ptr; -}); - - -#define RCO_ANIM_TYPE_POS 0x2 -#define RCO_ANIM_TYPE_COLOUR 0x3 -#define RCO_ANIM_TYPE_ROTATE 0x4 -#define RCO_ANIM_TYPE_SCALE 0x5 -#define RCO_ANIM_TYPE_ALPHA 0x6 -#define RCO_ANIM_TYPE_DELAY 0x7 -#define RCO_ANIM_TYPE_EVENT 0x8 -#define RCO_ANIM_TYPE_LOCK 0x9 // ? -#define RCO_ANIM_TYPE_UNLOCK 0xA // ? -#define RCO_ANIM_TYPE_0B 0xB // only appears on UMD RCOs? - - - - -PACK_STRUCT(HeaderComprInfo, { - uint32 lenPacked; - uint32 lenUnpacked; - uint32 lenLongestText; // length of the longest language's text data (unpacked) -}); - -PACK_STRUCT(TextComprInfo, { - uint16 lang; - uint16 unknown; // always 0x1 - uint32 nextOffset; // = ALIGN_TO_4(sizeof(TextComprInfo) + packedLen); is 0 for last text entry regardless of what actually comes after - uint32 packedLen; - uint32 unpackedLen; -}); - - - -#endif + +#ifndef __RCOFILE_H__ +#define __RCOFILE_H__ + +#include "general.h" + +#define RCO_NULL_PTR 0xFFFFFFFF + +#define RCO_TABLE_MAIN 1 +#define RCO_TABLE_VSMX 2 +#define RCO_TABLE_TEXT 3 +#define RCO_TABLE_IMG 4 +#define RCO_TABLE_MODEL 5 +#define RCO_TABLE_SOUND 6 +#define RCO_TABLE_FONT 7 +#define RCO_TABLE_OBJ 8 +#define RCO_TABLE_ANIM 9 + +#define RCO_SIGNATURE 0x46525000 //.PRF (PSP Resource File?) +PACK_STRUCT(PRFHeader, { + uint32 signature; // RCO_SIGNATURE + uint32 version; + // 0x70 - UMD RCOs (video?), FW1.00 + // 0x71 - UMD RCOs (audio?), FW1.50, FW2.50 + // 0x90 - FW2.60 + // 0x95 - FW2.70, FW2.71 + // 0x96 - FW2.80, FW2.82, FW3.00, FW3.03, FW3.10, FW3.30, FW3.40 + // 0x100 - FW3.50, FW3.52, FW5.00, FW5.55 + uint32 null; + uint32 compression; // upper nibble = compression, lower nibble: 0=flash0 RCOs, 1=UMD RCOs???? + /* + #define RCO_COMPRESSION_NONE 0x00 + // entries for 0x01 ?? + #define RCO_COMPRESSION_ZLIB 0x10 + #define RCO_COMPRESSION_RLZ 0x20 + */ + + // main table pointers + uint32 pMainTable; // type 1 + uint32 pVSMXTable; // type 2 + uint32 pTextTable; // type 3 + uint32 pSoundTable; // type 5 + uint32 pModelTable; // type 6 + uint32 pImgTable; // type 4 + uint32 pUnknown; // always 0xFFFFFFFF + uint32 pFontTable; // type 7 + uint32 pObjTable; // type 8 + uint32 pAnimTable; // type 9 + + // text stuff + uint32 pTextData; // NOTE: this may == pLabelData if lTextData == 0 + uint32 lTextData; + uint32 pLabelData; + uint32 lLabelData; + uint32 pEventData; + uint32 lEventData; + + // pointer data + uint32 pTextPtrs; + uint32 lTextPtrs; + uint32 pImgPtrs; + uint32 lImgPtrs; + uint32 pModelPtrs; + uint32 lModelPtrs; + uint32 pSoundPtrs; + uint32 lSoundPtrs; + uint32 pObjPtrs; + uint32 lObjPtrs; + uint32 pAnimPtrs; + uint32 lAnimPtrs; + + // attached data + uint32 pImgData; + uint32 lImgData; + uint32 pSoundData; + uint32 lSoundData; + uint32 pModelData; + uint32 lModelData; + + // always 0xFFFFFFFF + uint32 unknown[3]; +}); + +PACK_STRUCT(RCOEntry, { + //uint8 type; // main table uses 0x01; may be used as a current entry depth value + //uint8 id; + uint16 typeId; + uint16 blank; + uint32 labelOffset; + uint32 eHeadSize; // = sizeof(RCOEntry) = 0x28 [ only used for entries with extra info (ie not "main" entries) ] + uint32 entrySize; // main tables (main/img etc) uses 0x28 here, or is this the length of current entry (not including subentries)? + // 0x10 + uint32 numSubentries; + uint32 nextEntryOffset; + uint32 prevEntryOffset; // this is usually 0x0 however (does make writing RCOs easier though :P I guess Sony's tools do something similar...) + uint32 parentTblOffset; // offset of this entry from parent table + // 0x20 + uint32 blanks[2]; + +}); + +PACK_STRUCT(RCOVSMXEntry, { + uint32 offset; // always 0x0, so I assume this is an offset + uint32 length; // length of VSMX file +}); + +#define RCO_LANG_JAPANESE 0x0 +#define RCO_LANG_ENGLISH 0x1 +#define RCO_LANG_FRENCH 0x2 +#define RCO_LANG_SPANISH 0x3 +#define RCO_LANG_GERMAN 0x4 +#define RCO_LANG_ITALIAN 0x5 +#define RCO_LANG_DUTCH 0x6 +#define RCO_LANG_PORTUGESE 0x7 +#define RCO_LANG_RUSSIAN 0x8 +#define RCO_LANG_KOREAN 0x9 +#define RCO_LANG_CHINESE_TRADITIOAL 0xA +#define RCO_LANG_CHINESE_SIMPLIFIED 0xB +// the following are just guestimated from the p3tcompiler readme +#define RCO_LANG_FINNISH 0xC +#define RCO_LANG_SWEDISH 0xD +#define RCO_LANG_DANISH 0xE +#define RCO_LANG_NORWEGIAN 0xF +#define RCO_TEXT_FMT_UTF8 0x0 +#define RCO_TEXT_FMT_UTF16 0x1 +#define RCO_TEXT_FMT_UTF32 0x2 +PACK_STRUCT(RCOTextEntry, { + uint16 lang; + uint16 format; + uint32 numIndexes; +}); + +PACK_STRUCT(RCOTextIndex, { + uint32 labelOffset; + uint32 length; + uint32 offset; +}); + +#define RCO_DATA_COMPRESSION_NONE 0x0 +#define RCO_DATA_COMPRESSION_ZLIB 0x1 +#define RCO_DATA_COMPRESSION_RLZ 0x2 + +#define RCO_IMG_PNG 0x0 +#define RCO_IMG_JPEG 0x1 +#define RCO_IMG_TIFF 0x2 +#define RCO_IMG_GIF 0x3 +#define RCO_IMG_BMP 0x4 +#define RCO_IMG_GIM 0x5 +#define RCO_MODEL_GMO 0x0 +PACK_STRUCT(RCOImgModelEntry, { + uint16 format; + uint16 compression; // RCO_DATA_COMPRESSION_* constants + uint32 sizePacked; + uint32 offset; + uint32 sizeUnpacked; // this value doesn't exist if entry isn't compressed +}); +// note, some image/model entries which aren't compressed, don't have the last member +PACK_STRUCT(RCOPS3ImgModelEntry, { // PS3 version of the above + uint16 format; + uint16 compression; // RCO_DATA_COMPRESSION_* constants + uint32 sizePacked; + uint32 offset; + uint32 something; // PS3 RCOs seem to have this extra element - probably something to do with planes/frames?? usually 0x1 + uint32 sizeUnpacked; // this value doesn't exist if entry isn't compressed +}); + + +#define RCO_SOUND_VAG 0x1 +PACK_STRUCT(RCOSoundEntry, { + uint16 format; // 0x01 = VAG + uint16 channels; // 1 or 2 channels + uint32 sizeTotal; + uint32 offset; + // now pairs of size/offset for each channel +}); + + +PACK_STRUCT(RCOFontEntry, { + uint16 format; // 1 + uint16 compression; // 0 + uint32 unknown; // 0 + uint32 unknown2; +}); + +#define RCO_OBJ_TYPE_PAGE 0x1 +#define RCO_OBJ_TYPE_PLANE 0x2 +#define RCO_OBJ_TYPE_BUTTON 0x3 +#define RCO_OBJ_TYPE_XMENU 0x4 +#define RCO_OBJ_TYPE_XMLIST 0x5 +#define RCO_OBJ_TYPE_XLIST 0x6 +#define RCO_OBJ_TYPE_PROGRESS 0x7 +#define RCO_OBJ_TYPE_SCROLL 0x8 +#define RCO_OBJ_TYPE_MLIST 0x9 +#define RCO_OBJ_TYPE_MITEM 0xA + //#define RCO_OBJ_TYPE_0B = 0xB +#define RCO_OBJ_TYPE_XITEM 0xC +#define RCO_OBJ_TYPE_TEXT 0xD +#define RCO_OBJ_TYPE_MODEL 0xE +#define RCO_OBJ_TYPE_SPIN 0xF +#define RCO_OBJ_TYPE_ACTION 0x10 +#define RCO_OBJ_TYPE_ITEMSPIN 0x11 +#define RCO_OBJ_TYPE_GROUP 0x12 +#define RCO_OBJ_TYPE_LLIST 0x13 +#define RCO_OBJ_TYPE_LITEM 0x14 +#define RCO_OBJ_TYPE_EDIT 0x15 +#define RCO_OBJ_TYPE_CLOCK 0x16 +#define RCO_OBJ_TYPE_ILIST 0x17 +#define RCO_OBJ_TYPE_IITEM 0x18 +#define RCO_OBJ_TYPE_ICON 0x19 +#define RCO_OBJ_TYPE_UBUTTON 0x1A + +#define RCO_REF_EVENT 0x400 // relative +#define RCO_REF_TEXT 0x401 // # (0-based) +#define RCO_REF_IMG 0x402 // absolute +#define RCO_REF_MODEL 0x403 // absolute +#define RCO_REF_FONT 0x405 // absolute +#define RCO_REF_OBJ2 0x407 // same as 0x409?? +#define RCO_REF_ANIM 0x408 // absolute +#define RCO_REF_OBJ 0x409 // absolute +#define RCO_REF_NONE 0xFFFF +PACK_STRUCT(RCOReference, { + uint32 type; + uint32 ptr; +}); + + +#define RCO_ANIM_TYPE_POS 0x2 +#define RCO_ANIM_TYPE_COLOUR 0x3 +#define RCO_ANIM_TYPE_ROTATE 0x4 +#define RCO_ANIM_TYPE_SCALE 0x5 +#define RCO_ANIM_TYPE_ALPHA 0x6 +#define RCO_ANIM_TYPE_DELAY 0x7 +#define RCO_ANIM_TYPE_EVENT 0x8 +#define RCO_ANIM_TYPE_LOCK 0x9 // ? +#define RCO_ANIM_TYPE_UNLOCK 0xA // ? +#define RCO_ANIM_TYPE_0B 0xB // only appears on UMD RCOs? + + + + +PACK_STRUCT(HeaderComprInfo, { + uint32 lenPacked; + uint32 lenUnpacked; + uint32 lenLongestText; // length of the longest language's text data (unpacked) +}); + +PACK_STRUCT(TextComprInfo, { + uint16 lang; + uint16 unknown; // always 0x1 + uint32 nextOffset; // = ALIGN_TO_4(sizeof(TextComprInfo) + packedLen); is 0 for last text entry regardless of what actually comes after + uint32 packedLen; + uint32 unpackedLen; +}); + + + +#endif diff --git a/rcomain.c b/rcomain.c index 0b8f2fb..16e4deb 100644 --- a/rcomain.c +++ b/rcomain.c @@ -1,416 +1,416 @@ - -#include -#include -#include "rcomain.h" - -Bool suppressDecompWarnings = FALSE; - -void free_rco(rRCOFile* f) { - - // TODO: this function - - free(f); -} - -void rco_map_func(rRCOFile* rco, rRCOEntry* parent, void* arg, void(*func)(rRCOFile* rco, rRCOEntry* entry, void* arg)) { - rRCOEntry* rcoNode; - if(!parent) parent = &rco->tblMain; - func(rco, parent, arg); - for(rcoNode=parent->firstChild; rcoNode; rcoNode=rcoNode->next) - rco_map_func(rco, rcoNode, arg, func); -} - -char* get_label_from_offset(char* labels, uint labelOffset) { - static char labelBuf[255]; - if(labelOffset) - return labels + labelOffset; - else { - sprintf(labelBuf, "No Label"); - return labelBuf; - } -} - -/* -rRCOEntry* rco_add_entry(rRCOEntry* parent, rRCOEntry* newEntry, int pos) { - rRCOEntry* dest; - if(pos != -1 && pos > (int)(parent->numSubentries)) - return NULL; - - if(!parent->numSubentries) { - parent->subentries = (rRCOEntry*)malloc(sizeof(rRCOEntry)); - dest = parent->subentries; - } else { - parent->subentries = (rRCOEntry*)realloc(parent->subentries, sizeof(rRCOEntry) * (parent->numSubentries+1)); - if(pos == -1) - dest = &(parent->subentries[parent->numSubentries]); - else { - // shift things up by one place - dest = &(parent->subentries[pos]); - uint numEntriesToMove = parent->numSubentries - pos; - //rRCOEntry* tmpBuffer = (rRCOEntry*)malloc() - memcpy(&(parent->subentries[pos+1]), &(parent->subentries[pos]), numEntriesToMove * sizeof(rRCOEntry)); - } - } - - parent->numSubentries++; - memcpy(dest, newEntry, sizeof(rRCOEntry)); - - return dest; -} -*/ - -// parses an RCO (give it the main entry) to fix any resource which is compressed, but uncompressed size is unknown -// *new* actually, now will check decompressed size of all compressed resources -void rco_fix_decomp_sizes(rRCOFile* rco, rRCOEntry* entry) { - if(entry->srcFile[0] && entry->srcCompression) { - if(entry->srcCompression == RCO_DATA_COMPRESSION_ZLIB) { - // read and stuff - FILE* fp = fopen(entry->srcFile, "rb"); - if(fp) { - char* inBuf = (char*)malloc(entry->srcLen); - fseek(fp, entry->srcAddr, SEEK_SET); - fileread(fp, inBuf, entry->srcLen); - fclose(fp); - - uint unpackedLen = zlib_unpacked_size(inBuf, entry->srcLen); - if(unpackedLen != 0xFFFFFFFF) { - if(entry->srcLenUnpacked != 0xFFFFFFFF && unpackedLen != entry->srcLenUnpacked) - warning("Specified uncompressed size for entry '%s' is incorrect. Specified length = %d, actual length = %d", rco->labels + entry->labelOffset, entry->srcLenUnpacked, unpackedLen); - entry->srcLenUnpacked = unpackedLen; - } - free(inBuf); - } - } - - if(entry->srcLenUnpacked == 0xFFFFFFFF) { - warning("Uncompressed size for entry '%s' not specified and cannot be automatically determined! Setting size to 0.", rco->labels + entry->labelOffset); - entry->srcLenUnpacked = 0; - } - } - - rRCOEntry* rcoNode; - for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) - rco_fix_decomp_sizes(rco, rcoNode); -} - -void* read_resource(rRCOEntry* entry, uint* outLen) { - char* bufferMid; - if((!entry->srcFile[0] || strchr(entry->srcFile, '*')) && entry->srcBuffer) { - // this entry should be a sound/text entry (we assume this) - // also entry->srcAddr should be 0, and entry->srcLen == entry->srcLenUnpacked should be true (and equal the length of the buffer), _and_ entry->srcCompression should be NONE - bufferMid = (char*)malloc(entry->srcLen); - char* bufferPos = (char*)entry->srcBuffer + entry->srcAddr; - memcpy(bufferMid, bufferPos, entry->srcLen); - *outLen = entry->srcLen; - } else { - FILE* src = fopen(entry->srcFile, "rb"); - if(!src) { - warning("Unable to open file '%s'.", entry->srcFile); - return NULL; - } - - if(entry->srcCompression == RCO_DATA_COMPRESSION_RCO) { - // special case where stuff is embedded in RCO - this should only be used with VSMX, so we'll take a short cut here, but: - // TODO: do some extra checks to enforce the above - - // we assume it's in the tables section (otherwise, there's no point in referencing an RCO - if(entry->srcAddr < sizeof(PRFHeader)) return NULL; - PRFHeader header; - Bool eSwap; - fileread(src, &header, sizeof(header)); - eSwap = (header.signature == ENDIAN_SWAP_32(RCO_SIGNATURE)); - if(eSwap) es_rcoHeader(&header); - if(header.compression >> 4) { - HeaderComprInfo hci; - fileread(src, &hci, sizeof(hci)); - if(eSwap) es_headerComprInfo(&hci); - - // check if the entry _does_ exist in the RCO tree structure - if(!(entry->srcAddr >= sizeof(header) && entry->srcAddr < sizeof(header)+hci.lenUnpacked && entry->srcAddr+entry->srcLen <= sizeof(header)+hci.lenUnpacked)) return NULL; - - char* bufferIn = (char*)malloc(hci.lenPacked); - char* bufferInDecomp = (char*)calloc(1, hci.lenUnpacked); - fileread(src, bufferIn, hci.lenPacked); - fclose(src); - - if(header.compression >> 4 == RCO_DATA_COMPRESSION_ZLIB) { - int uRet = zlib_uncompress(bufferInDecomp, hci.lenUnpacked, bufferIn, hci.lenPacked); - if(uRet != Z_OK && uRet != Z_DATA_ERROR) { - free(bufferIn); - free(bufferInDecomp); - return NULL; - } else if(uRet == Z_DATA_ERROR && !suppressDecompWarnings) { - warning("Encountered 'data error' when decompressing resource."); - } - free(bufferIn); - - bufferMid = (char*)malloc(entry->srcLenUnpacked); - memcpy(bufferMid, bufferInDecomp+entry->srcAddr-sizeof(header), entry->srcLen); - free(bufferInDecomp); - - *outLen = entry->srcLen; - } else { - free(bufferIn); - free(bufferInDecomp); - return NULL; - } - } else { - // er, wtf? uncompressed RCO? - fseek(src, entry->srcAddr, SEEK_SET); - bufferMid = (char*)malloc(entry->srcLen); - fileread(src, bufferMid, entry->srcLen); - fclose(src); - } - - } else { - if(entry->srcAddr) fseek(src, entry->srcAddr, SEEK_SET); - - // who cares about mem usage ?? - char* bufferIn = (char*)malloc(entry->srcLen); - fileread(src, bufferIn, entry->srcLen); - fclose(src); - - if(entry->srcCompression) { - bufferMid = (char*)calloc(1, entry->srcLenUnpacked); - if(entry->srcCompression == RCO_DATA_COMPRESSION_ZLIB) { - int uRet = zlib_uncompress(bufferMid, entry->srcLenUnpacked, bufferIn, entry->srcLen); - if(uRet != Z_OK && uRet != Z_DATA_ERROR) { - free(bufferIn); - free(bufferMid); - return NULL; - } else if(uRet == Z_DATA_ERROR && !suppressDecompWarnings) { - warning("Encountered 'data error' when decompressing resource."); - } - free(bufferIn); - *outLen = entry->srcLenUnpacked; - } else if(entry->srcCompression == RCO_DATA_COMPRESSION_RLZ) { - warning("RLZ decompression not supported - data will be left uncompressed."); - bufferMid = bufferIn; - *outLen = entry->srcLen; - } else { - // unknown compression - free(bufferIn); - free(bufferMid); - return NULL; - } - } else { - bufferMid = bufferIn; - // entry->srcLenUnpacked == entry->srcLen SHOULD BE TRUE HERE (we assume this) - *outLen = entry->srcLen; - } - } - } - return bufferMid; -} - -uint count_all_subentries(rRCOEntry* entry) { - uint entries = entry->numSubentries; - rRCOEntry* rcoNode; - for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) - entries += count_all_subentries(rcoNode); - return entries; -} - -rRCOEntry** make_sorted_list_of_subentries(rRCOEntry* parent, int(*compar)(const rRCOEntry**, const rRCOEntry**)) { - - rRCOEntry** children = (rRCOEntry**)malloc(parent->numSubentries * sizeof(rRCOEntry*)); - children[0] = parent->firstChild; - uint i; - for(i=1; inumSubentries; i++) - children[i] = children[i-1]->next; - - qsort(children, parent->numSubentries, sizeof(rRCOEntry*), (int(*)(const void*, const void*))compar); - return children; -} - -rRCOEntry* find_entry_from_label(rRCOEntry* parent, const char* s) { - if(parent->labelOffset != RCO_NULL_PTR && !strcmp(parent->labelOffset + parent->rco->labels, s)) return parent; - - rRCOEntry* rcoNode; - for(rcoNode=parent->firstChild; rcoNode; rcoNode=rcoNode->next) { - rRCOEntry* res; - if((res = find_entry_from_label(rcoNode, s))) return res; - } - - return NULL; -} - -// returns -1 if not found -int find_text_from_label(char* labels, rRCOTextEntry* textExtra, const char* s) { - if(!textExtra->numIndexes) return -1; - - uint i; - for(i=0; inumIndexes; i++) { - if(!strcmp(labels + textExtra->indexes[i].labelOffset, s)) - return i; - } - return -1; -} - -void make_iconv_charset(char out[8], int fmt, Bool es) { - strcpy(out, "ucs-2le"); - if(es) out[5] = 'b'; // big endian - if(fmt == RCO_TEXT_FMT_UTF32) out[4] = '4'; - if(fmt == RCO_TEXT_FMT_UTF8) { - out[1] = 't'; - out[2] = 'f'; - out[4] = '8'; - out[5] = '\0'; - } -} - - - -// Endian swap functions -void es_rcoHeader(PRFHeader* h) { - h->signature = ENDIAN_SWAP(h->signature); - h->version = ENDIAN_SWAP(h->version); - h->compression = ENDIAN_SWAP(h->compression); - h->pMainTable = ENDIAN_SWAP(h->pMainTable); - h->pVSMXTable = ENDIAN_SWAP(h->pVSMXTable); - h->pTextTable = ENDIAN_SWAP(h->pTextTable); - h->pSoundTable = ENDIAN_SWAP(h->pSoundTable); - h->pModelTable = ENDIAN_SWAP(h->pModelTable); - h->pImgTable = ENDIAN_SWAP(h->pImgTable); - h->pUnknown = ENDIAN_SWAP(h->pUnknown); - h->pFontTable = ENDIAN_SWAP(h->pFontTable); - h->pObjTable = ENDIAN_SWAP(h->pObjTable); - h->pAnimTable = ENDIAN_SWAP(h->pAnimTable); - h->pTextData = ENDIAN_SWAP(h->pTextData); - h->lTextData = ENDIAN_SWAP(h->lTextData); - h->pLabelData = ENDIAN_SWAP(h->pLabelData); - h->lLabelData = ENDIAN_SWAP(h->lLabelData); - h->pEventData = ENDIAN_SWAP(h->pEventData); - h->lEventData = ENDIAN_SWAP(h->lEventData); - h->pTextPtrs = ENDIAN_SWAP(h->pTextPtrs); - h->lTextPtrs = ENDIAN_SWAP(h->lTextPtrs); - h->pImgPtrs = ENDIAN_SWAP(h->pImgPtrs); - h->lImgPtrs = ENDIAN_SWAP(h->lImgPtrs); - h->pModelPtrs = ENDIAN_SWAP(h->pModelPtrs); - h->lModelPtrs = ENDIAN_SWAP(h->lModelPtrs); - h->pSoundPtrs = ENDIAN_SWAP(h->pSoundPtrs); - h->lSoundPtrs = ENDIAN_SWAP(h->lSoundPtrs); - h->pObjPtrs = ENDIAN_SWAP(h->pObjPtrs); - h->lObjPtrs = ENDIAN_SWAP(h->lObjPtrs); - h->pAnimPtrs = ENDIAN_SWAP(h->pAnimPtrs); - h->lAnimPtrs = ENDIAN_SWAP(h->lAnimPtrs); - h->pImgData = ENDIAN_SWAP(h->pImgData); - h->lImgData = ENDIAN_SWAP(h->lImgData); - h->pSoundData = ENDIAN_SWAP(h->pSoundData); - h->lSoundData = ENDIAN_SWAP(h->lSoundData); - h->pModelData = ENDIAN_SWAP(h->pModelData); - h->lModelData = ENDIAN_SWAP(h->lModelData); - -} - -void es_rcoEntry(RCOEntry* e) { - e->typeId = ENDIAN_SWAP(e->typeId); - e->blank = ENDIAN_SWAP(e->blank); - e->labelOffset = ENDIAN_SWAP(e->labelOffset); - e->eHeadSize = ENDIAN_SWAP(e->eHeadSize); - e->entrySize = ENDIAN_SWAP(e->entrySize); - e->numSubentries = ENDIAN_SWAP(e->numSubentries); - e->nextEntryOffset = ENDIAN_SWAP(e->nextEntryOffset); - e->prevEntryOffset = ENDIAN_SWAP(e->prevEntryOffset); - e->parentTblOffset = ENDIAN_SWAP(e->parentTblOffset); -} - -void es_rcoVsmxEntry(RCOVSMXEntry* e) { - e->offset = ENDIAN_SWAP(e->offset); - e->length = ENDIAN_SWAP(e->length); -} -void es_rcoFontEntry(RCOFontEntry* e) { - e->format = ENDIAN_SWAP(e->format); - e->unknown = ENDIAN_SWAP(e->unknown); - e->unknown2 = ENDIAN_SWAP(e->unknown2); -} -void es_rcoTextEntry(RCOTextEntry* e) { - e->lang = ENDIAN_SWAP(e->lang); - e->format = ENDIAN_SWAP(e->format); - e->numIndexes = ENDIAN_SWAP(e->numIndexes); -} -void es_rcoTextIndex(RCOTextIndex* i) { - i->labelOffset = ENDIAN_SWAP(i->labelOffset); - i->length = ENDIAN_SWAP(i->length); - i->offset = ENDIAN_SWAP(i->offset); -} -void es_rcoImgModelEntry(RCOImgModelEntry* e) { - e->format = ENDIAN_SWAP(e->format); - e->compression = ENDIAN_SWAP(e->compression); - e->sizePacked = ENDIAN_SWAP(e->sizePacked); - e->offset = ENDIAN_SWAP(e->offset); - e->sizeUnpacked = ENDIAN_SWAP(e->sizeUnpacked); -} -void es_rcoSoundEntry(RCOSoundEntry* e) { - e->format = ENDIAN_SWAP(e->format); - //e->channels = ENDIAN_SWAP(e->channels); - e->sizeTotal = ENDIAN_SWAP(e->sizeTotal); - e->offset = ENDIAN_SWAP(e->offset); -} - -void es_headerComprInfo(HeaderComprInfo* hci) { - hci->lenPacked = ENDIAN_SWAP(hci->lenPacked); - hci->lenUnpacked = ENDIAN_SWAP(hci->lenUnpacked); - hci->lenLongestText = ENDIAN_SWAP(hci->lenLongestText); -} - -void es_textComprInfo(TextComprInfo* tci) { - tci->lang = ENDIAN_SWAP(tci->lang); - tci->unknown = ENDIAN_SWAP(tci->unknown); - tci->nextOffset = ENDIAN_SWAP(tci->nextOffset); - tci->packedLen = ENDIAN_SWAP(tci->packedLen); - tci->unpackedLen = ENDIAN_SWAP(tci->unpackedLen); -} - -void es_extraObjAnim(Bool isObj, int type, void* data, Bool isPS3) { - if(type == 0) return; - - uint len = 0; - const int* typeArray; - if(isObj && type <= (int)RCO_OBJ_EXTRA_LEN_NUM) { - len = RCO_OBJ_EXTRA_LEN[type]; - typeArray = RCO_OBJ_EXTRA_TYPES[type]; - } - else if(!isObj && type <= (int)RCO_ANIM_EXTRA_LEN_NUM) { - len = RCO_ANIM_EXTRA_LEN[type]; - typeArray = RCO_ANIM_EXTRA_TYPES[type]; - } - - if(!len) return; - - uint i=0, i2=0; - uint32* uData = (uint32*)data; - - #define ENDIAN_SWAP_HALF32(x) (((x) & 0xFF) << 8 | ((x) & 0xFF00) >> 8 | ((x) & 0xFF0000) << 8 | ((x) & 0xFF000000) >> 8) - - /* - if(!isObj && RCO_ANIM_EXTRA_REFS[type]) { - uData[0] = ENDIAN_SWAP_HALF32(uData[0]); - uData[1] = ENDIAN_SWAP(uData[1]); - i = 2; - } - */ - - for(; i +#include +#include "rcomain.h" + +Bool suppressDecompWarnings = FALSE; + +void free_rco(rRCOFile* f) { + + // TODO: this function + + free(f); +} + +void rco_map_func(rRCOFile* rco, rRCOEntry* parent, void* arg, void(*func)(rRCOFile* rco, rRCOEntry* entry, void* arg)) { + rRCOEntry* rcoNode; + if(!parent) parent = &rco->tblMain; + func(rco, parent, arg); + for(rcoNode=parent->firstChild; rcoNode; rcoNode=rcoNode->next) + rco_map_func(rco, rcoNode, arg, func); +} + +char* get_label_from_offset(char* labels, uint labelOffset) { + static char labelBuf[255]; + if(labelOffset) + return labels + labelOffset; + else { + sprintf(labelBuf, "No Label"); + return labelBuf; + } +} + +/* +rRCOEntry* rco_add_entry(rRCOEntry* parent, rRCOEntry* newEntry, int pos) { + rRCOEntry* dest; + if(pos != -1 && pos > (int)(parent->numSubentries)) + return NULL; + + if(!parent->numSubentries) { + parent->subentries = (rRCOEntry*)malloc(sizeof(rRCOEntry)); + dest = parent->subentries; + } else { + parent->subentries = (rRCOEntry*)realloc(parent->subentries, sizeof(rRCOEntry) * (parent->numSubentries+1)); + if(pos == -1) + dest = &(parent->subentries[parent->numSubentries]); + else { + // shift things up by one place + dest = &(parent->subentries[pos]); + uint numEntriesToMove = parent->numSubentries - pos; + //rRCOEntry* tmpBuffer = (rRCOEntry*)malloc() + memcpy(&(parent->subentries[pos+1]), &(parent->subentries[pos]), numEntriesToMove * sizeof(rRCOEntry)); + } + } + + parent->numSubentries++; + memcpy(dest, newEntry, sizeof(rRCOEntry)); + + return dest; +} +*/ + +// parses an RCO (give it the main entry) to fix any resource which is compressed, but uncompressed size is unknown +// *new* actually, now will check decompressed size of all compressed resources +void rco_fix_decomp_sizes(rRCOFile* rco, rRCOEntry* entry) { + if(entry->srcFile[0] && entry->srcCompression) { + if(entry->srcCompression == RCO_DATA_COMPRESSION_ZLIB) { + // read and stuff + FILE* fp = fopen(entry->srcFile, "rb"); + if(fp) { + char* inBuf = (char*)malloc(entry->srcLen); + fseek(fp, entry->srcAddr, SEEK_SET); + fileread(fp, inBuf, entry->srcLen); + fclose(fp); + + uint unpackedLen = zlib_unpacked_size(inBuf, entry->srcLen); + if(unpackedLen != 0xFFFFFFFF) { + if(entry->srcLenUnpacked != 0xFFFFFFFF && unpackedLen != entry->srcLenUnpacked) + warning("Specified uncompressed size for entry '%s' is incorrect. Specified length = %d, actual length = %d", rco->labels + entry->labelOffset, entry->srcLenUnpacked, unpackedLen); + entry->srcLenUnpacked = unpackedLen; + } + free(inBuf); + } + } + + if(entry->srcLenUnpacked == 0xFFFFFFFF) { + warning("Uncompressed size for entry '%s' not specified and cannot be automatically determined! Setting size to 0.", rco->labels + entry->labelOffset); + entry->srcLenUnpacked = 0; + } + } + + rRCOEntry* rcoNode; + for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) + rco_fix_decomp_sizes(rco, rcoNode); +} + +void* read_resource(rRCOEntry* entry, uint* outLen) { + char* bufferMid; + if((!entry->srcFile[0] || strchr(entry->srcFile, '*')) && entry->srcBuffer) { + // this entry should be a sound/text entry (we assume this) + // also entry->srcAddr should be 0, and entry->srcLen == entry->srcLenUnpacked should be true (and equal the length of the buffer), _and_ entry->srcCompression should be NONE + bufferMid = (char*)malloc(entry->srcLen); + char* bufferPos = (char*)entry->srcBuffer + entry->srcAddr; + memcpy(bufferMid, bufferPos, entry->srcLen); + *outLen = entry->srcLen; + } else { + FILE* src = fopen(entry->srcFile, "rb"); + if(!src) { + warning("Unable to open file '%s'.", entry->srcFile); + return NULL; + } + + if(entry->srcCompression == RCO_DATA_COMPRESSION_RCO) { + // special case where stuff is embedded in RCO - this should only be used with VSMX, so we'll take a short cut here, but: + // TODO: do some extra checks to enforce the above + + // we assume it's in the tables section (otherwise, there's no point in referencing an RCO + if(entry->srcAddr < sizeof(PRFHeader)) return NULL; + PRFHeader header; + Bool eSwap; + fileread(src, &header, sizeof(header)); + eSwap = (header.signature == ENDIAN_SWAP_32(RCO_SIGNATURE)); + if(eSwap) es_rcoHeader(&header); + if(header.compression >> 4) { + HeaderComprInfo hci; + fileread(src, &hci, sizeof(hci)); + if(eSwap) es_headerComprInfo(&hci); + + // check if the entry _does_ exist in the RCO tree structure + if(!(entry->srcAddr >= sizeof(header) && entry->srcAddr < sizeof(header)+hci.lenUnpacked && entry->srcAddr+entry->srcLen <= sizeof(header)+hci.lenUnpacked)) return NULL; + + char* bufferIn = (char*)malloc(hci.lenPacked); + char* bufferInDecomp = (char*)calloc(1, hci.lenUnpacked); + fileread(src, bufferIn, hci.lenPacked); + fclose(src); + + if(header.compression >> 4 == RCO_DATA_COMPRESSION_ZLIB) { + int uRet = zlib_uncompress(bufferInDecomp, hci.lenUnpacked, bufferIn, hci.lenPacked); + if(uRet != Z_OK && uRet != Z_DATA_ERROR) { + free(bufferIn); + free(bufferInDecomp); + return NULL; + } else if(uRet == Z_DATA_ERROR && !suppressDecompWarnings) { + warning("Encountered 'data error' when decompressing resource."); + } + free(bufferIn); + + bufferMid = (char*)malloc(entry->srcLenUnpacked); + memcpy(bufferMid, bufferInDecomp+entry->srcAddr-sizeof(header), entry->srcLen); + free(bufferInDecomp); + + *outLen = entry->srcLen; + } else { + free(bufferIn); + free(bufferInDecomp); + return NULL; + } + } else { + // er, wtf? uncompressed RCO? + fseek(src, entry->srcAddr, SEEK_SET); + bufferMid = (char*)malloc(entry->srcLen); + fileread(src, bufferMid, entry->srcLen); + fclose(src); + } + + } else { + if(entry->srcAddr) fseek(src, entry->srcAddr, SEEK_SET); + + // who cares about mem usage ?? + char* bufferIn = (char*)malloc(entry->srcLen); + fileread(src, bufferIn, entry->srcLen); + fclose(src); + + if(entry->srcCompression) { + bufferMid = (char*)calloc(1, entry->srcLenUnpacked); + if(entry->srcCompression == RCO_DATA_COMPRESSION_ZLIB) { + int uRet = zlib_uncompress(bufferMid, entry->srcLenUnpacked, bufferIn, entry->srcLen); + if(uRet != Z_OK && uRet != Z_DATA_ERROR) { + free(bufferIn); + free(bufferMid); + return NULL; + } else if(uRet == Z_DATA_ERROR && !suppressDecompWarnings) { + warning("Encountered 'data error' when decompressing resource."); + } + free(bufferIn); + *outLen = entry->srcLenUnpacked; + } else if(entry->srcCompression == RCO_DATA_COMPRESSION_RLZ) { + warning("RLZ decompression not supported - data will be left uncompressed."); + bufferMid = bufferIn; + *outLen = entry->srcLen; + } else { + // unknown compression + free(bufferIn); + free(bufferMid); + return NULL; + } + } else { + bufferMid = bufferIn; + // entry->srcLenUnpacked == entry->srcLen SHOULD BE TRUE HERE (we assume this) + *outLen = entry->srcLen; + } + } + } + return bufferMid; +} + +uint count_all_subentries(rRCOEntry* entry) { + uint entries = entry->numSubentries; + rRCOEntry* rcoNode; + for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) + entries += count_all_subentries(rcoNode); + return entries; +} + +rRCOEntry** make_sorted_list_of_subentries(rRCOEntry* parent, int(*compar)(const rRCOEntry**, const rRCOEntry**)) { + + rRCOEntry** children = (rRCOEntry**)malloc(parent->numSubentries * sizeof(rRCOEntry*)); + children[0] = parent->firstChild; + uint i; + for(i=1; inumSubentries; i++) + children[i] = children[i-1]->next; + + qsort(children, parent->numSubentries, sizeof(rRCOEntry*), (int(*)(const void*, const void*))compar); + return children; +} + +rRCOEntry* find_entry_from_label(rRCOEntry* parent, const char* s) { + if(parent->labelOffset != RCO_NULL_PTR && !strcmp(parent->labelOffset + parent->rco->labels, s)) return parent; + + rRCOEntry* rcoNode; + for(rcoNode=parent->firstChild; rcoNode; rcoNode=rcoNode->next) { + rRCOEntry* res; + if((res = find_entry_from_label(rcoNode, s))) return res; + } + + return NULL; +} + +// returns -1 if not found +int find_text_from_label(char* labels, rRCOTextEntry* textExtra, const char* s) { + if(!textExtra->numIndexes) return -1; + + uint i; + for(i=0; inumIndexes; i++) { + if(!strcmp(labels + textExtra->indexes[i].labelOffset, s)) + return i; + } + return -1; +} + +void make_iconv_charset(char out[8], int fmt, Bool es) { + strcpy(out, "ucs-2le"); + if(es) out[5] = 'b'; // big endian + if(fmt == RCO_TEXT_FMT_UTF32) out[4] = '4'; + if(fmt == RCO_TEXT_FMT_UTF8) { + out[1] = 't'; + out[2] = 'f'; + out[4] = '8'; + out[5] = '\0'; + } +} + + + +// Endian swap functions +void es_rcoHeader(PRFHeader* h) { + h->signature = ENDIAN_SWAP(h->signature); + h->version = ENDIAN_SWAP(h->version); + h->compression = ENDIAN_SWAP(h->compression); + h->pMainTable = ENDIAN_SWAP(h->pMainTable); + h->pVSMXTable = ENDIAN_SWAP(h->pVSMXTable); + h->pTextTable = ENDIAN_SWAP(h->pTextTable); + h->pSoundTable = ENDIAN_SWAP(h->pSoundTable); + h->pModelTable = ENDIAN_SWAP(h->pModelTable); + h->pImgTable = ENDIAN_SWAP(h->pImgTable); + h->pUnknown = ENDIAN_SWAP(h->pUnknown); + h->pFontTable = ENDIAN_SWAP(h->pFontTable); + h->pObjTable = ENDIAN_SWAP(h->pObjTable); + h->pAnimTable = ENDIAN_SWAP(h->pAnimTable); + h->pTextData = ENDIAN_SWAP(h->pTextData); + h->lTextData = ENDIAN_SWAP(h->lTextData); + h->pLabelData = ENDIAN_SWAP(h->pLabelData); + h->lLabelData = ENDIAN_SWAP(h->lLabelData); + h->pEventData = ENDIAN_SWAP(h->pEventData); + h->lEventData = ENDIAN_SWAP(h->lEventData); + h->pTextPtrs = ENDIAN_SWAP(h->pTextPtrs); + h->lTextPtrs = ENDIAN_SWAP(h->lTextPtrs); + h->pImgPtrs = ENDIAN_SWAP(h->pImgPtrs); + h->lImgPtrs = ENDIAN_SWAP(h->lImgPtrs); + h->pModelPtrs = ENDIAN_SWAP(h->pModelPtrs); + h->lModelPtrs = ENDIAN_SWAP(h->lModelPtrs); + h->pSoundPtrs = ENDIAN_SWAP(h->pSoundPtrs); + h->lSoundPtrs = ENDIAN_SWAP(h->lSoundPtrs); + h->pObjPtrs = ENDIAN_SWAP(h->pObjPtrs); + h->lObjPtrs = ENDIAN_SWAP(h->lObjPtrs); + h->pAnimPtrs = ENDIAN_SWAP(h->pAnimPtrs); + h->lAnimPtrs = ENDIAN_SWAP(h->lAnimPtrs); + h->pImgData = ENDIAN_SWAP(h->pImgData); + h->lImgData = ENDIAN_SWAP(h->lImgData); + h->pSoundData = ENDIAN_SWAP(h->pSoundData); + h->lSoundData = ENDIAN_SWAP(h->lSoundData); + h->pModelData = ENDIAN_SWAP(h->pModelData); + h->lModelData = ENDIAN_SWAP(h->lModelData); + +} + +void es_rcoEntry(RCOEntry* e) { + e->typeId = ENDIAN_SWAP(e->typeId); + e->blank = ENDIAN_SWAP(e->blank); + e->labelOffset = ENDIAN_SWAP(e->labelOffset); + e->eHeadSize = ENDIAN_SWAP(e->eHeadSize); + e->entrySize = ENDIAN_SWAP(e->entrySize); + e->numSubentries = ENDIAN_SWAP(e->numSubentries); + e->nextEntryOffset = ENDIAN_SWAP(e->nextEntryOffset); + e->prevEntryOffset = ENDIAN_SWAP(e->prevEntryOffset); + e->parentTblOffset = ENDIAN_SWAP(e->parentTblOffset); +} + +void es_rcoVsmxEntry(RCOVSMXEntry* e) { + e->offset = ENDIAN_SWAP(e->offset); + e->length = ENDIAN_SWAP(e->length); +} +void es_rcoFontEntry(RCOFontEntry* e) { + e->format = ENDIAN_SWAP(e->format); + e->unknown = ENDIAN_SWAP(e->unknown); + e->unknown2 = ENDIAN_SWAP(e->unknown2); +} +void es_rcoTextEntry(RCOTextEntry* e) { + e->lang = ENDIAN_SWAP(e->lang); + e->format = ENDIAN_SWAP(e->format); + e->numIndexes = ENDIAN_SWAP(e->numIndexes); +} +void es_rcoTextIndex(RCOTextIndex* i) { + i->labelOffset = ENDIAN_SWAP(i->labelOffset); + i->length = ENDIAN_SWAP(i->length); + i->offset = ENDIAN_SWAP(i->offset); +} +void es_rcoImgModelEntry(RCOImgModelEntry* e) { + e->format = ENDIAN_SWAP(e->format); + e->compression = ENDIAN_SWAP(e->compression); + e->sizePacked = ENDIAN_SWAP(e->sizePacked); + e->offset = ENDIAN_SWAP(e->offset); + e->sizeUnpacked = ENDIAN_SWAP(e->sizeUnpacked); +} +void es_rcoSoundEntry(RCOSoundEntry* e) { + e->format = ENDIAN_SWAP(e->format); + //e->channels = ENDIAN_SWAP(e->channels); + e->sizeTotal = ENDIAN_SWAP(e->sizeTotal); + e->offset = ENDIAN_SWAP(e->offset); +} + +void es_headerComprInfo(HeaderComprInfo* hci) { + hci->lenPacked = ENDIAN_SWAP(hci->lenPacked); + hci->lenUnpacked = ENDIAN_SWAP(hci->lenUnpacked); + hci->lenLongestText = ENDIAN_SWAP(hci->lenLongestText); +} + +void es_textComprInfo(TextComprInfo* tci) { + tci->lang = ENDIAN_SWAP(tci->lang); + tci->unknown = ENDIAN_SWAP(tci->unknown); + tci->nextOffset = ENDIAN_SWAP(tci->nextOffset); + tci->packedLen = ENDIAN_SWAP(tci->packedLen); + tci->unpackedLen = ENDIAN_SWAP(tci->unpackedLen); +} + +void es_extraObjAnim(Bool isObj, int type, void* data, Bool isPS3) { + if(type == 0) return; + + uint len = 0; + const int* typeArray; + if(isObj && type <= (int)RCO_OBJ_EXTRA_LEN_NUM) { + len = RCO_OBJ_EXTRA_LEN[type]; + typeArray = RCO_OBJ_EXTRA_TYPES[type]; + } + else if(!isObj && type <= (int)RCO_ANIM_EXTRA_LEN_NUM) { + len = RCO_ANIM_EXTRA_LEN[type]; + typeArray = RCO_ANIM_EXTRA_TYPES[type]; + } + + if(!len) return; + + uint i=0, i2=0; + uint32* uData = (uint32*)data; + + #define ENDIAN_SWAP_HALF32(x) (((x) & 0xFF) << 8 | ((x) & 0xFF00) >> 8 | ((x) & 0xFF0000) << 8 | ((x) & 0xFF000000) >> 8) + + /* + if(!isObj && RCO_ANIM_EXTRA_REFS[type]) { + uData[0] = ENDIAN_SWAP_HALF32(uData[0]); + uData[1] = ENDIAN_SWAP(uData[1]); + i = 2; + } + */ + + for(; i -#include - -#define MAX_FILENAME_LEN 260 - -typedef struct { - int packHeader, packImg, packImgCompr, packModel, packText; - int zlibMethod, zlibLevel; - #define WRITERCO_ZLIB_METHOD_ZDEFAULT Z_DEFAULT_STRATEGY - #define WRITERCO_ZLIB_METHOD_ZFILTERED Z_FILTERED - #define WRITERCO_ZLIB_METHOD_ZHUFFMAN Z_HUFFMAN_ONLY - #define WRITERCO_ZLIB_METHOD_ZRLE Z_RLE - #define WRITERCO_ZLIB_METHOD_ZFIXED Z_FIXED - #define WRITERCO_ZLIB_METHOD_7Z Z_USE_7Z - int rlzMode; -} writerco_options; - -typedef struct __rRCOEntry { - uint8 type; // main table uses 0x01; may be used as a current entry depth value - uint8 id; - //char* label; - uint labelOffset; - - uint offset; // absolute offset of this entry in file (only used when reading/writing RCOs - means nothing otherwise; also, writing the RCO may change this value) - // this value is also used to store the line number of the node when reading an XML - - struct __rRCOFile* rco; - struct __rRCOEntry* parent; - struct __rRCOEntry* firstChild; - struct __rRCOEntry* lastChild; - struct __rRCOEntry* prev; - struct __rRCOEntry* next; - uint numSubentries; - - void* extra; - uint extraLen; // in bytes - - // for entries with attached data - char srcFile[MAX_FILENAME_LEN]; - uint srcAddr; - uint srcLen; - uint srcLenUnpacked; - uint srcCompression; // use RCO_DATA_COMPRESSION_* constants in rcofile.h - void* srcBuffer; // work around for reading XML; should only be used for sound and text entries; need to check if srcFile contains a '*', then use this -} rRCOEntry; - - - -typedef struct { - uint16 lang; - uint16 format; - uint32 numIndexes; - RCOTextIndex* indexes; -} rRCOTextEntry; -/* packed_struct { - char* label; - uint length; - uint32 offset; -} rRCOTextIndex; */ -typedef struct { - rRCOEntry* textEntry; - RCOTextIndex* index; -} rRCOTextIdxPtr; - - - -typedef struct __rRCOFile { - - rRCOEntry tblMain; - - char* labels; - uint labelsLen; - char* events; - uint eventsLen; - - /* - // pointer segments - rRCOTextIdxPtr* ptrText; - uint numPtrText; - rRCOEntry** ptrImg; - uint numPtrImg; - rRCOEntry** ptrModel; - uint numPtrModel; - rRCOEntry** ptrSound; - uint numPtrSound; - rRCOEntry** ptrObj; - uint numPtrObj; - rRCOEntry** ptrAnim; - uint numPtrAnim; - */ - - // shortcuts - rRCOEntry* tblVSMX; - rRCOEntry* tblText; - rRCOEntry* tblImage; - rRCOEntry* tblSound; - rRCOEntry* tblModel; - rRCOEntry* tblFont; - rRCOEntry* tblObj; - rRCOEntry* tblAnim; - - // additional info about the source (mainly used for displaying info about the RCO) - uint32 verId; // offset 0x04 in file - uint umdFlag; - uint headerCompression; - Bool eSwap; - Bool ps3; - -} rRCOFile; - - -typedef struct { - uint32 type; - void* ptr; // will usu be rRCOEntry*, but may be char* for events; NULL if type is nothing - uint32 rawPtr; // raw value from source - means nothing if type is known -} rRCORef; - - -typedef struct { - uint format; - uint compression; - uint unkCompr; // unknown - usually 0, some PS3 rcos have 1 here -} rRCOImgModelEntry; - -typedef struct { - uint16 format; // 0x01 = VAG - uint16 channels; // 1 or 2 channels - uint32* channelData; //size/offset pairs -} rRCOSoundEntry; - -typedef struct { - uint format; // 1 - uint compression; // 0 - uint unknown; - uint unknown2; -} rRCOFontEntry; - -#define RCO_TEXT_FMT_CHARWIDTH(t) ((t) == RCO_TEXT_FMT_UTF32 ? 4 : (((t) == RCO_TEXT_FMT_UTF8)) ? 1 : 2) - - -#define RCO_TAGMAP_SIZE 50 -#define RCO_OBJMAP_SIZE 100 -typedef char ((*RcoTagMap)[RCO_TAGMAP_SIZE][30]); -typedef char ((*RcoObjMap)[RCO_OBJMAP_SIZE][30]); -typedef int ((*RcoObjTypes)[RCO_OBJMAP_SIZE]); - -extern int* RCO_OBJ_EXTRA_LEN; -extern uint RCO_OBJ_EXTRA_LEN_NUM; - -#define RCO_OBJANIM_IS_REF(v) (\ - (v == RCO_OBJ_EXTRA_TYPE_REF) || \ - (v == RCO_OBJ_EXTRA_TYPE_EVENT) || \ - (v == RCO_OBJ_EXTRA_TYPE_IMG) || \ - (v == RCO_OBJ_EXTRA_TYPE_OBJ) || \ - (v == RCO_OBJ_EXTRA_TYPE_TEXT) || \ - (v == RCO_OBJ_EXTRA_TYPE_MODEL) || \ - (v == RCO_OBJ_EXTRA_TYPE_FONT) \ -) -#define RCO_OBJ_IS_REF(a, b) RCO_OBJANIM_IS_REF(RCO_OBJ_EXTRA_TYPES[a][b]) -#define RCO_ANIM_IS_REF(a, b) RCO_OBJANIM_IS_REF(RCO_ANIM_EXTRA_TYPES[a][b]) - -// this doesn't include position info -extern RcoObjMap RCO_OBJ_EXTRA_NAMES; - -#define RCO_OBJ_EXTRA_TYPE_UNK 0 -#define RCO_OBJ_EXTRA_TYPE_FLOAT 1 -#define RCO_OBJ_EXTRA_TYPE_INT 2 -#define RCO_OBJ_EXTRA_TYPE_REF 3 // unknown reference -#define RCO_OBJ_EXTRA_TYPE_EVENT 4 -#define RCO_OBJ_EXTRA_TYPE_IMG 5 -#define RCO_OBJ_EXTRA_TYPE_OBJ 6 -#define RCO_OBJ_EXTRA_TYPE_TEXT 7 -#define RCO_OBJ_EXTRA_TYPE_MODEL 8 -#define RCO_OBJ_EXTRA_TYPE_FONT 9 - -extern RcoObjTypes RCO_OBJ_EXTRA_TYPES; - -extern int* RCO_ANIM_EXTRA_LEN; -extern uint RCO_ANIM_EXTRA_LEN_NUM; - -// this doesn't include references -extern RcoObjMap RCO_ANIM_EXTRA_NAMES; - -// we'll use the RCO_OBJ_EXTRA_TYPE_* constants here -extern RcoObjTypes RCO_ANIM_EXTRA_TYPES; - - - - - - - - - -rRCOFile* read_rco(char* fn); -Bool write_rco(rRCOFile* rco, char* fn, writerco_options opts); -void free_rco(rRCOFile* f); -void rco_map_func(rRCOFile* rco, rRCOEntry* parent, void* arg, void(*func)(rRCOFile* rco, rRCOEntry* entry, void* arg)); -char* get_label_from_offset(char* labels, uint labelOffset); -void* read_resource(rRCOEntry* entry, uint* outLen); -#define RCO_DATA_COMPRESSION_RCO 0xFF - -//rRCOEntry* rco_add_entry(rRCOEntry* parent, rRCOEntry* newEntry, int pos); - -void rco_fix_decomp_sizes(rRCOFile* rco, rRCOEntry* entry); - -uint count_all_subentries(rRCOEntry* entry); -rRCOEntry** make_sorted_list_of_subentries(rRCOEntry* parent, int(*compar)(const rRCOEntry**, const rRCOEntry**)); - -rRCOEntry* find_entry_from_label(rRCOEntry* parent, const char* s); -int find_text_from_label(char* labels, rRCOTextEntry* textExtra, const char* s); - -void make_iconv_charset(char out[8], int fmt, Bool es); - - -void es_rcoHeader(PRFHeader* h); -void es_rcoEntry(RCOEntry* e); -void es_rcoVsmxEntry(RCOVSMXEntry* e); -void es_rcoFontEntry(RCOFontEntry* e); -void es_rcoTextEntry(RCOTextEntry* e); -void es_rcoTextIndex(RCOTextIndex* i); -void es_rcoImgModelEntry(RCOImgModelEntry* e); -void es_rcoSoundEntry(RCOSoundEntry* e); -void es_headerComprInfo(HeaderComprInfo* hci); -void es_textComprInfo(TextComprInfo* tci); -void es_extraObjAnim(Bool isObj, int type, void* data, Bool isPS3); - - - - -PACK_STRUCT(RCOObjPos, { - float posX; - float posY; - float objScale; - float colR; - float colG; - float colB; - float colA; // RGBA colour weights - float dimW; - float dimH; - float unknown; - float sclX; - float sclY; // scale values - float elemScale; - uint32 iconOffset; // unknown weird value, appears to affect icon offsetting. Only lowest byte appears to have any effects - upper nibble appears to affect Y offset, and lower nibble affects X offset somehow. - rRCORef loadAction; //the event/anim executed on image load (note, load, not display) or describes when image is loaded? (eg onShadowInit) -}); - -/* -PACK_STRUCT(RCOObjSPage, { - uint32 unknown; // usu 0x111, sometimes 0xFFFF (only seen in UMD video RCOs) - rRCORef event1; - rRCORef event2; - rRCORef event3; - rRCORef event4; -}); // 0x01 -PACK_STRUCT(RCOObjSPlane, { - RCOObjPos pos; - rRCORef image; - uint32 unknownH; // either 0 or 0xFFFF. Has weird effect on height. Upper 2 bytes seem to have no effect. -}); // 0x02 -PACK_STRUCT(RCOObjSButton, { -// this page seems to be for buttons/pane items. for "buttons", has events for "Push", "FocusIn", "FocusOut" etc - RCOObjPos pos; - rRCORef image; - rRCORef shadow; // usu this & above images are img/shadow pairs - rRCORef image2; - rRCORef ref; - rRCORef event1; - rRCORef event2; - rRCORef event3; - rRCORef event4; - rRCORef event5; - rRCORef event6; - rRCORef event7; - rRCORef event8; - uint32 unknown; // appears to be 0xFFFFFFFF or 0 -}); // 0x03 -PACK_STRUCT(RCOObjSXmenu, { // only used for XMB menu? - RCOObjPos pos; - uint32 unknown; - rRCORef event1; - rRCORef event2; - rRCORef event3; - rRCORef event4; - rRCORef ref; -}); // 0x04 -PACK_STRUCT(RCOObjSXmList, { // only used for XMB menu icons? - uint32 unknown; // dunno if hex or float - rRCORef image; - rRCORef ref; -}); // 0x05 -PACK_STRUCT(RCOObjSXList, { - RCOObjPos pos; - uint32 unknown; - rRCORef event1; - rRCORef event2; - rRCORef event3; - rRCORef event4; - rRCORef event5; - rRCORef event6; - rRCORef event7; -}); // 0x06 -PACK_STRUCT(RCOObjSProgress, { - RCOObjPos pos; - float unknown; - uint32 unknown2; // dunno if hex or float - rRCORef ref1; - rRCORef ref2; - rRCORef ref3; -}); // 0x07 -PACK_STRUCT(RCOObjSScroll, { // used for ilist? - RCOObjPos pos; - float unknown; - float unknown2; - uint32 unknown3; // dunno if hex or float - rRCORef ref1; - rRCORef ref2; - rRCORef ref3; - rRCORef ref4; - rRCORef ref5; -}); // 0x08 -PACK_STRUCT(RCOObjSMList, { // seems to be used for lists/menus - RCOObjPos pos; - uint32 unknown; - uint32 unknown2; // dunno if hex or float - uint32 unknown3; - float unknown4; - float unknown5; - rRCORef ref1; - rRCORef event1; - rRCORef ref2; - rRCORef ref3; - rRCORef ref4; - rRCORef event2; - rRCORef ref5; - rRCORef ref6; - rRCORef ref7; - rRCORef event3; - rRCORef ref8; -}); // 0x09 -PACK_STRUCT(RCOObjSMItem, { // menu item? - rRCORef text; - rRCORef textAlt; - rRCORef ref; -}); // 0x0A -PACK_STRUCT(RCOObjSXItem, { - rRCORef image; - rRCORef text; - rRCORef ref; -}); // 0x0C -PACK_STRUCT(RCOObjSText, { - RCOObjPos pos; - rRCORef text; // the text displayed - rRCORef ref1; - uint32 unknownAlign; // bottom byte: something to do with multiline text align; 1=left align, 2=right, anything else=center - uint32 unknown; - float size; - float topR; // top RGB values - float topG; - float topB; - float bottomR; // bottom RGB values - float bottomG; - float bottomB; - float spacingHorizontal; - uint32 unknown2; // dunno whether hex or float - uint32 unknown3; // dunno whether hex or float - uint32 unknown4; // dunno whether hex or float - float spacingVertical; - float shadowX; - float shadowY; - float shadowPerspective; // ? - float shadowR; // ? - float shadowG; // ? - float shadowB; // ? - float shadowA; // ? - uint32 unknown5; // dunno whether hex or float - uint32 unknown6; // dunno whether hex or float - uint32 unknown7; // dunno whether hex or float - float unknown8; - float unknown9; - float unknown10; - float unknown11; - uint32 unknown12; // dunno whether hex or float? -}); // 0x0D -PACK_STRUCT(RCOObjSModel, { - RCOObjPos pos; - rRCORef model; -}); // 0x0E -PACK_STRUCT(RCOObjSSpin, { - RCOObjPos pos; - uint32 unknown; // dunno whether hex or float - uint32 unknown2; - rRCORef ref1; - rRCORef ref2; - rRCORef event1; - rRCORef event2; - rRCORef event3; - rRCORef ref3; - rRCORef ref4; - rRCORef ref5; - rRCORef ref6; - rRCORef ref7; -}); // 0x0F -PACK_STRUCT(RCOObjSAction, { - rRCORef ref; -}); // 0x10 -PACK_STRUCT(RCOObjSItemSpin, { - RCOObjPos pos; - uint32 unknown; - uint32 unknown2; - uint32 unknown3; - uint32 unknown4; - uint32 unknown5; - float unknown6; - rRCORef ref1; - rRCORef ref2; - rRCORef event1; - rRCORef event2; - rRCORef ref3; - rRCORef ref4; - rRCORef ref5; - rRCORef ref6; - rRCORef objPrev; - rRCORef objNext; -}); // 0x11 -PACK_STRUCT(RCOObjSGroup, { - RCOObjPos pos; -}); // 0x12 -PACK_STRUCT(RCOObjSLList, { - RCOObjPos pos; - uint32 unknown; - uint32 unknown2; // dunno whether hex or float - float unknown3; - rRCORef ref1; - rRCORef ref2; - rRCORef ref3; - rRCORef ref4; - rRCORef ref5; - rRCORef ref6; - rRCORef event; - rRCORef ref7; -}); // 0x13 -PACK_STRUCT(RCOObjSLItem, { - rRCORef text; - rRCORef ref1; - rRCORef ref2; -}); // 0x14 -PACK_STRUCT(RCOObjSEdit, { - RCOObjPos pos; - uint32 unknown; // dunno whether hex or float - uint32 unknown2; // dunno whether hex or float - uint32 unknown3; // dunno whether hex or float - uint32 unknown4; // dunno whether hex or float - rRCORef ref1; - rRCORef ref2; - rRCORef event1; - rRCORef ref3; - rRCORef ref4; - rRCORef ref5; - rRCORef event2; - rRCORef event3; - rRCORef obj1; - rRCORef obj2; - rRCORef ref6; -}); // 0x15 -PACK_STRUCT(RCOObjSClock, { - RCOObjPos pos; - uint32 unknown; - float unknown2; - rRCORef text1; - rRCORef text2; - rRCORef ref1; - rRCORef ref2; - rRCORef event1; - rRCORef event2; - rRCORef ref3; - rRCORef ref4; - rRCORef event3; - rRCORef event4; - rRCORef ref5; - rRCORef ref6; - rRCORef event5; -}); // 0x16 -PACK_STRUCT(RCOObjSIList, { - RCOObjPos pos; - float unknown; - rRCORef ref1; - rRCORef ref2; - rRCORef event; - rRCORef ref3; -}); // 0x17 -PACK_STRUCT(RCOObjSIItem, { - rRCORef textDefault; - rRCORef textError; -}); // 0x18 -PACK_STRUCT(RCOObjSIcon, { - RCOObjPos pos; - rRCORef img1; - rRCORef img2; - rRCORef ref1; -}); // 0x19 -PACK_STRUCT(RCOObjSUButton, { - RCOObjPos pos; - rRCORef img1; - rRCORef event1; // onPush - rRCORef event2; // onFocusIn - rRCORef event3; // [event/anim] onFocusOut - rRCORef ref1; // [object/event] - rRCORef ref2; // [object/event] - rRCORef ref3; // [object/event] prev/up? - rRCORef ref4; // [object/event] next/down? - uint32 unknown; // 0xFFFFFFFF or 0 -}); // 0x1A - - - - -PACK_STRUCT(RCOAnimSPos, { - rRCORef obj; - float time; - uint32 unknown; - float x; - float y; - float unknown2; -}); -PACK_STRUCT(RCOAnimSColour, { - rRCORef obj; - float time; - uint32 unknown; // dunno whether float or int (probably int) - float r; - float g; - float b; - float a; -}); -PACK_STRUCT(RCOAnimSRotate, { // TODO: this one needs checking - rRCORef obj; - float time; - uint32 unknown; // dunno whether float or int (probably int) - uint32 unknown2; // dunno whether float or int (probably int) - uint32 unknown3; // dunno whether float or int (probably int) - uint32 rotation; -}); -PACK_STRUCT(RCOAnimSScale, { - rRCORef obj; - float time; - uint32 unknown; - float w; - float h; - float unknown2; -}); -PACK_STRUCT(RCOAnimSAlpha, { - rRCORef obj; - float time; - uint32 unknown; - float a; -}); -PACK_STRUCT(RCOAnimSDelay, { - float time; -}); -PACK_STRUCT(RCOAnimSEvent, { - rRCORef event; -}); -PACK_STRUCT(RCOAnimSLock, { - uint32 unknown; // always seems to be 0xFFFFFFFF -}); -PACK_STRUCT(RCOAnimSUnlock, { - uint32 unknown; // always seems to be 0xFFFFFFFF -}); -PACK_STRUCT(RCOAnimSSlideout, { - rRCORef obj; - uint32 unknown; - uint32 unknown2; - float unknown3; - uint32 unknown4; - uint32 unknown5; - float unknown6; -}); -*/ - - -#endif + + +#ifndef __RCOMAIN_H__ +#define __RCOMAIN_H__ + +#include "general.h" +#include "rcofile.h" +#include +#include + +#define MAX_FILENAME_LEN 260 + +typedef struct { + int packHeader, packImg, packImgCompr, packModel, packText; + int zlibMethod, zlibLevel; + #define WRITERCO_ZLIB_METHOD_ZDEFAULT Z_DEFAULT_STRATEGY + #define WRITERCO_ZLIB_METHOD_ZFILTERED Z_FILTERED + #define WRITERCO_ZLIB_METHOD_ZHUFFMAN Z_HUFFMAN_ONLY + #define WRITERCO_ZLIB_METHOD_ZRLE Z_RLE + #define WRITERCO_ZLIB_METHOD_ZFIXED Z_FIXED + #define WRITERCO_ZLIB_METHOD_7Z Z_USE_7Z + int rlzMode; +} writerco_options; + +typedef struct __rRCOEntry { + uint8 type; // main table uses 0x01; may be used as a current entry depth value + uint8 id; + //char* label; + uint labelOffset; + + uint offset; // absolute offset of this entry in file (only used when reading/writing RCOs - means nothing otherwise; also, writing the RCO may change this value) + // this value is also used to store the line number of the node when reading an XML + + struct __rRCOFile* rco; + struct __rRCOEntry* parent; + struct __rRCOEntry* firstChild; + struct __rRCOEntry* lastChild; + struct __rRCOEntry* prev; + struct __rRCOEntry* next; + uint numSubentries; + + void* extra; + uint extraLen; // in bytes + + // for entries with attached data + char srcFile[MAX_FILENAME_LEN]; + uint srcAddr; + uint srcLen; + uint srcLenUnpacked; + uint srcCompression; // use RCO_DATA_COMPRESSION_* constants in rcofile.h + void* srcBuffer; // work around for reading XML; should only be used for sound and text entries; need to check if srcFile contains a '*', then use this +} rRCOEntry; + + + +typedef struct { + uint16 lang; + uint16 format; + uint32 numIndexes; + RCOTextIndex* indexes; +} rRCOTextEntry; +/* packed_struct { + char* label; + uint length; + uint32 offset; +} rRCOTextIndex; */ +typedef struct { + rRCOEntry* textEntry; + RCOTextIndex* index; +} rRCOTextIdxPtr; + + + +typedef struct __rRCOFile { + + rRCOEntry tblMain; + + char* labels; + uint labelsLen; + char* events; + uint eventsLen; + + /* + // pointer segments + rRCOTextIdxPtr* ptrText; + uint numPtrText; + rRCOEntry** ptrImg; + uint numPtrImg; + rRCOEntry** ptrModel; + uint numPtrModel; + rRCOEntry** ptrSound; + uint numPtrSound; + rRCOEntry** ptrObj; + uint numPtrObj; + rRCOEntry** ptrAnim; + uint numPtrAnim; + */ + + // shortcuts + rRCOEntry* tblVSMX; + rRCOEntry* tblText; + rRCOEntry* tblImage; + rRCOEntry* tblSound; + rRCOEntry* tblModel; + rRCOEntry* tblFont; + rRCOEntry* tblObj; + rRCOEntry* tblAnim; + + // additional info about the source (mainly used for displaying info about the RCO) + uint32 verId; // offset 0x04 in file + uint umdFlag; + uint headerCompression; + Bool eSwap; + Bool ps3; + +} rRCOFile; + + +typedef struct { + uint32 type; + void* ptr; // will usu be rRCOEntry*, but may be char* for events; NULL if type is nothing + uint32 rawPtr; // raw value from source - means nothing if type is known +} rRCORef; + + +typedef struct { + uint format; + uint compression; + uint unkCompr; // unknown - usually 0, some PS3 rcos have 1 here +} rRCOImgModelEntry; + +typedef struct { + uint16 format; // 0x01 = VAG + uint16 channels; // 1 or 2 channels + uint32* channelData; //size/offset pairs +} rRCOSoundEntry; + +typedef struct { + uint format; // 1 + uint compression; // 0 + uint unknown; + uint unknown2; +} rRCOFontEntry; + +#define RCO_TEXT_FMT_CHARWIDTH(t) ((t) == RCO_TEXT_FMT_UTF32 ? 4 : (((t) == RCO_TEXT_FMT_UTF8)) ? 1 : 2) + + +#define RCO_TAGMAP_SIZE 50 +#define RCO_OBJMAP_SIZE 100 +typedef char ((*RcoTagMap)[RCO_TAGMAP_SIZE][30]); +typedef char ((*RcoObjMap)[RCO_OBJMAP_SIZE][30]); +typedef int ((*RcoObjTypes)[RCO_OBJMAP_SIZE]); + +extern int* RCO_OBJ_EXTRA_LEN; +extern uint RCO_OBJ_EXTRA_LEN_NUM; + +#define RCO_OBJANIM_IS_REF(v) (\ + (v == RCO_OBJ_EXTRA_TYPE_REF) || \ + (v == RCO_OBJ_EXTRA_TYPE_EVENT) || \ + (v == RCO_OBJ_EXTRA_TYPE_IMG) || \ + (v == RCO_OBJ_EXTRA_TYPE_OBJ) || \ + (v == RCO_OBJ_EXTRA_TYPE_TEXT) || \ + (v == RCO_OBJ_EXTRA_TYPE_MODEL) || \ + (v == RCO_OBJ_EXTRA_TYPE_FONT) \ +) +#define RCO_OBJ_IS_REF(a, b) RCO_OBJANIM_IS_REF(RCO_OBJ_EXTRA_TYPES[a][b]) +#define RCO_ANIM_IS_REF(a, b) RCO_OBJANIM_IS_REF(RCO_ANIM_EXTRA_TYPES[a][b]) + +// this doesn't include position info +extern RcoObjMap RCO_OBJ_EXTRA_NAMES; + +#define RCO_OBJ_EXTRA_TYPE_UNK 0 +#define RCO_OBJ_EXTRA_TYPE_FLOAT 1 +#define RCO_OBJ_EXTRA_TYPE_INT 2 +#define RCO_OBJ_EXTRA_TYPE_REF 3 // unknown reference +#define RCO_OBJ_EXTRA_TYPE_EVENT 4 +#define RCO_OBJ_EXTRA_TYPE_IMG 5 +#define RCO_OBJ_EXTRA_TYPE_OBJ 6 +#define RCO_OBJ_EXTRA_TYPE_TEXT 7 +#define RCO_OBJ_EXTRA_TYPE_MODEL 8 +#define RCO_OBJ_EXTRA_TYPE_FONT 9 + +extern RcoObjTypes RCO_OBJ_EXTRA_TYPES; + +extern int* RCO_ANIM_EXTRA_LEN; +extern uint RCO_ANIM_EXTRA_LEN_NUM; + +// this doesn't include references +extern RcoObjMap RCO_ANIM_EXTRA_NAMES; + +// we'll use the RCO_OBJ_EXTRA_TYPE_* constants here +extern RcoObjTypes RCO_ANIM_EXTRA_TYPES; + + + + + + + + + +rRCOFile* read_rco(char* fn); +Bool write_rco(rRCOFile* rco, char* fn, writerco_options opts); +void free_rco(rRCOFile* f); +void rco_map_func(rRCOFile* rco, rRCOEntry* parent, void* arg, void(*func)(rRCOFile* rco, rRCOEntry* entry, void* arg)); +char* get_label_from_offset(char* labels, uint labelOffset); +void* read_resource(rRCOEntry* entry, uint* outLen); +#define RCO_DATA_COMPRESSION_RCO 0xFF + +//rRCOEntry* rco_add_entry(rRCOEntry* parent, rRCOEntry* newEntry, int pos); + +void rco_fix_decomp_sizes(rRCOFile* rco, rRCOEntry* entry); + +uint count_all_subentries(rRCOEntry* entry); +rRCOEntry** make_sorted_list_of_subentries(rRCOEntry* parent, int(*compar)(const rRCOEntry**, const rRCOEntry**)); + +rRCOEntry* find_entry_from_label(rRCOEntry* parent, const char* s); +int find_text_from_label(char* labels, rRCOTextEntry* textExtra, const char* s); + +void make_iconv_charset(char out[8], int fmt, Bool es); + + +void es_rcoHeader(PRFHeader* h); +void es_rcoEntry(RCOEntry* e); +void es_rcoVsmxEntry(RCOVSMXEntry* e); +void es_rcoFontEntry(RCOFontEntry* e); +void es_rcoTextEntry(RCOTextEntry* e); +void es_rcoTextIndex(RCOTextIndex* i); +void es_rcoImgModelEntry(RCOImgModelEntry* e); +void es_rcoSoundEntry(RCOSoundEntry* e); +void es_headerComprInfo(HeaderComprInfo* hci); +void es_textComprInfo(TextComprInfo* tci); +void es_extraObjAnim(Bool isObj, int type, void* data, Bool isPS3); + + + + +PACK_STRUCT(RCOObjPos, { + float posX; + float posY; + float objScale; + float colR; + float colG; + float colB; + float colA; // RGBA colour weights + float dimW; + float dimH; + float unknown; + float sclX; + float sclY; // scale values + float elemScale; + uint32 iconOffset; // unknown weird value, appears to affect icon offsetting. Only lowest byte appears to have any effects - upper nibble appears to affect Y offset, and lower nibble affects X offset somehow. + rRCORef loadAction; //the event/anim executed on image load (note, load, not display) or describes when image is loaded? (eg onShadowInit) +}); + +/* +PACK_STRUCT(RCOObjSPage, { + uint32 unknown; // usu 0x111, sometimes 0xFFFF (only seen in UMD video RCOs) + rRCORef event1; + rRCORef event2; + rRCORef event3; + rRCORef event4; +}); // 0x01 +PACK_STRUCT(RCOObjSPlane, { + RCOObjPos pos; + rRCORef image; + uint32 unknownH; // either 0 or 0xFFFF. Has weird effect on height. Upper 2 bytes seem to have no effect. +}); // 0x02 +PACK_STRUCT(RCOObjSButton, { +// this page seems to be for buttons/pane items. for "buttons", has events for "Push", "FocusIn", "FocusOut" etc + RCOObjPos pos; + rRCORef image; + rRCORef shadow; // usu this & above images are img/shadow pairs + rRCORef image2; + rRCORef ref; + rRCORef event1; + rRCORef event2; + rRCORef event3; + rRCORef event4; + rRCORef event5; + rRCORef event6; + rRCORef event7; + rRCORef event8; + uint32 unknown; // appears to be 0xFFFFFFFF or 0 +}); // 0x03 +PACK_STRUCT(RCOObjSXmenu, { // only used for XMB menu? + RCOObjPos pos; + uint32 unknown; + rRCORef event1; + rRCORef event2; + rRCORef event3; + rRCORef event4; + rRCORef ref; +}); // 0x04 +PACK_STRUCT(RCOObjSXmList, { // only used for XMB menu icons? + uint32 unknown; // dunno if hex or float + rRCORef image; + rRCORef ref; +}); // 0x05 +PACK_STRUCT(RCOObjSXList, { + RCOObjPos pos; + uint32 unknown; + rRCORef event1; + rRCORef event2; + rRCORef event3; + rRCORef event4; + rRCORef event5; + rRCORef event6; + rRCORef event7; +}); // 0x06 +PACK_STRUCT(RCOObjSProgress, { + RCOObjPos pos; + float unknown; + uint32 unknown2; // dunno if hex or float + rRCORef ref1; + rRCORef ref2; + rRCORef ref3; +}); // 0x07 +PACK_STRUCT(RCOObjSScroll, { // used for ilist? + RCOObjPos pos; + float unknown; + float unknown2; + uint32 unknown3; // dunno if hex or float + rRCORef ref1; + rRCORef ref2; + rRCORef ref3; + rRCORef ref4; + rRCORef ref5; +}); // 0x08 +PACK_STRUCT(RCOObjSMList, { // seems to be used for lists/menus + RCOObjPos pos; + uint32 unknown; + uint32 unknown2; // dunno if hex or float + uint32 unknown3; + float unknown4; + float unknown5; + rRCORef ref1; + rRCORef event1; + rRCORef ref2; + rRCORef ref3; + rRCORef ref4; + rRCORef event2; + rRCORef ref5; + rRCORef ref6; + rRCORef ref7; + rRCORef event3; + rRCORef ref8; +}); // 0x09 +PACK_STRUCT(RCOObjSMItem, { // menu item? + rRCORef text; + rRCORef textAlt; + rRCORef ref; +}); // 0x0A +PACK_STRUCT(RCOObjSXItem, { + rRCORef image; + rRCORef text; + rRCORef ref; +}); // 0x0C +PACK_STRUCT(RCOObjSText, { + RCOObjPos pos; + rRCORef text; // the text displayed + rRCORef ref1; + uint32 unknownAlign; // bottom byte: something to do with multiline text align; 1=left align, 2=right, anything else=center + uint32 unknown; + float size; + float topR; // top RGB values + float topG; + float topB; + float bottomR; // bottom RGB values + float bottomG; + float bottomB; + float spacingHorizontal; + uint32 unknown2; // dunno whether hex or float + uint32 unknown3; // dunno whether hex or float + uint32 unknown4; // dunno whether hex or float + float spacingVertical; + float shadowX; + float shadowY; + float shadowPerspective; // ? + float shadowR; // ? + float shadowG; // ? + float shadowB; // ? + float shadowA; // ? + uint32 unknown5; // dunno whether hex or float + uint32 unknown6; // dunno whether hex or float + uint32 unknown7; // dunno whether hex or float + float unknown8; + float unknown9; + float unknown10; + float unknown11; + uint32 unknown12; // dunno whether hex or float? +}); // 0x0D +PACK_STRUCT(RCOObjSModel, { + RCOObjPos pos; + rRCORef model; +}); // 0x0E +PACK_STRUCT(RCOObjSSpin, { + RCOObjPos pos; + uint32 unknown; // dunno whether hex or float + uint32 unknown2; + rRCORef ref1; + rRCORef ref2; + rRCORef event1; + rRCORef event2; + rRCORef event3; + rRCORef ref3; + rRCORef ref4; + rRCORef ref5; + rRCORef ref6; + rRCORef ref7; +}); // 0x0F +PACK_STRUCT(RCOObjSAction, { + rRCORef ref; +}); // 0x10 +PACK_STRUCT(RCOObjSItemSpin, { + RCOObjPos pos; + uint32 unknown; + uint32 unknown2; + uint32 unknown3; + uint32 unknown4; + uint32 unknown5; + float unknown6; + rRCORef ref1; + rRCORef ref2; + rRCORef event1; + rRCORef event2; + rRCORef ref3; + rRCORef ref4; + rRCORef ref5; + rRCORef ref6; + rRCORef objPrev; + rRCORef objNext; +}); // 0x11 +PACK_STRUCT(RCOObjSGroup, { + RCOObjPos pos; +}); // 0x12 +PACK_STRUCT(RCOObjSLList, { + RCOObjPos pos; + uint32 unknown; + uint32 unknown2; // dunno whether hex or float + float unknown3; + rRCORef ref1; + rRCORef ref2; + rRCORef ref3; + rRCORef ref4; + rRCORef ref5; + rRCORef ref6; + rRCORef event; + rRCORef ref7; +}); // 0x13 +PACK_STRUCT(RCOObjSLItem, { + rRCORef text; + rRCORef ref1; + rRCORef ref2; +}); // 0x14 +PACK_STRUCT(RCOObjSEdit, { + RCOObjPos pos; + uint32 unknown; // dunno whether hex or float + uint32 unknown2; // dunno whether hex or float + uint32 unknown3; // dunno whether hex or float + uint32 unknown4; // dunno whether hex or float + rRCORef ref1; + rRCORef ref2; + rRCORef event1; + rRCORef ref3; + rRCORef ref4; + rRCORef ref5; + rRCORef event2; + rRCORef event3; + rRCORef obj1; + rRCORef obj2; + rRCORef ref6; +}); // 0x15 +PACK_STRUCT(RCOObjSClock, { + RCOObjPos pos; + uint32 unknown; + float unknown2; + rRCORef text1; + rRCORef text2; + rRCORef ref1; + rRCORef ref2; + rRCORef event1; + rRCORef event2; + rRCORef ref3; + rRCORef ref4; + rRCORef event3; + rRCORef event4; + rRCORef ref5; + rRCORef ref6; + rRCORef event5; +}); // 0x16 +PACK_STRUCT(RCOObjSIList, { + RCOObjPos pos; + float unknown; + rRCORef ref1; + rRCORef ref2; + rRCORef event; + rRCORef ref3; +}); // 0x17 +PACK_STRUCT(RCOObjSIItem, { + rRCORef textDefault; + rRCORef textError; +}); // 0x18 +PACK_STRUCT(RCOObjSIcon, { + RCOObjPos pos; + rRCORef img1; + rRCORef img2; + rRCORef ref1; +}); // 0x19 +PACK_STRUCT(RCOObjSUButton, { + RCOObjPos pos; + rRCORef img1; + rRCORef event1; // onPush + rRCORef event2; // onFocusIn + rRCORef event3; // [event/anim] onFocusOut + rRCORef ref1; // [object/event] + rRCORef ref2; // [object/event] + rRCORef ref3; // [object/event] prev/up? + rRCORef ref4; // [object/event] next/down? + uint32 unknown; // 0xFFFFFFFF or 0 +}); // 0x1A + + + + +PACK_STRUCT(RCOAnimSPos, { + rRCORef obj; + float time; + uint32 unknown; + float x; + float y; + float unknown2; +}); +PACK_STRUCT(RCOAnimSColour, { + rRCORef obj; + float time; + uint32 unknown; // dunno whether float or int (probably int) + float r; + float g; + float b; + float a; +}); +PACK_STRUCT(RCOAnimSRotate, { // TODO: this one needs checking + rRCORef obj; + float time; + uint32 unknown; // dunno whether float or int (probably int) + uint32 unknown2; // dunno whether float or int (probably int) + uint32 unknown3; // dunno whether float or int (probably int) + uint32 rotation; +}); +PACK_STRUCT(RCOAnimSScale, { + rRCORef obj; + float time; + uint32 unknown; + float w; + float h; + float unknown2; +}); +PACK_STRUCT(RCOAnimSAlpha, { + rRCORef obj; + float time; + uint32 unknown; + float a; +}); +PACK_STRUCT(RCOAnimSDelay, { + float time; +}); +PACK_STRUCT(RCOAnimSEvent, { + rRCORef event; +}); +PACK_STRUCT(RCOAnimSLock, { + uint32 unknown; // always seems to be 0xFFFFFFFF +}); +PACK_STRUCT(RCOAnimSUnlock, { + uint32 unknown; // always seems to be 0xFFFFFFFF +}); +PACK_STRUCT(RCOAnimSSlideout, { + rRCORef obj; + uint32 unknown; + uint32 unknown2; + float unknown3; + uint32 unknown4; + uint32 unknown5; + float unknown6; +}); +*/ + + +#endif diff --git a/rcoreader.c b/rcoreader.c index 207b255..7066ea6 100644 --- a/rcoreader.c +++ b/rcoreader.c @@ -1,1083 +1,1083 @@ - -#include -#include -#include -#include "rcofile.h" -#include "rcomain.h" -#include "configscan.h" - -#define MAX_LABEL_DATA 16777216 // 16MB of data; also used for event/text data -#define MAX_TREE_DATA 33554432 // 32MB of data -#define MAX_SUBENTRIES 65536 // used for max num text indexes too - - - - -// items used only when reading the file -typedef struct { - char* fName; - uint fSize; - uint fSizeExpanded; // file size with decompressed header - - void* tables; // decompressed tables - uint tablesSize; - uint memPos; - uint memOffset; - - FILE* fp; // for uncompressed tables - - uint32* ptrsObj; - uint numObjPtrs; - uint32* ptrsAnim; - uint numAnimPtrs; - - Bool ps3; // TRUE if PS3 RCO - -} rRCOFile_readhelper; - - - -void read_entry(rRCOFile_readhelper* rcoH, rRCOFile* rco, rRCOEntry* data, Bool readSubEntries); -rRCOEntry* find_entry_from_offset(rRCOEntry* parent, uint32 offset); -Bool check_file_region(uint fSize, uint offset, uint size); -uint rco_fread(rRCOFile_readhelper* rcoH, void* buf, uint len); -uint rcoread_ftell(rRCOFile_readhelper* rcoH); -int rcoread_fseek(rRCOFile_readhelper* rcoH, uint pos); - -void fix_refs(rRCOFile* rco, rRCOEntry* entry, const int* lenArray, const uint lenNum, Bool isObj); - - - - -rRCOFile* read_rco(char* fn) { - rRCOFile_readhelper rcoH; - rRCOFile* rco = (rRCOFile*)malloc(sizeof(rRCOFile)); - rcoH.fName = fn; - rcoH.fp = fopen(fn, "rb"); - - if(!rcoH.fp) { - error("Unable to open file %s", fn); - return NULL; - } - - fseek(rcoH.fp, 0, SEEK_END); - rcoH.fSizeExpanded = rcoH.fSize = ftell(rcoH.fp); - rewind(rcoH.fp); - - PRFHeader header; - if(!check_file_region(rcoH.fSize, 0, sizeof(header))) { - error("File too small to be a valid RCO file."); - return NULL; - } - fileread(rcoH.fp, &header, sizeof(header)); - - // check for endian swapped signature - if(header.signature == ENDIAN_SWAP_32(RCO_SIGNATURE)) { - rco->eSwap = TRUE; - es_rcoHeader(&header); - } else - rco->eSwap = FALSE; - - if(header.signature != RCO_SIGNATURE) { - error("[header] Invalid signature - not a valid RCO file."); - return NULL; - } - if(header.null != 0) { - warning("[header] Unexpected value @ 0x8: 0x%d (expected: 0x0).", header.null); - } - - if(header.pUnknown != RCO_NULL_PTR) { - warning("[header] Unknown RCO section @ 0x28 (points to 0x%x)", header.pUnknown); - } - //if(header.pFontTable != RCO_NULL_PTR) { - // warning("[header] Unknown RCO section @ 0x2C (points to 0x%x)", header.pFontTable); - //} - if(header.unknown[0] != 0xFFFFFFFF || header.unknown[1] != 0xFFFFFFFF || header.unknown[2] != 0xFFFFFFFF) { - warning("[header] Unexpected value(s) in range 0x98-0xA4"); - } - - rco->umdFlag = header.compression & 0xF; // actually, I'm usure about this, but meh - rco->headerCompression = header.compression >> 4; - rco->verId = header.version; - - { - info("RCO header info:"); - if(rco->eSwap) { - info(" Endian = big (PS3)"); - } else { - info(" Endian = little (PSP)"); - } - info(" VersionID = 0x%x", rco->verId); - info(" Compression = 0x%x", rco->headerCompression); - info(" UMDFlag = %d", rco->umdFlag); - info(" MainTree Offset = 0x%x", header.pMainTable); - if(header.pVSMXTable != RCO_NULL_PTR) - info(" VSMXTree Offset = 0x%x", header.pVSMXTable); - if(header.pTextTable != RCO_NULL_PTR) - info(" TextTree Offset = 0x%x", header.pTextTable); - if(header.pSoundTable != RCO_NULL_PTR) - info(" SoundTree Offset = 0x%x", header.pSoundTable); - if(header.pModelTable != RCO_NULL_PTR) - info(" ModelTree Offset = 0x%x", header.pModelTable); - if(header.pFontTable != RCO_NULL_PTR) - info(" FontTree Offset = 0x%x", header.pFontTable); - if(header.pImgTable != RCO_NULL_PTR) - info(" ImageTree Offset = 0x%x", header.pImgTable); - if(header.pObjTable != RCO_NULL_PTR) - info(" ObjectTree Offset = 0x%x", header.pObjTable); - if(header.pAnimTable != RCO_NULL_PTR) - info(" AnimTree Offset = 0x%x", header.pAnimTable); - - if(header.lTextData) - info(" TextData Offset = 0x%x [length = 0x%x]", header.pTextData, header.lTextData); - if(header.lLabelData) - info(" NameData Offset = 0x%x [length = 0x%x]", header.pLabelData, header.lLabelData); - if(header.lEventData) - info(" EventData Offset = 0x%x [length = 0x%x]", header.pEventData, header.lEventData); - - if(header.lImgData) - info(" ImageData Offset = 0x%x [length = 0x%x]", header.pImgData, header.lImgData); - if(header.lSoundData) - info(" SoundData Offset = 0x%x [length = 0x%x]", header.pSoundData, header.lSoundData); - if(header.lModelData) - info(" ModelData Offset = 0x%x [length = 0x%x]", header.pModelData, header.lModelData); - } - - // check compression headers - switch(rco->headerCompression) { - case RCO_DATA_COMPRESSION_NONE: - break; - case RCO_DATA_COMPRESSION_ZLIB: - if(header.version < 0x90) - warning("[header] Unexpected version 0x%x for compression type", header.version); - break; - case RCO_DATA_COMPRESSION_RLZ: - if(header.version < 0x95) - warning("[header] Unexpected version 0x%x for compression type", header.version); - break; - - default: - error("[header] Unknown compression type 0x%x - process cannot continue.", rco->headerCompression); - return NULL; - /* warning("[header] Unknown compression type 0x%x - assuming uncompressed data.", rco->headerCompression); - // set it to no compression - rco->headerCompression = RCO_DATA_COMPRESSION_NONE; */ - } - - rco->ps3 = (rco->eSwap); // && header.version >= 0x97 - configLoadObjmap(rco->ps3); - configLoadAnimmap(rco->ps3); - //rcoH.ps3 = (rco->eSwap && header.version >= 0x107); - - // TODO: check file regions for decompression - - rcoH.tablesSize = 0; - rcoH.tables = 0; - rcoH.memPos = 0; - if(rco->headerCompression) { - rcoH.memOffset = ftell(rcoH.fp); // should = 0xA4 - HeaderComprInfo ci; - if(!fileread(rcoH.fp, &ci, sizeof(ci))) { - error("[header] Unable to read in compression info!"); - return NULL; - } - if(rco->eSwap) es_headerComprInfo(&ci); - info(" Header compression: compressed = 0x%x bytes, uncompressed = 0x%x bytes", ci.lenPacked, ci.lenUnpacked); - - rcoH.tablesSize = ci.lenUnpacked; - if(ci.lenUnpacked > MAX_TREE_DATA || ci.lenPacked > MAX_TREE_DATA) { - error("[header] Size of tree data exceeds sane limit. This is probably a bad RCO."); - return NULL; - } - - rcoH.tables = malloc(ci.lenUnpacked); - - switch(rco->headerCompression) { - case RCO_DATA_COMPRESSION_ZLIB: - { - // uses a bit of memory... - could use zlib's inflate routines, but eh, I doubt it really matters that much... - void* readBuf = malloc(ci.lenPacked); - fileread(rcoH.fp, readBuf, ci.lenPacked); - int uRet = zlib_uncompress(rcoH.tables, ci.lenUnpacked, readBuf, ci.lenPacked); - if(uRet != Z_OK && uRet != Z_DATA_ERROR) { - error("[entries] Unable to decompress tree entries!"); - return FALSE; - } else if(uRet == Z_DATA_ERROR) { - warning("Encountered 'data error' when decompressing tree entries."); - } - free(readBuf); - - rcoH.fSizeExpanded += ci.lenUnpacked - ALIGN_TO_4(ci.lenPacked); - } - - break; - - case RCO_DATA_COMPRESSION_RLZ: - error("[header] This RCO uses RLZ compression which currently cannot be decompressed with rcomage. (use Z33's Resurssiklunssi to decompress the RCO)"); - return NULL; - - default: // this won't actually ever be executed due to the new compression checking code above... :/ - error("[header] Unknown compression method specified (0x%x) - can't continue.", rco->headerCompression); - return NULL; - } - - - // decompress text data - if(header.pTextData != RCO_NULL_PTR && header.lTextData) { - fseek(rcoH.fp, header.pTextData, SEEK_SET); // TODO: check offset first! - TextComprInfo tci; - - info("TextData Compression info:"); - - rcoH.tablesSize = ALIGN_TO_4(rcoH.tablesSize); - do { - if(!fileread(rcoH.fp, &tci, sizeof(tci))) { - error("Failed to read in text compression info."); - return NULL; - } - if(rco->eSwap) es_textComprInfo(&tci); - - if(tci.unknown != 0x1) warning("[text-data] Unexpected value (0x%x) @ 0x2 - expected 0x1", tci.unknown); - info(" LangID = 0x%x, compressed = 0x%x bytes, uncompressed = 0x%x bytes", tci.lang, tci.packedLen, tci.unpackedLen); - - if(tci.unpackedLen > MAX_LABEL_DATA || tci.packedLen > MAX_LABEL_DATA) { - error("[text-data] Size of text data exceeds sane limits."); - return NULL; - } - uint oldSize = rcoH.tablesSize; - rcoH.tablesSize += ALIGN_TO_4(tci.unpackedLen); - rcoH.tables = realloc(rcoH.tables, rcoH.tablesSize); - - void* readBuf = malloc(tci.packedLen); - fileread(rcoH.fp, readBuf, tci.packedLen); - int uRet = zlib_uncompress((char*)rcoH.tables + oldSize, tci.unpackedLen, readBuf, tci.packedLen); - if(uRet != Z_OK && uRet != Z_DATA_ERROR) { - error("[text-data] Unable to decompress text data!"); - return FALSE; - } else if(uRet == Z_DATA_ERROR) { - warning("Encountered 'data error' when decompressing text data."); - } - free(readBuf); - - rcoH.fSizeExpanded += ALIGN_TO_4(tci.unpackedLen) - ALIGN_TO_4(tci.packedLen); // TODO: need to check if this is correct - - if(!tci.nextOffset) break; - int seekAmt = tci.nextOffset - sizeof(tci) - tci.packedLen; - if(seekAmt) - fseek(rcoH.fp, seekAmt, SEEK_CUR); - } while(tci.nextOffset); - } - } - - if(!check_file_region(rcoH.fSizeExpanded, header.pLabelData, header.lLabelData)) { - error("[header] Invalid label pointer/length specified."); - return NULL; - } - if(header.lLabelData > MAX_LABEL_DATA) { - header.lLabelData = MAX_LABEL_DATA; - warning("[labels] Total data length exceeds safety limit of 16MB - data has been truncated!"); - } - rco->labelsLen = header.lLabelData; - if(header.lLabelData) { - rco->labels = (char*)malloc(header.lLabelData); - rcoread_fseek(&rcoH, header.pLabelData); - rco_fread(&rcoH, rco->labels, header.lLabelData); - } - - if(!check_file_region(rcoH.fSizeExpanded, header.pEventData, header.lEventData)) { - error("[header] Invalid event pointer/length specified."); - return NULL; - } - if(header.lEventData > MAX_LABEL_DATA) { - warning("[events] Total data length (%d) exceeds safety limit of 16MB - data has been truncated!", header.lEventData); - header.lEventData = MAX_LABEL_DATA; - } - rco->eventsLen = header.lEventData; - if(header.lEventData) { - rco->events = (char*)malloc(header.lEventData); - rcoread_fseek(&rcoH, header.pEventData); - rco_fread(&rcoH, rco->events, header.lEventData); - } - - - // read pointer tables - // TODO: think about what to do with these - { - //rcoH.ptrsText = rcoH.ptrsImg = rcoH.ptrsModel = rcoH.ptrsSound = 0; - rcoH.ptrsObj = rcoH.ptrsAnim = 0; - - // macro to help save us some code - #define READ_RCO_READ_PTR_SEGMENT(hp, hl, dp, dl, s) \ - dp = 0; \ - dl = 0; \ - if(hp != RCO_NULL_PTR) { \ - if(!check_file_region(rcoH.fSizeExpanded, hp, hl)) { \ - error("[header] Invalid %s pointer/length specified.", s); \ - return NULL; \ - } \ - if((hl) > MAX_LABEL_DATA) { \ - warning("[%s] Total data length (%d) exceeds safety limit of 16MB - data has been truncated!", s, hl); \ - hl = MAX_LABEL_DATA; \ - } \ - dl = (hl) / sizeof(uint32); \ - hl = (dl) * sizeof(uint32); \ - if(dl) { \ - dp = (uint32*)malloc(hl); \ - rcoread_fseek(&rcoH, hp); \ - rco_fread(&rcoH, dp, hl); \ - } \ - } - /* - READ_RCO_READ_PTR_SEGMENT(header.pTextPtrs, header.lTextPtrs, rcoH.ptrsText, rcoH.numTextPtrs, "text"); - READ_RCO_READ_PTR_SEGMENT(header.pImgPtrs, header.lImgPtrs, rcoH.ptrsImg, rcoH.numImgPtrs, "image"); - READ_RCO_READ_PTR_SEGMENT(header.pModelPtrs, header.lModelPtrs, rcoH.ptrsModel, rcoH.numModelPtrs, "model"); - READ_RCO_READ_PTR_SEGMENT(header.pSoundPtrs, header.lSoundPtrs, rcoH.ptrsSound, rcoH.numSoundPtrs, "sound"); - */ - READ_RCO_READ_PTR_SEGMENT(header.pObjPtrs, header.lObjPtrs, rcoH.ptrsObj, rcoH.numObjPtrs, "object"); - READ_RCO_READ_PTR_SEGMENT(header.pAnimPtrs, header.lAnimPtrs, rcoH.ptrsAnim, rcoH.numAnimPtrs, "anim"); - - // TODO: possibly sort these for the following section (not that necessary - only useful if we find an unknown object/anim type) - // TODO: endian swap these entries if we intend to read them... - } - - - // read main/vsmx/text/sound/model/img/obj/anim tables - rcoread_fseek(&rcoH, header.pMainTable); - read_entry(&rcoH, rco, &(rco->tblMain), TRUE); - - // TODO: check for unreferenced labels / events? - - // assign shortcut tables - { - rco->tblVSMX = rco->tblText = rco->tblImage = rco->tblSound = rco->tblModel = rco->tblFont = rco->tblObj = rco->tblAnim = 0; - rRCOEntry* rcoNode; - for(rcoNode = rco->tblMain.firstChild; rcoNode; rcoNode = rcoNode->next) { - switch(rcoNode->id) { - #define READ_RCO_ASN_SHORTCUT_TBL(hp, sc, s) \ - if(hp == RCO_NULL_PTR) warning("[header] %c%s main tree not referenced but found in entries.", toupper(s[0]), s+1); \ - if(sc) { warning("[header] Multiple %s main trees found!", s); } \ - else { sc = rcoNode; } \ - break; - - case RCO_TABLE_VSMX: - READ_RCO_ASN_SHORTCUT_TBL(header.pVSMXTable, rco->tblVSMX, "VSMX"); - break; - case RCO_TABLE_TEXT: - READ_RCO_ASN_SHORTCUT_TBL(header.pTextTable, rco->tblText, "text"); - break; - case RCO_TABLE_IMG: - READ_RCO_ASN_SHORTCUT_TBL(header.pImgTable, rco->tblImage, "image"); - break; - case RCO_TABLE_SOUND: - READ_RCO_ASN_SHORTCUT_TBL(header.pSoundTable, rco->tblSound, "sound"); - break; - case RCO_TABLE_MODEL: - READ_RCO_ASN_SHORTCUT_TBL(header.pModelTable, rco->tblModel, "model"); - break; - case RCO_TABLE_FONT: - READ_RCO_ASN_SHORTCUT_TBL(header.pFontTable, rco->tblFont, "font"); - break; - case RCO_TABLE_OBJ: - READ_RCO_ASN_SHORTCUT_TBL(header.pObjTable, rco->tblObj, "object"); - break; - case RCO_TABLE_ANIM: - READ_RCO_ASN_SHORTCUT_TBL(header.pAnimTable, rco->tblAnim, "animation"); - break; - } - } - - if(header.pVSMXTable != RCO_NULL_PTR && !rco->tblVSMX) warning("[header] VSMX tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); - if(header.pTextTable != RCO_NULL_PTR && !rco->tblText) warning("[header] Text tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); - if(header.pImgTable != RCO_NULL_PTR && !rco->tblImage) warning("[header] Image tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); - if(header.pSoundTable != RCO_NULL_PTR && !rco->tblSound) warning("[header] Sound tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); - if(header.pModelTable != RCO_NULL_PTR && !rco->tblModel) warning("[header] Model tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); - if(header.pFontTable != RCO_NULL_PTR && !rco->tblFont) warning("[header] Font tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); - if(header.pObjTable != RCO_NULL_PTR && !rco->tblObj) warning("[header] Object tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); - if(header.pAnimTable != RCO_NULL_PTR && !rco->tblAnim) warning("[header] Animation tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); - } - - // add offset/len/compression info for text entries - if(rco->tblText && header.lTextData) { - // TODO: assign things for compressed text data - rRCOEntry* rcoNode = rco->tblText->firstChild; - if(rco->headerCompression != RCO_DATA_COMPRESSION_NONE) { - // if header is compressed, we've got to reparse the RCO file to get the positions :/ - FILE* fp2 = fopen(fn, "rb"); - HeaderComprInfo hci; - TextComprInfo tci; - - fseek(fp2, sizeof(header), SEEK_SET); - fileread(fp2, &hci, sizeof(hci)); - if(rco->eSwap) es_headerComprInfo(&hci); - fseek(fp2, ALIGN_TO_4(hci.lenPacked), SEEK_CUR); - - - // before parsing the file, we'll be on the safe side and blank out all text entries' src - for(; rcoNode; rcoNode = rcoNode->next) { - rcoNode->srcAddr = rcoNode->srcLenUnpacked = rcoNode->srcLen = 0; - rcoNode->srcCompression = RCO_DATA_COMPRESSION_NONE; - } - - - tci.nextOffset = 1; // dummy - while(tci.nextOffset) { - fileread(fp2, &tci, sizeof(tci)); - if(rco->eSwap) es_textComprInfo(&tci); - // search for correct entry to assign to - for(rcoNode = rco->tblText->firstChild; rcoNode; rcoNode = rcoNode->next) { - if(((rRCOTextEntry*)rcoNode->extra)->lang == tci.lang) { - rcoNode->srcAddr = ftell(fp2); - rcoNode->srcLenUnpacked = tci.unpackedLen; - rcoNode->srcLen = tci.packedLen; - rcoNode->srcCompression = rco->headerCompression; - break; - } - } - - if(!tci.nextOffset) break; - fseek(fp2, tci.nextOffset - sizeof(tci), SEEK_CUR); - } - - fclose(fp2); - } else { - for(; rcoNode; rcoNode = rcoNode->next) { - rcoNode->srcAddr = header.pTextData; - rcoNode->srcLenUnpacked = rcoNode->srcLen = header.lTextData; - rcoNode->srcCompression = RCO_DATA_COMPRESSION_NONE; - } - } - } - - // fix offsets for img/model/sound srcAddr - // TODO: really should put in the right values when traversing the tree instead of a post-fix - { - rRCOEntry* rcoNode; - if(rco->tblImage) { - for(rcoNode = rco->tblImage->firstChild; rcoNode; rcoNode = rcoNode->next) - rcoNode->srcAddr += header.pImgData; - } - if(rco->tblModel) { - for(rcoNode = rco->tblModel->firstChild; rcoNode; rcoNode = rcoNode->next) - rcoNode->srcAddr += header.pModelData; - } - if(rco->tblSound) { - for(rcoNode = rco->tblSound->firstChild; rcoNode; rcoNode = rcoNode->next) - rcoNode->srcAddr += header.pSoundData; - } - } - - // TODO: check for dupe labels - - // fix object/anim references - if(rco->tblObj) - fix_refs(rco, rco->tblObj, RCO_OBJ_EXTRA_LEN, RCO_OBJ_EXTRA_LEN_NUM, TRUE); - if(rco->tblAnim) - fix_refs(rco, rco->tblAnim, RCO_ANIM_EXTRA_LEN, RCO_ANIM_EXTRA_LEN_NUM, FALSE); - - - rco_fix_decomp_sizes(rco, &rco->tblMain); - - // check resources - { - // TODO: should use rcoH.fSizeExpanded here otherwise we stuff up the region tracking code - if(rco->tblText && rco->tblText->numSubentries) { - if(!check_file_region(rcoH.fSize, header.pTextData, header.lTextData)) - warning("[header] Text resource pointer/length is invalid."); - } - if(rco->tblImage && rco->tblImage->numSubentries) { - if(!check_file_region(rcoH.fSize, header.pImgData, header.lImgData)) - warning("[header] Image resource pointer/length is invalid."); - } - if(rco->tblSound && rco->tblSound->numSubentries) { - if(!check_file_region(rcoH.fSize, header.pSoundData, header.lSoundData)) - warning("[header] Sound resource pointer/length is invalid."); - } - if(rco->tblModel && rco->tblModel->numSubentries) { - if(!check_file_region(rcoH.fSize, header.pModelData, header.lModelData)) - warning("[header] Model resource pointer/length is invalid."); - } - - /* - rco->pDataText = header.pTextData; - rco->lDataText = header.lTextData; - if(!rco->lDataText) rco->pDataText = 0; - rco->pDataImg = header.pImgData; - rco->lDataImg = header.lImgData; - if(!rco->lDataImg) rco->pDataImg = 0; - rco->pDataSound = header.pSoundData; - rco->lDataSound = header.lSoundData; - if(!rco->lDataSound) rco->pDataSound = 0; - rco->pDataModel = header.pModelData; - rco->lDataModel = header.lModelData; - if(!rco->lDataModel) rco->pDataModel = 0; - - rco->attachSource = (char*)malloc(strlen(fn) +1); - strcpy(rco->attachSource, fn); - */ - } - - fclose(rcoH.fp); - if(rcoH.tables) free(rcoH.tables); - - /* if(rcoH.ptrsText) free(rcoH.ptrsText); - if(rcoH.ptrsImg) free(rcoH.ptrsImg); - if(rcoH.ptrsModel) free(rcoH.ptrsModel); - if(rcoH.ptrsSound) free(rcoH.ptrsSound); */ - if(rcoH.ptrsObj) free(rcoH.ptrsObj); - if(rcoH.ptrsAnim) free(rcoH.ptrsAnim); - - return rco; -} - -void read_entry(rRCOFile_readhelper* rcoH, rRCOFile* rco, rRCOEntry* data, Bool readSubEntries) { - - data->offset = rcoread_ftell(rcoH); - if(!check_file_region(rcoH->fSizeExpanded, data->offset, sizeof(RCOEntry))) { - error("[entry] Unable to read entry - file exhausted."); - return; - } - - RCOEntry re; - rco_fread(rcoH, &re, sizeof(re)); - if(rco->eSwap) es_rcoEntry(&re); - - data->type = re.typeId & 0xFF; - data->id = (re.typeId & 0xFF00) >> 8; - - if(re.blank != 0) { - warning("[entry (0x%x)] Unexpected entry value @ 0x2", data->offset); - } - data->labelOffset = RCO_NULL_PTR; - if(re.labelOffset != RCO_NULL_PTR) { - if(re.labelOffset >= rco->labelsLen) { - warning("[entry (0x%x)] Invalid label offset supplied - label removed", data->offset); - } else { - // WARNING! this assignment is unchecked (ie possible to cause this to point to anything) - data->labelOffset = re.labelOffset; - } - } - if(re.eHeadSize && re.eHeadSize != sizeof(re)) { - warning("[entry (0x%x)] Non-standard entry header size found!", data->offset); - } - - data->numSubentries = re.numSubentries; - data->rco = rco; - data->parent = data->firstChild = data->lastChild = data->prev = data->next = NULL; - if(re.blanks[0] != 0 || re.blanks[1] != 0) { - warning("[entry (0x%x)] Unexpected entry value in range 0x20-0x28", data->offset); - } - - - data->srcFile[0] = '\0'; - - data->extra = 0; - data->srcBuffer = NULL; - //data->extraSize = 0; - uint extraSize = 0; - { - // cbf checking for file exhaustion here - just let read calls fail - // a shortcut to help us - #define READ_ENTRY_MALLOC_AND_READ(s) { \ - extraSize = s; \ - data->extra = malloc(s); \ - if(!rco_fread(rcoH, data->extra, s)) { \ - error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); \ - return; \ - } \ - } - switch(data->id) { - case RCO_TABLE_MAIN: - if(data->type != 1) - warning("[entry (0x%x)] Unexpected 'main' type (0x%x)!", data->offset, data->type); - break; // nothing needs to be done - - case RCO_TABLE_VSMX: - if(data->type == 1) { - RCOVSMXEntry rve; - if(!rco_fread(rcoH, &rve, sizeof(rve))) { - error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); - return; - } - if(rco->eSwap) es_rcoVsmxEntry(&rve); - data->srcAddr = rcoread_ftell(rcoH) + rve.offset; - data->srcLen = data->srcLenUnpacked = rve.length; - strcpy(data->srcFile, rcoH->fName); - // need to consider possibility that VSMX table is compressed inside compressed RCO header - data->srcCompression = (rcoH->tables ? RCO_DATA_COMPRESSION_RCO : RCO_DATA_COMPRESSION_NONE); - - - extraSize = sizeof(RCOVSMXEntry) + rve.length; - // 4 byte alignment - extraSize = ALIGN_TO_4(extraSize); - rcoread_fseek(rcoH, rcoread_ftell(rcoH) + extraSize - sizeof(RCOVSMXEntry)); - } else - warning("[entry (0x%x)] Unknown VSMX type (0x%x)!", data->offset, data->type); - - break; - - case RCO_TABLE_TEXT: - if(data->type == 1) { - RCOTextEntry rte; - rRCOTextEntry* dest; - data->extraLen = sizeof(rRCOTextEntry); - data->extra = malloc(data->extraLen); - dest = (rRCOTextEntry*)data->extra; - if(!rco_fread(rcoH, &rte, sizeof(rte))) { - error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); - return; - } - if(rco->eSwap) es_rcoTextEntry(&rte); - - dest->lang = rte.lang; - dest->format = rte.format; - if(rte.format != RCO_TEXT_FMT_UTF8 && rte.format != RCO_TEXT_FMT_UTF16 && rte.format != RCO_TEXT_FMT_UTF32) { - warning("[entry (0x%x)] Unknown format for text entry (0x%x)", data->offset, rte.format); - } - dest->numIndexes = rte.numIndexes; - - - // alloc & read indexes - if(dest->numIndexes > MAX_SUBENTRIES) { - warning("[entry (0x%x)] Number of text indexes (0x%x) exceeds safety limit.", data->offset, ((rRCOTextEntry*)data->extra)->numIndexes); - dest->numIndexes = MAX_SUBENTRIES; - } - uint readAmt = dest->numIndexes *sizeof(RCOTextIndex); - extraSize = sizeof(RCOTextEntry) + readAmt; - dest->indexes = (RCOTextIndex*)malloc(readAmt); - if(!rco_fread(rcoH, dest->indexes, readAmt)) { - error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); - return; - } - if(rco->eSwap) { // endian swap all indexes - uint i; - for(i=0; inumIndexes; i++) - es_rcoTextIndex(dest->indexes + i); - } - - - strcpy(data->srcFile, rcoH->fName); - // special case for text - source address/length must be updated later - - } else if(data->type != 0) - warning("[entry (0x%x)] Unknown text type (0x%x)!", data->offset, data->type); - break; - - case RCO_TABLE_IMG: - case RCO_TABLE_MODEL: - if(data->type == 1) { - RCOImgModelEntry rie; - uint rimeSize = (rco->ps3 ? sizeof(RCOPS3ImgModelEntry) : sizeof(RCOImgModelEntry)); - uint16 compression; - extraSize = rimeSize; - - // check for those certain entries which aren't compressed and don't have a packed size value - // urgh, this is a bit of an ugly hack - these short entries are something I didn't know about before :/ - - if(rco_fread(rcoH, &rie, sizeof(uint32))) { - compression = rie.compression; - if(rco->eSwap) compression = ENDIAN_SWAP(compression); - compression &= 0xFF; - if(compression == RCO_DATA_COMPRESSION_NONE) { - // assume that this doesn't include the packed length value - // ...but we'll still at least _try_ to see if this isn't a short entry (not guaranteed to succeed, especially for the last image entry) - if(!re.nextEntryOffset || re.nextEntryOffset < sizeof(RCOEntry) + rimeSize) - extraSize -= sizeof(uint32); - } - } else { - error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); - return; - } - - if(rco->ps3) { - if(!rco_fread(rcoH, (uint8*)(&rie) +sizeof(uint32), sizeof(uint32)*3)) { - error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); - return; - } - if(rie.sizeUnpacked != ENDIAN_SWAP_32(1)) - warning("[entry (0x%x)] Unexpected value 0x%x for PS3 image - expected 0x1.", data->offset, ENDIAN_SWAP(rie.sizeUnpacked)); - rie.sizeUnpacked = rie.sizePacked; - if(compression) { - rco_fread(rcoH, &rie.sizeUnpacked, sizeof(uint32)); - } - } - else if(!rco_fread(rcoH, (uint8*)(&rie) +sizeof(uint32), extraSize - sizeof(uint32))) { - error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); - return; - } - - /* - // dirty hack for reading PS3 stuff - if(rco->ps3 && extraSize == rimeSize) extraSize -= sizeof(uint32); - - if(!rco_fread(rcoH, (uint8*)(&rie) +sizeof(uint32), extraSize - sizeof(uint32))) { - error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); - return; - } - if(rco->ps3 && rie.sizeUnpacked != ENDIAN_SWAP_32(1)) - warning("[entry (0x%x)] Unexpected value 0x%x for PS3 image - expected 0x1.", data->offset, ENDIAN_SWAP(rie.sizeUnpacked)); - if(rco->ps3 && compression && extraSize + sizeof(uint32) == rimeSize) { - extraSize += sizeof(uint32); - rco_fread(rcoH, &rie.sizeUnpacked, sizeof(uint32)); - // TODO: (fix this?) ignore the weird unknown entry for now - } - */ - if(rco->eSwap) es_rcoImgModelEntry(&rie); - - if(rimeSize != extraSize) { - // no packed size value - rie.sizeUnpacked = rie.sizePacked; - } - strcpy(data->srcFile, rcoH->fName); - data->srcAddr = rie.offset; - data->srcLen = rie.sizePacked; - data->srcLenUnpacked = rie.sizeUnpacked; - data->srcCompression = rie.compression & 0xFF; - - data->extraLen = sizeof(rRCOImgModelEntry); - data->extra = malloc(data->extraLen); - ((rRCOImgModelEntry*)data->extra)->format = rie.format; - ((rRCOImgModelEntry*)data->extra)->compression = rie.compression & 0xFF; - ((rRCOImgModelEntry*)data->extra)->unkCompr = rie.compression >> 8; - } else if(data->type != 0) - warning("[entry (0x%x)] Unknown image/model type (0x%x)!", data->offset, data->type); - break; - - case RCO_TABLE_SOUND: - if(data->type == 1) { - RCOSoundEntry rse; - extraSize = sizeof(RCOSoundEntry); - if(!rco_fread(rcoH, &rse, extraSize)) { - error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); \ - return; - } - if(rco->eSwap) es_rcoSoundEntry(&rse); - strcpy(data->srcFile, rcoH->fName); - data->srcAddr = rse.offset; - data->srcLenUnpacked = data->srcLen = rse.sizeTotal; - data->srcCompression = RCO_DATA_COMPRESSION_NONE; - - //data->extraSize = sizeof(rRCOSoundEntry); - data->extra = malloc(sizeof(rRCOSoundEntry)); - ((rRCOSoundEntry*)data->extra)->format = rse.format; - ((rRCOSoundEntry*)data->extra)->channels = rse.channels; - // alloc & read channels (max = 65535, so don't bother checking) - uint readAmt = rse.channels *2*sizeof(uint32); - extraSize = sizeof(RCOSoundEntry) + readAmt; - ((rRCOSoundEntry*)data->extra)->channelData = (uint32*)malloc(readAmt); - if(!rco_fread(rcoH, ((rRCOSoundEntry*)data->extra)->channelData, readAmt)) { - error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); - return; - } - uint i; - if(rco->eSwap) { - uint32* cd = ((rRCOSoundEntry*)data->extra)->channelData; - for(i=0; i<(uint)rse.channels*2; i++) - cd[i] = ENDIAN_SWAP(cd[i]); - } - - // make offsets relative - for(i=0; iextra)->channelData[i*2+1] -= rse.offset; - - if(rse.channels < 2) { - // check if we need to skip some stuff - // AMENDMENT: actually, it seems that there _must_ be two channels defined (no clear indication of size otherwise) - for(i=rse.channels; i<2; i++) { - struct {uint32 size; uint32 offset;} p; - // not sure if there's padding, so just read things one by one - rco_fread(rcoH, &(p.size), sizeof(uint32)); - rco_fread(rcoH, &(p.offset), sizeof(uint32)); - if(p.size || p.offset != RCO_NULL_PTR) - warning("[entry (0x%x)] Unexpected values found in sound entry data where null channels were expected.", data->offset); - - extraSize += sizeof(uint32)*2; - } - } - } else if(data->type != 0) - warning("[entry (0x%x)] Unknown sound type (0x%x)!", data->offset, data->type); - break; - - case RCO_TABLE_FONT: - if(data->type == 1) { - RCOFontEntry rfe; - if(!rco_fread(rcoH, &rfe, sizeof(rfe))) { - error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); - return; - } - if(rco->eSwap) es_rcoFontEntry(&rfe); - extraSize = sizeof(rfe); - - data->extraLen = sizeof(rRCOFontEntry); - data->extra = malloc(data->extraLen); - rRCOFontEntry* drfe = (rRCOFontEntry*)(data->extra); - drfe->format = rfe.format; - drfe->compression = rfe.compression; - drfe->unknown = rfe.unknown; - drfe->unknown2 = rfe.unknown2; - - } else if(data->type != 0) - warning("[entry (0x%x)] Unknown Font type (0x%x)!", data->offset, data->type); - - break; - - case RCO_TABLE_OBJ: - if(data->type > 0) { - uint entrySize = 0; - // determine size of entry from object type - if(data->type <= (int)RCO_OBJ_EXTRA_LEN_NUM && RCO_OBJ_EXTRA_LEN[data->type] != -1) { - entrySize = RCO_OBJ_EXTRA_LEN[data->type]*4; - } - else - warning("[entry (0x%x)] Unknown object type (0x%x)!", data->offset, data->type); - - if(re.entrySize) { - if(entrySize) { - if(entrySize != re.entrySize - sizeof(re)) - warning("[entry (0x%x)] Entry length does not match standard length for this type of object!", data->offset); - } - entrySize = re.entrySize - sizeof(re); - } else { - // TODO: check pointer table for entrySize - } - - if(entrySize) { - READ_ENTRY_MALLOC_AND_READ(entrySize); - if(rco->eSwap) es_extraObjAnim(TRUE, data->type, data->extra, rco->ps3); - } else { - error("[entry (0x%x)] Unable to determine object entry length!", data->offset); - return; - } - } - data->extraLen = extraSize; - break; - - - case RCO_TABLE_ANIM: - if(data->type > 1) { - uint entrySize = 0; - // determine size of entry from anim type - if(data->type <= RCO_ANIM_EXTRA_LEN_NUM && RCO_ANIM_EXTRA_LEN[data->type] != -1) - entrySize = RCO_ANIM_EXTRA_LEN[data->type]*4; - else - warning("[entry (0x%x)] Unknown animation type (0x%x)!", data->offset, data->type); - - if(re.nextEntryOffset) { - if(entrySize) { - if(entrySize != re.nextEntryOffset - sizeof(re)) - warning("[entry (0x%x)] Entry length (%d) does not match standard length (%d) for this type of animation action!", data->offset, re.nextEntryOffset-sizeof(re), entrySize); - } - entrySize = re.nextEntryOffset - sizeof(re); - } else { - // TODO: check pointer table for entrySize - } - - if(entrySize) { - READ_ENTRY_MALLOC_AND_READ(entrySize); - if(rco->eSwap) es_extraObjAnim(FALSE, data->type, data->extra, rco->ps3); - } else { - error("[entry (0x%x)] Unable to determine anim entry length!", data->offset); - return; - } - } - data->extraLen = extraSize; - break; - - default: - warning("[entry (0x%x)] Unknown entry type (0x%x)", data->offset, data->id); - } - - } - - // process subentries - if(readSubEntries && data->numSubentries) { - uint i; - - if(data->numSubentries > MAX_SUBENTRIES) { - warning("[entry (0x%x)] Number of subentries (%d) exceeds safety limit of 65536!", data->offset, data->numSubentries); - data->numSubentries = MAX_SUBENTRIES; - } - - uint curFPos = data->offset + sizeof(re) + extraSize; - rRCOEntry* rcoNode = (rRCOEntry*)malloc(sizeof(rRCOEntry)); - data->firstChild = rcoNode; - for(i=0; inumSubentries; i++) { - read_entry(rcoH, rco, rcoNode, readSubEntries); - rcoNode->parent = data; - if(i+1numSubentries) { - rcoNode->next = (rRCOEntry*)malloc(sizeof(rRCOEntry)); - rcoNode->next->prev = rcoNode; - rcoNode = rcoNode->next; - } else // is last node - data->lastChild = rcoNode; - } - extraSize += rcoread_ftell(rcoH) - curFPos; - } - - // check if lengths match - note, last entry in list won't have a next entry offset so will cause a misalignment for the parent - if(re.nextEntryOffset && re.nextEntryOffset != sizeof(re)+extraSize) { - warning("[entry (0x%x)] Entry boundaries not aligned (going forward %d bytes)", data->offset, re.nextEntryOffset - (sizeof(re)+extraSize)); - rcoread_fseek(rcoH, data->offset + re.nextEntryOffset); - - // our ugly hack to get around borked update_plugin.rco when decompressed with Resurssiklunssi - // the issue is that the image isn't compressed by default, so uses a short image entry (ie, doesn't include decompressed size), however Resurssiklunssi will compress it but won't add in this decompressed size since there is no room for it - // rcomage expects a decompressed size so will assume it exists (no way to tell if it's not there) but this will cause an alignment error for the parent. The only way to detect this is, thus, trap this alignment error and check the child entry - // still, we are left without a decompressed size, thus we have to figure out something for this - - // we'll do this by seeing if the misalignment is -4 and the previous entry is a compressed image/model; if there are child entries, we'll check this instead of the previous entry - // obviously this isn't guaranteed to work in 100% of cases, but will get around the update_plugin.rco bug - if(re.nextEntryOffset == sizeof(re)+extraSize -4) { - if(data->lastChild) { - if(data->lastChild->srcFile[0] && data->lastChild->srcCompression && data->lastChild->type == 1 && (data->lastChild->id == RCO_TABLE_IMG || data->lastChild->id == RCO_TABLE_MODEL)) { - // looks like we need a fix - data->lastChild->srcLenUnpacked = 0xFFFFFFFF; - } - } - - } else { - // TODO: figure out something for looking at previous entry - } - } -} - - -// linear/recursive search - not the fastest, but eh, performance isn't that much of an issue -rRCOEntry* find_entry_from_offset(rRCOEntry* parent, uint32 offset) { - if(parent->offset == offset) return parent; - - rRCOEntry* rcoNode; - for(rcoNode = parent->firstChild; rcoNode; rcoNode = rcoNode->next) { - rRCOEntry* f = find_entry_from_offset(rcoNode, offset); - if(f) return f; - } - - return NULL; // not found -} - - -void fix_refs(rRCOFile* rco, rRCOEntry* entry, const int* lenArray, const uint lenNum, Bool isObj) { - uint i, i2; - - // only fix refs if type is known, and not the main object(0x800)/anim(0x900) table - if(entry->type != 0 && entry->type <= (int)lenNum && lenArray[entry->type] != -1) { - - uint destSize = lenArray[entry->type] *sizeof(uint32); - if(isObj) { - for(i=0, i2=0; i<(uint)RCO_OBJ_EXTRA_LEN[entry->type]; i++, i2++) - if(RCO_OBJ_IS_REF(entry->type, i2)) { - destSize -= sizeof(uint32)*2; // size of ref source - destSize += sizeof(rRCORef); - i++; - } - } else { - /* if(RCO_ANIM_EXTRA_REFS[entry->type]) { - destSize -= sizeof(uint32)*2; // size of ref source - destSize += sizeof(rRCORef); - } */ - for(i=0, i2=0; i<(uint)RCO_ANIM_EXTRA_LEN[entry->type]; i++, i2++) - if(RCO_ANIM_IS_REF(entry->type, i2)) { - destSize -= sizeof(uint32)*2; // size of ref source - destSize += sizeof(rRCORef); - i++; - } - } - - - void* dest = malloc(destSize); - uint8* destPtr = (uint8*)dest; - - for(i=0, i2=0; i<(uint)lenArray[entry->type]; i++, i2++) { - Bool cond; - if(isObj) - cond = (RCO_OBJ_IS_REF(entry->type, i2)); - else // anim - cond = (RCO_ANIM_IS_REF(entry->type, i2)); - //cond = (RCO_ANIM_EXTRA_REFS[entry->type] && i == 0); - if(cond) { - RCOReference* ref = (RCOReference*)((uint8*)entry->extra + i*4); - rRCORef* newRef = (rRCORef*)destPtr; - - newRef->type = ref->type; - newRef->ptr = NULL; - newRef->rawPtr = ref->ptr; - // check for types - switch(ref->type) { - case RCO_REF_EVENT: - if(ref->ptr < rco->eventsLen) - newRef->ptr = rco->events + ref->ptr; - else - warning("[entry (0x%x)] Invalid event pointer.", entry->offset); - break; - case RCO_REF_TEXT: - // just rely on the raw pointer - // TODO: because of the above, we should probably check that all the labels for all text data are the same (though this app will work fine without it, except for XML writing/reading) - break; - case RCO_REF_IMG: case RCO_REF_MODEL: case RCO_REF_FONT: case RCO_REF_OBJ2: case RCO_REF_ANIM: case RCO_REF_OBJ: - // TODO: consider only searching specific sections instead of the entire table - newRef->ptr = find_entry_from_offset(&(rco->tblMain), ref->ptr); - if(!newRef->ptr) - warning("[entry (0x%x)] Unable to find referenced entry from supplied pointer.", entry->offset); - break; - case RCO_REF_NONE: - if(ref->ptr != RCO_NULL_PTR) - warning("[entry (0x%x)] Unexpected pointer value for null pointer (expected 0xFFFFFFFF but got 0x%x).", entry->offset, ref->ptr); - break; - default: - warning("[entry (0x%x)] Unknown reference type 0x%x.", entry->offset, ref->type); - } - i++; - destPtr += sizeof(rRCORef); - } else { - *(uint32*)destPtr = *(uint32*)((uint8*)entry->extra + i*4); - destPtr += sizeof(uint32); - } - } - - free(entry->extra); - entry->extra = dest; - } - - // recurse down to other objects - rRCOEntry* rcoNode; - for(rcoNode = entry->firstChild; rcoNode; rcoNode = rcoNode->next) - fix_refs(rco, rcoNode, lenArray, lenNum, isObj); -} - -Bool check_file_region(uint fSize, uint offset, uint size) { - if(offset+size > fSize) return FALSE; - // TODO: add declared section to list - - return TRUE; -} - -// wrapper file functions, but can read from memory -uint rco_fread(rRCOFile_readhelper* rcoH, void* buf, uint len) { - if(rcoH->tablesSize) { - if(rcoH->memPos + len > rcoH->tablesSize) - len = rcoH->tablesSize - rcoH->memPos; - memcpy(buf, ((uint8*)rcoH->tables) + rcoH->memPos, len); - - rcoH->memPos += len; - return len; - } else { - // regular file read - return fileread(rcoH->fp, buf, len); - } -} -uint rcoread_ftell(rRCOFile_readhelper* rcoH) { - if(rcoH->tablesSize) { - return rcoH->memOffset + rcoH->memPos; - } else { - return ftell(rcoH->fp); - } -} -int rcoread_fseek(rRCOFile_readhelper* rcoH, uint pos) { - if(rcoH->tablesSize) { - if(pos < rcoH->memOffset || pos - rcoH->memOffset > rcoH->tablesSize) - return -1; - rcoH->memPos = pos - rcoH->memOffset; - return 0; - } else { - return fseek(rcoH->fp, pos, SEEK_SET); - } -} - + +#include +#include +#include +#include "rcofile.h" +#include "rcomain.h" +#include "configscan.h" + +#define MAX_LABEL_DATA 16777216 // 16MB of data; also used for event/text data +#define MAX_TREE_DATA 33554432 // 32MB of data +#define MAX_SUBENTRIES 65536 // used for max num text indexes too + + + + +// items used only when reading the file +typedef struct { + char* fName; + uint fSize; + uint fSizeExpanded; // file size with decompressed header + + void* tables; // decompressed tables + uint tablesSize; + uint memPos; + uint memOffset; + + FILE* fp; // for uncompressed tables + + uint32* ptrsObj; + uint numObjPtrs; + uint32* ptrsAnim; + uint numAnimPtrs; + + Bool ps3; // TRUE if PS3 RCO + +} rRCOFile_readhelper; + + + +void read_entry(rRCOFile_readhelper* rcoH, rRCOFile* rco, rRCOEntry* data, Bool readSubEntries); +rRCOEntry* find_entry_from_offset(rRCOEntry* parent, uint32 offset); +Bool check_file_region(uint fSize, uint offset, uint size); +uint rco_fread(rRCOFile_readhelper* rcoH, void* buf, uint len); +uint rcoread_ftell(rRCOFile_readhelper* rcoH); +int rcoread_fseek(rRCOFile_readhelper* rcoH, uint pos); + +void fix_refs(rRCOFile* rco, rRCOEntry* entry, const int* lenArray, const uint lenNum, Bool isObj); + + + + +rRCOFile* read_rco(char* fn) { + rRCOFile_readhelper rcoH; + rRCOFile* rco = (rRCOFile*)malloc(sizeof(rRCOFile)); + rcoH.fName = fn; + rcoH.fp = fopen(fn, "rb"); + + if(!rcoH.fp) { + error("Unable to open file %s", fn); + return NULL; + } + + fseek(rcoH.fp, 0, SEEK_END); + rcoH.fSizeExpanded = rcoH.fSize = ftell(rcoH.fp); + rewind(rcoH.fp); + + PRFHeader header; + if(!check_file_region(rcoH.fSize, 0, sizeof(header))) { + error("File too small to be a valid RCO file."); + return NULL; + } + fileread(rcoH.fp, &header, sizeof(header)); + + // check for endian swapped signature + if(header.signature == ENDIAN_SWAP_32(RCO_SIGNATURE)) { + rco->eSwap = TRUE; + es_rcoHeader(&header); + } else + rco->eSwap = FALSE; + + if(header.signature != RCO_SIGNATURE) { + error("[header] Invalid signature - not a valid RCO file."); + return NULL; + } + if(header.null != 0) { + warning("[header] Unexpected value @ 0x8: 0x%d (expected: 0x0).", header.null); + } + + if(header.pUnknown != RCO_NULL_PTR) { + warning("[header] Unknown RCO section @ 0x28 (points to 0x%x)", header.pUnknown); + } + //if(header.pFontTable != RCO_NULL_PTR) { + // warning("[header] Unknown RCO section @ 0x2C (points to 0x%x)", header.pFontTable); + //} + if(header.unknown[0] != 0xFFFFFFFF || header.unknown[1] != 0xFFFFFFFF || header.unknown[2] != 0xFFFFFFFF) { + warning("[header] Unexpected value(s) in range 0x98-0xA4"); + } + + rco->umdFlag = header.compression & 0xF; // actually, I'm usure about this, but meh + rco->headerCompression = header.compression >> 4; + rco->verId = header.version; + + { + info("RCO header info:"); + if(rco->eSwap) { + info(" Endian = big (PS3)"); + } else { + info(" Endian = little (PSP)"); + } + info(" VersionID = 0x%x", rco->verId); + info(" Compression = 0x%x", rco->headerCompression); + info(" UMDFlag = %d", rco->umdFlag); + info(" MainTree Offset = 0x%x", header.pMainTable); + if(header.pVSMXTable != RCO_NULL_PTR) + info(" VSMXTree Offset = 0x%x", header.pVSMXTable); + if(header.pTextTable != RCO_NULL_PTR) + info(" TextTree Offset = 0x%x", header.pTextTable); + if(header.pSoundTable != RCO_NULL_PTR) + info(" SoundTree Offset = 0x%x", header.pSoundTable); + if(header.pModelTable != RCO_NULL_PTR) + info(" ModelTree Offset = 0x%x", header.pModelTable); + if(header.pFontTable != RCO_NULL_PTR) + info(" FontTree Offset = 0x%x", header.pFontTable); + if(header.pImgTable != RCO_NULL_PTR) + info(" ImageTree Offset = 0x%x", header.pImgTable); + if(header.pObjTable != RCO_NULL_PTR) + info(" ObjectTree Offset = 0x%x", header.pObjTable); + if(header.pAnimTable != RCO_NULL_PTR) + info(" AnimTree Offset = 0x%x", header.pAnimTable); + + if(header.lTextData) + info(" TextData Offset = 0x%x [length = 0x%x]", header.pTextData, header.lTextData); + if(header.lLabelData) + info(" NameData Offset = 0x%x [length = 0x%x]", header.pLabelData, header.lLabelData); + if(header.lEventData) + info(" EventData Offset = 0x%x [length = 0x%x]", header.pEventData, header.lEventData); + + if(header.lImgData) + info(" ImageData Offset = 0x%x [length = 0x%x]", header.pImgData, header.lImgData); + if(header.lSoundData) + info(" SoundData Offset = 0x%x [length = 0x%x]", header.pSoundData, header.lSoundData); + if(header.lModelData) + info(" ModelData Offset = 0x%x [length = 0x%x]", header.pModelData, header.lModelData); + } + + // check compression headers + switch(rco->headerCompression) { + case RCO_DATA_COMPRESSION_NONE: + break; + case RCO_DATA_COMPRESSION_ZLIB: + if(header.version < 0x90) + warning("[header] Unexpected version 0x%x for compression type", header.version); + break; + case RCO_DATA_COMPRESSION_RLZ: + if(header.version < 0x95) + warning("[header] Unexpected version 0x%x for compression type", header.version); + break; + + default: + error("[header] Unknown compression type 0x%x - process cannot continue.", rco->headerCompression); + return NULL; + /* warning("[header] Unknown compression type 0x%x - assuming uncompressed data.", rco->headerCompression); + // set it to no compression + rco->headerCompression = RCO_DATA_COMPRESSION_NONE; */ + } + + rco->ps3 = (rco->eSwap); // && header.version >= 0x97 + configLoadObjmap(rco->ps3); + configLoadAnimmap(rco->ps3); + //rcoH.ps3 = (rco->eSwap && header.version >= 0x107); + + // TODO: check file regions for decompression + + rcoH.tablesSize = 0; + rcoH.tables = 0; + rcoH.memPos = 0; + if(rco->headerCompression) { + rcoH.memOffset = ftell(rcoH.fp); // should = 0xA4 + HeaderComprInfo ci; + if(!fileread(rcoH.fp, &ci, sizeof(ci))) { + error("[header] Unable to read in compression info!"); + return NULL; + } + if(rco->eSwap) es_headerComprInfo(&ci); + info(" Header compression: compressed = 0x%x bytes, uncompressed = 0x%x bytes", ci.lenPacked, ci.lenUnpacked); + + rcoH.tablesSize = ci.lenUnpacked; + if(ci.lenUnpacked > MAX_TREE_DATA || ci.lenPacked > MAX_TREE_DATA) { + error("[header] Size of tree data exceeds sane limit. This is probably a bad RCO."); + return NULL; + } + + rcoH.tables = malloc(ci.lenUnpacked); + + switch(rco->headerCompression) { + case RCO_DATA_COMPRESSION_ZLIB: + { + // uses a bit of memory... - could use zlib's inflate routines, but eh, I doubt it really matters that much... + void* readBuf = malloc(ci.lenPacked); + fileread(rcoH.fp, readBuf, ci.lenPacked); + int uRet = zlib_uncompress(rcoH.tables, ci.lenUnpacked, readBuf, ci.lenPacked); + if(uRet != Z_OK && uRet != Z_DATA_ERROR) { + error("[entries] Unable to decompress tree entries!"); + return FALSE; + } else if(uRet == Z_DATA_ERROR) { + warning("Encountered 'data error' when decompressing tree entries."); + } + free(readBuf); + + rcoH.fSizeExpanded += ci.lenUnpacked - ALIGN_TO_4(ci.lenPacked); + } + + break; + + case RCO_DATA_COMPRESSION_RLZ: + error("[header] This RCO uses RLZ compression which currently cannot be decompressed with rcomage. (use Z33's Resurssiklunssi to decompress the RCO)"); + return NULL; + + default: // this won't actually ever be executed due to the new compression checking code above... :/ + error("[header] Unknown compression method specified (0x%x) - can't continue.", rco->headerCompression); + return NULL; + } + + + // decompress text data + if(header.pTextData != RCO_NULL_PTR && header.lTextData) { + fseek(rcoH.fp, header.pTextData, SEEK_SET); // TODO: check offset first! + TextComprInfo tci; + + info("TextData Compression info:"); + + rcoH.tablesSize = ALIGN_TO_4(rcoH.tablesSize); + do { + if(!fileread(rcoH.fp, &tci, sizeof(tci))) { + error("Failed to read in text compression info."); + return NULL; + } + if(rco->eSwap) es_textComprInfo(&tci); + + if(tci.unknown != 0x1) warning("[text-data] Unexpected value (0x%x) @ 0x2 - expected 0x1", tci.unknown); + info(" LangID = 0x%x, compressed = 0x%x bytes, uncompressed = 0x%x bytes", tci.lang, tci.packedLen, tci.unpackedLen); + + if(tci.unpackedLen > MAX_LABEL_DATA || tci.packedLen > MAX_LABEL_DATA) { + error("[text-data] Size of text data exceeds sane limits."); + return NULL; + } + uint oldSize = rcoH.tablesSize; + rcoH.tablesSize += ALIGN_TO_4(tci.unpackedLen); + rcoH.tables = realloc(rcoH.tables, rcoH.tablesSize); + + void* readBuf = malloc(tci.packedLen); + fileread(rcoH.fp, readBuf, tci.packedLen); + int uRet = zlib_uncompress((char*)rcoH.tables + oldSize, tci.unpackedLen, readBuf, tci.packedLen); + if(uRet != Z_OK && uRet != Z_DATA_ERROR) { + error("[text-data] Unable to decompress text data!"); + return FALSE; + } else if(uRet == Z_DATA_ERROR) { + warning("Encountered 'data error' when decompressing text data."); + } + free(readBuf); + + rcoH.fSizeExpanded += ALIGN_TO_4(tci.unpackedLen) - ALIGN_TO_4(tci.packedLen); // TODO: need to check if this is correct + + if(!tci.nextOffset) break; + int seekAmt = tci.nextOffset - sizeof(tci) - tci.packedLen; + if(seekAmt) + fseek(rcoH.fp, seekAmt, SEEK_CUR); + } while(tci.nextOffset); + } + } + + if(!check_file_region(rcoH.fSizeExpanded, header.pLabelData, header.lLabelData)) { + error("[header] Invalid label pointer/length specified."); + return NULL; + } + if(header.lLabelData > MAX_LABEL_DATA) { + header.lLabelData = MAX_LABEL_DATA; + warning("[labels] Total data length exceeds safety limit of 16MB - data has been truncated!"); + } + rco->labelsLen = header.lLabelData; + if(header.lLabelData) { + rco->labels = (char*)malloc(header.lLabelData); + rcoread_fseek(&rcoH, header.pLabelData); + rco_fread(&rcoH, rco->labels, header.lLabelData); + } + + if(!check_file_region(rcoH.fSizeExpanded, header.pEventData, header.lEventData)) { + error("[header] Invalid event pointer/length specified."); + return NULL; + } + if(header.lEventData > MAX_LABEL_DATA) { + warning("[events] Total data length (%d) exceeds safety limit of 16MB - data has been truncated!", header.lEventData); + header.lEventData = MAX_LABEL_DATA; + } + rco->eventsLen = header.lEventData; + if(header.lEventData) { + rco->events = (char*)malloc(header.lEventData); + rcoread_fseek(&rcoH, header.pEventData); + rco_fread(&rcoH, rco->events, header.lEventData); + } + + + // read pointer tables + // TODO: think about what to do with these + { + //rcoH.ptrsText = rcoH.ptrsImg = rcoH.ptrsModel = rcoH.ptrsSound = 0; + rcoH.ptrsObj = rcoH.ptrsAnim = 0; + + // macro to help save us some code + #define READ_RCO_READ_PTR_SEGMENT(hp, hl, dp, dl, s) \ + dp = 0; \ + dl = 0; \ + if(hp != RCO_NULL_PTR) { \ + if(!check_file_region(rcoH.fSizeExpanded, hp, hl)) { \ + error("[header] Invalid %s pointer/length specified.", s); \ + return NULL; \ + } \ + if((hl) > MAX_LABEL_DATA) { \ + warning("[%s] Total data length (%d) exceeds safety limit of 16MB - data has been truncated!", s, hl); \ + hl = MAX_LABEL_DATA; \ + } \ + dl = (hl) / sizeof(uint32); \ + hl = (dl) * sizeof(uint32); \ + if(dl) { \ + dp = (uint32*)malloc(hl); \ + rcoread_fseek(&rcoH, hp); \ + rco_fread(&rcoH, dp, hl); \ + } \ + } + /* + READ_RCO_READ_PTR_SEGMENT(header.pTextPtrs, header.lTextPtrs, rcoH.ptrsText, rcoH.numTextPtrs, "text"); + READ_RCO_READ_PTR_SEGMENT(header.pImgPtrs, header.lImgPtrs, rcoH.ptrsImg, rcoH.numImgPtrs, "image"); + READ_RCO_READ_PTR_SEGMENT(header.pModelPtrs, header.lModelPtrs, rcoH.ptrsModel, rcoH.numModelPtrs, "model"); + READ_RCO_READ_PTR_SEGMENT(header.pSoundPtrs, header.lSoundPtrs, rcoH.ptrsSound, rcoH.numSoundPtrs, "sound"); + */ + READ_RCO_READ_PTR_SEGMENT(header.pObjPtrs, header.lObjPtrs, rcoH.ptrsObj, rcoH.numObjPtrs, "object"); + READ_RCO_READ_PTR_SEGMENT(header.pAnimPtrs, header.lAnimPtrs, rcoH.ptrsAnim, rcoH.numAnimPtrs, "anim"); + + // TODO: possibly sort these for the following section (not that necessary - only useful if we find an unknown object/anim type) + // TODO: endian swap these entries if we intend to read them... + } + + + // read main/vsmx/text/sound/model/img/obj/anim tables + rcoread_fseek(&rcoH, header.pMainTable); + read_entry(&rcoH, rco, &(rco->tblMain), TRUE); + + // TODO: check for unreferenced labels / events? + + // assign shortcut tables + { + rco->tblVSMX = rco->tblText = rco->tblImage = rco->tblSound = rco->tblModel = rco->tblFont = rco->tblObj = rco->tblAnim = 0; + rRCOEntry* rcoNode; + for(rcoNode = rco->tblMain.firstChild; rcoNode; rcoNode = rcoNode->next) { + switch(rcoNode->id) { + #define READ_RCO_ASN_SHORTCUT_TBL(hp, sc, s) \ + if(hp == RCO_NULL_PTR) warning("[header] %c%s main tree not referenced but found in entries.", toupper(s[0]), s+1); \ + if(sc) { warning("[header] Multiple %s main trees found!", s); } \ + else { sc = rcoNode; } \ + break; + + case RCO_TABLE_VSMX: + READ_RCO_ASN_SHORTCUT_TBL(header.pVSMXTable, rco->tblVSMX, "VSMX"); + break; + case RCO_TABLE_TEXT: + READ_RCO_ASN_SHORTCUT_TBL(header.pTextTable, rco->tblText, "text"); + break; + case RCO_TABLE_IMG: + READ_RCO_ASN_SHORTCUT_TBL(header.pImgTable, rco->tblImage, "image"); + break; + case RCO_TABLE_SOUND: + READ_RCO_ASN_SHORTCUT_TBL(header.pSoundTable, rco->tblSound, "sound"); + break; + case RCO_TABLE_MODEL: + READ_RCO_ASN_SHORTCUT_TBL(header.pModelTable, rco->tblModel, "model"); + break; + case RCO_TABLE_FONT: + READ_RCO_ASN_SHORTCUT_TBL(header.pFontTable, rco->tblFont, "font"); + break; + case RCO_TABLE_OBJ: + READ_RCO_ASN_SHORTCUT_TBL(header.pObjTable, rco->tblObj, "object"); + break; + case RCO_TABLE_ANIM: + READ_RCO_ASN_SHORTCUT_TBL(header.pAnimTable, rco->tblAnim, "animation"); + break; + } + } + + if(header.pVSMXTable != RCO_NULL_PTR && !rco->tblVSMX) warning("[header] VSMX tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); + if(header.pTextTable != RCO_NULL_PTR && !rco->tblText) warning("[header] Text tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); + if(header.pImgTable != RCO_NULL_PTR && !rco->tblImage) warning("[header] Image tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); + if(header.pSoundTable != RCO_NULL_PTR && !rco->tblSound) warning("[header] Sound tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); + if(header.pModelTable != RCO_NULL_PTR && !rco->tblModel) warning("[header] Model tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); + if(header.pFontTable != RCO_NULL_PTR && !rco->tblFont) warning("[header] Font tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); + if(header.pObjTable != RCO_NULL_PTR && !rco->tblObj) warning("[header] Object tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); + if(header.pAnimTable != RCO_NULL_PTR && !rco->tblAnim) warning("[header] Animation tree referenced in header, but couldn't be found when parsing entries! Section has been discarded."); + } + + // add offset/len/compression info for text entries + if(rco->tblText && header.lTextData) { + // TODO: assign things for compressed text data + rRCOEntry* rcoNode = rco->tblText->firstChild; + if(rco->headerCompression != RCO_DATA_COMPRESSION_NONE) { + // if header is compressed, we've got to reparse the RCO file to get the positions :/ + FILE* fp2 = fopen(fn, "rb"); + HeaderComprInfo hci; + TextComprInfo tci; + + fseek(fp2, sizeof(header), SEEK_SET); + fileread(fp2, &hci, sizeof(hci)); + if(rco->eSwap) es_headerComprInfo(&hci); + fseek(fp2, ALIGN_TO_4(hci.lenPacked), SEEK_CUR); + + + // before parsing the file, we'll be on the safe side and blank out all text entries' src + for(; rcoNode; rcoNode = rcoNode->next) { + rcoNode->srcAddr = rcoNode->srcLenUnpacked = rcoNode->srcLen = 0; + rcoNode->srcCompression = RCO_DATA_COMPRESSION_NONE; + } + + + tci.nextOffset = 1; // dummy + while(tci.nextOffset) { + fileread(fp2, &tci, sizeof(tci)); + if(rco->eSwap) es_textComprInfo(&tci); + // search for correct entry to assign to + for(rcoNode = rco->tblText->firstChild; rcoNode; rcoNode = rcoNode->next) { + if(((rRCOTextEntry*)rcoNode->extra)->lang == tci.lang) { + rcoNode->srcAddr = ftell(fp2); + rcoNode->srcLenUnpacked = tci.unpackedLen; + rcoNode->srcLen = tci.packedLen; + rcoNode->srcCompression = rco->headerCompression; + break; + } + } + + if(!tci.nextOffset) break; + fseek(fp2, tci.nextOffset - sizeof(tci), SEEK_CUR); + } + + fclose(fp2); + } else { + for(; rcoNode; rcoNode = rcoNode->next) { + rcoNode->srcAddr = header.pTextData; + rcoNode->srcLenUnpacked = rcoNode->srcLen = header.lTextData; + rcoNode->srcCompression = RCO_DATA_COMPRESSION_NONE; + } + } + } + + // fix offsets for img/model/sound srcAddr + // TODO: really should put in the right values when traversing the tree instead of a post-fix + { + rRCOEntry* rcoNode; + if(rco->tblImage) { + for(rcoNode = rco->tblImage->firstChild; rcoNode; rcoNode = rcoNode->next) + rcoNode->srcAddr += header.pImgData; + } + if(rco->tblModel) { + for(rcoNode = rco->tblModel->firstChild; rcoNode; rcoNode = rcoNode->next) + rcoNode->srcAddr += header.pModelData; + } + if(rco->tblSound) { + for(rcoNode = rco->tblSound->firstChild; rcoNode; rcoNode = rcoNode->next) + rcoNode->srcAddr += header.pSoundData; + } + } + + // TODO: check for dupe labels + + // fix object/anim references + if(rco->tblObj) + fix_refs(rco, rco->tblObj, RCO_OBJ_EXTRA_LEN, RCO_OBJ_EXTRA_LEN_NUM, TRUE); + if(rco->tblAnim) + fix_refs(rco, rco->tblAnim, RCO_ANIM_EXTRA_LEN, RCO_ANIM_EXTRA_LEN_NUM, FALSE); + + + rco_fix_decomp_sizes(rco, &rco->tblMain); + + // check resources + { + // TODO: should use rcoH.fSizeExpanded here otherwise we stuff up the region tracking code + if(rco->tblText && rco->tblText->numSubentries) { + if(!check_file_region(rcoH.fSize, header.pTextData, header.lTextData)) + warning("[header] Text resource pointer/length is invalid."); + } + if(rco->tblImage && rco->tblImage->numSubentries) { + if(!check_file_region(rcoH.fSize, header.pImgData, header.lImgData)) + warning("[header] Image resource pointer/length is invalid."); + } + if(rco->tblSound && rco->tblSound->numSubentries) { + if(!check_file_region(rcoH.fSize, header.pSoundData, header.lSoundData)) + warning("[header] Sound resource pointer/length is invalid."); + } + if(rco->tblModel && rco->tblModel->numSubentries) { + if(!check_file_region(rcoH.fSize, header.pModelData, header.lModelData)) + warning("[header] Model resource pointer/length is invalid."); + } + + /* + rco->pDataText = header.pTextData; + rco->lDataText = header.lTextData; + if(!rco->lDataText) rco->pDataText = 0; + rco->pDataImg = header.pImgData; + rco->lDataImg = header.lImgData; + if(!rco->lDataImg) rco->pDataImg = 0; + rco->pDataSound = header.pSoundData; + rco->lDataSound = header.lSoundData; + if(!rco->lDataSound) rco->pDataSound = 0; + rco->pDataModel = header.pModelData; + rco->lDataModel = header.lModelData; + if(!rco->lDataModel) rco->pDataModel = 0; + + rco->attachSource = (char*)malloc(strlen(fn) +1); + strcpy(rco->attachSource, fn); + */ + } + + fclose(rcoH.fp); + if(rcoH.tables) free(rcoH.tables); + + /* if(rcoH.ptrsText) free(rcoH.ptrsText); + if(rcoH.ptrsImg) free(rcoH.ptrsImg); + if(rcoH.ptrsModel) free(rcoH.ptrsModel); + if(rcoH.ptrsSound) free(rcoH.ptrsSound); */ + if(rcoH.ptrsObj) free(rcoH.ptrsObj); + if(rcoH.ptrsAnim) free(rcoH.ptrsAnim); + + return rco; +} + +void read_entry(rRCOFile_readhelper* rcoH, rRCOFile* rco, rRCOEntry* data, Bool readSubEntries) { + + data->offset = rcoread_ftell(rcoH); + if(!check_file_region(rcoH->fSizeExpanded, data->offset, sizeof(RCOEntry))) { + error("[entry] Unable to read entry - file exhausted."); + return; + } + + RCOEntry re; + rco_fread(rcoH, &re, sizeof(re)); + if(rco->eSwap) es_rcoEntry(&re); + + data->type = re.typeId & 0xFF; + data->id = (re.typeId & 0xFF00) >> 8; + + if(re.blank != 0) { + warning("[entry (0x%x)] Unexpected entry value @ 0x2", data->offset); + } + data->labelOffset = RCO_NULL_PTR; + if(re.labelOffset != RCO_NULL_PTR) { + if(re.labelOffset >= rco->labelsLen) { + warning("[entry (0x%x)] Invalid label offset supplied - label removed", data->offset); + } else { + // WARNING! this assignment is unchecked (ie possible to cause this to point to anything) + data->labelOffset = re.labelOffset; + } + } + if(re.eHeadSize && re.eHeadSize != sizeof(re)) { + warning("[entry (0x%x)] Non-standard entry header size found!", data->offset); + } + + data->numSubentries = re.numSubentries; + data->rco = rco; + data->parent = data->firstChild = data->lastChild = data->prev = data->next = NULL; + if(re.blanks[0] != 0 || re.blanks[1] != 0) { + warning("[entry (0x%x)] Unexpected entry value in range 0x20-0x28", data->offset); + } + + + data->srcFile[0] = '\0'; + + data->extra = 0; + data->srcBuffer = NULL; + //data->extraSize = 0; + uint extraSize = 0; + { + // cbf checking for file exhaustion here - just let read calls fail + // a shortcut to help us + #define READ_ENTRY_MALLOC_AND_READ(s) { \ + extraSize = s; \ + data->extra = malloc(s); \ + if(!rco_fread(rcoH, data->extra, s)) { \ + error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); \ + return; \ + } \ + } + switch(data->id) { + case RCO_TABLE_MAIN: + if(data->type != 1) + warning("[entry (0x%x)] Unexpected 'main' type (0x%x)!", data->offset, data->type); + break; // nothing needs to be done + + case RCO_TABLE_VSMX: + if(data->type == 1) { + RCOVSMXEntry rve; + if(!rco_fread(rcoH, &rve, sizeof(rve))) { + error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); + return; + } + if(rco->eSwap) es_rcoVsmxEntry(&rve); + data->srcAddr = rcoread_ftell(rcoH) + rve.offset; + data->srcLen = data->srcLenUnpacked = rve.length; + strcpy(data->srcFile, rcoH->fName); + // need to consider possibility that VSMX table is compressed inside compressed RCO header + data->srcCompression = (rcoH->tables ? RCO_DATA_COMPRESSION_RCO : RCO_DATA_COMPRESSION_NONE); + + + extraSize = sizeof(RCOVSMXEntry) + rve.length; + // 4 byte alignment + extraSize = ALIGN_TO_4(extraSize); + rcoread_fseek(rcoH, rcoread_ftell(rcoH) + extraSize - sizeof(RCOVSMXEntry)); + } else + warning("[entry (0x%x)] Unknown VSMX type (0x%x)!", data->offset, data->type); + + break; + + case RCO_TABLE_TEXT: + if(data->type == 1) { + RCOTextEntry rte; + rRCOTextEntry* dest; + data->extraLen = sizeof(rRCOTextEntry); + data->extra = malloc(data->extraLen); + dest = (rRCOTextEntry*)data->extra; + if(!rco_fread(rcoH, &rte, sizeof(rte))) { + error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); + return; + } + if(rco->eSwap) es_rcoTextEntry(&rte); + + dest->lang = rte.lang; + dest->format = rte.format; + if(rte.format != RCO_TEXT_FMT_UTF8 && rte.format != RCO_TEXT_FMT_UTF16 && rte.format != RCO_TEXT_FMT_UTF32) { + warning("[entry (0x%x)] Unknown format for text entry (0x%x)", data->offset, rte.format); + } + dest->numIndexes = rte.numIndexes; + + + // alloc & read indexes + if(dest->numIndexes > MAX_SUBENTRIES) { + warning("[entry (0x%x)] Number of text indexes (0x%x) exceeds safety limit.", data->offset, ((rRCOTextEntry*)data->extra)->numIndexes); + dest->numIndexes = MAX_SUBENTRIES; + } + uint readAmt = dest->numIndexes *sizeof(RCOTextIndex); + extraSize = sizeof(RCOTextEntry) + readAmt; + dest->indexes = (RCOTextIndex*)malloc(readAmt); + if(!rco_fread(rcoH, dest->indexes, readAmt)) { + error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); + return; + } + if(rco->eSwap) { // endian swap all indexes + uint i; + for(i=0; inumIndexes; i++) + es_rcoTextIndex(dest->indexes + i); + } + + + strcpy(data->srcFile, rcoH->fName); + // special case for text - source address/length must be updated later + + } else if(data->type != 0) + warning("[entry (0x%x)] Unknown text type (0x%x)!", data->offset, data->type); + break; + + case RCO_TABLE_IMG: + case RCO_TABLE_MODEL: + if(data->type == 1) { + RCOImgModelEntry rie; + uint rimeSize = (rco->ps3 ? sizeof(RCOPS3ImgModelEntry) : sizeof(RCOImgModelEntry)); + uint16 compression; + extraSize = rimeSize; + + // check for those certain entries which aren't compressed and don't have a packed size value + // urgh, this is a bit of an ugly hack - these short entries are something I didn't know about before :/ + + if(rco_fread(rcoH, &rie, sizeof(uint32))) { + compression = rie.compression; + if(rco->eSwap) compression = ENDIAN_SWAP(compression); + compression &= 0xFF; + if(compression == RCO_DATA_COMPRESSION_NONE) { + // assume that this doesn't include the packed length value + // ...but we'll still at least _try_ to see if this isn't a short entry (not guaranteed to succeed, especially for the last image entry) + if(!re.nextEntryOffset || re.nextEntryOffset < sizeof(RCOEntry) + rimeSize) + extraSize -= sizeof(uint32); + } + } else { + error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); + return; + } + + if(rco->ps3) { + if(!rco_fread(rcoH, (uint8*)(&rie) +sizeof(uint32), sizeof(uint32)*3)) { + error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); + return; + } + if(rie.sizeUnpacked != ENDIAN_SWAP_32(1)) + warning("[entry (0x%x)] Unexpected value 0x%x for PS3 image - expected 0x1.", data->offset, ENDIAN_SWAP(rie.sizeUnpacked)); + rie.sizeUnpacked = rie.sizePacked; + if(compression) { + rco_fread(rcoH, &rie.sizeUnpacked, sizeof(uint32)); + } + } + else if(!rco_fread(rcoH, (uint8*)(&rie) +sizeof(uint32), extraSize - sizeof(uint32))) { + error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); + return; + } + + /* + // dirty hack for reading PS3 stuff + if(rco->ps3 && extraSize == rimeSize) extraSize -= sizeof(uint32); + + if(!rco_fread(rcoH, (uint8*)(&rie) +sizeof(uint32), extraSize - sizeof(uint32))) { + error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); + return; + } + if(rco->ps3 && rie.sizeUnpacked != ENDIAN_SWAP_32(1)) + warning("[entry (0x%x)] Unexpected value 0x%x for PS3 image - expected 0x1.", data->offset, ENDIAN_SWAP(rie.sizeUnpacked)); + if(rco->ps3 && compression && extraSize + sizeof(uint32) == rimeSize) { + extraSize += sizeof(uint32); + rco_fread(rcoH, &rie.sizeUnpacked, sizeof(uint32)); + // TODO: (fix this?) ignore the weird unknown entry for now + } + */ + if(rco->eSwap) es_rcoImgModelEntry(&rie); + + if(rimeSize != extraSize) { + // no packed size value + rie.sizeUnpacked = rie.sizePacked; + } + strcpy(data->srcFile, rcoH->fName); + data->srcAddr = rie.offset; + data->srcLen = rie.sizePacked; + data->srcLenUnpacked = rie.sizeUnpacked; + data->srcCompression = rie.compression & 0xFF; + + data->extraLen = sizeof(rRCOImgModelEntry); + data->extra = malloc(data->extraLen); + ((rRCOImgModelEntry*)data->extra)->format = rie.format; + ((rRCOImgModelEntry*)data->extra)->compression = rie.compression & 0xFF; + ((rRCOImgModelEntry*)data->extra)->unkCompr = rie.compression >> 8; + } else if(data->type != 0) + warning("[entry (0x%x)] Unknown image/model type (0x%x)!", data->offset, data->type); + break; + + case RCO_TABLE_SOUND: + if(data->type == 1) { + RCOSoundEntry rse; + extraSize = sizeof(RCOSoundEntry); + if(!rco_fread(rcoH, &rse, extraSize)) { + error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); \ + return; + } + if(rco->eSwap) es_rcoSoundEntry(&rse); + strcpy(data->srcFile, rcoH->fName); + data->srcAddr = rse.offset; + data->srcLenUnpacked = data->srcLen = rse.sizeTotal; + data->srcCompression = RCO_DATA_COMPRESSION_NONE; + + //data->extraSize = sizeof(rRCOSoundEntry); + data->extra = malloc(sizeof(rRCOSoundEntry)); + ((rRCOSoundEntry*)data->extra)->format = rse.format; + ((rRCOSoundEntry*)data->extra)->channels = rse.channels; + // alloc & read channels (max = 65535, so don't bother checking) + uint readAmt = rse.channels *2*sizeof(uint32); + extraSize = sizeof(RCOSoundEntry) + readAmt; + ((rRCOSoundEntry*)data->extra)->channelData = (uint32*)malloc(readAmt); + if(!rco_fread(rcoH, ((rRCOSoundEntry*)data->extra)->channelData, readAmt)) { + error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); + return; + } + uint i; + if(rco->eSwap) { + uint32* cd = ((rRCOSoundEntry*)data->extra)->channelData; + for(i=0; i<(uint)rse.channels*2; i++) + cd[i] = ENDIAN_SWAP(cd[i]); + } + + // make offsets relative + for(i=0; iextra)->channelData[i*2+1] -= rse.offset; + + if(rse.channels < 2) { + // check if we need to skip some stuff + // AMENDMENT: actually, it seems that there _must_ be two channels defined (no clear indication of size otherwise) + for(i=rse.channels; i<2; i++) { + struct {uint32 size; uint32 offset;} p; + // not sure if there's padding, so just read things one by one + rco_fread(rcoH, &(p.size), sizeof(uint32)); + rco_fread(rcoH, &(p.offset), sizeof(uint32)); + if(p.size || p.offset != RCO_NULL_PTR) + warning("[entry (0x%x)] Unexpected values found in sound entry data where null channels were expected.", data->offset); + + extraSize += sizeof(uint32)*2; + } + } + } else if(data->type != 0) + warning("[entry (0x%x)] Unknown sound type (0x%x)!", data->offset, data->type); + break; + + case RCO_TABLE_FONT: + if(data->type == 1) { + RCOFontEntry rfe; + if(!rco_fread(rcoH, &rfe, sizeof(rfe))) { + error("[entry (0x%x)] Unable to read entry extra data - file exhausted.", data->offset); + return; + } + if(rco->eSwap) es_rcoFontEntry(&rfe); + extraSize = sizeof(rfe); + + data->extraLen = sizeof(rRCOFontEntry); + data->extra = malloc(data->extraLen); + rRCOFontEntry* drfe = (rRCOFontEntry*)(data->extra); + drfe->format = rfe.format; + drfe->compression = rfe.compression; + drfe->unknown = rfe.unknown; + drfe->unknown2 = rfe.unknown2; + + } else if(data->type != 0) + warning("[entry (0x%x)] Unknown Font type (0x%x)!", data->offset, data->type); + + break; + + case RCO_TABLE_OBJ: + if(data->type > 0) { + uint entrySize = 0; + // determine size of entry from object type + if(data->type <= (int)RCO_OBJ_EXTRA_LEN_NUM && RCO_OBJ_EXTRA_LEN[data->type] != -1) { + entrySize = RCO_OBJ_EXTRA_LEN[data->type]*4; + } + else + warning("[entry (0x%x)] Unknown object type (0x%x)!", data->offset, data->type); + + if(re.entrySize) { + if(entrySize) { + if(entrySize != re.entrySize - sizeof(re)) + warning("[entry (0x%x)] Entry length does not match standard length for this type of object!", data->offset); + } + entrySize = re.entrySize - sizeof(re); + } else { + // TODO: check pointer table for entrySize + } + + if(entrySize) { + READ_ENTRY_MALLOC_AND_READ(entrySize); + if(rco->eSwap) es_extraObjAnim(TRUE, data->type, data->extra, rco->ps3); + } else { + error("[entry (0x%x)] Unable to determine object entry length!", data->offset); + return; + } + } + data->extraLen = extraSize; + break; + + + case RCO_TABLE_ANIM: + if(data->type > 1) { + uint entrySize = 0; + // determine size of entry from anim type + if(data->type <= RCO_ANIM_EXTRA_LEN_NUM && RCO_ANIM_EXTRA_LEN[data->type] != -1) + entrySize = RCO_ANIM_EXTRA_LEN[data->type]*4; + else + warning("[entry (0x%x)] Unknown animation type (0x%x)!", data->offset, data->type); + + if(re.nextEntryOffset) { + if(entrySize) { + if(entrySize != re.nextEntryOffset - sizeof(re)) + warning("[entry (0x%x)] Entry length (%d) does not match standard length (%d) for this type of animation action!", data->offset, re.nextEntryOffset-sizeof(re), entrySize); + } + entrySize = re.nextEntryOffset - sizeof(re); + } else { + // TODO: check pointer table for entrySize + } + + if(entrySize) { + READ_ENTRY_MALLOC_AND_READ(entrySize); + if(rco->eSwap) es_extraObjAnim(FALSE, data->type, data->extra, rco->ps3); + } else { + error("[entry (0x%x)] Unable to determine anim entry length!", data->offset); + return; + } + } + data->extraLen = extraSize; + break; + + default: + warning("[entry (0x%x)] Unknown entry type (0x%x)", data->offset, data->id); + } + + } + + // process subentries + if(readSubEntries && data->numSubentries) { + uint i; + + if(data->numSubentries > MAX_SUBENTRIES) { + warning("[entry (0x%x)] Number of subentries (%d) exceeds safety limit of 65536!", data->offset, data->numSubentries); + data->numSubentries = MAX_SUBENTRIES; + } + + uint curFPos = data->offset + sizeof(re) + extraSize; + rRCOEntry* rcoNode = (rRCOEntry*)malloc(sizeof(rRCOEntry)); + data->firstChild = rcoNode; + for(i=0; inumSubentries; i++) { + read_entry(rcoH, rco, rcoNode, readSubEntries); + rcoNode->parent = data; + if(i+1numSubentries) { + rcoNode->next = (rRCOEntry*)malloc(sizeof(rRCOEntry)); + rcoNode->next->prev = rcoNode; + rcoNode = rcoNode->next; + } else // is last node + data->lastChild = rcoNode; + } + extraSize += rcoread_ftell(rcoH) - curFPos; + } + + // check if lengths match - note, last entry in list won't have a next entry offset so will cause a misalignment for the parent + if(re.nextEntryOffset && re.nextEntryOffset != sizeof(re)+extraSize) { + warning("[entry (0x%x)] Entry boundaries not aligned (going forward %d bytes)", data->offset, re.nextEntryOffset - (sizeof(re)+extraSize)); + rcoread_fseek(rcoH, data->offset + re.nextEntryOffset); + + // our ugly hack to get around borked update_plugin.rco when decompressed with Resurssiklunssi + // the issue is that the image isn't compressed by default, so uses a short image entry (ie, doesn't include decompressed size), however Resurssiklunssi will compress it but won't add in this decompressed size since there is no room for it + // rcomage expects a decompressed size so will assume it exists (no way to tell if it's not there) but this will cause an alignment error for the parent. The only way to detect this is, thus, trap this alignment error and check the child entry + // still, we are left without a decompressed size, thus we have to figure out something for this + + // we'll do this by seeing if the misalignment is -4 and the previous entry is a compressed image/model; if there are child entries, we'll check this instead of the previous entry + // obviously this isn't guaranteed to work in 100% of cases, but will get around the update_plugin.rco bug + if(re.nextEntryOffset == sizeof(re)+extraSize -4) { + if(data->lastChild) { + if(data->lastChild->srcFile[0] && data->lastChild->srcCompression && data->lastChild->type == 1 && (data->lastChild->id == RCO_TABLE_IMG || data->lastChild->id == RCO_TABLE_MODEL)) { + // looks like we need a fix + data->lastChild->srcLenUnpacked = 0xFFFFFFFF; + } + } + + } else { + // TODO: figure out something for looking at previous entry + } + } +} + + +// linear/recursive search - not the fastest, but eh, performance isn't that much of an issue +rRCOEntry* find_entry_from_offset(rRCOEntry* parent, uint32 offset) { + if(parent->offset == offset) return parent; + + rRCOEntry* rcoNode; + for(rcoNode = parent->firstChild; rcoNode; rcoNode = rcoNode->next) { + rRCOEntry* f = find_entry_from_offset(rcoNode, offset); + if(f) return f; + } + + return NULL; // not found +} + + +void fix_refs(rRCOFile* rco, rRCOEntry* entry, const int* lenArray, const uint lenNum, Bool isObj) { + uint i, i2; + + // only fix refs if type is known, and not the main object(0x800)/anim(0x900) table + if(entry->type != 0 && entry->type <= (int)lenNum && lenArray[entry->type] != -1) { + + uint destSize = lenArray[entry->type] *sizeof(uint32); + if(isObj) { + for(i=0, i2=0; i<(uint)RCO_OBJ_EXTRA_LEN[entry->type]; i++, i2++) + if(RCO_OBJ_IS_REF(entry->type, i2)) { + destSize -= sizeof(uint32)*2; // size of ref source + destSize += sizeof(rRCORef); + i++; + } + } else { + /* if(RCO_ANIM_EXTRA_REFS[entry->type]) { + destSize -= sizeof(uint32)*2; // size of ref source + destSize += sizeof(rRCORef); + } */ + for(i=0, i2=0; i<(uint)RCO_ANIM_EXTRA_LEN[entry->type]; i++, i2++) + if(RCO_ANIM_IS_REF(entry->type, i2)) { + destSize -= sizeof(uint32)*2; // size of ref source + destSize += sizeof(rRCORef); + i++; + } + } + + + void* dest = malloc(destSize); + uint8* destPtr = (uint8*)dest; + + for(i=0, i2=0; i<(uint)lenArray[entry->type]; i++, i2++) { + Bool cond; + if(isObj) + cond = (RCO_OBJ_IS_REF(entry->type, i2)); + else // anim + cond = (RCO_ANIM_IS_REF(entry->type, i2)); + //cond = (RCO_ANIM_EXTRA_REFS[entry->type] && i == 0); + if(cond) { + RCOReference* ref = (RCOReference*)((uint8*)entry->extra + i*4); + rRCORef* newRef = (rRCORef*)destPtr; + + newRef->type = ref->type; + newRef->ptr = NULL; + newRef->rawPtr = ref->ptr; + // check for types + switch(ref->type) { + case RCO_REF_EVENT: + if(ref->ptr < rco->eventsLen) + newRef->ptr = rco->events + ref->ptr; + else + warning("[entry (0x%x)] Invalid event pointer.", entry->offset); + break; + case RCO_REF_TEXT: + // just rely on the raw pointer + // TODO: because of the above, we should probably check that all the labels for all text data are the same (though this app will work fine without it, except for XML writing/reading) + break; + case RCO_REF_IMG: case RCO_REF_MODEL: case RCO_REF_FONT: case RCO_REF_OBJ2: case RCO_REF_ANIM: case RCO_REF_OBJ: + // TODO: consider only searching specific sections instead of the entire table + newRef->ptr = find_entry_from_offset(&(rco->tblMain), ref->ptr); + if(!newRef->ptr) + warning("[entry (0x%x)] Unable to find referenced entry from supplied pointer.", entry->offset); + break; + case RCO_REF_NONE: + if(ref->ptr != RCO_NULL_PTR) + warning("[entry (0x%x)] Unexpected pointer value for null pointer (expected 0xFFFFFFFF but got 0x%x).", entry->offset, ref->ptr); + break; + default: + warning("[entry (0x%x)] Unknown reference type 0x%x.", entry->offset, ref->type); + } + i++; + destPtr += sizeof(rRCORef); + } else { + *(uint32*)destPtr = *(uint32*)((uint8*)entry->extra + i*4); + destPtr += sizeof(uint32); + } + } + + free(entry->extra); + entry->extra = dest; + } + + // recurse down to other objects + rRCOEntry* rcoNode; + for(rcoNode = entry->firstChild; rcoNode; rcoNode = rcoNode->next) + fix_refs(rco, rcoNode, lenArray, lenNum, isObj); +} + +Bool check_file_region(uint fSize, uint offset, uint size) { + if(offset+size > fSize) return FALSE; + // TODO: add declared section to list + + return TRUE; +} + +// wrapper file functions, but can read from memory +uint rco_fread(rRCOFile_readhelper* rcoH, void* buf, uint len) { + if(rcoH->tablesSize) { + if(rcoH->memPos + len > rcoH->tablesSize) + len = rcoH->tablesSize - rcoH->memPos; + memcpy(buf, ((uint8*)rcoH->tables) + rcoH->memPos, len); + + rcoH->memPos += len; + return len; + } else { + // regular file read + return fileread(rcoH->fp, buf, len); + } +} +uint rcoread_ftell(rRCOFile_readhelper* rcoH) { + if(rcoH->tablesSize) { + return rcoH->memOffset + rcoH->memPos; + } else { + return ftell(rcoH->fp); + } +} +int rcoread_fseek(rRCOFile_readhelper* rcoH, uint pos) { + if(rcoH->tablesSize) { + if(pos < rcoH->memOffset || pos - rcoH->memOffset > rcoH->tablesSize) + return -1; + rcoH->memPos = pos - rcoH->memOffset; + return 0; + } else { + return fseek(rcoH->fp, pos, SEEK_SET); + } +} + diff --git a/rcowriter.c b/rcowriter.c index 023f1ba..a8a416b 100644 --- a/rcowriter.c +++ b/rcowriter.c @@ -1,1049 +1,1049 @@ -// main todos: src duplication checker in rco_write_resource() - -#include // malloc & free -#include // compressBound() -#include // memset() -#include "general.h" -#include "rcomain.h" -#include "rcofile.h" - - -typedef struct { - FILE* fp; // for direct output - - - rRCOFile* rco; - - - // tracking size of resources - uint32 sizeImg, sizeModel, sizeSound; - uint32 sizeText; - - uint32 longestLangData; // just for keeping track for text compression - - - // memory compression thing - void* tables; - uint tablesSize; - uint tablesBuffered; // internal value used for buffering, following define is the amount to increase the buffer by when necessary - #define RCO_WRITE_MEM_BUFFER 65536 - uint memPos; - uint memOffset; // should always be 0xA4 - -} rRCOFile_writehelper; - - -uint write_entry(rRCOFile_writehelper* rcoH, rRCOEntry* entry, uint parentOffset, uint prevOffset, Bool isLastSubentry); -uint rcowrite_ftell(rRCOFile_writehelper* rcoH); -void rco_fwrite(rRCOFile_writehelper* rcoH, void* buffer, uint len); -void rcowrite_fseek(rRCOFile_writehelper* rcoH, uint pos); - -uint rco_write_resource(FILE* dest, rRCOEntry* entry, uint destCompression, writerco_options* opts, rRCOFile* rco); -uint rco_write_text_resource(rRCOFile_writehelper* rcoH, rRCOEntry* entry, uint destCompression, writerco_options* opts, uint lang, Bool isLast); - - -void rco_write_fix_refs(rRCOEntry* parent, rRCOFile_writehelper* rcoH, rRCOFile* rco, const int* lenArray, const int lenNum, Bool isObj); - -uint write_hash_table(rRCOFile_writehelper* rcoH, rRCOEntry* entry, rRCOFile* rco); -uint write_text_hash_table(rRCOFile_writehelper* rcoH, rRCOEntry* entry, rRCOFile* rco); -void do_hashing(rRCOEntry* entry, rRCOFile* rco, Bool recurse, uint32* hashTable, uint hashTableSize); -uint calc_hash(char* in, uint32* hashTable, uint hashTableSize); -int text_hash_table_qsort(const rRCOEntry** a, const rRCOEntry** b); - -// packing: use RCO_DATA_COMPRESSION_* constants -Bool write_rco(rRCOFile* rco, char* fn, writerco_options opts) { - uint i; - rRCOFile_writehelper rcoH; - - // delete file if exists - if(file_exists(fn)) { - if(remove(fn)) { - error("Unable to write to file %s", fn); - return FALSE; - } - } - - - rcoH.rco = rco; - rcoH.fp = fopen(fn, "wb"); - if(!rcoH.fp) { - error("Unable to open file %s", fn); - return FALSE; - } - - - PRFHeader header; - header.signature = RCO_SIGNATURE; - header.version = (opts.packHeader == RCO_DATA_COMPRESSION_RLZ ? 0x95 : opts.packHeader == RCO_DATA_COMPRESSION_ZLIB ? 0x90 : 0x71); - if(rco->verId) { // we won't actually use specified value, rather, we'll require using the minimum version from above - if(rco->verId > header.version) - header.version = rco->verId; - } - header.null = 0; - header.compression = (opts.packHeader << 4) | (rco->umdFlag & 0xF); - - header.pMainTable = 0xA4; // pretty much always the case - // set other sections to nothing for now - header.pVSMXTable = header.pTextTable = header.pSoundTable = header.pModelTable = header.pImgTable = header.pObjTable = header.pAnimTable = RCO_NULL_PTR; - header.pUnknown = header.pFontTable = RCO_NULL_PTR; - - // don't know positions of text/label/event data too, but we do know the lengths for label/events - //header.pTextData = header.pLabelData = header.pEventData = 0; - header.lLabelData = rco->labelsLen; - header.lEventData = rco->eventsLen; - header.lTextData = 0; - - // set pointer sections to blank too - header.pTextPtrs = header.pImgPtrs = header.pModelPtrs = header.pSoundPtrs = header.pObjPtrs = header.pAnimPtrs = RCO_NULL_PTR; - - header.lTextPtrs = header.lImgPtrs = header.lModelPtrs = header.lSoundPtrs = header.lObjPtrs = header.lAnimPtrs = 0; - - // also blank... - header.pImgData = header.pSoundData = header.pModelData = RCO_NULL_PTR; - header.lImgData = header.lSoundData = header.lModelData = 0; - - header.unknown[0] = header.unknown[1] = header.unknown[2] = 0xFFFFFFFF; - - - - - // write resources to a separate file to get around the issue of unknown packed size when writing the header (and you can't change it backed after the header is packed...) - FILE* fTmp = NULL; - if((rco->tblImage && rco->tblImage->numSubentries) - ||(rco->tblSound && rco->tblSound->numSubentries) - ||(rco->tblModel && rco->tblModel->numSubentries)) { - uint32 totalPackedLen = 0; - rRCOEntry* rcoNode; - fTmp = tmpfile(); - - if(rco->tblImage && rco->tblImage->numSubentries) { - for(rcoNode=rco->tblImage->firstChild; rcoNode; rcoNode=rcoNode->next) { - // our compression decision thing - uint c = ((rRCOImgModelEntry*)(rcoNode->extra))->compression; - if(((rRCOImgModelEntry*)(rcoNode->extra))->format < RCO_IMG_BMP) { - if(opts.packImgCompr != -1) - c = opts.packImgCompr; - } - else { - if(opts.packImg != -1) - c = opts.packImg; - } - - if(rcoNode->srcLenUnpacked) { - rcoNode->srcLen = rco_write_resource(fTmp, rcoNode, c, &opts, rco); - if(!rcoNode->srcLen && rcoNode->labelOffset != RCO_NULL_PTR) warning("[resource] Can't write image resource '%s'!", rco->labels + rcoNode->labelOffset); - } - rcoNode->srcCompression = c; - rcoNode->srcAddr = totalPackedLen; - - totalPackedLen += (rcoNode->srcLen % 4 ? (rcoNode->srcLen/4)*4+4 : rcoNode->srcLen); - } - header.lImgData = totalPackedLen; - } - - totalPackedLen = 0; - if(rco->tblSound && rco->tblSound->numSubentries) { - for(rcoNode=rco->tblSound->firstChild; rcoNode; rcoNode=rcoNode->next) { - if(rcoNode->srcLenUnpacked) { - uint packedLen = rco_write_resource(fTmp, rcoNode, RCO_DATA_COMPRESSION_NONE, &opts, rco); - if(!packedLen && rcoNode->labelOffset != RCO_NULL_PTR) warning("[resource] Can't write sound resource '%s'!", rco->labels + rcoNode->labelOffset); - totalPackedLen += ALIGN_TO_4(packedLen); - //if(totalPackedLen %4) totalPackedLen += 4-(totalPackedLen%4); - } - } - header.lSoundData = totalPackedLen; - } - - totalPackedLen = 0; - if(rco->tblModel && rco->tblModel->numSubentries) { - for(rcoNode=rco->tblModel->firstChild; rcoNode; rcoNode=rcoNode->next) { - uint c = ((rRCOImgModelEntry*)(rcoNode->extra))->compression; - if(opts.packModel != -1) - c = opts.packModel; - - if(rcoNode->srcLenUnpacked) { - rcoNode->srcLen = rco_write_resource(fTmp, rcoNode, c, &opts, rco); - if(!rcoNode->srcLen && rcoNode->labelOffset != RCO_NULL_PTR) warning("[resource] Can't write model resource '%s'!", rco->labels + rcoNode->labelOffset); - } - rcoNode->srcCompression = c; - rcoNode->srcAddr = totalPackedLen; - - totalPackedLen += (rcoNode->srcLen % 4 ? (rcoNode->srcLen/4)*4+4 : rcoNode->srcLen); - } - header.lModelData = totalPackedLen; - } - - rewind(fTmp); - } - - - - filewrite(rcoH.fp, &header, sizeof(header)); - - rcoH.tables = 0; - - // if compressing, write to memory - if(opts.packHeader) { - rcoH.tables = malloc(RCO_WRITE_MEM_BUFFER); - rcoH.memPos = rcoH.tablesSize = 0; - rcoH.tablesBuffered = RCO_WRITE_MEM_BUFFER; - rcoH.memOffset = ftell(rcoH.fp); - } - - rcoH.sizeImg = rcoH.sizeModel = rcoH.sizeSound = rcoH.sizeText = 0; - rcoH.longestLangData = 0; - - write_entry(&rcoH, &(rco->tblMain), 0xA4 /* typically where the main table is written to */, 0, TRUE); - - // fix up object/anim extra data - { - if(rco->tblObj) - rco_write_fix_refs(rco->tblObj, &rcoH, rco, RCO_OBJ_EXTRA_LEN, RCO_OBJ_EXTRA_LEN_NUM, TRUE); - if(rco->tblAnim) - rco_write_fix_refs(rco->tblAnim, &rcoH, rco, RCO_ANIM_EXTRA_LEN, RCO_ANIM_EXTRA_LEN_NUM, FALSE); - } - - { // write hashtable data - - /* { // special case for text hashes - if(rco->numPtrText) { - header.pTextPtrs = rcowrite_ftell(&rcoH); - for(i=0; inumPtrText; i++) { - uint32 writePtr = 0; - if(rco->ptrText[i].textEntry && rco->ptrText[i].index) - writePtr = rco->ptrText[i].textEntry->offset + sizeof(RCOEntry) + sizeof(RCOTextEntry) + (rco->ptrText[i].index - ((rRCOTextEntry*)(rco->ptrText[i].textEntry->extra))->indexes)*sizeof(RCOTextIndex); - rco_fwrite(&rcoH, &writePtr, sizeof(uint32)); - } - } - } */ - if(rco->tblText) { - header.pTextPtrs = rcowrite_ftell(&rcoH); - header.lTextPtrs = 0; - - // generate sorted list of text entries, sorted by languageID - rRCOEntry** sList = make_sorted_list_of_subentries(rco->tblText, text_hash_table_qsort); - - for(i=0; itblText->numSubentries; i++) - header.lTextPtrs += write_text_hash_table(&rcoH, sList[i], rco); - free(sList); - - header.lTextPtrs *= sizeof(uint32); - } - - if(rco->tblImage) { - header.pImgPtrs = rcowrite_ftell(&rcoH); - header.lImgPtrs = write_hash_table(&rcoH, rco->tblImage, rco) * sizeof(uint32); - } - if(rco->tblModel) { - header.pModelPtrs = rcowrite_ftell(&rcoH); - header.lModelPtrs = write_hash_table(&rcoH, rco->tblModel, rco) * sizeof(uint32); - } - if(rco->tblSound) { - header.pSoundPtrs = rcowrite_ftell(&rcoH); - header.lSoundPtrs = write_hash_table(&rcoH, rco->tblSound, rco) * sizeof(uint32); - } - if(rco->tblObj) { - header.pObjPtrs = rcowrite_ftell(&rcoH); - header.lObjPtrs = write_hash_table(&rcoH, rco->tblObj, rco) * sizeof(uint32); - } - if(rco->tblAnim) { - header.pAnimPtrs = rcowrite_ftell(&rcoH); - header.lAnimPtrs = write_hash_table(&rcoH, rco->tblAnim, rco) * sizeof(uint32); - } - /* - #define RCO_WRITERCO_WRITE_PTR_SECT(pd, pl, hp) { \ - if(pl) { \ - hp = rcowrite_ftell(&rcoH); \ - for(i=0; ioffset), sizeof(uint32)); \ - else { \ - uint32 zero = 0; \ - rco_fwrite(&rcoH, &zero, sizeof(uint32)); \ - } \ - } \ - } \ - } - //RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrText, rco->numPtrText, header.pTextPtrs); - RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrImg, rco->numPtrImg, header.pImgPtrs); - RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrModel, rco->numPtrModel, header.pModelPtrs); - RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrSound, rco->numPtrSound, header.pSoundPtrs); - RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrObj, rco->numPtrObj, header.pObjPtrs); - RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrAnim, rco->numPtrAnim, header.pAnimPtrs); - */ - } - - - { // write label/event data (and text if applicable) - - // write text (note, old behaviour - newer RCOs have text written in a different location) - if(!opts.packText && rco->tblText && rco->tblText->numSubentries) { - rRCOEntry* rcoNode; - header.pTextData = rcowrite_ftell(&rcoH); - header.lTextData = rcoH.sizeText; - for(rcoNode=rco->tblText->firstChild; rcoNode; rcoNode=rcoNode->next) { - rco_write_text_resource(&rcoH, rcoNode, RCO_DATA_COMPRESSION_NONE, &opts, ((rRCOTextEntry*)(rcoNode->extra))->lang, (rco->tblText->lastChild == rcoNode)); - } - } - - - // write label+event data - header.pLabelData = rcowrite_ftell(&rcoH); - if(rco->labelsLen) - rco_fwrite(&rcoH, rco->labels, rco->labelsLen); - header.pEventData = rcowrite_ftell(&rcoH); - if(rco->eventsLen) - rco_fwrite(&rcoH, rco->events, rco->eventsLen); - else if(rco->tblObj || rco->tblAnim) { // weird case: if there's object entries, there will be 4 bytes for events; I'll assume this covers anim as well (although there isn't an RCO with anim that doesn't have objects) - uint32 zero = 0; - rco_fwrite(&rcoH, &zero, sizeof(zero)); - header.lEventData = sizeof(zero); - } - - // the text pointer is weird in that if there's no text, it's set equal to the label pointer; even weirder, some RCOs have a null pointer (for FW5.00 all except lftv_* RCOs have null pointers for pTextData if there's no text) - // my theory: if compressing, it will be RCO_NULL_PTR, otherwise it'll = header.pLabelData - //if(!header.lTextData) header.pTextData = RCO_NULL_PTR; - //if(!header.lTextData) header.pTextData = header.pLabelData; - if(!header.lTextData) header.pTextData = (opts.packHeader ? RCO_NULL_PTR : header.pLabelData); - } - - // flush compression stuff here - HeaderComprInfo ci; - if(opts.packHeader) { - uint8* bufferOut = NULL; - ci.lenLongestText = rcoH.longestLangData; - ci.lenUnpacked = rcoH.tablesSize; - ci.lenPacked = 0; - - if(opts.packHeader == RCO_DATA_COMPRESSION_ZLIB) { - uint bound = compressBound(rcoH.tablesSize); - bufferOut = (uint8*)malloc(bound); - ci.lenPacked = zlib_compress(rcoH.tables, rcoH.tablesSize, bufferOut, bound, opts.zlibLevel, opts.zlibMethod); - } else if(opts.packHeader == RCO_DATA_COMPRESSION_RLZ) { - bufferOut = (uint8*)malloc(rcoH.tablesSize); - ci.lenPacked = rlz_compress(rcoH.tables, rcoH.tablesSize, bufferOut, rcoH.tablesSize, opts.rlzMode); - } else { - error("lulwut?"); - exit(1); - } - int comprMisalign = ci.lenPacked % 4; - uint packedLen = ci.lenPacked; - - if(rco->eSwap) es_headerComprInfo(&ci); - filewrite(rcoH.fp, &ci, sizeof(ci)); - filewrite(rcoH.fp, bufferOut, packedLen); - free(bufferOut); - - if(comprMisalign) { // 4 byte align - uint32 zero = 0; - filewrite(rcoH.fp, &zero, 4 - comprMisalign); - } - } - - // write text if packing header - if(opts.packText && rco->tblText && rco->tblText->numSubentries) { - rRCOEntry* rcoNode; - //header.pTextData = rcowrite_ftell(&rcoH); - header.pTextData = ftell(rcoH.fp); - header.lTextData = 0; // rcoH.sizeText; - for(rcoNode=rco->tblText->firstChild; rcoNode; rcoNode=rcoNode->next) { - header.lTextData += rco_write_text_resource(&rcoH, rcoNode, opts.packHeader, &opts, ((rRCOTextEntry*)(rcoNode->extra))->lang, (rco->tblText->lastChild == rcoNode)); - } - } - - // write resources - /* { - uint32 totalPackedLen = 0; - if(rco->tblImage) { - header.pImgData = rcowrite_ftell(&rcoH); - header.lImgData = rcoH.sizeImg; // TOxDO: this model actually won't work - we have to update the offsets of ALL the entries after packing... - for(i=0; itblImage->numSubentries; i++) { - uint32 packedSize = rco_write_resource(&rcoH, &(rco->tblImage->subentries[i]), RCO_DATA_COMPRESSION_NONE); // TOxDO: change this - // TOxDO: update packed size value - uint curFpos = rcowrite_ftell(rcoH.fp); - - - totalPackedLen += (packedSize % 4 ? (packedSize/4)*4+4 : packedSize); - } - header.lImgData = totalPackedLen; - } - - totalPackedLen = 0; - if(rco->tblSound) { - header.pSoundData = rcowrite_ftell(&rcoH); - header.lSoundData = rcoH.sizeSound; - for(i=0; itblSound->numSubentries; i++) { - totalPackedLen += rco_write_resource(&rcoH, &(rco->tblSound->subentries[i]), RCO_DATA_COMPRESSION_NONE); - if(totalPackedLen %4) totalPackedLen += 4-(totalPackedLen%4); - } - header.lSoundData = totalPackedLen; - } - - // TOxDO: write model resources - } */ - - if((rco->tblImage && rco->tblImage->numSubentries) - ||(rco->tblSound && rco->tblSound->numSubentries) - ||(rco->tblModel && rco->tblModel->numSubentries)) { - // update data pointers - uint32 pos = ftell(rcoH.fp); - if(rco->tblImage && rco->tblImage->numSubentries) { - header.pImgData = pos; - pos += header.lImgData; - } - if(rco->tblSound && rco->tblSound->numSubentries) { - header.pSoundData = pos; - pos += header.lSoundData; - } - if(rco->tblModel && rco->tblModel->numSubentries) { - header.pModelData = pos; - pos += header.lModelData; - } - // copy contents of fTmp across (uses a simple buffered copy) - uint len = header.lImgData + header.lSoundData + header.lModelData; - uint8 buffer[65536]; - while(len) { - uint readAmt = (len > 65536 ? 65536 : len); - fileread(fTmp, buffer, readAmt); - filewrite(rcoH.fp, buffer, readAmt); - len -= readAmt; - } - - fclose(fTmp); // this deletes our temp file - } - - - // fix header - if(rco->tblVSMX) header.pVSMXTable = rco->tblVSMX->offset; - if(rco->tblText) header.pTextTable = rco->tblText->offset; - if(rco->tblSound) header.pSoundTable = rco->tblSound->offset; - if(rco->tblModel) header.pModelTable = rco->tblModel->offset; - if(rco->tblImage) header.pImgTable = rco->tblImage->offset; - if(rco->tblFont) header.pFontTable = rco->tblFont->offset; - if(rco->tblObj) header.pObjTable = rco->tblObj->offset; - if(rco->tblAnim) header.pAnimTable = rco->tblAnim->offset; - - rewind(rcoH.fp); - if(rco->eSwap) es_rcoHeader(&header); - filewrite(rcoH.fp, &header, sizeof(header)); - - // TODO: fix resource pointers? - // TODO: tie things up etc?? - - - fclose(rcoH.fp); - - return TRUE; -} - -// returns next entry offset (like the length, but the last entry returns zero) - doesn't really have much meaning - it's primarily used for internal purposes -uint write_entry(rRCOFile_writehelper* rcoH, rRCOEntry* entry, uint parentOffset, uint prevOffset, Bool isLastSubentry) { - uint fPos = rcowrite_ftell(rcoH); - entry->offset = fPos; - RCOEntry re; - re.typeId = (entry->id << 8) | (entry->type); - re.blank = 0; - re.labelOffset = entry->labelOffset; - - if(entry->type == 0 || ((entry->id == RCO_TABLE_MAIN || entry->id == RCO_TABLE_ANIM) && entry->type == 1)) { - // a "parent" entry - re.eHeadSize = 0; - re.entrySize = sizeof(RCOEntry); - - // anim main tables have a prevOffset though :/ (object main tables don't) - if(entry->id == RCO_TABLE_ANIM) - re.prevEntryOffset = prevOffset; - else - re.prevEntryOffset = 0; - } else { - re.eHeadSize = sizeof(RCOEntry); - re.entrySize = 0; - - // not sure why, but it appears that "non-main" object/anim entries also have the total size of the entry stored in entrySize; only done if the entry has subentries - if((entry->id == RCO_TABLE_OBJ || entry->id == RCO_TABLE_ANIM) && entry->numSubentries) { - int lenNum = (entry->id == RCO_TABLE_OBJ ? RCO_OBJ_EXTRA_LEN_NUM : RCO_ANIM_EXTRA_LEN_NUM); - const int* lenArray; - lenArray = (entry->id == RCO_TABLE_OBJ ? RCO_OBJ_EXTRA_LEN : RCO_ANIM_EXTRA_LEN); - if(entry->type <= lenNum && lenArray[entry->type] > 0) - re.entrySize = sizeof(RCOEntry) + lenArray[entry->type] *sizeof(uint32); - } - - re.prevEntryOffset = prevOffset; - } - re.numSubentries = entry->numSubentries; - re.parentTblOffset = fPos - parentOffset; - re.blanks[0] = re.blanks[1] = 0; - re.nextEntryOffset = 0; - - if(entry->rco->eSwap) es_rcoEntry(&re); - rco_fwrite(rcoH, &re, sizeof(re)); - if(entry->rco->eSwap) es_rcoEntry(&re); - - // extra items here - switch(entry->id) { - case RCO_TABLE_VSMX: - if(entry->type == 1) { - RCOVSMXEntry rve; - rve.offset = 0; - rve.length = entry->srcLenUnpacked; - if(entry->rco->eSwap) es_rcoVsmxEntry(&rve); - rco_fwrite(rcoH, &rve, sizeof(rve)); - - uint vsmxLen=0; - uint8* bufferVSMX = (uint8*)read_resource(entry, &vsmxLen); - if(bufferVSMX) { - if(vsmxLen == entry->srcLenUnpacked) - rco_fwrite(rcoH, bufferVSMX, vsmxLen); - free(bufferVSMX); - } - } - break; - case RCO_TABLE_TEXT: - if(entry->type == 1) { - RCOTextEntry rte; - rRCOTextEntry* src = (rRCOTextEntry*)entry->extra; - uint i; - rte.lang = src->lang; - rte.format = src->format; - rte.numIndexes = src->numIndexes; - if(entry->rco->eSwap) es_rcoTextEntry(&rte); - rco_fwrite(rcoH, &rte, sizeof(rte)); - - // instead of blindly dumping src->indexes, we'll "pack" the entries together (allows source file to be of a different format, ie INI) - uint32 entryTextOffset = rcoH->sizeText; - for(i=0; inumIndexes; i++) { - RCOTextIndex rti; - rti.labelOffset = src->indexes[i].labelOffset; - rti.length = src->indexes[i].length; - // Sony is doing some weird stuff :| - if(src->indexes[i].offset == RCO_NULL_PTR) - rti.offset = RCO_NULL_PTR; - else if(rti.length) { - if(rcoH->tables) // compressing - we need to make the offset relative to the section of text data - rti.offset = rcoH->sizeText - entryTextOffset; - else - rti.offset = rcoH->sizeText; - } - else // TODO: experimental (it seems that Sony likes sticking in a weird pointer for a blank text entry) - rti.offset = entryTextOffset -1; - - rcoH->sizeText += rti.length; // doesn't have trailing null, so no +1 needed - // align to 4 byte boundary - rcoH->sizeText = ALIGN_TO_4(rcoH->sizeText); - - if(entry->rco->eSwap) es_rcoTextIndex(&rti); - rco_fwrite(rcoH, &rti, sizeof(rti)); - } - - if(rcoH->sizeText - entryTextOffset > rcoH->longestLangData) - rcoH->longestLangData = rcoH->sizeText - entryTextOffset; - } - break; - case RCO_TABLE_IMG: case RCO_TABLE_MODEL: - if(entry->type == 1) { - rRCOImgModelEntry* srcExtra = (rRCOImgModelEntry*)entry->extra; - RCOImgModelEntry rie; - uint32* totalResSize = (entry->id == RCO_TABLE_IMG ? &(rcoH->sizeImg) : &(rcoH->sizeModel)); - rie.format = srcExtra->format; - // we've already got the compression info done here, so just copy it over - rie.compression = entry->srcCompression | (srcExtra->unkCompr << 8); - rie.offset = *totalResSize; - rie.sizeUnpacked = entry->srcLenUnpacked; - rie.sizePacked = entry->srcLen; - *totalResSize += rie.sizePacked; - // align to 4 byte boundary - *totalResSize = ALIGN_TO_4(*totalResSize); - - if(entry->rco->eSwap) es_rcoImgModelEntry(&rie); - - // we'll omit the packed length value if this is an uncompressed entry - if(entry->rco->ps3) { // PS3 image quirk - uint32 one = ENDIAN_SWAP(1); - rco_fwrite(rcoH, &rie, sizeof(rie) - sizeof(uint32)); - rco_fwrite(rcoH, &one, sizeof(one)); - if(entry->srcCompression != RCO_DATA_COMPRESSION_NONE) - rco_fwrite(rcoH, &rie.sizeUnpacked, sizeof(rie.sizeUnpacked)); - } else { - rco_fwrite(rcoH, &rie, sizeof(rie) - (entry->srcCompression == RCO_DATA_COMPRESSION_NONE ? sizeof(uint32) : 0)); - } - } - break; - case RCO_TABLE_SOUND: - if(entry->type != 0) { - rRCOSoundEntry* srcExtra = (rRCOSoundEntry*)entry->extra; - RCOSoundEntry rse; - uint rseOffset; - rse.format = srcExtra->format; - rseOffset = rse.offset = rcoH->sizeSound; - rse.channels = srcExtra->channels; - rse.sizeTotal = entry->srcLenUnpacked; - - rcoH->sizeSound += rse.sizeTotal; - // align to 4 byte boundary - rcoH->sizeSound = ALIGN_TO_4(rcoH->sizeSound); - - if(entry->rco->eSwap) es_rcoSoundEntry(&rse); - rco_fwrite(rcoH, &rse, sizeof(rse)); - - // write size/offset pairs - uint i; - // TODO: might actually restrict this to two channels later on - for(i=0; ichannels; i++) { - uint32 stuffToWrite[] = {srcExtra->channelData[i*2], srcExtra->channelData[i*2+1] + rseOffset}; - if(entry->rco->eSwap) { - stuffToWrite[0] = ENDIAN_SWAP(stuffToWrite[0]); - stuffToWrite[1] = ENDIAN_SWAP(stuffToWrite[1]); - } - rco_fwrite(rcoH, stuffToWrite, sizeof(stuffToWrite)); - } - if(srcExtra->channels < 2) { - // we'll write an extra blank channel, complying with how Sony's RCO tools work - uint32 stuffToWrite[] = {0, RCO_NULL_PTR}; - uint i; - // actually, the following is unnecessary, but we'll keep it here for reference sake - if(entry->rco->eSwap) { - stuffToWrite[0] = ENDIAN_SWAP(stuffToWrite[0]); - stuffToWrite[1] = ENDIAN_SWAP(stuffToWrite[1]); - } - for(i=srcExtra->channels; i<2; i++) - rco_fwrite(rcoH, &stuffToWrite, sizeof(uint32)*2); - } - } - break; - case RCO_TABLE_FONT: - if(entry->type == 1) { - RCOFontEntry rfe; - rRCOFontEntry* srcExtra = (rRCOFontEntry*)entry->extra; - rfe.format = srcExtra->format; - rfe.compression = srcExtra->compression; - rfe.unknown = srcExtra->unknown; - rfe.unknown2 = srcExtra->unknown2; - if(entry->rco->eSwap) es_rcoFontEntry(&rfe); - rco_fwrite(rcoH, &rfe, sizeof(rfe)); - } - break; - case RCO_TABLE_OBJ: case RCO_TABLE_ANIM: - if(entry->type != 0) { - int lenNum; - lenNum = (entry->id == RCO_TABLE_OBJ ? RCO_OBJ_EXTRA_LEN_NUM : RCO_ANIM_EXTRA_LEN_NUM); - const int* lenArray; - lenArray = (entry->id == RCO_TABLE_OBJ ? RCO_OBJ_EXTRA_LEN : RCO_ANIM_EXTRA_LEN); - // just allocate space because we need to fix this later on - if(entry->type <= lenNum && lenArray[entry->type] > 0) { - uint32 anAwesomeVariable = lenArray[entry->type]; - while(anAwesomeVariable--) - rco_fwrite(rcoH, &anAwesomeVariable, sizeof(anAwesomeVariable)); - } - else { - // TODO: do something if type is unknown - } - } - break; - } - - // subentries - uint nextOffset = 0; - rRCOEntry* rcoNode; - for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) { - nextOffset = write_entry(rcoH, rcoNode, fPos, nextOffset, (rcoNode->next ? FALSE:TRUE)); - } - - - // write nextEntryOffset - if(!isLastSubentry) { - uint curPos = rcowrite_ftell(rcoH); - re.nextEntryOffset = curPos - fPos; - rcowrite_fseek(rcoH, fPos); - if(entry->rco->eSwap) es_rcoEntry(&re); - rco_fwrite(rcoH, &re, sizeof(re)); - if(entry->rco->eSwap) es_rcoEntry(&re); - rcowrite_fseek(rcoH, curPos); - } - return re.nextEntryOffset; -} - - -void rco_fwrite(rRCOFile_writehelper* rcoH, void* buffer, uint len) { - // memory writing - if(rcoH->tables) { - uint len4 = ALIGN_TO_4(len); - if(rcoH->memPos + len4 > rcoH->tablesBuffered) { - rcoH->tablesBuffered = rcoH->memPos + len4 + RCO_WRITE_MEM_BUFFER; - rcoH->tables = realloc(rcoH->tables, rcoH->tablesBuffered); - } - - memcpy((uint8*)rcoH->tables + rcoH->memPos, buffer, len); - if(len % 4) { - memset((uint8*)rcoH->tables + rcoH->memPos + len, 0, 4-(len%4)); - } - rcoH->memPos += len4; - if(rcoH->memPos > rcoH->tablesSize) - rcoH->tablesSize = rcoH->memPos; - - } else { - filewrite(rcoH->fp, buffer, len); - - // always add 4 byte padding (should add an argument, but will always be TRUE anyway, so I cbf) - if(len % 4) { - uint32 zero=0; - filewrite(rcoH->fp, &zero, 4-(len%4)); - } - } -} -uint rcowrite_ftell(rRCOFile_writehelper* rcoH) { - // memory stuff - if(rcoH->tables) return rcoH->memPos + rcoH->memOffset; - else return ftell(rcoH->fp); -} -void rcowrite_fseek(rRCOFile_writehelper* rcoH, uint pos) { - // memory stuff - if(rcoH->tables) rcoH->memPos = pos - rcoH->memOffset; - else fseek(rcoH->fp, pos, SEEK_SET); -} - -// returns the length of the packed data -// TBH, I really can't be stuffed writing a buffered copy/compress/decompressor (and anyway, it may not work with future compression routines, so meh) -uint rco_write_resource(FILE* dest, rRCOEntry* entry, uint destCompression, writerco_options* opts, rRCOFile* rco) { - - uint len=0; - uint8* bufferMid = (uint8*)read_resource(entry, &len); - if(!bufferMid) { - if(entry->labelOffset) - warning("Failed to read resource '%s'.", rco->labels + entry->labelOffset); - return 0; - } - - if(len != entry->srcLenUnpacked) { - free(bufferMid); - return 0; - } - - uint8* bufferOut; - uint packedSize = 0; - if(destCompression == RCO_DATA_COMPRESSION_ZLIB) { - uint compSize = compressBound(entry->srcLenUnpacked); - bufferOut = (uint8*)malloc(compSize); - packedSize = zlib_compress(bufferMid, entry->srcLenUnpacked, bufferOut, compSize, opts->zlibLevel, opts->zlibMethod); - if(!packedSize) { - if(entry->labelOffset) - warning("Failed to compress resource '%s'.", rco->labels + entry->labelOffset); - return 0; - } - - free(bufferMid); - } else if(destCompression == RCO_DATA_COMPRESSION_RLZ) { - #ifdef DISABLE_RLZ - error("RLZ compression not supported."); - exit(1); - #endif - bufferOut = (uint8*)malloc(entry->srcLenUnpacked); - packedSize = rlz_compress(bufferMid, entry->srcLenUnpacked, bufferOut, entry->srcLenUnpacked, opts->rlzMode); - if(!packedSize) { - if(entry->labelOffset) - warning("Failed to compress resource '%s'.", rco->labels + entry->labelOffset); - return 0; - } - - free(bufferMid); - } else { - bufferOut = bufferMid; - packedSize = entry->srcLenUnpacked; - } - - filewrite(dest, bufferOut, packedSize); - free(bufferOut); - - /* - char buffer[65536], outBuffer[65536]; - uint len=entry->srcLen; - z_stream out; - if(destStream == RCO_DATA_COMPRESSION_ZLIB) { - ZLIB_INIT_DEFLATE(out, 9, Z_DEFAULT_STRATEGY); - out.next_in = buffer; - out.avail_in = 65536; - out.next_out = outBuffer; - out.avail_out = 65536; - } - - // copy with buffer - while(len) { - uint readAmt = (len < 65536 ? len : 65536); - if(!fileread(src, &buffer, readAmt)) { - fclose(src); - return FALSE; - } - - if(destCompression == RCO_DATA_COMPRESSION_NONE) - filewrite(dest, &buffer, readAmt); - else if(destCompression == RCO_DATA_COMPRESSION_ZLIB) { - - } - - len -= readAmt; - } - - // TOxDO: also don't forget to check lenUnpacked - - - fclose(src); - - - if(destStream == RCO_DATA_COMPRESSION_ZLIB) - deflateEnd(&out); - */ - - // 4byte alignment - if(packedSize % 4) { - uint32 zero = 0; - filewrite(dest, &zero, 4 - (packedSize % 4)); - } - - return packedSize; -} - -uint rco_write_text_resource(rRCOFile_writehelper* rcoH, rRCOEntry* entry, uint destCompression, writerco_options* opts, uint lang, Bool isLast) { - - uint len=0; - uint8* bufferMid = (uint8*)read_resource(entry, &len); - if(!bufferMid) return 0; - - if(len != entry->srcLenUnpacked) { - free(bufferMid); - return 0; - } - - rRCOTextEntry* extra = (rRCOTextEntry*)(entry->extra); - - - TextComprInfo tci; - char* textBuffer = NULL; - uint textBufferPos = 0; - if(destCompression) { - tci.lang = lang; - tci.unknown = 1; - tci.unpackedLen = 0; - //textBuffer = (char*)malloc(1); - } - - uint i; - for(i=0; inumIndexes; i++) { - uint len = extra->indexes[i].length; - if(!len) continue; - - if(destCompression) { - tci.unpackedLen += ALIGN_TO_4(len); - textBuffer = (char*)realloc(textBuffer, tci.unpackedLen); - memcpy(textBuffer + textBufferPos, bufferMid + extra->indexes[i].offset, len); - - if(len % 4) { - memset(textBuffer + textBufferPos + len, 0, 4-(len%4)); - } - textBufferPos += ALIGN_TO_4(len); - } - else { - rco_fwrite(rcoH, bufferMid + extra->indexes[i].offset, len); - } - } - - free(bufferMid); - - if(destCompression) { - Bool success = TRUE; - uint8* bufferOut = NULL; - - if(destCompression == RCO_DATA_COMPRESSION_ZLIB) { - uint compSize = compressBound(tci.unpackedLen); - bufferOut = (uint8*)malloc(compSize); - tci.packedLen = zlib_compress(textBuffer, tci.unpackedLen, bufferOut, compSize, opts->zlibLevel, opts->zlibMethod); - } else if(destCompression == RCO_DATA_COMPRESSION_RLZ) { - #ifdef DISABLE_RLZ - error("RLZ compression not supported."); - exit(1); - #endif - bufferOut = (uint8*)malloc(tci.unpackedLen); - tci.packedLen = rlz_compress(textBuffer, tci.unpackedLen, bufferOut, tci.unpackedLen, opts->rlzMode); - } - - if(!tci.packedLen) { // compression failed - free(bufferOut); - bufferOut = NULL; - success = FALSE; - } - else if(bufferOut) { - if(isLast) - tci.nextOffset = 0; - else { - tci.nextOffset = sizeof(tci) + tci.packedLen; - tci.nextOffset = ALIGN_TO_4(tci.nextOffset); - } - if(entry->rco->eSwap) es_textComprInfo(&tci); - filewrite(rcoH->fp, &tci, sizeof(tci)); - if(entry->rco->eSwap) es_textComprInfo(&tci); - - filewrite(rcoH->fp, bufferOut, tci.packedLen); - free(bufferOut); - - if(tci.packedLen % 4) { - uint32 zero = 0; - filewrite(rcoH->fp, &zero, 4 - (tci.packedLen % 4)); - } - } else - success = FALSE; - - free(textBuffer); - if(!success) return 0; - return ALIGN_TO_4(tci.packedLen) + sizeof(tci); - } - - return 1; -} - -void rco_write_fix_refs(rRCOEntry* parent, rRCOFile_writehelper* rcoH, rRCOFile* rco, const int* lenArray, const int lenNum, Bool isObj) { - rRCOEntry* rcoNode; - for(rcoNode=parent->firstChild; rcoNode; rcoNode=rcoNode->next) { - rcowrite_fseek(rcoH, rcoNode->offset + sizeof(RCOEntry)); - uint j, j2; - uint8* extraPtr = (uint8*)rcoNode->extra; - if(rcoNode->type <= lenNum && lenArray[rcoNode->type] > 0) { - const int* typeArray = (isObj ? RCO_OBJ_EXTRA_TYPES[rcoNode->type] : RCO_ANIM_EXTRA_TYPES[rcoNode->type]); - for(j=0, j2=0; (int)jtype]; j++, j2++) { - Bool cond; - if(isObj) - cond = (RCO_OBJ_IS_REF(rcoNode->type, j2)); - else // anim - cond = (RCO_ANIM_IS_REF(rcoNode->type, j2)); - //cond = (RCO_ANIM_EXTRA_REFS[rcoNode->type] && j == 0); - if(cond) { - rRCORef* srcRef = (rRCORef*)extraPtr; - RCOReference destRef; - destRef.type = srcRef->type; - - switch(destRef.type) { - case RCO_REF_EVENT: - //destRef.ptr = ((char*)(srcRef->ptr)) - rco->events; - destRef.ptr = srcRef->rawPtr; - break; - case RCO_REF_NONE: - destRef.ptr = RCO_NULL_PTR; - break; - case RCO_REF_TEXT: - destRef.ptr = srcRef->rawPtr; - break; - case RCO_REF_IMG: case RCO_REF_MODEL: case RCO_REF_FONT: case RCO_REF_OBJ2: case RCO_REF_ANIM: case RCO_REF_OBJ: - if(srcRef->ptr) - destRef.ptr = ((rRCOEntry*)(srcRef->ptr))->offset; - break; - default: - destRef.ptr = srcRef->rawPtr; - } - - if(rco->eSwap) { - #define ENDIAN_SWAP_HALF32(x) (((x) & 0xFF) << 8 | ((x) & 0xFF00) >> 8 | ((x) & 0xFF0000) << 8 | ((x) & 0xFF000000) >> 8) - destRef.type = ENDIAN_SWAP_HALF32(destRef.type); - destRef.ptr = ENDIAN_SWAP(destRef.ptr); - } - rco_fwrite(rcoH, &destRef, sizeof(destRef)); - - extraPtr += sizeof(rRCORef); - j++; - //if(!isObj) j2--; - } else { - if(rco->eSwap && typeArray[j2] != RCO_OBJ_EXTRA_TYPE_UNK) { - uint32 val = *(uint32*)extraPtr; - val = ENDIAN_SWAP(val); - rco_fwrite(rcoH, &val, sizeof(val)); - } else - rco_fwrite(rcoH, extraPtr, sizeof(uint32)); - extraPtr += sizeof(uint32); - } - } - } - else { - // TODO: think up something for unknown object types - } - rco_write_fix_refs(rcoNode, rcoH, rco, lenArray, lenNum, isObj); - } -} - -// returns size of hash table -uint write_hash_table(rRCOFile_writehelper* rcoH, rRCOEntry* entry, rRCOFile* rco) { - uint num = entry->numSubentries; - if(entry->id == RCO_TABLE_OBJ) - num = count_all_subentries(entry); - //if(!rco->ps3) - // interestingly, it seems that some PS3 RCOs do not round to an upper prime, but most do - num = find_larger_prime(num); - - if(!num) return 0; - - uint32* hashTable = (uint32*)malloc(num * sizeof(uint32)); - memset(hashTable, 0, num * sizeof(uint32)); - rRCOEntry* rcoNode; - for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) - do_hashing(rcoNode, rco, (entry->id == RCO_TABLE_OBJ), hashTable, num); - - // write it - rco_fwrite(rcoH, hashTable, num*sizeof(uint32)); - - free(hashTable); - return num; -} - -uint write_text_hash_table(rRCOFile_writehelper* rcoH, rRCOEntry* entry, rRCOFile* rco) { - uint num = ((rRCOTextEntry*)entry->extra)->numIndexes; - if(!num) return 0; - - uint32* hashTable = (uint32*)malloc(num * sizeof(uint32)); - memset(hashTable, 0, num * sizeof(uint32)); - uint i; - for(i=0; iextra)->indexes[i]); - if(ti->labelOffset != RCO_NULL_PTR) { - uint32* hashPtr = &hashTable[calc_hash(rco->labels + ti->labelOffset, hashTable, num)]; - *hashPtr = entry->offset + sizeof(RCOEntry) + sizeof(RCOTextEntry) + i*sizeof(RCOTextIndex); - if(rco->eSwap) *hashPtr = ENDIAN_SWAP(*hashPtr); - } - } - - // write it - rco_fwrite(rcoH, hashTable, num*sizeof(uint32)); - - free(hashTable); - return num; -} - -void do_hashing(rRCOEntry* entry, rRCOFile* rco, Bool recurse, uint32* hashTable, uint hashTableSize) { - if(entry->labelOffset != RCO_NULL_PTR) { - uint32* hashPtr = &hashTable[calc_hash(rco->labels + entry->labelOffset, hashTable, hashTableSize)]; - *hashPtr = entry->offset; - if(rco->eSwap) *hashPtr = ENDIAN_SWAP(*hashPtr); - } - if(recurse) { - rRCOEntry* rcoNode; - for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) - do_hashing(rcoNode, rco, recurse, hashTable, hashTableSize); - } -} - -uint calc_hash(char* in, uint32* hashTable, uint hashTableSize) { - uint32 hash = 0; - // calculate hash code (just a summation of the chars) - while(*in) { - hash += *in; - in++; - } - // hash compression - hash %= hashTableSize; - - // linear probing - while(hashTable[hash]) { - hash++; - if(hash >= hashTableSize) hash = 0; - } - - return hash; -} - -int text_hash_table_qsort(const rRCOEntry** a, const rRCOEntry** b) { - return ((rRCOTextEntry*)((*a)->extra))->lang - ((rRCOTextEntry*)((*b)->extra))->lang; -} +// main todos: src duplication checker in rco_write_resource() + +#include // malloc & free +#include // compressBound() +#include // memset() +#include "general.h" +#include "rcomain.h" +#include "rcofile.h" + + +typedef struct { + FILE* fp; // for direct output + + + rRCOFile* rco; + + + // tracking size of resources + uint32 sizeImg, sizeModel, sizeSound; + uint32 sizeText; + + uint32 longestLangData; // just for keeping track for text compression + + + // memory compression thing + void* tables; + uint tablesSize; + uint tablesBuffered; // internal value used for buffering, following define is the amount to increase the buffer by when necessary + #define RCO_WRITE_MEM_BUFFER 65536 + uint memPos; + uint memOffset; // should always be 0xA4 + +} rRCOFile_writehelper; + + +uint write_entry(rRCOFile_writehelper* rcoH, rRCOEntry* entry, uint parentOffset, uint prevOffset, Bool isLastSubentry); +uint rcowrite_ftell(rRCOFile_writehelper* rcoH); +void rco_fwrite(rRCOFile_writehelper* rcoH, void* buffer, uint len); +void rcowrite_fseek(rRCOFile_writehelper* rcoH, uint pos); + +uint rco_write_resource(FILE* dest, rRCOEntry* entry, uint destCompression, writerco_options* opts, rRCOFile* rco); +uint rco_write_text_resource(rRCOFile_writehelper* rcoH, rRCOEntry* entry, uint destCompression, writerco_options* opts, uint lang, Bool isLast); + + +void rco_write_fix_refs(rRCOEntry* parent, rRCOFile_writehelper* rcoH, rRCOFile* rco, const int* lenArray, const int lenNum, Bool isObj); + +uint write_hash_table(rRCOFile_writehelper* rcoH, rRCOEntry* entry, rRCOFile* rco); +uint write_text_hash_table(rRCOFile_writehelper* rcoH, rRCOEntry* entry, rRCOFile* rco); +void do_hashing(rRCOEntry* entry, rRCOFile* rco, Bool recurse, uint32* hashTable, uint hashTableSize); +uint calc_hash(char* in, uint32* hashTable, uint hashTableSize); +int text_hash_table_qsort(const rRCOEntry** a, const rRCOEntry** b); + +// packing: use RCO_DATA_COMPRESSION_* constants +Bool write_rco(rRCOFile* rco, char* fn, writerco_options opts) { + uint i; + rRCOFile_writehelper rcoH; + + // delete file if exists + if(file_exists(fn)) { + if(remove(fn)) { + error("Unable to write to file %s", fn); + return FALSE; + } + } + + + rcoH.rco = rco; + rcoH.fp = fopen(fn, "wb"); + if(!rcoH.fp) { + error("Unable to open file %s", fn); + return FALSE; + } + + + PRFHeader header; + header.signature = RCO_SIGNATURE; + header.version = (opts.packHeader == RCO_DATA_COMPRESSION_RLZ ? 0x95 : opts.packHeader == RCO_DATA_COMPRESSION_ZLIB ? 0x90 : 0x71); + if(rco->verId) { // we won't actually use specified value, rather, we'll require using the minimum version from above + if(rco->verId > header.version) + header.version = rco->verId; + } + header.null = 0; + header.compression = (opts.packHeader << 4) | (rco->umdFlag & 0xF); + + header.pMainTable = 0xA4; // pretty much always the case + // set other sections to nothing for now + header.pVSMXTable = header.pTextTable = header.pSoundTable = header.pModelTable = header.pImgTable = header.pObjTable = header.pAnimTable = RCO_NULL_PTR; + header.pUnknown = header.pFontTable = RCO_NULL_PTR; + + // don't know positions of text/label/event data too, but we do know the lengths for label/events + //header.pTextData = header.pLabelData = header.pEventData = 0; + header.lLabelData = rco->labelsLen; + header.lEventData = rco->eventsLen; + header.lTextData = 0; + + // set pointer sections to blank too + header.pTextPtrs = header.pImgPtrs = header.pModelPtrs = header.pSoundPtrs = header.pObjPtrs = header.pAnimPtrs = RCO_NULL_PTR; + + header.lTextPtrs = header.lImgPtrs = header.lModelPtrs = header.lSoundPtrs = header.lObjPtrs = header.lAnimPtrs = 0; + + // also blank... + header.pImgData = header.pSoundData = header.pModelData = RCO_NULL_PTR; + header.lImgData = header.lSoundData = header.lModelData = 0; + + header.unknown[0] = header.unknown[1] = header.unknown[2] = 0xFFFFFFFF; + + + + + // write resources to a separate file to get around the issue of unknown packed size when writing the header (and you can't change it backed after the header is packed...) + FILE* fTmp = NULL; + if((rco->tblImage && rco->tblImage->numSubentries) + ||(rco->tblSound && rco->tblSound->numSubentries) + ||(rco->tblModel && rco->tblModel->numSubentries)) { + uint32 totalPackedLen = 0; + rRCOEntry* rcoNode; + fTmp = tmpfile(); + + if(rco->tblImage && rco->tblImage->numSubentries) { + for(rcoNode=rco->tblImage->firstChild; rcoNode; rcoNode=rcoNode->next) { + // our compression decision thing + uint c = ((rRCOImgModelEntry*)(rcoNode->extra))->compression; + if(((rRCOImgModelEntry*)(rcoNode->extra))->format < RCO_IMG_BMP) { + if(opts.packImgCompr != -1) + c = opts.packImgCompr; + } + else { + if(opts.packImg != -1) + c = opts.packImg; + } + + if(rcoNode->srcLenUnpacked) { + rcoNode->srcLen = rco_write_resource(fTmp, rcoNode, c, &opts, rco); + if(!rcoNode->srcLen && rcoNode->labelOffset != RCO_NULL_PTR) warning("[resource] Can't write image resource '%s'!", rco->labels + rcoNode->labelOffset); + } + rcoNode->srcCompression = c; + rcoNode->srcAddr = totalPackedLen; + + totalPackedLen += (rcoNode->srcLen % 4 ? (rcoNode->srcLen/4)*4+4 : rcoNode->srcLen); + } + header.lImgData = totalPackedLen; + } + + totalPackedLen = 0; + if(rco->tblSound && rco->tblSound->numSubentries) { + for(rcoNode=rco->tblSound->firstChild; rcoNode; rcoNode=rcoNode->next) { + if(rcoNode->srcLenUnpacked) { + uint packedLen = rco_write_resource(fTmp, rcoNode, RCO_DATA_COMPRESSION_NONE, &opts, rco); + if(!packedLen && rcoNode->labelOffset != RCO_NULL_PTR) warning("[resource] Can't write sound resource '%s'!", rco->labels + rcoNode->labelOffset); + totalPackedLen += ALIGN_TO_4(packedLen); + //if(totalPackedLen %4) totalPackedLen += 4-(totalPackedLen%4); + } + } + header.lSoundData = totalPackedLen; + } + + totalPackedLen = 0; + if(rco->tblModel && rco->tblModel->numSubentries) { + for(rcoNode=rco->tblModel->firstChild; rcoNode; rcoNode=rcoNode->next) { + uint c = ((rRCOImgModelEntry*)(rcoNode->extra))->compression; + if(opts.packModel != -1) + c = opts.packModel; + + if(rcoNode->srcLenUnpacked) { + rcoNode->srcLen = rco_write_resource(fTmp, rcoNode, c, &opts, rco); + if(!rcoNode->srcLen && rcoNode->labelOffset != RCO_NULL_PTR) warning("[resource] Can't write model resource '%s'!", rco->labels + rcoNode->labelOffset); + } + rcoNode->srcCompression = c; + rcoNode->srcAddr = totalPackedLen; + + totalPackedLen += (rcoNode->srcLen % 4 ? (rcoNode->srcLen/4)*4+4 : rcoNode->srcLen); + } + header.lModelData = totalPackedLen; + } + + rewind(fTmp); + } + + + + filewrite(rcoH.fp, &header, sizeof(header)); + + rcoH.tables = 0; + + // if compressing, write to memory + if(opts.packHeader) { + rcoH.tables = malloc(RCO_WRITE_MEM_BUFFER); + rcoH.memPos = rcoH.tablesSize = 0; + rcoH.tablesBuffered = RCO_WRITE_MEM_BUFFER; + rcoH.memOffset = ftell(rcoH.fp); + } + + rcoH.sizeImg = rcoH.sizeModel = rcoH.sizeSound = rcoH.sizeText = 0; + rcoH.longestLangData = 0; + + write_entry(&rcoH, &(rco->tblMain), 0xA4 /* typically where the main table is written to */, 0, TRUE); + + // fix up object/anim extra data + { + if(rco->tblObj) + rco_write_fix_refs(rco->tblObj, &rcoH, rco, RCO_OBJ_EXTRA_LEN, RCO_OBJ_EXTRA_LEN_NUM, TRUE); + if(rco->tblAnim) + rco_write_fix_refs(rco->tblAnim, &rcoH, rco, RCO_ANIM_EXTRA_LEN, RCO_ANIM_EXTRA_LEN_NUM, FALSE); + } + + { // write hashtable data + + /* { // special case for text hashes + if(rco->numPtrText) { + header.pTextPtrs = rcowrite_ftell(&rcoH); + for(i=0; inumPtrText; i++) { + uint32 writePtr = 0; + if(rco->ptrText[i].textEntry && rco->ptrText[i].index) + writePtr = rco->ptrText[i].textEntry->offset + sizeof(RCOEntry) + sizeof(RCOTextEntry) + (rco->ptrText[i].index - ((rRCOTextEntry*)(rco->ptrText[i].textEntry->extra))->indexes)*sizeof(RCOTextIndex); + rco_fwrite(&rcoH, &writePtr, sizeof(uint32)); + } + } + } */ + if(rco->tblText) { + header.pTextPtrs = rcowrite_ftell(&rcoH); + header.lTextPtrs = 0; + + // generate sorted list of text entries, sorted by languageID + rRCOEntry** sList = make_sorted_list_of_subentries(rco->tblText, text_hash_table_qsort); + + for(i=0; itblText->numSubentries; i++) + header.lTextPtrs += write_text_hash_table(&rcoH, sList[i], rco); + free(sList); + + header.lTextPtrs *= sizeof(uint32); + } + + if(rco->tblImage) { + header.pImgPtrs = rcowrite_ftell(&rcoH); + header.lImgPtrs = write_hash_table(&rcoH, rco->tblImage, rco) * sizeof(uint32); + } + if(rco->tblModel) { + header.pModelPtrs = rcowrite_ftell(&rcoH); + header.lModelPtrs = write_hash_table(&rcoH, rco->tblModel, rco) * sizeof(uint32); + } + if(rco->tblSound) { + header.pSoundPtrs = rcowrite_ftell(&rcoH); + header.lSoundPtrs = write_hash_table(&rcoH, rco->tblSound, rco) * sizeof(uint32); + } + if(rco->tblObj) { + header.pObjPtrs = rcowrite_ftell(&rcoH); + header.lObjPtrs = write_hash_table(&rcoH, rco->tblObj, rco) * sizeof(uint32); + } + if(rco->tblAnim) { + header.pAnimPtrs = rcowrite_ftell(&rcoH); + header.lAnimPtrs = write_hash_table(&rcoH, rco->tblAnim, rco) * sizeof(uint32); + } + /* + #define RCO_WRITERCO_WRITE_PTR_SECT(pd, pl, hp) { \ + if(pl) { \ + hp = rcowrite_ftell(&rcoH); \ + for(i=0; ioffset), sizeof(uint32)); \ + else { \ + uint32 zero = 0; \ + rco_fwrite(&rcoH, &zero, sizeof(uint32)); \ + } \ + } \ + } \ + } + //RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrText, rco->numPtrText, header.pTextPtrs); + RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrImg, rco->numPtrImg, header.pImgPtrs); + RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrModel, rco->numPtrModel, header.pModelPtrs); + RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrSound, rco->numPtrSound, header.pSoundPtrs); + RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrObj, rco->numPtrObj, header.pObjPtrs); + RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrAnim, rco->numPtrAnim, header.pAnimPtrs); + */ + } + + + { // write label/event data (and text if applicable) + + // write text (note, old behaviour - newer RCOs have text written in a different location) + if(!opts.packText && rco->tblText && rco->tblText->numSubentries) { + rRCOEntry* rcoNode; + header.pTextData = rcowrite_ftell(&rcoH); + header.lTextData = rcoH.sizeText; + for(rcoNode=rco->tblText->firstChild; rcoNode; rcoNode=rcoNode->next) { + rco_write_text_resource(&rcoH, rcoNode, RCO_DATA_COMPRESSION_NONE, &opts, ((rRCOTextEntry*)(rcoNode->extra))->lang, (rco->tblText->lastChild == rcoNode)); + } + } + + + // write label+event data + header.pLabelData = rcowrite_ftell(&rcoH); + if(rco->labelsLen) + rco_fwrite(&rcoH, rco->labels, rco->labelsLen); + header.pEventData = rcowrite_ftell(&rcoH); + if(rco->eventsLen) + rco_fwrite(&rcoH, rco->events, rco->eventsLen); + else if(rco->tblObj || rco->tblAnim) { // weird case: if there's object entries, there will be 4 bytes for events; I'll assume this covers anim as well (although there isn't an RCO with anim that doesn't have objects) + uint32 zero = 0; + rco_fwrite(&rcoH, &zero, sizeof(zero)); + header.lEventData = sizeof(zero); + } + + // the text pointer is weird in that if there's no text, it's set equal to the label pointer; even weirder, some RCOs have a null pointer (for FW5.00 all except lftv_* RCOs have null pointers for pTextData if there's no text) + // my theory: if compressing, it will be RCO_NULL_PTR, otherwise it'll = header.pLabelData + //if(!header.lTextData) header.pTextData = RCO_NULL_PTR; + //if(!header.lTextData) header.pTextData = header.pLabelData; + if(!header.lTextData) header.pTextData = (opts.packHeader ? RCO_NULL_PTR : header.pLabelData); + } + + // flush compression stuff here + HeaderComprInfo ci; + if(opts.packHeader) { + uint8* bufferOut = NULL; + ci.lenLongestText = rcoH.longestLangData; + ci.lenUnpacked = rcoH.tablesSize; + ci.lenPacked = 0; + + if(opts.packHeader == RCO_DATA_COMPRESSION_ZLIB) { + uint bound = compressBound(rcoH.tablesSize); + bufferOut = (uint8*)malloc(bound); + ci.lenPacked = zlib_compress(rcoH.tables, rcoH.tablesSize, bufferOut, bound, opts.zlibLevel, opts.zlibMethod); + } else if(opts.packHeader == RCO_DATA_COMPRESSION_RLZ) { + bufferOut = (uint8*)malloc(rcoH.tablesSize); + ci.lenPacked = rlz_compress(rcoH.tables, rcoH.tablesSize, bufferOut, rcoH.tablesSize, opts.rlzMode); + } else { + error("lulwut?"); + exit(1); + } + int comprMisalign = ci.lenPacked % 4; + uint packedLen = ci.lenPacked; + + if(rco->eSwap) es_headerComprInfo(&ci); + filewrite(rcoH.fp, &ci, sizeof(ci)); + filewrite(rcoH.fp, bufferOut, packedLen); + free(bufferOut); + + if(comprMisalign) { // 4 byte align + uint32 zero = 0; + filewrite(rcoH.fp, &zero, 4 - comprMisalign); + } + } + + // write text if packing header + if(opts.packText && rco->tblText && rco->tblText->numSubentries) { + rRCOEntry* rcoNode; + //header.pTextData = rcowrite_ftell(&rcoH); + header.pTextData = ftell(rcoH.fp); + header.lTextData = 0; // rcoH.sizeText; + for(rcoNode=rco->tblText->firstChild; rcoNode; rcoNode=rcoNode->next) { + header.lTextData += rco_write_text_resource(&rcoH, rcoNode, opts.packHeader, &opts, ((rRCOTextEntry*)(rcoNode->extra))->lang, (rco->tblText->lastChild == rcoNode)); + } + } + + // write resources + /* { + uint32 totalPackedLen = 0; + if(rco->tblImage) { + header.pImgData = rcowrite_ftell(&rcoH); + header.lImgData = rcoH.sizeImg; // TOxDO: this model actually won't work - we have to update the offsets of ALL the entries after packing... + for(i=0; itblImage->numSubentries; i++) { + uint32 packedSize = rco_write_resource(&rcoH, &(rco->tblImage->subentries[i]), RCO_DATA_COMPRESSION_NONE); // TOxDO: change this + // TOxDO: update packed size value + uint curFpos = rcowrite_ftell(rcoH.fp); + + + totalPackedLen += (packedSize % 4 ? (packedSize/4)*4+4 : packedSize); + } + header.lImgData = totalPackedLen; + } + + totalPackedLen = 0; + if(rco->tblSound) { + header.pSoundData = rcowrite_ftell(&rcoH); + header.lSoundData = rcoH.sizeSound; + for(i=0; itblSound->numSubentries; i++) { + totalPackedLen += rco_write_resource(&rcoH, &(rco->tblSound->subentries[i]), RCO_DATA_COMPRESSION_NONE); + if(totalPackedLen %4) totalPackedLen += 4-(totalPackedLen%4); + } + header.lSoundData = totalPackedLen; + } + + // TOxDO: write model resources + } */ + + if((rco->tblImage && rco->tblImage->numSubentries) + ||(rco->tblSound && rco->tblSound->numSubentries) + ||(rco->tblModel && rco->tblModel->numSubentries)) { + // update data pointers + uint32 pos = ftell(rcoH.fp); + if(rco->tblImage && rco->tblImage->numSubentries) { + header.pImgData = pos; + pos += header.lImgData; + } + if(rco->tblSound && rco->tblSound->numSubentries) { + header.pSoundData = pos; + pos += header.lSoundData; + } + if(rco->tblModel && rco->tblModel->numSubentries) { + header.pModelData = pos; + pos += header.lModelData; + } + // copy contents of fTmp across (uses a simple buffered copy) + uint len = header.lImgData + header.lSoundData + header.lModelData; + uint8 buffer[65536]; + while(len) { + uint readAmt = (len > 65536 ? 65536 : len); + fileread(fTmp, buffer, readAmt); + filewrite(rcoH.fp, buffer, readAmt); + len -= readAmt; + } + + fclose(fTmp); // this deletes our temp file + } + + + // fix header + if(rco->tblVSMX) header.pVSMXTable = rco->tblVSMX->offset; + if(rco->tblText) header.pTextTable = rco->tblText->offset; + if(rco->tblSound) header.pSoundTable = rco->tblSound->offset; + if(rco->tblModel) header.pModelTable = rco->tblModel->offset; + if(rco->tblImage) header.pImgTable = rco->tblImage->offset; + if(rco->tblFont) header.pFontTable = rco->tblFont->offset; + if(rco->tblObj) header.pObjTable = rco->tblObj->offset; + if(rco->tblAnim) header.pAnimTable = rco->tblAnim->offset; + + rewind(rcoH.fp); + if(rco->eSwap) es_rcoHeader(&header); + filewrite(rcoH.fp, &header, sizeof(header)); + + // TODO: fix resource pointers? + // TODO: tie things up etc?? + + + fclose(rcoH.fp); + + return TRUE; +} + +// returns next entry offset (like the length, but the last entry returns zero) - doesn't really have much meaning - it's primarily used for internal purposes +uint write_entry(rRCOFile_writehelper* rcoH, rRCOEntry* entry, uint parentOffset, uint prevOffset, Bool isLastSubentry) { + uint fPos = rcowrite_ftell(rcoH); + entry->offset = fPos; + RCOEntry re; + re.typeId = (entry->id << 8) | (entry->type); + re.blank = 0; + re.labelOffset = entry->labelOffset; + + if(entry->type == 0 || ((entry->id == RCO_TABLE_MAIN || entry->id == RCO_TABLE_ANIM) && entry->type == 1)) { + // a "parent" entry + re.eHeadSize = 0; + re.entrySize = sizeof(RCOEntry); + + // anim main tables have a prevOffset though :/ (object main tables don't) + if(entry->id == RCO_TABLE_ANIM) + re.prevEntryOffset = prevOffset; + else + re.prevEntryOffset = 0; + } else { + re.eHeadSize = sizeof(RCOEntry); + re.entrySize = 0; + + // not sure why, but it appears that "non-main" object/anim entries also have the total size of the entry stored in entrySize; only done if the entry has subentries + if((entry->id == RCO_TABLE_OBJ || entry->id == RCO_TABLE_ANIM) && entry->numSubentries) { + int lenNum = (entry->id == RCO_TABLE_OBJ ? RCO_OBJ_EXTRA_LEN_NUM : RCO_ANIM_EXTRA_LEN_NUM); + const int* lenArray; + lenArray = (entry->id == RCO_TABLE_OBJ ? RCO_OBJ_EXTRA_LEN : RCO_ANIM_EXTRA_LEN); + if(entry->type <= lenNum && lenArray[entry->type] > 0) + re.entrySize = sizeof(RCOEntry) + lenArray[entry->type] *sizeof(uint32); + } + + re.prevEntryOffset = prevOffset; + } + re.numSubentries = entry->numSubentries; + re.parentTblOffset = fPos - parentOffset; + re.blanks[0] = re.blanks[1] = 0; + re.nextEntryOffset = 0; + + if(entry->rco->eSwap) es_rcoEntry(&re); + rco_fwrite(rcoH, &re, sizeof(re)); + if(entry->rco->eSwap) es_rcoEntry(&re); + + // extra items here + switch(entry->id) { + case RCO_TABLE_VSMX: + if(entry->type == 1) { + RCOVSMXEntry rve; + rve.offset = 0; + rve.length = entry->srcLenUnpacked; + if(entry->rco->eSwap) es_rcoVsmxEntry(&rve); + rco_fwrite(rcoH, &rve, sizeof(rve)); + + uint vsmxLen=0; + uint8* bufferVSMX = (uint8*)read_resource(entry, &vsmxLen); + if(bufferVSMX) { + if(vsmxLen == entry->srcLenUnpacked) + rco_fwrite(rcoH, bufferVSMX, vsmxLen); + free(bufferVSMX); + } + } + break; + case RCO_TABLE_TEXT: + if(entry->type == 1) { + RCOTextEntry rte; + rRCOTextEntry* src = (rRCOTextEntry*)entry->extra; + uint i; + rte.lang = src->lang; + rte.format = src->format; + rte.numIndexes = src->numIndexes; + if(entry->rco->eSwap) es_rcoTextEntry(&rte); + rco_fwrite(rcoH, &rte, sizeof(rte)); + + // instead of blindly dumping src->indexes, we'll "pack" the entries together (allows source file to be of a different format, ie INI) + uint32 entryTextOffset = rcoH->sizeText; + for(i=0; inumIndexes; i++) { + RCOTextIndex rti; + rti.labelOffset = src->indexes[i].labelOffset; + rti.length = src->indexes[i].length; + // Sony is doing some weird stuff :| + if(src->indexes[i].offset == RCO_NULL_PTR) + rti.offset = RCO_NULL_PTR; + else if(rti.length) { + if(rcoH->tables) // compressing - we need to make the offset relative to the section of text data + rti.offset = rcoH->sizeText - entryTextOffset; + else + rti.offset = rcoH->sizeText; + } + else // TODO: experimental (it seems that Sony likes sticking in a weird pointer for a blank text entry) + rti.offset = entryTextOffset -1; + + rcoH->sizeText += rti.length; // doesn't have trailing null, so no +1 needed + // align to 4 byte boundary + rcoH->sizeText = ALIGN_TO_4(rcoH->sizeText); + + if(entry->rco->eSwap) es_rcoTextIndex(&rti); + rco_fwrite(rcoH, &rti, sizeof(rti)); + } + + if(rcoH->sizeText - entryTextOffset > rcoH->longestLangData) + rcoH->longestLangData = rcoH->sizeText - entryTextOffset; + } + break; + case RCO_TABLE_IMG: case RCO_TABLE_MODEL: + if(entry->type == 1) { + rRCOImgModelEntry* srcExtra = (rRCOImgModelEntry*)entry->extra; + RCOImgModelEntry rie; + uint32* totalResSize = (entry->id == RCO_TABLE_IMG ? &(rcoH->sizeImg) : &(rcoH->sizeModel)); + rie.format = srcExtra->format; + // we've already got the compression info done here, so just copy it over + rie.compression = entry->srcCompression | (srcExtra->unkCompr << 8); + rie.offset = *totalResSize; + rie.sizeUnpacked = entry->srcLenUnpacked; + rie.sizePacked = entry->srcLen; + *totalResSize += rie.sizePacked; + // align to 4 byte boundary + *totalResSize = ALIGN_TO_4(*totalResSize); + + if(entry->rco->eSwap) es_rcoImgModelEntry(&rie); + + // we'll omit the packed length value if this is an uncompressed entry + if(entry->rco->ps3) { // PS3 image quirk + uint32 one = ENDIAN_SWAP(1); + rco_fwrite(rcoH, &rie, sizeof(rie) - sizeof(uint32)); + rco_fwrite(rcoH, &one, sizeof(one)); + if(entry->srcCompression != RCO_DATA_COMPRESSION_NONE) + rco_fwrite(rcoH, &rie.sizeUnpacked, sizeof(rie.sizeUnpacked)); + } else { + rco_fwrite(rcoH, &rie, sizeof(rie) - (entry->srcCompression == RCO_DATA_COMPRESSION_NONE ? sizeof(uint32) : 0)); + } + } + break; + case RCO_TABLE_SOUND: + if(entry->type != 0) { + rRCOSoundEntry* srcExtra = (rRCOSoundEntry*)entry->extra; + RCOSoundEntry rse; + uint rseOffset; + rse.format = srcExtra->format; + rseOffset = rse.offset = rcoH->sizeSound; + rse.channels = srcExtra->channels; + rse.sizeTotal = entry->srcLenUnpacked; + + rcoH->sizeSound += rse.sizeTotal; + // align to 4 byte boundary + rcoH->sizeSound = ALIGN_TO_4(rcoH->sizeSound); + + if(entry->rco->eSwap) es_rcoSoundEntry(&rse); + rco_fwrite(rcoH, &rse, sizeof(rse)); + + // write size/offset pairs + uint i; + // TODO: might actually restrict this to two channels later on + for(i=0; ichannels; i++) { + uint32 stuffToWrite[] = {srcExtra->channelData[i*2], srcExtra->channelData[i*2+1] + rseOffset}; + if(entry->rco->eSwap) { + stuffToWrite[0] = ENDIAN_SWAP(stuffToWrite[0]); + stuffToWrite[1] = ENDIAN_SWAP(stuffToWrite[1]); + } + rco_fwrite(rcoH, stuffToWrite, sizeof(stuffToWrite)); + } + if(srcExtra->channels < 2) { + // we'll write an extra blank channel, complying with how Sony's RCO tools work + uint32 stuffToWrite[] = {0, RCO_NULL_PTR}; + uint i; + // actually, the following is unnecessary, but we'll keep it here for reference sake + if(entry->rco->eSwap) { + stuffToWrite[0] = ENDIAN_SWAP(stuffToWrite[0]); + stuffToWrite[1] = ENDIAN_SWAP(stuffToWrite[1]); + } + for(i=srcExtra->channels; i<2; i++) + rco_fwrite(rcoH, &stuffToWrite, sizeof(uint32)*2); + } + } + break; + case RCO_TABLE_FONT: + if(entry->type == 1) { + RCOFontEntry rfe; + rRCOFontEntry* srcExtra = (rRCOFontEntry*)entry->extra; + rfe.format = srcExtra->format; + rfe.compression = srcExtra->compression; + rfe.unknown = srcExtra->unknown; + rfe.unknown2 = srcExtra->unknown2; + if(entry->rco->eSwap) es_rcoFontEntry(&rfe); + rco_fwrite(rcoH, &rfe, sizeof(rfe)); + } + break; + case RCO_TABLE_OBJ: case RCO_TABLE_ANIM: + if(entry->type != 0) { + int lenNum; + lenNum = (entry->id == RCO_TABLE_OBJ ? RCO_OBJ_EXTRA_LEN_NUM : RCO_ANIM_EXTRA_LEN_NUM); + const int* lenArray; + lenArray = (entry->id == RCO_TABLE_OBJ ? RCO_OBJ_EXTRA_LEN : RCO_ANIM_EXTRA_LEN); + // just allocate space because we need to fix this later on + if(entry->type <= lenNum && lenArray[entry->type] > 0) { + uint32 anAwesomeVariable = lenArray[entry->type]; + while(anAwesomeVariable--) + rco_fwrite(rcoH, &anAwesomeVariable, sizeof(anAwesomeVariable)); + } + else { + // TODO: do something if type is unknown + } + } + break; + } + + // subentries + uint nextOffset = 0; + rRCOEntry* rcoNode; + for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) { + nextOffset = write_entry(rcoH, rcoNode, fPos, nextOffset, (rcoNode->next ? FALSE:TRUE)); + } + + + // write nextEntryOffset + if(!isLastSubentry) { + uint curPos = rcowrite_ftell(rcoH); + re.nextEntryOffset = curPos - fPos; + rcowrite_fseek(rcoH, fPos); + if(entry->rco->eSwap) es_rcoEntry(&re); + rco_fwrite(rcoH, &re, sizeof(re)); + if(entry->rco->eSwap) es_rcoEntry(&re); + rcowrite_fseek(rcoH, curPos); + } + return re.nextEntryOffset; +} + + +void rco_fwrite(rRCOFile_writehelper* rcoH, void* buffer, uint len) { + // memory writing + if(rcoH->tables) { + uint len4 = ALIGN_TO_4(len); + if(rcoH->memPos + len4 > rcoH->tablesBuffered) { + rcoH->tablesBuffered = rcoH->memPos + len4 + RCO_WRITE_MEM_BUFFER; + rcoH->tables = realloc(rcoH->tables, rcoH->tablesBuffered); + } + + memcpy((uint8*)rcoH->tables + rcoH->memPos, buffer, len); + if(len % 4) { + memset((uint8*)rcoH->tables + rcoH->memPos + len, 0, 4-(len%4)); + } + rcoH->memPos += len4; + if(rcoH->memPos > rcoH->tablesSize) + rcoH->tablesSize = rcoH->memPos; + + } else { + filewrite(rcoH->fp, buffer, len); + + // always add 4 byte padding (should add an argument, but will always be TRUE anyway, so I cbf) + if(len % 4) { + uint32 zero=0; + filewrite(rcoH->fp, &zero, 4-(len%4)); + } + } +} +uint rcowrite_ftell(rRCOFile_writehelper* rcoH) { + // memory stuff + if(rcoH->tables) return rcoH->memPos + rcoH->memOffset; + else return ftell(rcoH->fp); +} +void rcowrite_fseek(rRCOFile_writehelper* rcoH, uint pos) { + // memory stuff + if(rcoH->tables) rcoH->memPos = pos - rcoH->memOffset; + else fseek(rcoH->fp, pos, SEEK_SET); +} + +// returns the length of the packed data +// TBH, I really can't be stuffed writing a buffered copy/compress/decompressor (and anyway, it may not work with future compression routines, so meh) +uint rco_write_resource(FILE* dest, rRCOEntry* entry, uint destCompression, writerco_options* opts, rRCOFile* rco) { + + uint len=0; + uint8* bufferMid = (uint8*)read_resource(entry, &len); + if(!bufferMid) { + if(entry->labelOffset) + warning("Failed to read resource '%s'.", rco->labels + entry->labelOffset); + return 0; + } + + if(len != entry->srcLenUnpacked) { + free(bufferMid); + return 0; + } + + uint8* bufferOut; + uint packedSize = 0; + if(destCompression == RCO_DATA_COMPRESSION_ZLIB) { + uint compSize = compressBound(entry->srcLenUnpacked); + bufferOut = (uint8*)malloc(compSize); + packedSize = zlib_compress(bufferMid, entry->srcLenUnpacked, bufferOut, compSize, opts->zlibLevel, opts->zlibMethod); + if(!packedSize) { + if(entry->labelOffset) + warning("Failed to compress resource '%s'.", rco->labels + entry->labelOffset); + return 0; + } + + free(bufferMid); + } else if(destCompression == RCO_DATA_COMPRESSION_RLZ) { + #ifdef DISABLE_RLZ + error("RLZ compression not supported."); + exit(1); + #endif + bufferOut = (uint8*)malloc(entry->srcLenUnpacked); + packedSize = rlz_compress(bufferMid, entry->srcLenUnpacked, bufferOut, entry->srcLenUnpacked, opts->rlzMode); + if(!packedSize) { + if(entry->labelOffset) + warning("Failed to compress resource '%s'.", rco->labels + entry->labelOffset); + return 0; + } + + free(bufferMid); + } else { + bufferOut = bufferMid; + packedSize = entry->srcLenUnpacked; + } + + filewrite(dest, bufferOut, packedSize); + free(bufferOut); + + /* + char buffer[65536], outBuffer[65536]; + uint len=entry->srcLen; + z_stream out; + if(destStream == RCO_DATA_COMPRESSION_ZLIB) { + ZLIB_INIT_DEFLATE(out, 9, Z_DEFAULT_STRATEGY); + out.next_in = buffer; + out.avail_in = 65536; + out.next_out = outBuffer; + out.avail_out = 65536; + } + + // copy with buffer + while(len) { + uint readAmt = (len < 65536 ? len : 65536); + if(!fileread(src, &buffer, readAmt)) { + fclose(src); + return FALSE; + } + + if(destCompression == RCO_DATA_COMPRESSION_NONE) + filewrite(dest, &buffer, readAmt); + else if(destCompression == RCO_DATA_COMPRESSION_ZLIB) { + + } + + len -= readAmt; + } + + // TOxDO: also don't forget to check lenUnpacked + + + fclose(src); + + + if(destStream == RCO_DATA_COMPRESSION_ZLIB) + deflateEnd(&out); + */ + + // 4byte alignment + if(packedSize % 4) { + uint32 zero = 0; + filewrite(dest, &zero, 4 - (packedSize % 4)); + } + + return packedSize; +} + +uint rco_write_text_resource(rRCOFile_writehelper* rcoH, rRCOEntry* entry, uint destCompression, writerco_options* opts, uint lang, Bool isLast) { + + uint len=0; + uint8* bufferMid = (uint8*)read_resource(entry, &len); + if(!bufferMid) return 0; + + if(len != entry->srcLenUnpacked) { + free(bufferMid); + return 0; + } + + rRCOTextEntry* extra = (rRCOTextEntry*)(entry->extra); + + + TextComprInfo tci; + char* textBuffer = NULL; + uint textBufferPos = 0; + if(destCompression) { + tci.lang = lang; + tci.unknown = 1; + tci.unpackedLen = 0; + //textBuffer = (char*)malloc(1); + } + + uint i; + for(i=0; inumIndexes; i++) { + uint len = extra->indexes[i].length; + if(!len) continue; + + if(destCompression) { + tci.unpackedLen += ALIGN_TO_4(len); + textBuffer = (char*)realloc(textBuffer, tci.unpackedLen); + memcpy(textBuffer + textBufferPos, bufferMid + extra->indexes[i].offset, len); + + if(len % 4) { + memset(textBuffer + textBufferPos + len, 0, 4-(len%4)); + } + textBufferPos += ALIGN_TO_4(len); + } + else { + rco_fwrite(rcoH, bufferMid + extra->indexes[i].offset, len); + } + } + + free(bufferMid); + + if(destCompression) { + Bool success = TRUE; + uint8* bufferOut = NULL; + + if(destCompression == RCO_DATA_COMPRESSION_ZLIB) { + uint compSize = compressBound(tci.unpackedLen); + bufferOut = (uint8*)malloc(compSize); + tci.packedLen = zlib_compress(textBuffer, tci.unpackedLen, bufferOut, compSize, opts->zlibLevel, opts->zlibMethod); + } else if(destCompression == RCO_DATA_COMPRESSION_RLZ) { + #ifdef DISABLE_RLZ + error("RLZ compression not supported."); + exit(1); + #endif + bufferOut = (uint8*)malloc(tci.unpackedLen); + tci.packedLen = rlz_compress(textBuffer, tci.unpackedLen, bufferOut, tci.unpackedLen, opts->rlzMode); + } + + if(!tci.packedLen) { // compression failed + free(bufferOut); + bufferOut = NULL; + success = FALSE; + } + else if(bufferOut) { + if(isLast) + tci.nextOffset = 0; + else { + tci.nextOffset = sizeof(tci) + tci.packedLen; + tci.nextOffset = ALIGN_TO_4(tci.nextOffset); + } + if(entry->rco->eSwap) es_textComprInfo(&tci); + filewrite(rcoH->fp, &tci, sizeof(tci)); + if(entry->rco->eSwap) es_textComprInfo(&tci); + + filewrite(rcoH->fp, bufferOut, tci.packedLen); + free(bufferOut); + + if(tci.packedLen % 4) { + uint32 zero = 0; + filewrite(rcoH->fp, &zero, 4 - (tci.packedLen % 4)); + } + } else + success = FALSE; + + free(textBuffer); + if(!success) return 0; + return ALIGN_TO_4(tci.packedLen) + sizeof(tci); + } + + return 1; +} + +void rco_write_fix_refs(rRCOEntry* parent, rRCOFile_writehelper* rcoH, rRCOFile* rco, const int* lenArray, const int lenNum, Bool isObj) { + rRCOEntry* rcoNode; + for(rcoNode=parent->firstChild; rcoNode; rcoNode=rcoNode->next) { + rcowrite_fseek(rcoH, rcoNode->offset + sizeof(RCOEntry)); + uint j, j2; + uint8* extraPtr = (uint8*)rcoNode->extra; + if(rcoNode->type <= lenNum && lenArray[rcoNode->type] > 0) { + const int* typeArray = (isObj ? RCO_OBJ_EXTRA_TYPES[rcoNode->type] : RCO_ANIM_EXTRA_TYPES[rcoNode->type]); + for(j=0, j2=0; (int)jtype]; j++, j2++) { + Bool cond; + if(isObj) + cond = (RCO_OBJ_IS_REF(rcoNode->type, j2)); + else // anim + cond = (RCO_ANIM_IS_REF(rcoNode->type, j2)); + //cond = (RCO_ANIM_EXTRA_REFS[rcoNode->type] && j == 0); + if(cond) { + rRCORef* srcRef = (rRCORef*)extraPtr; + RCOReference destRef; + destRef.type = srcRef->type; + + switch(destRef.type) { + case RCO_REF_EVENT: + //destRef.ptr = ((char*)(srcRef->ptr)) - rco->events; + destRef.ptr = srcRef->rawPtr; + break; + case RCO_REF_NONE: + destRef.ptr = RCO_NULL_PTR; + break; + case RCO_REF_TEXT: + destRef.ptr = srcRef->rawPtr; + break; + case RCO_REF_IMG: case RCO_REF_MODEL: case RCO_REF_FONT: case RCO_REF_OBJ2: case RCO_REF_ANIM: case RCO_REF_OBJ: + if(srcRef->ptr) + destRef.ptr = ((rRCOEntry*)(srcRef->ptr))->offset; + break; + default: + destRef.ptr = srcRef->rawPtr; + } + + if(rco->eSwap) { + #define ENDIAN_SWAP_HALF32(x) (((x) & 0xFF) << 8 | ((x) & 0xFF00) >> 8 | ((x) & 0xFF0000) << 8 | ((x) & 0xFF000000) >> 8) + destRef.type = ENDIAN_SWAP_HALF32(destRef.type); + destRef.ptr = ENDIAN_SWAP(destRef.ptr); + } + rco_fwrite(rcoH, &destRef, sizeof(destRef)); + + extraPtr += sizeof(rRCORef); + j++; + //if(!isObj) j2--; + } else { + if(rco->eSwap && typeArray[j2] != RCO_OBJ_EXTRA_TYPE_UNK) { + uint32 val = *(uint32*)extraPtr; + val = ENDIAN_SWAP(val); + rco_fwrite(rcoH, &val, sizeof(val)); + } else + rco_fwrite(rcoH, extraPtr, sizeof(uint32)); + extraPtr += sizeof(uint32); + } + } + } + else { + // TODO: think up something for unknown object types + } + rco_write_fix_refs(rcoNode, rcoH, rco, lenArray, lenNum, isObj); + } +} + +// returns size of hash table +uint write_hash_table(rRCOFile_writehelper* rcoH, rRCOEntry* entry, rRCOFile* rco) { + uint num = entry->numSubentries; + if(entry->id == RCO_TABLE_OBJ) + num = count_all_subentries(entry); + //if(!rco->ps3) + // interestingly, it seems that some PS3 RCOs do not round to an upper prime, but most do + num = find_larger_prime(num); + + if(!num) return 0; + + uint32* hashTable = (uint32*)malloc(num * sizeof(uint32)); + memset(hashTable, 0, num * sizeof(uint32)); + rRCOEntry* rcoNode; + for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) + do_hashing(rcoNode, rco, (entry->id == RCO_TABLE_OBJ), hashTable, num); + + // write it + rco_fwrite(rcoH, hashTable, num*sizeof(uint32)); + + free(hashTable); + return num; +} + +uint write_text_hash_table(rRCOFile_writehelper* rcoH, rRCOEntry* entry, rRCOFile* rco) { + uint num = ((rRCOTextEntry*)entry->extra)->numIndexes; + if(!num) return 0; + + uint32* hashTable = (uint32*)malloc(num * sizeof(uint32)); + memset(hashTable, 0, num * sizeof(uint32)); + uint i; + for(i=0; iextra)->indexes[i]); + if(ti->labelOffset != RCO_NULL_PTR) { + uint32* hashPtr = &hashTable[calc_hash(rco->labels + ti->labelOffset, hashTable, num)]; + *hashPtr = entry->offset + sizeof(RCOEntry) + sizeof(RCOTextEntry) + i*sizeof(RCOTextIndex); + if(rco->eSwap) *hashPtr = ENDIAN_SWAP(*hashPtr); + } + } + + // write it + rco_fwrite(rcoH, hashTable, num*sizeof(uint32)); + + free(hashTable); + return num; +} + +void do_hashing(rRCOEntry* entry, rRCOFile* rco, Bool recurse, uint32* hashTable, uint hashTableSize) { + if(entry->labelOffset != RCO_NULL_PTR) { + uint32* hashPtr = &hashTable[calc_hash(rco->labels + entry->labelOffset, hashTable, hashTableSize)]; + *hashPtr = entry->offset; + if(rco->eSwap) *hashPtr = ENDIAN_SWAP(*hashPtr); + } + if(recurse) { + rRCOEntry* rcoNode; + for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) + do_hashing(rcoNode, rco, recurse, hashTable, hashTableSize); + } +} + +uint calc_hash(char* in, uint32* hashTable, uint hashTableSize) { + uint32 hash = 0; + // calculate hash code (just a summation of the chars) + while(*in) { + hash += *in; + in++; + } + // hash compression + hash %= hashTableSize; + + // linear probing + while(hashTable[hash]) { + hash++; + if(hash >= hashTableSize) hash = 0; + } + + return hash; +} + +int text_hash_table_qsort(const rRCOEntry** a, const rRCOEntry** b) { + return ((rRCOTextEntry*)((*a)->extra))->lang - ((rRCOTextEntry*)((*b)->extra))->lang; +} diff --git a/rlzpack.c b/rlzpack.c index d428439..5e6db16 100644 --- a/rlzpack.c +++ b/rlzpack.c @@ -1,545 +1,545 @@ - -#include -#include -#include -#include -#include - -#define RLZI_WINDOW_SIZE 0x7FED // *4 = 130,996 bytes (128KB - 76 bytes) -#define RLZI_DICTIONARY_SIZE 0x3FFFF // *4 = 1,048,572 bytes (1MB - 4 bytes) - -#define ABS_INPUT_POS ((unsigned int)rlzi->input + rlzi->inputpos) - -/** Function Prototypes **/ -int rlzcompress(void *output, int inlen, void *input, unsigned char mode); -//void sub_405810(int arg_CB8, int64_t arg_CB0); -void flush_output(void); -void write_output(void); -void write_bit(unsigned char* unk0ptr, unsigned char arg_flag); -void write_special(unsigned char* unk0ptr, int num, int arg_edx); -int write_match_len(unsigned char len, unsigned int arg_4); -int find_match(int *arg_0, unsigned char realMode); - - -#define log2(x) (log((long double)(x)) / (long double)0.30102999566398119521373889472449) - -typedef struct { // unk_421D80 - unsigned char literals[8 * 0xFF]; //grouped in 8 groups of 0xFF; group selected by has of input position, last byte and mode, the 0xFF part is based on the input byte with various shifts - unsigned char distDescLong[0xF0]; // 30x8 (match >= 4) - unsigned char distDescShort[0x40]; // 8x8 (match < 4) - unsigned char distances[12 * 0x20]; // grouped in 0x20 based on bit position - unsigned char lenDesc[0x40]; // 8 x 8; group selected by bit position, position is var_C from main function - unsigned char matchlens[0xFF]; - - void* input; // 0xCA8 - void* output; // 0xCAC - //long dword_CB0; // 0xCB0 (0x422A30) - only wrtten to in sub_405810 - //long dword_CB4; // 0xCB4 - int64_t rangeOffset; // 0xCB0 (0x422A30) - only written to in sub_405810(), affected by write_bit and write_match_len - // - appears to be a data queue - top 24 bits not used, next 8 bits->data to be written, next 8 bits->data mask; affected by CB8 in some way (CB8 is added to this) - long rangeSize; // 0xCB8 - char nextOutputByte; // 0xCBC - some byte offset; 3rd byte in CB0, only ever used in sub_405810; guaranteed never to == 0xFF - char bytesToWrite; // 0xCBD (0x422A3D) - only used in sub_405810 - char writeLock; // 0xCBE - only ever used as a switch - char lastSearchBack; // 0xCBF - some byte flag used in find_match() - unsigned long outputpos;// 0xCC0 (0x422A40) - //long unused_CC4; // 0xCC4 - align 8? - always 0 in program - unsigned long inputpos; // 0xCC8 (0x422A48) - unsigned long inputlen; // 0xCCC (0x422A4C) - - // window stores positions of stuff; the index itself is based on hashed data - long window[RLZI_WINDOW_SIZE]; // 0xCD0-0x20C84 - // dictionary stores same stuff as window, but indexed on positions - long dictionary[RLZI_DICTIONARY_SIZE]; // 1MB - dictionary?? - //long unused_120C80; - - float sgl_120C84; - float sgl_120C88; // starts as 0.0625 and increments by that amount every time the dictionary coder fails to pack stuff -} RLZ_Info; // about 1.128MB -RLZ_Info rlzip; -RLZ_Info* rlzi = &rlzip; - -/** Functions **/ -// sub_406070 -int rlzcompress(void *output, int inlen, void *input, unsigned char mode) -{ - if(!inlen) return -1; // sanity check - - /** ABOUT THE MODE ARGUMENT - * The mode argument contains two values broken up like (___|45|678) - * Bits 678 are the compression mode - * Bits 45 determine the default values to store in literals/distances etc array - * The first 3 bits aren't used - */ - int var_C = 0; // var_C appears to do some tracking in regards to how well the dictionary coder is packing things? - char realMode; - int dictSearchBack = 0; // just assign something in case... :S - - // variables introduced by me - unsigned int i; - unsigned int matchLen = 0; - //unsigned int iInputPosTemp; - - realMode = mode & 7; // grab the compression mode - - // originally init_rlzi() - rlzi->input = input; - rlzi->inputlen = inlen; - rlzi->output = output; - rlzi->rangeOffset = 0; - rlzi->rangeSize = -1; - rlzi->nextOutputByte = 0; - rlzi->bytesToWrite = 0; - // no writeLock=0 - writeLock is _always_ assigned before calling the write function anyway - rlzi->lastSearchBack = 0; - rlzi->outputpos = 0; - rlzi->inputpos = 0; //added by me - - rlzi->sgl_120C84 = 0; - rlzi->sgl_120C88 = 0.0625; - memset(rlzi->window, 0xFF, sizeof(rlzi->window)); - - - - // grab the 2nd number from the mode - //eax = (4 - ((mode >> 3) & 0x03)) << 5; // 12345678 -> 45 - /* ^ 4 possible combinations for the above - * if bits45 == 00 (should be) then eax => 0x80 (10000000) - * if bits45 == 01 then eax => 0x60 (01100000) - * if bits45 == 10 then eax => 0x40 (01000000) - * if bits45 == 11 then eax => 0x20 (00100000) - */ - { - unsigned char byteSet = ((4 - ((mode >> 3) & 0x03)) << 5); - memset(rlzi->literals, byteSet, sizeof(rlzi->literals)); - memset(rlzi->distDescLong, byteSet, sizeof(rlzi->distDescLong)); - memset(rlzi->distDescShort, byteSet, sizeof(rlzi->distDescShort)); - memset(rlzi->distances, byteSet, sizeof(rlzi->distances)); - memset(rlzi->lenDesc, byteSet, sizeof(rlzi->lenDesc)); - memset(rlzi->matchlens, byteSet, sizeof(rlzi->matchlens)); - } - - unsigned char lastInputByte = 0; - - // main loop - this will loop until all the data is processed - while(rlzi->inputpos < rlzi->inputlen) //loc_4060D0 - { - matchLen = find_match(&dictSearchBack, realMode); - if(matchLen < 2) matchLen = 1; - int sub_405B90_ret = write_match_len(matchLen -1, var_C); - if(matchLen == 1) // no match found - { - // write literal - // the following code was previously sub_405A10( (((((unsigned int)rlzi->inputpos & 7) << 8) | lastInputByte) >> realMode) & 7 ) - int arg_ecx = (( - (((unsigned int)rlzi->inputpos & 7) << 8) | lastInputByte - ) >> realMode) & 7; // top bit in (rlzi->inputpos & 7) will actually be discarded - that is, (rlzi->inputpos & 3) should give the same result - int oldRangeSize = rlzi->rangeSize; - unsigned int oldOutputpos = rlzi->outputpos; - unsigned int inputVal = *(unsigned char*)ABS_INPUT_POS + 0x100; - for(; !(inputVal & 0x10000); inputVal <<= 1) // loop 8 times - write_bit( - (unsigned char*)((unsigned int)rlzi->literals + arg_ecx*0xFF + (inputVal >> 8)-1), - (inputVal >> 7) & 1 - ); // last arg (now deleted) is actually ebx, but ebx is 3 at this time - - // these values are only ever used if realMode==7, and only in find_match() function - #define scast_405A10(v) ((long double)((unsigned int)(v))) - // (int) cast added to make it consistent with good rlz packer, but unsure if this is correct... - rlzi->sgl_120C84 += (float)(log2(scast_405A10(oldRangeSize) / scast_405A10(rlzi->rangeSize)) + (int)((rlzi->outputpos - oldOutputpos - 1)*8)); - rlzi->sgl_120C88 += 0.0625f; //flt_41A3AC - - if(var_C > 0) var_C--; - } - else - { - // write distance instead of literal - // originally sub_405C10(dictSearchBack, sub_405B90(matchLen -1, var_C), matchLen) - int searchBackPlus1 = dictSearchBack +1; - unsigned char* treeStore; - //int o_ebx=ebx, o_ebp=ebp, o_esi=esi, o_edi=edi; - int sbSignifBit = 0; - int var_edi; - i = 0; - if((searchBackPlus1 & 0xFFFFFFFE) > 0) // if searchBackPlus1 has bits other than the first set... (that is, searchBackPlus1 != 0 && searchBackPlus1 != 1) - { // this condition _should_ always be true, as min dictSearchBack is 1, and with +1, min value for searchBackPlus1 is 2 - // find the position of the most significant "1" - for(i=2; (searchBackPlus1 >> i) > 0; i++); - sbSignifBit = --i; // put the bit position in ebp - } - - if(matchLen < 4) { - var_edi = 4; - treeStore = rlzi->distDescShort; - } else { - var_edi = 16; - treeStore = rlzi->distDescLong; - sbSignifBit += 0xC; - } - - int var_ebx = 1; - int var_esi; - do { // write packed thing - perhaps a length of next number descriptor? - var_esi = ((sbSignifBit & var_edi) > 0 ? 1 : 0); // check if certain bit in ebp is set - write_bit((unsigned char*)((unsigned int)&treeStore[var_ebx*8] + sub_405B90_ret), var_esi); - var_edi >>= 1; - var_ebx = var_esi+var_ebx*2; - } while(var_edi > 0); // loop 3 or 5 times - - if(i > 0) // should always be true - { - // write distance - i--; - write_special(rlzi->distances + i*0x20, searchBackPlus1, i); - } - - var_C = 7 - ((rlzi->inputpos + matchLen) & 1); - } - - unsigned int bytesLeftMinus3 = rlzi->inputlen - rlzi->inputpos - 3; - if(matchLen < bytesLeftMinus3) - bytesLeftMinus3 = matchLen; - for(i=0; idictionary[(rlzi->inputpos+i) & RLZI_DICTIONARY_SIZE] = rlzi->window[brHash]; - rlzi->window[brHash] = rlzi->inputpos+i; - } - - rlzi->inputpos += matchLen; - lastInputByte = *(unsigned char*)(ABS_INPUT_POS-1); - if(rlzi->outputpos == rlzi->inputlen) // is the compressed output larger than the original stream? - return -1; - } - - write_match_len(0xFF, var_C); // end marker or is this used for flushing data? - - { // originally sub_4058E0() - i = (rlzi->writeLock ? 5 : 4); - //rlzi->writeLock = 0; - while(i--) - flush_output(); - } - - // stick the mode into the first byte of the output - // (otherwise, first byte is guaranteed to be 0) - *(unsigned char*)output = mode; - - return rlzi->outputpos; // length of compressed data -} - -// calls write_bit, write_special -// function handles some packing routines -int write_match_len(unsigned char len, unsigned int arg_4) // arg_4 -> something that keeps track of no. past blocks packed with find_match() (always var_C from rlzcompress()) -{ - // len is the length -1 (amount of data removed by dictionary coder), except when 0xFF (when called after loop has finished) - int i; - char flagTemp; - // determine how packed the data is - ie, 1 bytes removed? 2 bytes?, 4?, 8? ... - // or perhaps, this is actually outputting the length code! - for(i=0; i<8; i++) - { - flagTemp = (len >= (1u << i) ? 1 : 0); - write_bit((unsigned char*)((unsigned int)&rlzi->lenDesc[i*8] + arg_4), flagTemp); - if(flagTemp == 0) break; - } - i--; - if(i > 0) // --> identical to if(len != 0); this will only be true if len >= 1 (or len<0) - eg if data was packed - { - i--; - unsigned char* ptr = rlzi->matchlens + ((arg_4 & 7)+i*8)*4; - write_special(ptr + ((rlzi->inputpos << i) & 3), len, i); - i++; - } - return i; // returns packing threshold? -} - -// calls sub_405810 -void write_bit(unsigned char* unk0ptr, unsigned char arg_flag) -{ - unsigned int range = (unsigned int)rlzi->rangeSize; - - unsigned int pMid = (range >> 8) * (*unk0ptr); - if(!arg_flag) - { - rlzi->rangeOffset += pMid; - range -= pMid; - } - else - { - range = pMid; - *unk0ptr += 0xFF; // increase probability - } - *unk0ptr -= ((unsigned int)(*unk0ptr) >> 3); - rlzi->rangeSize = (long)range; - write_output(); -} - -// calls write_bit (used to)sub_4059B0 -// arg_4, always either rlzi->distances or rlzi->matchlens -// inputpos: either rlzi->inputpos or 0 -// arg_edx is likely to be a 8bit value; something to do with bit position in distance -// distance: but can be "len" when called from write_match_len -void write_special(unsigned char* unk0ptr, int num, int arg_edx) -//void write_special(int arg_4, int distance, int inputpos, int arg_ecx, int arg_edx) -{ - if(arg_edx > 255) - printf("ASSUMPTION FAILURE\n"); - //int var_ebx = ((inputpos << (arg_edx & 0xFF)) & 3) + arg_4 + ((arg_ecx & 7)+arg_edx*8)*4; - if(arg_edx>=3) - { - write_bit(unk0ptr, (num >> arg_edx) & 1); - arg_edx--; - if(arg_edx>=3) - { - write_bit(unk0ptr, (num >> arg_edx) & 1); - arg_edx--; - } - unk0ptr++; - if(arg_edx>=3) - { - // previously sub_4059B0() - unsigned int rangeTmp = (unsigned int)rlzi->rangeSize; - arg_edx -= 2; - while(arg_edx--) { - rangeTmp >>= 1; - if(((num >> (3 + arg_edx)) & 1) == 0) - { - rlzi->rangeOffset += rangeTmp; - } - } - arg_edx += 3; // 2 (reverse previous subtraction), and +1 for the above loop - rlzi->rangeSize = (long)rangeTmp; - write_output(); - } - } - do { - write_bit(unk0ptr, (num >> arg_edx) & 1); - unk0ptr++; - arg_edx--; - } while(arg_edx >= 0); -} - - -void flush_output(void) -{ - // if byte CB3 is NOT 0xFF, OR dword CB4 is true - // -- which means it can only be false if rlzi->rangeOffset == 0x00000000FF?????? - if((rlzi->rangeOffset >> 0x18) != 0xFF) - { - unsigned char byteOut = rlzi->nextOutputByte; // starts off as 0, then becomes 3rd byte of rlzi->rangeOffset - rlzi->nextOutputByte = (rlzi->rangeOffset >> 0x18) & 0xFF; - do { - if(rlzi->outputpos == rlzi->inputlen) - return; - *(unsigned char*)((unsigned int)rlzi->output + rlzi->outputpos) = ((rlzi->rangeOffset >> 32) & 0xFF) + byteOut; - rlzi->bytesToWrite--; - rlzi->outputpos++; - byteOut = 0xFF; - } while(rlzi->bytesToWrite >= 0); - } - - //loc_4058A4: - rlzi->bytesToWrite++; - //rlzi->rangeOffset = (arg_CB0 << 8) & 0xFFFFFFFF; // make sure high 32 bits are 0 - //rlzi->rangeSize = arg_CB8 << 8; - rlzi->rangeOffset <<= 8; - rlzi->rangeOffset &= 0xFFFFFFFF; // make sure high 32 bits are 0 - rlzi->rangeSize <<= 8; -} - -void write_output(void) -{ - rlzi->writeLock = ((rlzi->rangeSize >> 0x18) & 0xFF ? 1:0); - if(!rlzi->writeLock) { - flush_output(); - } -} - - -// sub_405CE0: arg_0 = buf -// this function determines how much data we can pack into a single byte -// return of 1 = can't compress; don't think this can return 0xFF or higher -// NEW: this function searches for distance-length, but doesn't actually encode anything -int find_match(int *searchBackUsed, unsigned char realMode) -{ - int tempCompare = 0; - int maxBlockSize = 0xFF, matchLen = 1, backRef; - - unsigned int bytesLeft = rlzi->inputlen - rlzi->inputpos; - unsigned int searchBack; // not too sure about this variable's name - - char realMode7Threshold; - int i; - - if(bytesLeft < 2) - return 1; - if(bytesLeft < 0xFF) // if we have less bytes left than the maximum block size - maxBlockSize = bytesLeft; - - //realMode7Threshold = ((unsigned int)(rlzi->sgl_120C84 / rlzi->sgl_120C88 + 128.5) > 0x64); //dbl_41A3B8 - //realMode7Threshold = ((int)((unsigned int)(rlzi->sgl_120C84 / rlzi->sgl_120C88 + 128.5)) > 0x64); - #define ROUND(n) ((int)((n) + 0.5)) - realMode7Threshold = (ROUND(rlzi->sgl_120C84 / rlzi->sgl_120C88) > -28); - // the second line above generates output consistent with the good rlzpacker, but is this typecasting all correct? - - // check if we have a continuous copy of something - searchBack = ABS_INPUT_POS - rlzi->lastSearchBack; // byte_CBF only used by find_match(); is initially 0 - if( - (unsigned int)rlzi->lastSearchBack < rlzi->inputpos - && *(unsigned char*)(searchBack-1) == *(unsigned char*)(ABS_INPUT_POS) - && *(unsigned char*)(searchBack) == *(unsigned char*)(ABS_INPUT_POS+1) - ){ - // if we've found repeated bytes... - *searchBackUsed = rlzi->lastSearchBack; - matchLen = 2; - } - // no continuous copy from previously found stream - check if any similarities in the past few (up to 3) bytes - else if((realMode == 7 && realMode7Threshold) || realMode != 7) - { - //i = (realMode == 7 ? 1 : 0); - //while((unsigned int)i < rlzi->inputpos && i <= 3) - for(i=(realMode == 7 ? 1 : 0); i<=3; i++) - { - if((unsigned int)i >= rlzi->inputpos) - break; - - searchBack = ABS_INPUT_POS - i; - if( - *(unsigned char*)(searchBack-1) == *(unsigned char*)(ABS_INPUT_POS) - && *(unsigned char*)(searchBack) == *(unsigned char*)(ABS_INPUT_POS+1) - ){ - *searchBackUsed = i; - matchLen = 2; - break; - } - //i++; - } - } - //loc_405DB7: - // if we have 2 bytes left in the buffer (remember, if there's 1 byte left, this function would've - // already returned), we'll either by able to pack 1 or 2 bytes into a single byte - // We can pack 2 bytes into a byte if repeat bytes (above) have been found. - if(maxBlockSize < 3) - { - return matchLen; - } - - //loc_405DC9: - //edx = ABS_INPUT_POS; - never actually read... - unsigned int brHash = *(unsigned int*)ABS_INPUT_POS << 8; // previously eax - - backRef = rlzi->window[(unsigned int)brHash % RLZI_WINDOW_SIZE]; - while(backRef != -1) - { // found a back reference - if(backRef < (int)(rlzi->inputpos - RLZI_DICTIONARY_SIZE-1)) // is the backreference out of bounds? - break; - - unsigned int matchAmt = matchLen +1; - - // verify that the current matchLen is valid? - for(i = matchLen; i >= 0; i--) - { - if(*(unsigned char*)((unsigned int)rlzi->input + backRef+i) != *(unsigned char*)(ABS_INPUT_POS+i)) - goto loc_405F97; // jumps to end of while-loop (not valid - search for more references which could be valid) - } - - // see how much more we can match - for(; matchAmt < maxBlockSize; matchAmt++) - { - //loc_405E70: - if( - *(unsigned char*)(((unsigned int)rlzi->input + matchAmt)+backRef) != - *(unsigned char*)(((unsigned int)rlzi->input + matchAmt)+rlzi->inputpos) - ) - break; - } - i = rlzi->inputpos - backRef - 1; // distance? penalising short matches which have a large distance?? - if(matchAmt >= 3 && (matchAmt != 3 || (i <= 0x7F && (i <= 0x30 || realMode != 5)))) - { - tempCompare=0; - switch(matchAmt) - { - case(3): tempCompare = 0x7E; break; - case(4): tempCompare = 0x4400; break; - case(5): tempCompare = 0x6A00; break; - case(6): tempCompare = 0x28400; break; - } - if(!tempCompare || (i <= tempCompare)) - { - matchLen = matchAmt; // this is a good match - *searchBackUsed = i; - if(*searchBackUsed) - { - // check if all matched is actually the same character - for(i=0; i < matchAmt; i++) { - if(*(unsigned char*)((unsigned int)rlzi->input+backRef) != *(unsigned char*)(ABS_INPUT_POS + i)) - break; - } - if(matchAmt == i) - { // all matched is all the same character! - do { // see how far ahead we can go - if(*(unsigned char*)((unsigned int)rlzi->input+backRef) != *(unsigned char*)(ABS_INPUT_POS + i)) - break; - i++; - } while(i < maxBlockSize); - //loc_405F3D: - if(i == maxBlockSize) - { - if(matchAmt <= 3) - return 3; - - // loc_405F60: - for(i=3; i < matchAmt; i++) // loop max of 6 times due to code below - { - if(i != 3 || (*searchBackUsed <= 0x7F && (*searchBackUsed <= 0x30 || realMode != 5))) - { - switch(i) - { - case(3): tempCompare = 0x7E; break; - case(4): tempCompare = 0x4400; break; - case(5): tempCompare = 0x6A00; break; - case(6): tempCompare = 0x28400; break; - default: - return i; - } - if(*searchBackUsed <= tempCompare) - break; - } - } - return i; - } - } - } - //loc_405F8F: - if(matchAmt == maxBlockSize) - { - //loc_406009: - if(maxBlockSize != 0xFF) - return maxBlockSize; - // since maxBlockSize <= 0xFF, this implies maxBlockSize==0xFF - if(bytesLeft <= 0xFF) // if bytesLeft < 0xFF, then maxBlockSize < 0xFF will be true (enforced above), thus this line is essentially "if(bytesLeft == 0xFF)" - { - return 0xFD; // or 0x100 - 3 - } - //loc_406030: - // note, at this point, matchAmt = maxBlockSize = 0xFF, bytesLeft > 0xFF - if(bytesLeft == 0x100) return 0xFD; - for(i = matchAmt; i < 0x101; i++) - if(*(unsigned char*)(ABS_INPUT_POS+i+backRef-rlzi->inputpos) != *(unsigned char*)(ABS_INPUT_POS+i)) - return 0xFD; - return 0xFF; - } - } - } - loc_405F97: - backRef = rlzi->dictionary[backRef & RLZI_DICTIONARY_SIZE]; - } - - //loc_405FB4: - if(matchLen == 2) - rlzi->lastSearchBack = *searchBackUsed & 0xFF; - return matchLen; -} + +#include +#include +#include +#include +#include + +#define RLZI_WINDOW_SIZE 0x7FED // *4 = 130,996 bytes (128KB - 76 bytes) +#define RLZI_DICTIONARY_SIZE 0x3FFFF // *4 = 1,048,572 bytes (1MB - 4 bytes) + +#define ABS_INPUT_POS ((unsigned int)rlzi->input + rlzi->inputpos) + +/** Function Prototypes **/ +int rlzcompress(void *output, int inlen, void *input, unsigned char mode); +//void sub_405810(int arg_CB8, int64_t arg_CB0); +void flush_output(void); +void write_output(void); +void write_bit(unsigned char* unk0ptr, unsigned char arg_flag); +void write_special(unsigned char* unk0ptr, int num, int arg_edx); +int write_match_len(unsigned char len, unsigned int arg_4); +int find_match(int *arg_0, unsigned char realMode); + + +#define log2(x) (log((long double)(x)) / (long double)0.30102999566398119521373889472449) + +typedef struct { // unk_421D80 + unsigned char literals[8 * 0xFF]; //grouped in 8 groups of 0xFF; group selected by has of input position, last byte and mode, the 0xFF part is based on the input byte with various shifts + unsigned char distDescLong[0xF0]; // 30x8 (match >= 4) + unsigned char distDescShort[0x40]; // 8x8 (match < 4) + unsigned char distances[12 * 0x20]; // grouped in 0x20 based on bit position + unsigned char lenDesc[0x40]; // 8 x 8; group selected by bit position, position is var_C from main function + unsigned char matchlens[0xFF]; + + void* input; // 0xCA8 + void* output; // 0xCAC + //long dword_CB0; // 0xCB0 (0x422A30) - only wrtten to in sub_405810 + //long dword_CB4; // 0xCB4 + int64_t rangeOffset; // 0xCB0 (0x422A30) - only written to in sub_405810(), affected by write_bit and write_match_len + // - appears to be a data queue - top 24 bits not used, next 8 bits->data to be written, next 8 bits->data mask; affected by CB8 in some way (CB8 is added to this) + long rangeSize; // 0xCB8 + char nextOutputByte; // 0xCBC - some byte offset; 3rd byte in CB0, only ever used in sub_405810; guaranteed never to == 0xFF + char bytesToWrite; // 0xCBD (0x422A3D) - only used in sub_405810 + char writeLock; // 0xCBE - only ever used as a switch + char lastSearchBack; // 0xCBF - some byte flag used in find_match() + unsigned long outputpos;// 0xCC0 (0x422A40) + //long unused_CC4; // 0xCC4 - align 8? - always 0 in program + unsigned long inputpos; // 0xCC8 (0x422A48) + unsigned long inputlen; // 0xCCC (0x422A4C) + + // window stores positions of stuff; the index itself is based on hashed data + long window[RLZI_WINDOW_SIZE]; // 0xCD0-0x20C84 + // dictionary stores same stuff as window, but indexed on positions + long dictionary[RLZI_DICTIONARY_SIZE]; // 1MB - dictionary?? + //long unused_120C80; + + float sgl_120C84; + float sgl_120C88; // starts as 0.0625 and increments by that amount every time the dictionary coder fails to pack stuff +} RLZ_Info; // about 1.128MB +RLZ_Info rlzip; +RLZ_Info* rlzi = &rlzip; + +/** Functions **/ +// sub_406070 +int rlzcompress(void *output, int inlen, void *input, unsigned char mode) +{ + if(!inlen) return -1; // sanity check + + /** ABOUT THE MODE ARGUMENT + * The mode argument contains two values broken up like (___|45|678) + * Bits 678 are the compression mode + * Bits 45 determine the default values to store in literals/distances etc array + * The first 3 bits aren't used + */ + int var_C = 0; // var_C appears to do some tracking in regards to how well the dictionary coder is packing things? + char realMode; + int dictSearchBack = 0; // just assign something in case... :S + + // variables introduced by me + unsigned int i; + unsigned int matchLen = 0; + //unsigned int iInputPosTemp; + + realMode = mode & 7; // grab the compression mode + + // originally init_rlzi() + rlzi->input = input; + rlzi->inputlen = inlen; + rlzi->output = output; + rlzi->rangeOffset = 0; + rlzi->rangeSize = -1; + rlzi->nextOutputByte = 0; + rlzi->bytesToWrite = 0; + // no writeLock=0 - writeLock is _always_ assigned before calling the write function anyway + rlzi->lastSearchBack = 0; + rlzi->outputpos = 0; + rlzi->inputpos = 0; //added by me + + rlzi->sgl_120C84 = 0; + rlzi->sgl_120C88 = 0.0625; + memset(rlzi->window, 0xFF, sizeof(rlzi->window)); + + + + // grab the 2nd number from the mode + //eax = (4 - ((mode >> 3) & 0x03)) << 5; // 12345678 -> 45 + /* ^ 4 possible combinations for the above + * if bits45 == 00 (should be) then eax => 0x80 (10000000) + * if bits45 == 01 then eax => 0x60 (01100000) + * if bits45 == 10 then eax => 0x40 (01000000) + * if bits45 == 11 then eax => 0x20 (00100000) + */ + { + unsigned char byteSet = ((4 - ((mode >> 3) & 0x03)) << 5); + memset(rlzi->literals, byteSet, sizeof(rlzi->literals)); + memset(rlzi->distDescLong, byteSet, sizeof(rlzi->distDescLong)); + memset(rlzi->distDescShort, byteSet, sizeof(rlzi->distDescShort)); + memset(rlzi->distances, byteSet, sizeof(rlzi->distances)); + memset(rlzi->lenDesc, byteSet, sizeof(rlzi->lenDesc)); + memset(rlzi->matchlens, byteSet, sizeof(rlzi->matchlens)); + } + + unsigned char lastInputByte = 0; + + // main loop - this will loop until all the data is processed + while(rlzi->inputpos < rlzi->inputlen) //loc_4060D0 + { + matchLen = find_match(&dictSearchBack, realMode); + if(matchLen < 2) matchLen = 1; + int sub_405B90_ret = write_match_len(matchLen -1, var_C); + if(matchLen == 1) // no match found + { + // write literal + // the following code was previously sub_405A10( (((((unsigned int)rlzi->inputpos & 7) << 8) | lastInputByte) >> realMode) & 7 ) + int arg_ecx = (( + (((unsigned int)rlzi->inputpos & 7) << 8) | lastInputByte + ) >> realMode) & 7; // top bit in (rlzi->inputpos & 7) will actually be discarded - that is, (rlzi->inputpos & 3) should give the same result + int oldRangeSize = rlzi->rangeSize; + unsigned int oldOutputpos = rlzi->outputpos; + unsigned int inputVal = *(unsigned char*)ABS_INPUT_POS + 0x100; + for(; !(inputVal & 0x10000); inputVal <<= 1) // loop 8 times + write_bit( + (unsigned char*)((unsigned int)rlzi->literals + arg_ecx*0xFF + (inputVal >> 8)-1), + (inputVal >> 7) & 1 + ); // last arg (now deleted) is actually ebx, but ebx is 3 at this time + + // these values are only ever used if realMode==7, and only in find_match() function + #define scast_405A10(v) ((long double)((unsigned int)(v))) + // (int) cast added to make it consistent with good rlz packer, but unsure if this is correct... + rlzi->sgl_120C84 += (float)(log2(scast_405A10(oldRangeSize) / scast_405A10(rlzi->rangeSize)) + (int)((rlzi->outputpos - oldOutputpos - 1)*8)); + rlzi->sgl_120C88 += 0.0625f; //flt_41A3AC + + if(var_C > 0) var_C--; + } + else + { + // write distance instead of literal + // originally sub_405C10(dictSearchBack, sub_405B90(matchLen -1, var_C), matchLen) + int searchBackPlus1 = dictSearchBack +1; + unsigned char* treeStore; + //int o_ebx=ebx, o_ebp=ebp, o_esi=esi, o_edi=edi; + int sbSignifBit = 0; + int var_edi; + i = 0; + if((searchBackPlus1 & 0xFFFFFFFE) > 0) // if searchBackPlus1 has bits other than the first set... (that is, searchBackPlus1 != 0 && searchBackPlus1 != 1) + { // this condition _should_ always be true, as min dictSearchBack is 1, and with +1, min value for searchBackPlus1 is 2 + // find the position of the most significant "1" + for(i=2; (searchBackPlus1 >> i) > 0; i++); + sbSignifBit = --i; // put the bit position in ebp + } + + if(matchLen < 4) { + var_edi = 4; + treeStore = rlzi->distDescShort; + } else { + var_edi = 16; + treeStore = rlzi->distDescLong; + sbSignifBit += 0xC; + } + + int var_ebx = 1; + int var_esi; + do { // write packed thing - perhaps a length of next number descriptor? + var_esi = ((sbSignifBit & var_edi) > 0 ? 1 : 0); // check if certain bit in ebp is set + write_bit((unsigned char*)((unsigned int)&treeStore[var_ebx*8] + sub_405B90_ret), var_esi); + var_edi >>= 1; + var_ebx = var_esi+var_ebx*2; + } while(var_edi > 0); // loop 3 or 5 times + + if(i > 0) // should always be true + { + // write distance + i--; + write_special(rlzi->distances + i*0x20, searchBackPlus1, i); + } + + var_C = 7 - ((rlzi->inputpos + matchLen) & 1); + } + + unsigned int bytesLeftMinus3 = rlzi->inputlen - rlzi->inputpos - 3; + if(matchLen < bytesLeftMinus3) + bytesLeftMinus3 = matchLen; + for(i=0; idictionary[(rlzi->inputpos+i) & RLZI_DICTIONARY_SIZE] = rlzi->window[brHash]; + rlzi->window[brHash] = rlzi->inputpos+i; + } + + rlzi->inputpos += matchLen; + lastInputByte = *(unsigned char*)(ABS_INPUT_POS-1); + if(rlzi->outputpos == rlzi->inputlen) // is the compressed output larger than the original stream? + return -1; + } + + write_match_len(0xFF, var_C); // end marker or is this used for flushing data? + + { // originally sub_4058E0() + i = (rlzi->writeLock ? 5 : 4); + //rlzi->writeLock = 0; + while(i--) + flush_output(); + } + + // stick the mode into the first byte of the output + // (otherwise, first byte is guaranteed to be 0) + *(unsigned char*)output = mode; + + return rlzi->outputpos; // length of compressed data +} + +// calls write_bit, write_special +// function handles some packing routines +int write_match_len(unsigned char len, unsigned int arg_4) // arg_4 -> something that keeps track of no. past blocks packed with find_match() (always var_C from rlzcompress()) +{ + // len is the length -1 (amount of data removed by dictionary coder), except when 0xFF (when called after loop has finished) + int i; + char flagTemp; + // determine how packed the data is - ie, 1 bytes removed? 2 bytes?, 4?, 8? ... + // or perhaps, this is actually outputting the length code! + for(i=0; i<8; i++) + { + flagTemp = (len >= (1u << i) ? 1 : 0); + write_bit((unsigned char*)((unsigned int)&rlzi->lenDesc[i*8] + arg_4), flagTemp); + if(flagTemp == 0) break; + } + i--; + if(i > 0) // --> identical to if(len != 0); this will only be true if len >= 1 (or len<0) - eg if data was packed + { + i--; + unsigned char* ptr = rlzi->matchlens + ((arg_4 & 7)+i*8)*4; + write_special(ptr + ((rlzi->inputpos << i) & 3), len, i); + i++; + } + return i; // returns packing threshold? +} + +// calls sub_405810 +void write_bit(unsigned char* unk0ptr, unsigned char arg_flag) +{ + unsigned int range = (unsigned int)rlzi->rangeSize; + + unsigned int pMid = (range >> 8) * (*unk0ptr); + if(!arg_flag) + { + rlzi->rangeOffset += pMid; + range -= pMid; + } + else + { + range = pMid; + *unk0ptr += 0xFF; // increase probability + } + *unk0ptr -= ((unsigned int)(*unk0ptr) >> 3); + rlzi->rangeSize = (long)range; + write_output(); +} + +// calls write_bit (used to)sub_4059B0 +// arg_4, always either rlzi->distances or rlzi->matchlens +// inputpos: either rlzi->inputpos or 0 +// arg_edx is likely to be a 8bit value; something to do with bit position in distance +// distance: but can be "len" when called from write_match_len +void write_special(unsigned char* unk0ptr, int num, int arg_edx) +//void write_special(int arg_4, int distance, int inputpos, int arg_ecx, int arg_edx) +{ + if(arg_edx > 255) + printf("ASSUMPTION FAILURE\n"); + //int var_ebx = ((inputpos << (arg_edx & 0xFF)) & 3) + arg_4 + ((arg_ecx & 7)+arg_edx*8)*4; + if(arg_edx>=3) + { + write_bit(unk0ptr, (num >> arg_edx) & 1); + arg_edx--; + if(arg_edx>=3) + { + write_bit(unk0ptr, (num >> arg_edx) & 1); + arg_edx--; + } + unk0ptr++; + if(arg_edx>=3) + { + // previously sub_4059B0() + unsigned int rangeTmp = (unsigned int)rlzi->rangeSize; + arg_edx -= 2; + while(arg_edx--) { + rangeTmp >>= 1; + if(((num >> (3 + arg_edx)) & 1) == 0) + { + rlzi->rangeOffset += rangeTmp; + } + } + arg_edx += 3; // 2 (reverse previous subtraction), and +1 for the above loop + rlzi->rangeSize = (long)rangeTmp; + write_output(); + } + } + do { + write_bit(unk0ptr, (num >> arg_edx) & 1); + unk0ptr++; + arg_edx--; + } while(arg_edx >= 0); +} + + +void flush_output(void) +{ + // if byte CB3 is NOT 0xFF, OR dword CB4 is true + // -- which means it can only be false if rlzi->rangeOffset == 0x00000000FF?????? + if((rlzi->rangeOffset >> 0x18) != 0xFF) + { + unsigned char byteOut = rlzi->nextOutputByte; // starts off as 0, then becomes 3rd byte of rlzi->rangeOffset + rlzi->nextOutputByte = (rlzi->rangeOffset >> 0x18) & 0xFF; + do { + if(rlzi->outputpos == rlzi->inputlen) + return; + *(unsigned char*)((unsigned int)rlzi->output + rlzi->outputpos) = ((rlzi->rangeOffset >> 32) & 0xFF) + byteOut; + rlzi->bytesToWrite--; + rlzi->outputpos++; + byteOut = 0xFF; + } while(rlzi->bytesToWrite >= 0); + } + + //loc_4058A4: + rlzi->bytesToWrite++; + //rlzi->rangeOffset = (arg_CB0 << 8) & 0xFFFFFFFF; // make sure high 32 bits are 0 + //rlzi->rangeSize = arg_CB8 << 8; + rlzi->rangeOffset <<= 8; + rlzi->rangeOffset &= 0xFFFFFFFF; // make sure high 32 bits are 0 + rlzi->rangeSize <<= 8; +} + +void write_output(void) +{ + rlzi->writeLock = ((rlzi->rangeSize >> 0x18) & 0xFF ? 1:0); + if(!rlzi->writeLock) { + flush_output(); + } +} + + +// sub_405CE0: arg_0 = buf +// this function determines how much data we can pack into a single byte +// return of 1 = can't compress; don't think this can return 0xFF or higher +// NEW: this function searches for distance-length, but doesn't actually encode anything +int find_match(int *searchBackUsed, unsigned char realMode) +{ + int tempCompare = 0; + int maxBlockSize = 0xFF, matchLen = 1, backRef; + + unsigned int bytesLeft = rlzi->inputlen - rlzi->inputpos; + unsigned int searchBack; // not too sure about this variable's name + + char realMode7Threshold; + int i; + + if(bytesLeft < 2) + return 1; + if(bytesLeft < 0xFF) // if we have less bytes left than the maximum block size + maxBlockSize = bytesLeft; + + //realMode7Threshold = ((unsigned int)(rlzi->sgl_120C84 / rlzi->sgl_120C88 + 128.5) > 0x64); //dbl_41A3B8 + //realMode7Threshold = ((int)((unsigned int)(rlzi->sgl_120C84 / rlzi->sgl_120C88 + 128.5)) > 0x64); + #define ROUND(n) ((int)((n) + 0.5)) + realMode7Threshold = (ROUND(rlzi->sgl_120C84 / rlzi->sgl_120C88) > -28); + // the second line above generates output consistent with the good rlzpacker, but is this typecasting all correct? + + // check if we have a continuous copy of something + searchBack = ABS_INPUT_POS - rlzi->lastSearchBack; // byte_CBF only used by find_match(); is initially 0 + if( + (unsigned int)rlzi->lastSearchBack < rlzi->inputpos + && *(unsigned char*)(searchBack-1) == *(unsigned char*)(ABS_INPUT_POS) + && *(unsigned char*)(searchBack) == *(unsigned char*)(ABS_INPUT_POS+1) + ){ + // if we've found repeated bytes... + *searchBackUsed = rlzi->lastSearchBack; + matchLen = 2; + } + // no continuous copy from previously found stream - check if any similarities in the past few (up to 3) bytes + else if((realMode == 7 && realMode7Threshold) || realMode != 7) + { + //i = (realMode == 7 ? 1 : 0); + //while((unsigned int)i < rlzi->inputpos && i <= 3) + for(i=(realMode == 7 ? 1 : 0); i<=3; i++) + { + if((unsigned int)i >= rlzi->inputpos) + break; + + searchBack = ABS_INPUT_POS - i; + if( + *(unsigned char*)(searchBack-1) == *(unsigned char*)(ABS_INPUT_POS) + && *(unsigned char*)(searchBack) == *(unsigned char*)(ABS_INPUT_POS+1) + ){ + *searchBackUsed = i; + matchLen = 2; + break; + } + //i++; + } + } + //loc_405DB7: + // if we have 2 bytes left in the buffer (remember, if there's 1 byte left, this function would've + // already returned), we'll either by able to pack 1 or 2 bytes into a single byte + // We can pack 2 bytes into a byte if repeat bytes (above) have been found. + if(maxBlockSize < 3) + { + return matchLen; + } + + //loc_405DC9: + //edx = ABS_INPUT_POS; - never actually read... + unsigned int brHash = *(unsigned int*)ABS_INPUT_POS << 8; // previously eax + + backRef = rlzi->window[(unsigned int)brHash % RLZI_WINDOW_SIZE]; + while(backRef != -1) + { // found a back reference + if(backRef < (int)(rlzi->inputpos - RLZI_DICTIONARY_SIZE-1)) // is the backreference out of bounds? + break; + + unsigned int matchAmt = matchLen +1; + + // verify that the current matchLen is valid? + for(i = matchLen; i >= 0; i--) + { + if(*(unsigned char*)((unsigned int)rlzi->input + backRef+i) != *(unsigned char*)(ABS_INPUT_POS+i)) + goto loc_405F97; // jumps to end of while-loop (not valid - search for more references which could be valid) + } + + // see how much more we can match + for(; matchAmt < maxBlockSize; matchAmt++) + { + //loc_405E70: + if( + *(unsigned char*)(((unsigned int)rlzi->input + matchAmt)+backRef) != + *(unsigned char*)(((unsigned int)rlzi->input + matchAmt)+rlzi->inputpos) + ) + break; + } + i = rlzi->inputpos - backRef - 1; // distance? penalising short matches which have a large distance?? + if(matchAmt >= 3 && (matchAmt != 3 || (i <= 0x7F && (i <= 0x30 || realMode != 5)))) + { + tempCompare=0; + switch(matchAmt) + { + case(3): tempCompare = 0x7E; break; + case(4): tempCompare = 0x4400; break; + case(5): tempCompare = 0x6A00; break; + case(6): tempCompare = 0x28400; break; + } + if(!tempCompare || (i <= tempCompare)) + { + matchLen = matchAmt; // this is a good match + *searchBackUsed = i; + if(*searchBackUsed) + { + // check if all matched is actually the same character + for(i=0; i < matchAmt; i++) { + if(*(unsigned char*)((unsigned int)rlzi->input+backRef) != *(unsigned char*)(ABS_INPUT_POS + i)) + break; + } + if(matchAmt == i) + { // all matched is all the same character! + do { // see how far ahead we can go + if(*(unsigned char*)((unsigned int)rlzi->input+backRef) != *(unsigned char*)(ABS_INPUT_POS + i)) + break; + i++; + } while(i < maxBlockSize); + //loc_405F3D: + if(i == maxBlockSize) + { + if(matchAmt <= 3) + return 3; + + // loc_405F60: + for(i=3; i < matchAmt; i++) // loop max of 6 times due to code below + { + if(i != 3 || (*searchBackUsed <= 0x7F && (*searchBackUsed <= 0x30 || realMode != 5))) + { + switch(i) + { + case(3): tempCompare = 0x7E; break; + case(4): tempCompare = 0x4400; break; + case(5): tempCompare = 0x6A00; break; + case(6): tempCompare = 0x28400; break; + default: + return i; + } + if(*searchBackUsed <= tempCompare) + break; + } + } + return i; + } + } + } + //loc_405F8F: + if(matchAmt == maxBlockSize) + { + //loc_406009: + if(maxBlockSize != 0xFF) + return maxBlockSize; + // since maxBlockSize <= 0xFF, this implies maxBlockSize==0xFF + if(bytesLeft <= 0xFF) // if bytesLeft < 0xFF, then maxBlockSize < 0xFF will be true (enforced above), thus this line is essentially "if(bytesLeft == 0xFF)" + { + return 0xFD; // or 0x100 - 3 + } + //loc_406030: + // note, at this point, matchAmt = maxBlockSize = 0xFF, bytesLeft > 0xFF + if(bytesLeft == 0x100) return 0xFD; + for(i = matchAmt; i < 0x101; i++) + if(*(unsigned char*)(ABS_INPUT_POS+i+backRef-rlzi->inputpos) != *(unsigned char*)(ABS_INPUT_POS+i)) + return 0xFD; + return 0xFF; + } + } + } + loc_405F97: + backRef = rlzi->dictionary[backRef & RLZI_DICTIONARY_SIZE]; + } + + //loc_405FB4: + if(matchLen == 2) + rlzi->lastSearchBack = *searchBackUsed & 0xFF; + return matchLen; +} diff --git a/rlzpack.h b/rlzpack.h index b254952..21f9ea1 100644 --- a/rlzpack.h +++ b/rlzpack.h @@ -1,4 +1,4 @@ -#ifndef __RLZPACK__ -#define __RLZPACK__ -int rlzcompress(void *output, int inlen, void *input, unsigned char mode); -#endif +#ifndef __RLZPACK__ +#define __RLZPACK__ +int rlzcompress(void *output, int inlen, void *input, unsigned char mode); +#endif diff --git a/strfuncs.h b/strfuncs.h index 4d6ef9a..e8c5f4e 100644 --- a/strfuncs.h +++ b/strfuncs.h @@ -1,7 +1,7 @@ -// stupid MSVC -#include -#ifdef WIN32 -#define strcasecmp _stricmp -#else -#include -#endif +// stupid MSVC +#include +#ifdef WIN32 +#define strcasecmp _stricmp +#else +#include +#endif diff --git a/vaghandler.c b/vaghandler.c index fd73d59..9f02644 100644 --- a/vaghandler.c +++ b/vaghandler.c @@ -1,467 +1,467 @@ - -/** - ** A big thanks goes to highboy for his VAG <-> WAV sample code, which this file is based upon - */ - -#include -#include -#include -#include -#include "vaghandler.h" -#include "general.h" - -const double f[5][2] = { { 0.0, 0.0 }, - { 60.0 / 64.0, 0.0 }, - { 115.0 / 64.0, -52.0 / 64.0 }, - { 98.0 / 64.0, -55.0 / 64.0 }, - { 122.0 / 64.0, -60.0 / 64.0 } }; - - -#define ROUND(x) ((int)(x<0 ? x-0.5 : x+0.5)) - - -#define VAG_SIGNATURE 0x70474156 -#define VAG_VERSION 0x4000000 //0x2000000 -PACK_STRUCT(VagHeader, { - uint32 signature; // "VAGp" - uint32 version; // 0x20 - uint32 reserved; // 0 - uint32 dataSize; // size of file - 0x30 - uint32 frequency; - uint8 reserved2[12]; // 0 - char name[16]; - char unknown[16]; -}); -PACK_STRUCT(VagChunk, { - char predict_shift; // upper 4 bits = predict, lower 4 bits = shift - char flags; // 0 or 7 (end) - char s[14]; -}); -#define WAV_SIGNATURE 0x46464952 -#define WAV_FORMAT 0x45564157 -#define WAV_S1_SIG 0x20746D66 -#define WAV_S1_SIZE_PCM 16 -#define WAV_PCM_FORMAT 1 -#define WAV_S2_SIG 0x61746164 -PACK_STRUCT(WavHeader, { - uint32 signature; // "RIFF" - uint32 size; // filesize - 8 - uint32 format; // "WAVE" - - uint32 s1sig; // "fmt " - uint32 s1size; // 16 for PCM - uint16 audioFmt; // PCM = 1 - uint16 channels; - uint32 frequency; - uint32 byterate; // = frequency * channels * bitDepth/8 - uint16 sampleSize; // = channels * bitDepth / 8 - uint16 bitDepth; // we'll always use 16 - - uint32 s2sig; // "data" - uint32 s2size; // = numSamples * channels * bitDepth/8 OR size-36 -}); - - -// TODO: sanity checks with lengths -Bool vag2wav(const char* fWav, int numChannels, int* vagLen, void** vagData) { - - FILE* fp = openwrite(fWav); - if(!fp) return FALSE; - - char** vag = (char**)malloc(numChannels * sizeof(char*)); - memcpy(vag, vagData, numChannels * sizeof(char*)); - - - /* put WAV header */ - WavHeader wh; - wh.signature = WAV_SIGNATURE; - wh.format = WAV_FORMAT; - wh.s1sig = WAV_S1_SIG; - wh.s1size = WAV_S1_SIZE_PCM; - wh.audioFmt = WAV_PCM_FORMAT; - wh.channels = numChannels; - wh.bitDepth = 16; - wh.sampleSize = wh.channels * wh.bitDepth/8; - wh.s2sig = WAV_S2_SIG; - - - int i, j; - // read VAG header - for(i=0; ifrequency; - // TODO: version check - if(vh->signature != VAG_SIGNATURE || vh->frequency != wh.frequency) { - free(vag); - fclose(fp); - return FALSE; - } - } - wh.frequency = ENDIAN_SWAP(wh.frequency); - wh.byterate = wh.sampleSize * wh.frequency; - - // take a guess at the length (we'll fix these later if wrong) - make assumption that all channels are of same size - wh.s2size = (((*vagLen - sizeof(VagHeader)) / sizeof(VagChunk)) -1) * 28/*samples*/ * 2/*16bit*/ * numChannels; - wh.size = wh.s2size + 28; - - filewrite(fp, &wh, sizeof(wh)); - - int wavSamplesSize = numChannels * 28 * sizeof(int16); - int16* wavSamples = (int16*)malloc(wavSamplesSize); - double* factors = (double*)calloc(numChannels*2, sizeof(double)); - - while(TRUE) { - Bool notEnded = FALSE; - memset(wavSamples, 0, numChannels*28); - for(i=0; iflags != 7) { - int shift = vc->predict_shift & 0xF; - int predict = (vc->predict_shift & 0xF0) >> 4; - int samples[28]; - // expand 4bit -> 8bit - for(j=0; j<14; j++) { - samples[j*2] = vc->s[j] & 0xF; - samples[j*2+1] = (vc->s[j] & 0xF0) >> 4; - } - for(j=0; j<28; j++) { - int s = samples[j] << 12; // shift 4 bits to top range of int16 - if(s & 0x8000) s |= 0xFFFF0000; - double sample = (double)(s >> shift) + factors[i*2] * f[predict][0] + factors[i*2+1] * f[predict][1]; - factors[i*2+1] = factors[i*2]; - factors[i*2] = sample; - wavSamples[j*numChannels + i] = (int16)ROUND(sample); - } - notEnded = TRUE; - } - } - - if(!notEnded) break; - filewrite(fp, wavSamples, wavSamplesSize); - } - - free(vag); - free(wavSamples); - free(factors); - - if(wh.size != (uint32)(ftell(fp) - 8)) { // fixup WAV sizes if wrong - if(strcmp(fWav, "-")) { // can only fix if not writing to stdout - wh.size = ftell(fp) - 8; - wh.s2size = wh.size - 36; - rewind(fp); - filewrite(fp, &wh, sizeof(wh)); - } else - warning("Written WAV lengths were incorrect - can't go back and fix."); - } - - fclose(fp); - return TRUE; -} - - -int wav2vag(const char* fWav, uint* len, void** vagData, const char* vagName) { - FILE* fp = openread(fWav); - if(!fp) return 0; - - WavHeader wh; - fileread(fp, &wh, sizeof(wh)); - -#define WAV2VAG_ERROR_EXIT(s) { \ - error(s, fWav); \ - fclose(fp); \ - return 0; \ - } - - if(wh.signature != WAV_SIGNATURE || wh.format != WAV_FORMAT) - WAV2VAG_ERROR_EXIT("File '%s' not a valid WAV file, conversion aborted.") - if(wh.s1size != WAV_S1_SIZE_PCM) { - warning("File '%s' appears to have a non-standard header length for PCM. Attempting to auto-skip...", fWav); - fseek(fp, sizeof(wh) + (wh.s1size - WAV_S1_SIZE_PCM) - sizeof(uint32)*2, SEEK_SET); - fileread(fp, &wh.s2sig, sizeof(uint32)); - } - if(wh.s1sig != WAV_S1_SIG || wh.s2sig != WAV_S2_SIG) - WAV2VAG_ERROR_EXIT("File '%s' structure not supported by rcomage's VAG handler; conversion aborted.") - if(wh.audioFmt != WAV_PCM_FORMAT || wh.bitDepth != 16) - WAV2VAG_ERROR_EXIT("rcomage's VAG handler only supports 16-bit PCM data, file '%s' not converted.") - - - if(wh.channels > 9) - WAV2VAG_ERROR_EXIT("Channels number too high for '%s'.") - - //if(wh.s2size != wh.size-36 || wh.size+8 != filesize(fWav) || wh.sampleSize != wh.channels*wh.bitDepth/8) - // WAV2VAG_ERROR_EXIT("Bad size values in WAV header for '%s'.") - - int i, j, k; - // size compression is 28 16-bit samples -> 16 bytes - uint numSamples = wh.s2size / wh.sampleSize; - *len = (numSamples/28 + (numSamples % 28 ? 2:1)) * sizeof(VagChunk) + sizeof(VagHeader); - *vagData = calloc(1, *len * wh.channels); - - for(i=0; isignature = VAG_SIGNATURE; - vh->version = VAG_VERSION; - vh->reserved = 0; - vh->dataSize = ENDIAN_SWAP(*len - 0x30); - vh->frequency = ENDIAN_SWAP(wh.frequency); - strncpy(vh->name, vagName, 16); - } - - uint pos; - int wavBufSize = 28 * (wh.bitDepth/8) * wh.channels; - int16* wavBuf = (int16*)malloc(wavBufSize); - double* factors = (double*)calloc(wh.channels*2, sizeof(double)); - double* factors2 = (double*)calloc(wh.channels*2, sizeof(double)); - for(pos=0; pos numSamples) - memset(wavBuf + ((numSamples-pos) * (wh.bitDepth/8) * wh.channels / sizeof(int16)), 0, (28 - numSamples+pos) * (wh.bitDepth/8) * wh.channels); - - uint ch; - for(ch=0; ch 30719.0) sample = 30719.0; - if(sample < -30720.0) sample = -30720.0; - predictBuf[j][k] = sample - s1[j] * f[j][0] - s2[j] * f[j][1]; - if(fabs(predictBuf[j][k]) > max) max = fabs(predictBuf[j][k]); - s2[j] = s1[j]; - s1[j] = sample; - } - if(max < min) { - min = max; - predict = j; - } - // ???? - //if(min <= 7) { - // predict = 0; - // break; - //} - } - factors[ch*2] = s1[predict]; - factors[ch*2+1] = s2[predict]; - - // find_shift - uint shiftMask; - for(shift=0, shiftMask=0x4000; shift<12; shift++, shiftMask>>=1) { - if(shiftMask & ((int)min + (shiftMask >> 3))) - break; - } - // so shift==12 if none found... - - vc->predict_shift = ((predict << 4) & 0xF0) | (shift & 0xF); - vc->flags = (numSamples - pos >= 28 ? 0:1); - - // pack - // I don't understand it, but it seems that the second transformation is required to prevent a clipping sound effect although it should produce worse reconverts... - int8 outBuf[28]; - for(k=0; k<28; k++) { - double s_double_trans = predictBuf[predict][k] - factors2[ch*2] * f[predict][0] - factors2[ch*2+1] * f[predict][1]; - // +0x800 for signed conversion?? - int sample = (((int)ROUND(s_double_trans) << shift) + 0x800) & 0xFFFFF000; - if(sample > 32767) sample = 32767; - if(sample < -32768) sample = -32768; - - outBuf[k] = (int8)(sample >> 12); - factors2[ch*2+1] = factors2[ch*2]; - factors2[ch*2] = (double)(sample >> shift) - s_double_trans; - } - for(k=0; k<14; k++) - vc->s[k] = ((outBuf[k*2+1] << 4) & 0xF0) | (outBuf[k*2] & 0xF); - - // checker code - /* { - int samples[28]; - // expand 4bit -> 8bit - for(j=0; j<14; j++) { - samples[j*2] = vc->s[j] & 0xF; - samples[j*2+1] = (vc->s[j] & 0xF0) >> 4; - } - for(j=0; j<28; j++) { - int s = samples[j] << 12; // shift 4 bits to top range of int16 - if(s & 0x8000) s |= 0xFFFF0000; - double sample = (double)(s >> shift) + factors2[ch*2] * f[predict][0] + factors2[ch*2+1] * f[predict][1]; - factors2[ch*2+1] = factors2[ch*2]; - factors2[ch*2] = sample; - if(wavBuf[j*wh.channels + ch] != (int16)ROUND(sample)) { - printf("a"); - } - } - } */ - } - } - - free(wavBuf); - free(factors); - free(factors2); - - // put terminating chunk - for(i=0; ipredict_shift = 0; - vc->flags = 7; - memset(&vc->s, 0x77, 14); - } - - fclose(fp); - return wh.channels; -} - - -/* -int wav2vag(const char* fWav, uint* len, void** vagData, const char* vagName) { - FILE* fp = fopen(fWav, "rb"); - if(!fp) return 0; - - WavHeader wh; - fileread(fp, &wh, sizeof(wh)); - -#define WAV2VAG_ERROR_EXIT(s) { \ - error(s, fWav); \ - fclose(fp); \ - return 0; \ - } - - if(wh.signature != WAV_SIGNATURE || wh.format != WAV_FORMAT) - WAV2VAG_ERROR_EXIT("File '%s' not a valid WAV file, conversion aborted.") - if(wh.s1sig != WAV_S1_SIG || wh.s1size != WAV_S1_SIZE_PCM || wh.s2sig != WAV_S2_SIG) - WAV2VAG_ERROR_EXIT("File '%s' structure not supported by rcomage's VAG handler; conversion aborted.") - if(wh.audioFmt != WAV_PCM_FORMAT || wh.bitDepth != 16) - WAV2VAG_ERROR_EXIT("rcomage's VAG handler only supports 16-bit PCM data, file '%s' not converted.") - - if(wh.channels > 9) - WAV2VAG_ERROR_EXIT("Channels number too high for '%s'.") - - //if(wh.s2size != wh.size-36 || wh.size+8 != filesize(fWav) || wh.sampleSize != wh.channels*wh.bitDepth/8) - // WAV2VAG_ERROR_EXIT("Bad size values in WAV header for '%s'.") - - - int i, j, k; - // size compression is 28 16-bit samples -> 16 bytes - uint numSamples = wh.s2size / wh.sampleSize; - *len = (numSamples/28 + (numSamples % 28 ? 2:1)) * sizeof(VagChunk) + sizeof(VagHeader); - *vagData = calloc(1, *len * wh.channels); - - for(i=0; isignature = VAG_SIGNATURE; - vh->version = VAG_VERSION; - vh->reserved = 0; - vh->dataSize = ENDIAN_SWAP(*len - 0x30); - vh->frequency = ENDIAN_SWAP(wh.frequency); - strncpy(vh->name, vagName, 16); - } - - uint pos; - int wavBufSize = 28 * (wh.bitDepth/8) * wh.channels; - int16* wavBuf = (int16*)malloc(wavBufSize); - double* factors = (double*)calloc(wh.channels*2, sizeof(double)); - double* factors2 = (double*)calloc(wh.channels*2, sizeof(double)); - for(pos=0; pos 30719.0) sample = 30719.0; - if(sample < -30720.0) sample = -30720.0; - predictBuf[j][k] = sample - s1 * f[j][0] - s2 * f[j][1]; - if(fabs(predictBuf[j][k]) > max) max = fabs(predictBuf[j][k]); - s2 = s1; - s1 = sample; - } - if(max < min) { - min = max; - predict = j; - // ???? store s1 & s2 into temp place? - } - // ???? - if(min <= 7) { - predict = 0; - break; - } - } - // ???? - factors[ch*2] = s1; - factors[ch*2+1] = s2; - - // find_shift - uint shiftMask; - for(shift=0, shiftMask=0x4000; shift<12; shift++, shiftMask>>=1) { - if(shiftMask & ((int)min + (shiftMask >> 3))) - break; - } - // so shift==12 if none found... - - vc->predict_shift = ((predict << 4) & 0xF0) | (shift & 0xF); - vc->flags = (numSamples - pos >= 28 ? 0:1); - - // pack - uint8 outBuf[28]; - for(k=0; k<28; k++) { - double sample = predictBuf[predict][k] - factors2[ch*2] * f[predict][0] - factors2[ch*2+1] * f[predict][1]; - int sample2 = ((int)(sample * (1 << shift)) + 0x800) & 0xfffff000; - if(sample2 > 32767) sample2 = 32767; - if(sample2 < -32768) sample2 = -32768; - outBuf[k] = (uint8)(sample2 >> 8); - sample2 >>= shift; - factors2[ch*2+1] = factors2[ch*2]; - factors2[ch*2] = (double)sample2 - sample; - } - for(k=0; k<14; k++) - vc->s[k] = (outBuf[k*2+1] & 0xF0) | ((outBuf[k*2] >> 4) & 0xF); - } - } - - free(wavBuf); - free(factors); - free(factors2); - - // put terminating chunk - for(i=0; ipredict_shift = 0; - vc->flags = 7; - memset(&vc->s, 0x77, 14); - } - - fclose(fp); - return wh.channels; -} -*/ + +/** + ** A big thanks goes to highboy for his VAG <-> WAV sample code, which this file is based upon + */ + +#include +#include +#include +#include +#include "vaghandler.h" +#include "general.h" + +const double f[5][2] = { { 0.0, 0.0 }, + { 60.0 / 64.0, 0.0 }, + { 115.0 / 64.0, -52.0 / 64.0 }, + { 98.0 / 64.0, -55.0 / 64.0 }, + { 122.0 / 64.0, -60.0 / 64.0 } }; + + +#define ROUND(x) ((int)(x<0 ? x-0.5 : x+0.5)) + + +#define VAG_SIGNATURE 0x70474156 +#define VAG_VERSION 0x4000000 //0x2000000 +PACK_STRUCT(VagHeader, { + uint32 signature; // "VAGp" + uint32 version; // 0x20 + uint32 reserved; // 0 + uint32 dataSize; // size of file - 0x30 + uint32 frequency; + uint8 reserved2[12]; // 0 + char name[16]; + char unknown[16]; +}); +PACK_STRUCT(VagChunk, { + char predict_shift; // upper 4 bits = predict, lower 4 bits = shift + char flags; // 0 or 7 (end) + char s[14]; +}); +#define WAV_SIGNATURE 0x46464952 +#define WAV_FORMAT 0x45564157 +#define WAV_S1_SIG 0x20746D66 +#define WAV_S1_SIZE_PCM 16 +#define WAV_PCM_FORMAT 1 +#define WAV_S2_SIG 0x61746164 +PACK_STRUCT(WavHeader, { + uint32 signature; // "RIFF" + uint32 size; // filesize - 8 + uint32 format; // "WAVE" + + uint32 s1sig; // "fmt " + uint32 s1size; // 16 for PCM + uint16 audioFmt; // PCM = 1 + uint16 channels; + uint32 frequency; + uint32 byterate; // = frequency * channels * bitDepth/8 + uint16 sampleSize; // = channels * bitDepth / 8 + uint16 bitDepth; // we'll always use 16 + + uint32 s2sig; // "data" + uint32 s2size; // = numSamples * channels * bitDepth/8 OR size-36 +}); + + +// TODO: sanity checks with lengths +Bool vag2wav(const char* fWav, int numChannels, int* vagLen, void** vagData) { + + FILE* fp = openwrite(fWav); + if(!fp) return FALSE; + + char** vag = (char**)malloc(numChannels * sizeof(char*)); + memcpy(vag, vagData, numChannels * sizeof(char*)); + + + /* put WAV header */ + WavHeader wh; + wh.signature = WAV_SIGNATURE; + wh.format = WAV_FORMAT; + wh.s1sig = WAV_S1_SIG; + wh.s1size = WAV_S1_SIZE_PCM; + wh.audioFmt = WAV_PCM_FORMAT; + wh.channels = numChannels; + wh.bitDepth = 16; + wh.sampleSize = wh.channels * wh.bitDepth/8; + wh.s2sig = WAV_S2_SIG; + + + int i, j; + // read VAG header + for(i=0; ifrequency; + // TODO: version check + if(vh->signature != VAG_SIGNATURE || vh->frequency != wh.frequency) { + free(vag); + fclose(fp); + return FALSE; + } + } + wh.frequency = ENDIAN_SWAP(wh.frequency); + wh.byterate = wh.sampleSize * wh.frequency; + + // take a guess at the length (we'll fix these later if wrong) - make assumption that all channels are of same size + wh.s2size = (((*vagLen - sizeof(VagHeader)) / sizeof(VagChunk)) -1) * 28/*samples*/ * 2/*16bit*/ * numChannels; + wh.size = wh.s2size + 28; + + filewrite(fp, &wh, sizeof(wh)); + + int wavSamplesSize = numChannels * 28 * sizeof(int16); + int16* wavSamples = (int16*)malloc(wavSamplesSize); + double* factors = (double*)calloc(numChannels*2, sizeof(double)); + + while(TRUE) { + Bool notEnded = FALSE; + memset(wavSamples, 0, numChannels*28); + for(i=0; iflags != 7) { + int shift = vc->predict_shift & 0xF; + int predict = (vc->predict_shift & 0xF0) >> 4; + int samples[28]; + // expand 4bit -> 8bit + for(j=0; j<14; j++) { + samples[j*2] = vc->s[j] & 0xF; + samples[j*2+1] = (vc->s[j] & 0xF0) >> 4; + } + for(j=0; j<28; j++) { + int s = samples[j] << 12; // shift 4 bits to top range of int16 + if(s & 0x8000) s |= 0xFFFF0000; + double sample = (double)(s >> shift) + factors[i*2] * f[predict][0] + factors[i*2+1] * f[predict][1]; + factors[i*2+1] = factors[i*2]; + factors[i*2] = sample; + wavSamples[j*numChannels + i] = (int16)ROUND(sample); + } + notEnded = TRUE; + } + } + + if(!notEnded) break; + filewrite(fp, wavSamples, wavSamplesSize); + } + + free(vag); + free(wavSamples); + free(factors); + + if(wh.size != (uint32)(ftell(fp) - 8)) { // fixup WAV sizes if wrong + if(strcmp(fWav, "-")) { // can only fix if not writing to stdout + wh.size = ftell(fp) - 8; + wh.s2size = wh.size - 36; + rewind(fp); + filewrite(fp, &wh, sizeof(wh)); + } else + warning("Written WAV lengths were incorrect - can't go back and fix."); + } + + fclose(fp); + return TRUE; +} + + +int wav2vag(const char* fWav, uint* len, void** vagData, const char* vagName) { + FILE* fp = openread(fWav); + if(!fp) return 0; + + WavHeader wh; + fileread(fp, &wh, sizeof(wh)); + +#define WAV2VAG_ERROR_EXIT(s) { \ + error(s, fWav); \ + fclose(fp); \ + return 0; \ + } + + if(wh.signature != WAV_SIGNATURE || wh.format != WAV_FORMAT) + WAV2VAG_ERROR_EXIT("File '%s' not a valid WAV file, conversion aborted.") + if(wh.s1size != WAV_S1_SIZE_PCM) { + warning("File '%s' appears to have a non-standard header length for PCM. Attempting to auto-skip...", fWav); + fseek(fp, sizeof(wh) + (wh.s1size - WAV_S1_SIZE_PCM) - sizeof(uint32)*2, SEEK_SET); + fileread(fp, &wh.s2sig, sizeof(uint32)); + } + if(wh.s1sig != WAV_S1_SIG || wh.s2sig != WAV_S2_SIG) + WAV2VAG_ERROR_EXIT("File '%s' structure not supported by rcomage's VAG handler; conversion aborted.") + if(wh.audioFmt != WAV_PCM_FORMAT || wh.bitDepth != 16) + WAV2VAG_ERROR_EXIT("rcomage's VAG handler only supports 16-bit PCM data, file '%s' not converted.") + + + if(wh.channels > 9) + WAV2VAG_ERROR_EXIT("Channels number too high for '%s'.") + + //if(wh.s2size != wh.size-36 || wh.size+8 != filesize(fWav) || wh.sampleSize != wh.channels*wh.bitDepth/8) + // WAV2VAG_ERROR_EXIT("Bad size values in WAV header for '%s'.") + + int i, j, k; + // size compression is 28 16-bit samples -> 16 bytes + uint numSamples = wh.s2size / wh.sampleSize; + *len = (numSamples/28 + (numSamples % 28 ? 2:1)) * sizeof(VagChunk) + sizeof(VagHeader); + *vagData = calloc(1, *len * wh.channels); + + for(i=0; isignature = VAG_SIGNATURE; + vh->version = VAG_VERSION; + vh->reserved = 0; + vh->dataSize = ENDIAN_SWAP(*len - 0x30); + vh->frequency = ENDIAN_SWAP(wh.frequency); + strncpy(vh->name, vagName, 16); + } + + uint pos; + int wavBufSize = 28 * (wh.bitDepth/8) * wh.channels; + int16* wavBuf = (int16*)malloc(wavBufSize); + double* factors = (double*)calloc(wh.channels*2, sizeof(double)); + double* factors2 = (double*)calloc(wh.channels*2, sizeof(double)); + for(pos=0; pos numSamples) + memset(wavBuf + ((numSamples-pos) * (wh.bitDepth/8) * wh.channels / sizeof(int16)), 0, (28 - numSamples+pos) * (wh.bitDepth/8) * wh.channels); + + uint ch; + for(ch=0; ch 30719.0) sample = 30719.0; + if(sample < -30720.0) sample = -30720.0; + predictBuf[j][k] = sample - s1[j] * f[j][0] - s2[j] * f[j][1]; + if(fabs(predictBuf[j][k]) > max) max = fabs(predictBuf[j][k]); + s2[j] = s1[j]; + s1[j] = sample; + } + if(max < min) { + min = max; + predict = j; + } + // ???? + //if(min <= 7) { + // predict = 0; + // break; + //} + } + factors[ch*2] = s1[predict]; + factors[ch*2+1] = s2[predict]; + + // find_shift + uint shiftMask; + for(shift=0, shiftMask=0x4000; shift<12; shift++, shiftMask>>=1) { + if(shiftMask & ((int)min + (shiftMask >> 3))) + break; + } + // so shift==12 if none found... + + vc->predict_shift = ((predict << 4) & 0xF0) | (shift & 0xF); + vc->flags = (numSamples - pos >= 28 ? 0:1); + + // pack + // I don't understand it, but it seems that the second transformation is required to prevent a clipping sound effect although it should produce worse reconverts... + int8 outBuf[28]; + for(k=0; k<28; k++) { + double s_double_trans = predictBuf[predict][k] - factors2[ch*2] * f[predict][0] - factors2[ch*2+1] * f[predict][1]; + // +0x800 for signed conversion?? + int sample = (((int)ROUND(s_double_trans) << shift) + 0x800) & 0xFFFFF000; + if(sample > 32767) sample = 32767; + if(sample < -32768) sample = -32768; + + outBuf[k] = (int8)(sample >> 12); + factors2[ch*2+1] = factors2[ch*2]; + factors2[ch*2] = (double)(sample >> shift) - s_double_trans; + } + for(k=0; k<14; k++) + vc->s[k] = ((outBuf[k*2+1] << 4) & 0xF0) | (outBuf[k*2] & 0xF); + + // checker code + /* { + int samples[28]; + // expand 4bit -> 8bit + for(j=0; j<14; j++) { + samples[j*2] = vc->s[j] & 0xF; + samples[j*2+1] = (vc->s[j] & 0xF0) >> 4; + } + for(j=0; j<28; j++) { + int s = samples[j] << 12; // shift 4 bits to top range of int16 + if(s & 0x8000) s |= 0xFFFF0000; + double sample = (double)(s >> shift) + factors2[ch*2] * f[predict][0] + factors2[ch*2+1] * f[predict][1]; + factors2[ch*2+1] = factors2[ch*2]; + factors2[ch*2] = sample; + if(wavBuf[j*wh.channels + ch] != (int16)ROUND(sample)) { + printf("a"); + } + } + } */ + } + } + + free(wavBuf); + free(factors); + free(factors2); + + // put terminating chunk + for(i=0; ipredict_shift = 0; + vc->flags = 7; + memset(&vc->s, 0x77, 14); + } + + fclose(fp); + return wh.channels; +} + + +/* +int wav2vag(const char* fWav, uint* len, void** vagData, const char* vagName) { + FILE* fp = fopen(fWav, "rb"); + if(!fp) return 0; + + WavHeader wh; + fileread(fp, &wh, sizeof(wh)); + +#define WAV2VAG_ERROR_EXIT(s) { \ + error(s, fWav); \ + fclose(fp); \ + return 0; \ + } + + if(wh.signature != WAV_SIGNATURE || wh.format != WAV_FORMAT) + WAV2VAG_ERROR_EXIT("File '%s' not a valid WAV file, conversion aborted.") + if(wh.s1sig != WAV_S1_SIG || wh.s1size != WAV_S1_SIZE_PCM || wh.s2sig != WAV_S2_SIG) + WAV2VAG_ERROR_EXIT("File '%s' structure not supported by rcomage's VAG handler; conversion aborted.") + if(wh.audioFmt != WAV_PCM_FORMAT || wh.bitDepth != 16) + WAV2VAG_ERROR_EXIT("rcomage's VAG handler only supports 16-bit PCM data, file '%s' not converted.") + + if(wh.channels > 9) + WAV2VAG_ERROR_EXIT("Channels number too high for '%s'.") + + //if(wh.s2size != wh.size-36 || wh.size+8 != filesize(fWav) || wh.sampleSize != wh.channels*wh.bitDepth/8) + // WAV2VAG_ERROR_EXIT("Bad size values in WAV header for '%s'.") + + + int i, j, k; + // size compression is 28 16-bit samples -> 16 bytes + uint numSamples = wh.s2size / wh.sampleSize; + *len = (numSamples/28 + (numSamples % 28 ? 2:1)) * sizeof(VagChunk) + sizeof(VagHeader); + *vagData = calloc(1, *len * wh.channels); + + for(i=0; isignature = VAG_SIGNATURE; + vh->version = VAG_VERSION; + vh->reserved = 0; + vh->dataSize = ENDIAN_SWAP(*len - 0x30); + vh->frequency = ENDIAN_SWAP(wh.frequency); + strncpy(vh->name, vagName, 16); + } + + uint pos; + int wavBufSize = 28 * (wh.bitDepth/8) * wh.channels; + int16* wavBuf = (int16*)malloc(wavBufSize); + double* factors = (double*)calloc(wh.channels*2, sizeof(double)); + double* factors2 = (double*)calloc(wh.channels*2, sizeof(double)); + for(pos=0; pos 30719.0) sample = 30719.0; + if(sample < -30720.0) sample = -30720.0; + predictBuf[j][k] = sample - s1 * f[j][0] - s2 * f[j][1]; + if(fabs(predictBuf[j][k]) > max) max = fabs(predictBuf[j][k]); + s2 = s1; + s1 = sample; + } + if(max < min) { + min = max; + predict = j; + // ???? store s1 & s2 into temp place? + } + // ???? + if(min <= 7) { + predict = 0; + break; + } + } + // ???? + factors[ch*2] = s1; + factors[ch*2+1] = s2; + + // find_shift + uint shiftMask; + for(shift=0, shiftMask=0x4000; shift<12; shift++, shiftMask>>=1) { + if(shiftMask & ((int)min + (shiftMask >> 3))) + break; + } + // so shift==12 if none found... + + vc->predict_shift = ((predict << 4) & 0xF0) | (shift & 0xF); + vc->flags = (numSamples - pos >= 28 ? 0:1); + + // pack + uint8 outBuf[28]; + for(k=0; k<28; k++) { + double sample = predictBuf[predict][k] - factors2[ch*2] * f[predict][0] - factors2[ch*2+1] * f[predict][1]; + int sample2 = ((int)(sample * (1 << shift)) + 0x800) & 0xfffff000; + if(sample2 > 32767) sample2 = 32767; + if(sample2 < -32768) sample2 = -32768; + outBuf[k] = (uint8)(sample2 >> 8); + sample2 >>= shift; + factors2[ch*2+1] = factors2[ch*2]; + factors2[ch*2] = (double)sample2 - sample; + } + for(k=0; k<14; k++) + vc->s[k] = (outBuf[k*2+1] & 0xF0) | ((outBuf[k*2] >> 4) & 0xF); + } + } + + free(wavBuf); + free(factors); + free(factors2); + + // put terminating chunk + for(i=0; ipredict_shift = 0; + vc->flags = 7; + memset(&vc->s, 0x77, 14); + } + + fclose(fp); + return wh.channels; +} +*/ diff --git a/vaghandler.h b/vaghandler.h index 1c6fd7a..59676bd 100644 --- a/vaghandler.h +++ b/vaghandler.h @@ -1,3 +1,3 @@ -#include "general.h" -Bool vag2wav(const char* fWav, int numChannels, int* vagLen, void** vagData); -int wav2vag(const char* fWav, uint* len, void** vagData, const char* vagName); +#include "general.h" +Bool vag2wav(const char* fWav, int numChannels, int* vagLen, void** vagData); +int wav2vag(const char* fWav, uint* len, void** vagData, const char* vagName); diff --git a/vsmx.c b/vsmx.c index 293c605..2176d39 100644 --- a/vsmx.c +++ b/vsmx.c @@ -1,1573 +1,1573 @@ - -#include -#include -#include -#include -#include "general.h" -#include "vsmx.h" - - -#if defined(MINGW) || defined(_MSC_VER) // lack of wcscasecmp -#include -int wcscasecmp(const wchar_t *s1, const wchar_t *s2) { - while(*s1 && *s2) { - int diff = towlower(*s1) - towlower(*s2); - if(diff) - return diff; - s1++; - s2++; - } - return towlower(*s1) - towlower(*s2); -} -#endif -// following doesn't seem to work - probably the define is required in the project -/* #ifdef _MSC_VER -#define _CRT_SECURE_NO_WARNINGS // get rid of MSVC's annoying security stuff -#define _CRT_NON_CONFORMING_SWPRINTFS // urgh... seems that mingw uses bad swprintf >_> -#endif */ - - -// since swprintf seems to accept different arguments... -#ifdef MINGW -#define SWPRINTF(s, l, f, ...) swprintf(s, f, __VA_ARGS__) -#else -#define SWPRINTF(s, l, f, ...) swprintf(s, l, f, __VA_ARGS__) -#endif - - -enum { - VID_NOTHING = 0x0, // dummy - VID_OPERATOR_ASSIGN = 0x1, - VID_OPERATOR_ADD = 0x2, - VID_OPERATOR_SUBTRACT = 0x3, // guess - VID_OPERATOR_MULTIPLY = 0x4, - VID_OPERATOR_DIVIDE = 0x5, // guess - VID_UNK_6 = 0x6, - - VID_OPERATOR_NEGATE = 0x8, - VID_OPERATOR_NOT = 0x9, - - - VID_INCREMENT = 0xc, - VID_DECREMENT = 0xd, // guess - VID_OPERATOR_EQUAL = 0xe, - VID_OPERATOR_NOT_EQUAL = 0xf, - VID_OPERATOR_CASE_LABEL = 0x10, // same as VID_OPERATOR_EQUAL, just used in switch statements - - VID_OPERATOR_LT = 0x12, - VID_OPERATOR_LTE = 0x13, - VID_OPERATOR_GT = 0x14, - VID_OPERATOR_GTE = 0x15, - - - VID_UNK_1A = 0x1a, - - - VID_STACK_PUSH = 0x20, - - VID_END_STMT = 0x22, - VID_CONST_NULL = 0x23, - VID_CONST_EMPTYARRAY = 0x24, - VID_CONST_BOOL = 0x25, - VID_CONST_INT = 0x26, - VID_CONST_FLOAT = 0x27, - VID_CONST_STRING = 0x28, - - VID_FUNCTION = 0x2a, - VID_ARRAY = 0x2b, // start an array constant - VID_THIS = 0x2c, - VID_UNNAMED_VAR = 0x2d, - VID_VARIABLE = 0x2e, - VID_PROPERTY = 0x2f, - VID_METHOD = 0x30, - VID_UNK_31 = 0x31, // appears to be an object set; pops last two items off the stack - VID_UNSET = 0x32, // guess; looks like above, but only with one item - - VID_ARRAY_INDEX = 0x34, - - VID_ARRAY_INDEX_ASSIGN = 0x36, - - VID_ARRAY_ELEM = 0x38, // push something into array constant - VID_SECT_START = 0x39, // jump statement; can indicate end of function, end of else/for, or return to beginning of loop - VID_JUMP_TRUE = 0x3a, // jump if previous value is true - VID_JUMP_FALSE = 0x3b, - VID_CALL_FUNC = 0x3c, - VID_CALL_METHOD = 0x3d, - VID_CALL_INBUILT = 0x3e, - VID_RETURN = 0x3f, - - - VID_END = 0x45, - - - VID_MAKE_FLOAT_ARRAY = 0x49 // weird?? -}; - -const wchar VsmxDecOps[][50] = { - L"UNKNOWN_0", - L"ASSIGN", - L"ADD", - L"SUBTRACT", - L"MULTIPLY", - L"DIVIDE", - L"UNK_6", - L"UNKNOWN_7", - L"NEGATE", - L"NOT", - L"UNKNOWN_A", - L"UNKNOWN_B", - L"INCREMENT", - L"DECREMENT", - L"TEST_EQUAL", - L"TEST_NOT_EQUAL", - L"TEST_CASE_EQUAL", - L"UNKNOWN_11", - L"TEST_LESS_THAN", - L"TEST_LESS_EQUAL_THAN", - L"TEST_MORE_THAN", - L"TEST_MORE_EQUAL_THAN", - L"UNKNOWN_16", - L"UNKNOWN_17", - L"UNKNOWN_18", - L"UNKNOWN_19", - L"UNK_1A", - L"UNKNOWN_1B", - L"UNKNOWN_1C", - L"UNKNOWN_1D", - L"UNKNOWN_1E", - L"UNKNOWN_1F", - L"STACK_PUSH", - L"UNKNOWN_21", - L"END_STATEMENT", - L"CONST_NULL", - L"CONST_EMPTY_ARRAY", - L"CONST_BOOL", - L"CONST_INT", - L"CONST_FLOAT", - L"CONST_STRING", - L"UNKNOWN_29", - L"FUNCTION", - L"CONST_ARRAY", - L"THIS_OBJECT", - L"UNNAMED_VARIABLE", - L"NAME", - L"PROPERTY", - L"METHOD", - L"SET", - L"UNSET", - L"UNKNOWN_33", - L"ARRAY_INDEX", - L"UNKNOWN_35", - L"ARRAY_INDEX_ASSIGN", - L"UNKNOWN_37", - L"ARRAY_PUSH", - L"JUMP", - L"JUMP_IF_TRUE", - L"JUMP_IF_FALSE", - L"CALL_FUNCTION", - L"CALL_METHOD", - L"CALL_INBUILT", - L"RETURN", - L"UNKNOWN_40", - L"UNKNOWN_41", - L"UNKNOWN_42", - L"UNKNOWN_43", - L"UNKNOWN_44", - L"END_SCRIPT", - L"UNKNOWN_46", - L"UNKNOWN_47", - L"UNKNOWN_48", - L"MAKE_FLOAT_ARRAY" -}; -const unsigned int VSMX_NUM_DEC_OPS = 0x49+1; - - - - -static inline wchar_t* strwcpy(wchar_t* dest, const char* src, unsigned int maxLen) { - while(--maxLen && (*dest = (wchar_t)(*src))) { - dest++; - src++; - } - *dest = 0; // ensure last char is null - return dest; -} - -VsmxMem* readVSMX(FILE* fp) { - - VSMXHeader header; - uint filePos; - VsmxMem* out; - - fileread(fp, &header, sizeof(header)); - if(header.sig != VSMX_SIGNATURE || header.ver != VSMX_VERSION) { - error("Not a valid VSMX file."); - return NULL; - } - - if(header.codeOffset != sizeof(header)) { - warning("Skipping range 0x%x-0x%x", sizeof(header), header.codeOffset); - fseek(fp, header.codeOffset, SEEK_SET); - } - - if(header.codeLength % sizeof(VSMXGroup)) { - error("Code size not aligned to 8 bytes."); - return NULL; - } - out = (VsmxMem*)malloc(sizeof(VsmxMem)); - - // TODO: check lengths are in sane ranges - out->code = (VSMXGroup*)malloc(header.codeLength); - fileread(fp, out->code, header.codeLength); - out->codeGroups = header.codeLength / sizeof(VSMXGroup); - - filePos = header.codeOffset + header.codeLength; - - #define READ_TEXT_SECTION(to, tl, te, typ, var, pVar, nam) \ - if(tl || te) { \ - if(to != filePos) { \ - warning("Skipping range 0x%x-0x%x", filePos, to); \ - fseek(fp, to, SEEK_SET); \ - } \ - if(!tl) { \ - error("Number of " nam " entries > 1 but length of data is 0!"); \ - free(out); \ - return NULL; \ - } \ - if(!te) { \ - error("Number of " nam " entries = 0 but length of data is > 0!"); \ - free(out); \ - return NULL; \ - } \ - if(tl % sizeof(typ)) { \ - error("Size of " nam " not aligned to %d byte(s).", sizeof(typ)); \ - free(out); \ - return NULL; \ - } \ - \ - var = (typ *)malloc(tl +sizeof(typ)); \ - fileread(fp, var, tl); \ - var[tl / sizeof(typ)] = 0; \ - \ - pVar = (typ **)malloc(te * sizeof(typ *)); \ - pVar[0] = var; \ - \ - uint pti = 1, ci; \ - for(ci = 1; ci < tl / sizeof(typ); ci++) { \ - if(var[ci-1] == 0) { \ - if(pti == te) { \ - error("Number of " nam " entries found exceeds number specified in header!"); \ - free(out); \ - return NULL; \ - } \ - pVar[pti++] = var + ci; \ - } \ - } \ - if(pti != te) { \ - error("Number of " nam " entries found is less than number specified in header!"); \ - free(out); \ - return NULL; \ - } \ - \ - filePos += tl; \ - } - - READ_TEXT_SECTION(header.textOffset, header.textLength, header.textEntries, wchar, out->text, out->pText, "text"); - READ_TEXT_SECTION(header.propOffset, header.propLength, header.propEntries, wchar, out->prop, out->pProp, "properties"); - READ_TEXT_SECTION(header.namesOffset, header.namesLength, header.namesEntries, char, out->names, out->pNames, "names"); - - out->numText = header.textEntries; - out->lenText = header.textLength; - out->numProp = header.propEntries; - out->lenProp = header.propLength; - out->numNames = header.namesEntries; - out->lenNames = header.namesLength; - - fclose(fp); - - return out; -} - -// urgh, this is a bit of a duplicate of the above -// TODO: don't make this so crap -VsmxMem* readVSMXMem(const void *in) { - - VSMXHeader* header = (VSMXHeader*)in; - uint filePos; - VsmxMem* out; - - if(header->sig != VSMX_SIGNATURE || header->ver != VSMX_VERSION) { - error("Not a valid VSMX file."); - return NULL; - } - - if(header->codeOffset != sizeof(VSMXHeader)) { - warning("Skipping range 0x%x-0x%x", sizeof(VSMXHeader), header->codeOffset); - //fseek(fp, header->codeOffset, SEEK_SET); - } - - if(header->codeLength % sizeof(VSMXGroup)) { - error("Code size not aligned to 8 bytes."); - return NULL; - } - out = (VsmxMem*)malloc(sizeof(VsmxMem)); - - // TODO: check lengths are in sane ranges - out->code = (VSMXGroup*)malloc(header->codeLength); - memcpy(out->code, (uint8*)in + header->codeOffset, header->codeLength); - out->codeGroups = header->codeLength / sizeof(VSMXGroup); - - filePos = header->codeOffset + header->codeLength; - - #define READ_TEXT_SECTION_MEM(to, tl, te, typ, var, pVar, nam) \ - if(tl || te) { \ - if(to != filePos) { \ - warning("Skipping range 0x%x-0x%x", filePos, to); \ - filePos = to; \ - } \ - if(!tl) { \ - error("Number of " nam " entries > 1 but length of data is 0!"); \ - free(out); \ - return NULL; \ - } \ - if(!te) { \ - error("Number of " nam " entries = 0 but length of data is > 0!"); \ - free(out); \ - return NULL; \ - } \ - if(tl % sizeof(typ)) { \ - error("Size of " nam " not aligned to %d byte(s).", sizeof(typ)); \ - free(out); \ - return NULL; \ - } \ - \ - var = (typ *)malloc(tl +sizeof(typ)); \ - memcpy(var, (uint8*)in + filePos, tl); \ - var[tl / sizeof(typ)] = 0; \ - \ - pVar = (typ **)malloc(te * sizeof(typ *)); \ - pVar[0] = var; \ - \ - uint pti = 1, ci; \ - for(ci = 1; ci < tl / sizeof(typ); ci++) { \ - if(var[ci-1] == 0) { \ - if(pti == te) { \ - error("Number of " nam " entries found exceeds number specified in header!"); \ - free(out); \ - return NULL; \ - } \ - pVar[pti++] = var + ci; \ - } \ - } \ - if(pti != te) { \ - error("Number of " nam " entries found is less than number specified in header!"); \ - free(out); \ - return NULL; \ - } \ - \ - filePos += tl; \ - } - - READ_TEXT_SECTION_MEM(header->textOffset, header->textLength, header->textEntries, wchar, out->text, out->pText, "text"); - READ_TEXT_SECTION_MEM(header->propOffset, header->propLength, header->propEntries, wchar, out->prop, out->pProp, "properties"); - READ_TEXT_SECTION_MEM(header->namesOffset, header->namesLength, header->namesEntries, char, out->names, out->pNames, "names"); - - out->numText = header->textEntries; - out->lenText = header->textLength; - out->numProp = header->propEntries; - out->lenProp = header->propLength; - out->numNames = header->namesEntries; - out->lenNames = header->namesLength; - - return out; -} - -void writeVSMX(FILE* fp, VsmxMem* in) { - VSMXHeader header; - unsigned int pos = 0x34; - header.sig = VSMX_SIGNATURE; - header.ver = VSMX_VERSION; - header.codeOffset = pos; - header.codeLength = in->codeGroups * sizeof(VSMXGroup); - pos += header.codeLength; - header.textOffset = pos; - header.textLength = in->lenText; - header.textEntries = in->numText; - pos += header.textLength; - header.propOffset = pos; - header.propLength = in->lenProp; - header.propEntries = in->numProp; - pos += header.propLength; - header.namesOffset = pos; - header.namesLength = in->lenNames; - header.namesEntries = in->numNames; - - fwrite(&header, sizeof(header), 1, fp); - - if(in->code) - fwrite(in->code, header.codeLength, 1, fp); - if(in->text) - fwrite(in->text, header.textLength, 1, fp); - if(in->prop) - fwrite(in->prop, header.propLength, 1, fp); - if(in->names) - fwrite(in->names, header.namesLength, 1, fp); -} -// like the read thing above, this sucks -void* writeVSMXMem(unsigned int* len, VsmxMem* in) { - *len = sizeof(VSMXHeader) + in->codeGroups * sizeof(VSMXGroup) + in->lenText + in->lenProp + in->lenNames; - void* ret = malloc(*len); - - VSMXHeader* header = (VSMXHeader*)ret; - unsigned int pos = sizeof(VSMXHeader); - header->sig = VSMX_SIGNATURE; - header->ver = VSMX_VERSION; - header->codeOffset = pos; - header->codeLength = in->codeGroups * sizeof(VSMXGroup); - pos += header->codeLength; - header->textOffset = pos; - header->textLength = in->lenText; - header->textEntries = in->numText; - pos += header->textLength; - header->propOffset = pos; - header->propLength = in->lenProp; - header->propEntries = in->numProp; - pos += header->propLength; - header->namesOffset = pos; - header->namesLength = in->lenNames; - header->namesEntries = in->numNames; - - pos = sizeof(VSMXHeader); - if(in->code) { - memcpy((uint8*)ret + pos, in->code, header->codeLength); - pos += header->codeLength; - } - if(in->text) { - memcpy((uint8*)ret + pos, in->text, header->textLength); - pos += header->textLength; - } - if(in->prop) { - memcpy((uint8*)ret + pos, in->prop, header->propLength); - pos += header->propLength; - } - if(in->names) { - memcpy((uint8*)ret + pos, in->names, header->namesLength); - pos += header->namesLength; - } - return ret; -} - -// macro used in both decode and decompile functions -#define CHECK_INDEX(num,nam) \ - if(in->code[i].val.u32 >= num) { \ - error("Invalid " nam " index 0x%x at group %d!", in->code[i].val.u32, i); \ - return 1; \ - } - -int VsmxDecode(VsmxMem* in, FILE* out) { - uint i; - - fputws(L"; Decoded VSMX file written by " APPNAME_VER "\n\n", out); - - for(i=0; i < in->codeGroups; i++) { - if((in->code[i].id & 0xFF) <= VSMX_NUM_DEC_OPS) { - fputws(VsmxDecOps[in->code[i].id & 0xFF], out); - //fputwc(L' ', out); - } else - fwprintf(out, L"UNKNOWN_%x", in->code[i].id); - - switch(in->code[i].id & 0xFF) { - - case VID_CONST_BOOL: - if(in->code[i].val.u32 == 1) - fputws(L" true", out); - else if(in->code[i].val.u32 == 0) - fputws(L" false", out); - else { - warning("Unexpected boolean value 0x%x at line %d!", in->code[i].val.u32, i+1); - fwprintf(out, L" 0x%x", in->code[i].val.u32); - } - break; - case VID_CONST_INT: - fwprintf(out, L" %u", in->code[i].val.u32); - break; - case VID_CONST_FLOAT: - fwprintf(out, L" %#g", in->code[i].val.f); - break; - case VID_CONST_STRING: - CHECK_INDEX(in->numText, "text"); - fwprintf(out, L" \"%ls\"", in->pText[in->code[i].val.u32]); - break; - case VID_VARIABLE: { - // for some reason, the %s modifier in fwprintf doesn't work properly... :/ so we need to convert to a wide char string - wchar *tmp; - uint tmpLen = strlen(in->pNames[in->code[i].val.u32]); - CHECK_INDEX(in->numNames, "name"); - tmp = (wchar*)malloc((tmpLen+1) * sizeof(wchar)); - strwcpy(tmp, in->pNames[in->code[i].val.u32], tmpLen+1); - fwprintf(out, L" %ls", tmp); - free(tmp); - } break; - case VID_PROPERTY: - case VID_METHOD: - case VID_UNK_31: - case VID_UNSET: - CHECK_INDEX(in->numProp, "property"); - fwprintf(out, L" %ls", in->pProp[in->code[i].val.u32]); - break; - case VID_FUNCTION: - if(in->code[i].id >> 16 & 0xFF) - warning("Unexpected flag value for function at line %d, expected 0, got %d", i+1, in->code[i].id >> 16 & 0xFF); - fwprintf(out, L" args=%u, flag=%u, start_line=%u", (in->code[i].id >> 8) & 0xFF, (in->code[i].id >> 24) & 0xFF, in->code[i].val.u32+1); - break; - case VID_UNNAMED_VAR: - fwprintf(out, L" %u", in->code[i].val.u32); - break; - - // jumps - case VID_SECT_START: - case VID_JUMP_TRUE: - case VID_JUMP_FALSE: - fwprintf(out, L" line=%u", in->code[i].val.u32+1); - break; - - // function calls - case VID_CALL_FUNC: - case VID_CALL_METHOD: - case VID_CALL_INBUILT: - fwprintf(out, L" args=%u", in->code[i].val.u32); - break; - - case VID_MAKE_FLOAT_ARRAY: - fwprintf(out, L" items=%u", in->code[i].val.u32); - break; - - // ops w/o arg - check for zero - case VID_OPERATOR_ASSIGN: - case VID_OPERATOR_ADD: - case VID_OPERATOR_SUBTRACT: - case VID_OPERATOR_MULTIPLY: - case VID_OPERATOR_DIVIDE: - case VID_UNK_6: - case VID_OPERATOR_NEGATE: - case VID_OPERATOR_NOT: - case VID_INCREMENT: - case VID_DECREMENT: - case VID_OPERATOR_EQUAL: - case VID_OPERATOR_NOT_EQUAL: - case VID_OPERATOR_CASE_LABEL: - case VID_OPERATOR_LT: - case VID_OPERATOR_LTE: - case VID_OPERATOR_GT: - case VID_OPERATOR_GTE: - case VID_UNK_1A: - case VID_STACK_PUSH: - case VID_END_STMT: - case VID_CONST_NULL: - case VID_CONST_EMPTYARRAY: - case VID_ARRAY: - case VID_THIS: - case VID_ARRAY_INDEX: - case VID_ARRAY_INDEX_ASSIGN: - case VID_ARRAY_ELEM: - case VID_RETURN: - case VID_END: - if(in->code[i].val.u32) - warning("Unexpected non-zero value at line %d!", i+1); - - break; - - default: - warning("Unknown ID 0x%x at line %d", in->code[i].id, i+1); - fwprintf(out, L" 0x%x", in->code[i].id, in->code[i].val.u32); - } - fputwc(L'\n', out); - } - return 0; -} - -// !! doesn't properly fix up pText !! -unsigned int VsmxAddText(void** text, unsigned int** offs, unsigned int* textLen, unsigned int* numText, wchar* newText, int charWidth) { - // first, see if we already have this text - unsigned int p = 0; - unsigned int len = wcslen(newText); - - char* newTextByte = NULL; - //wprintf(L"adding text %ls\n", newText); - //fflush(stdout); - if(charWidth == 1) { // convert wchar to char - char *ptr = (char*)malloc(len +1); - newTextByte = ptr; - if(!ptr) { - error("malloc failed"); - exit(1); - } - while((*ptr = (char)(*newText))) { - ptr++; - newText++; - } - } - - for(p=0; p < *numText; p++) { - Bool cond; - if(charWidth == 1) { - cond = strcmp(*(char**)text + (*offs)[p], newTextByte); - } - else - cond = wcscmp(*(wchar**)text + (*offs)[p] / charWidth, newText); - if(!cond) { - if(newTextByte) free(newTextByte); - return p; // found - } - } - - // don't have it? add it - (*numText)++; - *text = realloc(*text, *textLen + (len+1)*charWidth); - *offs = (unsigned int*)realloc(*offs, sizeof(unsigned int) * (*numText)); - if(charWidth == 1) { - strcpy(*(char**)text + *textLen, newTextByte); - } else { - wcscpy(*(wchar**)text + *textLen / charWidth, newText); - } - (*offs)[*numText -1] = *textLen; - *textLen += (len+1)*charWidth; - - if(newTextByte) free(newTextByte); - return *numText-1; -} - - -VsmxMem* VsmxEncode(FILE* in) { - wchar buf[2048]; - unsigned int lineCount = 1; - VsmxMem* ret = (VsmxMem*)malloc(sizeof(VsmxMem)); - unsigned int *oText = NULL, *oProp = NULL, *oNames = NULL; - - ret->codeGroups = ret->numText = ret->numProp = ret->numNames = ret->lenText = ret->lenProp = ret->lenNames = 0; - ret->code = NULL; - ret->text = NULL; - ret->prop = NULL; - ret->names = NULL; - ret->pText = NULL; - ret->pProp = NULL; - ret->pNames = NULL; - - while(fgetws(buf, 2048, in)) { - wchar *op = buf, *arg = NULL, *tmp; - int opNum = -1; - unsigned int opNum2, argNum = 0; - unsigned int lineLen; - // trim line - #define IS_WHITESPACE(x) (x == L'\t' || x == L' ' || x == L'\n' || x == L'\r') - while(*op && IS_WHITESPACE(*op)) - op++; - /* code is BAD - can remove stuff if in a string! - // don't forget to remove comments! - tmp = op; - while(*tmp) { - if(*tmp == L';') { - *tmp = L'\0'; - break; - } - tmp++; - } - */ - lineLen = wcslen(op); - while(lineLen && IS_WHITESPACE(op[lineLen-1])) - lineLen--; - op[lineLen] = L'\0'; - - if(lineLen == 0) continue; - if(op[0] == L';') continue; // comment line - - // find space, if any - tmp = op; - while(*(++tmp)) - if(*tmp == L' ') { - arg = tmp+1; - *tmp = L'\0'; - while(*arg && *arg == L' ') - arg++; - break; - } - - // determine op - if(swscanf(op, L"UNKNOWN_%x", &opNum) != 1) { - unsigned int i; - for(i=0; itext, &oText, &ret->lenText, &ret->numText, arg, sizeof(*(ret->text))); - } - } break; - case VID_VARIABLE: - argNum = VsmxAddText((void**)&ret->names, &oNames, &ret->lenNames, &ret->numNames, arg, sizeof(*(ret->names))); - break; - case VID_PROPERTY: - case VID_METHOD: - case VID_UNK_31: - case VID_UNSET: - argNum = VsmxAddText((void**)&ret->prop, &oProp, &ret->lenProp, &ret->numProp, arg, sizeof(*(ret->prop))); - break; - - case VID_FUNCTION: { - int iArgs, iFlag; - // TODO: better parser here - swscanf(arg, L"args=%u, flag=%u, start_line=%u", &iArgs, &iFlag, &argNum); - argNum--; - opNum2 = ((iFlag & 0xFF) << 24) | ((iArgs & 0xFF) << 8) | opNum2; - } break; - case VID_SECT_START: - case VID_JUMP_TRUE: - case VID_JUMP_FALSE: - swscanf(arg, L"line=%u", &argNum); - argNum--; - break; - case VID_CALL_FUNC: - case VID_CALL_METHOD: - case VID_CALL_INBUILT: - swscanf(arg, L"args=%u", &argNum); - break; - - case VID_MAKE_FLOAT_ARRAY: - swscanf(arg, L"items=%u", &argNum); - break; - - // ops w/o arg - check for zero - case VID_OPERATOR_ASSIGN: - case VID_OPERATOR_ADD: - case VID_OPERATOR_SUBTRACT: - case VID_OPERATOR_MULTIPLY: - case VID_OPERATOR_DIVIDE: - case VID_UNK_6: - case VID_OPERATOR_NEGATE: - case VID_OPERATOR_NOT: - case VID_INCREMENT: - case VID_DECREMENT: - case VID_OPERATOR_EQUAL: - case VID_OPERATOR_NOT_EQUAL: - case VID_OPERATOR_CASE_LABEL: - case VID_OPERATOR_LT: - case VID_OPERATOR_LTE: - case VID_OPERATOR_GT: - case VID_OPERATOR_GTE: - case VID_UNK_1A: - case VID_STACK_PUSH: - case VID_END_STMT: - case VID_CONST_NULL: - case VID_CONST_EMPTYARRAY: - case VID_ARRAY: - case VID_THIS: - case VID_ARRAY_INDEX: - case VID_ARRAY_INDEX_ASSIGN: - case VID_ARRAY_ELEM: - case VID_RETURN: - case VID_END: - if(arg) warning("[line %d] Operator does not have value.", lineCount); - break; - - default: - swscanf(arg, L"%i", &argNum); - } - - ret->codeGroups++; - ret->code = (VSMXGroup*)realloc(ret->code, ret->codeGroups * sizeof(VSMXGroup)); - ret->code[ret->codeGroups-1].id = opNum2; - ret->code[ret->codeGroups-1].val.u32 = argNum; - - lineCount++; - } - - if(oText) { - unsigned int i; - ret->pText = (wchar**)malloc(ret->numText * sizeof(wchar*)); - for(i=0; inumText; i++) - ret->pText[i] = ret->text + oText[i]; - free(oText); - } - if(oProp) { - unsigned int i; - ret->pProp = (wchar**)malloc(ret->numProp * sizeof(wchar*)); - for(i=0; inumProp; i++) - ret->pProp[i] = ret->prop + oProp[i]; - free(oProp); - } - if(oNames) { - unsigned int i; - ret->pNames = (char**)malloc(ret->numNames * sizeof(char*)); - for(i=0; inumNames; i++) - ret->pNames[i] = ret->names + oNames[i]; - free(oNames); - } - - return ret; -} - -#define MAX_TEXT_LEN 4096 -typedef struct __VsmxDecompileStack VsmxDecompileStack; - -struct __VsmxDecompileStack { - wchar str[MAX_TEXT_LEN]; // a bit of RAM wastage perhaps - VSMXGroup item; - int arrayFlag; - - VsmxDecompileStack* prev; - uint depth; -}; - -static inline void VsmxDecompileStackPush(VsmxDecompileStack** stack, VsmxDecompileStack* item) { - VsmxDecompileStack* newItem = (VsmxDecompileStack*)malloc(sizeof(VsmxDecompileStack)); - *newItem = *item; - newItem->prev = *stack; - if(*stack) newItem->depth = (*stack)->depth + 1; - else newItem->depth = 0; - *stack = newItem; -} -static inline VsmxDecompileStack* VsmxDecompileStackPop(VsmxDecompileStack** stack) { - if(!(*stack)) { - error("Stack underflow occurred!"); - return NULL; - } - VsmxDecompileStack* theItem = *stack; - *stack = (*stack)->prev; - return theItem; -} -static inline void VsmxDecompileStackDestroy(VsmxDecompileStack** stack) { - if(!*stack) return; - while(*stack) { - VsmxDecompileStack* tmp = (*stack)->prev; - free(*stack); - *stack = tmp; - } -} - -typedef struct __VsmxDecompMarkStack VsmxDecompMarkStack; - -struct __VsmxDecompMarkStack { - uint loc; - uint src; - - VsmxDecompMarkStack* prev; - uint depth; -}; - -static inline void VsmxDecompMarkStackPush(VsmxDecompMarkStack** stack, VsmxDecompMarkStack* item) { - VsmxDecompMarkStack* newItem = (VsmxDecompMarkStack*)malloc(sizeof(VsmxDecompMarkStack)); - *newItem = *item; - newItem->prev = *stack; - if(*stack) { - newItem->depth = (*stack)->depth + 1; - if(newItem->loc > (*stack)->loc) { - warning("Bad nesting hierachy detected!"); - // well, what to do? Just switch them around I guess... - uint tmp = newItem->loc; - newItem->loc = (*stack)->loc; - (*stack)->loc = tmp; - } - } - else newItem->depth = 0; - *stack = newItem; -} -static inline VsmxDecompMarkStack* VsmxDecompMarkStackPop(VsmxDecompMarkStack** stack) { - if(!(*stack)) { - error("Marker stack underflow occurred!"); - return NULL; - } - VsmxDecompMarkStack* theItem = *stack; - *stack = (*stack)->prev; - return theItem; -} -static inline void VsmxDecompMarkStackDestroy(VsmxDecompMarkStack** stack) { - if(!*stack) return; - while(*stack) { - VsmxDecompMarkStack* tmp = (*stack)->prev; - free(*stack); - *stack = tmp; - } -} - -static inline void writeTabs(FILE* out, uint num) { - while(num--) - fputwc('\t', out); -} - -int VsmxDecompile(VsmxMem* in, FILE* out) { - uint i, indent=0, stmtStart=0; - int indentAdd=0; - VsmxDecompileStack* stack = NULL; - VsmxDecompileStack item; - VsmxDecompMarkStack* mStack = NULL; - VsmxDecompMarkStack mItem; - uint endStmtConcat = 1; - - uint forStmtEnd = 0; - - - fputws(L"// Decompiled VSMX -> Javascript output by " APPNAME_VER "\n//Note, this is highly experimental and the output probably sucks.\n\n", out); - - for(i=0; i < in->codeGroups; i++) { - item.str[0] = 0; - item.arrayFlag = 0; - - item.item.id = 0; - item.item.val.u32 = 0; - - // TODO: check that in->code[i].val is 0 for various things - - - if(in->code[i].id >> 8 && (in->code[i].id & 0xFF) != VID_FUNCTION) { - warning("Unexpected flags 0x%x for id 0x%x at %d", in->code[i].id >> 8, in->code[i].id & 0xFF, i); - } - - - // check for markers - while(mStack && mStack->loc == i) { - if(stack) { - // inline marker - - // TODO: should we really concatenate here? - while(endStmtConcat--) { - VsmxDecompileStack *prev; - prev = VsmxDecompileStackPop(&stack); - wcscat(prev->str, item.str); - wcscpy(item.str, prev->str); - free(prev); - } - endStmtConcat = 1; - - if(in->code[mStack->src].id == VID_JUMP_FALSE) { - // check for special and operation - if(in->code[i].id == VID_JUMP_FALSE) { - // this upcomming if acts as an AND - we'll display this as a nested if though - mStack->loc = in->code[i].val.u32; - // duplicate this marker as a copy will get popped later - VsmxDecompMarkStackPush(&mStack, mStack); - wcscat(item.str, L" /* AND condition shown as nested if */ "); - } else { // inline if which doesn't have an else - wcscat(item.str, L" : false )"); - } - } - else - wcscat(item.str, L" )"); - VsmxDecompileStackPush(&stack, &item); - item.str[0] = 0; - } else { - if(indent > 0) indent--; // this should always be true - else warning("Internal state nesting error!"); - - // TODO: remove debugging line - fwprintf(out, L"/*%d*/\t", stmtStart); - - writeTabs(out, indent); - fwprintf(out, L"}\n"); - } - - VsmxDecompMarkStackPop(&mStack); - } - - - // stuff we'll use later - wchar op[50] = {0}; - Bool notSectStart = FALSE; - /* #ifdef MINGW - #define SWPRINTF_ITEM(s, ...) swprintf(item.str, L##s, __VA_ARGS__) - #else - #define SWPRINTF_ITEM(s, ...) swprintf(item.str, MAX_TEXT_LEN, L##s, __VA_ARGS__) - #endif */ - #define SWPRINTF_ITEM(s, ...) SWPRINTF(item.str, MAX_TEXT_LEN, L##s, __VA_ARGS__) - //#define SWPRINTF_ITEM(s, ...) swprintf(item.str, MAX_TEXT_LEN, L s, __VA_ARGS__) - - switch(in->code[i].id & 0xFF) { - - // binary operators - case VID_OPERATOR_ASSIGN: if(!op[0]) wcscpy(op, L"="); - case VID_OPERATOR_ADD: if(!op[0]) wcscpy(op, L"+"); - case VID_OPERATOR_SUBTRACT: if(!op[0]) wcscpy(op, L"-"); - case VID_OPERATOR_MULTIPLY: if(!op[0]) wcscpy(op, L"*"); - case VID_OPERATOR_DIVIDE: if(!op[0]) wcscpy(op, L"/"); - case VID_UNK_6: if(!op[0]) wcscpy(op, L""); // TODO: fix dummy - case VID_OPERATOR_EQUAL: if(!op[0]) wcscpy(op, L"=="); - case VID_OPERATOR_NOT_EQUAL: if(!op[0]) wcscpy(op, L"!="); - case VID_OPERATOR_LT: if(!op[0]) wcscpy(op, L"<"); - case VID_OPERATOR_LTE: if(!op[0]) wcscpy(op, L"<="); - case VID_OPERATOR_GT: if(!op[0]) wcscpy(op, L">"); - case VID_OPERATOR_GTE: if(!op[0]) wcscpy(op, L">="); - case VID_UNK_1A: if(!op[0]) wcscpy(op, L""); // TODO: fix dummy - { // TODO: need to consider when to use brackets - VsmxDecompileStack *left, *right; - right = VsmxDecompileStackPop(&stack); - left = VsmxDecompileStackPop(&stack); - SWPRINTF_ITEM("%ls %ls %ls", left->str, op, right->str); - //swprintf(item.str, MAX_TEXT_LEN, L"%s %s %s", left->str, op, right->str); - VsmxDecompileStackPush(&stack, &item); - free(left); free(right); - } break; - - - case VID_OPERATOR_NEGATE: if(!op[0]) wcscpy(op, L"-"); - case VID_OPERATOR_NOT: if(!op[0]) wcscpy(op, L"!"); - { - VsmxDecompileStack *prev; - prev = VsmxDecompileStackPop(&stack); - //swprintf(item.str, MAX_TEXT_LEN, L"-(%s)", prev->str); - SWPRINTF_ITEM("%ls(%ls)", op, prev->str); - VsmxDecompileStackPush(&stack, &item); - free(prev); - } break; - - case VID_INCREMENT: if(!op[0]) wcscpy(op, L"++"); - case VID_DECREMENT: if(!op[0]) wcscpy(op, L"--"); - { - VsmxDecompileStack *prev; - prev = VsmxDecompileStackPop(&stack); - //swprintf(item.str, MAX_TEXT_LEN, L"-(%s)", prev->str); - SWPRINTF_ITEM("(%ls)%ls", prev->str, op); - VsmxDecompileStackPush(&stack, &item); - free(prev); - } break; - - - // constants & variables - case VID_CONST_NULL: - wcscpy(item.str, L"null"); - VsmxDecompileStackPush(&stack, &item); - break; - case VID_CONST_EMPTYARRAY: - wcscpy(item.str, L"[]"); - VsmxDecompileStackPush(&stack, &item); - break; - - //case VID_CONST_TIME: break; // TODO: - case VID_CONST_BOOL: - if(in->code[i].val.u32 == 0) - wcscpy(item.str, L"false"); - else { - wcscpy(item.str, L"true"); - if(in->code[i].val.u32 != 1) - warning("Boolean value at group #%d is not 0 or 1.", i); - } - VsmxDecompileStackPush(&stack, &item); - break; - - case VID_CONST_INT: - //swprintf(item.str, MAX_TEXT_LEN, L"%u", in->code[i].val.u32); - SWPRINTF_ITEM("%u", in->code[i].val.u32); - VsmxDecompileStackPush(&stack, &item); - break; - case VID_CONST_FLOAT: - //swprintf(item.str, MAX_TEXT_LEN, L"%f", *(float*)(&in->code[i].val.u32)); - SWPRINTF_ITEM("%#g", in->code[i].val.f); - VsmxDecompileStackPush(&stack, &item); - break; - case VID_CONST_STRING: { // TODO: unicode issues... :( - CHECK_INDEX(in->numText, "text"); - //swprintf(item.str, MAX_TEXT_LEN, L"\"%s\"", in->pText[in->code[i].val.u32]); - SWPRINTF_ITEM("\"%ls\"", in->pText[in->code[i].val.u32]); - VsmxDecompileStackPush(&stack, &item); - } break; - case VID_THIS: { - wcscpy(item.str, L"this"); - VsmxDecompileStackPush(&stack, &item); - } break; - case VID_UNNAMED_VAR: - //swprintf(item.str, MAX_TEXT_LEN, L"__GLOBALS__[%u]", in->code[i].val.u32); - SWPRINTF_ITEM("__var%u", in->code[i].val.u32); - VsmxDecompileStackPush(&stack, &item); - break; - case VID_VARIABLE: - CHECK_INDEX(in->numNames, "name"); - strwcpy(item.str, in->pNames[in->code[i].val.u32], MAX_TEXT_LEN); - VsmxDecompileStackPush(&stack, &item); - break; - - case VID_UNK_31: - CHECK_INDEX(in->numProp, "prop"); - if(stack && stack->depth >= 1) { // TODO: this is really not correct - VsmxDecompileStack *obj, *val; - val = VsmxDecompileStackPop(&stack); - obj = VsmxDecompileStackPop(&stack); - SWPRINTF_ITEM("%ls.%ls = %ls", obj->str, in->pProp[in->code[i].val.u32], val->str); - free(obj); - free(val); - } else - wcscpy(item.str, in->pProp[in->code[i].val.u32]); - VsmxDecompileStackPush(&stack, &item); - break; - - case VID_PROPERTY: - case VID_METHOD: - case VID_UNSET: - { - VsmxDecompileStack *prev; - prev = VsmxDecompileStackPop(&stack); - CHECK_INDEX(in->numProp, "property/method"); - //swprintf(item.str, MAX_TEXT_LEN, L"%s.%s", prev->str, in->pProp[in->code[i].val.u32]); - if(in->code[i].id == VID_UNSET) - SWPRINTF_ITEM("delete %ls.%ls", prev->str, in->pProp[in->code[i].val.u32]); - else - SWPRINTF_ITEM("%ls.%ls", prev->str, in->pProp[in->code[i].val.u32]); - VsmxDecompileStackPush(&stack, &item); - free(prev); - } break; - - - case VID_MAKE_FLOAT_ARRAY: - { - VsmxDecompileStack *prev; - uint j; - - for(j=0; jcode[i].val.u32; j++) { - prev = VsmxDecompileStackPop(&stack); - if(j) - SWPRINTF_ITEM(" %ls,%ls", prev->str, item.str); - else - SWPRINTF_ITEM(" %ls ", prev->str); - free(prev); - } - SWPRINTF_ITEM("[%ls]", item.str); - VsmxDecompileStackPush(&stack, &item); - } break; - - case VID_ARRAY: - wcscpy(item.str, L"[]"); - item.arrayFlag = 1; - VsmxDecompileStackPush(&stack, &item); - break; - case VID_ARRAY_ELEM: { - VsmxDecompileStack *array, *prev; - prev = VsmxDecompileStackPop(&stack); - array = VsmxDecompileStackPop(&stack); - if(!array->arrayFlag) { - warning("Array elem being pushed, but array not found at %d!", i); - } else if(array->arrayFlag == 1) { // first element - SWPRINTF_ITEM("[ %ls ]", prev->str); - item.arrayFlag = 2; - VsmxDecompileStackPush(&stack, &item); - } else { - uint aLen = wcslen(array->str); - if(aLen < 3) { - error("Internal array handling error at %d!", i); - return 1; - } - array->str[aLen - 2] = L'\0'; - SWPRINTF_ITEM("%ls, %ls ]", array->str, prev->str); - item.arrayFlag = 2; - VsmxDecompileStackPush(&stack, &item); - } - free(prev); - free(array); - } break; - - case VID_STACK_PUSH: { - // we only consider two possible uses for this - // 1. push for conditional - we'll ignore these - // 2. push for -= and += operators - - // !!! look ahead used! - if(i+1 < in->codeGroups && (in->code[i+1].id == VID_JUMP_FALSE || in->code[i+1].id == VID_JUMP_TRUE)) { - // 1st condition (hoping there isn't a more complex situation) - ignore - } else { - // 2nd, duplicate top of stack - VsmxDecompileStackPush(&stack, stack); - } - } break; - - - case VID_FUNCTION: { - // TODO: need to check marker stack for look aheads - // !!! note look ahead used! - Bool funcEndStmtStyle = (i+3 < in->codeGroups && in->code[i+2].id == VID_END_STMT && in->code[i+3].id == VID_SECT_START && in->code[i].val.u32 == i+4); - - wchar args[4096] = L""; // large enough for anything - uint numArgs = (in->code[i].id >> 8) & 0xFF, argI; - - if(in->code[i].id >> 16 & 0xFF) { - warning("Unexpected flag value for function at %d, expected 0, got %d", i, in->code[i].id >> 16 & 0xFF); - } - SWPRINTF(args, 4096, L"/*flag=%d*/", in->code[i].id >> 24); - if(numArgs) wcscat(args, L" "); - - for(argI = 0; argI < numArgs; argI++) { - if(argI) wcscat(args, L", "); - SWPRINTF(args + wcslen(args), 4096-wcslen(args), L"__var%d", argI+1); - } - - if(i+2 < in->codeGroups && in->code[i+1].id == VID_OPERATOR_ASSIGN && ( - funcEndStmtStyle || - (in->code[i+2].id == VID_SECT_START && in->code[i].val.u32 == i+3) - )) { - // abc = function() style? - - if(in->code[i+1].val.u32 || (funcEndStmtStyle && in->code[i+2].val.u32)) { - warning("Unexpected values in function definition style at %d", i); - } - - if(!stack) { - warning("Function encountered at %d, but no name found on stack!", i); - SWPRINTF_ITEM("function __unnamed_%d__(%ls)", i, args); - VsmxDecompileStackPush(&stack, &item); - } else if(stack->depth == 0) { - VsmxDecompileStack *prev; - prev = VsmxDecompileStackPop(&stack); - //SWPRINTF_ITEM("function %ls(%ls)", prev->str, args); - SWPRINTF_ITEM("%ls = function(%ls)", prev->str, args); - VsmxDecompileStackPush(&stack, &item); - free(prev); - } else if(stack->depth == 1) { // weird... - VsmxDecompileStack *prev, *var; - prev = VsmxDecompileStackPop(&stack); - var = VsmxDecompileStackPop(&stack); - SWPRINTF_ITEM("%ls = function %ls(%ls)", var->str, prev->str, args); - VsmxDecompileStackPush(&stack, &item); - free(prev); free(var); - } else { // weird... - warning("Function encountered at %d, but more than one item found on stack!", i); - VsmxDecompileStack *prev; - prev = VsmxDecompileStackPop(&stack); - SWPRINTF_ITEM("%ls = function(%ls)", prev->str, args); - VsmxDecompileStackPush(&stack, &item); - free(prev); - } - if(funcEndStmtStyle) - i += 2; - else - i++; - } else if(i+1 < in->codeGroups && in->code[i+1].id == VID_SECT_START && in->code[i].val.u32 == i+2) { - // function abc() style? - VsmxDecompileStack *prev; - prev = VsmxDecompileStackPop(&stack); - SWPRINTF_ITEM("function %ls(%ls)", prev->str, args); - VsmxDecompileStackPush(&stack, &item); - free(prev); - - } - else { - warning("Unexpected function definition syntax at %d!", i); - SWPRINTF_ITEM("function __%u__(%ls)", in->code[i].val.u32, args); - VsmxDecompileStackPush(&stack, &item); - } - } break; - - case VID_ARRAY_INDEX: { - VsmxDecompileStack *idx, *parent; - // TODO: check that the number supplied in in->code[i].val.u32 isn't used (always is 0)! - idx = VsmxDecompileStackPop(&stack); - parent = VsmxDecompileStackPop(&stack); - SWPRINTF_ITEM("%ls[%ls]", parent->str, idx->str); - VsmxDecompileStackPush(&stack, &item); - free(idx); - free(parent); - } break; - case VID_ARRAY_INDEX_ASSIGN: { - VsmxDecompileStack *val, *idx, *parent; - val = VsmxDecompileStackPop(&stack); - idx = VsmxDecompileStackPop(&stack); - parent = VsmxDecompileStackPop(&stack); - SWPRINTF_ITEM("%ls[%ls] = %ls", parent->str, idx->str, val->str); - VsmxDecompileStackPush(&stack, &item); - free(val); - free(idx); - free(parent); - } break; - - case VID_CALL_FUNC: - case VID_CALL_METHOD: - case VID_CALL_INBUILT: - { - if(stack->depth < in->code[i].val.u32) { - error("Not enough arguments to perform requested function call at group %d.", i); - return 1; - } - VsmxDecompileStack *prev; - if(in->code[i].val.u32 > 0) { - uint arg; - strwcpy(item.str, " )", MAX_TEXT_LEN); - for(arg = 0; arg < in->code[i].val.u32; arg++) { - prev = VsmxDecompileStackPop(&stack); - //swprintf(item.str, MAX_TEXT_LEN, L"%s%s", prev->str, item.str); - //SWPRINTF_ITEM("%ls%ls", prev->str, item.str); - if(arg+1 < in->code[i].val.u32) { - //swprintf(item.str, MAX_TEXT_LEN, L", %s", item.str); - //SWPRINTF_ITEM(", %ls", item.str); - memmove(prev->str + 2, prev->str, (wcslen(prev->str)+1)*sizeof(wchar)); - //wcscat(prev->str, L", "); - prev->str[0] = ','; - prev->str[1] = ' '; - wcscat(prev->str, item.str); - } else - wcscat(prev->str, item.str); - wcscpy(item.str, prev->str); - free(prev); - } - // function name - prev = VsmxDecompileStackPop(&stack); - //swprintf(item.str, MAX_TEXT_LEN, L"%s( %s", prev->str, item.str); - //SWPRINTF_ITEM("%ls( %ls", prev->str, item.str); - wcscat(prev->str, L"( "); - wcscat(prev->str, item.str); - wcscpy(item.str, prev->str); - } - else { - prev = VsmxDecompileStackPop(&stack); - //swprintf(item.str, MAX_TEXT_LEN, L"%s( )", prev->str); - SWPRINTF_ITEM("%ls()", prev->str); - } - free(prev); - VsmxDecompileStackPush(&stack, &item); - } break; - - - // this is either an OR or a for loop - case VID_JUMP_TRUE: { - VsmxDecompileStack *prev; - prev = VsmxDecompileStackPop(&stack); - - // check for FOR loop - // !!! look ahead used !!! - if( - // check that next group is a sect start - i+1 < in->codeGroups && in->code[i+1].id == VID_SECT_START - // check that end of for stmt is a jump back - && in->code[i].val.u32 < in->codeGroups && in->code[in->code[i].val.u32-1].id == VID_SECT_START && in->code[in->code[i].val.u32-1].val.u32 == stmtStart - // end of for loop should be after end of for stmt - && in->code[i+1].val.u32 > in->code[i].val.u32 - // check that end of for loop jumps back to incrementor - && in->code[i+1].val.u32 < in->codeGroups && in->code[in->code[i+1].val.u32-1].id == VID_SECT_START && in->code[in->code[i+1].val.u32-1].val.u32 == i+2 - ) { - // assume for loop - SWPRINTF_ITEM("for(; %ls /* jump to %d */; ", prev->str, in->code[i].val.u32); - VsmxDecompileStackPush(&stack, &item); - - forStmtEnd = in->code[i].val.u32; - // push for end marker of loop - mItem.src = i+1; - mItem.loc = in->code[i+1].val.u32; - VsmxDecompMarkStackPush(&mStack, &mItem); - // push for end bracket on for() - mItem.src = i; - mItem.loc = in->code[i].val.u32; - VsmxDecompMarkStackPush(&mStack, &mItem); - i++; - } else { - // assume OR - SWPRINTF_ITEM("( %ls ) || /* ends at %d */ ( ", prev->str, in->code[i].val.u32); - VsmxDecompileStackPush(&stack, &item); - - mItem.src = i; - mItem.loc = in->code[i].val.u32; - VsmxDecompMarkStackPush(&mStack, &mItem); - } - free(prev); - endStmtConcat++; - } break; - - // section markers - case VID_SECT_START: if(!op[0]) { - // this is either a function start or "else" section, or end of loop - if(mStack && (in->code[mStack->src].id == VID_JUMP_FALSE || (in->code[mStack->src].id == VID_SECT_START && in->code[i].val.u32 < i))) { - // check whether else or end of while - if(in->code[i].val.u32 < i) { - // assume end of loop - SWPRINTF(op, 50, L"} %%ls/* jump back to %d */\n", in->code[i].val.u32); - // push dummy element - VsmxDecompileStackPush(&stack, &item); - if(indent > 0) indent--; // this should always be true - // prevent a section start as this really is an end of section - notSectStart = TRUE; - // remove top of marker stack - if(mStack->loc == i+1) - VsmxDecompMarkStackPop(&mStack); - else - warning("Unexpected loop structure at %d!", i); - } else { - // else segment - // pop top of stack off here, as it's a little weird - if(mStack->loc == i+1) { - if(indent > 0) indent--; // this should always be true - else warning("Internal state nesting error!"); - - if(stack) - warning("Unexpected elements found in stack at %d!", i); - - VsmxDecompMarkStackPop(&mStack); - } else { - warning("Unexpected else stack structure at %d!", i); - } - if(stack) { - // inline else - wcscat(stack->str, L" : "); - mItem.src = i; - mItem.loc = in->code[i].val.u32; - VsmxDecompMarkStackPush(&mStack, &mItem); - break; - } else { - SWPRINTF(op, 50, L"} else %%ls{ /* ends at %d */\n", in->code[i].val.u32); - // push dummy element - VsmxDecompileStackPush(&stack, &item); - } - } - } else if(mStack && in->code[mStack->src].id == VID_JUMP_TRUE && in->code[mStack->src].val.u32 == i+1) { - // for loop end of stmt probably - if(stack) warning("Unexpected stack at end of for at %d", i); - // TODO: need to fix this so it doesn't go on a new line - SWPRINTF(op, 50, L"%%ls /* return to %d */) {\n", in->code[i].val.u32); - // push dummy element - VsmxDecompileStackPush(&stack, &item); - indentAdd++; - // prevent a section start (this is a special case) - notSectStart = TRUE; - // remove top of marker stack - VsmxDecompMarkStackPop(&mStack); - - } else { - // function start - SWPRINTF(op, 50, L"%%ls { /* ends at %d */\n", in->code[i].val.u32); // wcscpy(op, L"%s {\n"); - } - } - case VID_JUMP_FALSE: if(!op[0]) { - // !!! look ahead! - if(in->code[i].val.u32-1 >= in->codeGroups) { - error("Invalid jump reference supplied at %d", i); - return 1; - } - // check whether this is an "if" or "while" - if(in->code[in->code[i].val.u32-1].id == VID_SECT_START && in->code[in->code[i].val.u32-1].val.u32 == stmtStart) { - // while loop - SWPRINTF(op, 50, L"while( %%s ) { /* ends at %d */\n", in->code[i].val.u32); // wcscpy(op, L"while( %s ) {\n"); - } else { - // if statement - if(stack->depth > endStmtConcat-1) { - // inline if - special case - wchar tmp[MAX_TEXT_LEN] = {0}; - - while(endStmtConcat--) { - VsmxDecompileStack *prev; - prev = VsmxDecompileStackPop(&stack); - wcscat(prev->str, tmp); - wcscpy(tmp, prev->str); - free(prev); - } - endStmtConcat = 2; - SWPRINTF_ITEM("( %ls ? /* ends at %d */ ", tmp, in->code[i].val.u32); - VsmxDecompileStackPush(&stack, &item); - mItem.src = i; - mItem.loc = in->code[i].val.u32; - VsmxDecompMarkStackPush(&mStack, &mItem); - break; - } else { - SWPRINTF(op, 50, L"if( %%s ) { /* ends at %d */\n", in->code[i].val.u32); // wcscpy(op, L"if( %s ) {\n"); - } - } - } - if(!notSectStart) { - mItem.src = i; - mItem.loc = in->code[i].val.u32; - VsmxDecompMarkStackPush(&mStack, &mItem); - indentAdd++; - } // fall through to end of statement - - case VID_RETURN: if(!op[0]) wcscpy(op, L"return %s;\n"); - case VID_END_STMT: if(!op[0]) wcscpy(op, L"%s;\n"); - { - // end of statement following conditional - skip - if(in->code[i].id == VID_END_STMT && i && (in->code[i-1].id == VID_JUMP_TRUE || in->code[i-1].id == VID_JUMP_FALSE)) - break; - - if(stack) { - if(stack->depth > endStmtConcat-1) { - warning("End of statement but more stack elements than expected at groups %d-%d!", stmtStart, i); - } - - - // TODO: remove debugging line - fwprintf(out, L"/*%d*/\t", stmtStart); - - - writeTabs(out, indent); - - while(endStmtConcat--) { - VsmxDecompileStack *prev; - prev = VsmxDecompileStackPop(&stack); - wcscat(prev->str, item.str); - wcscpy(item.str, prev->str); - free(prev); - } - fwprintf(out, op, item.str); - endStmtConcat = 1; - fflush(out); // TODO: remove later - - VsmxDecompileStackDestroy(&stack); - stmtStart = i+1; - indent += indentAdd; - indentAdd = 0; - } else - warning("No stack at end of statement at %d!", i); - } break; - - - - case VID_END: - if(i+1 < in->codeGroups) { - warning("End marker found at %d, but is not end of code!", i); - } - break; - - default: - warning("Unknown id 0x%x at %d", in->code[i].id, i); - SWPRINTF_ITEM("<< UNKNOWN 0x%x 0x%x >>", in->code[i].id, in->code[i].val.u32); - VsmxDecompileStackPush(&stack, &item); - } - - /*if(is_value) { - item.item = in->code[i]; - VsmxDecompileStackPush(&stack, &item); - }*/ - } - return 0; -} -void freeVsmxMem(VsmxMem* vm) { - free(vm->code); - free(vm->text); - free(vm->prop); - free(vm->names); - free(vm->pText); - free(vm->pProp); - free(vm->pNames); - free(vm); -} + +#include +#include +#include +#include +#include "general.h" +#include "vsmx.h" + + +#if defined(MINGW) || defined(_MSC_VER) // lack of wcscasecmp +#include +int wcscasecmp(const wchar_t *s1, const wchar_t *s2) { + while(*s1 && *s2) { + int diff = towlower(*s1) - towlower(*s2); + if(diff) + return diff; + s1++; + s2++; + } + return towlower(*s1) - towlower(*s2); +} +#endif +// following doesn't seem to work - probably the define is required in the project +/* #ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS // get rid of MSVC's annoying security stuff +#define _CRT_NON_CONFORMING_SWPRINTFS // urgh... seems that mingw uses bad swprintf >_> +#endif */ + + +// since swprintf seems to accept different arguments... +#ifdef MINGW +#define SWPRINTF(s, l, f, ...) swprintf(s, f, __VA_ARGS__) +#else +#define SWPRINTF(s, l, f, ...) swprintf(s, l, f, __VA_ARGS__) +#endif + + +enum { + VID_NOTHING = 0x0, // dummy + VID_OPERATOR_ASSIGN = 0x1, + VID_OPERATOR_ADD = 0x2, + VID_OPERATOR_SUBTRACT = 0x3, // guess + VID_OPERATOR_MULTIPLY = 0x4, + VID_OPERATOR_DIVIDE = 0x5, // guess + VID_UNK_6 = 0x6, + + VID_OPERATOR_NEGATE = 0x8, + VID_OPERATOR_NOT = 0x9, + + + VID_INCREMENT = 0xc, + VID_DECREMENT = 0xd, // guess + VID_OPERATOR_EQUAL = 0xe, + VID_OPERATOR_NOT_EQUAL = 0xf, + VID_OPERATOR_CASE_LABEL = 0x10, // same as VID_OPERATOR_EQUAL, just used in switch statements + + VID_OPERATOR_LT = 0x12, + VID_OPERATOR_LTE = 0x13, + VID_OPERATOR_GT = 0x14, + VID_OPERATOR_GTE = 0x15, + + + VID_UNK_1A = 0x1a, + + + VID_STACK_PUSH = 0x20, + + VID_END_STMT = 0x22, + VID_CONST_NULL = 0x23, + VID_CONST_EMPTYARRAY = 0x24, + VID_CONST_BOOL = 0x25, + VID_CONST_INT = 0x26, + VID_CONST_FLOAT = 0x27, + VID_CONST_STRING = 0x28, + + VID_FUNCTION = 0x2a, + VID_ARRAY = 0x2b, // start an array constant + VID_THIS = 0x2c, + VID_UNNAMED_VAR = 0x2d, + VID_VARIABLE = 0x2e, + VID_PROPERTY = 0x2f, + VID_METHOD = 0x30, + VID_UNK_31 = 0x31, // appears to be an object set; pops last two items off the stack + VID_UNSET = 0x32, // guess; looks like above, but only with one item + + VID_ARRAY_INDEX = 0x34, + + VID_ARRAY_INDEX_ASSIGN = 0x36, + + VID_ARRAY_ELEM = 0x38, // push something into array constant + VID_SECT_START = 0x39, // jump statement; can indicate end of function, end of else/for, or return to beginning of loop + VID_JUMP_TRUE = 0x3a, // jump if previous value is true + VID_JUMP_FALSE = 0x3b, + VID_CALL_FUNC = 0x3c, + VID_CALL_METHOD = 0x3d, + VID_CALL_INBUILT = 0x3e, + VID_RETURN = 0x3f, + + + VID_END = 0x45, + + + VID_MAKE_FLOAT_ARRAY = 0x49 // weird?? +}; + +const wchar VsmxDecOps[][50] = { + L"UNKNOWN_0", + L"ASSIGN", + L"ADD", + L"SUBTRACT", + L"MULTIPLY", + L"DIVIDE", + L"UNK_6", + L"UNKNOWN_7", + L"NEGATE", + L"NOT", + L"UNKNOWN_A", + L"UNKNOWN_B", + L"INCREMENT", + L"DECREMENT", + L"TEST_EQUAL", + L"TEST_NOT_EQUAL", + L"TEST_CASE_EQUAL", + L"UNKNOWN_11", + L"TEST_LESS_THAN", + L"TEST_LESS_EQUAL_THAN", + L"TEST_MORE_THAN", + L"TEST_MORE_EQUAL_THAN", + L"UNKNOWN_16", + L"UNKNOWN_17", + L"UNKNOWN_18", + L"UNKNOWN_19", + L"UNK_1A", + L"UNKNOWN_1B", + L"UNKNOWN_1C", + L"UNKNOWN_1D", + L"UNKNOWN_1E", + L"UNKNOWN_1F", + L"STACK_PUSH", + L"UNKNOWN_21", + L"END_STATEMENT", + L"CONST_NULL", + L"CONST_EMPTY_ARRAY", + L"CONST_BOOL", + L"CONST_INT", + L"CONST_FLOAT", + L"CONST_STRING", + L"UNKNOWN_29", + L"FUNCTION", + L"CONST_ARRAY", + L"THIS_OBJECT", + L"UNNAMED_VARIABLE", + L"NAME", + L"PROPERTY", + L"METHOD", + L"SET", + L"UNSET", + L"UNKNOWN_33", + L"ARRAY_INDEX", + L"UNKNOWN_35", + L"ARRAY_INDEX_ASSIGN", + L"UNKNOWN_37", + L"ARRAY_PUSH", + L"JUMP", + L"JUMP_IF_TRUE", + L"JUMP_IF_FALSE", + L"CALL_FUNCTION", + L"CALL_METHOD", + L"CALL_INBUILT", + L"RETURN", + L"UNKNOWN_40", + L"UNKNOWN_41", + L"UNKNOWN_42", + L"UNKNOWN_43", + L"UNKNOWN_44", + L"END_SCRIPT", + L"UNKNOWN_46", + L"UNKNOWN_47", + L"UNKNOWN_48", + L"MAKE_FLOAT_ARRAY" +}; +const unsigned int VSMX_NUM_DEC_OPS = 0x49+1; + + + + +static inline wchar_t* strwcpy(wchar_t* dest, const char* src, unsigned int maxLen) { + while(--maxLen && (*dest = (wchar_t)(*src))) { + dest++; + src++; + } + *dest = 0; // ensure last char is null + return dest; +} + +VsmxMem* readVSMX(FILE* fp) { + + VSMXHeader header; + uint filePos; + VsmxMem* out; + + fileread(fp, &header, sizeof(header)); + if(header.sig != VSMX_SIGNATURE || header.ver != VSMX_VERSION) { + error("Not a valid VSMX file."); + return NULL; + } + + if(header.codeOffset != sizeof(header)) { + warning("Skipping range 0x%x-0x%x", sizeof(header), header.codeOffset); + fseek(fp, header.codeOffset, SEEK_SET); + } + + if(header.codeLength % sizeof(VSMXGroup)) { + error("Code size not aligned to 8 bytes."); + return NULL; + } + out = (VsmxMem*)malloc(sizeof(VsmxMem)); + + // TODO: check lengths are in sane ranges + out->code = (VSMXGroup*)malloc(header.codeLength); + fileread(fp, out->code, header.codeLength); + out->codeGroups = header.codeLength / sizeof(VSMXGroup); + + filePos = header.codeOffset + header.codeLength; + + #define READ_TEXT_SECTION(to, tl, te, typ, var, pVar, nam) \ + if(tl || te) { \ + if(to != filePos) { \ + warning("Skipping range 0x%x-0x%x", filePos, to); \ + fseek(fp, to, SEEK_SET); \ + } \ + if(!tl) { \ + error("Number of " nam " entries > 1 but length of data is 0!"); \ + free(out); \ + return NULL; \ + } \ + if(!te) { \ + error("Number of " nam " entries = 0 but length of data is > 0!"); \ + free(out); \ + return NULL; \ + } \ + if(tl % sizeof(typ)) { \ + error("Size of " nam " not aligned to %d byte(s).", sizeof(typ)); \ + free(out); \ + return NULL; \ + } \ + \ + var = (typ *)malloc(tl +sizeof(typ)); \ + fileread(fp, var, tl); \ + var[tl / sizeof(typ)] = 0; \ + \ + pVar = (typ **)malloc(te * sizeof(typ *)); \ + pVar[0] = var; \ + \ + uint pti = 1, ci; \ + for(ci = 1; ci < tl / sizeof(typ); ci++) { \ + if(var[ci-1] == 0) { \ + if(pti == te) { \ + error("Number of " nam " entries found exceeds number specified in header!"); \ + free(out); \ + return NULL; \ + } \ + pVar[pti++] = var + ci; \ + } \ + } \ + if(pti != te) { \ + error("Number of " nam " entries found is less than number specified in header!"); \ + free(out); \ + return NULL; \ + } \ + \ + filePos += tl; \ + } + + READ_TEXT_SECTION(header.textOffset, header.textLength, header.textEntries, wchar, out->text, out->pText, "text"); + READ_TEXT_SECTION(header.propOffset, header.propLength, header.propEntries, wchar, out->prop, out->pProp, "properties"); + READ_TEXT_SECTION(header.namesOffset, header.namesLength, header.namesEntries, char, out->names, out->pNames, "names"); + + out->numText = header.textEntries; + out->lenText = header.textLength; + out->numProp = header.propEntries; + out->lenProp = header.propLength; + out->numNames = header.namesEntries; + out->lenNames = header.namesLength; + + fclose(fp); + + return out; +} + +// urgh, this is a bit of a duplicate of the above +// TODO: don't make this so crap +VsmxMem* readVSMXMem(const void *in) { + + VSMXHeader* header = (VSMXHeader*)in; + uint filePos; + VsmxMem* out; + + if(header->sig != VSMX_SIGNATURE || header->ver != VSMX_VERSION) { + error("Not a valid VSMX file."); + return NULL; + } + + if(header->codeOffset != sizeof(VSMXHeader)) { + warning("Skipping range 0x%x-0x%x", sizeof(VSMXHeader), header->codeOffset); + //fseek(fp, header->codeOffset, SEEK_SET); + } + + if(header->codeLength % sizeof(VSMXGroup)) { + error("Code size not aligned to 8 bytes."); + return NULL; + } + out = (VsmxMem*)malloc(sizeof(VsmxMem)); + + // TODO: check lengths are in sane ranges + out->code = (VSMXGroup*)malloc(header->codeLength); + memcpy(out->code, (uint8*)in + header->codeOffset, header->codeLength); + out->codeGroups = header->codeLength / sizeof(VSMXGroup); + + filePos = header->codeOffset + header->codeLength; + + #define READ_TEXT_SECTION_MEM(to, tl, te, typ, var, pVar, nam) \ + if(tl || te) { \ + if(to != filePos) { \ + warning("Skipping range 0x%x-0x%x", filePos, to); \ + filePos = to; \ + } \ + if(!tl) { \ + error("Number of " nam " entries > 1 but length of data is 0!"); \ + free(out); \ + return NULL; \ + } \ + if(!te) { \ + error("Number of " nam " entries = 0 but length of data is > 0!"); \ + free(out); \ + return NULL; \ + } \ + if(tl % sizeof(typ)) { \ + error("Size of " nam " not aligned to %d byte(s).", sizeof(typ)); \ + free(out); \ + return NULL; \ + } \ + \ + var = (typ *)malloc(tl +sizeof(typ)); \ + memcpy(var, (uint8*)in + filePos, tl); \ + var[tl / sizeof(typ)] = 0; \ + \ + pVar = (typ **)malloc(te * sizeof(typ *)); \ + pVar[0] = var; \ + \ + uint pti = 1, ci; \ + for(ci = 1; ci < tl / sizeof(typ); ci++) { \ + if(var[ci-1] == 0) { \ + if(pti == te) { \ + error("Number of " nam " entries found exceeds number specified in header!"); \ + free(out); \ + return NULL; \ + } \ + pVar[pti++] = var + ci; \ + } \ + } \ + if(pti != te) { \ + error("Number of " nam " entries found is less than number specified in header!"); \ + free(out); \ + return NULL; \ + } \ + \ + filePos += tl; \ + } + + READ_TEXT_SECTION_MEM(header->textOffset, header->textLength, header->textEntries, wchar, out->text, out->pText, "text"); + READ_TEXT_SECTION_MEM(header->propOffset, header->propLength, header->propEntries, wchar, out->prop, out->pProp, "properties"); + READ_TEXT_SECTION_MEM(header->namesOffset, header->namesLength, header->namesEntries, char, out->names, out->pNames, "names"); + + out->numText = header->textEntries; + out->lenText = header->textLength; + out->numProp = header->propEntries; + out->lenProp = header->propLength; + out->numNames = header->namesEntries; + out->lenNames = header->namesLength; + + return out; +} + +void writeVSMX(FILE* fp, VsmxMem* in) { + VSMXHeader header; + unsigned int pos = 0x34; + header.sig = VSMX_SIGNATURE; + header.ver = VSMX_VERSION; + header.codeOffset = pos; + header.codeLength = in->codeGroups * sizeof(VSMXGroup); + pos += header.codeLength; + header.textOffset = pos; + header.textLength = in->lenText; + header.textEntries = in->numText; + pos += header.textLength; + header.propOffset = pos; + header.propLength = in->lenProp; + header.propEntries = in->numProp; + pos += header.propLength; + header.namesOffset = pos; + header.namesLength = in->lenNames; + header.namesEntries = in->numNames; + + fwrite(&header, sizeof(header), 1, fp); + + if(in->code) + fwrite(in->code, header.codeLength, 1, fp); + if(in->text) + fwrite(in->text, header.textLength, 1, fp); + if(in->prop) + fwrite(in->prop, header.propLength, 1, fp); + if(in->names) + fwrite(in->names, header.namesLength, 1, fp); +} +// like the read thing above, this sucks +void* writeVSMXMem(unsigned int* len, VsmxMem* in) { + *len = sizeof(VSMXHeader) + in->codeGroups * sizeof(VSMXGroup) + in->lenText + in->lenProp + in->lenNames; + void* ret = malloc(*len); + + VSMXHeader* header = (VSMXHeader*)ret; + unsigned int pos = sizeof(VSMXHeader); + header->sig = VSMX_SIGNATURE; + header->ver = VSMX_VERSION; + header->codeOffset = pos; + header->codeLength = in->codeGroups * sizeof(VSMXGroup); + pos += header->codeLength; + header->textOffset = pos; + header->textLength = in->lenText; + header->textEntries = in->numText; + pos += header->textLength; + header->propOffset = pos; + header->propLength = in->lenProp; + header->propEntries = in->numProp; + pos += header->propLength; + header->namesOffset = pos; + header->namesLength = in->lenNames; + header->namesEntries = in->numNames; + + pos = sizeof(VSMXHeader); + if(in->code) { + memcpy((uint8*)ret + pos, in->code, header->codeLength); + pos += header->codeLength; + } + if(in->text) { + memcpy((uint8*)ret + pos, in->text, header->textLength); + pos += header->textLength; + } + if(in->prop) { + memcpy((uint8*)ret + pos, in->prop, header->propLength); + pos += header->propLength; + } + if(in->names) { + memcpy((uint8*)ret + pos, in->names, header->namesLength); + pos += header->namesLength; + } + return ret; +} + +// macro used in both decode and decompile functions +#define CHECK_INDEX(num,nam) \ + if(in->code[i].val.u32 >= num) { \ + error("Invalid " nam " index 0x%x at group %d!", in->code[i].val.u32, i); \ + return 1; \ + } + +int VsmxDecode(VsmxMem* in, FILE* out) { + uint i; + + fputws(L"; Decoded VSMX file written by " APPNAME_VER "\n\n", out); + + for(i=0; i < in->codeGroups; i++) { + if((in->code[i].id & 0xFF) <= VSMX_NUM_DEC_OPS) { + fputws(VsmxDecOps[in->code[i].id & 0xFF], out); + //fputwc(L' ', out); + } else + fwprintf(out, L"UNKNOWN_%x", in->code[i].id); + + switch(in->code[i].id & 0xFF) { + + case VID_CONST_BOOL: + if(in->code[i].val.u32 == 1) + fputws(L" true", out); + else if(in->code[i].val.u32 == 0) + fputws(L" false", out); + else { + warning("Unexpected boolean value 0x%x at line %d!", in->code[i].val.u32, i+1); + fwprintf(out, L" 0x%x", in->code[i].val.u32); + } + break; + case VID_CONST_INT: + fwprintf(out, L" %u", in->code[i].val.u32); + break; + case VID_CONST_FLOAT: + fwprintf(out, L" %#g", in->code[i].val.f); + break; + case VID_CONST_STRING: + CHECK_INDEX(in->numText, "text"); + fwprintf(out, L" \"%ls\"", in->pText[in->code[i].val.u32]); + break; + case VID_VARIABLE: { + // for some reason, the %s modifier in fwprintf doesn't work properly... :/ so we need to convert to a wide char string + wchar *tmp; + uint tmpLen = strlen(in->pNames[in->code[i].val.u32]); + CHECK_INDEX(in->numNames, "name"); + tmp = (wchar*)malloc((tmpLen+1) * sizeof(wchar)); + strwcpy(tmp, in->pNames[in->code[i].val.u32], tmpLen+1); + fwprintf(out, L" %ls", tmp); + free(tmp); + } break; + case VID_PROPERTY: + case VID_METHOD: + case VID_UNK_31: + case VID_UNSET: + CHECK_INDEX(in->numProp, "property"); + fwprintf(out, L" %ls", in->pProp[in->code[i].val.u32]); + break; + case VID_FUNCTION: + if(in->code[i].id >> 16 & 0xFF) + warning("Unexpected flag value for function at line %d, expected 0, got %d", i+1, in->code[i].id >> 16 & 0xFF); + fwprintf(out, L" args=%u, flag=%u, start_line=%u", (in->code[i].id >> 8) & 0xFF, (in->code[i].id >> 24) & 0xFF, in->code[i].val.u32+1); + break; + case VID_UNNAMED_VAR: + fwprintf(out, L" %u", in->code[i].val.u32); + break; + + // jumps + case VID_SECT_START: + case VID_JUMP_TRUE: + case VID_JUMP_FALSE: + fwprintf(out, L" line=%u", in->code[i].val.u32+1); + break; + + // function calls + case VID_CALL_FUNC: + case VID_CALL_METHOD: + case VID_CALL_INBUILT: + fwprintf(out, L" args=%u", in->code[i].val.u32); + break; + + case VID_MAKE_FLOAT_ARRAY: + fwprintf(out, L" items=%u", in->code[i].val.u32); + break; + + // ops w/o arg - check for zero + case VID_OPERATOR_ASSIGN: + case VID_OPERATOR_ADD: + case VID_OPERATOR_SUBTRACT: + case VID_OPERATOR_MULTIPLY: + case VID_OPERATOR_DIVIDE: + case VID_UNK_6: + case VID_OPERATOR_NEGATE: + case VID_OPERATOR_NOT: + case VID_INCREMENT: + case VID_DECREMENT: + case VID_OPERATOR_EQUAL: + case VID_OPERATOR_NOT_EQUAL: + case VID_OPERATOR_CASE_LABEL: + case VID_OPERATOR_LT: + case VID_OPERATOR_LTE: + case VID_OPERATOR_GT: + case VID_OPERATOR_GTE: + case VID_UNK_1A: + case VID_STACK_PUSH: + case VID_END_STMT: + case VID_CONST_NULL: + case VID_CONST_EMPTYARRAY: + case VID_ARRAY: + case VID_THIS: + case VID_ARRAY_INDEX: + case VID_ARRAY_INDEX_ASSIGN: + case VID_ARRAY_ELEM: + case VID_RETURN: + case VID_END: + if(in->code[i].val.u32) + warning("Unexpected non-zero value at line %d!", i+1); + + break; + + default: + warning("Unknown ID 0x%x at line %d", in->code[i].id, i+1); + fwprintf(out, L" 0x%x", in->code[i].id, in->code[i].val.u32); + } + fputwc(L'\n', out); + } + return 0; +} + +// !! doesn't properly fix up pText !! +unsigned int VsmxAddText(void** text, unsigned int** offs, unsigned int* textLen, unsigned int* numText, wchar* newText, int charWidth) { + // first, see if we already have this text + unsigned int p = 0; + unsigned int len = wcslen(newText); + + char* newTextByte = NULL; + //wprintf(L"adding text %ls\n", newText); + //fflush(stdout); + if(charWidth == 1) { // convert wchar to char + char *ptr = (char*)malloc(len +1); + newTextByte = ptr; + if(!ptr) { + error("malloc failed"); + exit(1); + } + while((*ptr = (char)(*newText))) { + ptr++; + newText++; + } + } + + for(p=0; p < *numText; p++) { + Bool cond; + if(charWidth == 1) { + cond = strcmp(*(char**)text + (*offs)[p], newTextByte); + } + else + cond = wcscmp(*(wchar**)text + (*offs)[p] / charWidth, newText); + if(!cond) { + if(newTextByte) free(newTextByte); + return p; // found + } + } + + // don't have it? add it + (*numText)++; + *text = realloc(*text, *textLen + (len+1)*charWidth); + *offs = (unsigned int*)realloc(*offs, sizeof(unsigned int) * (*numText)); + if(charWidth == 1) { + strcpy(*(char**)text + *textLen, newTextByte); + } else { + wcscpy(*(wchar**)text + *textLen / charWidth, newText); + } + (*offs)[*numText -1] = *textLen; + *textLen += (len+1)*charWidth; + + if(newTextByte) free(newTextByte); + return *numText-1; +} + + +VsmxMem* VsmxEncode(FILE* in) { + wchar buf[2048]; + unsigned int lineCount = 1; + VsmxMem* ret = (VsmxMem*)malloc(sizeof(VsmxMem)); + unsigned int *oText = NULL, *oProp = NULL, *oNames = NULL; + + ret->codeGroups = ret->numText = ret->numProp = ret->numNames = ret->lenText = ret->lenProp = ret->lenNames = 0; + ret->code = NULL; + ret->text = NULL; + ret->prop = NULL; + ret->names = NULL; + ret->pText = NULL; + ret->pProp = NULL; + ret->pNames = NULL; + + while(fgetws(buf, 2048, in)) { + wchar *op = buf, *arg = NULL, *tmp; + int opNum = -1; + unsigned int opNum2, argNum = 0; + unsigned int lineLen; + // trim line + #define IS_WHITESPACE(x) (x == L'\t' || x == L' ' || x == L'\n' || x == L'\r') + while(*op && IS_WHITESPACE(*op)) + op++; + /* code is BAD - can remove stuff if in a string! + // don't forget to remove comments! + tmp = op; + while(*tmp) { + if(*tmp == L';') { + *tmp = L'\0'; + break; + } + tmp++; + } + */ + lineLen = wcslen(op); + while(lineLen && IS_WHITESPACE(op[lineLen-1])) + lineLen--; + op[lineLen] = L'\0'; + + if(lineLen == 0) continue; + if(op[0] == L';') continue; // comment line + + // find space, if any + tmp = op; + while(*(++tmp)) + if(*tmp == L' ') { + arg = tmp+1; + *tmp = L'\0'; + while(*arg && *arg == L' ') + arg++; + break; + } + + // determine op + if(swscanf(op, L"UNKNOWN_%x", &opNum) != 1) { + unsigned int i; + for(i=0; itext, &oText, &ret->lenText, &ret->numText, arg, sizeof(*(ret->text))); + } + } break; + case VID_VARIABLE: + argNum = VsmxAddText((void**)&ret->names, &oNames, &ret->lenNames, &ret->numNames, arg, sizeof(*(ret->names))); + break; + case VID_PROPERTY: + case VID_METHOD: + case VID_UNK_31: + case VID_UNSET: + argNum = VsmxAddText((void**)&ret->prop, &oProp, &ret->lenProp, &ret->numProp, arg, sizeof(*(ret->prop))); + break; + + case VID_FUNCTION: { + int iArgs, iFlag; + // TODO: better parser here + swscanf(arg, L"args=%u, flag=%u, start_line=%u", &iArgs, &iFlag, &argNum); + argNum--; + opNum2 = ((iFlag & 0xFF) << 24) | ((iArgs & 0xFF) << 8) | opNum2; + } break; + case VID_SECT_START: + case VID_JUMP_TRUE: + case VID_JUMP_FALSE: + swscanf(arg, L"line=%u", &argNum); + argNum--; + break; + case VID_CALL_FUNC: + case VID_CALL_METHOD: + case VID_CALL_INBUILT: + swscanf(arg, L"args=%u", &argNum); + break; + + case VID_MAKE_FLOAT_ARRAY: + swscanf(arg, L"items=%u", &argNum); + break; + + // ops w/o arg - check for zero + case VID_OPERATOR_ASSIGN: + case VID_OPERATOR_ADD: + case VID_OPERATOR_SUBTRACT: + case VID_OPERATOR_MULTIPLY: + case VID_OPERATOR_DIVIDE: + case VID_UNK_6: + case VID_OPERATOR_NEGATE: + case VID_OPERATOR_NOT: + case VID_INCREMENT: + case VID_DECREMENT: + case VID_OPERATOR_EQUAL: + case VID_OPERATOR_NOT_EQUAL: + case VID_OPERATOR_CASE_LABEL: + case VID_OPERATOR_LT: + case VID_OPERATOR_LTE: + case VID_OPERATOR_GT: + case VID_OPERATOR_GTE: + case VID_UNK_1A: + case VID_STACK_PUSH: + case VID_END_STMT: + case VID_CONST_NULL: + case VID_CONST_EMPTYARRAY: + case VID_ARRAY: + case VID_THIS: + case VID_ARRAY_INDEX: + case VID_ARRAY_INDEX_ASSIGN: + case VID_ARRAY_ELEM: + case VID_RETURN: + case VID_END: + if(arg) warning("[line %d] Operator does not have value.", lineCount); + break; + + default: + swscanf(arg, L"%i", &argNum); + } + + ret->codeGroups++; + ret->code = (VSMXGroup*)realloc(ret->code, ret->codeGroups * sizeof(VSMXGroup)); + ret->code[ret->codeGroups-1].id = opNum2; + ret->code[ret->codeGroups-1].val.u32 = argNum; + + lineCount++; + } + + if(oText) { + unsigned int i; + ret->pText = (wchar**)malloc(ret->numText * sizeof(wchar*)); + for(i=0; inumText; i++) + ret->pText[i] = ret->text + oText[i]; + free(oText); + } + if(oProp) { + unsigned int i; + ret->pProp = (wchar**)malloc(ret->numProp * sizeof(wchar*)); + for(i=0; inumProp; i++) + ret->pProp[i] = ret->prop + oProp[i]; + free(oProp); + } + if(oNames) { + unsigned int i; + ret->pNames = (char**)malloc(ret->numNames * sizeof(char*)); + for(i=0; inumNames; i++) + ret->pNames[i] = ret->names + oNames[i]; + free(oNames); + } + + return ret; +} + +#define MAX_TEXT_LEN 4096 +typedef struct __VsmxDecompileStack VsmxDecompileStack; + +struct __VsmxDecompileStack { + wchar str[MAX_TEXT_LEN]; // a bit of RAM wastage perhaps + VSMXGroup item; + int arrayFlag; + + VsmxDecompileStack* prev; + uint depth; +}; + +static inline void VsmxDecompileStackPush(VsmxDecompileStack** stack, VsmxDecompileStack* item) { + VsmxDecompileStack* newItem = (VsmxDecompileStack*)malloc(sizeof(VsmxDecompileStack)); + *newItem = *item; + newItem->prev = *stack; + if(*stack) newItem->depth = (*stack)->depth + 1; + else newItem->depth = 0; + *stack = newItem; +} +static inline VsmxDecompileStack* VsmxDecompileStackPop(VsmxDecompileStack** stack) { + if(!(*stack)) { + error("Stack underflow occurred!"); + return NULL; + } + VsmxDecompileStack* theItem = *stack; + *stack = (*stack)->prev; + return theItem; +} +static inline void VsmxDecompileStackDestroy(VsmxDecompileStack** stack) { + if(!*stack) return; + while(*stack) { + VsmxDecompileStack* tmp = (*stack)->prev; + free(*stack); + *stack = tmp; + } +} + +typedef struct __VsmxDecompMarkStack VsmxDecompMarkStack; + +struct __VsmxDecompMarkStack { + uint loc; + uint src; + + VsmxDecompMarkStack* prev; + uint depth; +}; + +static inline void VsmxDecompMarkStackPush(VsmxDecompMarkStack** stack, VsmxDecompMarkStack* item) { + VsmxDecompMarkStack* newItem = (VsmxDecompMarkStack*)malloc(sizeof(VsmxDecompMarkStack)); + *newItem = *item; + newItem->prev = *stack; + if(*stack) { + newItem->depth = (*stack)->depth + 1; + if(newItem->loc > (*stack)->loc) { + warning("Bad nesting hierachy detected!"); + // well, what to do? Just switch them around I guess... + uint tmp = newItem->loc; + newItem->loc = (*stack)->loc; + (*stack)->loc = tmp; + } + } + else newItem->depth = 0; + *stack = newItem; +} +static inline VsmxDecompMarkStack* VsmxDecompMarkStackPop(VsmxDecompMarkStack** stack) { + if(!(*stack)) { + error("Marker stack underflow occurred!"); + return NULL; + } + VsmxDecompMarkStack* theItem = *stack; + *stack = (*stack)->prev; + return theItem; +} +static inline void VsmxDecompMarkStackDestroy(VsmxDecompMarkStack** stack) { + if(!*stack) return; + while(*stack) { + VsmxDecompMarkStack* tmp = (*stack)->prev; + free(*stack); + *stack = tmp; + } +} + +static inline void writeTabs(FILE* out, uint num) { + while(num--) + fputwc('\t', out); +} + +int VsmxDecompile(VsmxMem* in, FILE* out) { + uint i, indent=0, stmtStart=0; + int indentAdd=0; + VsmxDecompileStack* stack = NULL; + VsmxDecompileStack item; + VsmxDecompMarkStack* mStack = NULL; + VsmxDecompMarkStack mItem; + uint endStmtConcat = 1; + + uint forStmtEnd = 0; + + + fputws(L"// Decompiled VSMX -> Javascript output by " APPNAME_VER "\n//Note, this is highly experimental and the output probably sucks.\n\n", out); + + for(i=0; i < in->codeGroups; i++) { + item.str[0] = 0; + item.arrayFlag = 0; + + item.item.id = 0; + item.item.val.u32 = 0; + + // TODO: check that in->code[i].val is 0 for various things + + + if(in->code[i].id >> 8 && (in->code[i].id & 0xFF) != VID_FUNCTION) { + warning("Unexpected flags 0x%x for id 0x%x at %d", in->code[i].id >> 8, in->code[i].id & 0xFF, i); + } + + + // check for markers + while(mStack && mStack->loc == i) { + if(stack) { + // inline marker + + // TODO: should we really concatenate here? + while(endStmtConcat--) { + VsmxDecompileStack *prev; + prev = VsmxDecompileStackPop(&stack); + wcscat(prev->str, item.str); + wcscpy(item.str, prev->str); + free(prev); + } + endStmtConcat = 1; + + if(in->code[mStack->src].id == VID_JUMP_FALSE) { + // check for special and operation + if(in->code[i].id == VID_JUMP_FALSE) { + // this upcomming if acts as an AND - we'll display this as a nested if though + mStack->loc = in->code[i].val.u32; + // duplicate this marker as a copy will get popped later + VsmxDecompMarkStackPush(&mStack, mStack); + wcscat(item.str, L" /* AND condition shown as nested if */ "); + } else { // inline if which doesn't have an else + wcscat(item.str, L" : false )"); + } + } + else + wcscat(item.str, L" )"); + VsmxDecompileStackPush(&stack, &item); + item.str[0] = 0; + } else { + if(indent > 0) indent--; // this should always be true + else warning("Internal state nesting error!"); + + // TODO: remove debugging line + fwprintf(out, L"/*%d*/\t", stmtStart); + + writeTabs(out, indent); + fwprintf(out, L"}\n"); + } + + VsmxDecompMarkStackPop(&mStack); + } + + + // stuff we'll use later + wchar op[50] = {0}; + Bool notSectStart = FALSE; + /* #ifdef MINGW + #define SWPRINTF_ITEM(s, ...) swprintf(item.str, L##s, __VA_ARGS__) + #else + #define SWPRINTF_ITEM(s, ...) swprintf(item.str, MAX_TEXT_LEN, L##s, __VA_ARGS__) + #endif */ + #define SWPRINTF_ITEM(s, ...) SWPRINTF(item.str, MAX_TEXT_LEN, L##s, __VA_ARGS__) + //#define SWPRINTF_ITEM(s, ...) swprintf(item.str, MAX_TEXT_LEN, L s, __VA_ARGS__) + + switch(in->code[i].id & 0xFF) { + + // binary operators + case VID_OPERATOR_ASSIGN: if(!op[0]) wcscpy(op, L"="); + case VID_OPERATOR_ADD: if(!op[0]) wcscpy(op, L"+"); + case VID_OPERATOR_SUBTRACT: if(!op[0]) wcscpy(op, L"-"); + case VID_OPERATOR_MULTIPLY: if(!op[0]) wcscpy(op, L"*"); + case VID_OPERATOR_DIVIDE: if(!op[0]) wcscpy(op, L"/"); + case VID_UNK_6: if(!op[0]) wcscpy(op, L""); // TODO: fix dummy + case VID_OPERATOR_EQUAL: if(!op[0]) wcscpy(op, L"=="); + case VID_OPERATOR_NOT_EQUAL: if(!op[0]) wcscpy(op, L"!="); + case VID_OPERATOR_LT: if(!op[0]) wcscpy(op, L"<"); + case VID_OPERATOR_LTE: if(!op[0]) wcscpy(op, L"<="); + case VID_OPERATOR_GT: if(!op[0]) wcscpy(op, L">"); + case VID_OPERATOR_GTE: if(!op[0]) wcscpy(op, L">="); + case VID_UNK_1A: if(!op[0]) wcscpy(op, L""); // TODO: fix dummy + { // TODO: need to consider when to use brackets + VsmxDecompileStack *left, *right; + right = VsmxDecompileStackPop(&stack); + left = VsmxDecompileStackPop(&stack); + SWPRINTF_ITEM("%ls %ls %ls", left->str, op, right->str); + //swprintf(item.str, MAX_TEXT_LEN, L"%s %s %s", left->str, op, right->str); + VsmxDecompileStackPush(&stack, &item); + free(left); free(right); + } break; + + + case VID_OPERATOR_NEGATE: if(!op[0]) wcscpy(op, L"-"); + case VID_OPERATOR_NOT: if(!op[0]) wcscpy(op, L"!"); + { + VsmxDecompileStack *prev; + prev = VsmxDecompileStackPop(&stack); + //swprintf(item.str, MAX_TEXT_LEN, L"-(%s)", prev->str); + SWPRINTF_ITEM("%ls(%ls)", op, prev->str); + VsmxDecompileStackPush(&stack, &item); + free(prev); + } break; + + case VID_INCREMENT: if(!op[0]) wcscpy(op, L"++"); + case VID_DECREMENT: if(!op[0]) wcscpy(op, L"--"); + { + VsmxDecompileStack *prev; + prev = VsmxDecompileStackPop(&stack); + //swprintf(item.str, MAX_TEXT_LEN, L"-(%s)", prev->str); + SWPRINTF_ITEM("(%ls)%ls", prev->str, op); + VsmxDecompileStackPush(&stack, &item); + free(prev); + } break; + + + // constants & variables + case VID_CONST_NULL: + wcscpy(item.str, L"null"); + VsmxDecompileStackPush(&stack, &item); + break; + case VID_CONST_EMPTYARRAY: + wcscpy(item.str, L"[]"); + VsmxDecompileStackPush(&stack, &item); + break; + + //case VID_CONST_TIME: break; // TODO: + case VID_CONST_BOOL: + if(in->code[i].val.u32 == 0) + wcscpy(item.str, L"false"); + else { + wcscpy(item.str, L"true"); + if(in->code[i].val.u32 != 1) + warning("Boolean value at group #%d is not 0 or 1.", i); + } + VsmxDecompileStackPush(&stack, &item); + break; + + case VID_CONST_INT: + //swprintf(item.str, MAX_TEXT_LEN, L"%u", in->code[i].val.u32); + SWPRINTF_ITEM("%u", in->code[i].val.u32); + VsmxDecompileStackPush(&stack, &item); + break; + case VID_CONST_FLOAT: + //swprintf(item.str, MAX_TEXT_LEN, L"%f", *(float*)(&in->code[i].val.u32)); + SWPRINTF_ITEM("%#g", in->code[i].val.f); + VsmxDecompileStackPush(&stack, &item); + break; + case VID_CONST_STRING: { // TODO: unicode issues... :( + CHECK_INDEX(in->numText, "text"); + //swprintf(item.str, MAX_TEXT_LEN, L"\"%s\"", in->pText[in->code[i].val.u32]); + SWPRINTF_ITEM("\"%ls\"", in->pText[in->code[i].val.u32]); + VsmxDecompileStackPush(&stack, &item); + } break; + case VID_THIS: { + wcscpy(item.str, L"this"); + VsmxDecompileStackPush(&stack, &item); + } break; + case VID_UNNAMED_VAR: + //swprintf(item.str, MAX_TEXT_LEN, L"__GLOBALS__[%u]", in->code[i].val.u32); + SWPRINTF_ITEM("__var%u", in->code[i].val.u32); + VsmxDecompileStackPush(&stack, &item); + break; + case VID_VARIABLE: + CHECK_INDEX(in->numNames, "name"); + strwcpy(item.str, in->pNames[in->code[i].val.u32], MAX_TEXT_LEN); + VsmxDecompileStackPush(&stack, &item); + break; + + case VID_UNK_31: + CHECK_INDEX(in->numProp, "prop"); + if(stack && stack->depth >= 1) { // TODO: this is really not correct + VsmxDecompileStack *obj, *val; + val = VsmxDecompileStackPop(&stack); + obj = VsmxDecompileStackPop(&stack); + SWPRINTF_ITEM("%ls.%ls = %ls", obj->str, in->pProp[in->code[i].val.u32], val->str); + free(obj); + free(val); + } else + wcscpy(item.str, in->pProp[in->code[i].val.u32]); + VsmxDecompileStackPush(&stack, &item); + break; + + case VID_PROPERTY: + case VID_METHOD: + case VID_UNSET: + { + VsmxDecompileStack *prev; + prev = VsmxDecompileStackPop(&stack); + CHECK_INDEX(in->numProp, "property/method"); + //swprintf(item.str, MAX_TEXT_LEN, L"%s.%s", prev->str, in->pProp[in->code[i].val.u32]); + if(in->code[i].id == VID_UNSET) + SWPRINTF_ITEM("delete %ls.%ls", prev->str, in->pProp[in->code[i].val.u32]); + else + SWPRINTF_ITEM("%ls.%ls", prev->str, in->pProp[in->code[i].val.u32]); + VsmxDecompileStackPush(&stack, &item); + free(prev); + } break; + + + case VID_MAKE_FLOAT_ARRAY: + { + VsmxDecompileStack *prev; + uint j; + + for(j=0; jcode[i].val.u32; j++) { + prev = VsmxDecompileStackPop(&stack); + if(j) + SWPRINTF_ITEM(" %ls,%ls", prev->str, item.str); + else + SWPRINTF_ITEM(" %ls ", prev->str); + free(prev); + } + SWPRINTF_ITEM("[%ls]", item.str); + VsmxDecompileStackPush(&stack, &item); + } break; + + case VID_ARRAY: + wcscpy(item.str, L"[]"); + item.arrayFlag = 1; + VsmxDecompileStackPush(&stack, &item); + break; + case VID_ARRAY_ELEM: { + VsmxDecompileStack *array, *prev; + prev = VsmxDecompileStackPop(&stack); + array = VsmxDecompileStackPop(&stack); + if(!array->arrayFlag) { + warning("Array elem being pushed, but array not found at %d!", i); + } else if(array->arrayFlag == 1) { // first element + SWPRINTF_ITEM("[ %ls ]", prev->str); + item.arrayFlag = 2; + VsmxDecompileStackPush(&stack, &item); + } else { + uint aLen = wcslen(array->str); + if(aLen < 3) { + error("Internal array handling error at %d!", i); + return 1; + } + array->str[aLen - 2] = L'\0'; + SWPRINTF_ITEM("%ls, %ls ]", array->str, prev->str); + item.arrayFlag = 2; + VsmxDecompileStackPush(&stack, &item); + } + free(prev); + free(array); + } break; + + case VID_STACK_PUSH: { + // we only consider two possible uses for this + // 1. push for conditional - we'll ignore these + // 2. push for -= and += operators + + // !!! look ahead used! + if(i+1 < in->codeGroups && (in->code[i+1].id == VID_JUMP_FALSE || in->code[i+1].id == VID_JUMP_TRUE)) { + // 1st condition (hoping there isn't a more complex situation) - ignore + } else { + // 2nd, duplicate top of stack + VsmxDecompileStackPush(&stack, stack); + } + } break; + + + case VID_FUNCTION: { + // TODO: need to check marker stack for look aheads + // !!! note look ahead used! + Bool funcEndStmtStyle = (i+3 < in->codeGroups && in->code[i+2].id == VID_END_STMT && in->code[i+3].id == VID_SECT_START && in->code[i].val.u32 == i+4); + + wchar args[4096] = L""; // large enough for anything + uint numArgs = (in->code[i].id >> 8) & 0xFF, argI; + + if(in->code[i].id >> 16 & 0xFF) { + warning("Unexpected flag value for function at %d, expected 0, got %d", i, in->code[i].id >> 16 & 0xFF); + } + SWPRINTF(args, 4096, L"/*flag=%d*/", in->code[i].id >> 24); + if(numArgs) wcscat(args, L" "); + + for(argI = 0; argI < numArgs; argI++) { + if(argI) wcscat(args, L", "); + SWPRINTF(args + wcslen(args), 4096-wcslen(args), L"__var%d", argI+1); + } + + if(i+2 < in->codeGroups && in->code[i+1].id == VID_OPERATOR_ASSIGN && ( + funcEndStmtStyle || + (in->code[i+2].id == VID_SECT_START && in->code[i].val.u32 == i+3) + )) { + // abc = function() style? + + if(in->code[i+1].val.u32 || (funcEndStmtStyle && in->code[i+2].val.u32)) { + warning("Unexpected values in function definition style at %d", i); + } + + if(!stack) { + warning("Function encountered at %d, but no name found on stack!", i); + SWPRINTF_ITEM("function __unnamed_%d__(%ls)", i, args); + VsmxDecompileStackPush(&stack, &item); + } else if(stack->depth == 0) { + VsmxDecompileStack *prev; + prev = VsmxDecompileStackPop(&stack); + //SWPRINTF_ITEM("function %ls(%ls)", prev->str, args); + SWPRINTF_ITEM("%ls = function(%ls)", prev->str, args); + VsmxDecompileStackPush(&stack, &item); + free(prev); + } else if(stack->depth == 1) { // weird... + VsmxDecompileStack *prev, *var; + prev = VsmxDecompileStackPop(&stack); + var = VsmxDecompileStackPop(&stack); + SWPRINTF_ITEM("%ls = function %ls(%ls)", var->str, prev->str, args); + VsmxDecompileStackPush(&stack, &item); + free(prev); free(var); + } else { // weird... + warning("Function encountered at %d, but more than one item found on stack!", i); + VsmxDecompileStack *prev; + prev = VsmxDecompileStackPop(&stack); + SWPRINTF_ITEM("%ls = function(%ls)", prev->str, args); + VsmxDecompileStackPush(&stack, &item); + free(prev); + } + if(funcEndStmtStyle) + i += 2; + else + i++; + } else if(i+1 < in->codeGroups && in->code[i+1].id == VID_SECT_START && in->code[i].val.u32 == i+2) { + // function abc() style? + VsmxDecompileStack *prev; + prev = VsmxDecompileStackPop(&stack); + SWPRINTF_ITEM("function %ls(%ls)", prev->str, args); + VsmxDecompileStackPush(&stack, &item); + free(prev); + + } + else { + warning("Unexpected function definition syntax at %d!", i); + SWPRINTF_ITEM("function __%u__(%ls)", in->code[i].val.u32, args); + VsmxDecompileStackPush(&stack, &item); + } + } break; + + case VID_ARRAY_INDEX: { + VsmxDecompileStack *idx, *parent; + // TODO: check that the number supplied in in->code[i].val.u32 isn't used (always is 0)! + idx = VsmxDecompileStackPop(&stack); + parent = VsmxDecompileStackPop(&stack); + SWPRINTF_ITEM("%ls[%ls]", parent->str, idx->str); + VsmxDecompileStackPush(&stack, &item); + free(idx); + free(parent); + } break; + case VID_ARRAY_INDEX_ASSIGN: { + VsmxDecompileStack *val, *idx, *parent; + val = VsmxDecompileStackPop(&stack); + idx = VsmxDecompileStackPop(&stack); + parent = VsmxDecompileStackPop(&stack); + SWPRINTF_ITEM("%ls[%ls] = %ls", parent->str, idx->str, val->str); + VsmxDecompileStackPush(&stack, &item); + free(val); + free(idx); + free(parent); + } break; + + case VID_CALL_FUNC: + case VID_CALL_METHOD: + case VID_CALL_INBUILT: + { + if(stack->depth < in->code[i].val.u32) { + error("Not enough arguments to perform requested function call at group %d.", i); + return 1; + } + VsmxDecompileStack *prev; + if(in->code[i].val.u32 > 0) { + uint arg; + strwcpy(item.str, " )", MAX_TEXT_LEN); + for(arg = 0; arg < in->code[i].val.u32; arg++) { + prev = VsmxDecompileStackPop(&stack); + //swprintf(item.str, MAX_TEXT_LEN, L"%s%s", prev->str, item.str); + //SWPRINTF_ITEM("%ls%ls", prev->str, item.str); + if(arg+1 < in->code[i].val.u32) { + //swprintf(item.str, MAX_TEXT_LEN, L", %s", item.str); + //SWPRINTF_ITEM(", %ls", item.str); + memmove(prev->str + 2, prev->str, (wcslen(prev->str)+1)*sizeof(wchar)); + //wcscat(prev->str, L", "); + prev->str[0] = ','; + prev->str[1] = ' '; + wcscat(prev->str, item.str); + } else + wcscat(prev->str, item.str); + wcscpy(item.str, prev->str); + free(prev); + } + // function name + prev = VsmxDecompileStackPop(&stack); + //swprintf(item.str, MAX_TEXT_LEN, L"%s( %s", prev->str, item.str); + //SWPRINTF_ITEM("%ls( %ls", prev->str, item.str); + wcscat(prev->str, L"( "); + wcscat(prev->str, item.str); + wcscpy(item.str, prev->str); + } + else { + prev = VsmxDecompileStackPop(&stack); + //swprintf(item.str, MAX_TEXT_LEN, L"%s( )", prev->str); + SWPRINTF_ITEM("%ls()", prev->str); + } + free(prev); + VsmxDecompileStackPush(&stack, &item); + } break; + + + // this is either an OR or a for loop + case VID_JUMP_TRUE: { + VsmxDecompileStack *prev; + prev = VsmxDecompileStackPop(&stack); + + // check for FOR loop + // !!! look ahead used !!! + if( + // check that next group is a sect start + i+1 < in->codeGroups && in->code[i+1].id == VID_SECT_START + // check that end of for stmt is a jump back + && in->code[i].val.u32 < in->codeGroups && in->code[in->code[i].val.u32-1].id == VID_SECT_START && in->code[in->code[i].val.u32-1].val.u32 == stmtStart + // end of for loop should be after end of for stmt + && in->code[i+1].val.u32 > in->code[i].val.u32 + // check that end of for loop jumps back to incrementor + && in->code[i+1].val.u32 < in->codeGroups && in->code[in->code[i+1].val.u32-1].id == VID_SECT_START && in->code[in->code[i+1].val.u32-1].val.u32 == i+2 + ) { + // assume for loop + SWPRINTF_ITEM("for(; %ls /* jump to %d */; ", prev->str, in->code[i].val.u32); + VsmxDecompileStackPush(&stack, &item); + + forStmtEnd = in->code[i].val.u32; + // push for end marker of loop + mItem.src = i+1; + mItem.loc = in->code[i+1].val.u32; + VsmxDecompMarkStackPush(&mStack, &mItem); + // push for end bracket on for() + mItem.src = i; + mItem.loc = in->code[i].val.u32; + VsmxDecompMarkStackPush(&mStack, &mItem); + i++; + } else { + // assume OR + SWPRINTF_ITEM("( %ls ) || /* ends at %d */ ( ", prev->str, in->code[i].val.u32); + VsmxDecompileStackPush(&stack, &item); + + mItem.src = i; + mItem.loc = in->code[i].val.u32; + VsmxDecompMarkStackPush(&mStack, &mItem); + } + free(prev); + endStmtConcat++; + } break; + + // section markers + case VID_SECT_START: if(!op[0]) { + // this is either a function start or "else" section, or end of loop + if(mStack && (in->code[mStack->src].id == VID_JUMP_FALSE || (in->code[mStack->src].id == VID_SECT_START && in->code[i].val.u32 < i))) { + // check whether else or end of while + if(in->code[i].val.u32 < i) { + // assume end of loop + SWPRINTF(op, 50, L"} %%ls/* jump back to %d */\n", in->code[i].val.u32); + // push dummy element + VsmxDecompileStackPush(&stack, &item); + if(indent > 0) indent--; // this should always be true + // prevent a section start as this really is an end of section + notSectStart = TRUE; + // remove top of marker stack + if(mStack->loc == i+1) + VsmxDecompMarkStackPop(&mStack); + else + warning("Unexpected loop structure at %d!", i); + } else { + // else segment + // pop top of stack off here, as it's a little weird + if(mStack->loc == i+1) { + if(indent > 0) indent--; // this should always be true + else warning("Internal state nesting error!"); + + if(stack) + warning("Unexpected elements found in stack at %d!", i); + + VsmxDecompMarkStackPop(&mStack); + } else { + warning("Unexpected else stack structure at %d!", i); + } + if(stack) { + // inline else + wcscat(stack->str, L" : "); + mItem.src = i; + mItem.loc = in->code[i].val.u32; + VsmxDecompMarkStackPush(&mStack, &mItem); + break; + } else { + SWPRINTF(op, 50, L"} else %%ls{ /* ends at %d */\n", in->code[i].val.u32); + // push dummy element + VsmxDecompileStackPush(&stack, &item); + } + } + } else if(mStack && in->code[mStack->src].id == VID_JUMP_TRUE && in->code[mStack->src].val.u32 == i+1) { + // for loop end of stmt probably + if(stack) warning("Unexpected stack at end of for at %d", i); + // TODO: need to fix this so it doesn't go on a new line + SWPRINTF(op, 50, L"%%ls /* return to %d */) {\n", in->code[i].val.u32); + // push dummy element + VsmxDecompileStackPush(&stack, &item); + indentAdd++; + // prevent a section start (this is a special case) + notSectStart = TRUE; + // remove top of marker stack + VsmxDecompMarkStackPop(&mStack); + + } else { + // function start + SWPRINTF(op, 50, L"%%ls { /* ends at %d */\n", in->code[i].val.u32); // wcscpy(op, L"%s {\n"); + } + } + case VID_JUMP_FALSE: if(!op[0]) { + // !!! look ahead! + if(in->code[i].val.u32-1 >= in->codeGroups) { + error("Invalid jump reference supplied at %d", i); + return 1; + } + // check whether this is an "if" or "while" + if(in->code[in->code[i].val.u32-1].id == VID_SECT_START && in->code[in->code[i].val.u32-1].val.u32 == stmtStart) { + // while loop + SWPRINTF(op, 50, L"while( %%s ) { /* ends at %d */\n", in->code[i].val.u32); // wcscpy(op, L"while( %s ) {\n"); + } else { + // if statement + if(stack->depth > endStmtConcat-1) { + // inline if - special case + wchar tmp[MAX_TEXT_LEN] = {0}; + + while(endStmtConcat--) { + VsmxDecompileStack *prev; + prev = VsmxDecompileStackPop(&stack); + wcscat(prev->str, tmp); + wcscpy(tmp, prev->str); + free(prev); + } + endStmtConcat = 2; + SWPRINTF_ITEM("( %ls ? /* ends at %d */ ", tmp, in->code[i].val.u32); + VsmxDecompileStackPush(&stack, &item); + mItem.src = i; + mItem.loc = in->code[i].val.u32; + VsmxDecompMarkStackPush(&mStack, &mItem); + break; + } else { + SWPRINTF(op, 50, L"if( %%s ) { /* ends at %d */\n", in->code[i].val.u32); // wcscpy(op, L"if( %s ) {\n"); + } + } + } + if(!notSectStart) { + mItem.src = i; + mItem.loc = in->code[i].val.u32; + VsmxDecompMarkStackPush(&mStack, &mItem); + indentAdd++; + } // fall through to end of statement + + case VID_RETURN: if(!op[0]) wcscpy(op, L"return %s;\n"); + case VID_END_STMT: if(!op[0]) wcscpy(op, L"%s;\n"); + { + // end of statement following conditional - skip + if(in->code[i].id == VID_END_STMT && i && (in->code[i-1].id == VID_JUMP_TRUE || in->code[i-1].id == VID_JUMP_FALSE)) + break; + + if(stack) { + if(stack->depth > endStmtConcat-1) { + warning("End of statement but more stack elements than expected at groups %d-%d!", stmtStart, i); + } + + + // TODO: remove debugging line + fwprintf(out, L"/*%d*/\t", stmtStart); + + + writeTabs(out, indent); + + while(endStmtConcat--) { + VsmxDecompileStack *prev; + prev = VsmxDecompileStackPop(&stack); + wcscat(prev->str, item.str); + wcscpy(item.str, prev->str); + free(prev); + } + fwprintf(out, op, item.str); + endStmtConcat = 1; + fflush(out); // TODO: remove later + + VsmxDecompileStackDestroy(&stack); + stmtStart = i+1; + indent += indentAdd; + indentAdd = 0; + } else + warning("No stack at end of statement at %d!", i); + } break; + + + + case VID_END: + if(i+1 < in->codeGroups) { + warning("End marker found at %d, but is not end of code!", i); + } + break; + + default: + warning("Unknown id 0x%x at %d", in->code[i].id, i); + SWPRINTF_ITEM("<< UNKNOWN 0x%x 0x%x >>", in->code[i].id, in->code[i].val.u32); + VsmxDecompileStackPush(&stack, &item); + } + + /*if(is_value) { + item.item = in->code[i]; + VsmxDecompileStackPush(&stack, &item); + }*/ + } + return 0; +} +void freeVsmxMem(VsmxMem* vm) { + free(vm->code); + free(vm->text); + free(vm->prop); + free(vm->names); + free(vm->pText); + free(vm->pProp); + free(vm->pNames); + free(vm); +} diff --git a/vsmx.h b/vsmx.h index 871501b..f075a9c 100644 --- a/vsmx.h +++ b/vsmx.h @@ -1,57 +1,57 @@ - -#ifndef __VSMX_H__ -#define __VSMX_H__ - -typedef wchar_t wchar; - -#define VSMX_SIGNATURE 0x584D5356 // "VSMX" -#define VSMX_VERSION 0x00010000 -PACK_STRUCT(VSMXHeader, { - uint32 sig; - uint32 ver; - - uint32 codeOffset; - uint32 codeLength; - - uint32 textOffset; - uint32 textLength; - uint32 textEntries; - - uint32 propOffset; - uint32 propLength; - uint32 propEntries; - - uint32 namesOffset; - uint32 namesLength; - uint32 namesEntries; -}); - -PACK_STRUCT(VSMXGroup, { - uint32 id; - union { - uint32 u32; - float f; - } val; -}); - -typedef struct { - VSMXGroup* code; - wchar *text, *prop; - char *names; - wchar **pText, **pProp; - char **pNames; - - uint codeGroups, numText, numProp, numNames, lenText, lenProp, lenNames; -} VsmxMem; - - -VsmxMem* readVSMX(FILE* fp); -VsmxMem* readVSMXMem(const void *in); -void writeVSMX(FILE* fp, VsmxMem* in); -void* writeVSMXMem(unsigned int* len, VsmxMem* in); -int VsmxDecode(VsmxMem* in, FILE* out); -VsmxMem* VsmxEncode(FILE* in); -int VsmxDecompile(VsmxMem* in, FILE* out); -void freeVsmxMem(VsmxMem* vm); - -#endif + +#ifndef __VSMX_H__ +#define __VSMX_H__ + +typedef wchar_t wchar; + +#define VSMX_SIGNATURE 0x584D5356 // "VSMX" +#define VSMX_VERSION 0x00010000 +PACK_STRUCT(VSMXHeader, { + uint32 sig; + uint32 ver; + + uint32 codeOffset; + uint32 codeLength; + + uint32 textOffset; + uint32 textLength; + uint32 textEntries; + + uint32 propOffset; + uint32 propLength; + uint32 propEntries; + + uint32 namesOffset; + uint32 namesLength; + uint32 namesEntries; +}); + +PACK_STRUCT(VSMXGroup, { + uint32 id; + union { + uint32 u32; + float f; + } val; +}); + +typedef struct { + VSMXGroup* code; + wchar *text, *prop; + char *names; + wchar **pText, **pProp; + char **pNames; + + uint codeGroups, numText, numProp, numNames, lenText, lenProp, lenNames; +} VsmxMem; + + +VsmxMem* readVSMX(FILE* fp); +VsmxMem* readVSMXMem(const void *in); +void writeVSMX(FILE* fp, VsmxMem* in); +void* writeVSMXMem(unsigned int* len, VsmxMem* in); +int VsmxDecode(VsmxMem* in, FILE* out); +VsmxMem* VsmxEncode(FILE* in); +int VsmxDecompile(VsmxMem* in, FILE* out); +void freeVsmxMem(VsmxMem* vm); + +#endif diff --git a/xml.h b/xml.h index 8f787e6..220a8fb 100644 --- a/xml.h +++ b/xml.h @@ -1,37 +1,37 @@ - -#ifndef __RCOXML_H__ -#define __RCOXML_H__ - -#include "general.h" -#include "rcomain.h" - -Bool write_xml(rRCOFile* rco, FILE* fp, char* textDir, Bool textXmlOut, int sndDumped, Bool vsmxConv); -rRCOFile* read_xml(char* fn); - - -#define RCOXML_TABLE_2ND_DIM 20 -typedef char ((*RcoTableMap)[RCOXML_TABLE_2ND_DIM]); // doesn't make sense... I want a pointer to an array, not an array of pointers... -extern RcoTableMap RCOXML_TABLE_DATA_COMPRESSION; - -extern RcoTableMap RCOXML_TABLE_TEXT_LANG; -extern RcoTableMap RCOXML_TABLE_TEXT_FMT; -extern RcoTableMap RCOXML_TABLE_IMG_FMT; -extern RcoTableMap RCOXML_TABLE_MODEL_FMT; -extern RcoTableMap RCOXML_TABLE_SOUND_FMT; -extern RcoTableMap RCOXML_TABLE_REFTYPE; - - -extern RcoTagMap RCOXML_TABLE_TAGS; - -extern uint RCOXML_TABLE_TAGS_NUM; - -extern RcoTableMap RCOXML_TABLE_NAMES; - - - - -void rcoxml_int_to_text(uint in, const RcoTableMap map, char* out); -Bool rcoxml_text_to_int(char* s, const RcoTableMap map, uint* out); - - -#endif + +#ifndef __RCOXML_H__ +#define __RCOXML_H__ + +#include "general.h" +#include "rcomain.h" + +Bool write_xml(rRCOFile* rco, FILE* fp, char* textDir, Bool textXmlOut, int sndDumped, Bool vsmxConv); +rRCOFile* read_xml(char* fn); + + +#define RCOXML_TABLE_2ND_DIM 20 +typedef char ((*RcoTableMap)[RCOXML_TABLE_2ND_DIM]); // doesn't make sense... I want a pointer to an array, not an array of pointers... +extern RcoTableMap RCOXML_TABLE_DATA_COMPRESSION; + +extern RcoTableMap RCOXML_TABLE_TEXT_LANG; +extern RcoTableMap RCOXML_TABLE_TEXT_FMT; +extern RcoTableMap RCOXML_TABLE_IMG_FMT; +extern RcoTableMap RCOXML_TABLE_MODEL_FMT; +extern RcoTableMap RCOXML_TABLE_SOUND_FMT; +extern RcoTableMap RCOXML_TABLE_REFTYPE; + + +extern RcoTagMap RCOXML_TABLE_TAGS; + +extern uint RCOXML_TABLE_TAGS_NUM; + +extern RcoTableMap RCOXML_TABLE_NAMES; + + + + +void rcoxml_int_to_text(uint in, const RcoTableMap map, char* out); +Bool rcoxml_text_to_int(char* s, const RcoTableMap map, uint* out); + + +#endif diff --git a/xmlread.c b/xmlread.c index 074650d..82400dc 100644 --- a/xmlread.c +++ b/xmlread.c @@ -1,1508 +1,1508 @@ -// TODO: replace xmlstrcmp with strcasecmp - -#define LIBXML_STATIC -#include -#include -#include -#include -#include - -#include "rcomain.h" -#include "xml.h" -#include "strfuncs.h" -#include "configscan.h" - -#define _X(s) ((const xmlChar*)s) -#define INTERPRET_AS_FLOAT(n) (*((float*)(&(n)))) - -#define strtof(a,b) (float)strtod(a,b) - - -/* -typedef struct { - rRCORef* ref; - char* label; -} xmlrco_read_fix_refs; -*/ -typedef struct { - //xmlChar *ptrText, *ptrImg, *ptrSound, *ptrModel, *ptrObj, *ptrAnim; - xmlChar* textData; // labels data, that is - //xmlrco_read_fix_refs* refs; - //uint refsCnt; -} rcoxml_read_fixes; - - - -void parse_entry(xmlNodePtr node, rRCOEntry* entry, rRCOFile* rco, rcoxml_read_fixes* fixes); -//Bool rcoxml_text_to_int(char* s, const RcoTableMap map, uint* out); -uint rcoxml_add_label(char** labels, uint* labelsLen, char* label, Bool eventQuirk); -uint rcoxml_add_label_reordering(char* newLabels, uint* labelPos, char* label); -void rcoxml_reorder_labels(char* newLabels, uint* labelPos, rRCOFile* rco, rRCOEntry* entry); -int label_reorder_qsort(const rRCOEntry** a, const rRCOEntry** b); -void parse_obj_extra(xmlNodePtr node, rRCOEntry* entry); -void parse_anim_extra(xmlNodePtr node, rRCOEntry* entry); -xmlChar* rcoxml_get_unknown_attrib(xmlNodePtr node, uint num); -uint32 rcoxml_parse_value(char* s); -Bool rcoxml_parse_ref(char* val, rRCORef* out); -void rcoxml_fix_refs(rRCOEntry* entry, rRCOFile* rco); -Bool rcoxml_fix_ref(rRCORef* ref, rRCOFile* rco); -void rcoxml_fix_ptrs(rRCOEntry*** sect, uint* sectCnt, rRCOFile* rco, const char* text); -uint split_comma_list(char* s); -char* strtrimr(char* in); -char* expand_fname_to_fmt(char* in, char type); -Bool parse_text_xml(char* fn, rRCOFile* rco, rRCOEntry* entry); - -rRCOFile* read_xml(char* fn) { - xmlDocPtr doc; - xmlNodePtr node; - Bool bValidDoc = FALSE; - - rcoxml_read_fixes fixes; // post fixes need to be done - - //fixes.ptrText = fixes.ptrImg = fixes.ptrSound = fixes.ptrModel = fixes.ptrObj = fixes.ptrAnim = NULL; - fixes.textData = NULL; - //fixes.refsCnt = 0; - //fixes.refs = (xmlrco_read_fix_refs*)malloc(1); // dummy malloc to allow realloc to work later on - - // parse from stdin? - if(!strcmp(fn, "-")) { - char* buf = NULL; - int bufsize = 0; - while(!feof(stdin)) { - buf = (char*)realloc(buf, bufsize+65536); - uint readAmt = fread(buf+bufsize, 1, 65536, stdin); - if(!readAmt) break; - bufsize += readAmt; - } - fclose(stdin); - if(!(doc = xmlParseMemory(buf, bufsize))) { - error("Can't parse given XML data."); - return NULL; - } - free(buf); - } else { - if(!(doc = xmlParseFile(fn))) { - error("Can't parse given XML file %s.", fn); - return NULL; - } - } - if((node = xmlDocGetRootElement(doc))) - bValidDoc = (xmlStrcmp(node->name, _X("RcoFile")) ? FALSE : TRUE); - if(bValidDoc) bValidDoc = (node->xmlChildrenNode ? TRUE : FALSE); - - xmlNodePtr nodeChild; - if(bValidDoc) { // valid document must contain at least one thing under RcoFile (MainTree) - nodeChild = node->xmlChildrenNode; - while(nodeChild->next && nodeChild->type != XML_ELEMENT_NODE) - nodeChild = nodeChild->next; - bValidDoc = (nodeChild->type == XML_ELEMENT_NODE); - } - - if(!bValidDoc) { - error("Invalid XML file."); - xmlFreeDoc(doc); - return NULL; - } - - - rRCOFile* rco = (rRCOFile*)malloc(sizeof(rRCOFile)); - rco->labelsLen = rco->eventsLen = 0; - //rco->numPtrText = rco->numPtrImg = rco->numPtrModel = rco->numPtrSound = rco->numPtrObj = rco->numPtrAnim = 0; - rco->tblVSMX = rco->tblText = rco->tblImage = rco->tblSound = rco->tblModel = rco->tblFont = rco->tblObj = rco->tblAnim = NULL; - - // version id - rco->verId = 0x71; // assumed default - xmlChar* verId = xmlGetProp(node, _X("minFirmwareVer")); - if(verId) { - uint verIdInt = 0; - float verIdFlt = 0; - if(sscanf((const char*)verId, "unknownId%i", &verIdInt)) - rco->verId = verIdInt; - else if(!xmlStrcmp(verId, _X("ps3"))) // for compatibility - rco->verId = 0x107; - else if(sscanf((const char*)verId, "%f", &verIdFlt)) { - if(verIdFlt < 1.0) { - warning("[line %d] Invalid value for 'minFirmwareVer'.", node->line); - } else if(verIdFlt < 1.5) { - rco->verId = 0x70; - } else if(verIdFlt < 2.6) { - rco->verId = 0x71; - } else if(verIdFlt < 2.7) { - rco->verId = 0x90; - } else if(verIdFlt < 2.8) { - rco->verId = 0x95; - } else if(verIdFlt < 3.5) { - rco->verId = 0x96; - } else if(verIdFlt <= 6.2) { - rco->verId = 0x100; - } else { - warning("[line %d] Unknown ID for firmware version '%f'.", node->line, verIdFlt); - } - } - else - warning("[line %d] Unknown value for 'minFirmwareVer'.", node->line); - xmlFree(verId); - } - - // XML version - xmlChar* xmlVer = xmlGetProp(node, _X("rcomageXmlVer")); - if(xmlVer) { - float xmlVerFlt; - if(sscanf((const char*)xmlVer, "%f", &xmlVerFlt)) { - if(xmlVerFlt > APPVER) - warning("This XML file was generated by a newer version (v%f) of rcomage\nand may not read properly with this version.", xmlVerFlt); - } - else - warning("[line %d] Bad value for 'rcomageXmlVer'.\n So... do you have a reason for fiddling with this?? >_<", node->line); - xmlFree(xmlVer); - } - - rco->umdFlag = 0; // assumed default - xmlChar* umdFlag = xmlGetProp(node, _X("UMDFlag")); - if(umdFlag) { - rco->umdFlag = atoi((char*)umdFlag); - if(rco->umdFlag > 0xF || rco->umdFlag < 0) { - warning("The UMD Flag must be between 0 and 15, defaulting to 0."); - rco->umdFlag = 0; - } - xmlFree(umdFlag); - } - - rco->ps3 = FALSE; - xmlChar* ps3Flag = xmlGetProp(node, _X("type")); - if(ps3Flag) { - if(!xmlStrcmp(ps3Flag, _X("ps3"))) - rco->ps3 = TRUE; - else if(xmlStrcmp(ps3Flag, _X("psp"))) - warning("Unknown type %s", (char*)ps3Flag); - xmlFree(ps3Flag); - } - configLoadObjmap(rco->ps3); - configLoadAnimmap(rco->ps3); - - // TODO: check is this is correct - /* rco->labels = (char*)malloc(1); - rco->labels[0] = '\0'; - rco->events = (char*)malloc(1); - rco->events[0] = '\0'; */ - rco->labels = rco->events = NULL; - - rco->eSwap = rco->ps3; - - /* - rco->tblMain.id = rco->tblMain.type = 0; - rco->tblMain.labelOffset = 0; - rco->tblMain.numSubentries = rco->tblMain.extraLen = 0; - */ - - parse_entry(nodeChild, &(rco->tblMain), rco, &fixes); - // TODO: ensure the first table _is_ the main table (and possibly then verify tree structure) - - // - post fixes - - // fix all obj/anim refs - rcoxml_fix_refs(&(rco->tblMain), rco); - - xmlFreeDoc(doc); - - rco_fix_decomp_sizes(rco, &rco->tblMain); - - // label reordering - really optional, but does help to make RCOs similar to Sony's - { - char* newLabels = (char*)malloc(rco->labelsLen); - memset(newLabels, 0, rco->labelsLen); - uint labelPos = 0; - - // don't forget to add the main table label :P - if(rco->tblMain.labelOffset != RCO_NULL_PTR) - rco->tblMain.labelOffset = rcoxml_add_label_reordering(newLabels, &labelPos, rco->labels + rco->tblMain.labelOffset); - - // we re-order in order of IDs - uint i; - rRCOEntry** sList = make_sorted_list_of_subentries(&rco->tblMain, label_reorder_qsort); - for(i=0; itblMain.numSubentries; i++) - rcoxml_reorder_labels(newLabels, &labelPos, rco, sList[i]); - free(sList); - - if(labelPos < rco->labelsLen) { - // error! - free(newLabels); - } else { - free(rco->labels); - rco->labels = newLabels; - } - } - - return rco; -} - -void parse_entry(xmlNodePtr node, rRCOEntry* entry, rRCOFile* rco, rcoxml_read_fixes* fixes) { - uint i, j; - Bool knownEntryType = TRUE; - - // crap all over the memory in "entry" so we don't screw stuff over later on - entry->id = entry->type = 0; - entry->numSubentries = 0; - entry->rco = rco; - entry->parent = entry->firstChild = entry->lastChild = entry->prev = entry->next = NULL; - entry->labelOffset = RCO_NULL_PTR; - entry->extra = NULL; - entry->extraLen = 0; - entry->srcFile[0] = '\0'; - entry->srcAddr = entry->srcLen = entry->srcLenUnpacked = entry->srcCompression = 0; - entry->srcBuffer = NULL; - - for(i=0; iname, _X(RCOXML_TABLE_TAGS[i][j]))) { - entry->id = i; - entry->type = j; - break; - } - j++; - } - if(entry->id) break; - } - - // check unknown type tags - if(!entry->id) { - knownEntryType = FALSE; - if(!sscanf((const char*)node->name, "Unknown_%i_%i", (int*)&entry->id, (int*)&entry->type)) { - char tag[RCOXML_TABLE_2ND_DIM + 11]; - for(i=0; iname, tag, &entry->type)) { - entry->id = i; - break; - } - } - if(!entry->id) { - // since all the known tags have entry->id >0, we can do this - error("[line %d] Unknown entry type '%s'", node->line, node->name); - exit(99); - } - } - } - - entry->offset = node->line; // use the line number later on - - { // label - xmlChar* label = xmlGetProp(node, _X("name")); - if(label) { - entry->labelOffset = rcoxml_add_label(&(rco->labels), &(rco->labelsLen), (char*)label, FALSE); - xmlFree(label); - } - } - - if(knownEntryType) { - - // read src (used by section below this, so there is some importance with this ordering) - { - xmlChar* src = xmlGetProp(node, _X("src")); - if(src) { - strcpy(entry->srcFile, (const char*)src); - xmlFree(src); - - entry->srcCompression = 0; - src = xmlGetProp(node, _X("srcRange")); - if(src) { - // parse src params - char compr[50] = "\0"; - if(sscanf((const char*)src, "%i-%i%50s", &(entry->srcAddr), &(entry->srcLen), compr) >= 2) { - entry->srcLen -= entry->srcAddr; - entry->srcLenUnpacked = entry->srcLen; // default - assume no compression - if(compr[0]) { - // TODO: handle situations where uncompressed size is not specified - if(sscanf(compr, ";zlib[%u]", &(entry->srcLenUnpacked)) == 1) { // TODO: a temporary fix (prolly need to add more compression algos, since sscanf seems to be rather basic in retrieving %s) - rcoxml_text_to_int("zlib", RCOXML_TABLE_DATA_COMPRESSION, &(entry->srcCompression)); - } else if(!strcmp(compr, ";rco")) { - entry->srcCompression = RCO_DATA_COMPRESSION_RCO; - } else { - warning("[line %d] Invalid syntax in srcRange", node->line); - } - } - } else - warning("[line %d] Invalid syntax in srcRange", node->line); - - xmlFree(src); - } else { - entry->srcAddr = 0; - entry->srcLen = entry->srcLenUnpacked = filesize(entry->srcFile); // won't work if filename contains a '*' - } - } - } - - - - // extra - #define RCOXML_READ_ATTRIB_AS_INT(nd, name, tbl, dest, undefmsg) { \ - xmlChar* __attr = xmlGetProp(nd, _X(name)); \ - if(__attr) { \ - if(!rcoxml_text_to_int((char*)__attr, tbl, dest)) { \ - warning("[line %d] Unrecognised value '%s'", nd->line, __attr); \ - } else { \ - xmlFree(__attr); \ - } \ - } else { \ - warning("[line %d] %s", nd->line, undefmsg); \ - } \ - } - - switch(entry->id) { - case RCO_TABLE_IMG: case RCO_TABLE_MODEL: - if(entry->type == 1) { - entry->extraLen = sizeof(rRCOImgModelEntry); - entry->extra = malloc(entry->extraLen); - // default value - ((rRCOImgModelEntry*)entry->extra)->format = (entry->id == RCO_TABLE_IMG ? RCO_IMG_GIM : RCO_MODEL_GMO); - ((rRCOImgModelEntry*)entry->extra)->compression = RCO_DATA_COMPRESSION_NONE; - - if(entry->id == RCO_TABLE_IMG) { - RCOXML_READ_ATTRIB_AS_INT(node, "format", RCOXML_TABLE_IMG_FMT , &((rRCOImgModelEntry*)entry->extra)->format, "No format attribute defined, defaulting to GIM."); - } else { - RCOXML_READ_ATTRIB_AS_INT(node, "format", RCOXML_TABLE_MODEL_FMT , &((rRCOImgModelEntry*)entry->extra)->format, "No format attribute defined, defaulting to GMO."); - } - RCOXML_READ_ATTRIB_AS_INT(node, "compression", RCOXML_TABLE_DATA_COMPRESSION , &((rRCOImgModelEntry*)entry->extra)->compression, "No compression attribute defined, defaulting to 'uncompressed'."); - ((rRCOImgModelEntry*)entry->extra)->unkCompr = 0; - xmlChar* unk = xmlGetProp(node, _X("unknownByte")); - if(unk) { - sscanf((const char*)unk, "%i", &((rRCOImgModelEntry*)entry->extra)->unkCompr); - xmlFree(unk); - } - } - break; - case RCO_TABLE_SOUND: - if(entry->type == 1) { - entry->extraLen = sizeof(rRCOSoundEntry); - entry->extra = malloc(entry->extraLen); - // default value - rRCOSoundEntry* se = (rRCOSoundEntry*)entry->extra; - se->format = RCO_SOUND_VAG; - se->channels = 1; - se->channelData = NULL; - - RCOXML_READ_ATTRIB_AS_INT(node, "format", RCOXML_TABLE_SOUND_FMT, (uint*)&(se->format), "No format attribute defined, defaulting to VAG."); - - xmlChar* chStr = xmlGetProp(node, _X("channels")); - uint16 ch = 0; - if(chStr) - ch = (uint16)strtol((const char*)chStr, NULL, 10); - if(ch > 0) - se->channels = ch; - else if(!strcasecmp(entry->srcFile + strlen(entry->srcFile) -4, ".wav") && se->format == RCO_SOUND_VAG) - se->channels = 0; // we have to fix later - else - warning("[line %d] Number of channels either not specified, or no valid value found.", node->line); - xmlFree(chStr); - - - if(se->channels) { - se->channelData = (uint32*)malloc(se->channels * sizeof(uint32)*2); - memset(se->channelData, 0, se->channels * sizeof(uint32)*2); - - xmlChar* srcPartsX = xmlGetProp(node, _X("srcParts")); - if(srcPartsX) { - char* srcParts = (char*)srcPartsX; - uint numParts = split_comma_list(srcParts); - if(numParts == se->channels) { - for(i=0; ichannels; i++) { - while(isspace(srcParts[0])) srcParts++; // skip whitespace - strtrimr(srcParts); - if(sscanf(srcParts, "%i@%i", (int*)&(se->channelData[i*2]), (int*)&(se->channelData[i*2 +1])) != 2) - warning("[line %d] Invalid syntax for part '%s'", node->line, srcParts); - srcParts += strlen(srcParts) +1; - } - } else - warning("[line %d] Number of defined parts does not match number of defined sound channels!", node->line); - - xmlFree(srcPartsX); - } else if(strchr(entry->srcFile, '*')) { - // check files - char* srcFileFmt = expand_fname_to_fmt(entry->srcFile, 'd'); - // TODO: loop thru each file, record size and add to buffer - if(strlen(srcFileFmt) < MAX_FILENAME_LEN) { - char srcFile[MAX_FILENAME_LEN]; - uint curPos = 0; - void* srcBufferTmp = malloc(1); - for(i=0; ichannels; i++) { - sprintf(srcFile, srcFileFmt, i); - if(file_exists(srcFile)) { - se->channelData[i*2] = filesize(srcFile); - if(se->channelData[i*2]) { - // read into srcBuffer - FILE* fp = fopen(srcFile, "rb"); - if(fp) { - srcBufferTmp = realloc(srcBufferTmp, curPos + ALIGN_TO_4(se->channelData[i*2])); - uint8* bufferPos = (uint8*)(srcBufferTmp) + curPos; - fileread(fp, bufferPos, se->channelData[i*2]); - fclose(fp); - } else - warning("[line %d] Can't find or open file '%s'", node->line, srcFile); - } - } else { - se->channelData[i*2] = 0; - warning("[line %d] Can't find or open file '%s'", node->line, srcFile); - } - se->channelData[i*2+1] = curPos; - curPos += ALIGN_TO_4(se->channelData[i*2]); - } - entry->srcBuffer = srcBufferTmp; - entry->srcLen = entry->srcLenUnpacked = curPos; - } // else, they're trying to hack us... :/ - free(srcFileFmt); - } else if(se->channels == 1 && entry->srcLen) { - // err, if there's just one channel, we can just use the srcRange, lol - se->channelData[0] = entry->srcLen; - se->channelData[1] = 0; - } else { - warning("[line %d] Cannot determine sizes of sound channels!", node->line); - } - } - - } - break; - - case RCO_TABLE_TEXT: - if(entry->type == 1) { - Bool xmlInput = FALSE; - - entry->extraLen = sizeof(rRCOTextEntry); - entry->extra = malloc(entry->extraLen); - // default value - rRCOTextEntry* te = (rRCOTextEntry*)entry->extra; - te->lang = 0; - te->format = RCO_TEXT_FMT_UTF16; - te->numIndexes = 0; - - RCOXML_READ_ATTRIB_AS_INT(node, "language", RCOXML_TABLE_TEXT_LANG, (uint*)&te->lang, "No language attribute defined - RCO may no longer be valid."); - RCOXML_READ_ATTRIB_AS_INT(node, "format", RCOXML_TABLE_TEXT_FMT, (uint*)&te->format, "No destination format defined - assuming UTF16."); - - if(te->format != RCO_TEXT_FMT_UTF16 && !rco->ps3) { - warning("[line %d] Non UTF-16 text not supported on the PSP.", node->line); - } - - // text indexes - xmlChar* textEntriesX = xmlGetProp(node, _X("entries")); - if(textEntriesX) { - char* textEntries = (char*)textEntriesX; - te->numIndexes = split_comma_list(textEntries); - te->indexes = (RCOTextIndex*)malloc(te->numIndexes * sizeof(RCOTextIndex)); - memset(te->indexes, 0, te->numIndexes * sizeof(RCOTextIndex)); - - for(i=0; inumIndexes; i++) { - while(isspace(textEntries[0])) textEntries++; // skip whitespace - strtrimr(textEntries); - te->indexes[i].labelOffset = rcoxml_add_label(&rco->labels, &rco->labelsLen, textEntries, FALSE); - textEntries += strlen(textEntries) +1; - } - - xmlFree(textEntriesX); - } else { // assume XML input - if(!(xmlInput = parse_text_xml(entry->srcFile, rco, entry))) - warning("[line %d] Text entries not specified! Assuming no entries.", node->line); - } - - if(!xmlInput && te->numIndexes) { - xmlChar* srcPartsX = xmlGetProp(node, _X("srcParts")); - if(srcPartsX) { - char* srcParts = (char*)srcPartsX; - uint numParts = split_comma_list(srcParts); - if(numParts == te->numIndexes) { - for(i=0; inumIndexes; i++) { - while(isspace(srcParts[0])) srcParts++; // skip whitespace - strtrimr(srcParts); - if(sscanf(srcParts, "%i@%i", (int*)&(te->indexes[i].length), (int*)&(te->indexes[i].offset)) != 2) - warning("[line %d] Invalid syntax for part '%s'", node->line, srcParts); - srcParts += strlen(srcParts) +1; - } - } else - warning("[line %d] Number of defined parts does not match number of defined text entries!", node->line); - - xmlFree(srcPartsX); - } else if(strchr(entry->srcFile, '*')) { - // check files - char* srcFileFmt = expand_fname_to_fmt(entry->srcFile, 's'); - // loop thru each file, record size and add to buffer - if(strlen(srcFileFmt) < MAX_FILENAME_LEN) { - char srcFile[MAX_FILENAME_LEN]; - uint curPos = 0; - void* srcBufferTmp = malloc(1); - for(i=0; inumIndexes; i++) { - sprintf(srcFile, srcFileFmt, rco->labels + te->indexes[i].labelOffset); - if(file_exists(srcFile)) { - te->indexes[i].length = filesize(srcFile); - if(te->indexes[i].length) { - // read into srcBuffer - FILE* fp = fopen(srcFile, "rb"); - if(fp) { - // do we have a BOM? - unsigned char bom[4] = {0x80, 0x80, 0x80, 0x80}; // dummy values that aren't used - char srcFmt[10] = "", destFmt[8]; - uint32 bom32le = UTF32_BOM; - uint32 bom32be = ENDIAN_SWAP(UTF32_BOM); - uint16 bom16le = UTF16_BOM; - uint16 bom16be = ENDIAN_SWAP(UTF16_BOM); - uint32 bom8 = UTF8_BOM; - uint bomLen = (te->format == RCO_TEXT_FMT_UTF32 ? 4 : (te->format == RCO_TEXT_FMT_UTF8 ? 3 : 2)); - make_iconv_charset(destFmt, te->format, rco->eSwap); - - fileread(fp, bom, 4); - if(!memcmp(bom, &bom32le, sizeof(bom32le))) { - strcpy(srcFmt, "utf-32le"); - bomLen = 4; - } else if(!memcmp(bom, &bom32be, sizeof(bom32be))) { - strcpy(srcFmt, "utf-32be"); - bomLen = 4; - } else if(!memcmp(bom, &bom16le, sizeof(bom16le))) { - strcpy(srcFmt, "utf-16le"); - bomLen = 2; - } else if(!memcmp(bom, &bom16be, sizeof(bom16be))) { - strcpy(srcFmt, "utf-16be"); - bomLen = 2; - } else if(!memcmp(bom, &bom8, 3)) { - strcpy(srcFmt, "utf-8"); - bomLen = 3; - } else { // don't convert - strcpy(srcFmt, destFmt); - bomLen = 0; - } - - if(bomLen != 4) { - fseek(fp, bomLen, SEEK_SET); - } - te->indexes[i].length -= bomLen; - - // re-use BOMlen to specify character width - bomLen = RCO_TEXT_FMT_CHARWIDTH(te->format); - srcBufferTmp = realloc(srcBufferTmp, curPos + ALIGN_TO_4(te->indexes[i].length+bomLen)); - uint8* bufferPos = (uint8*)srcBufferTmp + curPos; - if(strcmp(srcFmt, destFmt)) { - uint fPos = ftell(fp); - uint fSize; - fseek(fp, 0, SEEK_END); - fSize = ftell(fp) - fPos; - fseek(fp, fPos, SEEK_SET); - - uint8 *fBuf = (uint8*)malloc(fSize); - fileread(fp, fBuf, fSize); - iconv_t ic = iconv_open(destFmt, srcFmt); - // get rid of BOM made by iconv - iconv(ic, (char**)(&fBuf), (size_t*)(&fSize), (char**)&bom, (size_t*)(&bomLen)); - iconv(ic, (char**)(&fBuf), (size_t*)(&fSize), (char**)&bufferPos, (size_t*)(&te->indexes[i].length)); - iconv_close(ic); - } else - fileread(fp, bufferPos, te->indexes[i].length); - // add terminating null & any necessary padding - memset(bufferPos + (te->indexes[i].length), 0, ALIGN_TO_4(te->indexes[i].length+bomLen) - (te->indexes[i].length)); - - /* uint16 unisig; - if(te->indexes[i].length >= 2) - fileread(fp, &unisig, sizeof(unisig)); - if(te->indexes[i].length == 1 || unisig != UNICODE_SIGNATURE) { - te->indexes[i].length += 2; // add space for trailing null - fseek(fp, 0, SEEK_SET); - } - if(te->indexes[i].length <= 2) { - // this is probably blank - te->indexes[i].length = 0; - } else { - srcBufferTmp = realloc(srcBufferTmp, curPos + ALIGN_TO_4(te->indexes[i].length)); - uint8* bufferPos = (uint8*)srcBufferTmp + curPos; - fileread(fp, bufferPos, te->indexes[i].length-2); - // add terminating null & any necessary padding - memset(bufferPos + (te->indexes[i].length-2), 0, ALIGN_TO_4(te->indexes[i].length) - (te->indexes[i].length-2)); - } */ - fclose(fp); - } else - warning("[line %d] Can't find or open file '%s'", node->line, srcFile); - } - } else { - te->indexes[i].length = 0; - warning("[line %d] Can't find or open file '%s'", node->line, srcFile); - } - te->indexes[i].offset = curPos; - curPos += ALIGN_TO_4(te->indexes[i].length); - } - entry->srcBuffer = srcBufferTmp; - entry->srcLen = entry->srcLenUnpacked = curPos; - } // else, they're trying to hack us... :/ - free(srcFileFmt); - } else if(te->numIndexes == 1 && entry->srcLen) { - // err, if there's just one text, we can just use the srcRange, lol - te->indexes[0].length = entry->srcLen; - te->indexes[0].offset = 0; - } else { - warning("[line %d] Cannot determine sizes of text data!", node->line); - } - } - - } - break; - - case RCO_TABLE_FONT: - if(entry->type == 1) { - entry->extraLen = sizeof(rRCOFontEntry); - entry->extra = malloc(entry->extraLen); - rRCOFontEntry* rfe = (rRCOFontEntry*)entry->extra; - // default value - rfe->format = 1; - rfe->compression = 0; - rfe->unknown = 0; - rfe->unknown2 = 0; - - xmlChar* val; - val = xmlGetProp(node, _X("unknownShort1")); - sscanf((const char*)val, "%i", &rfe->format); - xmlFree(val); - val = xmlGetProp(node, _X("unknownShort2")); - sscanf((const char*)val, "%i", &rfe->compression); - xmlFree(val); - val = xmlGetProp(node, _X("unknownInt3")); - sscanf((const char*)val, "%i", &rfe->unknown); - xmlFree(val); - val = xmlGetProp(node, _X("unknownInt4")); - sscanf((const char*)val, "%i", &rfe->unknown2); - xmlFree(val); - } - break; - case RCO_TABLE_OBJ: - parse_obj_extra(node, entry); - break; - case RCO_TABLE_ANIM: - parse_anim_extra(node, entry); - break; - } - - - - // pointer sect - if(entry->type == 0 || (entry->type == 1 && entry->id == RCO_TABLE_VSMX)) { - - // shortcut table assignment - rRCOEntry** shortcutTbl = NULL; - switch(entry->id) { - case RCO_TABLE_VSMX: shortcutTbl = &(rco->tblVSMX); break; - case RCO_TABLE_TEXT: shortcutTbl = &(rco->tblText); break; - case RCO_TABLE_IMG: shortcutTbl = &(rco->tblImage); break; - case RCO_TABLE_SOUND: shortcutTbl = &(rco->tblSound); break; - case RCO_TABLE_MODEL: shortcutTbl = &(rco->tblModel); break; - case RCO_TABLE_FONT: shortcutTbl = &(rco->tblFont); break; - case RCO_TABLE_OBJ: shortcutTbl = &(rco->tblObj); break; - case RCO_TABLE_ANIM: shortcutTbl = &(rco->tblAnim); break; - } - if(shortcutTbl) { - if(*shortcutTbl) { - warning("[line %d] 'Container' tree redefinition (only one should exist in an RCO file).", node->line); - } - else { - *shortcutTbl = entry; - } - } - } - } - - // subentries - if(node->xmlChildrenNode) { - xmlNodePtr np = node->xmlChildrenNode; - rRCOEntry* rcoNode = NULL; - while(np->next) { - if(np->type == XML_ELEMENT_NODE) { - rRCOEntry* curNode = (rRCOEntry*)malloc(sizeof(rRCOEntry)); - parse_entry(np, curNode, rco, fixes); - - curNode->parent = entry; - if(rcoNode) { - curNode->prev = rcoNode; - curNode->prev->next = curNode; - } else - entry->firstChild = curNode; - - rcoNode = curNode; - entry->numSubentries++; - } - np = np->next; - } - entry->lastChild = rcoNode; - - } - -} - -Bool rcoxml_text_to_int(char* s, const RcoTableMap map, uint* out) { - if(!s[0]) return FALSE; - - int i=0; - while(map[i][0]) { - if(!strcasecmp(map[i], s)) { - *out = (uint)i; - return TRUE; - } - i++; - } - - // see if this is "unknown" - return (sscanf(s, "unknown%i", out) == 1); -} - -// currently very basic - resize when adding a label -//uint rcoxml_add_label(rRCOFile* rco, char* label) { -uint rcoxml_add_label(char** labels, uint* labelsLen, char* label, Bool eventQuirk) { - // first, see if we already have this label - uint p = 0; - while(p < *labelsLen && (*labels)[p]) { - if(!strcmp(*labels + p, label)) - return p; // found - p += strlen(*labels + p) +1; - p = ALIGN_TO_4(p); // urgh, this is kinda a little dirty, but it works; if we hit a blank 4 bytes, the above line will cause it to go forward by 1, this will align it forward to 4, so, if we've hit some nulls, we're effectively jumping 4 bytes at a time - } - - // don't have it? add it - uint curLen = *labelsLen; - uint labelLen = strlen(label) +1; - if(eventQuirk) { - // TODO: - } - uint newLen = curLen + labelLen; - newLen = ALIGN_TO_4(newLen); - - *labels = (char*)realloc(*labels, newLen); - strcpy(*labels + curLen, label); - if(labelLen % 4) { - memset(*labels + curLen + labelLen, 0, 4 - (labelLen % 4)); - } - *labelsLen = newLen; -/* - uint curLen = rco->labelsLen; - uint newLen = rco->labelsLen + strlen(label) + 1; - - rco->labels = (char*)realloc(rco->labels, newLen); - strcpy(curLen, label); - rco->labelsLen = newLen; -*/ - return curLen; -} - -uint rcoxml_add_label_reordering(char* newLabels, uint* labelPos, char* label) { - // first, see if we already have this label - uint p = 0; - while(p < *labelPos && newLabels[p]) { - if(!strcmp(newLabels + p, label)) - return p; // found - p += strlen(newLabels + p) +1; - p = ALIGN_TO_4(p); // dirty, but it works (see note from rcoxml_add_label) - } - - // don't have it? add it - strcpy(newLabels + *labelPos, label); - uint curPos = *labelPos; - *labelPos += strlen(label) +1; - *labelPos = ALIGN_TO_4(*labelPos); - - return curPos; -} - -int label_reorder_qsort(const rRCOEntry** a, const rRCOEntry** b) { - return (*a)->id - (*b)->id; -} - -void rcoxml_reorder_labels(char* newLabels, uint* labelPos, rRCOFile* rco, rRCOEntry* entry) { - uint i; - - if(entry->labelOffset != RCO_NULL_PTR) - entry->labelOffset = rcoxml_add_label_reordering(newLabels, labelPos, rco->labels + entry->labelOffset); - - if(entry->id == RCO_TABLE_TEXT && entry->type == 1) { - rRCOTextEntry* extra = (rRCOTextEntry*)entry->extra; - for(i=0; inumIndexes; i++) - if(extra->indexes[i].labelOffset != RCO_NULL_PTR) - extra->indexes[i].labelOffset = rcoxml_add_label_reordering(newLabels, labelPos, rco->labels + extra->indexes[i].labelOffset); - } - - rRCOEntry* rcoNode; - for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) - rcoxml_reorder_labels(newLabels, labelPos, rco, rcoNode); -} - -void parse_obj_extra(xmlNodePtr node, rRCOEntry* entry) { - - if(entry->type <= RCO_OBJ_EXTRA_LEN_NUM && RCO_OBJ_EXTRA_LEN[entry->type] != -1) { - uint i = 0, i2 = 0; - - // work out the length of this thing, lol - entry->extraLen = RCO_OBJ_EXTRA_LEN[entry->type] * sizeof(uint32); - for(i=0, i2=0; i<(uint)RCO_OBJ_EXTRA_LEN[entry->type]; i++, i2++) { - if(RCO_OBJ_IS_REF(entry->type, i2)) { - entry->extraLen -= 2*sizeof(uint32); - entry->extraLen += sizeof(rRCORef); - i++; - } - } - - entry->extra = malloc(entry->extraLen); - uint8* extra = (uint8*)entry->extra; - memset(extra, 0, entry->extraLen); - - for(i=0, i2=0; (int)itype]; i++, i2++) { - Bool isRef = RCO_OBJ_IS_REF(entry->type, i2); - - xmlChar* val = NULL; - if(RCO_OBJ_EXTRA_NAMES[entry->type][i2][0]) { - val = xmlGetProp(node, _X(RCO_OBJ_EXTRA_NAMES[entry->type][i2])); - } - - if(!val) { - val = rcoxml_get_unknown_attrib(node, i); - } - - if(!val) { - if(RCO_OBJ_EXTRA_NAMES[entry->type][i2][0]) { - warning("[line %d] Missing attribute '%s', defaulting to 0 / nothing.", node->line, RCO_OBJ_EXTRA_NAMES[entry->type][i2]); - } else { - char ts[10] = "\0"; - switch(RCO_OBJ_EXTRA_TYPES[entry->type][i2]) { - case RCO_OBJ_EXTRA_TYPE_FLOAT: strcpy(ts, "Float"); break; - case RCO_OBJ_EXTRA_TYPE_INT: strcpy(ts, "Int"); break; - case RCO_OBJ_EXTRA_TYPE_EVENT: strcpy(ts, "Event"); break; - case RCO_OBJ_EXTRA_TYPE_IMG: strcpy(ts, "Image"); break; - case RCO_OBJ_EXTRA_TYPE_FONT: strcpy(ts, "Font"); break; - case RCO_OBJ_EXTRA_TYPE_MODEL: strcpy(ts, "Model"); break; - case RCO_OBJ_EXTRA_TYPE_OBJ: strcpy(ts, "Object"); break; - case RCO_OBJ_EXTRA_TYPE_UNK: case RCO_OBJ_EXTRA_TYPE_REF: - if(isRef) strcpy(ts, "Ref"); - break; - } - warning("[line %d] Missing attribute 'unknown%s%d', defaulting to 0 / nothing.", node->line, ts, i); - } - - // default values - if(isRef) - ((rRCORef*)extra)->type = RCO_REF_NONE; - else - *(uint32*)extra = 0; - } - else { - if(isRef) { - // refs may need fixing later on - if(!rcoxml_parse_ref((char*)val, (rRCORef*)extra)) - warning("[line %d] Unable to parse reference '%s' - defaulting to 'nothing'.", node->line, (char*)val); - /* - // we really need to fix refs after all entries are loaded - I'm going to be lazy here since we can't actually do anything terribly useful right now - ((rRCORef*)extra)->type = RCO_REF_NONE; - add_ref_to_fix(fixes, , (rRCORef*)extra); - */ - } else { - *(uint32*)extra = rcoxml_parse_value((char*)val); - } - xmlFree(val); - } - - if(isRef) { - extra += sizeof(rRCORef); - i++; - } else - extra += sizeof(uint32); - } - } else { - // TODO: handle unknown types - } -} - - -void parse_anim_extra(xmlNodePtr node, rRCOEntry* entry) { - - if(entry->type <= RCO_ANIM_EXTRA_LEN_NUM && RCO_ANIM_EXTRA_LEN[entry->type] != -1) { - uint i = 0, i2 = 0; - - // work out the length of this thing, lol - entry->extraLen = RCO_ANIM_EXTRA_LEN[entry->type] * sizeof(uint32); - for(i=0, i2=0; i<(uint)RCO_ANIM_EXTRA_LEN[entry->type]; i++, i2++) { - if(RCO_ANIM_IS_REF(entry->type, i2)) { - entry->extraLen -= 2*sizeof(uint32); - entry->extraLen += sizeof(rRCORef); - i++; - } - } - - entry->extra = malloc(entry->extraLen); - uint8* extra = (uint8*)entry->extra; - memset(extra, 0, entry->extraLen); - - for(i=0, i2=0; (int)itype]; i++, i2++) { - Bool isRef = RCO_ANIM_IS_REF(entry->type, i2); - - xmlChar* val = NULL; - if(RCO_ANIM_EXTRA_NAMES[entry->type][i2][0]) { - val = xmlGetProp(node, _X(RCO_ANIM_EXTRA_NAMES[entry->type][i2])); - } - - if(!val) { - val = rcoxml_get_unknown_attrib(node, i); - } - - if(!val) { - if(RCO_ANIM_EXTRA_NAMES[entry->type][i2][0]) { - warning("[line %d] Missing attribute '%s', defaulting to 0 / nothing.", node->line, RCO_ANIM_EXTRA_NAMES[entry->type][i2]); - } else { - char ts[10] = "\0"; - switch(RCO_ANIM_EXTRA_TYPES[entry->type][i2]) { - case RCO_OBJ_EXTRA_TYPE_FLOAT: strcpy(ts, "Float"); break; - case RCO_OBJ_EXTRA_TYPE_INT: strcpy(ts, "Int"); break; - case RCO_OBJ_EXTRA_TYPE_EVENT: strcpy(ts, "Event"); break; - case RCO_OBJ_EXTRA_TYPE_IMG: strcpy(ts, "Image"); break; - case RCO_OBJ_EXTRA_TYPE_FONT: strcpy(ts, "Font"); break; - case RCO_OBJ_EXTRA_TYPE_MODEL: strcpy(ts, "Model"); break; - case RCO_OBJ_EXTRA_TYPE_OBJ: strcpy(ts, "Object"); break; - case RCO_OBJ_EXTRA_TYPE_UNK: case RCO_OBJ_EXTRA_TYPE_REF: - if(isRef) strcpy(ts, "Ref"); - break; - } - warning("[line %d] Missing attribute 'unknown%s%d', defaulting to 0 / nothing.", node->line, ts, i); - } - - // default values - if(isRef) - ((rRCORef*)extra)->type = RCO_REF_NONE; - else - *(uint32*)extra = 0; - } - else { - if(isRef) { - // refs may need fixing later on - if(!rcoxml_parse_ref((char*)val, (rRCORef*)extra)) - warning("[line %d] Unable to parse reference '%s' - defaulting to 'nothing'.", node->line, (char*)val); - /* - // we really need to fix refs after all entries are loaded - I'm going to be lazy here since we can't actually do anything terribly useful right now - ((rRCORef*)extra)->type = RCO_REF_NONE; - add_ref_to_fix(fixes, , (rRCORef*)extra); - */ - } else { - *(uint32*)extra = rcoxml_parse_value((char*)val); - } - xmlFree(val); - } - - if(isRef) { - extra += sizeof(rRCORef); - i++; - } else - extra += sizeof(uint32); - } - } else { - // TODO: handle unknown types - } -} - -/* -void parse_anim_extra(xmlNodePtr node, rRCOEntry* entry) { - - if(entry->type <= RCO_ANIM_EXTRA_LEN_NUM && RCO_ANIM_EXTRA_LEN[entry->type] != -1) { - uint i = 0; - - entry->extraLen = 0; - for(i=0, i2=0; i<(uint)RCO_ANIM_EXTRA_LEN[entry->type]; i++, i2++) { - if(RCO_ANIM_IS_REF(entry->type, i2)) { - entry->extraLen += sizeof(rRCORef); - i++; - } else - entry->extraLen += sizeof(uint32); - } - - //if(RCO_ANIM_EXTRA_REFS[entry->type]) { - // entry->extraLen = (RCO_ANIM_EXTRA_LEN[entry->type]-2) * sizeof(uint32) + sizeof(rRCORef); - //} else { - // entry->extraLen = RCO_ANIM_EXTRA_LEN[entry->type] * sizeof(uint32); - //} - - entry->extra = malloc(entry->extraLen); - uint8* extra = (uint8*)entry->extra; - memset(extra, 0, entry->extraLen); - - uint entryLen = (uint)RCO_ANIM_EXTRA_LEN[entry->type]; - - if(RCO_ANIM_EXTRA_REFS[entry->type]) { - xmlChar* val = NULL; - if(entry->type == RCO_ANIM_TYPE_EVENT) - val = xmlGetProp(node, _X("event")); - else - val = xmlGetProp(node, _X("object")); - - if(val) { - if(!rcoxml_parse_ref((char*)val, (rRCORef*)extra)) - warning("[line %d] Unable to parse object reference '%s' - defaulting to 'nothing'.", node->line, (char*)val); - } else { - warning("[line %d] No object/event defined for anim entry. Defaulting to nothing.", node->line); - // blank ref - rRCORef* dr = ((rRCORef*)extra); - dr->type = RCO_REF_NONE; - dr->ptr = NULL; - dr->rawPtr = RCO_NULL_PTR; - } - extra += sizeof(rRCORef); - entryLen -=2; - } - - for(i=0; itype][i][0]) { - val = xmlGetProp(node, _X(RCO_ANIM_EXTRA_NAMES[entry->type][i])); - } - if(!val) { - val = rcoxml_get_unknown_attrib(node, i); - } - - if(!val) { - if(RCO_ANIM_EXTRA_NAMES[entry->type][i][0]) { - warning("[line %d] Missing attribute '%s', defaulting to 0 / nothing.", node->line, RCO_ANIM_EXTRA_NAMES[entry->type][i]); - } else { - warning("[line %d] Missing attribute (index %d), defaulting to 0 / nothing.", node->line, i); // TODO: better message - } - *(uint32*)extra = 0; - } - else { - *(uint32*)extra = rcoxml_parse_value((char*)val); - xmlFree(val); - } - - extra += sizeof(uint32); - } - } else { - // TODO: handle unknown types - } -} -*/ - -// TODO: this somewhat mixes normal vals with refs (not optimal) - may wish to do something about this -xmlChar* rcoxml_get_unknown_attrib(xmlNodePtr node, uint num) { - xmlChar* ret = NULL; - char n[30]; - - #define RCOXML_GET_UNKNOWN_ATTRIB_DO(t) { \ - sprintf(n, "unknown" t "%d", num); \ - ret = xmlGetProp(node, _X(n)); \ - if(ret) return ret; \ - } - RCOXML_GET_UNKNOWN_ATTRIB_DO(""); - RCOXML_GET_UNKNOWN_ATTRIB_DO("Int"); - RCOXML_GET_UNKNOWN_ATTRIB_DO("Float"); - RCOXML_GET_UNKNOWN_ATTRIB_DO("Event"); - RCOXML_GET_UNKNOWN_ATTRIB_DO("Image"); - RCOXML_GET_UNKNOWN_ATTRIB_DO("Model"); - RCOXML_GET_UNKNOWN_ATTRIB_DO("Font"); - RCOXML_GET_UNKNOWN_ATTRIB_DO("Object"); - RCOXML_GET_UNKNOWN_ATTRIB_DO("Ref"); - - return NULL; -} - -// parse object/anim attrib values -uint32 rcoxml_parse_value(char* s) { - uint32 retI=0; - float retF=0; - - if(sscanf(s, "0x%x", (uint*)&retI)) return retI; - - retF = strtof(s, NULL); - memcpy(&retI, &retF, sizeof(uint32)); - - return retI; -} - -Bool rcoxml_parse_ref(char* val, rRCORef* out) { - // defaults - out->type = RCO_REF_NONE; - out->rawPtr = RCO_NULL_PTR; - out->ptr = NULL; - - if(!strcasecmp(val, "nothing")) { - return TRUE; - } - - // search for ":" - char* colon = strchr(val, ':'); - if(!colon) return FALSE; - colon[0] = '\0'; - - if(!strcasecmp(val, "event")) - out->type = RCO_REF_EVENT; - else if(!strcasecmp(val, "text")) - out->type = RCO_REF_TEXT; - else if(!strcasecmp(val, "image")) - out->type = RCO_REF_IMG; - else if(!strcasecmp(val, "model")) - out->type = RCO_REF_MODEL; - else if(!strcasecmp(val, "font")) - out->type = RCO_REF_FONT; - else if(!strcasecmp(val, "object2")) - out->type = RCO_REF_OBJ2; - else if(!strcasecmp(val, "anim")) - out->type = RCO_REF_ANIM; - else if(!strcasecmp(val, "object")) - out->type = RCO_REF_OBJ; - - colon[0] = ':'; // restore for good measure - colon++; // colon is now "name" - - if(out->type == RCO_REF_NONE) { // not assigned by above ifs - if(sscanf(val, "unknown%i", (int*)&(out->type))) { // this is actually case sensitive... :| - out->rawPtr = strtol(colon, NULL, 10); - return TRUE; - } - else { - return FALSE; - } - } - - out->ptr = malloc(strlen(colon)+1); - strcpy((char*)out->ptr, colon); - - return TRUE; -} - -/* -// a very simple resize method :/ -void add_ref_to_fix(rcoxml_read_fixes* fixes, char* label, rRCORef* ref) { - fixes->refs = (xmlrco_read_fix_refs*)realloc(fixes->refs, sizeof(xmlrco_read_fix_refs) * (fixes->refCnt+1)); - xmlrco_read_fix_refs* fr = &(fixes->refs[fixes->refsCnt]); - - fr->ref = ref; - fr->label = (char*)malloc(strlen(label)+1); - strcpy(fr->label, label); - - fixes->refsCnt++; -} -*/ - -void rcoxml_fix_refs(rRCOEntry* entry, rRCOFile* rco) { - uint i = 0, i2 = 0; - - if((entry->id == RCO_TABLE_OBJ && entry->type > 0 && entry->type <= RCO_OBJ_EXTRA_LEN_NUM && RCO_OBJ_EXTRA_LEN[entry->type] != -1) - || (entry->id == RCO_TABLE_ANIM && entry->type > 1 && entry->type <= RCO_ANIM_EXTRA_LEN_NUM && RCO_ANIM_EXTRA_LEN[entry->type] != -1)) { - uint8* extra = (uint8*)entry->extra; - - uint len; - if(entry->id == RCO_TABLE_OBJ) - len = RCO_OBJ_EXTRA_LEN[entry->type]; - else - len = RCO_ANIM_EXTRA_LEN[entry->type]; - for(i=0, i2=0; iid == RCO_TABLE_OBJ && RCO_OBJ_IS_REF(entry->type, i2)) - || (entry->id != RCO_TABLE_OBJ && RCO_ANIM_IS_REF(entry->type, i2))) { - rcoxml_fix_ref((rRCORef*)extra, rco); - extra += sizeof(rRCORef); - i++; - } else - extra += sizeof(uint32); - } - /* - if(entry->id == RCO_TABLE_OBJ) { - for(i=0, i2=0; (int)itype]; i++, i2++) { - if(RCO_OBJ_IS_REF(entry->type, i2)) { - rcoxml_fix_ref((rRCORef*)extra, rco); - extra += sizeof(rRCORef); - i++; - } else - extra += sizeof(uint32); - } - - } else { // anim entries - if(RCO_ANIM_EXTRA_REFS[entry->type]) { - rcoxml_fix_ref((rRCORef*)extra, rco); - } - } - */ - } - - if(entry->numSubentries) { - rRCOEntry* rcoNode; - for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) - rcoxml_fix_refs(rcoNode, rco); - } -} - -// fixes the pointer of a reference if it needs one -// (current pointer needs to be pointing at label name) -Bool rcoxml_fix_ref(rRCORef* ref, rRCOFile* rco) { - if(ref->type == RCO_REF_IMG || ref->type == RCO_REF_MODEL || ref->type == RCO_REF_FONT || ref->type == RCO_REF_OBJ2 || ref->type == RCO_REF_ANIM || ref->type == RCO_REF_OBJ) { - if(!ref->ptr) return FALSE; // no label... (this should never happen...) - - rRCOEntry* e = find_entry_from_label(&(rco->tblMain), (const char*)ref->ptr); - if(!e) { - warning("[entry-reference] Cannot find entry with label '%s'!", (const char*)ref->ptr); - } - else if( - (ref->type == RCO_REF_IMG && e->id != RCO_TABLE_IMG) - || (ref->type == RCO_REF_MODEL && e->id != RCO_TABLE_MODEL) - || (ref->type == RCO_REF_FONT && e->id != RCO_TABLE_FONT) - || (ref->type == RCO_REF_OBJ && e->id != RCO_TABLE_OBJ) - || (ref->type == RCO_REF_ANIM && e->id != RCO_TABLE_ANIM) - ) { - warning("[entry-reference] Mismatching reference type and entry type for reference with label '%s' - have you used the right name?", (const char*)ref->ptr); - } - - free(ref->ptr); // unallocate malloc'd label - ref->ptr = e; - ref->rawPtr = 0; // makes no sense, so blank it out - - return (e != NULL); - } - else if(ref->type == RCO_REF_EVENT) { - if(!ref->ptr) return FALSE; // no event... (this should never happen...) - - ref->rawPtr = rcoxml_add_label(&(rco->events), &rco->eventsLen, (char*)ref->ptr, TRUE); - free(ref->ptr); // unallocate malloc'd label - return TRUE; - } - else if(ref->type == RCO_REF_TEXT) { - ref->rawPtr = RCO_NULL_PTR; - // assume first text entry is correct - if(rco->tblText && rco->tblText->numSubentries) { - int idx = find_text_from_label(rco->labels, (rRCOTextEntry*)(rco->tblText->firstChild->extra), (const char*)ref->ptr); - if(idx == -1) { - warning("[entry-reference] Cannot find text entry with label '%s'! (note, only first text language is checked as the RCO format assumes that all languages have the same entry labels)", (const char*)ref->ptr); - } else { - ref->rawPtr = idx; - } - } else - warning("[entry-reference] Unable to reference text entry '%s' as text tree cannot be found, or contains no text languages!", (const char*)ref->ptr); - - free(ref->ptr); - ref->ptr = NULL; - return (ref->rawPtr != RCO_NULL_PTR); - } - else // no fixing required - return TRUE; -} - -void rcoxml_fix_ptrs(rRCOEntry*** sect, uint* sectCnt, rRCOFile* rco, const char* text) { - uint textLen = strlen(text); - *sectCnt = 0; - if(!textLen) { - // no text, we'll blank the section - *sect = NULL; - return; - } - - *sectCnt = 1; - char* tmpText = (char*)malloc(textLen +1); - char* tmpTextPtr = tmpText; - strcpy(tmpText, text); - - uint i; - /* - for(i=0; itblMain), tmpTextPtr); - if(!(*entryPtr)) - warning("[entry-ptr] Cannot find entry with label '%s'!", tmpTextPtr); - } else // blank pointer - *entryPtr = NULL; - - tmpTextPtr += labelLen +1; - entryPtr++; - } - - free(tmpText); -} - -// takes a comma separated string, replaces commas with nullchars and returns number of items in the list -uint split_comma_list(char* s) { - uint cnt = 1; - uint i; - uint sLen = strlen(s); - if(!s[0]) return 0; // empty string - - for(i=0; iextra; - - if(!(doc = xmlParseFile(fn))) { - warning("Can't parse XML file %s.", fn); - return FALSE; - } - if((node = xmlDocGetRootElement(doc))) - bValidDoc = (xmlStrcmp(node->name, _X("TextLang")) ? FALSE : TRUE); - if(!bValidDoc) { - warning("Invalid XML document %s.", fn); - return FALSE; - } - - te->numIndexes = 0; - if(!node->xmlChildrenNode) { - xmlFreeDoc(doc); - return TRUE; - } - - // count num of child nodes (don't use xmlChildElementCount because this may include text nodes) - xmlNodePtr np = node->xmlChildrenNode; - while(np->next) { - if(np->type == XML_ELEMENT_NODE && !xmlStrcmp(np->name, _X("Text"))) - te->numIndexes++; - np = np->next; - } - - if(te->numIndexes) { - void* textBuffer = NULL; - uint curPos = 0; - uint fmt = te->format; - char icDestFmt[8]; - uint charWidth = 2; - make_iconv_charset(icDestFmt, fmt, rco->eSwap); - if(fmt == RCO_TEXT_FMT_UTF32) { - charWidth = 4; - } - if(fmt == RCO_TEXT_FMT_UTF8) { - charWidth = 1; - } - iconv_t ic = iconv_open(icDestFmt, "utf-8");; - te->indexes = (RCOTextIndex*)calloc(1, te->numIndexes * sizeof(RCOTextIndex)); - - - np = node->xmlChildrenNode; - i = 0; - while(np->next) { - if(np->type == XML_ELEMENT_NODE && !xmlStrcmp(np->name, _X("Text"))) { - xmlChar* n = xmlGetProp(np, _X("name")); - if(n) { - te->indexes[i].labelOffset = rcoxml_add_label(&rco->labels, &rco->labelsLen, (char*)n, FALSE); - xmlFree(n); - - te->indexes[i].length = 0; - te->indexes[i].offset = RCO_NULL_PTR; - - if(np->xmlChildrenNode && (np->xmlChildrenNode->type == XML_TEXT_NODE || np->xmlChildrenNode->type == XML_CDATA_SECTION_NODE)) { - n = np->xmlChildrenNode->content; - if(fmt == RCO_TEXT_FMT_UTF8) { - te->indexes[i].length = xmlStrlen(n) +1/*null*/; - if((*(uint32*)n & 0xFFFFFF) == UTF8_BOM) - te->indexes[i].length -= 3; - } - else { - // xmlUTF8Strlen used because libxml2 will guarantee src is UTF8 - te->indexes[i].length = (xmlUTF8Strlen(n)+1 /*null*/) * charWidth; - if((*(uint32*)n & 0xFFFFFF) == UTF8_BOM) { // fix for older versions of rcomage - if BOM exists, reduce length by 1 char - te->indexes[i].length -= charWidth; - } - } - if(te->indexes[i].length > 2) { - uint contentLen = xmlStrlen(n), outBufLen = te->indexes[i].length; - - textBuffer = realloc(textBuffer, ALIGN_TO_4(te->indexes[i].length + curPos)); - char* tbPtr = (char*)textBuffer; - tbPtr += curPos; - // first get rid of BOM if we have one (earlier versions of rcomage) - /* if((fmt == RCO_TEXT_FMT_UTF32 && *(uint32*)n == UTF32_BOM) - ||(fmt == RCO_TEXT_FMT_UTF16 && *(uint16*)n == UTF16_BOM) - ||(fmt == RCO_TEXT_FMT_UTF8 && (*(uint32*)n & 0x00FFFFFF) == UTF8_BOM)) { */ - if((*(uint32*)n & 0xFFFFFF) == UTF8_BOM) { - char bom[4]; - char *bomPtr = bom; - uint bomLen = (fmt == RCO_TEXT_FMT_UTF32 ? 4 : (fmt == RCO_TEXT_FMT_UTF8 ? 3 : 2)); - iconv(ic, (char**)(&n), (size_t*)(&contentLen), (char**)&bomPtr, (size_t*)(&bomLen)); - } - iconv(ic, (char**)(&n), (size_t*)(&contentLen), &tbPtr, (size_t*)(&outBufLen)); - if(outBufLen && outBufLen == charWidth) { // *should* always be true - memset(tbPtr, 0, ALIGN_TO_4(te->indexes[i].length) - (te->indexes[i].length-outBufLen)); - } else - warning("[%s:%d] iconv error when trying to convert text for '%s'! (%d byte(s) not converted)", fn, np->line, rco->labels + te->indexes[i].labelOffset, outBufLen - charWidth); - - te->indexes[i].offset = curPos; - curPos += ALIGN_TO_4(te->indexes[i].length); - } else - te->indexes[i].length = 0; // blank string - } - - i++; - } else - warning("[%s:%d] No name specified for Text!", fn, np->line); - } - np = np->next; - } - entry->srcBuffer = textBuffer; - entry->srcFile[0] = '\0'; - entry->srcLen = entry->srcLenUnpacked = curPos; - iconv_close(ic); - } - - xmlFreeDoc(doc); - return TRUE; -} +// TODO: replace xmlstrcmp with strcasecmp + +#define LIBXML_STATIC +#include +#include +#include +#include +#include + +#include "rcomain.h" +#include "xml.h" +#include "strfuncs.h" +#include "configscan.h" + +#define _X(s) ((const xmlChar*)s) +#define INTERPRET_AS_FLOAT(n) (*((float*)(&(n)))) + +#define strtof(a,b) (float)strtod(a,b) + + +/* +typedef struct { + rRCORef* ref; + char* label; +} xmlrco_read_fix_refs; +*/ +typedef struct { + //xmlChar *ptrText, *ptrImg, *ptrSound, *ptrModel, *ptrObj, *ptrAnim; + xmlChar* textData; // labels data, that is + //xmlrco_read_fix_refs* refs; + //uint refsCnt; +} rcoxml_read_fixes; + + + +void parse_entry(xmlNodePtr node, rRCOEntry* entry, rRCOFile* rco, rcoxml_read_fixes* fixes); +//Bool rcoxml_text_to_int(char* s, const RcoTableMap map, uint* out); +uint rcoxml_add_label(char** labels, uint* labelsLen, char* label, Bool eventQuirk); +uint rcoxml_add_label_reordering(char* newLabels, uint* labelPos, char* label); +void rcoxml_reorder_labels(char* newLabels, uint* labelPos, rRCOFile* rco, rRCOEntry* entry); +int label_reorder_qsort(const rRCOEntry** a, const rRCOEntry** b); +void parse_obj_extra(xmlNodePtr node, rRCOEntry* entry); +void parse_anim_extra(xmlNodePtr node, rRCOEntry* entry); +xmlChar* rcoxml_get_unknown_attrib(xmlNodePtr node, uint num); +uint32 rcoxml_parse_value(char* s); +Bool rcoxml_parse_ref(char* val, rRCORef* out); +void rcoxml_fix_refs(rRCOEntry* entry, rRCOFile* rco); +Bool rcoxml_fix_ref(rRCORef* ref, rRCOFile* rco); +void rcoxml_fix_ptrs(rRCOEntry*** sect, uint* sectCnt, rRCOFile* rco, const char* text); +uint split_comma_list(char* s); +char* strtrimr(char* in); +char* expand_fname_to_fmt(char* in, char type); +Bool parse_text_xml(char* fn, rRCOFile* rco, rRCOEntry* entry); + +rRCOFile* read_xml(char* fn) { + xmlDocPtr doc; + xmlNodePtr node; + Bool bValidDoc = FALSE; + + rcoxml_read_fixes fixes; // post fixes need to be done + + //fixes.ptrText = fixes.ptrImg = fixes.ptrSound = fixes.ptrModel = fixes.ptrObj = fixes.ptrAnim = NULL; + fixes.textData = NULL; + //fixes.refsCnt = 0; + //fixes.refs = (xmlrco_read_fix_refs*)malloc(1); // dummy malloc to allow realloc to work later on + + // parse from stdin? + if(!strcmp(fn, "-")) { + char* buf = NULL; + int bufsize = 0; + while(!feof(stdin)) { + buf = (char*)realloc(buf, bufsize+65536); + uint readAmt = fread(buf+bufsize, 1, 65536, stdin); + if(!readAmt) break; + bufsize += readAmt; + } + fclose(stdin); + if(!(doc = xmlParseMemory(buf, bufsize))) { + error("Can't parse given XML data."); + return NULL; + } + free(buf); + } else { + if(!(doc = xmlParseFile(fn))) { + error("Can't parse given XML file %s.", fn); + return NULL; + } + } + if((node = xmlDocGetRootElement(doc))) + bValidDoc = (xmlStrcmp(node->name, _X("RcoFile")) ? FALSE : TRUE); + if(bValidDoc) bValidDoc = (node->xmlChildrenNode ? TRUE : FALSE); + + xmlNodePtr nodeChild; + if(bValidDoc) { // valid document must contain at least one thing under RcoFile (MainTree) + nodeChild = node->xmlChildrenNode; + while(nodeChild->next && nodeChild->type != XML_ELEMENT_NODE) + nodeChild = nodeChild->next; + bValidDoc = (nodeChild->type == XML_ELEMENT_NODE); + } + + if(!bValidDoc) { + error("Invalid XML file."); + xmlFreeDoc(doc); + return NULL; + } + + + rRCOFile* rco = (rRCOFile*)malloc(sizeof(rRCOFile)); + rco->labelsLen = rco->eventsLen = 0; + //rco->numPtrText = rco->numPtrImg = rco->numPtrModel = rco->numPtrSound = rco->numPtrObj = rco->numPtrAnim = 0; + rco->tblVSMX = rco->tblText = rco->tblImage = rco->tblSound = rco->tblModel = rco->tblFont = rco->tblObj = rco->tblAnim = NULL; + + // version id + rco->verId = 0x71; // assumed default + xmlChar* verId = xmlGetProp(node, _X("minFirmwareVer")); + if(verId) { + uint verIdInt = 0; + float verIdFlt = 0; + if(sscanf((const char*)verId, "unknownId%i", &verIdInt)) + rco->verId = verIdInt; + else if(!xmlStrcmp(verId, _X("ps3"))) // for compatibility + rco->verId = 0x107; + else if(sscanf((const char*)verId, "%f", &verIdFlt)) { + if(verIdFlt < 1.0) { + warning("[line %d] Invalid value for 'minFirmwareVer'.", node->line); + } else if(verIdFlt < 1.5) { + rco->verId = 0x70; + } else if(verIdFlt < 2.6) { + rco->verId = 0x71; + } else if(verIdFlt < 2.7) { + rco->verId = 0x90; + } else if(verIdFlt < 2.8) { + rco->verId = 0x95; + } else if(verIdFlt < 3.5) { + rco->verId = 0x96; + } else if(verIdFlt <= 6.2) { + rco->verId = 0x100; + } else { + warning("[line %d] Unknown ID for firmware version '%f'.", node->line, verIdFlt); + } + } + else + warning("[line %d] Unknown value for 'minFirmwareVer'.", node->line); + xmlFree(verId); + } + + // XML version + xmlChar* xmlVer = xmlGetProp(node, _X("rcomageXmlVer")); + if(xmlVer) { + float xmlVerFlt; + if(sscanf((const char*)xmlVer, "%f", &xmlVerFlt)) { + if(xmlVerFlt > APPVER) + warning("This XML file was generated by a newer version (v%f) of rcomage\nand may not read properly with this version.", xmlVerFlt); + } + else + warning("[line %d] Bad value for 'rcomageXmlVer'.\n So... do you have a reason for fiddling with this?? >_<", node->line); + xmlFree(xmlVer); + } + + rco->umdFlag = 0; // assumed default + xmlChar* umdFlag = xmlGetProp(node, _X("UMDFlag")); + if(umdFlag) { + rco->umdFlag = atoi((char*)umdFlag); + if(rco->umdFlag > 0xF || rco->umdFlag < 0) { + warning("The UMD Flag must be between 0 and 15, defaulting to 0."); + rco->umdFlag = 0; + } + xmlFree(umdFlag); + } + + rco->ps3 = FALSE; + xmlChar* ps3Flag = xmlGetProp(node, _X("type")); + if(ps3Flag) { + if(!xmlStrcmp(ps3Flag, _X("ps3"))) + rco->ps3 = TRUE; + else if(xmlStrcmp(ps3Flag, _X("psp"))) + warning("Unknown type %s", (char*)ps3Flag); + xmlFree(ps3Flag); + } + configLoadObjmap(rco->ps3); + configLoadAnimmap(rco->ps3); + + // TODO: check is this is correct + /* rco->labels = (char*)malloc(1); + rco->labels[0] = '\0'; + rco->events = (char*)malloc(1); + rco->events[0] = '\0'; */ + rco->labels = rco->events = NULL; + + rco->eSwap = rco->ps3; + + /* + rco->tblMain.id = rco->tblMain.type = 0; + rco->tblMain.labelOffset = 0; + rco->tblMain.numSubentries = rco->tblMain.extraLen = 0; + */ + + parse_entry(nodeChild, &(rco->tblMain), rco, &fixes); + // TODO: ensure the first table _is_ the main table (and possibly then verify tree structure) + + // - post fixes - + // fix all obj/anim refs + rcoxml_fix_refs(&(rco->tblMain), rco); + + xmlFreeDoc(doc); + + rco_fix_decomp_sizes(rco, &rco->tblMain); + + // label reordering - really optional, but does help to make RCOs similar to Sony's + { + char* newLabels = (char*)malloc(rco->labelsLen); + memset(newLabels, 0, rco->labelsLen); + uint labelPos = 0; + + // don't forget to add the main table label :P + if(rco->tblMain.labelOffset != RCO_NULL_PTR) + rco->tblMain.labelOffset = rcoxml_add_label_reordering(newLabels, &labelPos, rco->labels + rco->tblMain.labelOffset); + + // we re-order in order of IDs + uint i; + rRCOEntry** sList = make_sorted_list_of_subentries(&rco->tblMain, label_reorder_qsort); + for(i=0; itblMain.numSubentries; i++) + rcoxml_reorder_labels(newLabels, &labelPos, rco, sList[i]); + free(sList); + + if(labelPos < rco->labelsLen) { + // error! + free(newLabels); + } else { + free(rco->labels); + rco->labels = newLabels; + } + } + + return rco; +} + +void parse_entry(xmlNodePtr node, rRCOEntry* entry, rRCOFile* rco, rcoxml_read_fixes* fixes) { + uint i, j; + Bool knownEntryType = TRUE; + + // crap all over the memory in "entry" so we don't screw stuff over later on + entry->id = entry->type = 0; + entry->numSubentries = 0; + entry->rco = rco; + entry->parent = entry->firstChild = entry->lastChild = entry->prev = entry->next = NULL; + entry->labelOffset = RCO_NULL_PTR; + entry->extra = NULL; + entry->extraLen = 0; + entry->srcFile[0] = '\0'; + entry->srcAddr = entry->srcLen = entry->srcLenUnpacked = entry->srcCompression = 0; + entry->srcBuffer = NULL; + + for(i=0; iname, _X(RCOXML_TABLE_TAGS[i][j]))) { + entry->id = i; + entry->type = j; + break; + } + j++; + } + if(entry->id) break; + } + + // check unknown type tags + if(!entry->id) { + knownEntryType = FALSE; + if(!sscanf((const char*)node->name, "Unknown_%i_%i", (int*)&entry->id, (int*)&entry->type)) { + char tag[RCOXML_TABLE_2ND_DIM + 11]; + for(i=0; iname, tag, &entry->type)) { + entry->id = i; + break; + } + } + if(!entry->id) { + // since all the known tags have entry->id >0, we can do this + error("[line %d] Unknown entry type '%s'", node->line, node->name); + exit(99); + } + } + } + + entry->offset = node->line; // use the line number later on + + { // label + xmlChar* label = xmlGetProp(node, _X("name")); + if(label) { + entry->labelOffset = rcoxml_add_label(&(rco->labels), &(rco->labelsLen), (char*)label, FALSE); + xmlFree(label); + } + } + + if(knownEntryType) { + + // read src (used by section below this, so there is some importance with this ordering) + { + xmlChar* src = xmlGetProp(node, _X("src")); + if(src) { + strcpy(entry->srcFile, (const char*)src); + xmlFree(src); + + entry->srcCompression = 0; + src = xmlGetProp(node, _X("srcRange")); + if(src) { + // parse src params + char compr[50] = "\0"; + if(sscanf((const char*)src, "%i-%i%50s", &(entry->srcAddr), &(entry->srcLen), compr) >= 2) { + entry->srcLen -= entry->srcAddr; + entry->srcLenUnpacked = entry->srcLen; // default - assume no compression + if(compr[0]) { + // TODO: handle situations where uncompressed size is not specified + if(sscanf(compr, ";zlib[%u]", &(entry->srcLenUnpacked)) == 1) { // TODO: a temporary fix (prolly need to add more compression algos, since sscanf seems to be rather basic in retrieving %s) + rcoxml_text_to_int("zlib", RCOXML_TABLE_DATA_COMPRESSION, &(entry->srcCompression)); + } else if(!strcmp(compr, ";rco")) { + entry->srcCompression = RCO_DATA_COMPRESSION_RCO; + } else { + warning("[line %d] Invalid syntax in srcRange", node->line); + } + } + } else + warning("[line %d] Invalid syntax in srcRange", node->line); + + xmlFree(src); + } else { + entry->srcAddr = 0; + entry->srcLen = entry->srcLenUnpacked = filesize(entry->srcFile); // won't work if filename contains a '*' + } + } + } + + + + // extra + #define RCOXML_READ_ATTRIB_AS_INT(nd, name, tbl, dest, undefmsg) { \ + xmlChar* __attr = xmlGetProp(nd, _X(name)); \ + if(__attr) { \ + if(!rcoxml_text_to_int((char*)__attr, tbl, dest)) { \ + warning("[line %d] Unrecognised value '%s'", nd->line, __attr); \ + } else { \ + xmlFree(__attr); \ + } \ + } else { \ + warning("[line %d] %s", nd->line, undefmsg); \ + } \ + } + + switch(entry->id) { + case RCO_TABLE_IMG: case RCO_TABLE_MODEL: + if(entry->type == 1) { + entry->extraLen = sizeof(rRCOImgModelEntry); + entry->extra = malloc(entry->extraLen); + // default value + ((rRCOImgModelEntry*)entry->extra)->format = (entry->id == RCO_TABLE_IMG ? RCO_IMG_GIM : RCO_MODEL_GMO); + ((rRCOImgModelEntry*)entry->extra)->compression = RCO_DATA_COMPRESSION_NONE; + + if(entry->id == RCO_TABLE_IMG) { + RCOXML_READ_ATTRIB_AS_INT(node, "format", RCOXML_TABLE_IMG_FMT , &((rRCOImgModelEntry*)entry->extra)->format, "No format attribute defined, defaulting to GIM."); + } else { + RCOXML_READ_ATTRIB_AS_INT(node, "format", RCOXML_TABLE_MODEL_FMT , &((rRCOImgModelEntry*)entry->extra)->format, "No format attribute defined, defaulting to GMO."); + } + RCOXML_READ_ATTRIB_AS_INT(node, "compression", RCOXML_TABLE_DATA_COMPRESSION , &((rRCOImgModelEntry*)entry->extra)->compression, "No compression attribute defined, defaulting to 'uncompressed'."); + ((rRCOImgModelEntry*)entry->extra)->unkCompr = 0; + xmlChar* unk = xmlGetProp(node, _X("unknownByte")); + if(unk) { + sscanf((const char*)unk, "%i", &((rRCOImgModelEntry*)entry->extra)->unkCompr); + xmlFree(unk); + } + } + break; + case RCO_TABLE_SOUND: + if(entry->type == 1) { + entry->extraLen = sizeof(rRCOSoundEntry); + entry->extra = malloc(entry->extraLen); + // default value + rRCOSoundEntry* se = (rRCOSoundEntry*)entry->extra; + se->format = RCO_SOUND_VAG; + se->channels = 1; + se->channelData = NULL; + + RCOXML_READ_ATTRIB_AS_INT(node, "format", RCOXML_TABLE_SOUND_FMT, (uint*)&(se->format), "No format attribute defined, defaulting to VAG."); + + xmlChar* chStr = xmlGetProp(node, _X("channels")); + uint16 ch = 0; + if(chStr) + ch = (uint16)strtol((const char*)chStr, NULL, 10); + if(ch > 0) + se->channels = ch; + else if(!strcasecmp(entry->srcFile + strlen(entry->srcFile) -4, ".wav") && se->format == RCO_SOUND_VAG) + se->channels = 0; // we have to fix later + else + warning("[line %d] Number of channels either not specified, or no valid value found.", node->line); + xmlFree(chStr); + + + if(se->channels) { + se->channelData = (uint32*)malloc(se->channels * sizeof(uint32)*2); + memset(se->channelData, 0, se->channels * sizeof(uint32)*2); + + xmlChar* srcPartsX = xmlGetProp(node, _X("srcParts")); + if(srcPartsX) { + char* srcParts = (char*)srcPartsX; + uint numParts = split_comma_list(srcParts); + if(numParts == se->channels) { + for(i=0; ichannels; i++) { + while(isspace(srcParts[0])) srcParts++; // skip whitespace + strtrimr(srcParts); + if(sscanf(srcParts, "%i@%i", (int*)&(se->channelData[i*2]), (int*)&(se->channelData[i*2 +1])) != 2) + warning("[line %d] Invalid syntax for part '%s'", node->line, srcParts); + srcParts += strlen(srcParts) +1; + } + } else + warning("[line %d] Number of defined parts does not match number of defined sound channels!", node->line); + + xmlFree(srcPartsX); + } else if(strchr(entry->srcFile, '*')) { + // check files + char* srcFileFmt = expand_fname_to_fmt(entry->srcFile, 'd'); + // TODO: loop thru each file, record size and add to buffer + if(strlen(srcFileFmt) < MAX_FILENAME_LEN) { + char srcFile[MAX_FILENAME_LEN]; + uint curPos = 0; + void* srcBufferTmp = malloc(1); + for(i=0; ichannels; i++) { + sprintf(srcFile, srcFileFmt, i); + if(file_exists(srcFile)) { + se->channelData[i*2] = filesize(srcFile); + if(se->channelData[i*2]) { + // read into srcBuffer + FILE* fp = fopen(srcFile, "rb"); + if(fp) { + srcBufferTmp = realloc(srcBufferTmp, curPos + ALIGN_TO_4(se->channelData[i*2])); + uint8* bufferPos = (uint8*)(srcBufferTmp) + curPos; + fileread(fp, bufferPos, se->channelData[i*2]); + fclose(fp); + } else + warning("[line %d] Can't find or open file '%s'", node->line, srcFile); + } + } else { + se->channelData[i*2] = 0; + warning("[line %d] Can't find or open file '%s'", node->line, srcFile); + } + se->channelData[i*2+1] = curPos; + curPos += ALIGN_TO_4(se->channelData[i*2]); + } + entry->srcBuffer = srcBufferTmp; + entry->srcLen = entry->srcLenUnpacked = curPos; + } // else, they're trying to hack us... :/ + free(srcFileFmt); + } else if(se->channels == 1 && entry->srcLen) { + // err, if there's just one channel, we can just use the srcRange, lol + se->channelData[0] = entry->srcLen; + se->channelData[1] = 0; + } else { + warning("[line %d] Cannot determine sizes of sound channels!", node->line); + } + } + + } + break; + + case RCO_TABLE_TEXT: + if(entry->type == 1) { + Bool xmlInput = FALSE; + + entry->extraLen = sizeof(rRCOTextEntry); + entry->extra = malloc(entry->extraLen); + // default value + rRCOTextEntry* te = (rRCOTextEntry*)entry->extra; + te->lang = 0; + te->format = RCO_TEXT_FMT_UTF16; + te->numIndexes = 0; + + RCOXML_READ_ATTRIB_AS_INT(node, "language", RCOXML_TABLE_TEXT_LANG, (uint*)&te->lang, "No language attribute defined - RCO may no longer be valid."); + RCOXML_READ_ATTRIB_AS_INT(node, "format", RCOXML_TABLE_TEXT_FMT, (uint*)&te->format, "No destination format defined - assuming UTF16."); + + if(te->format != RCO_TEXT_FMT_UTF16 && !rco->ps3) { + warning("[line %d] Non UTF-16 text not supported on the PSP.", node->line); + } + + // text indexes + xmlChar* textEntriesX = xmlGetProp(node, _X("entries")); + if(textEntriesX) { + char* textEntries = (char*)textEntriesX; + te->numIndexes = split_comma_list(textEntries); + te->indexes = (RCOTextIndex*)malloc(te->numIndexes * sizeof(RCOTextIndex)); + memset(te->indexes, 0, te->numIndexes * sizeof(RCOTextIndex)); + + for(i=0; inumIndexes; i++) { + while(isspace(textEntries[0])) textEntries++; // skip whitespace + strtrimr(textEntries); + te->indexes[i].labelOffset = rcoxml_add_label(&rco->labels, &rco->labelsLen, textEntries, FALSE); + textEntries += strlen(textEntries) +1; + } + + xmlFree(textEntriesX); + } else { // assume XML input + if(!(xmlInput = parse_text_xml(entry->srcFile, rco, entry))) + warning("[line %d] Text entries not specified! Assuming no entries.", node->line); + } + + if(!xmlInput && te->numIndexes) { + xmlChar* srcPartsX = xmlGetProp(node, _X("srcParts")); + if(srcPartsX) { + char* srcParts = (char*)srcPartsX; + uint numParts = split_comma_list(srcParts); + if(numParts == te->numIndexes) { + for(i=0; inumIndexes; i++) { + while(isspace(srcParts[0])) srcParts++; // skip whitespace + strtrimr(srcParts); + if(sscanf(srcParts, "%i@%i", (int*)&(te->indexes[i].length), (int*)&(te->indexes[i].offset)) != 2) + warning("[line %d] Invalid syntax for part '%s'", node->line, srcParts); + srcParts += strlen(srcParts) +1; + } + } else + warning("[line %d] Number of defined parts does not match number of defined text entries!", node->line); + + xmlFree(srcPartsX); + } else if(strchr(entry->srcFile, '*')) { + // check files + char* srcFileFmt = expand_fname_to_fmt(entry->srcFile, 's'); + // loop thru each file, record size and add to buffer + if(strlen(srcFileFmt) < MAX_FILENAME_LEN) { + char srcFile[MAX_FILENAME_LEN]; + uint curPos = 0; + void* srcBufferTmp = malloc(1); + for(i=0; inumIndexes; i++) { + sprintf(srcFile, srcFileFmt, rco->labels + te->indexes[i].labelOffset); + if(file_exists(srcFile)) { + te->indexes[i].length = filesize(srcFile); + if(te->indexes[i].length) { + // read into srcBuffer + FILE* fp = fopen(srcFile, "rb"); + if(fp) { + // do we have a BOM? + unsigned char bom[4] = {0x80, 0x80, 0x80, 0x80}; // dummy values that aren't used + char srcFmt[10] = "", destFmt[8]; + uint32 bom32le = UTF32_BOM; + uint32 bom32be = ENDIAN_SWAP(UTF32_BOM); + uint16 bom16le = UTF16_BOM; + uint16 bom16be = ENDIAN_SWAP(UTF16_BOM); + uint32 bom8 = UTF8_BOM; + uint bomLen = (te->format == RCO_TEXT_FMT_UTF32 ? 4 : (te->format == RCO_TEXT_FMT_UTF8 ? 3 : 2)); + make_iconv_charset(destFmt, te->format, rco->eSwap); + + fileread(fp, bom, 4); + if(!memcmp(bom, &bom32le, sizeof(bom32le))) { + strcpy(srcFmt, "utf-32le"); + bomLen = 4; + } else if(!memcmp(bom, &bom32be, sizeof(bom32be))) { + strcpy(srcFmt, "utf-32be"); + bomLen = 4; + } else if(!memcmp(bom, &bom16le, sizeof(bom16le))) { + strcpy(srcFmt, "utf-16le"); + bomLen = 2; + } else if(!memcmp(bom, &bom16be, sizeof(bom16be))) { + strcpy(srcFmt, "utf-16be"); + bomLen = 2; + } else if(!memcmp(bom, &bom8, 3)) { + strcpy(srcFmt, "utf-8"); + bomLen = 3; + } else { // don't convert + strcpy(srcFmt, destFmt); + bomLen = 0; + } + + if(bomLen != 4) { + fseek(fp, bomLen, SEEK_SET); + } + te->indexes[i].length -= bomLen; + + // re-use BOMlen to specify character width + bomLen = RCO_TEXT_FMT_CHARWIDTH(te->format); + srcBufferTmp = realloc(srcBufferTmp, curPos + ALIGN_TO_4(te->indexes[i].length+bomLen)); + uint8* bufferPos = (uint8*)srcBufferTmp + curPos; + if(strcmp(srcFmt, destFmt)) { + uint fPos = ftell(fp); + uint fSize; + fseek(fp, 0, SEEK_END); + fSize = ftell(fp) - fPos; + fseek(fp, fPos, SEEK_SET); + + uint8 *fBuf = (uint8*)malloc(fSize); + fileread(fp, fBuf, fSize); + iconv_t ic = iconv_open(destFmt, srcFmt); + // get rid of BOM made by iconv + iconv(ic, (char**)(&fBuf), (size_t*)(&fSize), (char**)&bom, (size_t*)(&bomLen)); + iconv(ic, (char**)(&fBuf), (size_t*)(&fSize), (char**)&bufferPos, (size_t*)(&te->indexes[i].length)); + iconv_close(ic); + } else + fileread(fp, bufferPos, te->indexes[i].length); + // add terminating null & any necessary padding + memset(bufferPos + (te->indexes[i].length), 0, ALIGN_TO_4(te->indexes[i].length+bomLen) - (te->indexes[i].length)); + + /* uint16 unisig; + if(te->indexes[i].length >= 2) + fileread(fp, &unisig, sizeof(unisig)); + if(te->indexes[i].length == 1 || unisig != UNICODE_SIGNATURE) { + te->indexes[i].length += 2; // add space for trailing null + fseek(fp, 0, SEEK_SET); + } + if(te->indexes[i].length <= 2) { + // this is probably blank + te->indexes[i].length = 0; + } else { + srcBufferTmp = realloc(srcBufferTmp, curPos + ALIGN_TO_4(te->indexes[i].length)); + uint8* bufferPos = (uint8*)srcBufferTmp + curPos; + fileread(fp, bufferPos, te->indexes[i].length-2); + // add terminating null & any necessary padding + memset(bufferPos + (te->indexes[i].length-2), 0, ALIGN_TO_4(te->indexes[i].length) - (te->indexes[i].length-2)); + } */ + fclose(fp); + } else + warning("[line %d] Can't find or open file '%s'", node->line, srcFile); + } + } else { + te->indexes[i].length = 0; + warning("[line %d] Can't find or open file '%s'", node->line, srcFile); + } + te->indexes[i].offset = curPos; + curPos += ALIGN_TO_4(te->indexes[i].length); + } + entry->srcBuffer = srcBufferTmp; + entry->srcLen = entry->srcLenUnpacked = curPos; + } // else, they're trying to hack us... :/ + free(srcFileFmt); + } else if(te->numIndexes == 1 && entry->srcLen) { + // err, if there's just one text, we can just use the srcRange, lol + te->indexes[0].length = entry->srcLen; + te->indexes[0].offset = 0; + } else { + warning("[line %d] Cannot determine sizes of text data!", node->line); + } + } + + } + break; + + case RCO_TABLE_FONT: + if(entry->type == 1) { + entry->extraLen = sizeof(rRCOFontEntry); + entry->extra = malloc(entry->extraLen); + rRCOFontEntry* rfe = (rRCOFontEntry*)entry->extra; + // default value + rfe->format = 1; + rfe->compression = 0; + rfe->unknown = 0; + rfe->unknown2 = 0; + + xmlChar* val; + val = xmlGetProp(node, _X("unknownShort1")); + sscanf((const char*)val, "%i", &rfe->format); + xmlFree(val); + val = xmlGetProp(node, _X("unknownShort2")); + sscanf((const char*)val, "%i", &rfe->compression); + xmlFree(val); + val = xmlGetProp(node, _X("unknownInt3")); + sscanf((const char*)val, "%i", &rfe->unknown); + xmlFree(val); + val = xmlGetProp(node, _X("unknownInt4")); + sscanf((const char*)val, "%i", &rfe->unknown2); + xmlFree(val); + } + break; + case RCO_TABLE_OBJ: + parse_obj_extra(node, entry); + break; + case RCO_TABLE_ANIM: + parse_anim_extra(node, entry); + break; + } + + + + // pointer sect + if(entry->type == 0 || (entry->type == 1 && entry->id == RCO_TABLE_VSMX)) { + + // shortcut table assignment + rRCOEntry** shortcutTbl = NULL; + switch(entry->id) { + case RCO_TABLE_VSMX: shortcutTbl = &(rco->tblVSMX); break; + case RCO_TABLE_TEXT: shortcutTbl = &(rco->tblText); break; + case RCO_TABLE_IMG: shortcutTbl = &(rco->tblImage); break; + case RCO_TABLE_SOUND: shortcutTbl = &(rco->tblSound); break; + case RCO_TABLE_MODEL: shortcutTbl = &(rco->tblModel); break; + case RCO_TABLE_FONT: shortcutTbl = &(rco->tblFont); break; + case RCO_TABLE_OBJ: shortcutTbl = &(rco->tblObj); break; + case RCO_TABLE_ANIM: shortcutTbl = &(rco->tblAnim); break; + } + if(shortcutTbl) { + if(*shortcutTbl) { + warning("[line %d] 'Container' tree redefinition (only one should exist in an RCO file).", node->line); + } + else { + *shortcutTbl = entry; + } + } + } + } + + // subentries + if(node->xmlChildrenNode) { + xmlNodePtr np = node->xmlChildrenNode; + rRCOEntry* rcoNode = NULL; + while(np->next) { + if(np->type == XML_ELEMENT_NODE) { + rRCOEntry* curNode = (rRCOEntry*)malloc(sizeof(rRCOEntry)); + parse_entry(np, curNode, rco, fixes); + + curNode->parent = entry; + if(rcoNode) { + curNode->prev = rcoNode; + curNode->prev->next = curNode; + } else + entry->firstChild = curNode; + + rcoNode = curNode; + entry->numSubentries++; + } + np = np->next; + } + entry->lastChild = rcoNode; + + } + +} + +Bool rcoxml_text_to_int(char* s, const RcoTableMap map, uint* out) { + if(!s[0]) return FALSE; + + int i=0; + while(map[i][0]) { + if(!strcasecmp(map[i], s)) { + *out = (uint)i; + return TRUE; + } + i++; + } + + // see if this is "unknown" + return (sscanf(s, "unknown%i", out) == 1); +} + +// currently very basic - resize when adding a label +//uint rcoxml_add_label(rRCOFile* rco, char* label) { +uint rcoxml_add_label(char** labels, uint* labelsLen, char* label, Bool eventQuirk) { + // first, see if we already have this label + uint p = 0; + while(p < *labelsLen && (*labels)[p]) { + if(!strcmp(*labels + p, label)) + return p; // found + p += strlen(*labels + p) +1; + p = ALIGN_TO_4(p); // urgh, this is kinda a little dirty, but it works; if we hit a blank 4 bytes, the above line will cause it to go forward by 1, this will align it forward to 4, so, if we've hit some nulls, we're effectively jumping 4 bytes at a time + } + + // don't have it? add it + uint curLen = *labelsLen; + uint labelLen = strlen(label) +1; + if(eventQuirk) { + // TODO: + } + uint newLen = curLen + labelLen; + newLen = ALIGN_TO_4(newLen); + + *labels = (char*)realloc(*labels, newLen); + strcpy(*labels + curLen, label); + if(labelLen % 4) { + memset(*labels + curLen + labelLen, 0, 4 - (labelLen % 4)); + } + *labelsLen = newLen; +/* + uint curLen = rco->labelsLen; + uint newLen = rco->labelsLen + strlen(label) + 1; + + rco->labels = (char*)realloc(rco->labels, newLen); + strcpy(curLen, label); + rco->labelsLen = newLen; +*/ + return curLen; +} + +uint rcoxml_add_label_reordering(char* newLabels, uint* labelPos, char* label) { + // first, see if we already have this label + uint p = 0; + while(p < *labelPos && newLabels[p]) { + if(!strcmp(newLabels + p, label)) + return p; // found + p += strlen(newLabels + p) +1; + p = ALIGN_TO_4(p); // dirty, but it works (see note from rcoxml_add_label) + } + + // don't have it? add it + strcpy(newLabels + *labelPos, label); + uint curPos = *labelPos; + *labelPos += strlen(label) +1; + *labelPos = ALIGN_TO_4(*labelPos); + + return curPos; +} + +int label_reorder_qsort(const rRCOEntry** a, const rRCOEntry** b) { + return (*a)->id - (*b)->id; +} + +void rcoxml_reorder_labels(char* newLabels, uint* labelPos, rRCOFile* rco, rRCOEntry* entry) { + uint i; + + if(entry->labelOffset != RCO_NULL_PTR) + entry->labelOffset = rcoxml_add_label_reordering(newLabels, labelPos, rco->labels + entry->labelOffset); + + if(entry->id == RCO_TABLE_TEXT && entry->type == 1) { + rRCOTextEntry* extra = (rRCOTextEntry*)entry->extra; + for(i=0; inumIndexes; i++) + if(extra->indexes[i].labelOffset != RCO_NULL_PTR) + extra->indexes[i].labelOffset = rcoxml_add_label_reordering(newLabels, labelPos, rco->labels + extra->indexes[i].labelOffset); + } + + rRCOEntry* rcoNode; + for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) + rcoxml_reorder_labels(newLabels, labelPos, rco, rcoNode); +} + +void parse_obj_extra(xmlNodePtr node, rRCOEntry* entry) { + + if(entry->type <= RCO_OBJ_EXTRA_LEN_NUM && RCO_OBJ_EXTRA_LEN[entry->type] != -1) { + uint i = 0, i2 = 0; + + // work out the length of this thing, lol + entry->extraLen = RCO_OBJ_EXTRA_LEN[entry->type] * sizeof(uint32); + for(i=0, i2=0; i<(uint)RCO_OBJ_EXTRA_LEN[entry->type]; i++, i2++) { + if(RCO_OBJ_IS_REF(entry->type, i2)) { + entry->extraLen -= 2*sizeof(uint32); + entry->extraLen += sizeof(rRCORef); + i++; + } + } + + entry->extra = malloc(entry->extraLen); + uint8* extra = (uint8*)entry->extra; + memset(extra, 0, entry->extraLen); + + for(i=0, i2=0; (int)itype]; i++, i2++) { + Bool isRef = RCO_OBJ_IS_REF(entry->type, i2); + + xmlChar* val = NULL; + if(RCO_OBJ_EXTRA_NAMES[entry->type][i2][0]) { + val = xmlGetProp(node, _X(RCO_OBJ_EXTRA_NAMES[entry->type][i2])); + } + + if(!val) { + val = rcoxml_get_unknown_attrib(node, i); + } + + if(!val) { + if(RCO_OBJ_EXTRA_NAMES[entry->type][i2][0]) { + warning("[line %d] Missing attribute '%s', defaulting to 0 / nothing.", node->line, RCO_OBJ_EXTRA_NAMES[entry->type][i2]); + } else { + char ts[10] = "\0"; + switch(RCO_OBJ_EXTRA_TYPES[entry->type][i2]) { + case RCO_OBJ_EXTRA_TYPE_FLOAT: strcpy(ts, "Float"); break; + case RCO_OBJ_EXTRA_TYPE_INT: strcpy(ts, "Int"); break; + case RCO_OBJ_EXTRA_TYPE_EVENT: strcpy(ts, "Event"); break; + case RCO_OBJ_EXTRA_TYPE_IMG: strcpy(ts, "Image"); break; + case RCO_OBJ_EXTRA_TYPE_FONT: strcpy(ts, "Font"); break; + case RCO_OBJ_EXTRA_TYPE_MODEL: strcpy(ts, "Model"); break; + case RCO_OBJ_EXTRA_TYPE_OBJ: strcpy(ts, "Object"); break; + case RCO_OBJ_EXTRA_TYPE_UNK: case RCO_OBJ_EXTRA_TYPE_REF: + if(isRef) strcpy(ts, "Ref"); + break; + } + warning("[line %d] Missing attribute 'unknown%s%d', defaulting to 0 / nothing.", node->line, ts, i); + } + + // default values + if(isRef) + ((rRCORef*)extra)->type = RCO_REF_NONE; + else + *(uint32*)extra = 0; + } + else { + if(isRef) { + // refs may need fixing later on + if(!rcoxml_parse_ref((char*)val, (rRCORef*)extra)) + warning("[line %d] Unable to parse reference '%s' - defaulting to 'nothing'.", node->line, (char*)val); + /* + // we really need to fix refs after all entries are loaded - I'm going to be lazy here since we can't actually do anything terribly useful right now + ((rRCORef*)extra)->type = RCO_REF_NONE; + add_ref_to_fix(fixes, , (rRCORef*)extra); + */ + } else { + *(uint32*)extra = rcoxml_parse_value((char*)val); + } + xmlFree(val); + } + + if(isRef) { + extra += sizeof(rRCORef); + i++; + } else + extra += sizeof(uint32); + } + } else { + // TODO: handle unknown types + } +} + + +void parse_anim_extra(xmlNodePtr node, rRCOEntry* entry) { + + if(entry->type <= RCO_ANIM_EXTRA_LEN_NUM && RCO_ANIM_EXTRA_LEN[entry->type] != -1) { + uint i = 0, i2 = 0; + + // work out the length of this thing, lol + entry->extraLen = RCO_ANIM_EXTRA_LEN[entry->type] * sizeof(uint32); + for(i=0, i2=0; i<(uint)RCO_ANIM_EXTRA_LEN[entry->type]; i++, i2++) { + if(RCO_ANIM_IS_REF(entry->type, i2)) { + entry->extraLen -= 2*sizeof(uint32); + entry->extraLen += sizeof(rRCORef); + i++; + } + } + + entry->extra = malloc(entry->extraLen); + uint8* extra = (uint8*)entry->extra; + memset(extra, 0, entry->extraLen); + + for(i=0, i2=0; (int)itype]; i++, i2++) { + Bool isRef = RCO_ANIM_IS_REF(entry->type, i2); + + xmlChar* val = NULL; + if(RCO_ANIM_EXTRA_NAMES[entry->type][i2][0]) { + val = xmlGetProp(node, _X(RCO_ANIM_EXTRA_NAMES[entry->type][i2])); + } + + if(!val) { + val = rcoxml_get_unknown_attrib(node, i); + } + + if(!val) { + if(RCO_ANIM_EXTRA_NAMES[entry->type][i2][0]) { + warning("[line %d] Missing attribute '%s', defaulting to 0 / nothing.", node->line, RCO_ANIM_EXTRA_NAMES[entry->type][i2]); + } else { + char ts[10] = "\0"; + switch(RCO_ANIM_EXTRA_TYPES[entry->type][i2]) { + case RCO_OBJ_EXTRA_TYPE_FLOAT: strcpy(ts, "Float"); break; + case RCO_OBJ_EXTRA_TYPE_INT: strcpy(ts, "Int"); break; + case RCO_OBJ_EXTRA_TYPE_EVENT: strcpy(ts, "Event"); break; + case RCO_OBJ_EXTRA_TYPE_IMG: strcpy(ts, "Image"); break; + case RCO_OBJ_EXTRA_TYPE_FONT: strcpy(ts, "Font"); break; + case RCO_OBJ_EXTRA_TYPE_MODEL: strcpy(ts, "Model"); break; + case RCO_OBJ_EXTRA_TYPE_OBJ: strcpy(ts, "Object"); break; + case RCO_OBJ_EXTRA_TYPE_UNK: case RCO_OBJ_EXTRA_TYPE_REF: + if(isRef) strcpy(ts, "Ref"); + break; + } + warning("[line %d] Missing attribute 'unknown%s%d', defaulting to 0 / nothing.", node->line, ts, i); + } + + // default values + if(isRef) + ((rRCORef*)extra)->type = RCO_REF_NONE; + else + *(uint32*)extra = 0; + } + else { + if(isRef) { + // refs may need fixing later on + if(!rcoxml_parse_ref((char*)val, (rRCORef*)extra)) + warning("[line %d] Unable to parse reference '%s' - defaulting to 'nothing'.", node->line, (char*)val); + /* + // we really need to fix refs after all entries are loaded - I'm going to be lazy here since we can't actually do anything terribly useful right now + ((rRCORef*)extra)->type = RCO_REF_NONE; + add_ref_to_fix(fixes, , (rRCORef*)extra); + */ + } else { + *(uint32*)extra = rcoxml_parse_value((char*)val); + } + xmlFree(val); + } + + if(isRef) { + extra += sizeof(rRCORef); + i++; + } else + extra += sizeof(uint32); + } + } else { + // TODO: handle unknown types + } +} + +/* +void parse_anim_extra(xmlNodePtr node, rRCOEntry* entry) { + + if(entry->type <= RCO_ANIM_EXTRA_LEN_NUM && RCO_ANIM_EXTRA_LEN[entry->type] != -1) { + uint i = 0; + + entry->extraLen = 0; + for(i=0, i2=0; i<(uint)RCO_ANIM_EXTRA_LEN[entry->type]; i++, i2++) { + if(RCO_ANIM_IS_REF(entry->type, i2)) { + entry->extraLen += sizeof(rRCORef); + i++; + } else + entry->extraLen += sizeof(uint32); + } + + //if(RCO_ANIM_EXTRA_REFS[entry->type]) { + // entry->extraLen = (RCO_ANIM_EXTRA_LEN[entry->type]-2) * sizeof(uint32) + sizeof(rRCORef); + //} else { + // entry->extraLen = RCO_ANIM_EXTRA_LEN[entry->type] * sizeof(uint32); + //} + + entry->extra = malloc(entry->extraLen); + uint8* extra = (uint8*)entry->extra; + memset(extra, 0, entry->extraLen); + + uint entryLen = (uint)RCO_ANIM_EXTRA_LEN[entry->type]; + + if(RCO_ANIM_EXTRA_REFS[entry->type]) { + xmlChar* val = NULL; + if(entry->type == RCO_ANIM_TYPE_EVENT) + val = xmlGetProp(node, _X("event")); + else + val = xmlGetProp(node, _X("object")); + + if(val) { + if(!rcoxml_parse_ref((char*)val, (rRCORef*)extra)) + warning("[line %d] Unable to parse object reference '%s' - defaulting to 'nothing'.", node->line, (char*)val); + } else { + warning("[line %d] No object/event defined for anim entry. Defaulting to nothing.", node->line); + // blank ref + rRCORef* dr = ((rRCORef*)extra); + dr->type = RCO_REF_NONE; + dr->ptr = NULL; + dr->rawPtr = RCO_NULL_PTR; + } + extra += sizeof(rRCORef); + entryLen -=2; + } + + for(i=0; itype][i][0]) { + val = xmlGetProp(node, _X(RCO_ANIM_EXTRA_NAMES[entry->type][i])); + } + if(!val) { + val = rcoxml_get_unknown_attrib(node, i); + } + + if(!val) { + if(RCO_ANIM_EXTRA_NAMES[entry->type][i][0]) { + warning("[line %d] Missing attribute '%s', defaulting to 0 / nothing.", node->line, RCO_ANIM_EXTRA_NAMES[entry->type][i]); + } else { + warning("[line %d] Missing attribute (index %d), defaulting to 0 / nothing.", node->line, i); // TODO: better message + } + *(uint32*)extra = 0; + } + else { + *(uint32*)extra = rcoxml_parse_value((char*)val); + xmlFree(val); + } + + extra += sizeof(uint32); + } + } else { + // TODO: handle unknown types + } +} +*/ + +// TODO: this somewhat mixes normal vals with refs (not optimal) - may wish to do something about this +xmlChar* rcoxml_get_unknown_attrib(xmlNodePtr node, uint num) { + xmlChar* ret = NULL; + char n[30]; + + #define RCOXML_GET_UNKNOWN_ATTRIB_DO(t) { \ + sprintf(n, "unknown" t "%d", num); \ + ret = xmlGetProp(node, _X(n)); \ + if(ret) return ret; \ + } + RCOXML_GET_UNKNOWN_ATTRIB_DO(""); + RCOXML_GET_UNKNOWN_ATTRIB_DO("Int"); + RCOXML_GET_UNKNOWN_ATTRIB_DO("Float"); + RCOXML_GET_UNKNOWN_ATTRIB_DO("Event"); + RCOXML_GET_UNKNOWN_ATTRIB_DO("Image"); + RCOXML_GET_UNKNOWN_ATTRIB_DO("Model"); + RCOXML_GET_UNKNOWN_ATTRIB_DO("Font"); + RCOXML_GET_UNKNOWN_ATTRIB_DO("Object"); + RCOXML_GET_UNKNOWN_ATTRIB_DO("Ref"); + + return NULL; +} + +// parse object/anim attrib values +uint32 rcoxml_parse_value(char* s) { + uint32 retI=0; + float retF=0; + + if(sscanf(s, "0x%x", (uint*)&retI)) return retI; + + retF = strtof(s, NULL); + memcpy(&retI, &retF, sizeof(uint32)); + + return retI; +} + +Bool rcoxml_parse_ref(char* val, rRCORef* out) { + // defaults + out->type = RCO_REF_NONE; + out->rawPtr = RCO_NULL_PTR; + out->ptr = NULL; + + if(!strcasecmp(val, "nothing")) { + return TRUE; + } + + // search for ":" + char* colon = strchr(val, ':'); + if(!colon) return FALSE; + colon[0] = '\0'; + + if(!strcasecmp(val, "event")) + out->type = RCO_REF_EVENT; + else if(!strcasecmp(val, "text")) + out->type = RCO_REF_TEXT; + else if(!strcasecmp(val, "image")) + out->type = RCO_REF_IMG; + else if(!strcasecmp(val, "model")) + out->type = RCO_REF_MODEL; + else if(!strcasecmp(val, "font")) + out->type = RCO_REF_FONT; + else if(!strcasecmp(val, "object2")) + out->type = RCO_REF_OBJ2; + else if(!strcasecmp(val, "anim")) + out->type = RCO_REF_ANIM; + else if(!strcasecmp(val, "object")) + out->type = RCO_REF_OBJ; + + colon[0] = ':'; // restore for good measure + colon++; // colon is now "name" + + if(out->type == RCO_REF_NONE) { // not assigned by above ifs + if(sscanf(val, "unknown%i", (int*)&(out->type))) { // this is actually case sensitive... :| + out->rawPtr = strtol(colon, NULL, 10); + return TRUE; + } + else { + return FALSE; + } + } + + out->ptr = malloc(strlen(colon)+1); + strcpy((char*)out->ptr, colon); + + return TRUE; +} + +/* +// a very simple resize method :/ +void add_ref_to_fix(rcoxml_read_fixes* fixes, char* label, rRCORef* ref) { + fixes->refs = (xmlrco_read_fix_refs*)realloc(fixes->refs, sizeof(xmlrco_read_fix_refs) * (fixes->refCnt+1)); + xmlrco_read_fix_refs* fr = &(fixes->refs[fixes->refsCnt]); + + fr->ref = ref; + fr->label = (char*)malloc(strlen(label)+1); + strcpy(fr->label, label); + + fixes->refsCnt++; +} +*/ + +void rcoxml_fix_refs(rRCOEntry* entry, rRCOFile* rco) { + uint i = 0, i2 = 0; + + if((entry->id == RCO_TABLE_OBJ && entry->type > 0 && entry->type <= RCO_OBJ_EXTRA_LEN_NUM && RCO_OBJ_EXTRA_LEN[entry->type] != -1) + || (entry->id == RCO_TABLE_ANIM && entry->type > 1 && entry->type <= RCO_ANIM_EXTRA_LEN_NUM && RCO_ANIM_EXTRA_LEN[entry->type] != -1)) { + uint8* extra = (uint8*)entry->extra; + + uint len; + if(entry->id == RCO_TABLE_OBJ) + len = RCO_OBJ_EXTRA_LEN[entry->type]; + else + len = RCO_ANIM_EXTRA_LEN[entry->type]; + for(i=0, i2=0; iid == RCO_TABLE_OBJ && RCO_OBJ_IS_REF(entry->type, i2)) + || (entry->id != RCO_TABLE_OBJ && RCO_ANIM_IS_REF(entry->type, i2))) { + rcoxml_fix_ref((rRCORef*)extra, rco); + extra += sizeof(rRCORef); + i++; + } else + extra += sizeof(uint32); + } + /* + if(entry->id == RCO_TABLE_OBJ) { + for(i=0, i2=0; (int)itype]; i++, i2++) { + if(RCO_OBJ_IS_REF(entry->type, i2)) { + rcoxml_fix_ref((rRCORef*)extra, rco); + extra += sizeof(rRCORef); + i++; + } else + extra += sizeof(uint32); + } + + } else { // anim entries + if(RCO_ANIM_EXTRA_REFS[entry->type]) { + rcoxml_fix_ref((rRCORef*)extra, rco); + } + } + */ + } + + if(entry->numSubentries) { + rRCOEntry* rcoNode; + for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) + rcoxml_fix_refs(rcoNode, rco); + } +} + +// fixes the pointer of a reference if it needs one +// (current pointer needs to be pointing at label name) +Bool rcoxml_fix_ref(rRCORef* ref, rRCOFile* rco) { + if(ref->type == RCO_REF_IMG || ref->type == RCO_REF_MODEL || ref->type == RCO_REF_FONT || ref->type == RCO_REF_OBJ2 || ref->type == RCO_REF_ANIM || ref->type == RCO_REF_OBJ) { + if(!ref->ptr) return FALSE; // no label... (this should never happen...) + + rRCOEntry* e = find_entry_from_label(&(rco->tblMain), (const char*)ref->ptr); + if(!e) { + warning("[entry-reference] Cannot find entry with label '%s'!", (const char*)ref->ptr); + } + else if( + (ref->type == RCO_REF_IMG && e->id != RCO_TABLE_IMG) + || (ref->type == RCO_REF_MODEL && e->id != RCO_TABLE_MODEL) + || (ref->type == RCO_REF_FONT && e->id != RCO_TABLE_FONT) + || (ref->type == RCO_REF_OBJ && e->id != RCO_TABLE_OBJ) + || (ref->type == RCO_REF_ANIM && e->id != RCO_TABLE_ANIM) + ) { + warning("[entry-reference] Mismatching reference type and entry type for reference with label '%s' - have you used the right name?", (const char*)ref->ptr); + } + + free(ref->ptr); // unallocate malloc'd label + ref->ptr = e; + ref->rawPtr = 0; // makes no sense, so blank it out + + return (e != NULL); + } + else if(ref->type == RCO_REF_EVENT) { + if(!ref->ptr) return FALSE; // no event... (this should never happen...) + + ref->rawPtr = rcoxml_add_label(&(rco->events), &rco->eventsLen, (char*)ref->ptr, TRUE); + free(ref->ptr); // unallocate malloc'd label + return TRUE; + } + else if(ref->type == RCO_REF_TEXT) { + ref->rawPtr = RCO_NULL_PTR; + // assume first text entry is correct + if(rco->tblText && rco->tblText->numSubentries) { + int idx = find_text_from_label(rco->labels, (rRCOTextEntry*)(rco->tblText->firstChild->extra), (const char*)ref->ptr); + if(idx == -1) { + warning("[entry-reference] Cannot find text entry with label '%s'! (note, only first text language is checked as the RCO format assumes that all languages have the same entry labels)", (const char*)ref->ptr); + } else { + ref->rawPtr = idx; + } + } else + warning("[entry-reference] Unable to reference text entry '%s' as text tree cannot be found, or contains no text languages!", (const char*)ref->ptr); + + free(ref->ptr); + ref->ptr = NULL; + return (ref->rawPtr != RCO_NULL_PTR); + } + else // no fixing required + return TRUE; +} + +void rcoxml_fix_ptrs(rRCOEntry*** sect, uint* sectCnt, rRCOFile* rco, const char* text) { + uint textLen = strlen(text); + *sectCnt = 0; + if(!textLen) { + // no text, we'll blank the section + *sect = NULL; + return; + } + + *sectCnt = 1; + char* tmpText = (char*)malloc(textLen +1); + char* tmpTextPtr = tmpText; + strcpy(tmpText, text); + + uint i; + /* + for(i=0; itblMain), tmpTextPtr); + if(!(*entryPtr)) + warning("[entry-ptr] Cannot find entry with label '%s'!", tmpTextPtr); + } else // blank pointer + *entryPtr = NULL; + + tmpTextPtr += labelLen +1; + entryPtr++; + } + + free(tmpText); +} + +// takes a comma separated string, replaces commas with nullchars and returns number of items in the list +uint split_comma_list(char* s) { + uint cnt = 1; + uint i; + uint sLen = strlen(s); + if(!s[0]) return 0; // empty string + + for(i=0; iextra; + + if(!(doc = xmlParseFile(fn))) { + warning("Can't parse XML file %s.", fn); + return FALSE; + } + if((node = xmlDocGetRootElement(doc))) + bValidDoc = (xmlStrcmp(node->name, _X("TextLang")) ? FALSE : TRUE); + if(!bValidDoc) { + warning("Invalid XML document %s.", fn); + return FALSE; + } + + te->numIndexes = 0; + if(!node->xmlChildrenNode) { + xmlFreeDoc(doc); + return TRUE; + } + + // count num of child nodes (don't use xmlChildElementCount because this may include text nodes) + xmlNodePtr np = node->xmlChildrenNode; + while(np->next) { + if(np->type == XML_ELEMENT_NODE && !xmlStrcmp(np->name, _X("Text"))) + te->numIndexes++; + np = np->next; + } + + if(te->numIndexes) { + void* textBuffer = NULL; + uint curPos = 0; + uint fmt = te->format; + char icDestFmt[8]; + uint charWidth = 2; + make_iconv_charset(icDestFmt, fmt, rco->eSwap); + if(fmt == RCO_TEXT_FMT_UTF32) { + charWidth = 4; + } + if(fmt == RCO_TEXT_FMT_UTF8) { + charWidth = 1; + } + iconv_t ic = iconv_open(icDestFmt, "utf-8");; + te->indexes = (RCOTextIndex*)calloc(1, te->numIndexes * sizeof(RCOTextIndex)); + + + np = node->xmlChildrenNode; + i = 0; + while(np->next) { + if(np->type == XML_ELEMENT_NODE && !xmlStrcmp(np->name, _X("Text"))) { + xmlChar* n = xmlGetProp(np, _X("name")); + if(n) { + te->indexes[i].labelOffset = rcoxml_add_label(&rco->labels, &rco->labelsLen, (char*)n, FALSE); + xmlFree(n); + + te->indexes[i].length = 0; + te->indexes[i].offset = RCO_NULL_PTR; + + if(np->xmlChildrenNode && (np->xmlChildrenNode->type == XML_TEXT_NODE || np->xmlChildrenNode->type == XML_CDATA_SECTION_NODE)) { + n = np->xmlChildrenNode->content; + if(fmt == RCO_TEXT_FMT_UTF8) { + te->indexes[i].length = xmlStrlen(n) +1/*null*/; + if((*(uint32*)n & 0xFFFFFF) == UTF8_BOM) + te->indexes[i].length -= 3; + } + else { + // xmlUTF8Strlen used because libxml2 will guarantee src is UTF8 + te->indexes[i].length = (xmlUTF8Strlen(n)+1 /*null*/) * charWidth; + if((*(uint32*)n & 0xFFFFFF) == UTF8_BOM) { // fix for older versions of rcomage - if BOM exists, reduce length by 1 char + te->indexes[i].length -= charWidth; + } + } + if(te->indexes[i].length > 2) { + uint contentLen = xmlStrlen(n), outBufLen = te->indexes[i].length; + + textBuffer = realloc(textBuffer, ALIGN_TO_4(te->indexes[i].length + curPos)); + char* tbPtr = (char*)textBuffer; + tbPtr += curPos; + // first get rid of BOM if we have one (earlier versions of rcomage) + /* if((fmt == RCO_TEXT_FMT_UTF32 && *(uint32*)n == UTF32_BOM) + ||(fmt == RCO_TEXT_FMT_UTF16 && *(uint16*)n == UTF16_BOM) + ||(fmt == RCO_TEXT_FMT_UTF8 && (*(uint32*)n & 0x00FFFFFF) == UTF8_BOM)) { */ + if((*(uint32*)n & 0xFFFFFF) == UTF8_BOM) { + char bom[4]; + char *bomPtr = bom; + uint bomLen = (fmt == RCO_TEXT_FMT_UTF32 ? 4 : (fmt == RCO_TEXT_FMT_UTF8 ? 3 : 2)); + iconv(ic, (char**)(&n), (size_t*)(&contentLen), (char**)&bomPtr, (size_t*)(&bomLen)); + } + iconv(ic, (char**)(&n), (size_t*)(&contentLen), &tbPtr, (size_t*)(&outBufLen)); + if(outBufLen && outBufLen == charWidth) { // *should* always be true + memset(tbPtr, 0, ALIGN_TO_4(te->indexes[i].length) - (te->indexes[i].length-outBufLen)); + } else + warning("[%s:%d] iconv error when trying to convert text for '%s'! (%d byte(s) not converted)", fn, np->line, rco->labels + te->indexes[i].labelOffset, outBufLen - charWidth); + + te->indexes[i].offset = curPos; + curPos += ALIGN_TO_4(te->indexes[i].length); + } else + te->indexes[i].length = 0; // blank string + } + + i++; + } else + warning("[%s:%d] No name specified for Text!", fn, np->line); + } + np = np->next; + } + entry->srcBuffer = textBuffer; + entry->srcFile[0] = '\0'; + entry->srcLen = entry->srcLenUnpacked = curPos; + iconv_close(ic); + } + + xmlFreeDoc(doc); + return TRUE; +} diff --git a/xmlwrite.c b/xmlwrite.c index e7fba8a..bf2f45a 100644 --- a/xmlwrite.c +++ b/xmlwrite.c @@ -1,547 +1,547 @@ - -// TODO: htmlspecialchars anywhere? - -#include -#include -#include -#include "general.h" -#include "rcomain.h" -#include "xml.h" - -#define IMP(a,b) (!(a) || (b)) // logical implication, ie, a implies b - -void xmlwrite_entry(rRCOEntry* entry, uint depth, rRCOFile* rco, FILE* fp, char* textDir, Bool textXmlOut, int sndDumped, Bool vsmxConv); -void xmlwrite_entry_extra_object(uint16 type, uint8* info, rRCOFile* rco, FILE* fp); -void xmlwrite_entry_extra_anim(uint16 type, uint8* info, rRCOFile* rco, FILE* fp); - -void xml_fputref(rRCORef* ref, rRCOFile* rco, FILE* fp); - -void rcoxml_fput_escstr(FILE* fp, char* str); - -Bool write_xml(rRCOFile* rco, FILE* fp, char* textDir, Bool textXmlOut, int sndDumped, Bool vsmxConv) { - - fputs("\n", fp); - fprintf(fp, "\n", APPNAME_VER); - fprintf(fp, "umdFlag, APPXMLVER); - if(rco->ps3) fprintf(fp, " type=\"ps3\""); - else fprintf(fp, " type=\"psp\""); - if(rco->verId) { - fputs(" minFirmwareVer=\"", fp); - switch(rco->verId) { - case 0x70: fputs("1.0", fp); break; - case 0x71: fputs("1.5", fp); break; - case 0x90: fputs("2.6", fp); break; - case 0x95: fputs("2.7", fp); break; - case 0x96: fputs("2.8", fp); break; - case 0x100: fputs("3.5", fp); break; - //case 0x107: fputs("ps3", fp); break; - default: fprintf(fp, "unknownId0x%x", rco->verId); - } - fputs("\"", fp); - } - fputs(">\n", fp); - // write entries - xmlwrite_entry(&(rco->tblMain), 1, rco, fp, textDir, textXmlOut, sndDumped, vsmxConv); - - fputs("\n", fp); - - fclose(fp); - return TRUE; -} - - -void xmlwrite_entry(rRCOEntry* entry, uint depth, rRCOFile* rco, FILE* fp, char* textDir, Bool textXmlOut, int sndDumped, Bool vsmxConv) { - uint i; - char dummy[50] = "\0"; - char* tagName = dummy; - - if(entry->id < RCOXML_TABLE_TAGS_NUM) { - uint maxType = 0; - while(RCOXML_TABLE_TAGS[entry->id][maxType][0]) - maxType++; - if(entry->type < maxType) { - strcpy(tagName, RCOXML_TABLE_TAGS[entry->id][entry->type]); - } else { - if(RCOXML_TABLE_NAMES[entry->id][0]) { - sprintf(tagName, "%sUnknown_0x%x", RCOXML_TABLE_NAMES[entry->id], entry->type); - } else { - sprintf(tagName, "Unknown_0x%x_0x%x", entry->id, entry->type); - } - } - } else { - sprintf(tagName, "Unknown_0x%x_0x%x", entry->id, entry->type); - } - - /* - switch(entry->id) { - case RCO_TABLE_MAIN: - strcpy(tagName, "Main"); break; - case RCO_TABLE_IMG: - strcpy(tagName, "Image"); break; - case RCO_TABLE_SOUND: - strcpy(tagName, "Sound"); break; - case RCO_TABLE_MODEL: - strcpy(tagName, "Model"); break; - case RCO_TABLE_TEXT: - strcpy(tagName, "Text"); break; - case RCO_TABLE_VSMX: - strcpy(tagName, "VSMX"); break; - case RCO_TABLE_OBJ: - if(entry->type <= RCO_OBJ_EXTRA_LEN_NUM && entry->type != 0xB) { - //char* objTags[] = {"Object", "Page", "Plane", "Button", "XMenu", "XMList", "XList", "Progress", "Scroll", "MList", "MItem", "ObjUnknown0xB", "XItem", "Text", "ModelObject", "Spin", "Action", "ItemSpin", "Group", "LList", "LItem", "Edit", "Clock", "IList", "IItem", "Icon", "UButton"}; // note "ObjUnknown0xB" doesn't exist - //tagName = objTags[entry->type]; - tagName = (char*)(RCOXML_TABLE_TAGS[RCO_TABLE_OBJ][entry->type]); - } - else - sprintf(tagName, "ObjUnknown0x%x", entry->type); - break; - case RCO_TABLE_ANIM: - if(entry->type <= RCO_ANIM_EXTRA_LEN_NUM) { - char* animTags[] = {"Anim", "Animation", "Move", "ColourChange", "Rotate", "Resize", "Fade", "Delay", "FireEvent", "Lock", "Unlock", "UnknownB"}; - tagName = animTags[entry->type]; - } - else - sprintf(tagName, "AnimUnknown0x%x", entry->type); - break; - } - */ - - for(i=0; itype == 0 || (entry->id == RCO_TABLE_MAIN && entry->type == 1)); - //if(isMainTable) - // fputs("Table", fp); - - if(entry->labelOffset != RCO_NULL_PTR) { - fputs(" name=\"", fp); - rcoxml_fput_escstr(fp, rco->labels + entry->labelOffset); - fputs("\"", fp); - } - if(entry->srcFile[0]) { - fprintf(fp, " src=\"%s\"", entry->srcFile); - if((IMP(entry->id == RCO_TABLE_TEXT, !textXmlOut && !textDir) && IMP(entry->id == RCO_TABLE_SOUND, !sndDumped) && IMP(entry->id == RCO_TABLE_VSMX, !vsmxConv)) && (entry->srcAddr || entry->srcCompression || entry->srcLen != filesize(entry->srcFile))) { - fprintf(fp, " srcRange=\"0x%x-0x%x", entry->srcAddr, entry->srcAddr+entry->srcLen); - if(entry->srcCompression) { - if(entry->srcCompression == RCO_DATA_COMPRESSION_RCO) { - fputs(";rco", fp); - } else { - char compr[30]; - rcoxml_int_to_text(entry->srcCompression, RCOXML_TABLE_DATA_COMPRESSION, compr); - fprintf(fp, ";%s[%u]", compr, entry->srcLenUnpacked); - } - } - fputs("\"", fp); - } - } - // extra attribs - if(isMainTable) { - /* - // pointer ordering - uint numPtrs = 0; - void* ptrs; - switch(entry->id) { - case RCO_TABLE_TEXT: - ptrs = rco->ptrText; - numPtrs = rco->numPtrText; - break; - case RCO_TABLE_IMG: - ptrs = rco->ptrImg; - numPtrs = rco->numPtrImg; - break; - case RCO_TABLE_SOUND: - ptrs = rco->ptrSound; - numPtrs = rco->numPtrSound; - break; - case RCO_TABLE_MODEL: - ptrs = rco->ptrModel; - numPtrs = rco->numPtrModel; - break; - case RCO_TABLE_OBJ: - ptrs = rco->ptrObj; - numPtrs = rco->numPtrObj; - break; - case RCO_TABLE_ANIM: - ptrs = rco->ptrAnim; - numPtrs = rco->numPtrAnim; - break; - } - if(numPtrs) { - uint j; - fputs(" ptrorder=\"", fp); - for(i=0; iid == RCO_TABLE_TEXT) { - rRCOTextIdxPtr* tip = &(((rRCOTextIdxPtr*)ptrs)[i]); - if(tip->textEntry && tip->index) { - char tmp[30]; - rcoxml_int_to_text(((rRCOTextEntry*)tip->textEntry->extra)->lang, RCOXML_TABLE_TEXT_LANG, tmp); - fprintf(fp, "%s:%s", tmp, rco->labels + tip->index->labelOffset); - } - } else { - if(((rRCOEntry**)ptrs)[i]) - fputs(((rRCOEntry**)ptrs)[i]->labelOffset + rco->labels, fp); - } - } - fputc('\n', fp); - for(j=0; jid) { - case RCO_TABLE_VSMX: - // do nothing - break; - case RCO_TABLE_TEXT: - { - rRCOTextEntry* te = (rRCOTextEntry*)entry->extra; - rcoxml_int_to_text(te->lang, RCOXML_TABLE_TEXT_LANG, tmp); - fprintf(fp, " language=\"%s\"", tmp); - rcoxml_int_to_text(te->format, RCOXML_TABLE_TEXT_FMT, tmp); - fprintf(fp, " format=\"%s\"", tmp); - if(te->numIndexes && !textXmlOut) { // write text labels - uint j; - fputs(" entries=\"", fp); - for(i=0; inumIndexes; i++) { - if(i) fputc(',', fp); - fputc('\n', fp); - for(j=0; jindexes[i].labelOffset != RCO_NULL_PTR) - fputs(rco->labels + te->indexes[i].labelOffset, fp); - } - fputc('\n', fp); - for(j=0; jnumIndexes; i++) { - if(i) fputc(',', fp); - fprintf(fp, "0x%x@0x%x", te->indexes[i].length, te->indexes[i].offset); - } - fputs("\"", fp); - } - } - } - break; - case RCO_TABLE_IMG: case RCO_TABLE_MODEL: - rcoxml_int_to_text(((rRCOImgModelEntry*)entry->extra)->format, (entry->id == RCO_TABLE_IMG ? RCOXML_TABLE_IMG_FMT : RCOXML_TABLE_MODEL_FMT), tmp); - fprintf(fp, " format=\"%s\"", tmp); - rcoxml_int_to_text(((rRCOImgModelEntry*)entry->extra)->compression, RCOXML_TABLE_DATA_COMPRESSION, tmp); - fprintf(fp, " compression=\"%s\"", tmp); - fprintf(fp, " unknownByte=\"%d\"", ((rRCOImgModelEntry*)entry->extra)->unkCompr); - break; - case RCO_TABLE_SOUND: - { - rRCOSoundEntry* se = ((rRCOSoundEntry*)entry->extra); - rcoxml_int_to_text(se->format, RCOXML_TABLE_SOUND_FMT, tmp); - fprintf(fp, " format=\"%s\"", tmp); - if(se->format == RCO_SOUND_VAG && sndDumped != 2) - fprintf(fp, " channels=\"%d\"", se->channels); - - if(se->channels && !sndDumped) { - // write srcParts - fputs(" srcParts=\"", fp); - for(i=0; ichannels; i++) { - if(i) fputc(',', fp); - fprintf(fp, "0x%x@0x%x", se->channelData[i*2], se->channelData[i*2 +1]); - } - fputs("\"", fp); - } - - } - break; - case RCO_TABLE_FONT: { - rRCOFontEntry* rfe = (rRCOFontEntry*)entry->extra; - - fprintf(fp, " unknownShort1=\"0x%x\" unknownShort2=\"0x%x\" unknownInt3=\"0x%x\" unknownInt4=\"0x%x\"", rfe->format, rfe->compression, rfe->unknown, rfe->unknown2); - - } break; - case RCO_TABLE_OBJ: - xmlwrite_entry_extra_object(entry->type, (uint8*)entry->extra, rco, fp); - break; - case RCO_TABLE_ANIM: - xmlwrite_entry_extra_anim(entry->type, (uint8*)entry->extra, rco, fp); - break; - } - } - - if(entry->numSubentries) { - rRCOEntry* rcoNode; - fputs(">\n", fp); - - for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) - xmlwrite_entry(rcoNode, depth+1, rco, fp, textDir, textXmlOut, sndDumped, vsmxConv); - - - for(i=0; i\n", tagName, (isMainTable ? "Table" : "")); - fprintf(fp, "\n", tagName); - - } else { - if(entry->id == RCO_TABLE_OBJ || entry->type == 0 || (entry->type == 1 && (entry->id == RCO_TABLE_ANIM || entry->id == RCO_TABLE_MAIN || entry->id == RCO_TABLE_VSMX))) - fprintf(fp, ">\n", tagName); - else - fputs(" />\n", fp); - } -} - - - -void xmlwrite_entry_extra_object(uint16 type, uint8* info, rRCOFile* rco, FILE* fp) { - - int numEntries = RCO_OBJ_EXTRA_LEN[type]; - int i=0, i2=0; - - if(numEntries < 1) return; // TODO: handle unknown object types? - - for(i=0, i2=0; itype) { - case RCO_REF_EVENT: fputs("event:", fp); break; - case RCO_REF_TEXT: fputs("text:", fp); break; - case RCO_REF_IMG: fputs("image:", fp); break; - case RCO_REF_MODEL: fputs("model:", fp); break; - case RCO_REF_FONT: fputs("font:", fp); break; - case RCO_REF_OBJ2: fputs("object2:", fp); break; - case RCO_REF_ANIM: fputs("anim:", fp); break; - case RCO_REF_OBJ: fputs("object:", fp); break; - case RCO_REF_NONE: fputs("nothing", fp); break; - default: - fprintf(fp, "unknown0x%x:", ref->type); - unkType = TRUE; - } - if(unkType) { - fprintf(fp, "0x%x", ref->rawPtr); - } else if(ref->type == RCO_REF_EVENT) { - //fputs((char*)ref->ptr, fp); - fputs(rco->events + ref->rawPtr, fp); - } else if(ref->type == RCO_REF_TEXT) { - // TODO: check this - // assume first text lang is accurate - if(rco->tblText && rco->tblText->numSubentries) - fputs(((rRCOTextEntry*)(rco->tblText->firstChild->extra))->indexes[ref->rawPtr].labelOffset + rco->labels, fp); - } else if(ref->type != RCO_REF_NONE) { - if(((rRCOEntry*)(ref->ptr))->labelOffset != RCO_NULL_PTR) - fputs(((rRCOEntry*)(ref->ptr))->labelOffset + rco->labels, fp); - else - fputs("", fp); // TODO: handle situations where there isn't a label... - } -} - -void rcoxml_int_to_text(uint in, const RcoTableMap map, char* out) { - uint len=0; - // determine length of map - while(map[len][0]) len++; - - // perhaps think about allowing blank/unknown values in midle of map? - if(in < len) { - strcpy(out, map[in]); - return; - } - sprintf(out, "unknown0x%x", in); -} - - -// custom reserved characters: ',' ':' (kinda) -void rcoxml_fput_escstr(FILE* fp, char* str) { - while(*str) { - switch(*str) { - case '<': fputs("<", fp); break; - case '>': fputs(">", fp); break; - case '"': fputs(""", fp); break; - case '&': fputs("&", fp); break; - case '\n': - //if(allowNL) { - fputc(*str, fp); - break; - //} - default: - if(*str < 32) - fprintf(fp, "&#%d;", *str); - else - fputc(*str, fp); - } - - str++; - } -} + +// TODO: htmlspecialchars anywhere? + +#include +#include +#include +#include "general.h" +#include "rcomain.h" +#include "xml.h" + +#define IMP(a,b) (!(a) || (b)) // logical implication, ie, a implies b + +void xmlwrite_entry(rRCOEntry* entry, uint depth, rRCOFile* rco, FILE* fp, char* textDir, Bool textXmlOut, int sndDumped, Bool vsmxConv); +void xmlwrite_entry_extra_object(uint16 type, uint8* info, rRCOFile* rco, FILE* fp); +void xmlwrite_entry_extra_anim(uint16 type, uint8* info, rRCOFile* rco, FILE* fp); + +void xml_fputref(rRCORef* ref, rRCOFile* rco, FILE* fp); + +void rcoxml_fput_escstr(FILE* fp, char* str); + +Bool write_xml(rRCOFile* rco, FILE* fp, char* textDir, Bool textXmlOut, int sndDumped, Bool vsmxConv) { + + fputs("\n", fp); + fprintf(fp, "\n", APPNAME_VER); + fprintf(fp, "umdFlag, APPXMLVER); + if(rco->ps3) fprintf(fp, " type=\"ps3\""); + else fprintf(fp, " type=\"psp\""); + if(rco->verId) { + fputs(" minFirmwareVer=\"", fp); + switch(rco->verId) { + case 0x70: fputs("1.0", fp); break; + case 0x71: fputs("1.5", fp); break; + case 0x90: fputs("2.6", fp); break; + case 0x95: fputs("2.7", fp); break; + case 0x96: fputs("2.8", fp); break; + case 0x100: fputs("3.5", fp); break; + //case 0x107: fputs("ps3", fp); break; + default: fprintf(fp, "unknownId0x%x", rco->verId); + } + fputs("\"", fp); + } + fputs(">\n", fp); + // write entries + xmlwrite_entry(&(rco->tblMain), 1, rco, fp, textDir, textXmlOut, sndDumped, vsmxConv); + + fputs("\n", fp); + + fclose(fp); + return TRUE; +} + + +void xmlwrite_entry(rRCOEntry* entry, uint depth, rRCOFile* rco, FILE* fp, char* textDir, Bool textXmlOut, int sndDumped, Bool vsmxConv) { + uint i; + char dummy[50] = "\0"; + char* tagName = dummy; + + if(entry->id < RCOXML_TABLE_TAGS_NUM) { + uint maxType = 0; + while(RCOXML_TABLE_TAGS[entry->id][maxType][0]) + maxType++; + if(entry->type < maxType) { + strcpy(tagName, RCOXML_TABLE_TAGS[entry->id][entry->type]); + } else { + if(RCOXML_TABLE_NAMES[entry->id][0]) { + sprintf(tagName, "%sUnknown_0x%x", RCOXML_TABLE_NAMES[entry->id], entry->type); + } else { + sprintf(tagName, "Unknown_0x%x_0x%x", entry->id, entry->type); + } + } + } else { + sprintf(tagName, "Unknown_0x%x_0x%x", entry->id, entry->type); + } + + /* + switch(entry->id) { + case RCO_TABLE_MAIN: + strcpy(tagName, "Main"); break; + case RCO_TABLE_IMG: + strcpy(tagName, "Image"); break; + case RCO_TABLE_SOUND: + strcpy(tagName, "Sound"); break; + case RCO_TABLE_MODEL: + strcpy(tagName, "Model"); break; + case RCO_TABLE_TEXT: + strcpy(tagName, "Text"); break; + case RCO_TABLE_VSMX: + strcpy(tagName, "VSMX"); break; + case RCO_TABLE_OBJ: + if(entry->type <= RCO_OBJ_EXTRA_LEN_NUM && entry->type != 0xB) { + //char* objTags[] = {"Object", "Page", "Plane", "Button", "XMenu", "XMList", "XList", "Progress", "Scroll", "MList", "MItem", "ObjUnknown0xB", "XItem", "Text", "ModelObject", "Spin", "Action", "ItemSpin", "Group", "LList", "LItem", "Edit", "Clock", "IList", "IItem", "Icon", "UButton"}; // note "ObjUnknown0xB" doesn't exist + //tagName = objTags[entry->type]; + tagName = (char*)(RCOXML_TABLE_TAGS[RCO_TABLE_OBJ][entry->type]); + } + else + sprintf(tagName, "ObjUnknown0x%x", entry->type); + break; + case RCO_TABLE_ANIM: + if(entry->type <= RCO_ANIM_EXTRA_LEN_NUM) { + char* animTags[] = {"Anim", "Animation", "Move", "ColourChange", "Rotate", "Resize", "Fade", "Delay", "FireEvent", "Lock", "Unlock", "UnknownB"}; + tagName = animTags[entry->type]; + } + else + sprintf(tagName, "AnimUnknown0x%x", entry->type); + break; + } + */ + + for(i=0; itype == 0 || (entry->id == RCO_TABLE_MAIN && entry->type == 1)); + //if(isMainTable) + // fputs("Table", fp); + + if(entry->labelOffset != RCO_NULL_PTR) { + fputs(" name=\"", fp); + rcoxml_fput_escstr(fp, rco->labels + entry->labelOffset); + fputs("\"", fp); + } + if(entry->srcFile[0]) { + fprintf(fp, " src=\"%s\"", entry->srcFile); + if((IMP(entry->id == RCO_TABLE_TEXT, !textXmlOut && !textDir) && IMP(entry->id == RCO_TABLE_SOUND, !sndDumped) && IMP(entry->id == RCO_TABLE_VSMX, !vsmxConv)) && (entry->srcAddr || entry->srcCompression || entry->srcLen != filesize(entry->srcFile))) { + fprintf(fp, " srcRange=\"0x%x-0x%x", entry->srcAddr, entry->srcAddr+entry->srcLen); + if(entry->srcCompression) { + if(entry->srcCompression == RCO_DATA_COMPRESSION_RCO) { + fputs(";rco", fp); + } else { + char compr[30]; + rcoxml_int_to_text(entry->srcCompression, RCOXML_TABLE_DATA_COMPRESSION, compr); + fprintf(fp, ";%s[%u]", compr, entry->srcLenUnpacked); + } + } + fputs("\"", fp); + } + } + // extra attribs + if(isMainTable) { + /* + // pointer ordering + uint numPtrs = 0; + void* ptrs; + switch(entry->id) { + case RCO_TABLE_TEXT: + ptrs = rco->ptrText; + numPtrs = rco->numPtrText; + break; + case RCO_TABLE_IMG: + ptrs = rco->ptrImg; + numPtrs = rco->numPtrImg; + break; + case RCO_TABLE_SOUND: + ptrs = rco->ptrSound; + numPtrs = rco->numPtrSound; + break; + case RCO_TABLE_MODEL: + ptrs = rco->ptrModel; + numPtrs = rco->numPtrModel; + break; + case RCO_TABLE_OBJ: + ptrs = rco->ptrObj; + numPtrs = rco->numPtrObj; + break; + case RCO_TABLE_ANIM: + ptrs = rco->ptrAnim; + numPtrs = rco->numPtrAnim; + break; + } + if(numPtrs) { + uint j; + fputs(" ptrorder=\"", fp); + for(i=0; iid == RCO_TABLE_TEXT) { + rRCOTextIdxPtr* tip = &(((rRCOTextIdxPtr*)ptrs)[i]); + if(tip->textEntry && tip->index) { + char tmp[30]; + rcoxml_int_to_text(((rRCOTextEntry*)tip->textEntry->extra)->lang, RCOXML_TABLE_TEXT_LANG, tmp); + fprintf(fp, "%s:%s", tmp, rco->labels + tip->index->labelOffset); + } + } else { + if(((rRCOEntry**)ptrs)[i]) + fputs(((rRCOEntry**)ptrs)[i]->labelOffset + rco->labels, fp); + } + } + fputc('\n', fp); + for(j=0; jid) { + case RCO_TABLE_VSMX: + // do nothing + break; + case RCO_TABLE_TEXT: + { + rRCOTextEntry* te = (rRCOTextEntry*)entry->extra; + rcoxml_int_to_text(te->lang, RCOXML_TABLE_TEXT_LANG, tmp); + fprintf(fp, " language=\"%s\"", tmp); + rcoxml_int_to_text(te->format, RCOXML_TABLE_TEXT_FMT, tmp); + fprintf(fp, " format=\"%s\"", tmp); + if(te->numIndexes && !textXmlOut) { // write text labels + uint j; + fputs(" entries=\"", fp); + for(i=0; inumIndexes; i++) { + if(i) fputc(',', fp); + fputc('\n', fp); + for(j=0; jindexes[i].labelOffset != RCO_NULL_PTR) + fputs(rco->labels + te->indexes[i].labelOffset, fp); + } + fputc('\n', fp); + for(j=0; jnumIndexes; i++) { + if(i) fputc(',', fp); + fprintf(fp, "0x%x@0x%x", te->indexes[i].length, te->indexes[i].offset); + } + fputs("\"", fp); + } + } + } + break; + case RCO_TABLE_IMG: case RCO_TABLE_MODEL: + rcoxml_int_to_text(((rRCOImgModelEntry*)entry->extra)->format, (entry->id == RCO_TABLE_IMG ? RCOXML_TABLE_IMG_FMT : RCOXML_TABLE_MODEL_FMT), tmp); + fprintf(fp, " format=\"%s\"", tmp); + rcoxml_int_to_text(((rRCOImgModelEntry*)entry->extra)->compression, RCOXML_TABLE_DATA_COMPRESSION, tmp); + fprintf(fp, " compression=\"%s\"", tmp); + fprintf(fp, " unknownByte=\"%d\"", ((rRCOImgModelEntry*)entry->extra)->unkCompr); + break; + case RCO_TABLE_SOUND: + { + rRCOSoundEntry* se = ((rRCOSoundEntry*)entry->extra); + rcoxml_int_to_text(se->format, RCOXML_TABLE_SOUND_FMT, tmp); + fprintf(fp, " format=\"%s\"", tmp); + if(se->format == RCO_SOUND_VAG && sndDumped != 2) + fprintf(fp, " channels=\"%d\"", se->channels); + + if(se->channels && !sndDumped) { + // write srcParts + fputs(" srcParts=\"", fp); + for(i=0; ichannels; i++) { + if(i) fputc(',', fp); + fprintf(fp, "0x%x@0x%x", se->channelData[i*2], se->channelData[i*2 +1]); + } + fputs("\"", fp); + } + + } + break; + case RCO_TABLE_FONT: { + rRCOFontEntry* rfe = (rRCOFontEntry*)entry->extra; + + fprintf(fp, " unknownShort1=\"0x%x\" unknownShort2=\"0x%x\" unknownInt3=\"0x%x\" unknownInt4=\"0x%x\"", rfe->format, rfe->compression, rfe->unknown, rfe->unknown2); + + } break; + case RCO_TABLE_OBJ: + xmlwrite_entry_extra_object(entry->type, (uint8*)entry->extra, rco, fp); + break; + case RCO_TABLE_ANIM: + xmlwrite_entry_extra_anim(entry->type, (uint8*)entry->extra, rco, fp); + break; + } + } + + if(entry->numSubentries) { + rRCOEntry* rcoNode; + fputs(">\n", fp); + + for(rcoNode=entry->firstChild; rcoNode; rcoNode=rcoNode->next) + xmlwrite_entry(rcoNode, depth+1, rco, fp, textDir, textXmlOut, sndDumped, vsmxConv); + + + for(i=0; i\n", tagName, (isMainTable ? "Table" : "")); + fprintf(fp, "\n", tagName); + + } else { + if(entry->id == RCO_TABLE_OBJ || entry->type == 0 || (entry->type == 1 && (entry->id == RCO_TABLE_ANIM || entry->id == RCO_TABLE_MAIN || entry->id == RCO_TABLE_VSMX))) + fprintf(fp, ">\n", tagName); + else + fputs(" />\n", fp); + } +} + + + +void xmlwrite_entry_extra_object(uint16 type, uint8* info, rRCOFile* rco, FILE* fp) { + + int numEntries = RCO_OBJ_EXTRA_LEN[type]; + int i=0, i2=0; + + if(numEntries < 1) return; // TODO: handle unknown object types? + + for(i=0, i2=0; itype) { + case RCO_REF_EVENT: fputs("event:", fp); break; + case RCO_REF_TEXT: fputs("text:", fp); break; + case RCO_REF_IMG: fputs("image:", fp); break; + case RCO_REF_MODEL: fputs("model:", fp); break; + case RCO_REF_FONT: fputs("font:", fp); break; + case RCO_REF_OBJ2: fputs("object2:", fp); break; + case RCO_REF_ANIM: fputs("anim:", fp); break; + case RCO_REF_OBJ: fputs("object:", fp); break; + case RCO_REF_NONE: fputs("nothing", fp); break; + default: + fprintf(fp, "unknown0x%x:", ref->type); + unkType = TRUE; + } + if(unkType) { + fprintf(fp, "0x%x", ref->rawPtr); + } else if(ref->type == RCO_REF_EVENT) { + //fputs((char*)ref->ptr, fp); + fputs(rco->events + ref->rawPtr, fp); + } else if(ref->type == RCO_REF_TEXT) { + // TODO: check this + // assume first text lang is accurate + if(rco->tblText && rco->tblText->numSubentries) + fputs(((rRCOTextEntry*)(rco->tblText->firstChild->extra))->indexes[ref->rawPtr].labelOffset + rco->labels, fp); + } else if(ref->type != RCO_REF_NONE) { + if(((rRCOEntry*)(ref->ptr))->labelOffset != RCO_NULL_PTR) + fputs(((rRCOEntry*)(ref->ptr))->labelOffset + rco->labels, fp); + else + fputs("", fp); // TODO: handle situations where there isn't a label... + } +} + +void rcoxml_int_to_text(uint in, const RcoTableMap map, char* out) { + uint len=0; + // determine length of map + while(map[len][0]) len++; + + // perhaps think about allowing blank/unknown values in midle of map? + if(in < len) { + strcpy(out, map[in]); + return; + } + sprintf(out, "unknown0x%x", in); +} + + +// custom reserved characters: ',' ':' (kinda) +void rcoxml_fput_escstr(FILE* fp, char* str) { + while(*str) { + switch(*str) { + case '<': fputs("<", fp); break; + case '>': fputs(">", fp); break; + case '"': fputs(""", fp); break; + case '&': fputs("&", fp); break; + case '\n': + //if(allowNL) { + fputc(*str, fp); + break; + //} + default: + if(*str < 32) + fprintf(fp, "&#%d;", *str); + else + fputc(*str, fp); + } + + str++; + } +} -- 2.39.5