125 const std::string& patch_content, std::vector<uint8_t>& rom_data,
126 const std::string& base_path) {
128 return absl::FailedPreconditionError(
"Asar not initialized");
135 fs::path temp_patch_path = fs::temp_directory_path() /
"yaze_asar_temp.asm";
137 std::ofstream temp_patch_file(temp_patch_path);
138 if (!temp_patch_file) {
139 return absl::InternalError(
"Failed to create temporary patch file");
142 temp_patch_file << patch_content;
143 temp_patch_file.close();
147 ApplyPatch(temp_patch_path.string(), rom_data, {base_path});
151 fs::remove(temp_patch_path, ec);
285 const std::string& patch_path, std::vector<uint8_t>& rom_data,
286 const std::vector<std::string>& ) {
287#ifdef YAZE_ENABLE_ASAR
289 return absl::FailedPreconditionError(
"Asar library not initialized");
296 int rom_size =
static_cast<int>(rom_data.size());
298 std::max(rom_size, 16 * 1024 * 1024);
301 if (rom_data.size() <
static_cast<size_t>(buffer_size)) {
302 rom_data.resize(buffer_size, 0);
307 asar_patch(patch_path.c_str(),
reinterpret_cast<char*
>(rom_data.data()),
308 buffer_size, &rom_size);
320 rom_data.resize(rom_size);
327 result.
symbols.push_back(symbol);
334 return absl::InternalError(
335 absl::StrFormat(
"Patch failed: %s", absl::StrJoin(
last_errors_,
"; ")));
340 return absl::UnimplementedError(
341 "ASAR library not enabled - build with YAZE_ENABLE_ASAR=1");
346 const std::string& patch_path, std::vector<uint8_t>& rom_data,
347 const std::vector<std::string>& include_paths) {
352 fs::path patch_fs = fs::absolute(patch_path, ec);
354 return absl::InvalidArgumentError(
355 absl::StrFormat(
"Failed to resolve patch path: %s", ec.message()));
357 if (!fs::exists(patch_fs)) {
358 return absl::InvalidArgumentError(
359 absl::StrFormat(
"Patch file not found: %s", patch_fs.string()));
363 if (!asar_path_opt) {
364 return absl::FailedPreconditionError(
"Asar CLI path could not be resolved");
367 fs::path patch_dir = patch_fs.parent_path();
368 fs::path patch_name = patch_fs.filename();
371 auto timestamp = std::chrono::steady_clock::now().time_since_epoch().count();
372 fs::path temp_rom = fs::temp_directory_path() /
373 (
"yaze_asar_cli_" + std::to_string(timestamp) +
".sfc");
374 fs::path temp_symbols =
375 fs::temp_directory_path() /
376 (
"yaze_asar_symbols_" + std::to_string(timestamp) +
".sym");
378 std::ofstream temp_rom_file(temp_rom, std::ios::binary);
379 if (!temp_rom_file) {
380 return absl::InternalError(
381 "Failed to create temporary ROM file for Asar CLI");
383 temp_rom_file.write(
reinterpret_cast<const char*
>(rom_data.data()),
384 static_cast<std::streamsize
>(rom_data.size()));
385 if (!temp_rom_file) {
386 return absl::InternalError(
"Failed to write temporary ROM file");
391 std::ostringstream cmd;
392 cmd <<
"\"" << *asar_path_opt <<
"\" --symbols=wla"
393 <<
" --symbols-path=\"" << temp_symbols.string() <<
"\"";
394 for (
const auto& include_path : include_paths) {
395 cmd <<
" -I\"" << include_path <<
"\"";
398 cmd <<
" \"" << patch_name.string() <<
"\" \"" << temp_rom.string()
402 fs::path original_cwd = fs::current_path(ec);
403 if (!patch_dir.empty()) {
404 fs::current_path(patch_dir, ec);
408 std::array<char, 128> buffer;
411 FILE* pipe = _popen(cmd.str().c_str(),
"r");
413 FILE* pipe = popen(cmd.str().c_str(),
"r");
416 fs::remove(temp_rom, ec);
417 fs::remove(temp_symbols, ec);
418 if (!patch_dir.empty())
419 fs::current_path(original_cwd, ec);
420 return absl::InternalError(
"popen() failed for Asar CLI");
422 while (fgets(buffer.data(), buffer.size(), pipe) !=
nullptr) {
423 output += buffer.data();
426 int exit_status = pclose(pipe);
427 int exit_code = exit_status;
428 if (exit_status != -1) {
429 if (WIFEXITED(exit_status)) {
430 exit_code = WEXITSTATUS(exit_status);
431 }
else if (WIFSIGNALED(exit_status)) {
432 exit_code = 128 + WTERMSIG(exit_status);
436 int exit_code = _pclose(pipe);
439 if (!patch_dir.empty()) {
440 fs::current_path(original_cwd, ec);
445 std::istringstream output_stream(output);
447 while (std::getline(output_stream, line)) {
448 if (line.find(
"error: ") != std::string::npos ||
449 line.find(
": error:") != std::string::npos) {
451 }
else if (line.find(
"warning: ") != std::string::npos) {
458 absl::StrFormat(
"Asar CLI exited with status %d", exit_code));
462 fs::remove(temp_rom, ec);
463 fs::remove(temp_symbols, ec);
464 return absl::InternalError(
465 absl::StrFormat(
"Patch failed: %s", absl::StrJoin(
last_errors_,
"; ")));
469 std::ifstream patched_rom(temp_rom, std::ios::binary);
471 last_errors_.push_back(
"Failed to read patched ROM from Asar CLI");
472 fs::remove(temp_rom, ec);
473 fs::remove(temp_symbols, ec);
477 std::vector<uint8_t> new_data((std::istreambuf_iterator<char>(patched_rom)),
478 std::istreambuf_iterator<char>());
479 rom_data.swap(new_data);
480 fs::remove(temp_rom, ec);
482 if (fs::exists(temp_symbols, ec)) {
484 if (!symbol_status.ok()) {
487 fs::remove(temp_symbols, ec);
497 result.
symbols.push_back(symbol);
500 result.
rom_size =
static_cast<uint32_t
>(rom_data.size());
530 std::ifstream file(symbol_path);
531 if (!file.is_open()) {
532 return absl::InvalidArgumentError(
533 absl::StrFormat(
"Cannot open symbol file: %s", symbol_path));
538 while (std::getline(file, line)) {
543 if (line.empty() || line[0] ==
'[' || line[0] ==
';')
546 size_t colon_pos = line.find(
':');
547 if (colon_pos == std::string::npos || colon_pos != 2)
550 size_t space_pos = line.find(
' ', colon_pos);
551 if (space_pos == std::string::npos)
555 std::string bank_str = line.substr(0, colon_pos);
556 std::string addr_str =
557 line.substr(colon_pos + 1, space_pos - (colon_pos + 1));
558 std::string name = line.substr(space_pos + 1);
561 name.erase(0, name.find_first_not_of(
" \t\r\n"));
562 name.erase(name.find_last_not_of(
" \t\r\n") + 1);
564 int bank = std::stoi(bank_str,
nullptr, 16);
565 int addr = std::stoi(addr_str,
nullptr, 16);
566 uint32_t full_addr = (bank << 16) | addr;
579 return absl::OkStatus();