Extend RGBASM and RGBLINK verbosity flags to have multiple levels like RGBGFX (#1772)

This commit is contained in:
Rangi
2025-08-02 17:10:10 -04:00
committed by GitHub
parent b51056f743
commit 752b273aec
28 changed files with 688 additions and 347 deletions

View File

@@ -74,7 +74,8 @@ rgbasm_obj := \
src/extern/utf8decoder.o \ src/extern/utf8decoder.o \
src/linkdefs.o \ src/linkdefs.o \
src/opmath.o \ src/opmath.o \
src/util.o src/util.o \
src/verbosity.o
src/asm/lexer.o src/asm/main.o: src/asm/parser.hpp src/asm/lexer.o src/asm/main.o: src/asm/parser.hpp
@@ -95,7 +96,8 @@ rgblink_obj := \
src/extern/utf8decoder.o \ src/extern/utf8decoder.o \
src/linkdefs.o \ src/linkdefs.o \
src/opmath.o \ src/opmath.o \
src/util.o src/util.o \
src/verbosity.o
src/link/lexer.o src/link/main.o: src/link/script.hpp src/link/lexer.o src/link/main.o: src/link/script.hpp
@@ -117,7 +119,8 @@ rgbgfx_obj := \
src/gfx/reverse.o \ src/gfx/reverse.o \
src/gfx/rgba.o \ src/gfx/rgba.o \
src/gfx/warning.o \ src/gfx/warning.o \
src/util.o src/util.o \
src/verbosity.o
rgbasm: ${rgbasm_obj} rgbasm: ${rgbasm_obj}
$Q${CXX} ${REALLDFLAGS} -o $@ ${rgbasm_obj} ${REALCXXFLAGS} src/version.cpp $Q${CXX} ${REALLDFLAGS} -o $@ ${rgbasm_obj} ${REALCXXFLAGS} src/version.cpp

View File

@@ -39,7 +39,7 @@ local args=(
'(- : * options)'{-h,--help}'[Print help text and exit]' '(- : * options)'{-h,--help}'[Print help text and exit]'
'(-E --export-all)'{-E,--export-all}'[Export all symbols]' '(-E --export-all)'{-E,--export-all}'[Export all symbols]'
'(-v --verbose)'{-v,--verbose}'[Print additional messages regarding progression]' '(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
-w'[Disable all warnings]' -w'[Disable all warnings]'
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:' '(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'

View File

@@ -49,6 +49,8 @@ struct FileStackNode {
struct MacroArgs; struct MacroArgs;
void fstk_VerboseOutputConfig();
bool fstk_DumpCurrent(); bool fstk_DumpCurrent();
std::shared_ptr<FileStackNode> fstk_GetFileStack(); std::shared_ptr<FileStackNode> fstk_GetFileStack();
std::shared_ptr<std::string> fstk_GetUniqueIDStr(); std::shared_ptr<std::string> fstk_GetUniqueIDStr();

View File

@@ -7,6 +7,8 @@
#include <stdio.h> #include <stdio.h>
#include <string> #include <string>
#include "verbosity.hpp"
enum MissingInclude { enum MissingInclude {
INC_ERROR, // A missing included file is an error that halts assembly INC_ERROR, // A missing included file is an error that halts assembly
GEN_EXIT, // A missing included file is assumed to be generated; exit normally GEN_EXIT, // A missing included file is assumed to be generated; exit normally
@@ -14,11 +16,11 @@ enum MissingInclude {
}; };
struct Options { struct Options {
bool exportAll = false; // -E
uint8_t fixPrecision = 16; // -Q uint8_t fixPrecision = 16; // -Q
size_t maxRecursionDepth = 64; // -r size_t maxRecursionDepth = 64; // -r
char binDigits[2] = {'0', '1'}; // -b char binDigits[2] = {'0', '1'}; // -b
char gfxDigits[4] = {'0', '1', '2', '3'}; // -g char gfxDigits[4] = {'0', '1', '2', '3'}; // -g
bool verbose = false; // -v
FILE *dependFile = nullptr; // -M FILE *dependFile = nullptr; // -M
std::string targetFileName; // -MQ, -MT std::string targetFileName; // -MQ, -MT
MissingInclude missingIncludeState = INC_ERROR; // -MC, -MG MissingInclude missingIncludeState = INC_ERROR; // -MC, -MG
@@ -42,11 +44,4 @@ struct Options {
extern Options options; extern Options options;
#define verbosePrint(...) \
do { \
if (options.verbose) { \
fprintf(stderr, __VA_ARGS__); \
} \
} while (0)
#endif // RGBDS_ASM_MAIN_HPP #endif // RGBDS_ASM_MAIN_HPP

View File

@@ -71,7 +71,6 @@ struct Symbol {
void sym_ForEach(void (*callback)(Symbol &)); void sym_ForEach(void (*callback)(Symbol &));
void sym_SetExportAll(bool set);
Symbol *sym_AddLocalLabel(std::string const &symName); Symbol *sym_AddLocalLabel(std::string const &symName);
Symbol *sym_AddLabel(std::string const &symName); Symbol *sym_AddLabel(std::string const &symName);
Symbol *sym_AddAnonLabel(); Symbol *sym_AddAnonLabel();

View File

@@ -11,6 +11,7 @@
#include <vector> #include <vector>
#include "helpers.hpp" #include "helpers.hpp"
#include "verbosity.hpp"
#include "gfx/rgba.hpp" #include "gfx/rgba.hpp"
@@ -20,7 +21,6 @@ struct Options {
bool allowMirroringX = false; // -X, -m bool allowMirroringX = false; // -X, -m
bool allowMirroringY = false; // -Y, -m bool allowMirroringY = false; // -Y, -m
bool columnMajor = false; // -Z bool columnMajor = false; // -Z
uint8_t verbosity = 0; // -v
std::string attrmap{}; // -a, -A std::string attrmap{}; // -a, -A
std::optional<Rgba> bgColor{}; // -B std::optional<Rgba> bgColor{}; // -B
@@ -42,6 +42,7 @@ struct Options {
uint16_t height; uint16_t height;
uint32_t right() const { return left + width * 8; } uint32_t right() const { return left + width * 8; }
uint32_t bottom() const { return top + height * 8; } uint32_t bottom() const { return top + height * 8; }
bool specified() const { return left || top || width || height; }
} inputSlice{0, 0, 0, 0}; // -L (margins in clockwise order, like CSS) } inputSlice{0, 0, 0, 0}; // -L (margins in clockwise order, like CSS)
uint8_t basePalID = 0; // -l uint8_t basePalID = 0; // -l
std::array<uint16_t, 2> maxNbTiles{UINT16_MAX, 0}; // -N std::array<uint16_t, 2> maxNbTiles{UINT16_MAX, 0}; // -N
@@ -56,18 +57,6 @@ struct Options {
std::string input{}; // positional arg std::string input{}; // positional arg
// clang-format off: vertically align values
static constexpr uint8_t VERB_NONE = 0; // Normal, no extra output
static constexpr uint8_t VERB_CFG = 1; // Print configuration after parsing options
static constexpr uint8_t VERB_LOG_ACT = 2; // Log actions before doing them
static constexpr uint8_t VERB_INTERM = 3; // Print some intermediate results
static constexpr uint8_t VERB_DEBUG = 4; // Internals are logged
static constexpr uint8_t VERB_TRACE = 5; // Step-by-step algorithm details
static constexpr uint8_t VERB_VVVVVV = 6; // What, can't I have a little fun?
// clang-format on
[[gnu::format(printf, 3, 4)]]
void verbosePrint(uint8_t level, char const *fmt, ...) const;
mutable bool hasTransparentPixels = false; mutable bool hasTransparentPixels = false;
uint8_t maxOpaqueColors() const { return nbColorsPerPal - hasTransparentPixels; } uint8_t maxOpaqueColors() const { return nbColorsPerPal - hasTransparentPixels; }

View File

@@ -10,6 +10,7 @@
#include <vector> #include <vector>
#include "linkdefs.hpp" #include "linkdefs.hpp"
#include "verbosity.hpp"
struct Options { struct Options {
bool isDmgMode; // -d bool isDmgMode; // -d
@@ -25,20 +26,12 @@ struct Options {
uint16_t scrambleWRAMX; uint16_t scrambleWRAMX;
uint16_t scrambleSRAM; uint16_t scrambleSRAM;
bool is32kMode; // -t bool is32kMode; // -t
bool beVerbose; // -v
bool isWRAM0Mode; // -w bool isWRAM0Mode; // -w
bool disablePadding; // -x bool disablePadding; // -x
}; };
extern Options options; extern Options options;
#define verbosePrint(...) \
do { \
if (options.beVerbose) { \
fprintf(stderr, __VA_ARGS__); \
} \
} while (0)
struct FileStackNode { struct FileStackNode {
FileStackNodeType type; FileStackNodeType type;
std::variant< std::variant<

29
include/verbosity.hpp Normal file
View File

@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_VERBOSITY_HPP
#define RGBDS_VERBOSITY_HPP
// This macro does not evaluate its arguments unless the condition is true.
#define verbosePrint(level, ...) \
do { \
if (checkVerbosity(level)) { \
fprintf(stderr, __VA_ARGS__); \
} \
} while (0)
enum Verbosity {
VERB_NONE, // 0. Default, no extra output
VERB_CONFIG, // 1. Basic configuration, after parsing CLI options
VERB_NOTICE, // 2. Before significant actions
VERB_INFO, // 3. Some intermediate action results
VERB_DEBUG, // 4. Internals useful for debugging
VERB_TRACE, // 5. Step-by-step algorithm details
VERB_VVVVVV, // 6. What, can't I have a little fun?
};
void incrementVerbosity();
bool checkVerbosity(Verbosity level);
void printVVVVVVerbosity();
#endif // RGBDS_VERBOSITY_HPP

View File

@@ -233,6 +233,24 @@ below).
Print the version of the program and exit. Print the version of the program and exit.
.It Fl v , Fl \-verbose .It Fl v , Fl \-verbose
Be verbose. Be verbose.
The verbosity level is increased by one each time the flag is specified, with each level including the previous:
.Bl -enum -compact
.It
Print the
.Nm
configuration before taking actions.
.It
Print a notice before significant actions.
.It
Print some of the actions' intermediate results.
.It
Print some internal debug information.
.It
Print detailed internal information.
.El
The verbosity level does not go past 6.
.Pp
Note that verbose output is only intended to be consumed by humans, and may change without notice between RGBDS releases; relying on those for scripts is not advised.
.It Fl W Ar warning , Fl \-warning Ar warning .It Fl W Ar warning , Fl \-warning Ar warning
Set warning flag Set warning flag
.Ar warning . .Ar warning .

View File

@@ -382,14 +382,17 @@ Be verbose.
The verbosity level is increased by one each time the flag is specified, with each level including the previous: The verbosity level is increased by one each time the flag is specified, with each level including the previous:
.Bl -enum -compact .Bl -enum -compact
.It .It
Print the
.Nm .Nm
prints out its configuration before doing anything. configuration before taking actions.
.It .It
A generic message is printed before doing most actions. Print a notice before significant actions.
.It .It
Some of the actions' intermediate results are printed. Print some of the actions' intermediate results.
.It .It
Some internal debug printing is enabled. Print some internal debug information.
.It
Print detailed internal information.
.El .El
The verbosity level does not go past 6. The verbosity level does not go past 6.
.Pp .Pp

View File

@@ -114,7 +114,25 @@ Useful for ROMs that fit in 32 KiB.
.It Fl V , Fl \-version .It Fl V , Fl \-version
Print the version of the program and exit. Print the version of the program and exit.
.It Fl v , Fl \-verbose .It Fl v , Fl \-verbose
Verbose: enable printing more information to standard error. Be verbose.
The verbosity level is increased by one each time the flag is specified, with each level including the previous:
.Bl -enum -compact
.It
Print the
.Nm
configuration before taking actions.
.It
Print a notice before significant actions.
.It
Print some of the actions' intermediate results.
.It
Print some internal debug information.
.It
Print detailed internal information.
.El
The verbosity level does not go past 6.
.Pp
Note that verbose output is only intended to be consumed by humans, and may change without notice between RGBDS releases; relying on those for scripts is not advised.
.It Fl W Ar warning , Fl \-warning Ar warning .It Fl W Ar warning , Fl \-warning Ar warning
Set warning flag Set warning flag
.Ar warning . .Ar warning .

View File

@@ -53,6 +53,7 @@ set(rgbasm_src
"linkdefs.cpp" "linkdefs.cpp"
"opmath.cpp" "opmath.cpp"
"util.cpp" "util.cpp"
"verbosity.cpp"
) )
set(rgblink_src set(rgblink_src
@@ -72,6 +73,7 @@ set(rgblink_src
"linkdefs.cpp" "linkdefs.cpp"
"opmath.cpp" "opmath.cpp"
"util.cpp" "util.cpp"
"verbosity.cpp"
) )
set(rgbfix_src set(rgbfix_src
@@ -92,6 +94,7 @@ set(rgbgfx_src
"gfx/rgba.cpp" "gfx/rgba.cpp"
"gfx/warning.cpp" "gfx/warning.cpp"
"util.cpp" "util.cpp"
"verbosity.cpp"
) )
foreach(PROG "asm" "fix" "gfx" "link") foreach(PROG "asm" "fix" "gfx" "link")

View File

@@ -15,6 +15,7 @@
#include "helpers.hpp" #include "helpers.hpp"
#include "linkdefs.hpp" #include "linkdefs.hpp"
#include "platform.hpp" // S_ISDIR (stat macro) #include "platform.hpp" // S_ISDIR (stat macro)
#include "verbosity.hpp"
#include "asm/lexer.hpp" #include "asm/lexer.hpp"
#include "asm/macro.hpp" #include "asm/macro.hpp"
@@ -88,6 +89,28 @@ bool fstk_DumpCurrent() {
return true; return true;
} }
// LCOV_EXCL_START
void fstk_VerboseOutputConfig() {
assume(checkVerbosity(VERB_CONFIG));
// -I/--include
if (includePaths.size() > 1) {
fputs("\tInclude file paths:\n", stderr);
for (std::string const &path : includePaths) {
if (!path.empty()) {
fprintf(stderr, "\t - %s\n", path.c_str());
}
}
}
// -P/--preinclude
if (!preIncludeNames.empty()) {
fputs("\tPreincluded files:\n", stderr);
for (std::string const &name : preIncludeNames) {
fprintf(stderr, "\t - %s\n", name.c_str());
}
}
}
// LCOV_EXCL_STOP
std::shared_ptr<FileStackNode> fstk_GetFileStack() { std::shared_ptr<FileStackNode> fstk_GetFileStack() {
return contextStack.empty() ? nullptr : contextStack.top().fileInfo; return contextStack.empty() ? nullptr : contextStack.top().fileInfo;
} }
@@ -124,7 +147,6 @@ void fstk_AddIncludePath(std::string const &path) {
void fstk_AddPreIncludeFile(std::string const &path) { void fstk_AddPreIncludeFile(std::string const &path) {
preIncludeNames.emplace_front(path); preIncludeNames.emplace_front(path);
verbosePrint("Pre-included filename %s\n", path.c_str()); // LCOV_EXCL_LINE
} }
static bool isValidFilePath(std::string const &path) { static bool isValidFilePath(std::string const &path) {
@@ -308,7 +330,11 @@ bool fstk_FileError(std::string const &path, char const *functionName) {
// LCOV_EXCL_START // LCOV_EXCL_START
if (options.missingIncludeState == GEN_EXIT) { if (options.missingIncludeState == GEN_EXIT) {
verbosePrint( verbosePrint(
"Aborting (-MG) on %s file '%s' (%s)\n", functionName, path.c_str(), strerror(errno) VERB_NOTICE,
"Aborting (-MG) on %s file '%s' (%s)\n",
functionName,
path.c_str(),
strerror(errno)
); );
return true; return true;
} }

View File

@@ -18,6 +18,7 @@
#include "helpers.hpp" #include "helpers.hpp"
#include "util.hpp" #include "util.hpp"
#include "verbosity.hpp"
#include "asm/fixpoint.hpp" #include "asm/fixpoint.hpp"
#include "asm/format.hpp" #include "asm/format.hpp"
@@ -325,7 +326,7 @@ void LexerState::setFileAsNextState(std::string const &filePath, bool updateStat
if (filePath == "-") { if (filePath == "-") {
path = "<stdin>"; path = "<stdin>";
content.emplace<BufferedContent>(STDIN_FILENO); content.emplace<BufferedContent>(STDIN_FILENO);
verbosePrint("Opening stdin\n"); // LCOV_EXCL_LINE verbosePrint(VERB_INFO, "Opening stdin\n"); // LCOV_EXCL_LINE
} else { } else {
struct stat statBuf; struct stat statBuf;
if (stat(filePath.c_str(), &statBuf) != 0) { if (stat(filePath.c_str(), &statBuf) != 0) {
@@ -352,13 +353,17 @@ void LexerState::setFileAsNextState(std::string const &filePath, bool updateStat
} }
content.emplace<ViewedContent>(ptr, size); content.emplace<ViewedContent>(ptr, size);
verbosePrint("File \"%s\" is fully read\n", path.c_str()); // LCOV_EXCL_LINE // LCOV_EXCL_START
verbosePrint(VERB_INFO, "File \"%s\" is fully read\n", path.c_str());
// LCOV_EXCL_STOP
} else { } else {
// LCOV_EXCL_START // LCOV_EXCL_START
if (statBuf.st_size == 0) { if (statBuf.st_size == 0) {
verbosePrint("File \"%s\" is empty\n", path.c_str()); verbosePrint(VERB_INFO, "File \"%s\" is empty\n", path.c_str());
} else { } else {
verbosePrint("Failed to stat file \"%s\": %s\n", path.c_str(), strerror(errno)); verbosePrint(
VERB_INFO, "Failed to stat file \"%s\": %s\n", path.c_str(), strerror(errno)
);
} }
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
@@ -371,7 +376,7 @@ void LexerState::setFileAsNextState(std::string const &filePath, bool updateStat
} }
content.emplace<BufferedContent>(fd); content.emplace<BufferedContent>(fd);
verbosePrint("File \"%s\" is opened\n", path.c_str()); // LCOV_EXCL_LINE verbosePrint(VERB_INFO, "File \"%s\" is opened\n", path.c_str()); // LCOV_EXCL_LINE
} }
} }

View File

@@ -17,6 +17,7 @@
#include "parser.hpp" // Generated from parser.y #include "parser.hpp" // Generated from parser.y
#include "usage.hpp" #include "usage.hpp"
#include "util.hpp" // UpperMap #include "util.hpp" // UpperMap
#include "verbosity.hpp"
#include "version.hpp" #include "version.hpp"
#include "asm/charmap.hpp" #include "asm/charmap.hpp"
@@ -28,23 +29,8 @@
Options options; Options options;
// Escapes Make-special chars from a string static char const *dependFileName = nullptr; // -M
static std::string make_escape(std::string &str) { static std::unordered_map<std::string, std::vector<StateFeature>> stateFileSpecs; // -s
std::string escaped;
size_t pos = 0;
for (;;) {
// All dollars needs to be doubled
size_t nextPos = str.find('$', pos);
if (nextPos == std::string::npos) {
break;
}
escaped.append(str, pos, nextPos - pos);
escaped.append("$$");
pos = nextPos + literal_strlen("$");
}
escaped.append(str, pos, str.length() - pos);
return escaped;
}
// Short options // Short options
static char const *optstring = "b:D:Eg:hI:M:o:P:p:Q:r:s:VvW:wX:"; static char const *optstring = "b:D:Eg:hI:M:o:P:p:Q:r:s:VvW:wX:";
@@ -105,6 +91,148 @@ static Usage usage(
); );
// clang-format on // clang-format on
// LCOV_EXCL_START
static void verboseOutputConfig(int argc, char *argv[]) {
if (!checkVerbosity(VERB_CONFIG)) {
return;
}
fprintf(stderr, "rgbasm %s\n", get_package_version_string());
printVVVVVVerbosity();
fputs("Options:\n", stderr);
// -E/--export-all
if (options.exportAll) {
fputs("\tExport all labels by default\n", stderr);
}
// -b/--binary-digits
if (options.binDigits[0] != '0' || options.binDigits[1] != '1') {
fprintf(
stderr, "\tBinary digits: '%c', '%c'\n", options.binDigits[0], options.binDigits[1]
);
}
// -g/--gfx-chars
if (options.gfxDigits[0] != '0' || options.gfxDigits[1] != '1' || options.gfxDigits[2] != '2'
|| options.gfxDigits[3] != '3') {
fprintf(
stderr,
"\tGraphics characters: '%c', '%c', '%c', '%c'\n",
options.gfxDigits[0],
options.gfxDigits[1],
options.gfxDigits[2],
options.gfxDigits[3]
);
}
// -Q/--q-precision
fprintf(
stderr,
"\tFixed-point precision: Q%d.%" PRIu8 "\n",
32 - options.fixPrecision,
options.fixPrecision
);
// -p/--pad-value
fprintf(stderr, "\tPad value: 0x%02" PRIx8 "\n", options.padByte);
// -r/--recursion-depth
fprintf(stderr, "\tMaximum recursion depth %zu\n", options.maxRecursionDepth);
// -X/--max-errors
if (options.maxErrors) {
fprintf(stderr, "\tMaximum %" PRIu64 " errors\n", options.maxErrors);
}
// -D/--define
static bool hasDefines = false; // `static` so `sym_ForEach` callback can see it
sym_ForEach([](Symbol &sym) {
if (!sym.isBuiltin && sym.type == SYM_EQUS) {
if (!hasDefines) {
fputs("\tDefinitions:\n", stderr);
hasDefines = true;
}
fprintf(stderr, "\t - def %s equs \"%s\"\n", sym.name.c_str(), sym.getEqus()->c_str());
}
});
// -s/--state
if (!stateFileSpecs.empty()) {
fputs("\tOutput state files:\n", stderr);
static char const *featureNames[NB_STATE_FEATURES] = {
"equ",
"var",
"equs",
"char",
"macro",
};
for (auto [name, features] : stateFileSpecs) {
fprintf(stderr, "\t - %s: ", name == "-" ? "<stdout>" : name.c_str());
for (size_t i = 0; i < features.size(); ++i) {
if (i > 0) {
fputs(", ", stderr);
}
fputs(featureNames[features[i]], stderr);
}
putc('\n', stderr);
}
}
// asmfile
if (musl_optind < argc) {
fprintf(stderr, "\tInput asm file: %s", argv[musl_optind]);
if (musl_optind + 1 < argc) {
fprintf(stderr, " (and %d more)", argc - musl_optind - 1);
}
putc('\n', stderr);
}
// -o/--output
if (!options.objectFileName.empty()) {
fprintf(stderr, "\tOutput object file: %s\n", options.objectFileName.c_str());
}
fstk_VerboseOutputConfig();
if (dependFileName) {
fprintf(
stderr,
"\tOutput dependency file: %s\n",
strcmp(dependFileName, "-") ? dependFileName : "<stdout>"
);
// -MT or -MQ
if (!options.targetFileName.empty()) {
fprintf(stderr, "\tTarget file(s): %s\n", options.targetFileName.c_str());
}
// -MG or -MC
switch (options.missingIncludeState) {
case INC_ERROR:
fputs("\tExit with an error on a missing dependency\n", stderr);
break;
case GEN_EXIT:
fputs("\tExit normally on a missing dependency\n", stderr);
break;
case GEN_CONTINUE:
fputs("\tContinue processing after a missing dependency\n", stderr);
break;
}
// -MP
if (options.generatePhonyDeps) {
fputs("\tGenerate phony dependencies\n", stderr);
}
// [-MG] [-MC]
}
fputs("Ready.\n", stderr);
}
// LCOV_EXCL_STOP
static std::string escapeMakeChars(std::string &str) {
std::string escaped;
size_t pos = 0;
for (;;) {
// All dollars needs to be doubled
size_t nextPos = str.find('$', pos);
if (nextPos == std::string::npos) {
break;
}
escaped.append(str, pos, nextPos - pos);
escaped.append("$$");
pos = nextPos + literal_strlen("$");
}
escaped.append(str, pos, str.length() - pos);
return escaped;
}
// Parse a comma-separated string of '-s/--state' features // Parse a comma-separated string of '-s/--state' features
static std::vector<StateFeature> parseStateFeatures(char *str) { static std::vector<StateFeature> parseStateFeatures(char *str) {
std::vector<StateFeature> features; std::vector<StateFeature> features;
@@ -164,10 +292,6 @@ int main(int argc, char *argv[]) {
options.maxErrors = 100; options.maxErrors = 100;
} }
// Local options
char const *dependFileName = nullptr; // -M
std::unordered_map<std::string, std::vector<StateFeature>> stateFileSpecs; // -s
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) { for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
switch (ch) { switch (ch) {
char *endptr; char *endptr;
@@ -192,7 +316,7 @@ int main(int argc, char *argv[]) {
break; break;
case 'E': case 'E':
sym_SetExportAll(true); options.exportAll = true;
break; break;
case 'g': case 'g':
@@ -211,21 +335,13 @@ int main(int argc, char *argv[]) {
break; break;
case 'M': case 'M':
if (options.dependFile) { if (dependFileName) {
warnx("Overriding dependfile %s", dependFileName); warnx(
"Overriding dependency file %s",
strcmp(dependFileName, "-") ? dependFileName : "<stdout>"
);
} }
if (strcmp("-", musl_optarg)) {
options.dependFile = fopen(musl_optarg, "w");
dependFileName = musl_optarg; dependFileName = musl_optarg;
} else {
options.dependFile = stdout;
dependFileName = "<stdout>";
}
if (options.dependFile == nullptr) {
// LCOV_EXCL_START
fatal("Failed to open dependfile \"%s\": %s", dependFileName, strerror(errno));
// LCOV_EXCL_STOP
}
break; break;
case 'o': case 'o':
@@ -233,7 +349,6 @@ int main(int argc, char *argv[]) {
warnx("Overriding output filename %s", options.objectFileName.c_str()); warnx("Overriding output filename %s", options.objectFileName.c_str());
} }
options.objectFileName = musl_optarg; options.objectFileName = musl_optarg;
verbosePrint("Output filename %s\n", options.objectFileName.c_str()); // LCOV_EXCL_LINE
break; break;
case 'P': case 'P':
@@ -295,7 +410,6 @@ int main(int argc, char *argv[]) {
if (stateFileSpecs.find(name) != stateFileSpecs.end()) { if (stateFileSpecs.find(name) != stateFileSpecs.end()) {
warnx("Overriding state filename %s", name); warnx("Overriding state filename %s", name);
} }
verbosePrint("State filename %s\n", name); // LCOV_EXCL_LINE
stateFileSpecs.emplace(name, std::move(features)); stateFileSpecs.emplace(name, std::move(features));
break; break;
} }
@@ -306,7 +420,7 @@ int main(int argc, char *argv[]) {
case 'v': case 'v':
// LCOV_EXCL_START // LCOV_EXCL_START
options.verbose = true; incrementVerbosity();
break; break;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
@@ -352,7 +466,7 @@ int main(int argc, char *argv[]) {
case 'T': { case 'T': {
std::string newTarget = musl_optarg; std::string newTarget = musl_optarg;
if (depType == 'Q') { if (depType == 'Q') {
newTarget = make_escape(newTarget); newTarget = escapeMakeChars(newTarget);
} }
if (!options.targetFileName.empty()) { if (!options.targetFileName.empty()) {
options.targetFileName += ' '; options.targetFileName += ' ';
@@ -373,6 +487,8 @@ int main(int argc, char *argv[]) {
options.targetFileName = options.objectFileName; options.targetFileName = options.objectFileName;
} }
verboseOutputConfig(argc, argv);
if (argc == musl_optind) { if (argc == musl_optind) {
usage.printAndExit("Please specify an input file (pass `-` to read from standard input)"); usage.printAndExit("Please specify an input file (pass `-` to read from standard input)");
} else if (argc != musl_optind + 1) { } else if (argc != musl_optind + 1) {
@@ -381,7 +497,20 @@ int main(int argc, char *argv[]) {
std::string mainFileName = argv[musl_optind]; std::string mainFileName = argv[musl_optind];
verbosePrint("Assembling %s\n", mainFileName.c_str()); // LCOV_EXCL_LINE verbosePrint(VERB_NOTICE, "Assembling %s\n", mainFileName.c_str()); // LCOV_EXCL_LINE
if (dependFileName) {
if (strcmp("-", dependFileName)) {
options.dependFile = fopen(dependFileName, "w");
if (options.dependFile == nullptr) {
// LCOV_EXCL_START
fatal("Failed to open dependency file \"%s\": %s", dependFileName, strerror(errno));
// LCOV_EXCL_STOP
}
} else {
options.dependFile = stdout;
}
}
if (options.dependFile && options.targetFileName.empty()) { if (options.dependFile && options.targetFileName.empty()) {
fatal("Dependency files can only be created if a target file is specified with either " fatal("Dependency files can only be created if a target file is specified with either "

View File

@@ -17,6 +17,7 @@
#include "asm/fstack.hpp" #include "asm/fstack.hpp"
#include "asm/lexer.hpp" #include "asm/lexer.hpp"
#include "asm/macro.hpp" #include "asm/macro.hpp"
#include "asm/main.hpp"
#include "asm/output.hpp" #include "asm/output.hpp"
#include "asm/warning.hpp" #include "asm/warning.hpp"
@@ -39,8 +40,6 @@ static char savedDATE[256];
static char savedTIMESTAMP_ISO8601_LOCAL[256]; static char savedTIMESTAMP_ISO8601_LOCAL[256];
static char savedTIMESTAMP_ISO8601_UTC[256]; static char savedTIMESTAMP_ISO8601_UTC[256];
static bool exportAll = false; // -E
bool sym_IsPC(Symbol const *sym) { bool sym_IsPC(Symbol const *sym) {
return sym == PCSymbol; return sym == PCSymbol;
} }
@@ -497,7 +496,7 @@ static Symbol *addLabel(std::string const &symName) {
sym->type = SYM_LABEL; sym->type = SYM_LABEL;
sym->data = static_cast<int32_t>(sect_GetSymbolOffset()); sym->data = static_cast<int32_t>(sect_GetSymbolOffset());
// Don't export anonymous labels // Don't export anonymous labels
if (exportAll && !symName.starts_with('!')) { if (options.exportAll && !symName.starts_with('!')) {
sym->isExported = true; sym->isExported = true;
} }
sym->section = sect_GetSymbolSection(); sym->section = sect_GetSymbolSection();
@@ -635,11 +634,6 @@ Symbol *sym_Ref(std::string const &symName) {
return sym; return sym;
} }
// Set whether to export all relocatable symbols by default
void sym_SetExportAll(bool set) {
exportAll = set;
}
// Define the built-in symbols // Define the built-in symbols
void sym_Init(time_t now) { void sym_Init(time_t now) {
PCSymbol = &createSymbol("@"s); PCSymbol = &createSymbol("@"s);

View File

@@ -65,13 +65,14 @@ static Usage usage(
" [-n <rom_version>] [-p <pad_value>] [-r <ram_size>] [-t <title_str>]\n" " [-n <rom_version>] [-p <pad_value>] [-r <ram_size>] [-t <title_str>]\n"
" [-W warning] <file> ...\n" " [-W warning] <file> ...\n"
"Useful options:\n" "Useful options:\n"
" -m, --mbc-type <value> set the MBC type byte to this value; refer\n" " -m, --mbc-type <value> set the MBC type byte to this value; `-m help'\n"
" to the man page for a list of values\n" " or `-m list' prints the accepted values\n"
" -p, --pad-value <value> pad to the next valid size using this value\n" " -p, --pad-value <value> pad to the next valid size using this value\n"
" -r, --ram-size <code> set the cart RAM size byte to this value\n" " -r, --ram-size <code> set the cart RAM size byte to this value\n"
" -o, --output <path> set the output file\n" " -o, --output <path> set the output file\n"
" -V, --version print RGBFIX version and exit\n" " -V, --version print RGBFIX version and exit\n"
" -v, --validate fix the header logo and both checksums (-f lhg)\n" " -v, --validate fix the header logo and both checksums (`-f lhg')\n"
" -W, --warning <warning> enable or disable warnings\n"
"\n" "\n"
"For help, use `man rgbfix' or go to https://rgbds.gbdev.io/docs/\n" "For help, use `man rgbfix' or go to https://rgbds.gbdev.io/docs/\n"
); );

View File

@@ -19,6 +19,7 @@
#include "file.hpp" #include "file.hpp"
#include "platform.hpp" #include "platform.hpp"
#include "usage.hpp" #include "usage.hpp"
#include "verbosity.hpp"
#include "version.hpp" #include "version.hpp"
#include "gfx/pal_spec.hpp" #include "gfx/pal_spec.hpp"
@@ -40,18 +41,6 @@ static struct LocalOptions {
bool reverse; bool reverse;
} localOptions; } localOptions;
void Options::verbosePrint(uint8_t level, char const *fmt, ...) const {
// LCOV_EXCL_START
if (verbosity >= level) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
// LCOV_EXCL_STOP
}
// Short options // Short options
static char const *optstring = "-Aa:B:b:Cc:d:hi:L:l:mN:n:Oo:Pp:Qq:r:s:Tt:U:uVvW:wXx:YZ"; static char const *optstring = "-Aa:B:b:Cc:d:hi:L:l:mN:n:Oo:Pp:Qq:r:s:Tt:U:uVvW:wXx:YZ";
@@ -112,6 +101,7 @@ static Usage usage(
" -t, --tilemap <path> output the tile map to this path\n" " -t, --tilemap <path> output the tile map to this path\n"
" -u, --unique-tiles optimize out identical tiles\n" " -u, --unique-tiles optimize out identical tiles\n"
" -V, --version print RGBGFX version and exit\n" " -V, --version print RGBGFX version and exit\n"
" -W, --warning <warning> enable or disable warnings\n"
"\n" "\n"
"For help, use `man rgbgfx' or go to https://rgbds.gbdev.io/docs/\n" "For help, use `man rgbgfx' or go to https://rgbds.gbdev.io/docs/\n"
); );
@@ -536,9 +526,7 @@ static char *parseArgv(int argc, char *argv[]) {
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
case 'v': case 'v':
// LCOV_EXCL_START // LCOV_EXCL_START
if (options.verbosity < Options::VERB_VVVVVV) { incrementVerbosity();
++options.verbosity;
}
break; break;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
case 'W': case 'W':
@@ -580,93 +568,66 @@ static char *parseArgv(int argc, char *argv[]) {
return nullptr; // Done processing this argv return nullptr; // Done processing this argv
} }
// LCOV_EXCL_START
static void verboseOutputConfig() { static void verboseOutputConfig() {
if (!checkVerbosity(VERB_CONFIG)) {
return;
}
fprintf(stderr, "rgbgfx %s\n", get_package_version_string()); fprintf(stderr, "rgbgfx %s\n", get_package_version_string());
if (options.verbosity >= Options::VERB_VVVVVV) { printVVVVVVerbosity();
putc('\n', stderr);
// clang-format off: vertically align values
static std::array<uint16_t, 21> gfx{
0b0111111110,
0b1111111111,
0b1110011001,
0b1110011001,
0b1111111111,
0b1111111111,
0b1110000001,
0b1111000011,
0b0111111110,
0b0001111000,
0b0111111110,
0b1111111111,
0b1111111111,
0b1111111111,
0b1101111011,
0b1101111011,
0b0011111100,
0b0011001100,
0b0111001110,
0b0111001110,
0b0111001110,
};
// clang-format on
static std::array<char const *, 3> textbox{
" ,----------------------------------------.",
" | Augh, dimensional interference again?! |",
" `----------------------------------------'",
};
for (size_t i = 0; i < gfx.size(); ++i) {
uint16_t row = gfx[i];
for (uint8_t _ = 0; _ < 10; ++_) {
unsigned char c = row & 1 ? '0' : ' ';
putc(c, stderr);
// Double the pixel horizontally, otherwise the aspect ratio looks wrong
putc(c, stderr);
row >>= 1;
}
if (i < textbox.size()) {
fputs(textbox[i], stderr);
}
putc('\n', stderr);
}
putc('\n', stderr);
}
fputs("Options:\n", stderr); fputs("Options:\n", stderr);
// -Z/--columns
if (options.columnMajor) { if (options.columnMajor) {
fputs("\tVisit image in column-major order\n", stderr); fputs("\tVisit image in column-major order\n", stderr);
} }
// -u/--unique-tiles
if (options.allowDedup) { if (options.allowDedup) {
fputs("\tAllow deduplicating tiles\n", stderr); fputs("\tAllow deduplicating identical tiles\n", stderr);
} }
if (options.allowMirroringX) { // -m/--mirror-tiles
if (options.allowMirroringX && options.allowMirroringY) {
fputs("\tAllow deduplicating mirrored tiles\n", stderr);
}
// -X/--mirror-x
else if (options.allowMirroringX) {
fputs("\tAllow deduplicating horizontally mirrored tiles\n", stderr); fputs("\tAllow deduplicating horizontally mirrored tiles\n", stderr);
} }
if (options.allowMirroringY) { // -Y/--mirror-y
else if (options.allowMirroringY) {
fputs("\tAllow deduplicating vertically mirrored tiles\n", stderr); fputs("\tAllow deduplicating vertically mirrored tiles\n", stderr);
} }
// -C/--color-curve
if (options.useColorCurve) { if (options.useColorCurve) {
fputs("\tUse color curve\n", stderr); fputs("\tUse color curve\n", stderr);
} }
// -d/--depth
fprintf(stderr, "\tBit depth: %" PRIu8 "bpp\n", options.bitDepth); fprintf(stderr, "\tBit depth: %" PRIu8 "bpp\n", options.bitDepth);
// -x/--trim-end
if (options.trim != 0) { if (options.trim != 0) {
fprintf(stderr, "\tTrim the last %" PRIu64 " tiles\n", options.trim); fprintf(stderr, "\tTrim the last %" PRIu64 " tiles\n", options.trim);
} }
// -n/--nb-palettes
fprintf(stderr, "\tMaximum %" PRIu16 " palettes\n", options.nbPalettes); fprintf(stderr, "\tMaximum %" PRIu16 " palettes\n", options.nbPalettes);
// -s/--palette-size
fprintf(stderr, "\tPalettes contain %" PRIu8 " colors\n", options.nbColorsPerPal); fprintf(stderr, "\tPalettes contain %" PRIu8 " colors\n", options.nbColorsPerPal);
// -c/--colors
if (options.palSpecType != Options::NO_SPEC) {
fprintf(stderr, "\t%s palette spec\n", [] { fprintf(stderr, "\t%s palette spec\n", [] {
switch (options.palSpecType) { switch (options.palSpecType) {
case Options::NO_SPEC:
return "No";
case Options::EXPLICIT: case Options::EXPLICIT:
return "Explicit"; return "Explicit";
case Options::EMBEDDED: case Options::EMBEDDED:
return "Embedded"; return "Embedded";
case Options::DMG: case Options::DMG:
return "DMG"; return "DMG";
} default:
return "???"; return "???";
}
}()); }());
}
if (options.palSpecType == Options::EXPLICIT) { if (options.palSpecType == Options::EXPLICIT) {
fputs("\t[\n", stderr); fputs("\t[\n", stderr);
for (auto const &pal : options.palSpec) { for (auto const &pal : options.palSpec) {
@@ -682,6 +643,8 @@ static void verboseOutputConfig() {
} }
fputs("\t]\n", stderr); fputs("\t]\n", stderr);
} }
// -L/--slice
if (options.inputSlice.specified()) {
fprintf( fprintf(
stderr, stderr,
"\tInput image slice: %" PRIu16 "x%" PRIu16 " pixels starting at (%" PRIu16 ", %" PRIu16 "\tInput image slice: %" PRIu16 "x%" PRIu16 " pixels starting at (%" PRIu16 ", %" PRIu16
@@ -691,31 +654,54 @@ static void verboseOutputConfig() {
options.inputSlice.left, options.inputSlice.left,
options.inputSlice.top options.inputSlice.top
); );
}
// -b/--base-tiles
if (options.baseTileIDs[0] || options.baseTileIDs[1]) {
fprintf( fprintf(
stderr, stderr,
"\tBase tile IDs: [%" PRIu8 ", %" PRIu8 "]\n", "\tBase tile IDs: bank 0 = 0x%02" PRIx8 ", bank 1 = 0x%02" PRIx8 "\n",
options.baseTileIDs[0], options.baseTileIDs[0],
options.baseTileIDs[1] options.baseTileIDs[1]
); );
}
// -l/--base-palette
if (options.basePalID) {
fprintf(stderr, "\tBase palette ID: %" PRIu8 "\n", options.basePalID); fprintf(stderr, "\tBase palette ID: %" PRIu8 "\n", options.basePalID);
}
// -N/--nb-tiles
fprintf( fprintf(
stderr, stderr,
"\tMaximum %" PRIu16 " tiles in bank 0, %" PRIu16 " in bank 1\n", "\tMaximum %" PRIu16 " tiles in bank 0, and %" PRIu16 " in bank 1\n",
options.maxNbTiles[0], options.maxNbTiles[0],
options.maxNbTiles[1] options.maxNbTiles[1]
); );
// -O/--group-outputs (influences other options)
auto printPath = [](char const *name, std::string const &path) { auto printPath = [](char const *name, std::string const &path) {
if (!path.empty()) { if (!path.empty()) {
fprintf(stderr, "\t%s: %s\n", name, path.c_str()); fprintf(stderr, "\t%s: %s\n", name, path.c_str());
} }
}; };
// file
printPath("Input image", options.input); printPath("Input image", options.input);
// -i/--input-tileset
printPath("Input tileset", options.inputTileset);
// -o/--output
printPath("Output tile data", options.output); printPath("Output tile data", options.output);
// -t/--tilemap or -T/--auto-tilemap
printPath("Output tilemap", options.tilemap); printPath("Output tilemap", options.tilemap);
// -a/--attrmap or -A/--auto-attrmap
printPath("Output attrmap", options.attrmap); printPath("Output attrmap", options.attrmap);
// -p/--palette or -P/--auto-palette
printPath("Output palettes", options.palettes); printPath("Output palettes", options.palettes);
// -q/--palette-map or -Q/--auto-palette-map
printPath("Output palette map", options.palmap);
// -r/--reverse
if (localOptions.reverse) {
fprintf(stderr, "\tReverse image width: %" PRIu16 " tiles\n", options.reversedWidth);
}
fputs("Ready.\n", stderr); fputs("Ready.\n", stderr);
} }
// LCOV_EXCL_STOP
// Manual implementation of std::filesystem::path.replace_extension(). // Manual implementation of std::filesystem::path.replace_extension().
// macOS <10.15 did not support std::filesystem::path. // macOS <10.15 did not support std::filesystem::path.
@@ -839,11 +825,7 @@ int main(int argc, char *argv[]) {
parseExternalPalSpec(localOptions.externalPalSpec); parseExternalPalSpec(localOptions.externalPalSpec);
} }
// LCOV_EXCL_START verboseOutputConfig(); // LCOV_EXCL_LINE
if (options.verbosity >= Options::VERB_CFG) {
verboseOutputConfig();
}
// LCOV_EXCL_STOP
// Do not do anything if option parsing went wrong. // Do not do anything if option parsing went wrong.
requireZeroErrors(); requireZeroErrors();

View File

@@ -14,6 +14,7 @@
#include <utility> #include <utility>
#include "helpers.hpp" #include "helpers.hpp"
#include "verbosity.hpp"
#include "gfx/color_set.hpp" #include "gfx/color_set.hpp"
#include "gfx/main.hpp" #include "gfx/main.hpp"
@@ -247,6 +248,10 @@ public:
static void verboseOutputAssignments( static void verboseOutputAssignments(
std::vector<AssignedSets> const &assignments, std::vector<ColorSet> const &colorSets std::vector<AssignedSets> const &assignments, std::vector<ColorSet> const &colorSets
) { ) {
if (!checkVerbosity(VERB_INFO)) {
return;
}
for (AssignedSets const &assignment : assignments) { for (AssignedSets const &assignment : assignments) {
fputs("{ ", stderr); fputs("{ ", stderr);
for (ColorSetAttrs const &attrs : assignment) { for (ColorSetAttrs const &attrs : assignment) {
@@ -284,9 +289,7 @@ static void decant(std::vector<AssignedSets> &assignments, std::vector<ColorSet>
} }
}; };
options.verbosePrint( verbosePrint(VERB_DEBUG, "%zu palettes before decanting\n", assignments.size());
Options::VERB_DEBUG, "%zu palettes before decanting\n", assignments.size()
);
// Decant on palettes // Decant on palettes
decantOn([&colorSets](AssignedSets &to, AssignedSets &from) { decantOn([&colorSets](AssignedSets &to, AssignedSets &from) {
@@ -298,9 +301,7 @@ static void decant(std::vector<AssignedSets> &assignments, std::vector<ColorSet>
from.clear(); from.clear();
} }
}); });
options.verbosePrint( verbosePrint(VERB_DEBUG, "%zu palettes after decanting on palettes\n", assignments.size());
Options::VERB_DEBUG, "%zu palettes after decanting on palettes\n", assignments.size()
);
// Decant on "components" (color sets sharing colors) // Decant on "components" (color sets sharing colors)
decantOn([&colorSets](AssignedSets &to, AssignedSets &from) { decantOn([&colorSets](AssignedSets &to, AssignedSets &from) {
@@ -344,8 +345,8 @@ static void decant(std::vector<AssignedSets> &assignments, std::vector<ColorSet>
} }
} }
}); });
options.verbosePrint( verbosePrint(
Options::VERB_DEBUG, "%zu palettes after decanting on \"components\"\n", assignments.size() VERB_DEBUG, "%zu palettes after decanting on \"components\"\n", assignments.size()
); );
// Decant on individual color sets // Decant on individual color sets
@@ -357,15 +358,11 @@ static void decant(std::vector<AssignedSets> &assignments, std::vector<ColorSet>
} }
} }
}); });
options.verbosePrint( verbosePrint(VERB_DEBUG, "%zu palettes after decanting on color sets\n", assignments.size());
Options::VERB_DEBUG, "%zu palettes after decanting on color sets\n", assignments.size()
);
} }
std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet> const &colorSets) { std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet> const &colorSets) {
options.verbosePrint( verbosePrint(VERB_NOTICE, "Paginating palettes using \"overload-and-remove\" strategy...\n");
Options::VERB_LOG_ACT, "Paginating palettes using \"overload-and-remove\" strategy...\n"
);
// Sort the color sets by size, which improves the packing algorithm's efficiency // Sort the color sets by size, which improves the packing algorithm's efficiency
auto const indexOfLargestColorSetFirst = [&colorSets](size_t left, size_t right) { auto const indexOfLargestColorSetFirst = [&colorSets](size_t left, size_t right) {
@@ -389,7 +386,7 @@ std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet>
!queue.empty(); !queue.empty();
queue.pop()) { queue.pop()) {
ColorSetAttrs const &attrs = queue.front(); // Valid until the `queue.pop()` ColorSetAttrs const &attrs = queue.front(); // Valid until the `queue.pop()`
options.verbosePrint(Options::VERB_TRACE, "Handling color set %zu\n", attrs.colorSetIndex); verbosePrint(VERB_TRACE, "Handling color set %zu\n", attrs.colorSetIndex);
ColorSet const &colorSet = colorSets[attrs.colorSetIndex]; ColorSet const &colorSet = colorSets[attrs.colorSetIndex];
size_t bestPalIndex = assignments.size(); size_t bestPalIndex = assignments.size();
@@ -404,8 +401,8 @@ std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet>
} }
uint32_t relSize = assignments[i].relSizeOf(colorSet); uint32_t relSize = assignments[i].relSizeOf(colorSet);
options.verbosePrint( verbosePrint(
Options::VERB_TRACE, VERB_TRACE,
" Relative size to palette %zu (of %zu): %" PRIu32 " (size = %zu)\n", " Relative size to palette %zu (of %zu): %" PRIu32 " (size = %zu)\n",
i, i,
assignments.size(), assignments.size(),
@@ -420,8 +417,8 @@ std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet>
if (bestPalIndex == assignments.size()) { if (bestPalIndex == assignments.size()) {
// Found nowhere to put it, create a new page containing just that one // Found nowhere to put it, create a new page containing just that one
options.verbosePrint( verbosePrint(
Options::VERB_TRACE, VERB_TRACE,
"Assigning color set %zu to new palette %zu\n", "Assigning color set %zu to new palette %zu\n",
attrs.colorSetIndex, attrs.colorSetIndex,
bestPalIndex bestPalIndex
@@ -430,8 +427,8 @@ std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet>
continue; continue;
} }
options.verbosePrint( verbosePrint(
Options::VERB_TRACE, VERB_TRACE,
"Assigning color set %zu to palette %zu\n", "Assigning color set %zu to palette %zu\n",
attrs.colorSetIndex, attrs.colorSetIndex,
bestPalIndex bestPalIndex
@@ -448,8 +445,8 @@ std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet>
uint32_t relSize1 = bestPal.relSizeOf(colorSet1); uint32_t relSize1 = bestPal.relSizeOf(colorSet1);
uint32_t relSize2 = bestPal.relSizeOf(colorSet2); uint32_t relSize2 = bestPal.relSizeOf(colorSet2);
options.verbosePrint( verbosePrint(
Options::VERB_TRACE, VERB_TRACE,
" Color sets %zu <=> %zu: Efficiency: %zu / %" PRIu32 " <=> %zu / " " Color sets %zu <=> %zu: Efficiency: %zu / %" PRIu32 " <=> %zu / "
"%" PRIu32 "\n", "%" PRIu32 "\n",
attrs1.colorSetIndex, attrs1.colorSetIndex,
@@ -470,8 +467,8 @@ std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet>
// If this overloads the palette, get it back to normal (if possible) // If this overloads the palette, get it back to normal (if possible)
while (bestPal.volume() > options.maxOpaqueColors()) { while (bestPal.volume() > options.maxOpaqueColors()) {
options.verbosePrint( verbosePrint(
Options::VERB_TRACE, VERB_TRACE,
"Palette %zu is overloaded! (%zu > %" PRIu8 ")\n", "Palette %zu is overloaded! (%zu > %" PRIu8 ")\n",
bestPalIndex, bestPalIndex,
bestPal.volume(), bestPal.volume(),
@@ -488,13 +485,13 @@ std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet>
// All efficiencies are identical iff min equals max // All efficiencies are identical iff min equals max
if (compareEfficiency(*minEfficiencyIter, *maxEfficiencyIter) == 0) { if (compareEfficiency(*minEfficiencyIter, *maxEfficiencyIter) == 0) {
options.verbosePrint(Options::VERB_TRACE, " All efficiencies are identical\n"); verbosePrint(VERB_TRACE, " All efficiencies are identical\n");
break; break;
} }
// Remove the color set with minimal efficiency // Remove the color set with minimal efficiency
options.verbosePrint( verbosePrint(
Options::VERB_TRACE, " Removing color set %zu\n", minEfficiencyIter->colorSetIndex VERB_TRACE, " Removing color set %zu\n", minEfficiencyIter->colorSetIndex
); );
queue.emplace(std::move(*minEfficiencyIter)); queue.emplace(std::move(*minEfficiencyIter));
queue.back().banFrom(bestPalIndex); // Ban it from this palette queue.back().banFrom(bestPalIndex); // Ban it from this palette
@@ -526,16 +523,16 @@ std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet>
return pal.canFit(colorSet); return pal.canFit(colorSet);
}); });
if (iter == assignments.end()) { // No such page, create a new one if (iter == assignments.end()) { // No such page, create a new one
options.verbosePrint( verbosePrint(
Options::VERB_DEBUG, VERB_DEBUG,
"Adding new palette (%zu) for overflowing color set %zu\n", "Adding new palette (%zu) for overflowing color set %zu\n",
assignments.size(), assignments.size(),
attrs.colorSetIndex attrs.colorSetIndex
); );
assignments.emplace_back(colorSets, std::move(attrs)); assignments.emplace_back(colorSets, std::move(attrs));
} else { } else {
options.verbosePrint( verbosePrint(
Options::VERB_DEBUG, VERB_DEBUG,
"Assigning overflowing color set %zu to palette %zu\n", "Assigning overflowing color set %zu to palette %zu\n",
attrs.colorSetIndex, attrs.colorSetIndex,
iter - assignments.begin() iter - assignments.begin()
@@ -544,21 +541,13 @@ std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet>
} }
} }
// LCOV_EXCL_START verboseOutputAssignments(assignments, colorSets); // LCOV_EXCL_LINE
if (options.verbosity >= Options::VERB_INTERM) {
verboseOutputAssignments(assignments, colorSets);
}
// LCOV_EXCL_STOP
// "Decant" the result // "Decant" the result
decant(assignments, colorSets); decant(assignments, colorSets);
// Note that the result does not contain any empty palettes // Note that the result does not contain any empty palettes
// LCOV_EXCL_START verboseOutputAssignments(assignments, colorSets); // LCOV_EXCL_LINE
if (options.verbosity >= Options::VERB_INTERM) {
verboseOutputAssignments(assignments, colorSets);
}
// LCOV_EXCL_STOP
std::vector<size_t> mappings(colorSets.size()); std::vector<size_t> mappings(colorSets.size());
for (size_t i = 0; i < assignments.size(); ++i) { for (size_t i = 0; i < assignments.size(); ++i) {

View File

@@ -5,11 +5,12 @@
#include <algorithm> #include <algorithm>
#include "helpers.hpp" #include "helpers.hpp"
#include "verbosity.hpp"
#include "gfx/main.hpp" #include "gfx/main.hpp"
void sortIndexed(std::vector<Palette> &palettes, std::vector<Rgba> const &embPal) { void sortIndexed(std::vector<Palette> &palettes, std::vector<Rgba> const &embPal) {
options.verbosePrint(Options::VERB_LOG_ACT, "Sorting palettes using embedded palette...\n"); verbosePrint(VERB_NOTICE, "Sorting palettes using embedded palette...\n");
for (Palette &pal : palettes) { for (Palette &pal : palettes) {
std::sort(RANGE(pal), [&](uint16_t lhs, uint16_t rhs) { std::sort(RANGE(pal), [&](uint16_t lhs, uint16_t rhs) {
@@ -35,7 +36,7 @@ void sortIndexed(std::vector<Palette> &palettes, std::vector<Rgba> const &embPal
void sortGrayscale( void sortGrayscale(
std::vector<Palette> &palettes, std::array<std::optional<Rgba>, NB_COLOR_SLOTS> const &colors std::vector<Palette> &palettes, std::array<std::optional<Rgba>, NB_COLOR_SLOTS> const &colors
) { ) {
options.verbosePrint(Options::VERB_LOG_ACT, "Sorting palette by grayscale bins...\n"); verbosePrint(VERB_NOTICE, "Sorting palette by grayscale bins...\n");
// This method is only applicable if there are at most as many colors as colors per palette, so // This method is only applicable if there are at most as many colors as colors per palette, so
// we should only have a single palette. // we should only have a single palette.
@@ -59,7 +60,7 @@ static unsigned int luminance(uint16_t color) {
} }
void sortRgb(std::vector<Palette> &palettes) { void sortRgb(std::vector<Palette> &palettes) {
options.verbosePrint(Options::VERB_LOG_ACT, "Sorting palettes by luminance...\n"); verbosePrint(VERB_NOTICE, "Sorting palettes by luminance...\n");
for (Palette &pal : palettes) { for (Palette &pal : palettes) {
// Sort from lightest to darkest // Sort from lightest to darkest

View File

@@ -4,6 +4,8 @@
#include <png.h> #include <png.h>
#include "verbosity.hpp"
#include "gfx/main.hpp" #include "gfx/main.hpp"
#include "gfx/rgba.hpp" #include "gfx/rgba.hpp"
#include "gfx/warning.hpp" #include "gfx/warning.hpp"
@@ -47,7 +49,7 @@ static void readData(png_structp png, png_bytep data, size_t length) {
Png::Png(char const *filename, std::streambuf &file) { Png::Png(char const *filename, std::streambuf &file) {
Input input(filename, file); Input input(filename, file);
options.verbosePrint(Options::VERB_LOG_ACT, "Reading PNG file \"%s\"\n", input.filename); verbosePrint(VERB_NOTICE, "Reading PNG file \"%s\"\n", input.filename);
std::array<unsigned char, 8> pngHeader; std::array<unsigned char, 8> pngHeader;
if (input.file.sgetn(reinterpret_cast<char *>(pngHeader.data()), pngHeader.size()) if (input.file.sgetn(reinterpret_cast<char *>(pngHeader.data()), pngHeader.size())
@@ -56,7 +58,7 @@ Png::Png(char const *filename, std::streambuf &file) {
fatal("File \"%s\" is not a valid PNG image", input.filename); // LCOV_EXCL_LINE fatal("File \"%s\" is not a valid PNG image", input.filename); // LCOV_EXCL_LINE
} }
options.verbosePrint(Options::VERB_INTERM, "PNG header signature is OK\n"); verbosePrint(VERB_INFO, "PNG header signature is OK\n");
png_structp png = png_create_read_struct( png_structp png = png_create_read_struct(
PNG_LIBPNG_VER_STRING, static_cast<png_voidp>(&input), handleError, handleWarning PNG_LIBPNG_VER_STRING, static_cast<png_voidp>(&input), handleError, handleWarning
@@ -110,8 +112,8 @@ Png::Png(char const *filename, std::streambuf &file) {
return "unknown interlace type"; return "unknown interlace type";
} }
}; };
options.verbosePrint( verbosePrint(
Options::VERB_INTERM, VERB_INFO,
"PNG image: %" PRIu32 "x%" PRIu32 " pixels, %dbpp %s, %s\n", "PNG image: %" PRIu32 "x%" PRIu32 " pixels, %dbpp %s, %s\n",
width, width,
height, height,
@@ -139,18 +141,15 @@ Png::Png(char const *filename, std::streambuf &file) {
); );
} }
options.verbosePrint( if (checkVerbosity(VERB_INFO)) {
Options::VERB_INTERM, "Embedded PNG palette has %d colors: [", nbColors fprintf(stderr, "Embedded PNG palette has %d colors: [", nbColors);
);
for (int i = 0; i < nbColors; ++i) { for (int i = 0; i < nbColors; ++i) {
if (i > 0) { fprintf(stderr, "%s#%08x", i > 0 ? ", " : "", palette[i].toCSS());
options.verbosePrint(Options::VERB_INTERM, ", ");
} }
options.verbosePrint(Options::VERB_INTERM, "#%08x", palette[i].toCSS()); fprintf(stderr, "]\n");
} }
options.verbosePrint(Options::VERB_INTERM, "]\n");
} else { } else {
options.verbosePrint(Options::VERB_INTERM, "No embedded PNG palette\n"); verbosePrint(VERB_INFO, "No embedded PNG palette\n");
} }
// Set up transformations to turn everything into RGBA8888 for simplicity of handling // Set up transformations to turn everything into RGBA8888 for simplicity of handling

View File

@@ -19,6 +19,7 @@
#include "file.hpp" #include "file.hpp"
#include "helpers.hpp" #include "helpers.hpp"
#include "itertools.hpp" #include "itertools.hpp"
#include "verbosity.hpp"
#include "gfx/color_set.hpp" #include "gfx/color_set.hpp"
#include "gfx/main.hpp" #include "gfx/main.hpp"
@@ -79,8 +80,8 @@ struct Image {
bool isSuitableForGrayscale() const { bool isSuitableForGrayscale() const {
// Check that all of the grays don't fall into the same "bin" // Check that all of the grays don't fall into the same "bin"
if (colors.size() > options.maxOpaqueColors()) { // Apply the Pigeonhole Principle if (colors.size() > options.maxOpaqueColors()) { // Apply the Pigeonhole Principle
options.verbosePrint( verbosePrint(
Options::VERB_DEBUG, VERB_DEBUG,
"Too many colors for grayscale sorting (%zu > %" PRIu8 ")\n", "Too many colors for grayscale sorting (%zu > %" PRIu8 ")\n",
colors.size(), colors.size(),
options.maxOpaqueColors() options.maxOpaqueColors()
@@ -93,8 +94,8 @@ struct Image {
continue; continue;
} }
if (!color->isGray()) { if (!color->isGray()) {
options.verbosePrint( verbosePrint(
Options::VERB_DEBUG, VERB_DEBUG,
"Found non-gray color #%08x, not using grayscale sorting\n", "Found non-gray color #%08x, not using grayscale sorting\n",
color->toCSS() color->toCSS()
); );
@@ -102,8 +103,8 @@ struct Image {
} }
uint8_t mask = 1 << color->grayIndex(); uint8_t mask = 1 << color->grayIndex();
if (bins & mask) { // Two in the same bin! if (bins & mask) { // Two in the same bin!
options.verbosePrint( verbosePrint(
Options::VERB_DEBUG, VERB_DEBUG,
"Color #%08x conflicts with another one, not using grayscale sorting\n", "Color #%08x conflicts with another one, not using grayscale sorting\n",
color->toCSS() color->toCSS()
); );
@@ -336,7 +337,7 @@ static std::tuple<std::vector<size_t>, std::vector<Palette>>
assume(mappings.size() == colorSets.size()); assume(mappings.size() == colorSets.size());
// LCOV_EXCL_START // LCOV_EXCL_START
if (options.verbosity >= Options::VERB_INTERM) { if (checkVerbosity(VERB_INFO)) {
fprintf( fprintf(
stderr, "Color set mappings: (%zu palette%s)\n", nbPalettes, nbPalettes != 1 ? "s" : "" stderr, "Color set mappings: (%zu palette%s)\n", nbPalettes, nbPalettes != 1 ? "s" : ""
); );
@@ -438,7 +439,7 @@ static std::tuple<std::vector<size_t>, std::vector<Palette>>
static void outputPalettes(std::vector<Palette> const &palettes) { static void outputPalettes(std::vector<Palette> const &palettes) {
// LCOV_EXCL_START // LCOV_EXCL_START
if (options.verbosity >= Options::VERB_INTERM) { if (checkVerbosity(VERB_INFO)) {
for (Palette const &palette : palettes) { for (Palette const &palette : palettes) {
fputs("{ ", stderr); fputs("{ ", stderr);
for (uint16_t colorIndex : palette) { for (uint16_t colorIndex : palette) {
@@ -897,7 +898,7 @@ static void
} }
void processPalettes() { void processPalettes() {
options.verbosePrint(Options::VERB_CFG, "Using libpng %s\n", png_get_libpng_ver(nullptr)); verbosePrint(VERB_CONFIG, "Using libpng %s\n", png_get_libpng_ver(nullptr));
std::vector<ColorSet> colorSets; std::vector<ColorSet> colorSets;
std::vector<Palette> palettes; std::vector<Palette> palettes;
@@ -907,13 +908,13 @@ void processPalettes() {
} }
void process() { void process() {
options.verbosePrint(Options::VERB_CFG, "Using libpng %s\n", png_get_libpng_ver(nullptr)); verbosePrint(VERB_CONFIG, "Using libpng %s\n", png_get_libpng_ver(nullptr));
options.verbosePrint(Options::VERB_LOG_ACT, "Reading tiles...\n"); verbosePrint(VERB_NOTICE, "Reading tiles...\n");
Image image(options.input); // This also sets `hasTransparentPixels` as a side effect Image image(options.input); // This also sets `hasTransparentPixels` as a side effect
// LCOV_EXCL_START // LCOV_EXCL_START
if (options.verbosity >= Options::VERB_INTERM) { if (checkVerbosity(VERB_INFO)) {
fputs("Image colors: [ ", stderr); fputs("Image colors: [ ", stderr);
for (std::optional<Rgba> const &slot : image.colors) { for (std::optional<Rgba> const &slot : image.colors) {
if (!slot.has_value()) { if (!slot.has_value()) {
@@ -1025,14 +1026,14 @@ void process() {
continue_visiting_tiles:; continue_visiting_tiles:;
} }
options.verbosePrint( verbosePrint(
Options::VERB_INTERM, VERB_INFO,
"Image contains %zu color set%s\n", "Image contains %zu color set%s\n",
colorSets.size(), colorSets.size(),
colorSets.size() != 1 ? "s" : "" colorSets.size() != 1 ? "s" : ""
); );
// LCOV_EXCL_START // LCOV_EXCL_START
if (options.verbosity >= Options::VERB_INTERM) { if (checkVerbosity(VERB_INFO)) {
for (ColorSet const &colorSet : colorSets) { for (ColorSet const &colorSet : colorSets) {
fputs("[ ", stderr); fputs("[ ", stderr);
for (uint16_t color : colorSet) { for (uint16_t color : colorSet) {
@@ -1074,20 +1075,19 @@ continue_visiting_tiles:;
} }
if (!options.output.empty()) { if (!options.output.empty()) {
options.verbosePrint(Options::VERB_LOG_ACT, "Generating unoptimized tile data...\n"); verbosePrint(VERB_NOTICE, "Generating unoptimized tile data...\n");
outputUnoptimizedTileData(image, attrmap, palettes, mappings); outputUnoptimizedTileData(image, attrmap, palettes, mappings);
} }
if (!options.tilemap.empty() || !options.attrmap.empty() || !options.palmap.empty()) { if (!options.tilemap.empty() || !options.attrmap.empty() || !options.palmap.empty()) {
options.verbosePrint( verbosePrint(
Options::VERB_LOG_ACT, VERB_NOTICE, "Generating unoptimized tilemap and/or attrmap and/or palmap...\n"
"Generating unoptimized tilemap and/or attrmap and/or palmap...\n"
); );
outputUnoptimizedMaps(attrmap, mappings); outputUnoptimizedMaps(attrmap, mappings);
} }
} else { } else {
// All of these require the deduplication process to be performed to be output // All of these require the deduplication process to be performed to be output
options.verbosePrint(Options::VERB_LOG_ACT, "Deduplicating tiles...\n"); verbosePrint(VERB_NOTICE, "Deduplicating tiles...\n");
UniqueTiles tiles = dedupTiles(image, attrmap, palettes, mappings); UniqueTiles tiles = dedupTiles(image, attrmap, palettes, mappings);
if (size_t nbTiles = tiles.size(); if (size_t nbTiles = tiles.size();
@@ -1101,22 +1101,22 @@ continue_visiting_tiles:;
} }
if (!options.output.empty()) { if (!options.output.empty()) {
options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized tile data...\n"); verbosePrint(VERB_NOTICE, "Generating optimized tile data...\n");
outputTileData(tiles); outputTileData(tiles);
} }
if (!options.tilemap.empty()) { if (!options.tilemap.empty()) {
options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized tilemap...\n"); verbosePrint(VERB_NOTICE, "Generating optimized tilemap...\n");
outputTilemap(attrmap); outputTilemap(attrmap);
} }
if (!options.attrmap.empty()) { if (!options.attrmap.empty()) {
options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized attrmap...\n"); verbosePrint(VERB_NOTICE, "Generating optimized attrmap...\n");
outputAttrmap(attrmap, mappings); outputAttrmap(attrmap, mappings);
} }
if (!options.palmap.empty()) { if (!options.palmap.empty()) {
options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized palmap...\n"); verbosePrint(VERB_NOTICE, "Generating optimized palmap...\n");
outputPalmap(attrmap, mappings); outputPalmap(attrmap, mappings);
} }
} }

View File

@@ -15,6 +15,7 @@
#include "diagnostics.hpp" #include "diagnostics.hpp"
#include "file.hpp" #include "file.hpp"
#include "helpers.hpp" // assume #include "helpers.hpp" // assume
#include "verbosity.hpp"
#include "gfx/main.hpp" #include "gfx/main.hpp"
#include "gfx/warning.hpp" #include "gfx/warning.hpp"
@@ -98,7 +99,7 @@ static void printPalette(std::array<std::optional<Rgba>, 4> const &palette) {
} }
void reverse() { void reverse() {
options.verbosePrint(Options::VERB_CFG, "Using libpng %s\n", png_get_libpng_ver(nullptr)); verbosePrint(VERB_CONFIG, "Using libpng %s\n", png_get_libpng_ver(nullptr));
// Check for weird flag combinations // Check for weird flag combinations
@@ -127,7 +128,7 @@ void reverse() {
); );
} }
options.verbosePrint(Options::VERB_LOG_ACT, "Reading tiles...\n"); verbosePrint(VERB_NOTICE, "Reading tiles...\n");
std::vector<uint8_t> const tiles = readInto(options.output); std::vector<uint8_t> const tiles = readInto(options.output);
uint8_t tileSize = 8 * options.bitDepth; uint8_t tileSize = 8 * options.bitDepth;
if (tiles.size() % tileSize != 0) { if (tiles.size() % tileSize != 0) {
@@ -140,13 +141,13 @@ void reverse() {
// By default, assume tiles are not deduplicated, and add the (allegedly) trimmed tiles // By default, assume tiles are not deduplicated, and add the (allegedly) trimmed tiles
size_t const nbTiles = tiles.size() / tileSize; size_t const nbTiles = tiles.size() / tileSize;
options.verbosePrint(Options::VERB_INTERM, "Read %zu tiles.\n", nbTiles); verbosePrint(VERB_INFO, "Read %zu tiles.\n", nbTiles);
size_t mapSize = nbTiles + options.trim; // Image size in tiles size_t mapSize = nbTiles + options.trim; // Image size in tiles
std::optional<std::vector<uint8_t>> tilemap; std::optional<std::vector<uint8_t>> tilemap;
if (!options.tilemap.empty()) { if (!options.tilemap.empty()) {
tilemap = readInto(options.tilemap); tilemap = readInto(options.tilemap);
mapSize = tilemap->size(); mapSize = tilemap->size();
options.verbosePrint(Options::VERB_INTERM, "Read %zu tilemap entries.\n", mapSize); verbosePrint(VERB_INFO, "Read %zu tilemap entries.\n", mapSize);
} }
if (mapSize == 0) { if (mapSize == 0) {
@@ -172,7 +173,7 @@ void reverse() {
break; break;
} }
} }
options.verbosePrint(Options::VERB_INTERM, "Picked reversing width of %zu tiles\n", width); verbosePrint(VERB_INFO, "Picked reversing width of %zu tiles\n", width);
} }
if (mapSize % width != 0) { if (mapSize % width != 0) {
if (options.trim == 0 && !tilemap) { if (options.trim == 0 && !tilemap) {
@@ -192,9 +193,7 @@ void reverse() {
} }
height = mapSize / width; height = mapSize / width;
options.verbosePrint( verbosePrint(VERB_INFO, "Reversed image dimensions: %zux%zu tiles\n", width, height);
Options::VERB_INTERM, "Reversed image dimensions: %zux%zu tiles\n", width, height
);
Rgba const grayColors[4] = { Rgba const grayColors[4] = {
Rgba(0xFFFFFFFF), Rgba(0xAAAAAAFF), Rgba(0x555555FF), Rgba(0x000000FF) Rgba(0xFFFFFFFF), Rgba(0xAAAAAAFF), Rgba(0x555555FF), Rgba(0x000000FF)
@@ -320,8 +319,8 @@ void reverse() {
} }
} }
options.verbosePrint( verbosePrint(
Options::VERB_INTERM, VERB_INFO,
"Number of tiles in bank {0: %" PRIu16 ", 1: %" PRIu16 "}\n", "Number of tiles in bank {0: %" PRIu16 ", 1: %" PRIu16 "}\n",
nbTilesInBank[0], nbTilesInBank[0],
nbTilesInBank[1] nbTilesInBank[1]
@@ -404,7 +403,7 @@ void reverse() {
} }
} }
options.verbosePrint(Options::VERB_LOG_ACT, "Writing image...\n"); verbosePrint(VERB_NOTICE, "Writing image...\n");
File pngFile; File pngFile;
if (!pngFile.open(options.input, std::ios::out | std::ios::binary)) { if (!pngFile.open(options.input, std::ios::out | std::ios::binary)) {
// LCOV_EXCL_START // LCOV_EXCL_START

View File

@@ -14,6 +14,7 @@
#include "itertools.hpp" #include "itertools.hpp"
#include "linkdefs.hpp" #include "linkdefs.hpp"
#include "platform.hpp" #include "platform.hpp"
#include "verbosity.hpp"
#include "link/main.hpp" #include "link/main.hpp"
#include "link/output.hpp" #include "link/output.hpp"
@@ -405,7 +406,7 @@ static std::vector<Section const *> checkOverlayCompat() {
} }
void assign_AssignSections() { void assign_AssignSections() {
verbosePrint("Beginning assignment...\n"); verbosePrint(VERB_NOTICE, "Beginning assignment...\n");
// Initialize assignment // Initialize assignment
initFreeSpace(); initFreeSpace();
@@ -443,7 +444,7 @@ void assign_AssignSections() {
// Assign sections in decreasing constraint order // Assign sections in decreasing constraint order
for (uint8_t constraints = std::size(unassignedSections); constraints--;) { for (uint8_t constraints = std::size(unassignedSections); constraints--;) {
if (char const *constraintName = constraintNames[constraints]; constraintName) { if (char const *constraintName = constraintNames[constraints]; constraintName) {
verbosePrint("Assigning %sconstrained sections...\n", constraintName); verbosePrint(VERB_INFO, "Assigning %sconstrained sections...\n", constraintName);
} else { } else {
assume(unassignedSections[constraints].empty()); assume(unassignedSections[constraints].empty());
} }

View File

@@ -18,6 +18,7 @@
#include "script.hpp" // Generated from script.y #include "script.hpp" // Generated from script.y
#include "usage.hpp" #include "usage.hpp"
#include "util.hpp" // UpperMap, printChar #include "util.hpp" // UpperMap, printChar
#include "verbosity.hpp"
#include "version.hpp" #include "version.hpp"
#include "link/assign.hpp" #include "link/assign.hpp"
@@ -31,28 +32,7 @@
Options options; Options options;
std::string const &FileStackNode::dump(uint32_t curLineNo) const { static char const *linkerScriptName = nullptr; // -l
if (std::holds_alternative<std::vector<uint32_t>>(data)) {
assume(parent); // REPT nodes use their parent's name
std::string const &lastName = parent->dump(lineNo);
fputs(" -> ", stderr);
fputs(lastName.c_str(), stderr);
for (uint32_t iter : iters()) {
fprintf(stderr, "::REPT~%" PRIu32, iter);
}
fprintf(stderr, "(%" PRIu32 ")", curLineNo);
return lastName;
} else {
if (parent) {
parent->dump(lineNo);
fputs(" -> ", stderr);
}
std::string const &nodeName = name();
fputs(nodeName.c_str(), stderr);
fprintf(stderr, "(%" PRIu32 ")", curLineNo);
return nodeName;
}
}
// Short options // Short options
static char const *optstring = "dhl:m:Mn:O:o:p:S:tVvW:wx"; static char const *optstring = "dhl:m:Mn:O:o:p:S:tVvW:wx";
@@ -96,18 +76,131 @@ static Usage usage(
" -o, --output <path> set the output file\n" " -o, --output <path> set the output file\n"
" -p, --pad <value> set the value to pad between sections with\n" " -p, --pad <value> set the value to pad between sections with\n"
" -x, --nopad disable padding of output binary\n" " -x, --nopad disable padding of output binary\n"
" -V, --version print RGBLINK version and exits\n" " -V, --version print RGBLINK version and exit\n"
" -W, --warning <warning> enable or disable warnings\n"
"\n" "\n"
"For help, use `man rgblink' or go to https://rgbds.gbdev.io/docs/\n" "For help, use `man rgblink' or go to https://rgbds.gbdev.io/docs/\n"
); );
// clang-format on // clang-format on
// LCOV_EXCL_START
static void verboseOutputConfig(int argc, char *argv[]) {
if (!checkVerbosity(VERB_CONFIG)) {
return;
}
fprintf(stderr, "rgblink %s\n", get_package_version_string());
printVVVVVVerbosity();
fputs("Options:\n", stderr);
// -d/--dmg
if (options.isDmgMode) {
fputs("\tDMG mode prohibits non-DMG section types\n", stderr);
}
// -t/--tiny
if (options.is32kMode) {
fputs("\tROM0 covers the full 32 KiB of ROM\n", stderr);
}
// -w/--wramx
if (options.isWRAM0Mode) {
fputs("\tWRAM0 covers the full 8 KiB of WRAM\n", stderr);
}
// -x/--nopad
if (options.disablePadding) {
fputs("\tNo padding at the end of the ROM file\n", stderr);
}
// -p/--pad
fprintf(stderr, "\tPad value: 0x%02" PRIx8 "\n", options.padValue);
// -S/--scramble
if (options.scrambleROMX || options.scrambleWRAMX || options.scrambleSRAM) {
fputs("\tScramble: ", stderr);
if (options.scrambleROMX) {
fprintf(stderr, "ROMX = %" PRIu16, options.scrambleROMX);
if (options.scrambleWRAMX || options.scrambleSRAM) {
fputs(", ", stderr);
}
}
if (options.scrambleWRAMX) {
fprintf(stderr, "WRAMX = %" PRIu16, options.scrambleWRAMX);
if (options.scrambleSRAM) {
fputs(", ", stderr);
}
}
if (options.scrambleSRAM) {
fprintf(stderr, "SRAM = %" PRIu16, options.scrambleSRAM);
}
putc('\n', stderr);
}
// file ...
if (musl_optind < argc) {
fprintf(stderr, "\tInput object files: ");
for (int i = musl_optind; i < argc; ++i) {
if (i > musl_optind) {
fputs(", ", stderr);
}
if (i - musl_optind == 10) {
fprintf(stderr, "and %d more", argc - i);
break;
}
fputs(argv[i], stderr);
}
putc('\n', stderr);
}
auto printPath = [](char const *name, char const *path) {
if (path) {
fprintf(stderr, "\t%s: %s\n", name, path);
}
};
// -O/--overlay
printPath("Overlay file", options.overlayFileName);
// -l/--linkerscript
printPath("Linker script", linkerScriptName);
// -o/--output
printPath("Output ROM file", options.outputFileName);
// -m/--map
printPath("Output map file", options.mapFileName);
// -M/--no-sym-in-map
if (options.mapFileName && options.noSymInMap) {
fputs("\tNo symbols in map file\n", stderr);
}
// -n/--sym
printPath("Output sym file", options.symFileName);
fputs("Ready.\n", stderr);
}
// LCOV_EXCL_STOP
std::string const &FileStackNode::dump(uint32_t curLineNo) const {
if (std::holds_alternative<std::vector<uint32_t>>(data)) {
assume(parent); // REPT nodes use their parent's name
std::string const &lastName = parent->dump(lineNo);
fputs(" -> ", stderr);
fputs(lastName.c_str(), stderr);
for (uint32_t iter : iters()) {
fprintf(stderr, "::REPT~%" PRIu32, iter);
}
fprintf(stderr, "(%" PRIu32 ")", curLineNo);
return lastName;
} else {
if (parent) {
parent->dump(lineNo);
fputs(" -> ", stderr);
}
std::string const &nodeName = name();
fputs(nodeName.c_str(), stderr);
fprintf(stderr, "(%" PRIu32 ")", curLineNo);
return nodeName;
}
}
static void parseScrambleSpec(char *spec) { static void parseScrambleSpec(char *spec) {
// clang-format off: vertically align nested initializers
static UpperMap<std::pair<uint16_t *, uint16_t>> scrambleSpecs{ static UpperMap<std::pair<uint16_t *, uint16_t>> scrambleSpecs{
{"ROMX", std::pair{&options.scrambleROMX, 65535}}, {"ROMX", std::pair{&options.scrambleROMX, 65535}},
{"SRAM", std::pair{&options.scrambleSRAM, 255 }}, {"SRAM", std::pair{&options.scrambleSRAM, 255 }},
{"WRAMX", std::pair{&options.scrambleWRAMX, 7 }}, {"WRAMX", std::pair{&options.scrambleWRAMX, 7 }},
}; };
// clang-format on
// Skip leading whitespace before the regions. // Skip leading whitespace before the regions.
spec += strspn(spec, " \t"); spec += strspn(spec, " \t");
@@ -211,8 +304,6 @@ static void parseScrambleSpec(char *spec) {
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
char const *linkerScriptName = nullptr; // -l
// Parse options // Parse options
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) { for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
switch (ch) { switch (ch) {
@@ -283,7 +374,7 @@ int main(int argc, char *argv[]) {
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
case 'v': case 'v':
// LCOV_EXCL_START // LCOV_EXCL_START
options.beVerbose = true; incrementVerbosity();
break; break;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
case 'W': case 'W':
@@ -302,10 +393,10 @@ int main(int argc, char *argv[]) {
} }
} }
int curArgIndex = musl_optind; verboseOutputConfig(argc, argv);
// If no input files were specified, the user must have screwed up // If no input files were specified, the user must have screwed up
if (curArgIndex == argc) { if (musl_optind == argc) {
usage.printAndExit("Please specify an input file (pass `-` to read from standard input)"); usage.printAndExit("Please specify an input file (pass `-` to read from standard input)");
} }
@@ -323,13 +414,14 @@ int main(int argc, char *argv[]) {
} }
// Read all object files first, // Read all object files first,
for (obj_Setup(argc - curArgIndex); curArgIndex < argc; ++curArgIndex) { obj_Setup(argc - musl_optind);
obj_ReadFile(argv[curArgIndex], argc - curArgIndex - 1); for (int i = musl_optind; i < argc; ++i) {
obj_ReadFile(argv[i], argc - i - 1);
} }
// apply the linker script's modifications, // apply the linker script's modifications,
if (linkerScriptName) { if (linkerScriptName) {
verbosePrint("Reading linker script...\n"); verbosePrint(VERB_NOTICE, "Reading linker script...\n");
if (lexer_Init(linkerScriptName)) { if (lexer_Init(linkerScriptName)) {
yy::parser parser; yy::parser parser;

View File

@@ -17,6 +17,7 @@
#include "helpers.hpp" #include "helpers.hpp"
#include "linkdefs.hpp" #include "linkdefs.hpp"
#include "platform.hpp" #include "platform.hpp"
#include "verbosity.hpp"
#include "version.hpp" #include "version.hpp"
#include "link/assign.hpp" #include "link/assign.hpp"
@@ -480,7 +481,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
fatal("%s: Not a RGBDS object file", fileName); fatal("%s: Not a RGBDS object file", fileName);
} }
verbosePrint("Reading object file %s\n", fileName); verbosePrint(VERB_NOTICE, "Reading object file %s\n", fileName);
uint32_t revNum; uint32_t revNum;
tryReadLong(revNum, file, "%s: Cannot read revision number: %s", fileName); tryReadLong(revNum, file, "%s: Cannot read revision number: %s", fileName);
@@ -506,7 +507,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
uint32_t nbNodes; uint32_t nbNodes;
tryReadLong(nbNodes, file, "%s: Cannot read number of nodes: %s", fileName); tryReadLong(nbNodes, file, "%s: Cannot read number of nodes: %s", fileName);
nodes[fileID].resize(nbNodes); nodes[fileID].resize(nbNodes);
verbosePrint("Reading %u nodes...\n", nbNodes); verbosePrint(VERB_INFO, "Reading %u nodes...\n", nbNodes);
for (uint32_t i = nbNodes; i--;) { for (uint32_t i = nbNodes; i--;) {
readFileStackNode(file, nodes[fileID], i, fileName); readFileStackNode(file, nodes[fileID], i, fileName);
} }
@@ -515,7 +516,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
std::vector<Symbol> &fileSymbols = symbolLists.emplace_front(nbSymbols); std::vector<Symbol> &fileSymbols = symbolLists.emplace_front(nbSymbols);
std::vector<uint32_t> nbSymPerSect(nbSections, 0); std::vector<uint32_t> nbSymPerSect(nbSections, 0);
verbosePrint("Reading %" PRIu32 " symbols...\n", nbSymbols); verbosePrint(VERB_INFO, "Reading %" PRIu32 " symbols...\n", nbSymbols);
for (uint32_t i = 0; i < nbSymbols; ++i) { for (uint32_t i = 0; i < nbSymbols; ++i) {
// Read symbol // Read symbol
Symbol &symbol = fileSymbols[i]; Symbol &symbol = fileSymbols[i];
@@ -531,7 +532,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
// This file's sections, stored in a table to link symbols to them // This file's sections, stored in a table to link symbols to them
std::vector<std::unique_ptr<Section>> fileSections(nbSections); std::vector<std::unique_ptr<Section>> fileSections(nbSections);
verbosePrint("Reading %" PRIu32 " sections...\n", nbSections); verbosePrint(VERB_INFO, "Reading %" PRIu32 " sections...\n", nbSections);
for (uint32_t i = 0; i < nbSections; ++i) { for (uint32_t i = 0; i < nbSections; ++i) {
// Read section // Read section
fileSections[i] = std::make_unique<Section>(); fileSections[i] = std::make_unique<Section>();
@@ -543,7 +544,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
uint32_t nbAsserts; uint32_t nbAsserts;
tryReadLong(nbAsserts, file, "%s: Cannot read number of assertions: %s", fileName); tryReadLong(nbAsserts, file, "%s: Cannot read number of assertions: %s", fileName);
verbosePrint("Reading %" PRIu32 " assertions...\n", nbAsserts); verbosePrint(VERB_INFO, "Reading %" PRIu32 " assertions...\n", nbAsserts);
for (uint32_t i = 0; i < nbAsserts; ++i) { for (uint32_t i = 0; i < nbAsserts; ++i) {
Assertion &assertion = patch_AddAssertion(); Assertion &assertion = patch_AddAssertion();

View File

@@ -10,6 +10,7 @@
#include "helpers.hpp" // assume, clz, ctz #include "helpers.hpp" // assume, clz, ctz
#include "linkdefs.hpp" #include "linkdefs.hpp"
#include "opmath.hpp" #include "opmath.hpp"
#include "verbosity.hpp"
#include "link/main.hpp" #include "link/main.hpp"
#include "link/section.hpp" #include "link/section.hpp"
@@ -462,7 +463,7 @@ Assertion &patch_AddAssertion() {
} }
void patch_CheckAssertions() { void patch_CheckAssertions() {
verbosePrint("Checking assertions...\n"); verbosePrint(VERB_NOTICE, "Checking assertions...\n");
for (Assertion &assert : assertions) { for (Assertion &assert : assertions) {
int32_t value = computeRPNExpr(assert.patch, *assert.fileSymbols); int32_t value = computeRPNExpr(assert.patch, *assert.fileSymbols);
@@ -505,7 +506,7 @@ void patch_CheckAssertions() {
// Applies all of a section's patches to a data section // Applies all of a section's patches to a data section
static void applyFilePatches(Section &section, Section &dataSection) { static void applyFilePatches(Section &section, Section &dataSection) {
verbosePrint("Patching section \"%s\"...\n", section.name.c_str()); verbosePrint(VERB_INFO, "Patching section \"%s\"...\n", section.name.c_str());
for (Patch &patch : section.patches) { for (Patch &patch : section.patches) {
int32_t value = computeRPNExpr(patch, *section.fileSymbols); int32_t value = computeRPNExpr(patch, *section.fileSymbols);
uint16_t offset = patch.offset + section.offset; uint16_t offset = patch.offset + section.offset;

69
src/verbosity.cpp Normal file
View File

@@ -0,0 +1,69 @@
#include "verbosity.hpp"
#include <array>
#include <bitset>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
static Verbosity verbosity = VERB_NONE;
void incrementVerbosity() {
if (verbosity < VERB_VVVVVV) {
verbosity = static_cast<Verbosity>(verbosity + 1);
}
}
bool checkVerbosity(Verbosity level) {
return verbosity >= level;
}
void printVVVVVVerbosity() {
if (!checkVerbosity(VERB_VVVVVV)) {
return;
}
putc('\n', stderr);
// clang-format off: vertically align values
static std::array<std::bitset<10>, 21> gfx{
0b0111111110,
0b1111111111,
0b1110011001,
0b1110011001,
0b1111111111,
0b1111111111,
0b1110000001,
0b1111000011,
0b0111111110,
0b0001111000,
0b0111111110,
0b1111111111,
0b1111111111,
0b1111111111,
0b1101111011,
0b1101111011,
0b0011111100,
0b0011001100,
0b0111001110,
0b0111001110,
0b0111001110,
};
// clang-format on
static std::array<char const *, 3> textbox{
" ,----------------------------------------.",
" | Augh, dimensional interference again?! |",
" `----------------------------------------'",
};
for (size_t i = 0; i < gfx.size(); ++i) {
std::bitset<10> const &row = gfx[i];
for (uint8_t j = row.size(); j--;) {
// Double the pixel horizontally, otherwise the aspect ratio looks wrong
fputs(row[j] ? "00" : " ", stderr);
}
if (i < textbox.size()) {
fputs(textbox[i], stderr);
}
putc('\n', stderr);
}
putc('\n', stderr);
}