yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_test_harness.cc
Go to the documentation of this file.
1#include <filesystem>
2#include <fstream>
3#include <iomanip>
4#include <iostream>
5#include <vector>
6
7#include "app/emu/snes.h"
8#include "app/rom.h"
9
10using namespace yaze;
11
22 public:
23 explicit DungeonTestHarness(const std::string& rom_path)
24 : rom_path_(rom_path) {}
25
26 absl::Status GenerateHarnessState(const std::string& output_path) {
27 // Load ROM
28 Rom rom;
30 auto rom_data = rom.vector();
31
32 // Initialize SNES
33 emu::Snes snes;
34 snes.Init(rom_data);
35 snes.Reset(false);
36
37 auto& cpu = snes.cpu();
38 auto& ppu = snes.ppu();
39
40 // Run emulator until the main game loop is reached
41 int max_cycles = 15000000; // 15 million cycles should be plenty
42 int cycles = 0;
43 while (cycles < max_cycles) {
44 snes.RunCycle();
45 cycles++;
46 if (cpu.PB == 0x00 && cpu.PC == 0x8034) {
47 break; // Reached MainGameLoop
48 }
49 }
50
51 if (cycles >= max_cycles) {
52 return absl::InternalError("Emulator timed out; did not reach main game loop.");
53 }
54
55 std::ofstream out_file(output_path);
56 if (!out_file.is_open()) {
57 return absl::InternalError("Failed to open output file: " + output_path);
58 }
59
60 // Write header
61 out_file << "// =============================================================================" << std::endl;
62 out_file << "// YAZE Dungeon Test Harness State - Generated from: " << rom_path_ << std::endl;
63 out_file << "// Generated on: " << __DATE__ << " " << __TIME__ << std::endl;
64 out_file << "// =============================================================================" << std::endl;
65 out_file << std::endl;
66 out_file << "#pragma once" << std::endl;
67 out_file << std::endl;
68 out_file << "#include <cstdint>" << std::endl;
69 out_file << "#include <array>" << std::endl;
70 out_file << std::endl;
71 out_file << "namespace yaze {" << std::endl;
72 out_file << "namespace emu {" << std::endl;
73 out_file << std::endl;
74
75 // Write WRAM state
76 out_file << "constexpr std::array<uint8_t, 0x20000> kInitialWRAMState = {{" << std::endl;
77 for (int i = 0; i < 0x20000; ++i) {
78 if (i % 16 == 0) out_file << " ";
79 out_file << "0x" << std::hex << std::setw(2) << std::setfill('0')
80 << static_cast<int>(snes.Read(0x7E0000 + i));
81 if (i < 0x1FFFF) out_file << ", ";
82 if (i % 16 == 15) out_file << std::endl;
83 }
84 out_file << "}};" << std::endl << std::endl;
85
86 // Write CPU/PPU register state
87 out_file << "// =============================================================================" << std::endl;
88 out_file << "// Initial Register States" << std::endl;
89 out_file << "// =============================================================================" << std::endl;
90 out_file << std::endl;
91
92 out_file << "struct InitialPpuState {" << std::endl;
93 out_file << " uint8_t inidisp = 0x" << std::hex << ppu.Read(0x2100, false) << ";" << std::endl;
94 out_file << " uint8_t objsel = 0x" << std::hex << ppu.Read(0x2101, false) << ";" << std::endl;
95 out_file << " uint8_t bgmode = 0x" << std::hex << ppu.Read(0x2105, false) << ";" << std::endl;
96 out_file << " uint8_t mosaic = 0x" << std::hex << ppu.Read(0x2106, false) << ";" << std::endl;
97 out_file << " uint8_t tm = 0x" << std::hex << ppu.Read(0x212C, false) << ";" << std::endl;
98 out_file << " uint8_t ts = 0x" << std::hex << ppu.Read(0x212D, false) << ";" << std::endl;
99 out_file << " uint8_t cgwsel = 0x" << std::hex << ppu.Read(0x2130, false) << ";" << std::endl;
100 out_file << " uint8_t cgadsub = 0x" << std::hex << ppu.Read(0x2131, false) << ";" << std::endl;
101 out_file << " uint8_t setini = 0x" << std::hex << ppu.Read(0x2133, false) << ";" << std::endl;
102 out_file << "};" << std::endl << std::endl;
103
104 out_file << "} // namespace emu" << std::endl;
105 out_file << "} // namespace yaze" << std::endl;
106
107 return absl::OkStatus();
108 }
109
110 private:
111 std::string rom_path_;
112};
113
114int main(int argc, char* argv[]) {
115 if (argc != 3) {
116 std::cerr << "Usage: " << argv[0] << " <rom_path> <output_path>" << std::endl;
117 return 1;
118 }
119
120 std::string rom_path = argv[1];
121 std::string output_path = argv[2];
122
123 if (!std::filesystem::exists(rom_path)) {
124 std::cerr << "Error: ROM file not found: " << rom_path << std::endl;
125 return 1;
126 }
127
128 DungeonTestHarness harness(rom_path);
129 auto status = harness.GenerateHarnessState(output_path);
130
131 if (status.ok()) {
132 std::cout << "Successfully generated dungeon harness state from " << rom_path
133 << " to " << output_path << std::endl;
134 return 0;
135 } else {
136 std::cerr << "Error generating harness state: " << status.message() << std::endl;
137 return 1;
138 }
139}
Dumps the state of WRAM and CPU/PPU registers after initialization.
absl::Status GenerateHarnessState(const std::string &output_path)
DungeonTestHarness(const std::string &rom_path)
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:71
absl::Status LoadFromFile(const std::string &filename, bool z3_load=true)
Definition rom.cc:289
auto vector() const
Definition rom.h:207
auto ppu() -> Ppu &
Definition snes.h:72
uint8_t Read(uint32_t adr)
Definition snes.cc:463
void Reset(bool hard=false)
Definition snes.cc:54
auto cpu() -> Cpu &
Definition snes.h:71
void Init(std::vector< uint8_t > &rom_data)
Definition snes.cc:36
void RunCycle()
Definition snes.cc:173
#define RETURN_IF_ERROR(expression)
Definition macro.h:53
Main namespace for the application.