129 const std::string& patch_content, std::vector<uint8_t>& rom_data,
130 const std::string& base_path) {
132 return absl::FailedPreconditionError(
"Asar not initialized");
141 static std::atomic<uint64_t> seq{0};
142 const auto timestamp =
143 std::chrono::steady_clock::now().time_since_epoch().count();
144 const uint64_t nonce = seq.fetch_add(1, std::memory_order_relaxed);
146 const int pid = _getpid();
148 const int pid = getpid();
150 fs::path temp_patch_path =
151 fs::temp_directory_path() /
152 absl::StrFormat(
"yaze_asar_temp_%d_%lld_%llu.asm", pid,
153 static_cast<long long>(timestamp),
154 static_cast<unsigned long long>(nonce));
156 std::ofstream temp_patch_file(temp_patch_path);
157 if (!temp_patch_file) {
158 return absl::InternalError(
"Failed to create temporary patch file");
161 temp_patch_file << patch_content;
162 temp_patch_file.close();
166 ApplyPatch(temp_patch_path.string(), rom_data, {base_path});
170 fs::remove(temp_patch_path, ec);
304 const std::string& patch_path, std::vector<uint8_t>& rom_data,
305 const std::vector<std::string>& ) {
306#ifdef YAZE_ENABLE_ASAR
308 return absl::FailedPreconditionError(
"Asar library not initialized");
315 int rom_size =
static_cast<int>(rom_data.size());
317 std::max(rom_size, 16 * 1024 * 1024);
320 if (rom_data.size() <
static_cast<size_t>(buffer_size)) {
321 rom_data.resize(buffer_size, 0);
326 asar_patch(patch_path.c_str(),
reinterpret_cast<char*
>(rom_data.data()),
327 buffer_size, &rom_size);
339 rom_data.resize(rom_size);
346 result.
symbols.push_back(symbol);
353 return absl::InternalError(
354 absl::StrFormat(
"Patch failed: %s", absl::StrJoin(
last_errors_,
"; ")));
359 return absl::UnimplementedError(
360 "ASAR library not enabled - build with YAZE_ENABLE_ASAR=1");
365 const std::string& patch_path, std::vector<uint8_t>& rom_data,
366 const std::vector<std::string>& include_paths) {
371 fs::path patch_fs = fs::absolute(patch_path, ec);
373 return absl::InvalidArgumentError(
374 absl::StrFormat(
"Failed to resolve patch path: %s", ec.message()));
376 if (!fs::exists(patch_fs)) {
377 return absl::InvalidArgumentError(
378 absl::StrFormat(
"Patch file not found: %s", patch_fs.string()));
382 if (!asar_path_opt) {
383 return absl::FailedPreconditionError(
"Asar CLI path could not be resolved");
386 fs::path patch_dir = patch_fs.parent_path();
387 fs::path patch_name = patch_fs.filename();
390 auto timestamp = std::chrono::steady_clock::now().time_since_epoch().count();
391 fs::path temp_rom = fs::temp_directory_path() /
392 (
"yaze_asar_cli_" + std::to_string(timestamp) +
".sfc");
393 fs::path temp_symbols =
394 fs::temp_directory_path() /
395 (
"yaze_asar_symbols_" + std::to_string(timestamp) +
".sym");
397 std::ofstream temp_rom_file(temp_rom, std::ios::binary);
398 if (!temp_rom_file) {
399 return absl::InternalError(
400 "Failed to create temporary ROM file for Asar CLI");
402 temp_rom_file.write(
reinterpret_cast<const char*
>(rom_data.data()),
403 static_cast<std::streamsize
>(rom_data.size()));
404 if (!temp_rom_file) {
405 return absl::InternalError(
"Failed to write temporary ROM file");
410 std::ostringstream cmd;
411 cmd <<
"\"" << *asar_path_opt <<
"\" --symbols=wla"
412 <<
" --symbols-path=\"" << temp_symbols.string() <<
"\"";
413 for (
const auto& include_path : include_paths) {
414 cmd <<
" -I\"" << include_path <<
"\"";
417 cmd <<
" \"" << patch_name.string() <<
"\" \"" << temp_rom.string()
421 fs::path original_cwd = fs::current_path(ec);
422 if (!patch_dir.empty()) {
423 fs::current_path(patch_dir, ec);
427 std::array<char, 128> buffer;
430 FILE* pipe = _popen(cmd.str().c_str(),
"r");
432 FILE* pipe = popen(cmd.str().c_str(),
"r");
435 fs::remove(temp_rom, ec);
436 fs::remove(temp_symbols, ec);
437 if (!patch_dir.empty())
438 fs::current_path(original_cwd, ec);
439 return absl::InternalError(
"popen() failed for Asar CLI");
441 while (fgets(buffer.data(), buffer.size(), pipe) !=
nullptr) {
442 output += buffer.data();
445 int exit_status = pclose(pipe);
446 int exit_code = exit_status;
447 if (exit_status != -1) {
448 if (WIFEXITED(exit_status)) {
449 exit_code = WEXITSTATUS(exit_status);
450 }
else if (WIFSIGNALED(exit_status)) {
451 exit_code = 128 + WTERMSIG(exit_status);
455 int exit_code = _pclose(pipe);
458 if (!patch_dir.empty()) {
459 fs::current_path(original_cwd, ec);
464 std::istringstream output_stream(output);
466 while (std::getline(output_stream, line)) {
467 if (line.find(
"error: ") != std::string::npos ||
468 line.find(
": error:") != std::string::npos) {
470 }
else if (line.find(
"warning: ") != std::string::npos) {
477 absl::StrFormat(
"Asar CLI error: exited with status %d", exit_code));
481 fs::remove(temp_rom, ec);
482 fs::remove(temp_symbols, ec);
483 return absl::InternalError(
484 absl::StrFormat(
"Patch failed: %s", absl::StrJoin(
last_errors_,
"; ")));
488 std::ifstream patched_rom(temp_rom, std::ios::binary);
490 last_errors_.push_back(
"Failed to read patched ROM from Asar CLI");
491 fs::remove(temp_rom, ec);
492 fs::remove(temp_symbols, ec);
496 std::vector<uint8_t> new_data((std::istreambuf_iterator<char>(patched_rom)),
497 std::istreambuf_iterator<char>());
498 rom_data.swap(new_data);
499 fs::remove(temp_rom, ec);
501 if (fs::exists(temp_symbols, ec)) {
503 if (!symbol_status.ok()) {
506 fs::remove(temp_symbols, ec);
516 result.
symbols.push_back(symbol);
519 result.
rom_size =
static_cast<uint32_t
>(rom_data.size());
549 std::ifstream file(symbol_path);
550 if (!file.is_open()) {
551 return absl::InvalidArgumentError(
552 absl::StrFormat(
"Cannot open symbol file: %s", symbol_path));
557 while (std::getline(file, line)) {
562 if (line.empty() || line[0] ==
'[' || line[0] ==
';')
565 size_t colon_pos = line.find(
':');
566 if (colon_pos == std::string::npos || colon_pos != 2)
569 size_t space_pos = line.find(
' ', colon_pos);
570 if (space_pos == std::string::npos)
574 std::string bank_str = line.substr(0, colon_pos);
575 std::string addr_str =
576 line.substr(colon_pos + 1, space_pos - (colon_pos + 1));
577 std::string name = line.substr(space_pos + 1);
580 name.erase(0, name.find_first_not_of(
" \t\r\n"));
581 name.erase(name.find_last_not_of(
" \t\r\n") + 1);
583 int bank = std::stoi(bank_str,
nullptr, 16);
584 int addr = std::stoi(addr_str,
nullptr, 16);
585 uint32_t full_addr = (bank << 16) | addr;
598 return absl::OkStatus();