SR3 zone file format

1. I know property names are stored as CRCs ("uint32 name crc") to save space (as you told me in this post), but is there any way I can obtain a list of the actual text names for the CRCs in the zone file? If I can get a list of text names, I can build a dictionary to perform reverse lookups and display the friendly text name of each property.
I don't know of a way to go back, we just keep the crc in game usually.

3. In your spec, there's the "uint16 name offset - offset into the property list for the name of the object". In many cases this points to a valid string value in one of the properties, which I display in my "SRReadZone" tool, but in some zone files this is frequently 0 (zero). Does a zero value represent an unnamed object or is there another algorithm I can use to determine the object name (e.g. the first string property in the property list for that object)?
An offset of 0 would be the first string in the string pool, a zero offset. If it isn't that, I can check into the code to see if we have a special way to handle unnamed things.
 
I don't know of a way to go back, we just keep the crc in game usually.
I was suspecting that was the case, but thank you for confirming it. I was hoping there was a pool of strings somewhere, since Minimaul has code to generate a CRC from strings, and then I could generate a reverse lookup table from just the strings. But it makes sense that those strings are not actually stored anywhere and are only used at the source level.
An offset of 0 would be the first string in the string pool, a zero offset. If it isn't that, I can check into the code to see if we have a special way to handle unnamed things.
Well, when it's not zero, this offset points to the first character of a string value in a string property. But since each string property has 8 bytes of header info (type, size, and name crc) that proceeds the actual string value itself, the first property (if it were a string) would still have an offset of 8. So I'm thinking zero just means it has no name, and it's identified purely by the handle. I've observed that, in some cases, the unnamed objects have no string properties at all. But if you have a spare moment, any information you can find would be very helpful. Thanks so much! :)
 
Last edited:
Ok, so the crunched reference geometry section could be of value to people. It contains cobjects, which are like mini-objects to save space and processing time. We don't need to worry about a cobject moving around like a full-blown object so we can not do as much processing on it on a per-frame basis. Here is the format of that section:

Code:
int32 num_meshes
int32 mesh_names_size
char *mesh_names(mesh_names_size bytes) 
c_mesh_base meshes[num_meshes]
c_object fast_objects[num_meshes]

if (zone_header.version >= 14) {
  // load variant information, which is
  c_mesh_variant_data variant_data;
  level_mesh_variant_value *pointer_array[3] // filled and used by the game, not written to file
  level_mesh_material_variant mat_variants[variant_data.m_num_material_variants];
}

Here we step off the deep end. We have havok data which I don't know the size of so I can't really tell you what to do here. I would write this byte by byte, but I think we might have to dig into this more eventually as this is the collision data for the level meshes. If you try to remove a level mesh, you might have to go in here and remove the collision data from the mopp. The part that I know is this:
Code:
uint32 num_mopp_mesh_indicies
uint32 mopp_indicies[num_mopp_mesh_indicies]
uint32 num_mopps;
uint16 static_collision_instance_id

num_mopps havok reads...

structures of interest used above:
Code:
// probably just written as nulls to disk
struct cmesh_base {
  rl_mesh *r_mesh;
  rl_material_map *material_map;
}

struct c_object {
  object_handle obj_handle; // uint64
  cobject *m_render_update_next;
  char *name;
  cmesh m_mesh;

  union {
    c_object    *m_al_cobject_p;  //pointer to the always loaded version of the c_object, if there is one, otherwise it is NULL.
    int     m_al_cobject_i;  //index to it in the al zone (gets converted to a pointer on load)
  };

  constraint_instance_list* m_constraint_instance_list;  // List of constraints that this object is apart of.	

  uint32 flags;

  // zone I'm in.
  zone_cell_id  zone_id;  //	2 bytes

  // This holds the time in milliseconds when a fade starts.
  // Note: Since this is only 16 bits, the longest fade
  //			can be around 65 seconds.
  uint16  fade_time;  // 2 bytes
}

struct c_mesh {
	fl_vector					pos;
	fl_quaternion				quat_orient;
	
	const level_mesh_header		*m_rendering_lmesh; // may be NULL. If it is, this c_mesh does not reference a level mesh (this is the lod 0 for interiors, and lod 1 for fl zone, and lod 2 for AL zone)
	rl_zbias_decal				*first_zbias_decal; // if this instance has any associated zbias decals, this is the beginning of the list of zbias decal instances
	const level_mesh_header		*m_collision_lmesh; // collision version of the level mesh (always medium lod)
	c_mesh_variant_data			*m_variant_data;

	const level_mesh_lod_array	*m_lod_array;
	city_zone					*m_zone_p;
private:	// don't access this directly
	rl_renderable_instance		*m_renderable_instance;
public:

	union {

		uint					m_default_lod_set_index;			// Crunched data.
		uint					m_lod_fade_category_name_crc;		// Crunched data.
		float const*			m_lod_distances;						// Run-time data.
	};

	float						render_alpha;
	rl_occluder					*m_first_occluder;
	float						m_current_opacity_fade_value; //controls opacity overriding of windows and such
	c_mesh_base					*base;						// the base object type.
}

struct c_mesh_variant_data {
	uint32						m_num_material_variants;
	level_mesh_material_variant	*m_material_variants;
	level_mesh_variant_value	**m_variant_values;
}

struct level_mesh_material_variant {
	uint32 m_material_name_checksum;
	uint16 m_variant_0_id;
	uint16 m_variant_1_id;
}
 
I determined that each "c_mesh_base" structure is organized as follows on disk:
Code:
struct c_mesh_base {
  uint32 r_mesh_offset;        // offset of mesh file name in v_file_header.reference_data
  uint32 material_map_offset;  // offset of material map somewhere ???
}
Now I know why everything broke when I changed the length of a reference file name in the v_file_header. That would have caused all the offsets to change, and would have broken all of these offset references. So now it will be possible for me to update these offsets when I write a modified zone file, and if there are no other offset references in the zone file, everything should work!

I'm still not sure what "material_map_offset" references.
 
Last edited:
We have havok data which I don't know the size of so I can't really tell you what to do here. I would write this byte by byte, but I think we might have to dig into this more eventually as this is the collision data for the level meshes. If you try to remove a level mesh, you might have to go in here and remove the collision data from the mopp.
Question:
Each "c_object fast_objects[num_meshes]" entry contains a position and an orientation (part of the "c_mesh" structure), which I have been able to successfully parse. Do you know if Havok MOPP shapes are positioned relative to these coordinates, such that, if I modify the position coordinates of the c_object, the corresponding Havok MOPP will also move? If this is the case, I should be able to move an object along with it's collision data by simply changing the coordinates. Then I don't even have to parse the Havok data. To "remove" a level mesh along with it's collision data, I could simply move it below the map.

Thanks!

UPDATE [2015-09-14]: I answered this question myself by trying it: YES! If I change the coordinates, the collision data does move with the mesh! So with this new zone information, I can now move cobjects on the map! I was able to remove the garage door of Syndicate Tower by moving it below the map, and it's now gone with no red exclamation point!

P.S. Sorry for the multiple posts, but they are each a slightly different purpose. I can combine them if the moderators would prefer me to.
 
Last edited:
This is great news. I can't wait to see what people are able to do with this tool. Do you have intentions of being able to create a nav point object with it? I see that as being useful since you can spawn a mission if you have a nav point.
 
This is great news. I can't wait to see what people are able to do with this tool.
Thanks! Me too! :D
Do you have intentions of being able to create a nav point object with it? I see that as being useful since you can spawn a mission if you have a nav point.
I believe my SRZoneTool could create a nav point since you can add objects and properties to a zone file using that tool already (by editing the XML). The main problem is that I don't know the structure of a nav point object. And, I am assuming that a nav point is represented by a single object in the "objects" section of a zone file. Information I would need to know is:
  1. The object type hash (uint32)
  2. The list of properties required for a nav point, which includes the following for each property:
    1. property type (uint16)
    2. property name CRC (uint32)
    3. value
I assume at least one of the properties would be a type 2 compressed transform which specifies the position of the nav point, and another property may specify the name string or CRC of the nav point name (which may also be the object name). Do I sound like I'm on the right track? Do you think you would be able to dig up this information? :D
Thanks again!
 
There are nav points out there now to copy. We just need to find one I'm sure. I believe they are used for all the mission start nodes...or maybe that is actually a mission start node? As for what you need to do to custom build one, you would need the hash which I would grab from another object. If you guys can find one I can try to step through the creation to see just what is used for what.
 
Back
Top