20 size_t pos = path.find_last_of(
"/\\");
21 if (pos == std::string::npos)
return "";
22 return path.substr(0, pos);
26 std::ifstream f(path);
37 std::filesystem::path p(path);
38 return p.parent_path().string();
42 return std::filesystem::exists(path);
46 std::filesystem::create_directories(path);
57 : snes_(snes), rom_(rom) {
66 return absl::FailedPreconditionError(
"SNES or ROM not provided");
71 printf(
"[StateManager] ROM checksum: 0x%08X\n",
rom_checksum_);
74 if (
const char* home = std::getenv(
"HOME")) {
84 return absl::OkStatus();
90 if (!FileExists(path)) {
91 return absl::NotFoundError(
"State file not found: " + path);
96 if (FileExists(meta_path)) {
99 return absl::FailedPreconditionError(
100 "State incompatible with current ROM (checksum mismatch)");
108 printf(
"[StateManager] Generating state for room %d...\n", room_id);
140 metadata.
description =
"Room " + std::to_string(room_id);
148 const std::vector<std::pair<int, const char*>> baseline_rooms = {
149 {0x0012,
"Sanctuary"},
150 {0x0020,
"Hyrule Castle Entrance"},
151 {0x0028,
"Eastern Palace Entrance"},
152 {0x0004,
"Link's House"},
153 {0x0044,
"Desert Palace Entrance"},
154 {0x0075,
"Tower of Hera Entrance"},
157 int success_count = 0;
158 for (
const auto& [room_id, name] : baseline_rooms) {
159 printf(
"[StateManager] Generating %s (0x%04X)...\n", name, room_id);
164 printf(
"[StateManager] Warning: Failed to generate %s: %s\n", name,
165 std::string(status.message()).c_str());
169 printf(
"[StateManager] Generated %d/%zu baseline states\n", success_count,
170 baseline_rooms.size());
171 return absl::OkStatus();
176 return FileExists(path);
183 std::ifstream file(path, std::ios::binary);
185 return absl::NotFoundError(
"Metadata file not found");
189 file.read(
reinterpret_cast<char*
>(&metadata.
version),
sizeof(metadata.
version));
190 file.read(
reinterpret_cast<char*
>(&metadata.
rom_checksum),
192 file.read(
reinterpret_cast<char*
>(&metadata.
rom_region),
194 file.read(
reinterpret_cast<char*
>(&metadata.
room_id),
sizeof(metadata.
room_id));
195 file.read(
reinterpret_cast<char*
>(&metadata.
game_module),
199 file.read(
reinterpret_cast<char*
>(&desc_len),
sizeof(desc_len));
200 if (desc_len > 0 && desc_len < 1024) {
206 return absl::InternalError(
"Failed to read metadata");
215 std::string parent_path = GetParentPath(path);
216 if (!parent_path.empty()) {
217 CreateDirectories(parent_path);
222 if (!save_status.ok()) {
227 std::string meta_path = path +
".meta";
228 std::ofstream meta_file(meta_path, std::ios::binary);
230 return absl::InternalError(
"Failed to create metadata file");
233 meta_file.write(
reinterpret_cast<const char*
>(&metadata.
version),
235 meta_file.write(
reinterpret_cast<const char*
>(&metadata.
rom_checksum),
237 meta_file.write(
reinterpret_cast<const char*
>(&metadata.
rom_region),
239 meta_file.write(
reinterpret_cast<const char*
>(&metadata.
room_id),
241 meta_file.write(
reinterpret_cast<const char*
>(&metadata.
game_module),
245 meta_file.write(
reinterpret_cast<const char*
>(&desc_len),
sizeof(desc_len));
246 meta_file.write(metadata.
description.data(), desc_len);
249 return absl::InternalError(
"Failed while writing metadata");
251 printf(
"[StateManager] Saved state to %s\n", path.c_str());
252 return absl::OkStatus();
260 printf(
"[StateManager] Loaded state from %s\n", path.c_str());
261 return absl::OkStatus();
277 const int kMaxFrames = 2000;
278 for (
int i = 0; i < kMaxFrames; ++i) {
280 uint8_t
module = GetGameModule();
283 printf(
"[StateManager] Frame %d: module=0x%02X\n", i, module);
287 if (module == 0x01) {
288 printf(
"[StateManager] Reached File Select (module 0x01) at frame %d\n", i);
289 return absl::OkStatus();
293 if (module == 0x14) {
296 if (i % 60 == 0) printf(
"[StateManager] In Attract Mode, holding Start...\n");
304 else if (module == 0x00 && i > 300) {
307 if (i % 60 == 0) printf(
"[StateManager] In Intro, holding Start...\n");
315 printf(
"[StateManager] Boot timeout, module=0x%02X\n",
GetGameModule());
316 return absl::DeadlineExceededError(
"Failed to reach File Select");
323 const int kMaxFrames = 120;
324 for (
int i = 0; i < kMaxFrames; ++i) {
325 uint8_t
module = GetGameModule();
326 if (module == 0x01) {
327 printf(
"[StateManager] Navigated to file select (module 0x01)\n");
328 return absl::OkStatus();
332 if (module == 0x00 || module == 0x14) {
340 printf(
"[StateManager] File select timeout, module=0x%02X (continuing)\n",
342 return absl::OkStatus();
346 printf(
"[StateManager] Starting new game sequence...\n");
350 const int kFileSelectTimeout = 600;
351 for (
int i = 0; i < kFileSelectTimeout; ++i) {
353 uint8_t
module = GetGameModule();
355 if (module == 0x04) {
356 printf(
"[StateManager] Reached Name Entry (module 0x04) at frame %d\n", i);
361 if (module == 0x01) {
367 }
else if (module == 0x03) {
371 if (i == kFileSelectTimeout - 1) {
372 printf(
"[StateManager] Timeout waiting for Name Entry (current: 0x%02X)\n", module);
379 const int kNameEntryTimeout = 2000;
380 for (
int i = 0; i < kNameEntryTimeout; ++i) {
382 uint8_t
module = GetGameModule();
384 if (module == 0x07 || module == 0x09) {
385 printf(
"[StateManager] Started new game (module 0x%02X) at frame %d\n", module, i);
386 return absl::OkStatus();
390 if (module == 0x04) {
393 printf(
"[StateManager] Stuck in Name Entry, forcing Module 0x05 (Load Level)...\n");
407 }
else if (cycle >= 60 && cycle < 80) {
418 printf(
"[StateManager] Game start timeout, module=0x%02X\n",
GetGameModule());
419 return absl::DeadlineExceededError(
"Game failed to start");
426 return absl::OkStatus();
430 printf(
"[StateManager] WRAM teleport failed: %s, trying TAS fallback\n",
431 std::string(status.message()).c_str());
438 snes_->
Write(0x7E00A1, (room_id >> 8) & 0xFF);
441 bool is_dungeon = (room_id < 0x128);
442 snes_->
Write(0x7E001B, is_dungeon ? 0x01 : 0x00);
455 const int kMaxFrames = 600;
456 for (
int i = 0; i < kMaxFrames; ++i) {
461 snes_->
Write(0x7E00A1, (room_id >> 8) & 0xFF);
462 snes_->
Write(0x7E001B, (room_id < 0x128) ? 0x01 : 0x00);
465 uint8_t submodule =
ReadWram(0x7E0011);
466 printf(
"[StateManager] Teleport wait frame %d: module=0x%02X, sub=0x%02X, room=0x%04X\n",
471 printf(
"[StateManager] WRAM teleport to room 0x%04X successful\n",
473 return absl::OkStatus();
477 return absl::DeadlineExceededError(
478 "WRAM teleport failed for room " + std::to_string(room_id));
484 const int kMaxFrames = 600;
485 for (
int i = 0; i < kMaxFrames; ++i) {
490 printf(
"[StateManager] TAS: Room loaded: 0x%04X (target: 0x%04X)\n",
491 current_room, room_id);
492 if (current_room == room_id) {
493 return absl::OkStatus();
497 return absl::OkStatus();
501 return absl::DeadlineExceededError(
"TAS navigation timeout");
505 for (
int i = 0; i < frames; ++i) {
522 for (
int i = 0; i < frames; ++i) {
544 for (
int i = 0; i < max_frames; ++i) {
556 uint8_t
module = GetGameModule();
557 uint8_t submodule =
ReadWram(0x7E0011);
560 return (module == 0x07 || module == 0x09) && (submodule == 0x00 || submodule == 0x0F);
564 int context_id)
const {
565 std::string type_str;
571 type_str =
"overworld";
577 return state_directory_ +
"/" + type_str +
"_" + std::to_string(context_id) +
582 int context_id)
const {
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::Status saveState(const std::string &path)
void SetButtonState(int player, int button, bool pressed)
uint8_t Read(uint32_t adr)
void Reset(bool hard=false)
void Write(uint32_t adr, uint8_t val)
absl::Status loadState(const std::string &path)
absl::Status GenerateRoomState(int room_id)
bool HasCachedState(StateType type, int context_id=0) const
void WaitFrames(int frames)
bool IsStateCompatible(const StateMetadata &metadata) const
uint8_t ReadWram(uint32_t addr)
uint16_t ReadWram16(uint32_t addr)
SaveStateManager(emu::Snes *snes, Rom *rom)
std::string GetStatePath(StateType type, int context_id) const
std::string state_directory_
absl::Status LoadState(StateType type, int context_id=0)
void PressButton(int button, int frames=1)
absl::Status LoadStateFromFile(const std::string &path)
absl::StatusOr< StateMetadata > GetStateMetadata(StateType type, int context_id=0) const
absl::Status NavigateToFileSelect()
bool WaitForModule(uint8_t target, int max_frames)
uint32_t CalculateRomChecksum() const
absl::Status TeleportToRoomViaWram(int room_id)
absl::Status NavigateToRoomViaTas(int room_id)
absl::Status StartNewGame()
absl::Status Initialize()
absl::Status GenerateAllBaselineStates()
std::string GetMetadataPath(StateType type, int context_id) const
absl::Status SaveStateToFile(const std::string &path, const StateMetadata &metadata)
absl::Status NavigateToRoom(int room_id)
absl::Status BootToTitleScreen()
std::string GetParentPath(const std::string &path)
void CreateDirectories(const std::string &path)
bool FileExists(const std::string &path)
constexpr uint32_t kGameModule
constexpr uint32_t kRoomId
uint32_t CalculateCRC32(const uint8_t *data, size_t size)