Difference between revisions of "Sonic Generations"
From Sonic Retro
(→CPKREDIR) |
|||
Line 8: | Line 8: | ||
==CPKREDIR== | ==CPKREDIR== | ||
− | * [http://www32.zippyshare.com/v/56917710/file.html CPKREDIR] by Community member Korama | + | * [http://www32.zippyshare.com/v/56917710/file.html CPKREDIR] by Community member Korama is a hacked DLL of imagehlp.dll, and due to this file being a [http://blogs.msdn.com/b/larryosterman/archive/2004/07/19/187752.aspx Known DLL], the string that calls this DLL needs to be patched inside the Sonic Generations executable(either with an installer or a simple hex edit). It intercepts CPK file read operations by Sonic Generations and redirects them to external files. This makes it possible to have the game load modified data files without the need for unpacking and repacking those large CPK archives every time. |
− | * Dario FF's Classic Sonic Adventures | + | * CPKREDIR has Dario FF's Classic Sonic Adventures included with it, which allows users to play as Classic Sonic in Green Hill Act 2 and vice versa. You will need [http://www.mediafire.com/?fq333b0k5686tap the files in this zip], because the game crashes if Classic Sonic goes through rainbow rings or trick panels. |
− | * CPKREDIR also comes with the functionality of combining | + | * CPKREDIR also comes with the functionality of combining mods. This opens up many possibilities such as Classic Sonic in Wave Ocean, which is available through [http://www.youtube.com/user/Melpontro/videos Melpontro's YouTube channel]. |
==Generations Archive Editor== | ==Generations Archive Editor== | ||
Line 18: | Line 18: | ||
* Community member Dario FF has created a level editing and importing program called SonicGLVL, available for download [http://code.google.com/p/sonic-glvl/downloads/list here]. This allows users to create and remove objects, as well as import Ogre .scene files into Sonic Generations to use as level geometry. | * Community member Dario FF has created a level editing and importing program called SonicGLVL, available for download [http://code.google.com/p/sonic-glvl/downloads/list here]. This allows users to create and remove objects, as well as import Ogre .scene files into Sonic Generations to use as level geometry. | ||
* SonicGLVL also has another function: model converting. This allows users to convert Ogre .mesh files to Sonic Generations' .model files, ready for importing into Sonic Generations. It can also convert Sonic Generations' .model files into COLLADA's .dae format. The same exporting function is also available for Terrain geometry. | * SonicGLVL also has another function: model converting. This allows users to convert Ogre .mesh files to Sonic Generations' .model files, ready for importing into Sonic Generations. It can also convert Sonic Generations' .model files into COLLADA's .dae format. The same exporting function is also available for Terrain geometry. | ||
− | * SonicGLVL currently cannot view splines, but it does support viewing of | + | * SonicGLVL currently cannot view splines, but it does support viewing of spline knots. It also allows you to snap objects to the nearest spline, which makes placing objects to paths such as 2D sections much easier. |
=File Specifications= | =File Specifications= |
Revision as of 17:04, 13 June 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 Programs
QuickBMS
- QuickBMS is used to extract .cpk files from Sonic Generations' disk and sound folders with this script (right click => save as).
- It is recommended that you have a folder that has clean versions of the files in the .cpk archive so you do not have to unpack every single time you want to revert a file back to its original state.
CPKREDIR
- CPKREDIR by Community member Korama is a hacked DLL of imagehlp.dll, and due to this file being a Known DLL, the string that calls this DLL needs to be patched inside the Sonic Generations executable(either with an installer or a simple hex edit). It intercepts CPK file read operations by Sonic Generations and redirects them to external files. This makes it possible to have the game load modified data files without the need for unpacking and repacking those large CPK archives every time.
- CPKREDIR has Dario FF's Classic Sonic Adventures included with it, which allows users to play as Classic Sonic in Green Hill Act 2 and vice versa. You will need the files in this zip, because the game crashes if Classic Sonic goes through rainbow rings or trick panels.
- CPKREDIR also comes with the functionality of combining mods. This opens up many possibilities such as Classic Sonic in Wave Ocean, which is available through Melpontro's YouTube channel.
Generations Archive Editor
- Community member MainMemory has adapted his SADXsndSharp tool to work with Generations' .ar.** files and its stage .pfd files, available here. It provides a GUI interface for adding, removing and extracting files; and and a command-line interface for extracting and packing files.
SonicGLVL
- Community member Dario FF has created a level editing and importing program called SonicGLVL, available for download here. This allows users to create and remove objects, as well as import Ogre .scene files into Sonic Generations to use as level geometry.
- SonicGLVL also has another function: model converting. This allows users to convert Ogre .mesh files to Sonic Generations' .model files, ready for importing into Sonic Generations. It can also convert Sonic Generations' .model files into COLLADA's .dae format. The same exporting function is also available for Terrain geometry.
- SonicGLVL currently cannot view splines, but it does support viewing of spline knots. It also allows you to snap objects to the nearest spline, which makes placing objects to paths such as 2D sections much easier.
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 offsets in the table to reach a sub_alpha_mesh structure uint32_be sub_alpha_mesh_offset[alpha_mesh_total]; struct sub_alpha_mesh { uint32_be id_total; uint32_be id_offset; uint32_be face_count; uint32_be face_data_start_offset; uint32_be id_data[id_total]; // 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 extracted_folder_size_total; // Calculate this by adding the size of each file // in the uncompressed tg folder uint32_be spheres_total; // Matches up to the amount 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; }
light-field.lft
// Very WIP 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 { // XYZ coordinates of the world AABB float32_be aabb_x1; float32_be aabb_x2; float32_be aabb_y1; float32_be aabb_y2; float32_be aabb_z1; float32_be aabb_z2; uint32_be id_block_total; uint32_be id_block_table_offset; uint32_be color_block_total; uint32_be color_block_table_offset; uint32_be index_total; uint32_be index_table_offset; } struct id_block { // This value goes from 0 to 3 in most files tested // Given that GIA Levels are on that range, it might be related uint32_be type; // Value seems to be in the [0..index_total-8] range for most tested files // So it's most likely an index to those indices? uint32_be value; } id_block id_block_table[id_block_total]; // The purpose of the color block table is to build a palette to use // for light field cubes. Saves up on space by reusing the same blocks struct color_block { // Map [0, 255] to [0.0, 1.0] to get each color(R, G, B) channel. // Corresponds to the Box borders color value for light fields byte rgb[8][3]; // Either FF or 00. byte flag; } color_block id_block_table[color_block_total]; // Points to indices of the palette table uint32_be index_table[index_total]; // 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]
.gi-texture-group-info
// gi-texture.gi-texture-group-info files struct header { uint32_be file_size; uint32_be root_node_type; // Most of the time 2 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 name_total; // Matches instance count uint32_be name_offset_table_offset; uint32_be sphere_offset_table_offset; uint32_be gia_total; uint32_be gia_offset_table_offset; uint32_be low_gia_total; uint32_be low_gia_index_table_offset; } // Points to name structures uint32_be name_offset_table[name_total]; struct name { // Null terminated string // Add padding to reach a 0x4 address // The name is the same as the instance referenced char instance_name[]; } // Points to instance sphere structures uint32_be sphere_offset_table[name_total]; struct instance_sphere { float32_be position[3]; float32_be radius; } // Points to GIA Info structures uint32_be gia_info_offset_table[gia_total]; struct gia_info { // Quality Level of the GIA. Range: 0-3. uint32_be gia_level; uint32_be instance_count; uint32_be index_table_offset; uint32_be bounding_sphere_offset; // Total size of the files in the GIA folder of this file uint32_be folder_size; // If the quality level of the GIA is 0, these indexes // directly reference the above tables to retrieve the names/bounding spheres // If the level is above 0, it will use the indexes of the GIA files that have the instances in one level before // For example, a GIA file of Level 1 will reference many files of GIA level 0 to define its own instances // Searching recursively should retrieve all the instances you need for calculating the bounding sphere uint32_be index_table[instance_count]; // General bounding sphere, it's the result from all the bounding spheres of the instances // Refer to the above comment for an explanation of how to retrieve them float32_be bounding_sphere_center[3]; float32_be bounding_sphere_radius; } // Indexes of the GIA files which are in Stage.pfd instead of Stage-add.pfd. These are the low quality GIA files. uint32_be low_gia_index_table[low_gia_total]; // 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]
atlasinfo
struct atlasinfo_header { char padding=0; unsigned int8 textures_ammount; // How many DDS files it needs to load char add_padding=0; }; // Read this for textures_ammount times after reading sub_texture_info struct texture_header { unsigned int8 texture_name_size; // How many chars the texture name has char texture_name[texture_name_size]; // DDS texture name (without the .dds extension), non-null terminated unsigned int8 sub_texture_size; // How many subtextures are in this file char padding=0; }; // Read the next structure structure for sub_texture_size times struct sub_texture_info { unsigned int8 subtexture_name_size; // How many chars the subtexture name has char subtexture_name[subtexture_name_size]; // non-null terminated string, internal name of the texture this gia data belongs to in the terrain(or perhaps the object) // Multipliers for the UV coords unsigned int8 u_mult; unsigned int8 v_mult; // Absolute value of the UV coordinate this texture is // Should be multiplied by u_mult, and v_mult unsigned int8 u_scale; unsigned int8 v_scale; };
visibility-tree.vt
// Visibility Tree file struct header { uint32_be file_size; uint32_be root_node_type; // Most of the time 0 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 offset_to_id_definition_loader_header; uint32_be vblock_total; uint32_be offset_to_vblock_offset_table; uint32_be offset_to_sub_id_offset_table; // Uses same aabb, but only has one ID definition object. uint32_be offset_to_id_definition_loader_footer; } // Only one is used id_definition_loader id_definition_loader_header; struct id_definition_loader { uint32_be offset_aabb; uint32_be id_definition_total; uint32_be offset_to_id_definition_table; uint32_be unknown_total=0; // Read as X1 X2 Y1 Y2 Z1 Z2 float32_be aabb[6]; } id_definition id_definition_table[id_definition_total]; struct id_definition { // Type 0 1 and 2 unknown, but value points to an index on the same table // If no node references its own ID, it might mean it's the root of the linked list // If the type is 3, value will either point to a vblock or be FF FF FF FF. uint32_be type; uint32_be value; } uint32_be vblock_offset_table[vblock_total]; struct vblock { uint32_be bit_total; uint32_be bit_table_offset; // Presumably binary flags bit binary_block[bit_total]; // Add the necessary bits to align the structure to a 0x4 address bit padding[]=0; } uint32_be subid_offset_table[vblock_total]; struct subid { uint32_be id_total; uint32_be id_table_offset; // Points to ids uint32_be ids[id_total]; } // Again, just use one for the footer id_definition_loader id_definition_loader_footer; // 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]