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); }
public static WriteableBitmap Decode(byte[] data, int startOffset, int length) { int endIndex = startOffset + length; int finalw = BitConverter.ToInt16(data, startOffset); int finalh = BitConverter.ToInt16(data, startOffset + 2); int sourcew = finalw; int sourceh = finalh; PalEntry[] pixels = null; int curIdx = 0x80 + startOffset; GIFTag gifTag = new GIFTag(); gifTag.parse(data, curIdx); // This is basically heuristics. Writing a full GIF parser is complex and as the texture files are written by a tool, // we can safely make some assumptions about their structure. if (gifTag.nloop == 4) { int palw = DataUtil.getLEShort(data, curIdx + 0x30); int palh = DataUtil.getLEShort(data, curIdx + 0x34); curIdx += 0x50; GIFTag gifTag2 = new GIFTag(); gifTag2.parse(data, curIdx); // 8 bit palletised PalEntry[] palette = PalEntry.readPalette(data, curIdx + 0x10, palw, palh); palette = PalEntry.unswizzlePalette(palette); int palLen = palw * palh * 4; curIdx += (palLen + 0x10); GIFTag gifTag50 = new GIFTag(); gifTag50.parse(data, curIdx); curIdx += 0x20; int dbw = (sourcew / 2 + 0x07) & ~0x07; int dbh = (sourceh / 2 + 0x07) & ~0x07; // The following should be a loop, there are repeating sections while (curIdx < endIndex - 0x10) { GIFTag gifTag3 = new GIFTag(); gifTag3.parse(data, curIdx); int dimOffset = 0x10; int thisRrw = DataUtil.getLEShort(data, curIdx + dimOffset); int thisRrh = DataUtil.getLEShort(data, curIdx + dimOffset + 4); int startx = DataUtil.getLEShort(data, curIdx + dimOffset + 20); int starty = DataUtil.getLEShort(data, curIdx + dimOffset + 22); curIdx += gifTag.nloop * 0x10 + 0x10; pixels = readPixels32(pixels, data, palette, curIdx, startx, starty, thisRrw, thisRrh, dbw, dbh); curIdx += thisRrw * thisRrh * 4; } if (palLen != 64) { pixels = unswizzle8bpp(pixels, dbw * 2, dbh * 2); sourcew = dbw * 2; sourceh = dbh * 2; } else { sourcew = dbw; sourceh = dbh; } } else if (gifTag.nloop == 3) { GIFTag gifTag2 = new GIFTag(); gifTag2.parse(data, startOffset + 0xC0); if (gifTag2.flg == 2) { // image mode pixels = readPixels32(data, startOffset + 0xD0, finalw, finalh); } } WriteableBitmap image = null; if (finalw != 0 && pixels != null) { image = new WriteableBitmap( finalw, finalh, 96, 96, PixelFormats.Bgr32, null); image.Lock(); unsafe { IntPtr pBackBuffer = image.BackBuffer; for (int y = 0; y < sourceh; ++y) { for (int x = 0; x < sourcew; ++x) { PalEntry pixel = pixels[y * sourcew + x]; if (pixel != null) { if (x < finalw && y < finalh) { var p = pBackBuffer + y * image.BackBufferStride + x * 4; *((int *)p) = pixel.argb(); } } } } } // Specify the area of the bitmap that changed. image.AddDirtyRect(new Int32Rect(0, 0, finalw, finalh)); // Release the back buffer and make it available for display. image.Unlock(); } return(image); }
public static WriteableBitmap Decode(byte[] data, int startOffset, int length) { int endIndex = startOffset + length; int finalw = BitConverter.ToInt16(data, startOffset); int finalh = BitConverter.ToInt16(data, startOffset + 2); int sourcew = finalw; int sourceh = finalh; PalEntry[] pixels = null; int curIdx = 0x80 + startOffset; GIFTag gifTag = new GIFTag(); gifTag.parse(data, curIdx); // This is basically heuristics. Writing a full GIF parser is complex and as the texture files are written by a tool, // we can safely make some assumptions about their structure. if (gifTag.nloop == 4) { int palw = DataUtil.getLEShort(data, curIdx + 0x30); int palh = DataUtil.getLEShort(data, curIdx + 0x34); curIdx += 0x50; GIFTag gifTag2 = new GIFTag(); gifTag2.parse(data, curIdx); // 8 bit palletised PalEntry[] palette = PalEntry.readPalette(data, curIdx + 0x10, palw, palh); palette = PalEntry.unswizzlePalette(palette); int palLen = palw * palh * 4; curIdx += (palLen + 0x10); GIFTag gifTag50 = new GIFTag(); gifTag50.parse(data, curIdx); curIdx += 0x20; int dbw = (sourcew / 2 + 0x07) & ~0x07; int dbh = (sourceh / 2 + 0x07) & ~0x07; // The following should be a loop, there are repeating sections while (curIdx < endIndex - 0x10) { GIFTag gifTag3 = new GIFTag(); gifTag3.parse(data, curIdx); int dimOffset = 0x10; int thisRrw = DataUtil.getLEShort(data, curIdx + dimOffset); int thisRrh = DataUtil.getLEShort(data, curIdx + dimOffset + 4); int startx = DataUtil.getLEShort(data, curIdx + dimOffset + 20); int starty = DataUtil.getLEShort(data, curIdx + dimOffset + 22); curIdx += gifTag.nloop * 0x10 + 0x10; pixels = readPixels32(pixels, data, palette, curIdx, startx, starty, thisRrw, thisRrh, dbw, dbh); curIdx += thisRrw * thisRrh * 4; } if (palLen != 64) { pixels = unswizzle8bpp(pixels, dbw * 2, dbh * 2); sourcew = dbw * 2; sourceh = dbh * 2; } else { sourcew = dbw; sourceh = dbh; } } else if (gifTag.nloop == 3) { GIFTag gifTag2 = new GIFTag(); gifTag2.parse(data, startOffset + 0xC0); if (gifTag2.flg == 2) { // image mode pixels = readPixels32(data, startOffset + 0xD0, finalw, finalh); } } WriteableBitmap image = null; if (finalw != 0 && pixels != null) { image = new WriteableBitmap( finalw, finalh, 96, 96, PixelFormats.Bgr32, null); image.Lock(); unsafe { IntPtr pBackBuffer = image.BackBuffer; for (int y = 0; y < sourceh; ++y) { for (int x = 0; x < sourcew; ++x) { PalEntry pixel = pixels[y * sourcew + x]; if (pixel != null) { if (x < finalw && y < finalh) { var p = pBackBuffer + y * image.BackBufferStride + x * 4; *((int*)p) = pixel.argb(); } } } } } // Specify the area of the bitmap that changed. image.AddDirtyRect(new Int32Rect(0, 0, finalw, finalh)); // Release the back buffer and make it available for display. image.Unlock(); } return image; }
public static List<Chunk> ReadVerts(ILogger log, byte[] fileData, int offset, int endOffset) { var chunks = new List<Chunk>(); Chunk currentChunk = new Chunk(); Chunk previousChunk = null; while (offset < endOffset) { int vifCommand = fileData[offset + 3] & 0x7f; int 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; int 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"); GIFTag[] tags = new GIFTag[immCommand]; for (int 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 bool mask = ((vifCommand & 0x10) == 0x10); int vn = (vifCommand >> 2) & 3; int vl = vifCommand & 3; int addr = immCommand & 0x1ff; bool flag = (immCommand & 0x8000) == 0x8000; bool usn = (immCommand & 0x4000) == 0x4000; DebugWrite(HexUtil.formatHex(offset) + " "); String 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 (int uvnum = 0; uvnum < numCommand; ++uvnum) { short u = DataUtil.getLEShort(fileData, offset); short v = DataUtil.getLEShort(fileData, offset + 2); previousChunk.uvs.Add(new UV(u, v)); offset += 4; } } else { int 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 (int vnum = 0; vnum < numCommand; ++vnum) { if (!usn) { short x = DataUtil.getLEShort(fileData, offset); short y = DataUtil.getLEShort(fileData, offset + 2); short z = DataUtil.getLEShort(fileData, offset + 4); offset += 6; Vertex vertex = new Vertex(); vertex.x = x; vertex.y = y; vertex.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; VLoc vloc = new VLoc(); vloc.v1 = x; vloc.v2 = y; vloc.v3 = z; currentChunk.vlocs.Add(vloc); } } offset = (offset + 3) & ~3; } else if (vn == 2 && vl == 2) { // v3-8 int idx = offset; for (int vnum = 0; vnum < numCommand; ++vnum) { SByteVector vec = new SByteVector(); vec.x = (sbyte)fileData[idx++]; vec.y = (sbyte)fileData[idx++]; vec.z = (sbyte)fileData[idx++]; currentChunk.normals.Add(vec); } int 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."); } int 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); int numShorts = numCommand * 4; if (usn) { currentChunk.extraVlocs = new ushort[numShorts]; for (int 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 int numBytes = numCommand * 4; currentChunk.vertexWeights = new List<VertexWeight>(); int curVertex=0; for (int i = 0; i < numCommand; ++i) { VertexWeight vw = new VertexWeight(); vw.startVertex = curVertex; vw.bone1 = fileData[offset++] / 4; vw.boneWeight1 = fileData[offset++]; vw.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; }
public static WriteableBitmap Decode(byte[] data, int startOffset) { var gsMem = new GSMemory(); var length = DataUtil.getLEShort(data, startOffset + 6) * 16; int finalw = BitConverter.ToInt16(data, startOffset); int finalh = BitConverter.ToInt16(data, startOffset + 2); var offsetToGIF = DataUtil.getLEInt(data, startOffset + 16); var sourcew = finalw; var sourceh = finalh; PalEntry[] pixels = null; byte[] bytes = null; var curIdx = offsetToGIF + startOffset; var endIndex = curIdx + length; var gifTag = new GIFTag(); gifTag.parse(data, curIdx); // This is basically heuristics. Writing a full GIF parser is complex and as the texture files are written by a tool, // we can safely make some assumptions about their structure. if (gifTag.nloop == 4) { int palw = DataUtil.getLEShort(data, curIdx + 0x30); int palh = DataUtil.getLEShort(data, curIdx + 0x34); curIdx += gifTag.Length; var gifTag2 = new GIFTag(); gifTag2.parse(data, curIdx); // 8 bit palletised var palette = PalEntry.readPalette(data, curIdx + GIFTag.Size, palw, palh); palette = PalEntry.unswizzlePalette(palette); curIdx += gifTag2.Length; var destWBytes = (finalw + 0x0f) & ~0x0f; var destHBytes = (finalh + 0x0f) & ~0x0f; var dpsm = PSMCT32; var dbw = 0; var dbp = 0; var rrw = 0; var rrh = 0; var startx = 0; var starty = 0; while (curIdx < endIndex - GIFTag.Size) { var gifTag3 = new GIFTag(); gifTag3.parse(data, curIdx); while (!gifTag3.IsImage) { var trxregOffset = findADEntry(data, curIdx + GIFTag.Size, gifTag3.nloop, TRXREG); if (trxregOffset != 0) { rrw = DataUtil.getLEShort(data, trxregOffset); rrh = DataUtil.getLEShort(data, trxregOffset + 4); } var trxposOffset = findADEntry(data, curIdx + GIFTag.Size, gifTag3.nloop, TRXPOS); if (trxposOffset != 0) { startx = DataUtil.getLEShort(data, trxposOffset + 0x04) & 0x07FF; starty = DataUtil.getLEShort(data, trxposOffset + 0x06) & 0x07FF; } var bitbltOffset = findADEntry(data, curIdx + GIFTag.Size, gifTag3.nloop, BITBLTBUF); if (bitbltOffset != 0) { //int sbw = fileData[bitbltOffset + 0x02] & 0x3F; dbp = data[bitbltOffset + 0x04] & 0x3FFF; dbw = data[bitbltOffset + 0x06] & 0x3F; dpsm = data[bitbltOffset + 0x07] & 0x3F; } curIdx += gifTag3.Length; if (curIdx + GIFTag.Size >= endIndex) { break; } gifTag3.parse(data, curIdx); } curIdx += GIFTag.Size; // image gif tag var bytesToTransfer = gifTag3.nloop * 16; if (palette.Length == 16) { // source is PSMT4. Dest can be PSMT4 or PSMCT32 if (dpsm == PSMCT32) { var imageData = data; var imageDataIdx = curIdx; // check for multiple IMAGE entries. var nextTagInd = bytesToTransfer + curIdx; if (nextTagInd < endIndex - GIFTag.Size) { var imageTag2 = new GIFTag(); imageTag2.parse(data, nextTagInd); if (imageTag2.flg == 2) { // IMAGE var bytesToTransfer2 = imageTag2.nloop * 16; imageDataIdx = 0; imageData = new byte[bytesToTransfer + bytesToTransfer2]; var j = curIdx; for (var i = 0; i < bytesToTransfer; ++i) { imageData[i] = data[j]; } j = nextTagInd + GIFTag.Size; for (var i = bytesToTransfer; i < bytesToTransfer + bytesToTransfer2; ++i) { imageData[i] = data[j]; } bytesToTransfer += imageTag2.Length; } } gsMem.writeTexPSMCT32(dbp, dbw, startx, starty, rrw, rrh, imageData, imageDataIdx); destWBytes = (finalw + 0x3f) & ~0x3f; bytes = gsMem.readTexPSMT4(dbp, destWBytes / 0x40, startx, starty, destWBytes, destHBytes); bytes = expand4bit(bytes); } else { // dest and source are the same and so image isn't swizzled bytes = transferPSMT4(bytes, data, curIdx, startx, starty, rrw, rrh, destWBytes, destHBytes); } } else { // source is PSMT8. Dest is always PSMCT32. gsMem.writeTexPSMCT32(dbp, dbw, startx, starty, rrw, rrh, data, curIdx); } curIdx += bytesToTransfer; } if (palette.Length == 256) { destWBytes = (finalw + 0x3f) & ~0x3f; dbw = destWBytes / 0x40; bytes = gsMem.readTexPSMT8(dbp, dbw, 0, 0, destWBytes, finalh); } // THIS IS A HACK if (palette.Length == 1024) { destWBytes = (finalw + 0x3f) & ~0x3f; dbw = destWBytes / 0x40; bytes = gsMem.readTexPSMT8(dbp, dbw, 0, 0, destWBytes, finalh); } pixels = applyPalette(palette, bytes); sourcew = destWBytes; sourceh = destHBytes; } else if (gifTag.nloop == 3) { var gifTag2 = new GIFTag(); gifTag2.parse(data, startOffset + 0xC0); if (gifTag2.flg == 2) { // image mode pixels = readPixels32(data, startOffset + 0xD0, finalw, finalh); } } WriteableBitmap image = null; if (finalw != 0) { image = new WriteableBitmap( finalw, finalh, 96, 96, PixelFormats.Bgra32, null); var imageBytes = new int[finalw * finalh]; var stride = 4 * finalw; var pixOffset = 0; if (pixels != null) { for (var y = 0; y < sourceh && y < finalh; ++y) { for (var x = 0; x < sourcew && x < finalw; ++x) { var pixel = pixels[y * sourcew + x]; if (pixel != null) { if (x < finalw && y < finalh) { imageBytes[pixOffset++] = pixel.argb(); } } } } } image.WritePixels(new Int32Rect(0, 0, finalw, finalh), imageBytes, stride, 0); } return(image); }