Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
72fb886
MemoryManager: Try search secrets by looped ASM call instead of behav ID
Mushie64 Feb 4, 2025
e16af5f
MemoryManager: Add segmented_to_virtual-like function
Mushie64 Feb 5, 2025
ae2f5c6
MemoryManager: Fix behav script start pointer arithmetic + endianness
Mushie64 Feb 5, 2025
1004354
MemoryManager: Add 0C to checked lengthy behav cmds outside loops
Mushie64 Feb 5, 2025
a635e4b
MemoryManager: SegmentedToAddress() -> SegmentedToVirtual()
Mushie64 Feb 6, 2025
ed87a71
MemoryManager: Cache searched behavs by call + move cmd length checks…
Mushie64 Feb 6, 2025
37f10ef
Move behavCmdLengths dict out of concrete class code
Mushie64 Feb 10, 2025
c6ffef2
ROMManager: Search objects by looped ASM call instead of behav ID
Mushie64 Feb 10, 2025
8fbe8b5
MemoryManager: Clean up debug Console.WriteLines
Mushie64 Feb 10, 2025
bdd023e
MemoryManager: Break read on behav terminating commands
Mushie64 Feb 11, 2025
5768590
MemoryManager: Copy SearchObjectsByBehavCalls with state param + adapt
Mushie64 Feb 11, 2025
6574b52
MemoryManager: Clean up comments
Mushie64 Feb 11, 2025
cf5739f
ROMManager: Replace array copy hack with full behav address read
Mushie64 Feb 11, 2025
51642aa
MemoryManager: Fix counting of active panels
Mushie64 Mar 18, 2025
124380e
ROMManager: Optimize reads for collectable behavs
Mushie64 Apr 25, 2025
6cf9681
MemoryManager: Revert SearchObjects(), get target behavs from ROMManager
Mushie64 Apr 25, 2025
30dc032
ROMManager: Read entire bank 13 + optimize reads for behav addrs
Mushie64 Aug 27, 2025
4b4bf5e
Allow multiple trackable behaviors per collectible
Mushie64 Aug 27, 2025
cadda93
Clean up leftover code
Mushie64 Aug 28, 2025
7ad347f
Limit loops in scanning functions
Mushie64 Aug 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion StarManager/MainWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ private void UpdateStars(object sender)
if (!mm.isReadyToRead())
return;

mm.PerformRead();
mm.PerformRead(rm);
}
catch (Exception)
{
Expand Down
51 changes: 40 additions & 11 deletions StarManager/Managers/MemoryManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ public void doMagic()
catch (Exception) { }
}

public void PerformRead()
public void PerformRead(ROMManager rm)
{
Igt = Process.ReadValue<int>(igtPtr);

Expand All @@ -309,8 +309,8 @@ public void PerformRead()
Area = Process.ReadValue<byte>(areaPtr);
Reds = Process.ReadValue<sbyte>(redsPtr);

RestSecrets = GetSecrets();
ActivePanels = GetActivePanels();
RestSecrets = GetSecrets(rm);
ActivePanels = GetActivePanels(rm);

SelectedStar = Process.ReadValue<byte>(selectedStarPtr);
}
Expand Down Expand Up @@ -412,20 +412,50 @@ public sbyte GetReds()
return request;
}

int GetSecrets()
int GetSecrets(ROMManager rm)
{
return SearchObjects(GetBehaviourRAMAddress(0x3F1C));
if (rm == null)
return SearchObjects(GetBehaviourRAMAddress(0x3F1C));
else
{
int objCount = 0;
foreach (uint behavAddr in rm.GetSecretsBehavAddresses())
{
objCount += SearchObjects(GetBehaviourRAMAddress(behavAddr));
}
return objCount;
}
}

int GetActivePanels()
int GetActivePanels(ROMManager rm)
{
uint request = GetBehaviourRAMAddress(0x5D8);
return SearchObjects(request, 1) + SearchObjects(request, 2); //1 - active, 2 - finalized
if (rm == null)
return SearchObjects(GetBehaviourRAMAddress(0x5D8), 1) + SearchObjects(GetBehaviourRAMAddress(0x5D8), 2); //1 - active, 2 - finalized
else
{
int objCount = 0;
foreach (uint behavAddr in rm.GetPanelsBehavAddresses())
{
objCount += SearchObjects(GetBehaviourRAMAddress(behavAddr), 1);
objCount += SearchObjects(GetBehaviourRAMAddress(behavAddr), 2);
}
return objCount;
}
}

int GetAllPanels()
int GetAllPanels(ROMManager rm)
{
return SearchObjects(GetBehaviourRAMAddress(0x5D8));
if (rm == null)
return SearchObjects(GetBehaviourRAMAddress(0x5D8));
else
{
int objCount = 0;
foreach (uint behavAddr in rm.GetPanelsBehavAddresses())
{
objCount += SearchObjects(GetBehaviourRAMAddress(behavAddr));
}
return objCount;
}
}

public Bitmap GetImage()
Expand Down Expand Up @@ -637,7 +667,6 @@ public int SearchObjects(UInt32 searchBehaviour)
{
UInt32 intparam = BitConverter.ToUInt32(data, 0x180);
UInt32 behaviour = BitConverter.ToUInt32(data, 0x20C);
UInt32 scriptParameter = BitConverter.ToUInt32(data, 0x0F0);

if (behaviour == searchBehaviour)
{
Expand Down
164 changes: 153 additions & 11 deletions StarManager/Managers/ROMManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ public class ROMManager : CachedManager, IDisposable
{
BinaryReader reader;

static int courseBaseAddress = 0x02AC094;
static int courseBaseAddress = 0x02AC094; // BBH ("first" level, 0x04) ROM address
static uint seg15StartRomAddress = 0x02ABCA0; // global(?) segment loads before switching by course ID, incl. bank 13 (see Quad64)
static uint seg13StartRomAddress = 0x0219E00; // default in old binary (Editor) ROMs, but scanned for in seg15 on init
static uint seg13EndRomAddress = 0x021F4C0; // default in old binary (Editor) ROMs, but scanned for in seg15 on init
static uint[] seg13Words;

static byte levelscriptEndDescriptor = 0x02;
static byte jumpDescriptor = 0x05;
Expand Down Expand Up @@ -45,9 +49,13 @@ public class ROMManager : CachedManager, IDisposable

static byte[] bossMIPSBehaviour = { 0x00, 0x44, 0xFC };

static byte[] redsBehaviour = { 0x00, 0x3E, 0xAC };
static byte[] secretsBehaviour = { 0x00, 0x3F, 0x1C };
static byte[] flipswitchBehaviour = { 0x00, 0x05, 0xD8 };

List<uint> redsBehaviours;
static uint redsBehavLoopCall = 0x802F2F2C;
List<uint> secretsBehaviours;
static uint secretsBehavLoopCall = 0x802F31BC;
List<uint> flipswitchBehaviours;
static uint flipswitchBehavLoopCall = 0x802A8238;

Object[] boxObjects;

Expand Down Expand Up @@ -116,13 +124,95 @@ public ROMManager(string fileName)

reader = new BinaryReader(new MemoryStream(data));
boxObjects = ReadBoxBehaviours();

ReadSegment13ROMRangeAddrs();
//uint[] seg13Words;
{
reader.BaseStream.Position = seg13StartRomAddress;
byte[] seg13Bytes = reader.ReadBytes((int)(seg13EndRomAddress - seg13StartRomAddress)); // casts may lose info if segment size > 7FFFFFFF, which shouldn't happen
int size = seg13Bytes.Count() / 4;
seg13Words = new uint[size];
for (int idx = 0; idx < size; idx++)
{
byte[] dataInt = new byte[4];
dataInt[0] = seg13Bytes[3 + 4 * idx];
dataInt[1] = seg13Bytes[2 + 4 * idx];
dataInt[2] = seg13Bytes[1 + 4 * idx];
dataInt[3] = seg13Bytes[0 + 4 * idx];
seg13Words[idx] = BitConverter.ToUInt32(dataInt, 0);
}
}
redsBehaviours = FindSeg13BehavAddrs(redsBehavLoopCall);
secretsBehaviours = FindSeg13BehavAddrs(secretsBehavLoopCall);
flipswitchBehaviours = FindSeg13BehavAddrs(flipswitchBehavLoopCall);
}

public ROMManager(byte[] data)
{
if (data == null) throw new IOException("Data is null");
reader = new BinaryReader(new MemoryStream(data));
boxObjects = ReadBoxBehaviours();

ReadSegment13ROMRangeAddrs();
//uint[] seg13Words;
{
reader.BaseStream.Position = seg13StartRomAddress;
byte[] seg13Bytes = reader.ReadBytes((int)(seg13EndRomAddress - seg13StartRomAddress)); // casts may lose info if segment size > 7FFFFFFF, which shouldn't happen
int size = seg13Bytes.Count() / 4;
seg13Words = new uint[size];
for (int idx = 0; idx < size; idx++)
{
byte[] dataInt = new byte[4];
dataInt[0] = seg13Bytes[3 + 4 * idx];
dataInt[1] = seg13Bytes[2 + 4 * idx];
dataInt[2] = seg13Bytes[1 + 4 * idx];
dataInt[3] = seg13Bytes[0 + 4 * idx];
seg13Words[idx] = BitConverter.ToUInt32(dataInt, 0);
}
}
redsBehaviours = FindSeg13BehavAddrs(redsBehavLoopCall);
secretsBehaviours = FindSeg13BehavAddrs(secretsBehavLoopCall);
flipswitchBehaviours = FindSeg13BehavAddrs(flipswitchBehavLoopCall);
}

public List<uint> GetRedsBehavAddresses() { return redsBehaviours; }
public List<uint> GetSecretsBehavAddresses() { return secretsBehaviours; }
public List<uint> GetPanelsBehavAddresses() { return flipswitchBehaviours; }

private List<uint> FindSeg13BehavAddrs(uint targetCallWord)
{
List<uint> wordOffsets = new List<uint>();

// find addresses calling the target function
for (int i = 0; i < seg13Words.Length; i++)
{
if (seg13Words[i] == targetCallWord)
wordOffsets.Add(4 * (uint)i);
}
if (wordOffsets.Count == 0) return wordOffsets; // target function not used, we won't find a behavior below. return empty list early

// for each offset considered valid, go backwards towards start of script to turn offset into script address
for (int i = 0; i < wordOffsets.Count; i++)
{
reader.BaseStream.Position = seg13StartRomAddress + wordOffsets[i];
byte[] behavScriptLineBytes;
int reads = 0;
wordOffsets[i] += 0x04; // workaround for last step in while-loop happening before it figures out whether it needs to do it
do
{
behavScriptLineBytes = reader.ReadBytes(4);
reader.BaseStream.Position -= 0x08;
wordOffsets[i] -= 0x04;
reads++;
}
// FIXME: "Start behav" cmd is 00 XX 00 00, XX == 00-0C, ignore bigger values. also ignore 00 because in cmd 00 that group is only Mario.
// This MAY STILL get garbage, if a command has parameter looking like 00 [00-0C] [...] aligned to 4 bytes (see cmds 23, 30).
// XXX: Line reads are limited to 40, rounding up from longest vanilla behav (13000338, particle; 0x84 bytes/0x21 words).
// Same behav would also trip this function with its cmd parameter values.
while (reads < 0x28 && (behavScriptLineBytes[0] != 0x00 || behavScriptLineBytes[1] == 0x00 || behavScriptLineBytes[1] > 0x0C));
}

return wordOffsets;
}

public void Dispose()
Expand Down Expand Up @@ -162,6 +252,13 @@ private byte[] ReadBehaviour(int offset)
return reader.ReadBytes(3);
}

// level cmd 24 18 ... [BS BS BS BS] - BS read in full. useful if you will pass it indiscriminately into a BitConverter 4-byte read.
private byte[] ReadBehaviourFullAddr(int offset)
{
reader.BaseStream.Position = offset + 0x14;
return reader.ReadBytes(4);
}

private byte ReadBParam1(int offset)
{
reader.BaseStream.Position = offset + 0x10;
Expand Down Expand Up @@ -240,26 +337,35 @@ public int ParseReds(int level, int currentStar, int currentArea)
int result = PrepareAddresses(level, out int levelAddressStart, out int levelAddressEnd, out int levelOffset);
if (result != 0) return 0;

return GetAmountOfObjects(levelAddressStart, levelAddressEnd, levelOffset, redsBehaviour, currentStar, currentArea);
int objCount = 0;
foreach (uint redsBehav in redsBehaviours)
objCount += GetAmountOfObjects(levelAddressStart, levelAddressEnd, levelOffset, redsBehav, currentStar, currentArea);
return objCount;
}

public int ParseSecrets(int level, int currentStar, int currentArea)
{
int result = PrepareAddresses(level, out int levelAddressStart, out int levelAddressEnd, out int levelOffset);
if (result != 0) return 0;

return GetAmountOfObjects(levelAddressStart, levelAddressEnd, levelOffset, secretsBehaviour, currentStar, currentArea);
int objCount = 0;
foreach (uint secretsBehav in secretsBehaviours)
objCount += GetAmountOfObjects(levelAddressStart, levelAddressEnd, levelOffset, secretsBehav, currentStar, currentArea);
return objCount;
}

public int ParseFlipswitches(int level, int currentStar, int currentArea)
{
int result = PrepareAddresses(level, out int levelAddressStart, out int levelAddressEnd, out int levelOffset);
if (result != 0) return 0;

return GetAmountOfObjects(levelAddressStart, levelAddressEnd, levelOffset, flipswitchBehaviour, currentStar, currentArea);
int objCount = 0;
foreach (uint flipswitchBehav in flipswitchBehaviours)
objCount += GetAmountOfObjects(levelAddressStart, levelAddressEnd, levelOffset, flipswitchBehav, currentStar, currentArea);
return objCount;
}

private int GetAmountOfObjects(int start, int end, int offset, byte[] searchBehaviour, int currentStar, int currentArea)
private int GetAmountOfObjects(int start, int end, int offset, uint searchBehaviour, int currentStar, int currentArea)
{
int area = 0;
return GetAmountOfObjectsInternal(start, end, offset, searchBehaviour, currentStar, currentArea, ref area);
Expand All @@ -273,8 +379,16 @@ public int SwapBytes(int x)
((x & 0xff000000) >> 24));
}

public uint SwapBytes(uint x)
{
return ((x & 0x000000ff) << 24) +
((x & 0x0000ff00) << 8) +
((x & 0x00ff0000) >> 8) +
((x & 0xff000000) >> 24);
}


private int GetAmountOfObjectsInternal (int start, int end, int Loffset, byte[] searchBehaviour, int currentStar, int currentArea, ref int area)
private int GetAmountOfObjectsInternal (int start, int end, int Loffset, uint searchBehaviour, int currentStar, int currentArea, ref int area)
{
if (currentArea == 0) currentArea = 1;
byte currentStarMask = (byte) (1 << currentStar);
Expand Down Expand Up @@ -342,8 +456,10 @@ private int GetAmountOfObjectsInternal (int start, int end, int Loffset, byte[]
continue;
}

byte[] behaviour = ReadBehaviour(offset);
if (behaviour.SequenceEqual(searchBehaviour))
byte[] behaviour = ReadBehaviourFullAddr(offset);
behaviour[0] = 0x00; // read 4 bytes for BitConverter below, but clear the unnecessary segment byte
uint behaviorAsAddr = SwapBytes(BitConverter.ToUInt32(behaviour, 0));
if (behaviorAsAddr == searchBehaviour)
{
counter++;
}
Expand Down Expand Up @@ -475,6 +591,32 @@ public Object[] ReadBoxBehaviours()
}
}

// hardcoded to reading for segment 13; returns void + values to class vars as lazy way to not design for "multiple returns"
// if it somehow fails, default values are used
private void ReadSegment13ROMRangeAddrs()
{
reader.BaseStream.Position = seg15StartRomAddress;
int offset = 0;
// XXX: Reads are limited to 0x20, because there are that many segments, assumption is each only needs to be loaded once.
for (int reads = 0; reads < 0x20; reads++)
{
// load cmds 0x16, 17, 18 are all length 0xC
byte[] loadCmdLineBytes = reader.ReadBytes(0xC);

if (loadCmdLineBytes[0] == 0x1D)
break;

if (loadCmdLineBytes[3] == 0x13) // 4th byte is segment number
{
seg13StartRomAddress = SwapBytes(BitConverter.ToUInt32(loadCmdLineBytes, 0x4));
seg13EndRomAddress = SwapBytes(BitConverter.ToUInt32(loadCmdLineBytes, 0x8));
}

offset += 0xC;
reader.BaseStream.Position = seg15StartRomAddress + offset;
}
}

public Bitmap GetStarImage()
{
reader.BaseStream.Position = 0x807956; //0x803156 + 0x4800
Expand Down