private void EditRoutineMenuItem_Click(object sender, EventArgs e) { int idx = listBox_routines.SelectedIndex; if (idx >= 0 && idx < listBox_routines.Items.Count) { var routine = _routines[idx]; EditValueForm form = new EditValueForm("Edit Dlist", "Enter the address and coordinates of the dlist to add.", IsInputValid, $"{routine.Address:X8}; {routine.X}; {routine.Y}; {routine.Z}"); if (form.ShowDialog() == DialogResult.OK) { var parts = form.Result.Replace(" ", "").Split(";"); routine.X = routine.Y = routine.Z = 0; routine.Address = SegmentedAddress.Parse(parts[0], true); if (parts.Length > 1) { routine.X = int.Parse(parts[1]); routine.Y = int.Parse(parts[2]); routine.Z = int.Parse(parts[3]); } NewRender(); } } }
private string IsInputValid(string input) { string err = "Invalid format, must be \"<address in hex>(; <x>; <y>; <z>)\""; var parts = input.Replace(" ", "").Split(";"); if (parts.Length != 1 && parts.Length != 4) { return(err); } string addrStr = parts[0]; if (addrStr.StartsWith("0x")) { addrStr = addrStr.Substring(2); } if (!SegmentedAddress.TryParse(addrStr, true, out SegmentedAddress addr)) { return(err); } for (int i = 1; i < parts.Length; i++) { if (!int.TryParse(parts[i], out int res)) { return(err); } } return(null); }
public static void FindAnimations(Z64Object obj, byte[] data, int segmentId) { // Search for Animation Headers // Structure: FF FF 00 00 SS OO OO OO SS OO OO OO II II 00 00 for (int i = 0; i < data.Length - Z64Object.AnimationHolder.HEADER_SIZE; i += 4) { var frameCount = ArrayUtil.ReadInt16BE(data, i); // check positive nonzero frame count, check struct padding zeroes if (frameCount > 0 && data[i + 2] == 0 && data[i + 3] == 0 && data[i + 14] == 0 && data[i + 15] == 0) { if (!obj.IsOffsetFree(i)) { continue; } var frameDataSeg = new SegmentedAddress(ArrayUtil.ReadUint32BE(data, i + 4)); var jointIndicesSeg = new SegmentedAddress(ArrayUtil.ReadUint32BE(data, i + 8)); // check for segmentId match, check for valid segment offsets if (frameDataSeg.SegmentId == segmentId && jointIndicesSeg.SegmentId == segmentId && frameDataSeg.SegmentOff < data.Length && jointIndicesSeg.SegmentOff < data.Length) { // Assumes these are all in order and end at the start of the next, which seems to be the case so far int frameDataSize = (int)(jointIndicesSeg.SegmentOff - frameDataSeg.SegmentOff); int jointIndicesSize = (int)(i - jointIndicesSeg.SegmentOff); // if not a multiple of 2, check for struct padding if ((frameDataSize % 2) != 0) { byte[] possiblePadding = new byte[frameDataSize % 2]; Buffer.BlockCopy(data, (int)(frameDataSeg.SegmentOff + frameDataSize - (frameDataSize % 2)), possiblePadding, 0, frameDataSize % 2); // if assumed struct padding is nonzero, consider invalid if (possiblePadding.Any(b => b != 0)) { continue; } frameDataSize -= (frameDataSize % 2); } // if not a multiple of 6, check for struct padding if ((jointIndicesSize % 6) != 0) { byte[] possiblePadding = new byte[jointIndicesSize % 6]; Buffer.BlockCopy(data, (int)(jointIndicesSeg.SegmentOff + jointIndicesSize - (jointIndicesSize % 6)), possiblePadding, 0, jointIndicesSize % 6); // if assumed struct padding is nonzero, consider invalid if (possiblePadding.Any(b => b != 0)) { continue; } jointIndicesSize -= (jointIndicesSize % 6); } obj.AddAnimation(off: i); obj.AddFrameData(frameDataSize / 2, off: (int)frameDataSeg.SegmentOff); obj.AddJointIndices(jointIndicesSize / Z64Object.AnimationJointIndicesHolder.ENTRY_SIZE, off: (int)jointIndicesSeg.SegmentOff); } } } }
private static void AddRegion(List <ReservedRegion> regions, int curSegId, uint vaddr, int size, int totalSize) { var addr = new SegmentedAddress(vaddr); if (addr.Segmented && addr.SegmentId == curSegId && ((int)addr.SegmentOff + size) <= totalSize) { regions.Add(new ReservedRegion((int)addr.SegmentOff, size)); } }
private static void AddDlist(List <int> dlists, int curSegId, uint vaddr, int totalSize) { var addr = new SegmentedAddress(vaddr); if (addr.Segmented && addr.SegmentId == curSegId && (int)addr.SegmentOff < totalSize) { dlists.Add((int)addr.SegmentOff); } }
public override void SetData(byte[] data) { using (var ms = new MemoryStream(data)) { BinaryStream br = new BinaryStream(ms, ByteConverter.Big); LimbsSeg = new SegmentedAddress(br.ReadUInt32()); LimbCount = br.Read1Byte(); } }
public byte[] ReadBytes(uint vaddr, int count) { SegmentedAddress addr = ResolveAddress(vaddr, out string path); // read data try { if (!addr.Segmented) { return(_game.Memory.ReadBytes(vaddr, count)); } else { var seg = Segments[addr.SegmentId]; switch (seg.Type) { case SegmentType.Fill: { byte[] buff = new byte[count]; int rest = count; int dstOff = 0; while (rest > 0) { int srcOff = ((int)addr.SegmentOff + dstOff) % seg.Data.Length; int curCount = seg.Data.Length - srcOff; System.Buffer.BlockCopy(seg.Data, srcOff, buff, count - rest, curCount); rest -= curCount; dstOff += curCount; } return(buff); } case SegmentType.Buffer: if (addr.SegmentOff + count <= seg.Data.Length) { byte[] buff = new byte[count]; System.Buffer.BlockCopy(seg.Data, (int)addr.SegmentOff, buff, 0, count); return(buff); } break; case SegmentType.Vram: return(_game.Memory.ReadBytes(seg.Address + addr.SegmentOff, count)); case SegmentType.Empty: default: break; } } } catch { } throw new Exception($"Could not read 0x{count:X} bytes at address {path}"); }
public override void SetData(byte[] data) { using (var ms = new MemoryStream(data)) { BinaryStream br = new BinaryStream(ms, ByteConverter.Big); FrameCount = br.ReadInt16(); br.ReadBytes(2); // padding FrameData = new SegmentedAddress(br.ReadUInt32()); JointIndices = new SegmentedAddress(br.ReadUInt32()); StaticIndexMax = br.ReadUInt16(); } }
public override void SetData(byte[] data) { using (MemoryStream ms = new MemoryStream(data)) { BinaryStream br = new BinaryStream(ms, ByteConverter.Big); JointX = br.ReadInt16(); JointY = br.ReadInt16(); JointZ = br.ReadInt16(); Child = br.Read1Byte(); Sibling = br.Read1Byte(); DListSeg = new SegmentedAddress(br.ReadUInt32()); } }
public override void SetData(byte[] data) { if ((data.Length % 4) != 0) { throw new Z64ObjectException($"Invalid data size (0x{data.Length:X}) should be a multiple of 4"); } LimbSegments = new SegmentedAddress[data.Length / 4]; using (MemoryStream ms = new MemoryStream(data)) { BinaryStream br = new BinaryStream(ms, ByteConverter.Big); for (int i = 0; i < LimbSegments.Length; i++) { LimbSegments[i] = new SegmentedAddress(br.ReadUInt32()); } } }
private void AddRoutineMenuItem_Click(object sender, System.EventArgs e) { EditValueForm form = new EditValueForm("Add Dlist", "Enter the address and coordinates of the dlist to add.", IsInputValid); if (form.ShowDialog() == DialogResult.OK) { var parts = form.Result.Replace(" ", "").Split(";"); int x = 0, y = 0, z = 0; var addr = SegmentedAddress.Parse(parts[0], true); if (parts.Length > 1) { x = int.Parse(parts[1]); y = int.Parse(parts[2]); z = int.Parse(parts[3]); } AddDList(addr.VAddr, x, y, z); } }
private void okBtn_Click(object sender, EventArgs e) { switch (comboBox1.SelectedItem) { case SRC_ADDR: uint addr = SegmentedAddress.Parse(addressValue.Text).VAddr; ResultSegment = Memory.Segment.FromVram($"{addr:X8}", addr); break; case SRC_IDENT_MTX: ResultSegment = Memory.Segment.FromFill("Ident Matrices", new byte[] { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }); break; case SRC_NULL: ResultSegment = Memory.Segment.FromFill("Null Bytes"); break; case SRC_EMPTY: ResultSegment = Memory.Segment.Empty(); break; case SRC_EMPTY_DLIST: ResultSegment = Memory.Segment.FromBytes("Empty Dlist", new byte[] { 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); break; default: break; } DialogResult = DialogResult.OK; Close(); }
public SegmentedAddress ResolveAddress(uint vaddr, out string path) { SegmentedAddress addr = new SegmentedAddress(vaddr); path = $"{vaddr:X8}"; // resolve address int resolveCount = 0; while (addr.Segmented && Segments[addr.SegmentId].Type == SegmentType.Vram) { if (resolveCount > 16 || new SegmentedAddress(Segments[addr.SegmentId].Address).SegmentId == addr.SegmentId) { throw new Exception($"Could not resolve address 0x{vaddr:X}. Path: {path}"); } path += $" -> {Segments[addr.SegmentId].Label}+0x{addr.SegmentOff:X}"; addr = new SegmentedAddress(Segments[addr.SegmentId].Address + addr.SegmentOff); resolveCount++; } return(addr); }
public byte[] ReadBytes(SegmentedAddress addr, int count) => ReadBytes(addr.VAddr, count);
public static void FindSkeletons(Z64Object obj, byte[] data, int segmentId) { // Search for Skeleton Headers // Structure: SS OO OO OO XX 00 00 00 [XX 00 00 00] for (int i = 0; i < data.Length - Z64Object.SkeletonHolder.HEADER_SIZE; i += 4) { var segment = new SegmentedAddress(ArrayUtil.ReadUint32BE(data, i)); // check for segmentId match, check for valid segment offset, // check for Limbs 0x4 alignment, check for nonzero limb count, // check for zeroes in struct padding if (segment.SegmentId == segmentId && segment.SegmentOff < data.Length && (segment.SegmentOff % 4) == 0 && data[i + 4] != 0 && data[i + 5] == 0 && data[i + 6] == 0 && data[i + 7] == 0) { if (!obj.IsOffsetFree(i)) { continue; } int nLimbs = data[i + 4]; byte[] limbsData = new byte[nLimbs * 4]; Buffer.BlockCopy(data, (int)segment.SegmentOff, limbsData, 0, nLimbs * 4); // check for limbs array ending at the start of the skeleton header, // check for limbs array's segmented addresses being 0xC apart from one another if (segment.SegmentOff + nLimbs * 4 == i && ArrayUtil.ReadUint32BE(limbsData, 4) - ArrayUtil.ReadUint32BE(limbsData, 0) == Z64Object.SkeletonLimbHolder.ENTRY_SIZE) { int nNonNullDlists = 0; obj.AddSkeletonLimbs(nLimbs, off: (int)segment.SegmentOff); for (int j = 0; j < nLimbs * 4; j += 4) { SegmentedAddress limbSeg = new SegmentedAddress(ArrayUtil.ReadUint32BE(limbsData, j)); if (limbSeg.SegmentId != segmentId) { throw new Z64ObjectAnalyzerException( $"Limb segment {limbSeg.Segmented} is not the correct segment id, mis-detected SkeletonHeader?"); } obj.AddSkeletonLimb(off: (int)limbSeg.SegmentOff); // check if dlist is non-null (dlists may be null in general, this is only for FlexSkeletonHeader detection) if (ArrayUtil.ReadUint32BE(data, (int)(limbSeg.SegmentOff + 0x8)) != 0) { nNonNullDlists++; } } // try to detect flex headers over normal headers // check for the existence of extra bytes beyond standard header size, // check if nothing is already assumed to occupy that space, // check if the number of dlists is equal to the actual number of non-null dlists, // check struct padding if (i < data.Length - Z64Object.FlexSkeletonHolder.HEADER_SIZE && obj.IsOffsetFree(i + Z64Object.SkeletonHolder.HEADER_SIZE) && data[i + 8] == nNonNullDlists && data[i + 9] == 0 && data[i + 10] == 0 && data[i + 11] == 0) { obj.AddFlexSkeleton(off: i); } else { obj.AddSkeleton(off: i); } } } } }
private void listView_map_SelectedIndexChanged(object sender, EventArgs e) { var holder = GetCurrentHolder <Z64Object.ObjectHolder>(); if (holder == null) { tabControl1.SelectedTab = tabPage_empty; return; } openInDlistViewerMenuItem.Visible = addToDlistViewerMenuItem.Visible = openSkeletonViewerMenuItem.Visible = false; switch (holder.GetEntryType()) { case Z64Object.EntryType.DList: { openInDlistViewerMenuItem.Visible = addToDlistViewerMenuItem.Visible = true; tabControl1.SelectedTab = tabPage_text; UpdateDisassembly(); break; } case Z64Object.EntryType.Vertex: { tabControl1.SelectedTab = tabPage_vtx; var vtx = (Z64Object.VertexHolder)holder; listView_vtx.BeginUpdate(); listView_vtx.Items.Clear(); uint addr = new SegmentedAddress(_segment, _obj.OffsetOf(holder)).VAddr; for (int i = 0; i < vtx.Vertices.Count; i++) { var item = listView_vtx.Items.Add($"{addr:X8}"); item.SubItems.Add($"{vtx.Vertices[i].X}, {vtx.Vertices[i].Y}, {vtx.Vertices[i].Z}"); item.SubItems.Add($"0x{vtx.Vertices[i].Flag:X8}"); item.SubItems.Add($"{vtx.Vertices[i].TexX}, {vtx.Vertices[i].TexY}"); item.SubItems.Add($"{vtx.Vertices[i].R}, {vtx.Vertices[i].G}, {vtx.Vertices[i].B}, {vtx.Vertices[i].A}"); addr += 0x10; } listView_vtx.EndUpdate(); break; } case Z64Object.EntryType.Texture: { tabControl1.SelectedTab = tabPage_texture; var tex = (Z64Object.TextureHolder)holder; label_textureInfo.Text = $"{tex.Width}x{tex.Height} {tex.Format}"; if ((tex.Format != N64.N64TexFormat.CI4 && tex.Format != N64.N64TexFormat.CI8) || tex.Tlut != null) { pic_texture.Image = tex.GetBitmap(); } if (tex.Tlut != null) { uint tlutAddr = new SegmentedAddress(_segment, _obj.OffsetOf(tex.Tlut)).VAddr; label_textureInfo.Text += $" (TLUT : 0x{tlutAddr:X8} {tex.Tlut.Width}x{tex.Tlut.Height} {tex.Tlut.Format})"; } break; } case Z64Object.EntryType.Mtx: { tabControl1.SelectedTab = tabPage_text; var matrices = (Z64Object.MtxHolder)holder; StringWriter sw = new StringWriter(); for (int n = 0; n < matrices.Matrices.Count; n++) { sw.WriteLine($" ┌ ┐ "); for (int i = 0; i < 4; i++) { var values = ""; for (int j = 0; j < 4; j++) { values += $"0x{ArrayUtil.ReadUint32BE(matrices.Matrices[n].GetBuffer(), 4*(4 * i + j)):X08}"; if (j != 3) { values += $" "; } } sw.WriteLine($" │ {values} │ "); } sw.WriteLine($" └ ┘ "); } textBox_holderInfo.Text = sw.ToString(); break; } case Z64Object.EntryType.SkeletonHeader: { openSkeletonViewerMenuItem.Visible = true; tabControl1.SelectedTab = tabPage_text; var skel = (Z64Object.SkeletonHolder)holder; StringWriter sw = new StringWriter(); sw.WriteLine($"Limbs: 0x{skel.LimbsSeg.VAddr:X8}"); sw.WriteLine($"Limb Count: {skel.LimbCount}"); textBox_holderInfo.Text = sw.ToString(); break; } case Z64Object.EntryType.FlexSkeletonHeader: { openSkeletonViewerMenuItem.Visible = true; tabControl1.SelectedTab = tabPage_text; var skel = (Z64Object.FlexSkeletonHolder)holder; StringWriter sw = new StringWriter(); sw.WriteLine($"Limbs: 0x{skel.LimbsSeg.VAddr:X8}"); sw.WriteLine($"Limb Count: {skel.LimbCount}"); sw.WriteLine($"DList Count: {skel.DListCount}"); textBox_holderInfo.Text = sw.ToString(); break; } case Z64Object.EntryType.SkeletonLimbs: { tabControl1.SelectedTab = tabPage_text; var limbs = (Z64Object.SkeletonLimbsHolder)holder; StringWriter sw = new StringWriter(); sw.WriteLine($"Limbs:"); foreach (var limb in limbs.LimbSegments) { sw.WriteLine($"0x{limb.VAddr:X8}"); } textBox_holderInfo.Text = sw.ToString(); break; } case Z64Object.EntryType.SkeletonLimb: { tabControl1.SelectedTab = tabPage_text; var limb = (Z64Object.SkeletonLimbHolder)holder; StringWriter sw = new StringWriter(); sw.WriteLine($"Position: {{ {limb.JointX}, {limb.JointY}, {limb.JointZ} }}"); sw.WriteLine($"Child: 0x{limb.Child:X2}"); sw.WriteLine($"Sibling: 0x{limb.Sibling:X2}"); sw.WriteLine($"DList : 0x{limb.DListSeg.VAddr:X8}"); textBox_holderInfo.Text = sw.ToString(); break; } case Z64Object.EntryType.AnimationHeader: { tabControl1.SelectedTab = tabPage_text; var anim = (Z64Object.AnimationHolder)holder; StringWriter sw = new StringWriter(); sw.WriteLine($"Frame Count: {anim.FrameCount}"); sw.WriteLine($"Frame Data: 0x{anim.FrameData.VAddr:X8}"); sw.WriteLine($"Joint Indices: 0x{anim.JointIndices.VAddr:X8}"); sw.WriteLine($"Static Index Max: 0x{anim.StaticIndexMax}"); textBox_holderInfo.Text = sw.ToString(); break; } case Z64Object.EntryType.JointIndices: { tabControl1.SelectedTab = tabPage_text; var joints = (Z64Object.AnimationJointIndicesHolder)holder; StringWriter sw = new StringWriter(); sw.WriteLine($"Joints:"); foreach (var joint in joints.JointIndices) { sw.WriteLine($"{{ frameData[{joint.X}], frameData[{joint.Y}], frameData[{joint.Z}] }}"); } textBox_holderInfo.Text = sw.ToString(); break; } case Z64Object.EntryType.FrameData: case Z64Object.EntryType.Unknown: { tabControl1.SelectedTab = tabPage_unknow; var provider = new DynamicByteProvider(holder.GetData());; hexBox1.ByteProvider = provider; hexBox1.LineInfoOffset = new SegmentedAddress(_segment, _obj.OffsetOf(holder)).VAddr; break; } default: tabControl1.SelectedTab = tabPage_empty; break; } listView_map.Focus(); }
private void addressValue_TextChanged(object sender, EventArgs e) { okBtn.Enabled = SegmentedAddress.TryParse(addressValue.Text, true, out var _); }
public static List <string> AnalyzeDlists(Z64Object obj, byte[] data, int segmentId) { List <string> errors = new List <string>(); List <int> dlists = new List <int>(); for (int i = 0; i < obj.Entries.Count; i++) { var entry = obj.Entries[i]; if (entry.GetEntryType() == Z64Object.EntryType.DList) { dlists.Add(obj.OffsetOf(entry)); } else { obj.Entries[i] = new Z64Object.UnknowHolder($"unk_{obj.OffsetOf(entry):X8}", entry.GetData()); } } foreach (var dlist in dlists) { uint lastTexAddr = 0xFFFFFFFF; G_IM_FMT lastFmt = (G_IM_FMT)(-1); G_IM_SIZ lastSiz = (G_IM_SIZ)(-1); Z64Object.TextureHolder lastTlut = null; Z64Object.TextureHolder lastCiTex = null; bool exit = false; for (int i = dlist; i < data.Length && !exit; i += 8) { CmdID op = (CmdID)data[i]; switch (op) { case CmdID.G_QUAD: case CmdID.G_TRI2: case CmdID.G_TRI1: case CmdID.G_TEXRECTFLIP: case CmdID.G_TEXRECT: { if (lastCiTex != null && lastTlut != null) { lastCiTex.Tlut = lastTlut; lastCiTex = null; } break; } case CmdID.G_ENDDL: { exit = true; break; } case CmdID.G_MTX: { var gmtx = CmdInfo.DecodeCommand <GMtx>(data, i); var addr = new SegmentedAddress(gmtx.mtxaddr); if (addr.Segmented && addr.SegmentId == segmentId) { obj.AddMtx(1, off: (int)addr.SegmentOff); } break; } case CmdID.G_VTX: { var gvtx = CmdInfo.DecodeCommand <GVtx>(data, i); var addr = new SegmentedAddress(gvtx.vaddr); if (addr.Segmented && addr.SegmentId == segmentId) { try { obj.AddVertices(gvtx.numv, off: (int)addr.SegmentOff); } catch (Exception ex) { errors.Add($"Error in Dlist 0x{new SegmentedAddress(segmentId, dlist).VAddr:X8} : {ex.Message}"); } } break; } case CmdID.G_SETTIMG: { var settimg = CmdInfo.DecodeCommand <GSetTImg>(data, i); lastTexAddr = settimg.imgaddr; break; } case CmdID.G_SETTILE: { var settile = CmdInfo.DecodeCommand <GSetTile>(data, i); if (settile.tile != G_TX_TILE.G_TX_LOADTILE) { lastFmt = settile.fmt; lastSiz = settile.siz; } break; } case CmdID.G_SETTILESIZE: { var settilesize = CmdInfo.DecodeCommand <GLoadTile>(data, i); var addr = new SegmentedAddress(lastTexAddr); if ((int)lastFmt == -1 || (int)lastSiz == -1 || lastTexAddr == 0xFFFFFFFF) { /* can't really thow an exception here since in some object files, there are two gsDPSetTileSize next to each other (see object_en_warp_uzu) */ //throw new Z64ObjectAnalyzerException(); break; } if (addr.Segmented && addr.SegmentId == segmentId) { try { var tex = obj.AddTexture((int)(settilesize.lrs.Float() + 1), (int)(settilesize.lrt.Float() + 1), N64Texture.ConvertFormat(lastFmt, lastSiz), off: (int)addr.SegmentOff); if (lastFmt == G_IM_FMT.G_IM_FMT_CI) { lastCiTex = tex; } } catch (Exception ex) { errors.Add($"Error in Dlist 0x{new SegmentedAddress(segmentId, dlist).VAddr:X8} : {ex.Message}"); } } lastFmt = (G_IM_FMT)(-1); lastSiz = (G_IM_SIZ)(-1); lastTexAddr = 0xFFFFFFFF; break; } case CmdID.G_LOADTLUT: { var loadtlut = CmdInfo.DecodeCommand <GLoadTlut>(data, i); var addr = new SegmentedAddress(lastTexAddr); if (lastTexAddr == 0xFFFFFFFF) { throw new Z64ObjectAnalyzerException(); } int w = GetTlutWidth(loadtlut.count + 1); if (addr.Segmented && addr.SegmentId == segmentId) { try { lastTlut = obj.AddTexture(w, (loadtlut.count + 1) / w, N64Texture.ConvertFormat(G_IM_FMT.G_IM_FMT_RGBA, G_IM_SIZ.G_IM_SIZ_16b), off: (int)addr.SegmentOff); } catch (Exception ex) { errors.Add($"Error in Dlist 0x{new SegmentedAddress(segmentId, dlist).VAddr:X8} : {ex.Message}"); } } break; } } } } // These are carried out here as they are dependent on a lot of heuristics. // Having lots of the object already mapped out reduces possible mis-identifications. FindSkeletons(obj, data, segmentId); FindAnimations(obj, data, segmentId); obj.GroupUnkEntries(); obj.FixNames(); obj.SetData(data); return(errors); }