Sonic Generations
From Sonic Retro
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 community members DarioFF and darkspines35. 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]
.terrainblock
// 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 unknown_total_1; uint32_be stage_block_id_1; uint32_be stage_block_id_2; uint32_be offset_to_bounding_sphere_center; float32_be bounding_sphere_center[3]; // XYZ coordinates 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_quaternion; 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 unknown_quaternion[4]; } 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_bounding_sphere_center; uint32_be offset_to_center_position; uint32_be unknown_offset; 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_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)]