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"
29#include <emscripten.h>
33#if !defined(__EMSCRIPTEN__)
63 rom_data.erase(rom_data.begin(), rom_data.begin() +
kHeaderSize);
65 LOG_INFO(
"Rom",
"Stripped SMC header from ROM (new size: %lu)", size);
70 std::string timestamp = std::ctime(&now_c);
71 timestamp.erase(std::remove(timestamp.begin(), timestamp.end(),
'\n'),
73 std::replace(timestamp.begin(), timestamp.end(),
' ',
'_');
77 for (
char& ch : timestamp) {
98inline void MaybeBroadcastChange(uint32_t offset,
99 const std::vector<uint8_t>& old_bytes,
100 const std::vector<uint8_t>& new_bytes) {
101 if (new_bytes.empty())
103 auto& collab = app::platform::GetWasmCollaborationInstance();
104 if (!collab.IsConnected() || collab.IsApplyingRemoteChange()) {
107 (void)collab.BroadcastChange(offset, old_bytes, new_bytes);
111#if !defined(__EMSCRIPTEN__)
116 CreateFileW(path.wstring().c_str(), GENERIC_WRITE,
117 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
118 nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
nullptr);
119 if (handle == INVALID_HANDLE_VALUE) {
122 (void)FlushFileBuffers(handle);
123 (void)CloseHandle(handle);
125 int fd = open(path.c_str(), O_RDONLY);
139 std::filesystem::path dir_path = file_path.parent_path();
140 if (dir_path.empty()) {
143 int fd = open(dir_path.c_str(), O_RDONLY);
158 return absl::InvalidArgumentError(
159 "Could not load ROM: parameter `filename` is empty.");
164 std::ifstream test_file(
filename_, std::ios::binary);
165 if (!test_file.is_open()) {
166 return absl::NotFoundError(absl::StrCat(
167 "ROM file does not exist or cannot be opened: ",
filename_));
169 test_file.seekg(0, std::ios::end);
170 size_ = test_file.tellg();
174 return absl::InvalidArgumentError(absl::StrFormat(
175 "ROM file too small (%zu bytes), minimum is 32KB",
size_));
178 if (!std::filesystem::exists(
filename)) {
179 return absl::NotFoundError(
180 absl::StrCat(
"ROM file does not exist: ",
filename));
186 std::ifstream file(
filename_, std::ios::binary);
187 if (!file.is_open()) {
188 return absl::NotFoundError(
189 absl::StrCat(
"Could not open ROM file: ",
filename_));
192#ifndef __EMSCRIPTEN__
196 return absl::InvalidArgumentError(absl::StrFormat(
197 "ROM file too small (%zu bytes), minimum is 32KB",
size_));
200 file.seekg(0, std::ios::end);
201 size_ = file.tellg();
207 file.seekg(0, std::ios::beg);
209 }
catch (
const std::bad_alloc&
e) {
210 return absl::ResourceExhaustedError(absl::StrFormat(
211 "Failed to allocate memory for ROM (%zu bytes)",
size_));
236 char buffer[22] = {0};
237 for (
int i = 0; i < 21; ++i) {
239 buffer[i] = (
c >= 32 &&
c <= 126) ?
c :
' ';
241 title_ = std::string(buffer);
252 return absl::OkStatus();
258 return absl::InvalidArgumentError(
259 "Could not load ROM: parameter `data` is empty.");
273 char buffer[22] = {0};
274 for (
int i = 0; i < 21; ++i) {
276 buffer[i] = (
c >= 32 &&
c <= 126) ?
c :
' ';
278 title_ = std::string(buffer);
288 return absl::OkStatus();
293 return absl::InternalError(
"ROM data is empty.");
302 auto now = std::chrono::system_clock::now();
303 auto now_c = std::chrono::system_clock::to_time_t(
now);
305 absl::StrCat(
filename,
"_backup_", MakeSafeTimestamp(
now_c));
309 std::filesystem::copy_options::overwrite_existing);
310 }
catch (
const std::filesystem::filesystem_error&
e) {
311 LOG_WARN(
"Rom",
"Could not create backup: %s",
e.what());
316 auto now = std::chrono::system_clock::now();
317 auto now_c = std::chrono::system_clock::to_time_t(
now);
329 std::ofstream file(
temp_path, std::ios::binary | std::ios::trunc);
331 return absl::InternalError(absl::StrCat(
332 "Could not open temp ROM file for writing: ",
temp_path.string()));
339 std::error_code
rm_ec;
341 return absl::InternalError(
342 absl::StrCat(
"Error while writing ROM file: ",
temp_path.string()));
347#if !defined(__EMSCRIPTEN__)
365 std::error_code
rm_ec;
367 return absl::InternalError(absl::StrCat(
368 "Failed to move temp ROM into place: ",
rename_ec.message()));
371#if !defined(__EMSCRIPTEN__)
377 return absl::OkStatus();
381 if (
fence ==
nullptr) {
388 if (
fence ==
nullptr) {
401 LOG_WARN(
"Rom",
"Popped non-top write fence (mismatched scope)");
405 LOG_WARN(
"Rom",
"PopWriteFence called for unknown fence");
410 return absl::OutOfRangeError(absl::StrFormat(
411 "Offset %d out of range (size: %d)", offset,
rom_data_.size()));
418 return absl::OutOfRangeError(
"Offset out of range");
425 return absl::OutOfRangeError(
"Offset out of range");
432 uint32_t offset, uint32_t length)
const {
434 return absl::OutOfRangeError(
"Offset and length out of range");
436 std::vector<uint8_t> result;
437 result.reserve(length);
438 for (
uint32_t i = offset; i < offset + length; i++) {
445 uint32_t tile16_ptr) {
473 return absl::OkStatus();
478 return absl::OutOfRangeError(
"Address out of range");
492 return absl::OkStatus();
497 return absl::OutOfRangeError(
"Address out of range");
509 {
static_cast<uint8_t>(value & 0xFF),
510 static_cast<uint8_t>((value >> 8) & 0xFF)});
515 return absl::OkStatus();
524 return absl::OutOfRangeError(
"Address out of range");
538 {
static_cast<uint8_t>(value & 0xFF),
539 static_cast<uint8_t>((value >> 8) & 0xFF),
540 static_cast<uint8_t>((value >> 16) & 0xFF)});
543 fence->RecordWrite(addr, 3);
545 return absl::OkStatus();
550 return absl::OutOfRangeError(
"Address out of range");
552 if (addr +
static_cast<int>(
data.size()) >
554 return absl::OutOfRangeError(
"Address out of range");
561 std::vector<uint8_t> old_data;
562 old_data.reserve(
data.size());
575 return absl::OkStatus();
580 (color.
snes() & 0x7C00);
585 if (std::holds_alternative<uint8_t>(action.
value)) {
587 }
else if (std::holds_alternative<uint16_t>(action.
value) ||
588 std::holds_alternative<short>(action.
value)) {
590 }
else if (std::holds_alternative<std::vector<uint8_t>>(action.
value)) {
592 std::get<std::vector<uint8_t>>(action.
value));
593 }
else if (std::holds_alternative<gfx::SnesColor>(action.
value)) {
596 return absl::InvalidArgumentError(
"Invalid write argument type");
absl::StatusOr< std::vector< uint8_t > > ReadByteVector(uint32_t offset, uint32_t length) const
void PushWriteFence(rom::WriteFence *fence)
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)
void PopWriteFence(rom::WriteFence *fence)
absl::Status WriteByte(int addr, uint8_t value)
absl::StatusOr< uint8_t > ReadByte(int offset) const
absl::Status WriteTile16(int tile16_id, uint32_t tile16_ptr, const gfx::Tile16 &tile)
const auto & vector() const
absl::Status WriteVector(int addr, std::vector< uint8_t > data)
absl::Status SaveToFile(const SaveSettings &settings)
absl::StatusOr< uint16_t > ReadWord(int offset) const
std::vector< uint8_t > rom_data_
std::vector< rom::WriteFence * > write_fence_stack_
absl::Status LoadFromData(const std::vector< uint8_t > &data, const LoadOptions &options=LoadOptions::Defaults())
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)
absl::StatusOr< uint32_t > ReadLong(int offset) const
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)
std::string MakeSafeTimestamp(std::time_t now_c)
void BestEffortFsyncFile(const std::filesystem::path &path)
void BestEffortFsyncParentDir(const std::filesystem::path &file_path)
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)