Exemple #1
0
        // A segmented address is 4 bytes. The first byte contains the index of the segment in the segment table, the
        // other 3 bytes are the offset from the segment. Segmented addresses are used for locating object behavior scripts,
        // display lists, textures and other resources.
        // todo: not limited to Fast3D display lists, can be put in a more general utilitiies class
        public static uint DecodeSegmentedAddress(uint segmentedAddress)
        {
            var offset  = segmentedAddress & 0xFFFFFF;
            var segment = (segmentedAddress >> 24);

            return(offset + Config.Stream.GetUInt32(4 * segment + RomVersionConfig.Switch(0x33B400, 0x33A090)));
        }
Exemple #2
0
        private void UpdateComboBoxes()
        {
            // Rom Version
            RomVersionConfig.UpdateRomVersion(comboBoxRomVersion);

            // Readonly / Read+Write
            Config.Stream.Readonly = (ReadWriteMode)comboBoxReadWriteMode.SelectedItem == ReadWriteMode.ReadOnly;
        }
Exemple #3
0
        public SoundManager(TabPage tabPage)
        {
            SplitContainer splitContainerSound = tabPage.Controls["splitContainerSound"] as SplitContainer;

            SplitContainer splitContainerSoundMusic = splitContainerSound.Panel1.Controls["splitContainerSoundMusic"] as SplitContainer;
            ListBox        listBoxSoundMusic        = splitContainerSoundMusic.Panel1.Controls["listBoxSoundMusic"] as ListBox;
            TextBox        textBoxSoundMusic        = splitContainerSoundMusic.Panel2.Controls["textBoxSoundMusic"] as TextBox;
            Button         buttonSoundPlayMusic     = splitContainerSoundMusic.Panel2.Controls["buttonSoundPlayMusic"] as Button;

            SplitContainer splitContainerSoundSoundEffect = splitContainerSound.Panel2.Controls["splitContainerSoundSoundEffect"] as SplitContainer;
            ListBox        listBoxSoundSoundEffect        = splitContainerSoundSoundEffect.Panel1.Controls["listBoxSoundSoundEffect"] as ListBox;
            TextBox        textBoxSoundSoundEffect        = splitContainerSoundSoundEffect.Panel2.Controls["textBoxSoundSoundEffect"] as TextBox;
            Button         buttonSoundPlaySoundEffect     = splitContainerSoundSoundEffect.Panel2.Controls["buttonSoundPlaySoundEffect"] as Button;

            TableConfig.MusicData.GetMusicEntryList().ForEach(musicEntry => listBoxSoundMusic.Items.Add(musicEntry));
            listBoxSoundMusic.Click += (sender, e) =>
            {
                MusicEntry musicEntry = listBoxSoundMusic.SelectedItem as MusicEntry;
                textBoxSoundMusic.Text = musicEntry.Index.ToString();
            };
            buttonSoundPlayMusic.Click += (sender, e) =>
            {
                int?musicIndexNullable = ParsingUtilities.ParseIntNullable(textBoxSoundMusic.Text);
                if (musicIndexNullable == null)
                {
                    return;
                }
                int musicIndex = musicIndexNullable.Value;
                if (musicIndex < 0 || musicIndex > 34)
                {
                    return;
                }
                uint setMusic = RomVersionConfig.SwitchMap(0x80320544, 0x8031F690);
                InGameFunctionCall.WriteInGameFunctionCall(setMusic, 0, (uint)musicIndex, 0);
            };

            foreach (uint soundEffect in _soundEffects)
            {
                string soundEffectString = HexUtilities.FormatValue(soundEffect, 4);
                listBoxSoundSoundEffect.Items.Add(soundEffectString);
            }
            listBoxSoundSoundEffect.Click += (sender, e) =>
            {
                textBoxSoundSoundEffect.Text = listBoxSoundSoundEffect.SelectedItem.ToString() + "FF81";
            };
            buttonSoundPlaySoundEffect.Click += (sender, e) =>
            {
                uint setSound            = RomVersionConfig.SwitchMap(0x8031EB00, 0x8031DC78);
                uint soundArg            = RomVersionConfig.SwitchMap(0x803331F0, 0x803320E0);
                uint?soundEffectNullable = ParsingUtilities.ParseHexNullable(textBoxSoundSoundEffect.Text);
                if (!soundEffectNullable.HasValue)
                {
                    return;
                }
                uint soundEffect = soundEffectNullable.Value;
                InGameFunctionCall.WriteInGameFunctionCall(setSound, soundEffect, soundArg);
            };
        }
Exemple #4
0
        /**
         * When refresh is clicked, the old GFX tree is discarded and a new one is read
         */
        private void RefreshButton_Click(object sender, EventArgs e)
        {
            treeViewGfx.Nodes.Clear();

            // A pointer to the root node of the GFX tree is stored at offset 0x04 in a certain struct
            var StructWithGfxRoot = Config.Stream.GetUInt32(RomVersionConfig.SwitchMap(0x8032DDCC, 0x8032CE6C));

            if (StructWithGfxRoot > 0x80000000u)
            {
                AddToTreeView(Config.Stream.GetUInt32(StructWithGfxRoot + 0x04));
            }

            ExpandNodesUpTo(treeViewGfx.Nodes, 4);
        }
Exemple #5
0
        public override void InitializeTab()
        {
            base.InitializeTab();

            TableConfig.MusicData = XmlConfigParser.OpenMusicTable(@"Config/MusicData.xml");
            TableConfig.MusicData.GetMusicEntryList().ForEach(musicEntry => listBoxSoundMusic.Items.Add(musicEntry));
            listBoxSoundMusic.Click += (sender, e) =>
            {
                MusicEntry musicEntry = listBoxSoundMusic.SelectedItem as MusicEntry;
                textBoxSoundMusic.Text = musicEntry.Index.ToString();
            };
            buttonSoundPlayMusic.Click += (sender, e) =>
            {
                int?musicIndexNullable = ParsingUtilities.ParseIntNullable(textBoxSoundMusic.Text);
                if (musicIndexNullable == null)
                {
                    return;
                }
                int musicIndex = musicIndexNullable.Value;
                if (musicIndex < 0 || musicIndex > 34)
                {
                    return;
                }
                uint setMusic = RomVersionConfig.SwitchMap(0x80320544, 0x8031F690);
                InGameFunctionCall.WriteInGameFunctionCall(setMusic, 0, (uint)musicIndex, 0);
            };

            foreach (uint soundEffect in _soundEffects)
            {
                string soundEffectString = HexUtilities.FormatValue(soundEffect, 4);
                listBoxSoundSoundEffect.Items.Add(soundEffectString);
            }
            listBoxSoundSoundEffect.Click += (sender, e) =>
            {
                textBoxSoundSoundEffect.Text = listBoxSoundSoundEffect.SelectedItem.ToString() + "FF81";
            };
            buttonSoundPlaySoundEffect.Click += (sender, e) =>
            {
                uint setSound            = RomVersionConfig.SwitchMap(0x8031EB00, 0x8031DC78);
                uint soundArg            = RomVersionConfig.SwitchMap(0x803331F0, 0x803320E0);
                uint?soundEffectNullable = ParsingUtilities.ParseHexNullable(textBoxSoundSoundEffect.Text);
                if (!soundEffectNullable.HasValue)
                {
                    return;
                }
                uint soundEffect = soundEffectNullable.Value;
                InGameFunctionCall.WriteInGameFunctionCall(setSound, soundEffect, soundArg);
            };
        }
Exemple #6
0
        public void HookUpTeleporters()
        {
            uint mainSegmentEnd     = RomVersionConfig.SwitchMap(0x80367460, 0x803660F0);
            uint engineSegmentStart = RomVersionConfig.SwitchMap(0x80378800, 0x80378800);

            uint lastWarpNodeAddress = WatchVariableSpecialUtilities.GetWarpNodeAddresses().LastOrDefault();

            if (lastWarpNodeAddress == 0)
            {
                return;
            }

            List <uint> objAddresses = Config.ObjectSlotsManager.SelectedObjects.ConvertAll(obj => obj.Address);

            if (objAddresses.Count < 2)
            {
                return;
            }

            uint  teleporter1Address = objAddresses[0];
            uint  teleporter2Address = objAddresses[1];
            short teleporter1Id      = Config.Stream.GetShort(teleporter1Address + 0x188);
            short teleporter2Id      = Config.Stream.GetShort(teleporter2Address + 0x188);

            uint warpNode1Address = mainSegmentEnd;
            uint warpNode2Address = mainSegmentEnd + 0xC;

            byte level = Config.Stream.GetByte(MiscConfig.WarpDestinationAddress + MiscConfig.LevelOffset);
            byte area  = Config.Stream.GetByte(MiscConfig.WarpDestinationAddress + MiscConfig.AreaOffset);

            Config.Stream.SetValue((byte)teleporter1Id, warpNode1Address + 0x0);
            Config.Stream.SetValue(level, warpNode1Address + 0x1);
            Config.Stream.SetValue(area, warpNode1Address + 0x2);
            Config.Stream.SetValue((byte)teleporter2Id, warpNode1Address + 0x3);
            Config.Stream.SetValue(teleporter1Address, warpNode1Address + 0x4);
            Config.Stream.SetValue(warpNode2Address, warpNode1Address + 0x8);

            Config.Stream.SetValue((byte)teleporter2Id, warpNode2Address + 0x0);
            Config.Stream.SetValue(level, warpNode2Address + 0x1);
            Config.Stream.SetValue(area, warpNode2Address + 0x2);
            Config.Stream.SetValue((byte)teleporter1Id, warpNode2Address + 0x3);
            Config.Stream.SetValue(teleporter2Address, warpNode2Address + 0x4);
            Config.Stream.SetValue(0x00000000U, warpNode2Address + 0x8);

            Config.Stream.SetValue(warpNode1Address, lastWarpNodeAddress + 0x8);
        }
        // Inject asm in the game that executes a function call once and then removes
        // itself. Example:
        //
        // WriteInGameFunctionCall(0x8031F690, 0, 33, 0); // play music with index 33 (JP version)
        //
        // It works by changing the function pointer of the level script update function
        // to some custom asm. Since this is an indirect call to something that hasn't
        // been code yet, it will work even if the emulator isn't in pure interpreter
        // mode and caches code. A memcpy at the end removes the injected asm and also forces
        // the emulator to remove the cache so another function can be injected later.
        public static void WriteInGameFunctionCall(uint address, params uint[] arguments)
        {
            const int maxArguments = 4;

            if (arguments.Length > maxArguments)
            {
                throw new System.Exception("trying to call function with " + arguments.Length + " arguments, max is " + maxArguments);
            }
            uint       startAddress = 0x803FFF00; // some free 0-memory (hopefully)
            uint       currAddress  = startAddress;
            const uint A0           = 4;          // register index for argument 0

            // Stack frame:
            //ADDIU SP, SP, 0xFFE0
            //SW RA, 0x0014 (SP)
            WriteWords(ref currAddress, 0x27BDFFE0, 0xAFBF0014);

            // Restore level script pointer:
            //LI T0, 0x8037EB04
            //LUI AT, 0x8039
            //SW T0, 0xB900(AT)
            WriteWords(ref currAddress, 0x3C088037, 0x3508EB04, 0x3C018039, 0xAC28B900);

            // Write function call
            for (int i = 0; i < arguments.Length; i++)
            {
                uint reg = (uint)(A0 + i);
                WriteRegisterAssign(ref currAddress, reg, arguments[i]);
                //WriteWords(ref baseAddress, LUI(reg, (ushort) (arguments[i] >> 16)), ORI(reg, reg, (ushort) (arguments[i] & 0xFFFF)));
            }
            WriteWords(ref currAddress, JAL(address), 0x00000000); // NOP for delay slot

            // Erase self and return as if nothing happened:

            uint       memcpyAddress = RomVersionConfig.SwitchMap(0x803273F0, 0x803264C0);
            const uint eraseBytes    = 16 * 4 + maxArguments * 8; //fixed instructions + 2 instructions per argument

            WriteWords(ref currAddress, 0x3C1F8037, 0x37FFEB0C);  //LI RA, 0x8037EB0C
            WriteRegisterAssign(ref currAddress, A0, currAddress);
            WriteRegisterAssign(ref currAddress, A0 + 1, currAddress - 1);
            WriteWords(ref currAddress, J(memcpyAddress), 0x24060000 | eraseBytes); //ADDIU A2, R0, eraseBytes

            // Hijack level script function pointer to point to injected asm
            Config.Stream.SetValue(startAddress | 0x80000000, 0x8038B900);
        }
Exemple #8
0
        public MiscManager(string varFilePath, WatchVariableFlowLayoutPanel variableTable, Control miscControl)
            : base(varFilePath, variableTable, ALL_VAR_GROUPS, VISIBLE_VAR_GROUPS)
        {
            SplitContainer splitContainerMisc = miscControl.Controls["splitContainerMisc"] as SplitContainer;

            _checkBoxTurnOffMusic = splitContainerMisc.Panel1.Controls["checkBoxTurnOffMusic"] as CheckBox;

            GroupBox groupBoxRNGIndexTester = splitContainerMisc.Panel1.Controls["groupBoxRNGIndexTester"] as GroupBox;
            TextBox  textBoxRNGIndexTester  = groupBoxRNGIndexTester.Controls["textBoxRNGIndexTester"] as TextBox;
            Button   buttonRNGIndexTester   = groupBoxRNGIndexTester.Controls["buttonRNGIndexTester"] as Button;

            buttonRNGIndexTester.Click += (sender, e) =>
            {
                int?rngIndexNullable = ParsingUtilities.ParseIntNullable(textBoxRNGIndexTester.Text);
                if (!rngIndexNullable.HasValue)
                {
                    return;
                }
                int    rngIndex = rngIndexNullable.Value;
                ushort rngValue = RngIndexer.GetRngValue(rngIndex);
                Config.Stream.SetValue(rngValue, MiscConfig.RngAddress);
                int nextRngIndex = rngIndex + 1;
                textBoxRNGIndexTester.Text = nextRngIndex.ToString();
            };

            Button buttonMiscGoToCourse = splitContainerMisc.Panel1.Controls["buttonMiscGoToCourse"] as Button;

            buttonMiscGoToCourse.ContextMenuStrip = new ContextMenuStrip();
            foreach (CourseToGoTo courseToGoTo in _coursesToGoTo)
            {
                ToolStripMenuItem item = new ToolStripMenuItem(courseToGoTo.Name);
                item.Click += (sender, e) => InGameFunctionCall.WriteInGameFunctionCall(
                    RomVersionConfig.SwitchMap(0x8024978C, 0x8024975C), (uint)courseToGoTo.Index);
                buttonMiscGoToCourse.ContextMenuStrip.Items.Add(item);
            }
            buttonMiscGoToCourse.Click += (sender, e) => buttonMiscGoToCourse.ContextMenuStrip.Show(Cursor.Position);
        }
Exemple #9
0
        public override void InitializeTab()
        {
            base.InitializeTab();
            // Misc Image
            pictureBoxMisc.Image      = Config.ObjectAssociations.MiscImage.Value;
            panelMiscBorder.BackColor = Config.ObjectAssociations.MiscColor;
            pictureBoxMisc.BackColor  = Config.ObjectAssociations.MiscColor.Lighten(0.5);

            watchVariablePanelMisc.SetGroups(ALL_VAR_GROUPS, VISIBLE_VAR_GROUPS);


            buttonRNGIndexTester.Click += (sender, e) =>
            {
                int?rngIncrementullable = ParsingUtilities.ParseIntNullable(txtRNGIncrement.Text);
                int?rngIndexNullable    = ParsingUtilities.ParseIntNullable(textBoxRNGIndexTester.Text);
                if (!rngIndexNullable.HasValue || !rngIncrementullable.HasValue)
                {
                    return;
                }
                int    rngIndex = rngIndexNullable.Value;
                ushort rngValue = RngIndexer.GetRngValue(rngIndex);
                Config.Stream.SetValue(rngValue, MiscConfig.RngAddress);
                int nextRngIndex = rngIndex + rngIncrementullable.Value;
                textBoxRNGIndexTester.Text = nextRngIndex.ToString();
            };

            buttonMiscGoToCourse.ContextMenuStrip = new ContextMenuStrip();
            foreach (CourseToGoTo courseToGoTo in _coursesToGoTo)
            {
                ToolStripMenuItem item = new ToolStripMenuItem(courseToGoTo.Name);
                item.Click += (sender, e) => InGameFunctionCall.WriteInGameFunctionCall(
                    RomVersionConfig.SwitchMap(0x8024978C, 0x8024975C), (uint)courseToGoTo.Index);
                buttonMiscGoToCourse.ContextMenuStrip.Items.Add(item);
            }
            buttonMiscGoToCourse.Click += (sender, e) => buttonMiscGoToCourse.ContextMenuStrip.Show(Cursor.Position);
        }