16#include "absl/status/status.h"
17#include "absl/status/statusor.h"
18#include "absl/strings/str_cat.h"
19#include "absl/strings/str_format.h"
20#include "absl/strings/string_view.h"
28#include <emscripten.h>
53 rom_data.erase(rom_data.begin(), rom_data.begin() +
kHeaderSize);
55 LOG_INFO(
"Rom",
"Stripped SMC header from ROM (new size: %lu)", size);
60inline void MaybeBroadcastChange(uint32_t offset,
61 const std::vector<uint8_t>& old_bytes,
62 const std::vector<uint8_t>& new_bytes) {
63 if (new_bytes.empty())
65 auto& collab = app::platform::GetWasmCollaborationInstance();
66 if (!collab.IsConnected() || collab.IsApplyingRemoteChange()) {
69 (void)collab.BroadcastChange(offset, old_bytes, new_bytes);
78 return absl::InvalidArgumentError(
79 "Could not load ROM: parameter `filename` is empty.");
84 std::ifstream test_file(
filename_, std::ios::binary);
85 if (!test_file.is_open()) {
86 return absl::NotFoundError(absl::StrCat(
87 "ROM file does not exist or cannot be opened: ",
filename_));
89 test_file.seekg(0, std::ios::end);
90 size_ = test_file.tellg();
94 return absl::InvalidArgumentError(absl::StrFormat(
95 "ROM file too small (%zu bytes), minimum is 32KB",
size_));
98 if (!std::filesystem::exists(
filename)) {
99 return absl::NotFoundError(
100 absl::StrCat(
"ROM file does not exist: ",
filename));
106 std::ifstream file(
filename_, std::ios::binary);
107 if (!file.is_open()) {
108 return absl::NotFoundError(
109 absl::StrCat(
"Could not open ROM file: ",
filename_));
112#ifndef __EMSCRIPTEN__
116 return absl::InvalidArgumentError(absl::StrFormat(
117 "ROM file too small (%zu bytes), minimum is 32KB",
size_));
120 file.seekg(0, std::ios::end);
121 size_ = file.tellg();
127 file.seekg(0, std::ios::beg);
129 }
catch (
const std::bad_alloc&
e) {
130 return absl::ResourceExhaustedError(absl::StrFormat(
131 "Failed to allocate memory for ROM (%zu bytes)",
size_));
145 return absl::OkStatus();
151 return absl::InvalidArgumentError(
152 "Could not load ROM: parameter `data` is empty.");
162 return absl::OkStatus();
167 return absl::InternalError(
"ROM data is empty.");
176 auto now = std::chrono::system_clock::now();
177 auto now_c = std::chrono::system_clock::to_time_t(
now);
188 std::filesystem::copy_options::overwrite_existing);
189 }
catch (
const std::filesystem::filesystem_error&
e) {
190 LOG_WARN(
"Rom",
"Could not create backup: %s",
e.what());
195 auto now = std::chrono::system_clock::now();
196 auto now_c = std::chrono::system_clock::to_time_t(
now);
206 std::ofstream file(
filename.data(), std::ios::binary | std::ios::trunc);
208 return absl::InternalError(
209 absl::StrCat(
"Could not open ROM file for writing: ",
filename));
214 return absl::InternalError(
215 absl::StrCat(
"Error while writing to ROM file: ",
filename));
219 return absl::OkStatus();
224 return absl::OutOfRangeError(absl::StrFormat(
225 "Offset %d out of range (size: %d)", offset,
rom_data_.size()));
232 return absl::OutOfRangeError(
"Offset out of range");
239 return absl::OutOfRangeError(
"Offset out of range");
246 uint32_t offset, uint32_t length)
const {
248 return absl::OutOfRangeError(
"Offset and length out of range");
250 std::vector<uint8_t> result;
251 result.reserve(length);
252 for (
uint32_t i = offset; i < offset + length; i++) {
259 uint32_t tile16_ptr) {
287 return absl::OkStatus();
291 if (addr >=
static_cast<int>(
rom_data_.size())) {
292 return absl::OutOfRangeError(
"Address out of range");
300 return absl::OkStatus();
304 if (addr + 1 >=
static_cast<int>(
rom_data_.size())) {
305 return absl::OutOfRangeError(
"Address out of range");
314 {
static_cast<uint8_t>(value & 0xFF),
315 static_cast<uint8_t>((value >> 8) & 0xFF)});
317 return absl::OkStatus();
326 return absl::OutOfRangeError(
"Address out of range");
337 {
static_cast<uint8_t>(value & 0xFF),
338 static_cast<uint8_t>((value >> 8) & 0xFF),
339 static_cast<uint8_t>((value >> 16) & 0xFF)});
341 return absl::OkStatus();
345 if (addr +
static_cast<int>(
data.size()) >
347 return absl::OutOfRangeError(
"Address out of range");
349 std::vector<uint8_t> old_data;
350 old_data.reserve(
data.size());
359 return absl::OkStatus();
364 (color.
snes() & 0x7C00);
369 if (std::holds_alternative<uint8_t>(action.
value)) {
371 }
else if (std::holds_alternative<uint16_t>(action.
value) ||
372 std::holds_alternative<short>(action.
value)) {
374 }
else if (std::holds_alternative<std::vector<uint8_t>>(action.
value)) {
376 std::get<std::vector<uint8_t>>(action.
value));
377 }
else if (std::holds_alternative<gfx::SnesColor>(action.
value)) {
380 return absl::InvalidArgumentError(
"Invalid write argument type");
absl::StatusOr< std::vector< uint8_t > > ReadByteVector(uint32_t offset, uint32_t length) const
absl::Status LoadFromFile(const std::string &filename, const LoadOptions &options=LoadOptions::Defaults())
absl::StatusOr< gfx::Tile16 > ReadTile16(uint32_t tile16_id, uint32_t tile16_ptr)
absl::Status WriteColor(uint32_t address, const gfx::SnesColor &color)
absl::Status WriteByte(int addr, uint8_t value)
absl::Status WriteTile16(int tile16_id, uint32_t tile16_ptr, const gfx::Tile16 &tile)
const auto & vector() const
absl::StatusOr< uint16_t > ReadWord(int offset)
absl::Status WriteVector(int addr, std::vector< uint8_t > data)
absl::Status SaveToFile(const SaveSettings &settings)
absl::StatusOr< uint32_t > ReadLong(int offset)
std::vector< uint8_t > rom_data_
absl::Status LoadFromData(const std::vector< uint8_t > &data, const LoadOptions &options=LoadOptions::Defaults())
absl::StatusOr< uint8_t > ReadByte(int offset)
absl::Status WriteShort(int addr, uint16_t value)
project::ResourceLabelManager resource_label_manager_
absl::Status WriteWord(int addr, uint16_t value)
virtual absl::Status WriteHelper(const WriteAction &action)
absl::Status WriteLong(uint32_t addr, uint32_t value)
constexpr uint16_t snes() const
Get SNES 15-bit color.
Tile composition of four 8x8 tiles.
#define LOG_WARN(category, format,...)
#define LOG_INFO(category, format,...)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
void MaybeStripSmcHeader(std::vector< uint8_t > &rom_data, unsigned long &size)
constexpr size_t kHeaderSize
Size of the optional SMC/SFC copier header that some ROM dumps include.
constexpr size_t kBaseRomSize
Standard SNES ROM size for The Legend of Zelda: A Link to the Past (1MB)
uint16_t TileInfoToWord(TileInfo tile_info)
TileInfo WordToTileInfo(uint16_t word)
#define RETURN_IF_ERROR(expr)
bool load_resource_labels
bool LoadLabels(const std::string &filename)