private String makeString(List <int> list) { String s = ""; foreach (int i in list) { if (s.Length != 0) { s += ", "; } s += HexUtil.formatHex(i); } return(s); }
public override string ToString() { var sb = new StringBuilder(); sb.Append("nloop: ").Append(nloop).Append(", "); sb.Append("eop: ").Append(eop).Append(", "); sb.Append("pre: ").Append(pre).Append(", "); sb.Append("prim: ").Append(HexUtil.formatHex(prim)).Append(", "); sb.Append("flg: ").Append(flagString()).Append(", "); sb.Append("nreg: ").Append(nreg).Append(", "); sb.Append("regs: "); for (var r = 0; r < nreg; ++r) { sb.Append(regs[r]); if (r != nreg) { sb.Append(", "); } } return(sb.ToString()); }
public static List <Chunk> ReadVerts(ILogger log, byte[] fileData, int offset, int endOffset) { var chunks = new List <Chunk>(); var currentChunk = new Chunk(); Chunk previousChunk = null; while (offset < endOffset) { var vifCommand = fileData[offset + 3] & 0x7f; var numCommand = fileData[offset + 2] & 0xff; int immCommand = DataUtil.getLEShort(fileData, offset); switch (vifCommand) { case NOP_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("NOP"); offset += 4; break; case STCYCL_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("STCYCL: WL: " + (immCommand >> 8) + " CL: " + (immCommand & 0xFF)); offset += 4; break; case ITOP_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("ITOP: " + immCommand); offset += 4; break; case STMOD_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("STMOD: " + immCommand); offset += 4; break; case MSCAL_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("MSCAL: " + immCommand); if (immCommand != 66 && immCommand != 68 && immCommand != 70) { DebugWriteLine("**** Microcode " + immCommand + " not supported"); } currentChunk.mscalID = immCommand; chunks.Add(currentChunk); previousChunk = currentChunk; currentChunk = new Chunk(); offset += 4; break; case STMASK_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); offset += 4; var stmask = DataUtil.getLEInt(fileData, offset); DebugWriteLine("STMASK: " + stmask); offset += 4; break; case FLUSH_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("FLUSH"); offset += 4; break; case DIRECT_CMD: DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("DIRECT, " + immCommand * 16 + " bytes"); var tags = new GIFTag[immCommand]; for (var i = 0; i < immCommand; i++) { tags[i] = new GIFTag(); tags[i].parse(fileData, offset + 4 + i * 16); } currentChunk.DIRECTGifTags.AddRange(tags); offset += 4; offset += immCommand * 16; break; default: if ((vifCommand & 0x60) == 0x60) { // unpack command var mask = ((vifCommand & 0x10) == 0x10); var vn = (vifCommand >> 2) & 3; var vl = vifCommand & 3; var addr = immCommand & 0x1ff; var flag = (immCommand & 0x8000) == 0x8000; var usn = (immCommand & 0x4000) == 0x4000; DebugWrite(HexUtil.formatHex(offset) + " "); var debugMsg = "UNPACK: vn: " + vn + ", vl: " + vl + ", Addr: " + addr + ", num: " + numCommand; if (flag) { debugMsg += ", Flag"; } if (usn) { debugMsg += ", Unsigned"; } if (mask) { debugMsg += ", Mask"; } DebugWriteLine(debugMsg); offset += 4; if (vn == 1 && vl == 1) { // v2-16 // I don't know why but the UVs come after the MSCAL instruction. if (previousChunk != null) { for (var uvnum = 0; uvnum < numCommand; ++uvnum) { var u = DataUtil.getLEShort(fileData, offset); var v = DataUtil.getLEShort(fileData, offset + 2); previousChunk.uvs.Add(new UV(u, v)); offset += 4; } } else { var numBytes = numCommand * 4; offset += numBytes; } } else if (vn == 2 && vl == 1) { // v3-16 // each vertex is 128 bits, so num is the number of vertices for (var vnum = 0; vnum < numCommand; ++vnum) { if (!usn) { var x = DataUtil.getLEShort(fileData, offset); var y = DataUtil.getLEShort(fileData, offset + 2); var z = DataUtil.getLEShort(fileData, offset + 4); offset += 6; var vertex = new Vertex { x = x, y = y, z = z }; currentChunk.vertices.Add(vertex); } else { int x = DataUtil.getLEUShort(fileData, offset); int y = DataUtil.getLEUShort(fileData, offset + 2); int z = DataUtil.getLEUShort(fileData, offset + 4); offset += 6; var vloc = new VLoc { v1 = x, v2 = y, v3 = z }; currentChunk.vlocs.Add(vloc); } } offset = (offset + 3) & ~3; } else if (vn == 2 && vl == 2) { // v3-8 var idx = offset; for (var vnum = 0; vnum < numCommand; ++vnum) { var vec = new SByteVector { x = (sbyte)fileData[idx++], y = (sbyte)fileData[idx++], z = (sbyte)fileData[idx++] }; currentChunk.normals.Add(vec); } var numBytes = ((numCommand * 3) + 3) & ~3; offset += numBytes; } else if (vn == 3 && vl == 0) { // v4-32 log.LogLine("v4-32 data, " + numCommand + (numCommand == 1 ? " entry" : " entries") + ", addr=" + addr); if (1 == numCommand) { currentChunk.gifTag0 = new GIFTag(); currentChunk.gifTag0.parse(fileData, offset); DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("GifTag: " + currentChunk.gifTag0.ToString()); } else if (2 == numCommand) { currentChunk.gifTag0 = new GIFTag(); currentChunk.gifTag0.parse(fileData, offset); currentChunk.gifTag1 = new GIFTag(); currentChunk.gifTag1.parse(fileData, offset + 16); DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("GifTag0: " + currentChunk.gifTag0.ToString()); DebugWrite(HexUtil.formatHex(offset) + " "); DebugWriteLine("GifTag1: " + currentChunk.gifTag1.ToString()); } else { log.LogLine("unknown number of gif commands."); } var numBytes = numCommand * 16; offset += numBytes; } else if (vn == 3 && vl == 1) { // v4-16 log.LogLine("v4-16 data, " + numCommand + (numCommand == 1 ? " entry" : " entries") + ", addr=" + addr); var numShorts = numCommand * 4; if (usn) { currentChunk.extraVlocs = new ushort[numShorts]; for (var i = 0; i < numCommand; ++i) { currentChunk.extraVlocs[i * 4] = DataUtil.getLEUShort(fileData, offset + i * 8); currentChunk.extraVlocs[i * 4 + 1] = DataUtil.getLEUShort(fileData, offset + i * 8 + 2); currentChunk.extraVlocs[i * 4 + 2] = DataUtil.getLEUShort(fileData, offset + i * 8 + 4); currentChunk.extraVlocs[i * 4 + 3] = DataUtil.getLEUShort(fileData, offset + i * 8 + 6); } } else { log.LogLine("Unsupported tag"); } offset += numShorts * 2; } else if (vn == 3 && vl == 2) { // v4-8 var numBytes = numCommand * 4; currentChunk.vertexWeights = new List <VertexWeight>(); var curVertex = 0; for (var i = 0; i < numCommand; ++i) { var vw = new VertexWeight { startVertex = curVertex, bone1 = fileData[offset++] / 4, boneWeight1 = fileData[offset++], bone2 = fileData[offset++] }; if (vw.bone2 == 0xFF) { // Single bone vw.boneWeight2 = 0; int count = fileData[offset++]; curVertex += count; } else { vw.bone2 /= 4; vw.boneWeight2 = fileData[offset++]; ++curVertex; if (vw.boneWeight1 + vw.boneWeight2 < 255) { ++i; vw.bone3 = fileData[offset++] / 4; vw.boneWeight3 = fileData[offset++]; vw.bone4 = fileData[offset++]; int bw4 = fileData[offset++]; if (vw.bone4 != 255) { vw.bone4 /= 4; vw.boneWeight4 = bw4; } } } vw.endVertex = curVertex - 1; currentChunk.vertexWeights.Add(vw); } } else { DebugWriteLine("Unknown vnvl combination: vn=" + vn + ", vl=" + vl); offset = endOffset; } } else { DebugWriteLine("Unknown command: " + vifCommand); offset = endOffset; } break; } } return(chunks); }
private WorldElement ReadWorldElement(EngineVersion engineVersion, WorldTexFile texFile, ILogger log, byte[] data, int startOffset, WorldData worldData, DataReader reader, int elementArrayStart, int texX0, int texY0, int elementIdx) { var element = new WorldElement(); if (EngineVersion.ReturnToArms == engineVersion || EngineVersion.JusticeLeagueHeroes == engineVersion) { reader.SetOffset(elementArrayStart + elementIdx * 0x3C); } else // Default to Dark Allience version { reader.SetOffset(elementArrayStart + elementIdx * 0x38); } var vifDataOffset = reader.ReadInt32(); if (EngineVersion.DarkAlliance == engineVersion) { var tex2 = reader.ReadInt32(); if (tex2 != 0) { log.LogLine("Tex2=" + tex2); } } var vifLen = reader.ReadInt32(); log.LogLine("-----------"); log.LogLine("vifdata: " + vifDataOffset + ", " + vifLen); var x1 = reader.ReadFloat(); var y1 = reader.ReadFloat(); var z1 = reader.ReadFloat(); var x2 = reader.ReadFloat(); var y2 = reader.ReadFloat(); var z2 = reader.ReadFloat(); element.boundingBox = new Rect3D(x1, y1, z1, x2 - x1, y2 - y1, z2 - z1); log.LogLine("Bounding Box: " + element.boundingBox.ToString()); var textureNum = reader.ReadInt32() / 0x40; log.LogLine("Texture Num: " + textureNum); int texCellxy = reader.ReadInt16(); var y = texCellxy / 100; var x = texCellxy % 100; if (EngineVersion.ReturnToArms == engineVersion || EngineVersion.JusticeLeagueHeroes == engineVersion) { x += texX0; y += texY0; } if (textureNum != 0 && texFile != null) { if (EngineVersion.ReturnToArms == engineVersion || EngineVersion.JusticeLeagueHeroes == engineVersion) { element.Texture = texFile.GetBitmapRTA(x, y, textureNum); } else { element.Texture = texFile.GetBitmap(worldData.textureChunkOffsets[y, x], textureNum); } } if (element.Texture != null) { log.LogLine("Found in texture chunk: " + x + ", " + y); } var vifLogger = new StringLogger(); var texWidth = 100; var texHeight = 100; if (element.Texture != null) { texWidth = element.Texture.PixelWidth; texHeight = element.Texture.PixelHeight; } var nregs = data[startOffset + vifDataOffset + 0x10]; var vifStartOffset = (nregs + 2) * 0x10; element.VifDataOffset = startOffset + vifDataOffset + vifStartOffset; element.VifDataLength = vifLen * 0x10 - vifStartOffset; element.model = decodeModel(engineVersion, vifLogger, data, startOffset + vifDataOffset + vifStartOffset, vifLen * 0x10 - vifStartOffset, texWidth, texHeight); if (EngineVersion.ReturnToArms == engineVersion || EngineVersion.JusticeLeagueHeroes == engineVersion) { int unk = reader.ReadInt16(); log.LogLine("Unknown: " + unk); } int posx = reader.ReadInt16(); int posy = reader.ReadInt16(); int posz = reader.ReadInt16(); log.LogLine("Position : " + posx + ", " + posy + ", " + posz); element.pos = new Vector3D(posx / 16.0, posy / 16.0, posz / 16.0); if (EngineVersion.ReturnToArms == engineVersion || EngineVersion.JusticeLeagueHeroes == engineVersion) { // Just a guess, maybe wrong. element.pos = new Vector3D(posx / 16.0, posz / 16.0, posy / 16.0); } // I don't think RTA uses this flags scheme. From the data it looks like there are // 2 shorts (or possibly floats) following. var flags = reader.ReadInt32(); if ((flags & 0x01) == 0) { log.LogLine("Flags : " + HexUtil.formatHexUShort(flags & 0xFFFF)); element.cosAlpha = (flags >> 16) / 32767.0; element.sinAlpha = reader.ReadInt16() / 32767.0; log.LogLine("cos alpha : " + element.cosAlpha); log.LogLine("sin alpha : " + element.sinAlpha); log.LogLine("alpha(cos, sin): " + Math.Acos(element.cosAlpha) * 180.0 / Math.PI + ", " + Math.Asin(element.sinAlpha) * 180.0 / Math.PI); element.usesRotFlags = false; } else { reader.ReadInt16(); // not necessary but makes the code more obvious. log.LogLine("Flags : " + HexUtil.formatHex(flags)); element.xyzRotFlags = (flags >> 16) & 7; element.usesRotFlags = true; log.LogLine("Rot Flags : " + element.xyzRotFlags); } element.negYaxis = (flags & 0x40) == 0x40; if (EngineVersion.ReturnToArms == engineVersion || EngineVersion.JusticeLeagueHeroes == engineVersion) { flags = 0; element.usesRotFlags = true; log.LogLine("Forcing flags to 0 until we know the format better"); } return(element); }
public WorldData Decode(EngineVersion engineVersion, WorldTexFile texFile, ILogger log, byte[] data, int startOffset, int length) { WorldData worldData = new WorldData(); var reader = new DataReader(data, startOffset, length); int numElements = reader.ReadInt32(); // 0 reader.Skip(12); // Skipping 3 ints int numCols = reader.ReadInt32(); // x10 int numRows = reader.ReadInt32(); // x14 reader.Skip(12); // Skipping 3 ints // x18 x1c x20 int elementArrayStart = reader.ReadInt32(); // x24 reader.Skip(8); // Skipping 2 ints int off38Cols = reader.ReadInt32(); int off38Rows = reader.ReadInt32(); int off38 = reader.ReadInt32(); reader.Skip(28); int texll = reader.ReadInt32(); int texur = reader.ReadInt32(); int texX0 = texll % 100; int texY0 = texll / 100; int texX1 = texur % 100; int texY1 = texur / 100; reader.Skip(4); int worldTexOffsetsOffset = reader.ReadInt32(); worldData.textureChunkOffsets = readTextureChunkOffsets(engineVersion, data, startOffset + worldTexOffsetsOffset, texX0, texY0, texX1 + 1, texY1); worldData.worldElements = new List <WorldElement>(numElements); for (int elementIdx = 0; elementIdx < numElements; ++elementIdx) { var element = new WorldElement(); if (EngineVersion.ReturnToArms == engineVersion) { reader.SetOffset(elementArrayStart + elementIdx * 0x3C); } else // Default to Dark Allience version { reader.SetOffset(elementArrayStart + elementIdx * 0x38); } int vifDataOffset = reader.ReadInt32(); if (EngineVersion.DarkAlliance == engineVersion) { int tex2 = reader.ReadInt32(); if (tex2 != 0) { log.LogLine("Tex2=" + tex2); } } int vifLen = reader.ReadInt32(); log.LogLine("-----------"); log.LogLine("vifdata: " + vifDataOffset + ", " + vifLen); float x1 = reader.ReadFloat(); float y1 = reader.ReadFloat(); float z1 = reader.ReadFloat(); float x2 = reader.ReadFloat(); float y2 = reader.ReadFloat(); float z2 = reader.ReadFloat(); element.boundingBox = new Rect3D(x1, y1, z1, x2 - x1, y2 - y1, z2 - z1); log.LogLine("Bounding Box: " + element.boundingBox.ToString()); int textureNum = reader.ReadInt32() / 0x40; log.LogLine("Texture Num: " + textureNum); int texCellxy = reader.ReadInt16(); int y = texCellxy / 100; int x = texCellxy % 100; if (EngineVersion.ReturnToArms == engineVersion) { x += texX0; y += texY0; } if (textureNum != 0) { if (EngineVersion.ReturnToArms == engineVersion) { element.Texture = texFile.GetBitmapRTA(x, y, textureNum); } else { element.Texture = texFile.GetBitmap(worldData.textureChunkOffsets[y, x], textureNum); } } if (element.Texture != null) { log.LogLine("Found in texture chunk: " + x + ", " + y); } var vifLogger = new StringLogger(); int texWidth = 100; int texHeight = 100; if (element.Texture != null) { texWidth = element.Texture.PixelWidth; texHeight = element.Texture.PixelHeight; } byte nregs = data[startOffset + vifDataOffset + 0x10]; int vifStartOffset = (nregs + 2) * 0x10; element.VifDataOffset = startOffset + vifDataOffset + vifStartOffset; element.VifDataLength = vifLen * 0x10 - vifStartOffset; element.model = decodeModel(engineVersion, vifLogger, data, startOffset + vifDataOffset + vifStartOffset, vifLen * 0x10 - vifStartOffset, texWidth, texHeight); if (EngineVersion.ReturnToArms == engineVersion) { int unk = reader.ReadInt16(); log.LogLine("Unknown: " + unk); } int posx = reader.ReadInt16(); int posy = reader.ReadInt16(); int posz = reader.ReadInt16(); log.LogLine("Position : " + posx + ", " + posy + ", " + posz); element.pos = new Vector3D(posx / 16.0, posy / 16.0, posz / 16.0); if (EngineVersion.ReturnToArms == engineVersion) { // Just a guess, maybe wrong. element.pos = new Vector3D(posx / 16.0, posz / 16.0, posy / 16.0); } // I don't think RTA uses this flags scheme. From the data it looks like there are // 2 shorts (or possibly floats) following. int flags = reader.ReadInt32(); if ((flags & 0x01) == 0) { log.LogLine("Flags : " + HexUtil.formatHexUShort(flags & 0xFFFF)); element.cosAlpha = (flags >> 16) / 32767.0; element.sinAlpha = reader.ReadInt16() / 32767.0; log.LogLine("cos alpha : " + element.cosAlpha); log.LogLine("sin alpha : " + element.sinAlpha); log.LogLine("alpha(cos, sin): " + Math.Acos(element.cosAlpha) * 180.0 / Math.PI + ", " + Math.Asin(element.sinAlpha) * 180.0 / Math.PI); element.usesRotFlags = false; } else { reader.ReadInt16(); // not necessary but makes the code more obvious. log.LogLine("Flags : " + HexUtil.formatHex(flags)); element.xyzRotFlags = (flags >> 16) & 7; element.usesRotFlags = true; log.LogLine("Rot Flags : " + element.xyzRotFlags); } element.negYaxis = (flags & 0x40) == 0x40; if (EngineVersion.ReturnToArms == engineVersion) { flags = 0; element.usesRotFlags = true; log.LogLine("Forcing flags to 0 until we know the format better"); } worldData.worldElements.Add(element); } return(worldData); }