Exemple #1
0
        private String makeString(List <int> list)
        {
            String s = "";

            foreach (int i in list)
            {
                if (s.Length != 0)
                {
                    s += ", ";
                }
                s += HexUtil.formatHex(i);
            }

            return(s);
        }
Exemple #2
0
        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());
        }
Exemple #3
0
        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);
        }
Exemple #5
0
        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);
        }