/* If a node exists in the flowgraph, return it - otherwise create it, and return it */ public CathodeNodeEntity GetNodeByID(byte[] id) { foreach (CathodeNodeEntity node in nodes) { if (node.nodeID.SequenceEqual(id)) { return(node); } } CathodeNodeEntity newNode = new CathodeNodeEntity(); newNode.nodeID = id; nodes.Add(newNode); return(newNode); }
/* Read all flowgraphs from the PAK */ private void ReadFlowgraphs(BinaryReader reader) { int scriptStart = parameter_offsets[parameter_offsets.Length - 1] + 8; //Relies on the last param always being 4 in length for (int i = 0; i < flowgraph_count; i++) { CathodeFlowgraph flowgraph = new CathodeFlowgraph(); //Game doesn't parse the script name, so there's no real nice way of grabbing it!! reader.BaseStream.Position = scriptStart; flowgraph.globalID = reader.ReadBytes(4); string name = ""; while (true) { byte thisByte = reader.ReadByte(); if (thisByte == 0x00) { break; } name += (char)thisByte; } flowgraph.name = name; scriptStart = flowgraph_offsets[i] + 116; //End of crappy namegrab reader.BaseStream.Position = flowgraph_offsets[i]; reader.BaseStream.Position += 4; //Skip 0x00,0x00,0x00,0x00 //Read the offsets and counts List <OffsetPair> offsetPairs = new List <OffsetPair>(); for (int x = 0; x < 13; x++) { if (x == 0) { flowgraph.uniqueID = reader.ReadBytes(4); } if (x == 1) { flowgraph.nodeID = reader.ReadBytes(4); } OffsetPair newPair = new OffsetPair(); newPair.GlobalOffset = reader.ReadInt32() * 4; newPair.EntryCount = reader.ReadInt32(); offsetPairs.Add(newPair); } //Pull data from those offsets for (int x = 0; x < offsetPairs.Count; x++) { reader.BaseStream.Position = offsetPairs[x].GlobalOffset; for (int y = 0; y < offsetPairs[x].EntryCount; y++) { switch ((CathodeScriptBlocks)x) { case CathodeScriptBlocks.DEFINE_NODE_LINKS: { reader.BaseStream.Position = offsetPairs[x].GlobalOffset + (y * 12); byte[] parentID = reader.ReadBytes(4); int OffsetToFindParams = reader.ReadInt32() * 4; int NumberOfParams = reader.ReadInt32(); for (int z = 0; z < NumberOfParams; z++) { reader.BaseStream.Position = OffsetToFindParams + (z * 16); CathodeNodeLink newLink = new CathodeNodeLink(); newLink.connectionID = reader.ReadBytes(4); newLink.parentParamID = reader.ReadBytes(4); newLink.childParamID = reader.ReadBytes(4); newLink.childID = reader.ReadBytes(4); newLink.parentID = parentID; flowgraph.links.Add(newLink); } break; } case CathodeScriptBlocks.DEFINE_NODE_PARAMETERS: { reader.BaseStream.Position = offsetPairs[x].GlobalOffset + (y * 12); CathodeNodeEntity thisNode = flowgraph.GetNodeByID(reader.ReadBytes(4)); int OffsetToFindParams = reader.ReadInt32() * 4; int NumberOfParams = reader.ReadInt32(); for (int z = 0; z < NumberOfParams; z++) { reader.BaseStream.Position = OffsetToFindParams + (z * 8); CathodeParameterReference thisParamRef = new CathodeParameterReference(); thisParamRef.paramID = reader.ReadBytes(4); thisParamRef.editOffset = (int)reader.BaseStream.Position; thisParamRef.offset = reader.ReadInt32() * 4; thisNode.nodeParameterReferences.Add(thisParamRef); } break; } //NOT PARSING: This appears to define links to EnvironmentModelReference nodes through flowgraph ref nodes case CathodeScriptBlocks.DEFINE_ENV_MODEL_REF_LINKS: { break; reader.BaseStream.Position = offsetPairs[x].GlobalOffset + (y * 12); //This block defines some kind of ID, then an offset and a count of data at that offset byte[] thisID = reader.ReadBytes(4); int OffsetToFindParams = reader.ReadInt32() * 4; int NumberOfParams = reader.ReadInt32(); //We jump to that offset, and read the x-ref listing reader.BaseStream.Position = OffsetToFindParams; List <byte[]> content = new List <byte[]>(); for (int z = 0; z < NumberOfParams; z++) { content.Add(reader.ReadBytes(4)); //cross-refs: node ids (of flowgraph refs), then the EnvironmentModelReference node, then 0x00 (x4) } break; } //NOT PARSING: This block is only 8 bytes - first 4 is an ID for the block above, second is an ID not used anywhere else - potentially four 1-byte numbers? case CathodeScriptBlocks.DEFINE_ENV_MODEL_REF_LINKS_EXTRA: { break; reader.BaseStream.Position = offsetPairs[x].GlobalOffset + (y * 8); byte[] linkID = reader.ReadBytes(4); //ID from DEFINE_ENV_MODEL_REF_LINKS (is this actually a node id?) byte[] unk2 = reader.ReadBytes(4); //Dunno what this is, only appears to ever be used once break; } case CathodeScriptBlocks.DEFINE_NODE_DATATYPES: { reader.BaseStream.Position = offsetPairs[x].GlobalOffset + (y * 12); CathodeNodeEntity thisNode = flowgraph.GetNodeByID(reader.ReadBytes(4)); thisNode.dataType = GetDataType(reader.ReadBytes(4)); thisNode.dataTypeParam = reader.ReadBytes(4); break; } //NOT PARSING: This block is another x-ref list, potentially related to mission critical things (doors, maybe?) case CathodeScriptBlocks.DEFINE_LINKED_NODES: { break; //This block appears to be populated mainly in mission flowgraphs, rather than other ones like archetypes or model placement //It defines a node from another flowgraph, which is referenced by executation hierarchy reader.BaseStream.Position = offsetPairs[x].GlobalOffset + (y * 20); byte[] unk1 = reader.ReadBytes(4); //flowgraph id? int OffsetToFindParams = reader.ReadInt32() * 4; //offset int NumberOfParams = reader.ReadInt32(); //count int resetPos = (int)reader.BaseStream.Position; reader.BaseStream.Position = OffsetToFindParams; for (int p = 0; p < NumberOfParams; p++) { byte[] unk69 = reader.ReadBytes(4); //cross-refs: node ids (of flowgraph refs), then the node, then 0x00 (x4) } reader.BaseStream.Position = resetPos; byte[] unk4 = reader.ReadBytes(4); //flowgraph id again byte[] unk5 = reader.ReadBytes(4); //another id for something else break; } case CathodeScriptBlocks.DEFINE_NODE_NODETYPES: { CathodeNodeEntity thisNode = flowgraph.GetNodeByID(reader.ReadBytes(4)); thisNode.nodeType = reader.ReadBytes(4); break; } //PARSING: I'm currently unsure on a lot of this, as the types vary (see entryType) case CathodeScriptBlocks.DEFINE_RENDERABLE_ELEMENTS: { reader.BaseStream.Position = offsetPairs[x].GlobalOffset + (y * 40); //TODO: these values change by entry type - need to work out what they're for before allowing editing CathodeResourceReference resource_ref = new CathodeResourceReference(); resource_ref.editOffset = (int)reader.BaseStream.Position; resource_ref.resourceRefID = reader.ReadBytes(4); //renderable element ID (also used in one of the param blocks for something) reader.BaseStream.Position += 4; //unk (always 0x00 x4?) resource_ref.positionOffset = new Vec3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); //position offset reader.BaseStream.Position += 4; //unk (always 0x00 x4?) resource_ref.resourceID = reader.ReadBytes(4); //resource id resource_ref.entryType = GetResourceEntryType(reader.ReadBytes(4)); //entry type switch (resource_ref.entryType) { case CathodeResourceReferenceType.RENDERABLE_INSTANCE: resource_ref.entryIndexREDS = reader.ReadInt32(); //REDS.BIN entry index resource_ref.entryCountREDS = reader.ReadInt32(); //REDS.BIN entry count break; case CathodeResourceReferenceType.COLLISION_MAPPING: resource_ref.unknownInteger = reader.ReadInt32(); //unknown integer resource_ref.nodeID = reader.ReadBytes(4); //ID which maps to the node using the resource (?) - check GetFriendlyName break; case CathodeResourceReferenceType.EXCLUSIVE_MASTER_STATE_RESOURCE: case CathodeResourceReferenceType.NAV_MESH_BARRIER_RESOURCE: case CathodeResourceReferenceType.TRAVERSAL_SEGMENT: reader.BaseStream.Position += 8; //just two -1 32-bit integers for some reason break; case CathodeResourceReferenceType.ANIMATED_MODEL: case CathodeResourceReferenceType.DYNAMIC_PHYSICS_SYSTEM: resource_ref.unknownInteger = reader.ReadInt32(); //unknown integer reader.BaseStream.Position += 4; break; } flowgraph.resources.Add(resource_ref); break; } //NOT PARSING: This is very similar in format to DEFINE_ENV_MODEL_REF_LINKS with the cross-references case CathodeScriptBlocks.UNKNOWN_8: { break; //This block is only four bytes - which translates to a pointer to another location... so read that reader.BaseStream.Position = offsetPairs[x].GlobalOffset + (y * 4); int offsetPos = reader.ReadInt32() * 4; //Jump to the pointer location - this defines a node ID and another offset with count reader.BaseStream.Position = offsetPos; CathodeNodeEntity thisNode = flowgraph.GetNodeByID(reader.ReadBytes(4)); //These always seem to be animation related nodes int OffsetToFindParams = reader.ReadInt32() * 4; int NumberOfParams = reader.ReadInt32(); //Now we jump to THAT pointer's location, and iterate by count. The blocks here are of length 32. for (int z = 0; z < NumberOfParams; z++) { reader.BaseStream.Position = OffsetToFindParams + (z * 32); //First 4: unknown id byte[] unk1 = reader.ReadBytes(4); //Second 4: datatype byte[] datatype1 = reader.ReadBytes(4); CathodeDataType datatype1_converted = GetDataType(datatype1); //Third 4: unknown id byte[] unk2 = reader.ReadBytes(4); //Fourth 4: parameter id byte[] unk3 = reader.ReadBytes(4); //string unk3_paramname_string = NodeDB.GetName(unk3); //Fifth 4: datatype byte[] datatype2 = reader.ReadBytes(4); CathodeDataType datatype2_converted = GetDataType(datatype2); //Sixth 4: unknown ID byte[] unk4 = reader.ReadBytes(4); //Now we get ANOTHER offset. This is a pointer to a location and a count of IDs at that location. int offset1 = reader.ReadInt32() * 4; int count1 = reader.ReadInt32(); for (int p = 0; p < count1; p++) { reader.BaseStream.Position = offset1 + (p * 4); byte[] unk69 = reader.ReadBytes(4); //cross-refs: node ids (of flowgraph refs), then the node, then 0x00 (x4) } } break; } //NOT PARSING: This is very similar in format to DEFINE_ENV_MODEL_REF_LINKS with the cross-references case CathodeScriptBlocks.DEFINE_ZONE_CONTENT: { break; reader.BaseStream.Position = offsetPairs[x].GlobalOffset + (y * 4); int offsetPos = reader.ReadInt32() * 4; reader.BaseStream.Position = offsetPos; CathodeNodeEntity thisNode = flowgraph.GetNodeByID(reader.ReadBytes(4)); //string test0 = NodeDB.GetFriendlyName(thisNode.nodeID); int OffsetToFindParams = reader.ReadInt32() * 4; int NumberOfParams = reader.ReadInt32(); int goTo = OffsetToFindParams; for (int m = 0; m < NumberOfParams; m++) { reader.BaseStream.Position = goTo; byte[] firstFour = reader.ReadBytes(4); int offset1 = -1; if (m > 0) //for some reason only the first entry is 8 bytes long, the rest are 12, with four bytes of something at the start (some kind of ID, zone id maybe?) { offset1 = reader.ReadInt32() * 4; } else { offset1 = BitConverter.ToInt32(firstFour, 0) * 4; } int count1 = reader.ReadInt32(); goTo = (int)reader.BaseStream.Position; reader.BaseStream.Position = offset1; for (int p = 0; p < count1; p++) { byte[] unk55 = reader.ReadBytes(4); //cross-refs: node ids (of flowgraph refs), then the node, then 0x00 (x4) } } break; } } } } flowgraphs.Add(flowgraph); } }