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())
return;
64 auto& collab = app::platform::GetWasmCollaborationInstance();
65 if (!collab.IsConnected() || collab.IsApplyingRemoteChange()) {
68 (void)collab.BroadcastChange(offset, old_bytes, new_bytes);
77 return absl::InvalidArgumentError(
78 "Could not load ROM: parameter `filename` is empty.");
83 std::ifstream test_file(
filename_, std::ios::binary);
84 if (!test_file.is_open()) {
85 return absl::NotFoundError(
86 absl::StrCat(
"ROM file does not exist or cannot be opened: ",
filename_));
88 test_file.seekg(0, std::ios::end);
89 size_ = test_file.tellg();
93 return absl::InvalidArgumentError(absl::StrFormat(
94 "ROM file too small (%zu bytes), minimum is 32KB",
size_));
97 if (!std::filesystem::exists(
filename)) {
98 return absl::NotFoundError(
99 absl::StrCat(
"ROM file does not exist: ",
filename));
105 std::ifstream file(
filename_, std::ios::binary);
106 if (!file.is_open()) {
107 return absl::NotFoundError(
108 absl::StrCat(
"Could not open ROM file: ",
filename_));
111#ifndef __EMSCRIPTEN__
115 return absl::InvalidArgumentError(absl::StrFormat(
116 "ROM file too small (%zu bytes), minimum is 32KB",
size_));
119 file.seekg(0, std::ios::end);
120 size_ = file.tellg();
126 file.seekg(0, std::ios::beg);
128 }
catch (
const std::bad_alloc&
e) {
129 return absl::ResourceExhaustedError(absl::StrFormat(
130 "Failed to allocate memory for ROM (%zu bytes)",
size_));
144 return absl::OkStatus();
150 return absl::InvalidArgumentError(
151 "Could not load ROM: parameter `data` is empty.");
161 return absl::OkStatus();
166 return absl::InternalError(
"ROM data is empty.");
175 auto now = std::chrono::system_clock::now();
176 auto now_c = std::chrono::system_clock::to_time_t(
now);
187 std::filesystem::copy_options::overwrite_existing);
188 }
catch (
const std::filesystem::filesystem_error&
e) {
189 LOG_WARN(
"Rom",
"Could not create backup: %s",
e.what());
194 auto now = std::chrono::system_clock::now();
195 auto now_c = std::chrono::system_clock::to_time_t(
now);
205 std::ofstream file(
filename.data(), std::ios::binary | std::ios::trunc);
207 return absl::InternalError(
208 absl::StrCat(
"Could not open ROM file for writing: ",
filename));
213 return absl::InternalError(
214 absl::StrCat(
"Error while writing to ROM file: ",
filename));
218 return absl::OkStatus();
223 return absl::OutOfRangeError(absl::StrFormat(
"Offset %d out of range (size: %d)", offset,
rom_data_.size()));
230 return absl::OutOfRangeError(
"Offset out of range");
237 return absl::OutOfRangeError(
"Offset out of range");
244 uint32_t offset, uint32_t length)
const {
246 return absl::OutOfRangeError(
"Offset and length out of range");
248 std::vector<uint8_t> result;
249 result.reserve(length);
250 for (
uint32_t i = offset; i < offset + length; i++) {
283 return absl::OkStatus();
287 if (addr >=
static_cast<int>(
rom_data_.size())) {
288 return absl::OutOfRangeError(
"Address out of range");
296 return absl::OkStatus();
300 if (addr + 1 >=
static_cast<int>(
rom_data_.size())) {
301 return absl::OutOfRangeError(
"Address out of range");
310 {
static_cast<uint8_t>(value & 0xFF),
311 static_cast<uint8_t>((value >> 8) & 0xFF)});
313 return absl::OkStatus();
322 return absl::OutOfRangeError(
"Address out of range");
333 {
static_cast<uint8_t>(value & 0xFF),
334 static_cast<uint8_t>((value >> 8) & 0xFF),
335 static_cast<uint8_t>((value >> 16) & 0xFF)});
337 return absl::OkStatus();
341 if (addr +
static_cast<int>(
data.size()) >
static_cast<int>(
rom_data_.size())) {
342 return absl::OutOfRangeError(
"Address out of range");
344 std::vector<uint8_t> old_data;
345 old_data.reserve(
data.size());
354 return absl::OkStatus();
359 (color.
snes() & 0x7C00);
364 if (std::holds_alternative<uint8_t>(action.
value)) {
366 }
else if (std::holds_alternative<uint16_t>(action.
value) ||
367 std::holds_alternative<short>(action.
value)) {
369 }
else if (std::holds_alternative<std::vector<uint8_t>>(action.
value)) {
371 std::get<std::vector<uint8_t>>(action.
value));
372 }
else if (std::holds_alternative<gfx::SnesColor>(action.
value)) {
375 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)