public static utf_query_result query_utf_nofail(Stream infile, long offset, utf_query query) { utf_query_result result = query_utf(infile, offset, query); ErrorStuff.CHECK_ERROR(result.valid == 0, "didn't find valid @UTF table where one was expected"); ErrorStuff.CHECK_ERROR(query != null && result.found == 0, "key not found"); return result; }
public static byte[] load_utf_string_table(Stream infile, long offset) { utf_query_result result = query_utf_nofail(infile, offset, null); ulong string_table_size = result.data_offset - result.string_table_offset; long string_table_offset = offset + 8 + result.string_table_offset; byte[] string_table = new byte[string_table_size + 1]; Util.get_bytes_seek(string_table_offset, infile, string_table, (long)string_table_size); return string_table; }
public static void analyze_CPK(Stream infile, string base_name, long file_length) { const long CpkHeader_offset = 0x0; byte[] toc_string_table = null; // check header { byte[] buf = new byte[4]; byte[] CPK_signature = Encoding.ASCII.GetBytes("CPK "); Util.get_bytes_seek(CpkHeader_offset, infile, buf, 4); ErrorStuff.CHECK_ERROR(!Util.memcmp(buf, CPK_signature), "CPK signature not found"); } // check CpkHeader { utf_query_result result = UtfTab.query_utf_nofail(infile, CpkHeader_offset + 0x10, null); ErrorStuff.CHECK_ERROR(result.rows != 1, "wrong number of rows in CpkHeader"); } // get TOC offset long toc_offset = (long)UtfTab.query_utf_8byte(infile, CpkHeader_offset + 0x10, 0, "TocOffset"); // get content offset long content_offset = (long)UtfTab.query_utf_8byte(infile, CpkHeader_offset + 0x10, 0, "ContentOffset"); // get file count from CpkHeader long CpkHeader_count = UtfTab.query_utf_4byte(infile, CpkHeader_offset + 0x10, 0, "Files"); // check TOC header { byte[] buf = new byte[4]; byte[] TOC_signature = Encoding.ASCII.GetBytes("TOC "); Util.get_bytes_seek(toc_offset, infile, buf, 4); ErrorStuff.CHECK_ERROR(!Util.memcmp(buf, TOC_signature), "TOC signature not found"); } // get TOC entry count, string table long toc_entries; { utf_query_result result = UtfTab.query_utf_nofail(infile, toc_offset + 0x10, null); toc_entries = result.rows; toc_string_table = UtfTab.load_utf_string_table(infile, toc_offset + 0x10); } // check that counts match ErrorStuff.CHECK_ERROR(toc_entries != CpkHeader_count, "CpkHeader file count and TOC entry count do not match"); // extract files for (int i = 0; i < toc_entries; i++) { // get file name string file_name = UtfTab.query_utf_string(infile, toc_offset + 0x10, i, "FileName", toc_string_table); // get directory name string dir_name = UtfTab.query_utf_string(infile, toc_offset + 0x10, i, "DirName", toc_string_table); // get file size long file_size = UtfTab.query_utf_4byte(infile, toc_offset + 0x10, i, "FileSize"); // get extract size long extract_size = UtfTab.query_utf_4byte(infile, toc_offset + 0x10, i, "ExtractSize"); // get file offset ulong file_offset_raw = UtfTab.query_utf_8byte(infile, toc_offset + 0x10, i, "FileOffset"); if (content_offset < toc_offset) { file_offset_raw += (ulong)content_offset; } else { file_offset_raw += (ulong)toc_offset; } ErrorStuff.CHECK_ERROR(file_offset_raw > (ulong)long.MaxValue, "File offset too large, will be unable to seek"); long file_offset = (long)file_offset_raw; Console.WriteLine("{0}/{1} 0x{2:x} {3}", dir_name, file_name, (ulong)file_offset, file_size); using (Stream outfile = Util.open_file_in_directory(base_name, dir_name, '/', file_name, FileMode.Create, FileAccess.ReadWrite, FileShare.None)) { ErrorStuff.CHECK_ERRNO(outfile == null, "fopen"); if (extract_size > file_size) { long uncompressed_size = CpkUncompress.uncompress(infile, file_offset, file_size, outfile); Console.WriteLine(" uncompressed to {0}", uncompressed_size); ErrorStuff.CHECK_ERROR(uncompressed_size != extract_size, "uncompressed size != ExtractSize"); } else { Util.dump(infile, outfile, file_offset, file_size); } } } toc_string_table = null; }
public static utf_query_result analyze_utf(Stream infile, long offset, int indent, int print, utf_query query) { byte[] buf = new byte[4]; long table_info_table_offset; uint table_info_table_size; uint table_info_schema_offset; uint table_info_rows_offset; uint table_info_string_table_offset; uint table_info_data_offset; uint table_info_table_name; // references index in string_table ushort table_info_columns; ushort table_info_row_width; uint table_info_rows; byte[] string_table = null; utf_column_info[] schema = null; utf_query_result result = new utf_query_result(); result.valid = 0; if (print != 0) { Util.printf_indent(indent); Console.WriteLine("{"); } indent += INDENT_LEVEL; table_info_table_offset = offset; // check header byte[] UTF_signature = Encoding.ASCII.GetBytes("@UTF"); Util.get_bytes_seek(offset, infile, buf, 4); if (!Util.memcmp(buf, UTF_signature)) { if (print != 0) { Util.printf_indent(indent); Console.WriteLine("not a @UTF table at {0:X8}", offset); } indent -= INDENT_LEVEL; if (print != 0) { Util.printf_indent(indent); Console.WriteLine("}"); } string_table = null; schema = null; return result; } // get table size table_info_table_size = Util.get_32_be(infile); table_info_schema_offset = 0x20; table_info_rows_offset = Util.get_32_be(infile); table_info_string_table_offset = Util.get_32_be(infile); table_info_data_offset = Util.get_32_be(infile); uint table_name_string = Util.get_32_be(infile); table_info_columns = Util.get_16_be(infile); table_info_row_width = Util.get_16_be(infile); table_info_rows = Util.get_32_be(infile); // allocate for string table long string_table_size = table_info_data_offset - table_info_string_table_offset; string_table = new byte[string_table_size + 1]; // load schema schema = new utf_column_info[table_info_columns]; for (int i = 0; i < schema.Length; ++i) { schema[i] = new utf_column_info(); } { int i; for (i = 0; i < table_info_columns; i++) { schema[i].type = Util.get_byte(infile); schema[i].column_name = Util.get_32_be(infile); if ((schema[i].type & COLUMN_STORAGE_MASK) == COLUMN_STORAGE_CONSTANT) { schema[i].constant_offset = infile.Position; switch (schema[i].type & COLUMN_TYPE_MASK) { case COLUMN_TYPE_STRING: Util.get_32_be(infile); break; case COLUMN_TYPE_8BYTE: case COLUMN_TYPE_DATA: Util.get_32_be(infile); Util.get_32_be(infile); break; case COLUMN_TYPE_FLOAT: case COLUMN_TYPE_4BYTE2: case COLUMN_TYPE_4BYTE: Util.get_32_be(infile); break; case COLUMN_TYPE_2BYTE2: case COLUMN_TYPE_2BYTE: Util.get_16_be(infile); break; case COLUMN_TYPE_1BYTE2: case COLUMN_TYPE_1BYTE: Util.get_byte(infile); break; default: ErrorStuff.CHECK_ERROR(true, "unknown type for constant"); break; } } } } // read string table Util.get_bytes_seek(table_info_string_table_offset + 8 + offset, infile, string_table, string_table_size); table_info_table_name = table_name_string; // fill in the default stuff result.valid = 1; result.found = 0; result.rows = table_info_rows; result.name_offset = table_name_string; result.string_table_offset = table_info_string_table_offset; result.data_offset = table_info_data_offset; // explore the values if (query != null || print != 0) { int i, j; for (i = 0; i < table_info_rows; i++) { if (print == 0 && query != null && i != query.index) continue; long row_offset = table_info_table_offset + 8 + table_info_rows_offset + i * table_info_row_width; long row_start_offset = row_offset; if (print != 0) { Util.printf_indent(indent); Console.WriteLine("{0}[{1}] = {{", ReadString(string_table, table_info_table_name), i); } indent += INDENT_LEVEL; for (j = 0; j < table_info_columns; j++) { byte type = schema[j].type; long constant_offset = schema[j].constant_offset; int constant = 0; int qthis = (query != null && i == query.index && ReadString(string_table, schema[j].column_name) == query.name) ? 1 : 0; if (print != 0) { Util.printf_indent(indent); Console.Write("{0:x8} {1:x2} {2} = ", row_offset - row_start_offset, type, ReadString(string_table, schema[j].column_name)); } if (qthis != 0) { result.found = 1; result.type = schema[j].type & COLUMN_TYPE_MASK; } switch (schema[j].type & COLUMN_STORAGE_MASK) { case COLUMN_STORAGE_PERROW: break; case COLUMN_STORAGE_CONSTANT: constant = 1; break; case COLUMN_STORAGE_ZERO: if (print != 0) { Console.WriteLine("UNDEFINED"); } if (qthis != 0) { result.value_u64 = 0; } continue; default: ErrorStuff.CHECK_ERROR(true, "unknown storage class"); break; } if (true) { long data_offset; int bytes_read = 0; if (constant != 0) { data_offset = constant_offset; if (print != 0) { Console.Write("constant "); } } else { data_offset = row_offset; } if (qthis != 0) { result.data_position = data_offset; } switch (type & COLUMN_TYPE_MASK) { case COLUMN_TYPE_STRING: { uint string_offset; string_offset = Util.get_32_be_seek(data_offset, infile); bytes_read = 4; if (print != 0) { Console.WriteLine("\"{0}\"", ReadString(string_table, string_offset)); } if (qthis != 0) { result.value_string = string_offset; } } break; case COLUMN_TYPE_DATA: { uint vardata_offset, vardata_size; vardata_offset = Util.get_32_be_seek(data_offset, infile); vardata_size = Util.get_32_be(infile); bytes_read = 8; if (print != 0) { Console.Write("[0x{0:x8}]", vardata_offset); Console.WriteLine(" (size 0x{0:x8})", vardata_size); } if (qthis != 0) { result.value_data = new offset_size_pair() { offset = vardata_offset, size = vardata_size }; } if (vardata_size != 0 && print != 0) { // assume that the data is another table analyze_utf(infile, table_info_table_offset + 8 + table_info_data_offset + vardata_offset, indent, print, null ); } } break; case COLUMN_TYPE_8BYTE: { ulong value = Util.get_64_be_seek(data_offset, infile); if (print != 0) { Console.WriteLine("0x{0:x}", value); } if (qthis != 0) { result.value_u64 = value; } bytes_read = 8; break; } case COLUMN_TYPE_4BYTE2: case COLUMN_TYPE_4BYTE: if ((type & COLUMN_TYPE_MASK) == COLUMN_TYPE_4BYTE2 && print != 0) { Console.Write("type 2 "); } { uint value = Util.get_32_be_seek(data_offset, infile); if (print != 0) { Console.WriteLine("{0}", value); } if (qthis != 0) { result.value_u32 = value; } bytes_read = 4; } break; case COLUMN_TYPE_2BYTE2: case COLUMN_TYPE_2BYTE: if ((type & COLUMN_TYPE_MASK) == COLUMN_TYPE_2BYTE2 && print != 0) { Console.Write("type 2 "); } { ushort value = Util.get_16_be_seek(data_offset, infile); if (print != 0) { Console.WriteLine("{0}", value); } if (qthis != 0) { result.value_u16 = value; } bytes_read = 2; } break; case COLUMN_TYPE_FLOAT: if (true) { uint int_float; int_float = Util.get_32_be_seek(data_offset, infile); if (print != 0) { Console.WriteLine("{0}", Util.reinterpret_to_float(int_float)); } if (qthis != 0) { result.value_u32 = int_float; } } bytes_read = 4; break; case COLUMN_TYPE_1BYTE2: case COLUMN_TYPE_1BYTE: if ((type & COLUMN_TYPE_MASK) == COLUMN_TYPE_1BYTE2 && print != 0) { Console.Write("type 2 "); } { byte value = Util.get_byte_seek(data_offset, infile); if (print != 0) { Console.WriteLine("{0}", value); } if (qthis != 0) { result.value_u8 = value; } bytes_read = 1; } break; default: ErrorStuff.CHECK_ERROR(true, "unknown normal type"); break; } if (constant == 0) { row_offset += bytes_read; } } // useless if end } // column for loop end indent -= INDENT_LEVEL; if (print != 0) { Util.printf_indent(indent); Console.WriteLine("}"); } ErrorStuff.CHECK_ERROR(row_offset - row_start_offset != table_info_row_width, "column widths do now add up to row width"); if (query != null && print == 0 && i >= query.index) break; } // row for loop end } // explore values block end indent -= INDENT_LEVEL; if (print != 0) { Util.printf_indent(indent); Console.WriteLine("}"); } string_table = null; schema = null; return result; }
public static offset_size_pair query_utf_data(Stream infile, long offset, int index, string name) { utf_query_result result = query_utf_key(infile, offset, index, name); ErrorStuff.CHECK_ERROR(result.type != COLUMN_TYPE_DATA, "value is not data"); return result.value_data; }
public static uint query_utf_string(Stream infile, long offset, int index, string name) { utf_query_result result = query_utf_key(infile, offset, index, name); ErrorStuff.CHECK_ERROR(result.type != COLUMN_TYPE_STRING, "value is not a string"); return result.value_string; }
public static ushort query_utf_2byte(Stream infile, long offset, int index, string name) { utf_query_result result = query_utf_key(infile, offset, index, name); ErrorStuff.CHECK_ERROR(result.type != COLUMN_TYPE_2BYTE, "value is not a 2 byte uint"); return result.value_u16; }