Difference between revisions of "Sonic Generations"
From Sonic Retro
(→terrain-block.tbst) |
(→.terrain) |
||
Line 410: | Line 410: | ||
uint32_be offset_to_center_position; | uint32_be offset_to_center_position; | ||
uint32_be offset_to_filename; | uint32_be offset_to_filename; | ||
− | uint32_be | + | uint32_be unknown_value; |
uint32_be spheres_total; // Matches up to the ammount of unique instances, as in, the ones that don't start | uint32_be spheres_total; // Matches up to the ammount of unique instances, as in, the ones that don't start | ||
// with the same prefix name | // with the same prefix name | ||
uint32_be offset_to_sphere_offset_table; | uint32_be offset_to_sphere_offset_table; | ||
− | int32_be | + | int32_be unknown_ff_value=-1; |
float32_be center_position_x; | float32_be center_position_x; |
Revision as of 13:40, 19 March 2012
SCHG: Sonic Generations |
---|
Main Article |
File Index |
BB |
How-To |
Import Levels |
This is the Sonic Community Hacking Guide for the 2011 US/European PC version of Sonic Generations
Contents
Hacking Tools
QuickBMS
- QuickBMS is used to extract files from Sonic Generations.
- Primarily it is used to unpack .CPK files with this script (right click => save as).
- However, it can also be used to unpack .ar.** files with this script. For editing purposes, it is best not to use QuickBMS to hack .ar.** files.
Cri Packed File Maker
- Crifilesystem has a tool used specifically for hacking Sonic Generations; CriPackedFileMaker.exe
- While QuickBMS is the best tool for extracting .CPK files, CRI Packed File Maker is the best tool for repacking (or making, rather) .CPK files.
- Do not pack .cpk files with compression; it is unnecessary and takes extra time out of your life.
- Data alignment is usually set to 64. Presumably this is unnecessary, but for precautionary purposes, it's best to just leave data alignment at 64.
Generations Archive Editor
- Community member MainMemory has adapted his tool SADXsndSharp to work with Generations' .ar.** files, available here.
- Generally this can be considered an easier way to edit .ar.** files, since individual files can be brought out for editing and put back in without having to make new .ar.** files.
File Specifications
Over the past few months, several files have had their formats cracked thanks to various members in and outside of the Sonic Retro community. Here are some of the cracked format specifications:
.instanceinfo
// instanceinfo files // All offsets not specified as absolute needs to be added 0x18(dec 24) to reach the right referenced address struct header { uint32_be file_size; uint32_be root_node_type; (likely 5) uint32_be sub_offset_to_offset_table; uint32_be offset_to_root; (absolute) uint32_be offset_to_offset_table; (absolute) uint32_be offset_to_eof; (absolute) } struct root_node { uint32_be offset_model_filename; uint32_be offset_matrix; uint32_be offset_filename; uint32_be instance_total; // Unknown what's this total used for? uint32_be offset_filename_end; } struct instance_matrix { float32_be m[16]; // 4x4 matrix, has position, scale, and rotation info } struct filename { char name[]; // Use as many characters are needed, must be null terminated, // add extra padding to align the end to a 0x4 address } // Write after filename // Code names for mesh loaders: alpha, beta, gamma struct face_loader { uint32_be loader_offset; // offset to this struct + 4, points to the next value uint32_be alpha_mesh_loader_total; uint32_be alpha_mesh_loader_offset; // If loader total is 0, use the biggest address out of the 3 offsets uint32_be beta_mesh_loader_total; uint32_be beta_mesh_loader_offset; // Same case as with alpha uint32_be gamma_mesh_loader_total; uint32_be gamma_mesh_loader_offset; // Same case as with alpha } @alpha_mesh_loader_offset // Follow each of these offsets to reach yet another loader uint32_be alpha_mesh_offset[alpha_mesh_loader_total]; struct alpha_mesh_loader { uint32_be alpha_mesh_total; uint32_be alpha_mesh_offset; // Leads to sub_alpha_mesh_offset } // Follow these to reach a sub_loader_alpha_mesh_offset uint32_be sub_alpha_mesh_offset[alpha_mesh_total]; // Offset to sub_alpha_mesh uint32_be sub_loader_alpha_mesh_offset; struct sub_alpha_mesh { uint32_be unknown_total; uint32_be unknown_offset; uint32_be face_count; uint32_be face_data_start_offset; uint32_be unknown_value; // Most of the time 4. unknown_offset points to it // Read new faces as triangle strips, each 65535 value means a new strip starts uint16_be face_data[face_count]; uint16_be optional_padding; // Add if face_count is an odd value so the structure aligns to 0x4 } // * Repeat the above structures for beta and gamma mesh loaders * struct final_offset_table { uint32_be offset_count; uint32_be offset_table[offset_count]; uint32_be padding=0; // Marks end of file } Offset table contents: [Addresses to any declared offset in any structure whatsoever in order of writing]
.material
// Material files // Any offsets not declared as absolute must be added 0x18(dec 24) to reach the proper referenced address struct header { uint32_be file_size; uint32_be root_node_type; // Most of the time 3 for Generations // 1 for Unleashed uint32_be offset_final_table; uint32_be root_node_offset; // Always 24 uint32_be offset_final_table_abs; // (Absolute) uint32_be padding=0; } struct root_node { uint32_be offset_type_string; uint32_be offset_type_string_2; uint32_be texset_offset; uint32_be texture_offset; uint32_be unknown_value; // Probably 2 short ints, or 1 byte multipler/scale uv components uint8 total_material_definitions; uint8 unknown_ints[2]={0,0}; uint8 total_texture_info; // Used for texset and texture tables uint32_be offset_to_material_offset_table; uint32_be unknown_value_2=0; uint32_be unknown_value_3=0; char type_string_1[]; // Null terminated, no padding needed char type_string_2[]; // Null terminated, add padding to reach 0x4 address at the end uint32_be material_definition_offset_table[total_material_definitions]; } // Write this structure total_material_definitions times, fill with 0s and null strings // the marked values if not used struct material_definition { uint16_be map_size_width; uint16_be map_size_height; uint32_be offset_mat_name; uint32_be offset_rgba; char mat_name[]; // Null terminated, add padding to reach 0x4 address at the end // Write 4 null bytes in case there's no name float32_be rgba_values[4]; // 4th value is 0 most of the time // Fill with 0s if not used } // Read at texset_offset, only 1 structure uint32_be texset_name_offset_table[total_texture_info]; // Read on each offset a null terminated string // last string adds padding to reach a 0x4 address char texset_name[total_texture_info][]; uint32_be texture_offset_table[total_texture_info]; struct texture_info { uint32_be offset_tex_name; uint32_be unknown_total=0; uint32_be offset_tex_type_name; char tex_name[]; // Null terminated, no padding needed char tex_type_name[]; // Null terminated, add padding to reach a 0x4 address at the end } // Final offset table struct final_offset_table { uint32_be offset_total; uint32_be offset_table[offset_total]; } // Since the first EOF offset in the header is 0, there's no need to write an extra 0 value at the end // of the offset table Offset table contents: [Addresses to any declared offset in any structure whatsoever in order of writing the file(non-absolute)]
.pfi
struct header { uint32_be file_size; uint32_be root_node_type; // most cases for pfis is 0 uint32_be struct_table_offset; // Add 0x18(dec 24) to reach it uint32_be root_node_offset; // always 0x18(dec 24) uint32_be struct_table_offset_2; // Same as before but already has an additional 0x18 uint32_be end_of_file_offset; // file_size - 4 } // Write the root node after the main header. Only 1 root node is needed struct root_node { uint32_be ar_package_total; // Ammount of AR files the PFD has. uint32_be ar_offset_table_address; // Add 0x18(dec 24) to it to reach the table } struct ar_offset_table { // For writing this one, just fill it with blank if you want // then fill it after you're done with the rest // Each address is relative to the start of the file plus 0x18(as with any other pointers) // The address points to each of the package info structs below uint32_be ar_package_offset[ar_package_total]; } // Write the following structure ar_package_total times // And fill the offsets of the previous table with them struct ar_package_info { uint32_be package_name_offset; // Pointer to the name of the AR file uint32_be package_pfd_offset; // Absolute offset of the ar file inside the PFD(not needed to add 0x18) uint32_be package_size; // Absolute size of the AR file, in bytes char name[]; // Size is as long as the ar filename, adds padding if it doesn't align to 0x4 at the end. } // There's another extra offsets table of structures written at the end // After writing all of the above, you have to write the following struct final_offset_table { uint32_be offset_total; // Considering PFIs, this would be ar_package_total*2 + 1 uint32_be offset_table[offset_total]; uint32_be padding; // Just an extra 0 to fill the end of file } // This final offset table is filled with the following // All of them use the pointer math I mentioned before(add 0x18(dec 24) to all of them) Offset table contents: [Addresses to any declared offset in any structure whatsoever in order of writing]
terrain-block.tbst
// Terrain Block files struct header { uint32_be file_size; uint32_be root_node_type; // Most of the time 1 uint32_be offset_final_table; uint32_be root_node_offset; // Always 24 uint32_be offset_final_table_abs; // (Absolute) uint32_be offset_eof; (Absolute) } struct root_node { uint32_be instance_total; uint32_be instance_table_offset; uint32_be unknown_total; // Equals instance_total - 1? } // Offset to each Instanceinfo structure uint32_be instance_offset_table[instance_total]; struct instance_info { uint32_be block_type; // If it's 1, follow the below struct // If it's 0, it's still in research uint32_be terrain_instance_index; uint32_be terrain_instance_sub_index; uint32_be offset_to_bounding_sphere_center; float32_be bounding_sphere_center[3]; // XYZ coordinates of the bounding sphere // You can read it on the terrain file using the // index and sub index mentioned above float32_be bounding_sphere_radius; } // Final offset table struct final_offset_table { uint32_be offset_total; uint32_be offset_table[offset_total]; uint32_be extra_padding_eof=0; } Offset table contents: [Addresses to any declared offset in any structure whatsoever in order of writing]
.terrain-group
// Terrain Group file struct header { uint32_be file_size; uint32_be root_node_type; // Most of the time 1 uint32_be offset_final_table; uint32_be root_node_offset; // Always 24 uint32_be offset_final_table_abs; // (Absolute) uint32_be offset_eof; (Absolute) } struct root_node { uint32_be instanceinfo_total; uint32_be instanceinfo_offset_table; // Address to a table uint32_be terrainmodel_total; uint32_be terrainmodel_offset_table; // Address to a table } uint32_be instanceinfo_offsets[instanceinfo_total]; struct instance_info { uint32_be filename_total; uint32_be offset_to_filename_offset; uint32_be offset_to_bounding_sphere; uint32_be offset_to_filename[filename_total]; char filename[filename_total][]; // Null-terminated, add padding to reach a 0x4 multiple at the end of each string float32_be bounding_sphere_center[3]; // XYZ Coordinates float32_be bounding_sphere_radius; } uint32_be terrainmodel_offsets[terrainmodel_total]; struct terrainmodel_info { char filename[]; // Null-terminated, add padding to reach a 0x4 multiple } // All filenames don't have the extension // Final offset table struct final_offset_table { uint32_be offset_total; uint32_be offset_table[offset_total]; uint32_be extra_padding_eof=0; } Offset table contents: [Addresses to any declared offset in any structure whatsoever in order of writing] ========== EOF
.terrain
// Terrain.terrain files // Any offsets not declared as absolute must be added 0x18(dec 24) to reach the proper referenced address struct header { uint32_be file_size; uint32_be root_node_type; // Most of the time 1 uint32_be offset_final_table; uint32_be root_node_offset; // Always 24 uint32_be offset_final_table_abs; // (Absolute) uint32_be offset_eof; (Absolute) } struct root_node { uint32_be group_total; uint32_be group_table_offset; } // Offset to each group_info structure uint32_be group_offset_table[group_total]; struct group_info { uint32_be offset_to_center_position; uint32_be offset_to_filename; uint32_be unknown_value; uint32_be spheres_total; // Matches up to the ammount of unique instances, as in, the ones that don't start // with the same prefix name uint32_be offset_to_sphere_offset_table; int32_be unknown_ff_value=-1; float32_be center_position_x; float32_be center_position_y; float32_be center_position_z; float32_be center_position_radius; char filename[]; // Null terminated, add padding to reach a 0x4 address at the end // Offset to each group_instance_info_sphere structure uint32_be sphere_offset_table[spheres_total]; } // Write this structure after it spheres_total times struct group_instance_info_sphere { float32_be center_x; float32_be center_y; float32_be center_z; float32_be center_radius; } // Final offset table struct final_offset_table { uint32_be offset_total; uint32_be offset_table[offset_total]; uint32_be extra_padding_eof=0; } Offset table contents: [Addresses to any declared offset in any structure whatsoever in order of writing the file(non-absolute)]
.light-list
// Any offsets not declared as absolute must be added 0x18(dec 24) to reach the proper referenced address struct header { uint32_be file_size; uint32_be root_node_type; // Most of the time 1 uint32_be offset_final_table; uint32_be root_node_offset; // Always 24 uint32_be offset_final_table_abs; // (Absolute) uint32_be offset_eof; (Absolute) } struct root_node { uint32_be light_total; uint32_be light_table_offset; } // Address to each name written uint32_be offset_light_table[light_total]; char light_name[light_total][]; // Null terminated strings big as needed // Add padding to each string to reach a 0x4 multiple address // Final offset table struct final_offset_table { uint32_be name_stuff; uint32_be offset_table[offset_total]; uint32_be extra_padding_eof=0; } Offset table contents: [Addresses to any declared offset in any structure whatsoever in order of writing the file(non-absolute)]
.light
struct header { uint32_be file_size; uint32_be root_node_type; // Most of the time 1 uint32_be offset_final_table; uint32_be root_node_offset; // Always 24 uint32_be offset_final_table_abs; // (Absolute) uint32_be padding=0; } struct root_node { // Read one of the following structures depending on light_type uint32_be light_type; } // light_type = 0 - Directional Light // light_type = 1 - Omni Light struct directional_light { float32_be position[3]; // Direction in the case of directional lights float32_be rgb[3]; } struct omni_light { float32_be position[3]; // Position of omni/point lights float32_be rgb[3]; uint32_be unknown_total_1; uint32_be unknown_total_2; uint32_be unknown_total_3; float32_be unknown_float_1; // It's assumed these two are distance and falloff distance float32_be unknown_float_2; } // Final offset table struct final_offset_table { // No offsets in this structure uint32_be total=0; }