8#include "absl/status/status.h"
9#include "absl/strings/match.h"
10#include "absl/strings/numbers.h"
11#include "absl/strings/str_format.h"
12#include "absl/strings/str_join.h"
13#include "absl/strings/str_split.h"
14#include "absl/strings/strip.h"
17#include "nlohmann/json.hpp"
31 std::set<uint16_t> sheets;
32 for (absl::string_view part : absl::StrSplit(value,
',')) {
33 part = absl::StripAsciiWhitespace(part);
39 const bool hex = absl::StartsWith(part,
"0x") ||
40 absl::StartsWith(part,
"0X") ||
41 absl::StartsWith(part,
"$");
44 : absl::SimpleAtoi(part, &sheet);
46 return absl::InvalidArgumentError(
47 absl::StrFormat(
"--%s: '%s' is not a sheet id (0-%zu)", flag,
50 sheets.insert(
static_cast<uint16_t
>(sheet));
60 if (rom ==
nullptr || !rom->
is_loaded()) {
61 return absl::InvalidArgumentError(
"ROM not loaded");
64 return absl::FailedPreconditionError(
65 "This ROM uses the Japanese graphics pointer tables (header byte "
66 "$7FD9 = 0); gfx-sheet-inventory supports US-table ROMs only");
71 if (
auto reserved = parser.
GetString(
"reserved"); reserved.has_value()) {
75 if (
auto flagged = parser.
GetString(
"flagged"); flagged.has_value()) {
79 if (
auto csv_path = parser.
GetString(
"labels-csv"); csv_path.has_value()) {
80 std::ifstream file(*csv_path);
82 return absl::NotFoundError(
"Cannot read --labels-csv " + *csv_path);
84 std::stringstream text;
94 nlohmann::json document = nlohmann::json::object();
97 document[
"rom_sha256"] = *sha;
99 nlohmann::json reserved_rules = nlohmann::json::object();
101 reserved_rules[absl::StrFormat(
"0x%02X", sheet)] =
102 "reserved by project config";
104 document[
"rules"] = {
105 {
"sprite_sheet",
"spriteset value + 0x73 = gfx index"},
106 {
"dungeon_spriteset",
"room header spriteset + 0x40 = spriteset index"},
107 {
"reserved", reserved_rules},
111 if (
auto out_path = parser.
GetString(
"out"); out_path.has_value()) {
112 std::ofstream out(*out_path, std::ios::trunc);
114 return absl::PermissionDeniedError(
"Cannot write --out " + *out_path);
116 out << document.dump(1) <<
"\n";
118 return absl::DataLossError(
"Failed writing --out " + *out_path);
123 for (
const auto& [key, value] : document.items()) {
126 return absl::OkStatus();
131 int unreferenced = 0;
132 for (
const auto& sheet : inventory.
sheets) {
133 const auto free = sheet.FreeBlocks();
134 free_blocks +=
static_cast<int>(free.size());
135 unreferenced += sheet.unreferenced ? 1 : 0;
136 if (!sheet.sprite_value.has_value() || free.empty()) {
140 absl::StrFormat(
"sheet 0x%02X (value 0x%02X)", sheet.gfx,
141 *sheet.sprite_value),
142 absl::StrFormat(
"%zu free 16x16 [%s]%s%s; spritesets %zu, areas %zu, "
144 free.size(), absl::StrJoin(free,
","),
145 sheet.flagged ?
" FLAGGED" :
"",
148 : (
" " + absl::StrJoin(sheet.labels,
"; ")).c_str(),
149 sheet.usage.spritesets.size(),
150 sheet.usage.ow_areas_sprite.size(),
151 sheet.usage.rooms_sprite.size()));
153 formatter.
AddField(
"sheets",
static_cast<int>(inventory.
sheets.size()));
154 formatter.
AddField(
"overworld_areas",
static_cast<int>(areas.size()));
155 formatter.
AddField(
"rooms",
static_cast<int>(rooms.size()));
156 formatter.
AddField(
"free_16x16_blocks", free_blocks);
157 formatter.
AddField(
"unreferenced_sheets", unreferenced);
158 formatter.
AddField(
"reserved_sheets",
160 return absl::OkStatus();
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
Utility for parsing common CLI argument patterns.
std::optional< std::string > GetString(const std::string &name) const
Parse a named argument (e.g., –format=json or –format json)
project::YazeProject * project_
Which graphics sheets exist, which tables use them, and which 16x16 blocks are free.
Locates graphics sheets in the ROM and writes edited sheets back.
#define ASSIGN_OR_RETURN(type_variable_name, expression)
absl::StatusOr< std::set< uint16_t > > ParseSheetList(const std::string &value, const char *flag)
zelda3::GfxSheetInventoryOptions BuildGfxSheetInventoryOptions(const project::YazeProject *project)
bool ParseHexString(absl::string_view str, uint64_t *out)
std::map< int, OverworldAreaGfxInfo > CollectOverworldAreaGfx(Rom &rom, GameData *game_data)
absl::StatusOr< GfxSheetInventory > BuildGfxSheetInventory(const Rom &rom, const std::map< int, OverworldAreaGfxInfo > &areas, const std::map< int, RoomGfxInfo > &rooms, const GfxSheetInventoryOptions &options)
std::map< int, RoomGfxInfo > CollectRoomGfx(const Rom &rom)
constexpr size_t kGfxSheetCount
bool UsesJapaneseGfxSheetTables(const Rom &rom)
std::map< uint8_t, std::set< std::string > > ParseSpritesetLabelCsv(const std::string &csv_text)
nlohmann::json GfxSheetInventoryToJson(const GfxSheetInventory &inventory)
absl::StatusOr< std::string > ComputeSha256(const std::string &file_path)
std::set< uint16_t > flagged_sheets
std::map< uint8_t, std::set< std::string > > sprite_value_labels
std::set< uint16_t > reserved_sheets
std::vector< GfxSheetInventoryEntry > sheets