mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Extend RGBASM and RGBLINK verbosity flags to have multiple levels like RGBGFX (#1772)
This commit is contained in:
9
Makefile
9
Makefile
@@ -74,7 +74,8 @@ rgbasm_obj := \
|
||||
src/extern/utf8decoder.o \
|
||||
src/linkdefs.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
|
||||
|
||||
@@ -95,7 +96,8 @@ rgblink_obj := \
|
||||
src/extern/utf8decoder.o \
|
||||
src/linkdefs.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
|
||||
|
||||
@@ -117,7 +119,8 @@ rgbgfx_obj := \
|
||||
src/gfx/reverse.o \
|
||||
src/gfx/rgba.o \
|
||||
src/gfx/warning.o \
|
||||
src/util.o
|
||||
src/util.o \
|
||||
src/verbosity.o
|
||||
|
||||
rgbasm: ${rgbasm_obj}
|
||||
$Q${CXX} ${REALLDFLAGS} -o $@ ${rgbasm_obj} ${REALCXXFLAGS} src/version.cpp
|
||||
|
||||
@@ -39,7 +39,7 @@ local args=(
|
||||
'(- : * options)'{-h,--help}'[Print help text and exit]'
|
||||
|
||||
'(-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]'
|
||||
|
||||
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'
|
||||
|
||||
@@ -49,6 +49,8 @@ struct FileStackNode {
|
||||
|
||||
struct MacroArgs;
|
||||
|
||||
void fstk_VerboseOutputConfig();
|
||||
|
||||
bool fstk_DumpCurrent();
|
||||
std::shared_ptr<FileStackNode> fstk_GetFileStack();
|
||||
std::shared_ptr<std::string> fstk_GetUniqueIDStr();
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#include "verbosity.hpp"
|
||||
|
||||
enum MissingInclude {
|
||||
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
|
||||
@@ -14,11 +16,11 @@ enum MissingInclude {
|
||||
};
|
||||
|
||||
struct Options {
|
||||
bool exportAll = false; // -E
|
||||
uint8_t fixPrecision = 16; // -Q
|
||||
size_t maxRecursionDepth = 64; // -r
|
||||
char binDigits[2] = {'0', '1'}; // -b
|
||||
char gfxDigits[4] = {'0', '1', '2', '3'}; // -g
|
||||
bool verbose = false; // -v
|
||||
FILE *dependFile = nullptr; // -M
|
||||
std::string targetFileName; // -MQ, -MT
|
||||
MissingInclude missingIncludeState = INC_ERROR; // -MC, -MG
|
||||
@@ -42,11 +44,4 @@ struct Options {
|
||||
|
||||
extern Options options;
|
||||
|
||||
#define verbosePrint(...) \
|
||||
do { \
|
||||
if (options.verbose) { \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif // RGBDS_ASM_MAIN_HPP
|
||||
|
||||
@@ -71,7 +71,6 @@ struct Symbol {
|
||||
|
||||
void sym_ForEach(void (*callback)(Symbol &));
|
||||
|
||||
void sym_SetExportAll(bool set);
|
||||
Symbol *sym_AddLocalLabel(std::string const &symName);
|
||||
Symbol *sym_AddLabel(std::string const &symName);
|
||||
Symbol *sym_AddAnonLabel();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "verbosity.hpp"
|
||||
|
||||
#include "gfx/rgba.hpp"
|
||||
|
||||
@@ -20,7 +21,6 @@ struct Options {
|
||||
bool allowMirroringX = false; // -X, -m
|
||||
bool allowMirroringY = false; // -Y, -m
|
||||
bool columnMajor = false; // -Z
|
||||
uint8_t verbosity = 0; // -v
|
||||
|
||||
std::string attrmap{}; // -a, -A
|
||||
std::optional<Rgba> bgColor{}; // -B
|
||||
@@ -42,6 +42,7 @@ struct Options {
|
||||
uint16_t height;
|
||||
uint32_t right() const { return left + width * 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)
|
||||
uint8_t basePalID = 0; // -l
|
||||
std::array<uint16_t, 2> maxNbTiles{UINT16_MAX, 0}; // -N
|
||||
@@ -56,18 +57,6 @@ struct Options {
|
||||
|
||||
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;
|
||||
uint8_t maxOpaqueColors() const { return nbColorsPerPal - hasTransparentPixels; }
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "linkdefs.hpp"
|
||||
#include "verbosity.hpp"
|
||||
|
||||
struct Options {
|
||||
bool isDmgMode; // -d
|
||||
@@ -25,20 +26,12 @@ struct Options {
|
||||
uint16_t scrambleWRAMX;
|
||||
uint16_t scrambleSRAM;
|
||||
bool is32kMode; // -t
|
||||
bool beVerbose; // -v
|
||||
bool isWRAM0Mode; // -w
|
||||
bool disablePadding; // -x
|
||||
};
|
||||
|
||||
extern Options options;
|
||||
|
||||
#define verbosePrint(...) \
|
||||
do { \
|
||||
if (options.beVerbose) { \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
struct FileStackNode {
|
||||
FileStackNodeType type;
|
||||
std::variant<
|
||||
|
||||
29
include/verbosity.hpp
Normal file
29
include/verbosity.hpp
Normal 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
|
||||
18
man/rgbasm.1
18
man/rgbasm.1
@@ -233,6 +233,24 @@ below).
|
||||
Print the version of the program and exit.
|
||||
.It Fl v , Fl \-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
|
||||
Set warning flag
|
||||
.Ar warning .
|
||||
|
||||
11
man/rgbgfx.1
11
man/rgbgfx.1
@@ -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:
|
||||
.Bl -enum -compact
|
||||
.It
|
||||
Print the
|
||||
.Nm
|
||||
prints out its configuration before doing anything.
|
||||
configuration before taking actions.
|
||||
.It
|
||||
A generic message is printed before doing most actions.
|
||||
Print a notice before significant actions.
|
||||
.It
|
||||
Some of the actions' intermediate results are printed.
|
||||
Print some of the actions' intermediate results.
|
||||
.It
|
||||
Some internal debug printing is enabled.
|
||||
Print some internal debug information.
|
||||
.It
|
||||
Print detailed internal information.
|
||||
.El
|
||||
The verbosity level does not go past 6.
|
||||
.Pp
|
||||
|
||||
@@ -114,7 +114,25 @@ Useful for ROMs that fit in 32 KiB.
|
||||
.It Fl V , Fl \-version
|
||||
Print the version of the program and exit.
|
||||
.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
|
||||
Set warning flag
|
||||
.Ar warning .
|
||||
|
||||
@@ -53,6 +53,7 @@ set(rgbasm_src
|
||||
"linkdefs.cpp"
|
||||
"opmath.cpp"
|
||||
"util.cpp"
|
||||
"verbosity.cpp"
|
||||
)
|
||||
|
||||
set(rgblink_src
|
||||
@@ -72,6 +73,7 @@ set(rgblink_src
|
||||
"linkdefs.cpp"
|
||||
"opmath.cpp"
|
||||
"util.cpp"
|
||||
"verbosity.cpp"
|
||||
)
|
||||
|
||||
set(rgbfix_src
|
||||
@@ -92,6 +94,7 @@ set(rgbgfx_src
|
||||
"gfx/rgba.cpp"
|
||||
"gfx/warning.cpp"
|
||||
"util.cpp"
|
||||
"verbosity.cpp"
|
||||
)
|
||||
|
||||
foreach(PROG "asm" "fix" "gfx" "link")
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "helpers.hpp"
|
||||
#include "linkdefs.hpp"
|
||||
#include "platform.hpp" // S_ISDIR (stat macro)
|
||||
#include "verbosity.hpp"
|
||||
|
||||
#include "asm/lexer.hpp"
|
||||
#include "asm/macro.hpp"
|
||||
@@ -88,6 +89,28 @@ bool fstk_DumpCurrent() {
|
||||
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() {
|
||||
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) {
|
||||
preIncludeNames.emplace_front(path);
|
||||
verbosePrint("Pre-included filename %s\n", path.c_str()); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
static bool isValidFilePath(std::string const &path) {
|
||||
@@ -308,7 +330,11 @@ bool fstk_FileError(std::string const &path, char const *functionName) {
|
||||
// LCOV_EXCL_START
|
||||
if (options.missingIncludeState == GEN_EXIT) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "util.hpp"
|
||||
#include "verbosity.hpp"
|
||||
|
||||
#include "asm/fixpoint.hpp"
|
||||
#include "asm/format.hpp"
|
||||
@@ -325,7 +326,7 @@ void LexerState::setFileAsNextState(std::string const &filePath, bool updateStat
|
||||
if (filePath == "-") {
|
||||
path = "<stdin>";
|
||||
content.emplace<BufferedContent>(STDIN_FILENO);
|
||||
verbosePrint("Opening stdin\n"); // LCOV_EXCL_LINE
|
||||
verbosePrint(VERB_INFO, "Opening stdin\n"); // LCOV_EXCL_LINE
|
||||
} else {
|
||||
struct stat statBuf;
|
||||
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);
|
||||
|
||||
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 {
|
||||
// LCOV_EXCL_START
|
||||
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 {
|
||||
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
|
||||
|
||||
@@ -371,7 +376,7 @@ void LexerState::setFileAsNextState(std::string const &filePath, bool updateStat
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
209
src/asm/main.cpp
209
src/asm/main.cpp
@@ -17,6 +17,7 @@
|
||||
#include "parser.hpp" // Generated from parser.y
|
||||
#include "usage.hpp"
|
||||
#include "util.hpp" // UpperMap
|
||||
#include "verbosity.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
#include "asm/charmap.hpp"
|
||||
@@ -28,23 +29,8 @@
|
||||
|
||||
Options options;
|
||||
|
||||
// Escapes Make-special chars from a string
|
||||
static std::string make_escape(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;
|
||||
}
|
||||
static char const *dependFileName = nullptr; // -M
|
||||
static std::unordered_map<std::string, std::vector<StateFeature>> stateFileSpecs; // -s
|
||||
|
||||
// Short options
|
||||
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
|
||||
|
||||
// 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
|
||||
static std::vector<StateFeature> parseStateFeatures(char *str) {
|
||||
std::vector<StateFeature> features;
|
||||
@@ -164,10 +292,6 @@ int main(int argc, char *argv[]) {
|
||||
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;) {
|
||||
switch (ch) {
|
||||
char *endptr;
|
||||
@@ -192,7 +316,7 @@ int main(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
sym_SetExportAll(true);
|
||||
options.exportAll = true;
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
@@ -211,21 +335,13 @@ int main(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
if (options.dependFile) {
|
||||
warnx("Overriding dependfile %s", dependFileName);
|
||||
if (dependFileName) {
|
||||
warnx(
|
||||
"Overriding dependency file %s",
|
||||
strcmp(dependFileName, "-") ? dependFileName : "<stdout>"
|
||||
);
|
||||
}
|
||||
if (strcmp("-", musl_optarg)) {
|
||||
options.dependFile = fopen(musl_optarg, "w");
|
||||
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;
|
||||
|
||||
case 'o':
|
||||
@@ -233,7 +349,6 @@ int main(int argc, char *argv[]) {
|
||||
warnx("Overriding output filename %s", options.objectFileName.c_str());
|
||||
}
|
||||
options.objectFileName = musl_optarg;
|
||||
verbosePrint("Output filename %s\n", options.objectFileName.c_str()); // LCOV_EXCL_LINE
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
@@ -295,7 +410,6 @@ int main(int argc, char *argv[]) {
|
||||
if (stateFileSpecs.find(name) != stateFileSpecs.end()) {
|
||||
warnx("Overriding state filename %s", name);
|
||||
}
|
||||
verbosePrint("State filename %s\n", name); // LCOV_EXCL_LINE
|
||||
stateFileSpecs.emplace(name, std::move(features));
|
||||
break;
|
||||
}
|
||||
@@ -306,7 +420,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
case 'v':
|
||||
// LCOV_EXCL_START
|
||||
options.verbose = true;
|
||||
incrementVerbosity();
|
||||
break;
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
@@ -352,7 +466,7 @@ int main(int argc, char *argv[]) {
|
||||
case 'T': {
|
||||
std::string newTarget = musl_optarg;
|
||||
if (depType == 'Q') {
|
||||
newTarget = make_escape(newTarget);
|
||||
newTarget = escapeMakeChars(newTarget);
|
||||
}
|
||||
if (!options.targetFileName.empty()) {
|
||||
options.targetFileName += ' ';
|
||||
@@ -373,6 +487,8 @@ int main(int argc, char *argv[]) {
|
||||
options.targetFileName = options.objectFileName;
|
||||
}
|
||||
|
||||
verboseOutputConfig(argc, argv);
|
||||
|
||||
if (argc == musl_optind) {
|
||||
usage.printAndExit("Please specify an input file (pass `-` to read from standard input)");
|
||||
} else if (argc != musl_optind + 1) {
|
||||
@@ -381,7 +497,20 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
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()) {
|
||||
fatal("Dependency files can only be created if a target file is specified with either "
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "asm/fstack.hpp"
|
||||
#include "asm/lexer.hpp"
|
||||
#include "asm/macro.hpp"
|
||||
#include "asm/main.hpp"
|
||||
#include "asm/output.hpp"
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
@@ -39,8 +40,6 @@ static char savedDATE[256];
|
||||
static char savedTIMESTAMP_ISO8601_LOCAL[256];
|
||||
static char savedTIMESTAMP_ISO8601_UTC[256];
|
||||
|
||||
static bool exportAll = false; // -E
|
||||
|
||||
bool sym_IsPC(Symbol const *sym) {
|
||||
return sym == PCSymbol;
|
||||
}
|
||||
@@ -497,7 +496,7 @@ static Symbol *addLabel(std::string const &symName) {
|
||||
sym->type = SYM_LABEL;
|
||||
sym->data = static_cast<int32_t>(sect_GetSymbolOffset());
|
||||
// Don't export anonymous labels
|
||||
if (exportAll && !symName.starts_with('!')) {
|
||||
if (options.exportAll && !symName.starts_with('!')) {
|
||||
sym->isExported = true;
|
||||
}
|
||||
sym->section = sect_GetSymbolSection();
|
||||
@@ -635,11 +634,6 @@ Symbol *sym_Ref(std::string const &symName) {
|
||||
return sym;
|
||||
}
|
||||
|
||||
// Set whether to export all relocatable symbols by default
|
||||
void sym_SetExportAll(bool set) {
|
||||
exportAll = set;
|
||||
}
|
||||
|
||||
// Define the built-in symbols
|
||||
void sym_Init(time_t now) {
|
||||
PCSymbol = &createSymbol("@"s);
|
||||
|
||||
@@ -65,13 +65,14 @@ static Usage usage(
|
||||
" [-n <rom_version>] [-p <pad_value>] [-r <ram_size>] [-t <title_str>]\n"
|
||||
" [-W warning] <file> ...\n"
|
||||
"Useful options:\n"
|
||||
" -m, --mbc-type <value> set the MBC type byte to this value; refer\n"
|
||||
" to the man page for a list of values\n"
|
||||
" -m, --mbc-type <value> set the MBC type byte to this value; `-m help'\n"
|
||||
" or `-m list' prints the accepted values\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"
|
||||
" -o, --output <path> set the output file\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"
|
||||
"For help, use `man rgbfix' or go to https://rgbds.gbdev.io/docs/\n"
|
||||
);
|
||||
|
||||
134
src/gfx/main.cpp
134
src/gfx/main.cpp
@@ -19,6 +19,7 @@
|
||||
#include "file.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "usage.hpp"
|
||||
#include "verbosity.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
#include "gfx/pal_spec.hpp"
|
||||
@@ -40,18 +41,6 @@ static struct LocalOptions {
|
||||
bool reverse;
|
||||
} 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
|
||||
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"
|
||||
" -u, --unique-tiles optimize out identical tiles\n"
|
||||
" -V, --version print RGBGFX version and exit\n"
|
||||
" -W, --warning <warning> enable or disable warnings\n"
|
||||
"\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
|
||||
case 'v':
|
||||
// LCOV_EXCL_START
|
||||
if (options.verbosity < Options::VERB_VVVVVV) {
|
||||
++options.verbosity;
|
||||
}
|
||||
incrementVerbosity();
|
||||
break;
|
||||
// LCOV_EXCL_STOP
|
||||
case 'W':
|
||||
@@ -580,93 +568,66 @@ static char *parseArgv(int argc, char *argv[]) {
|
||||
return nullptr; // Done processing this argv
|
||||
}
|
||||
|
||||
// LCOV_EXCL_START
|
||||
static void verboseOutputConfig() {
|
||||
if (!checkVerbosity(VERB_CONFIG)) {
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "rgbgfx %s\n", get_package_version_string());
|
||||
|
||||
if (options.verbosity >= Options::VERB_VVVVVV) {
|
||||
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);
|
||||
}
|
||||
printVVVVVVerbosity();
|
||||
|
||||
fputs("Options:\n", stderr);
|
||||
// -Z/--columns
|
||||
if (options.columnMajor) {
|
||||
fputs("\tVisit image in column-major order\n", stderr);
|
||||
}
|
||||
// -u/--unique-tiles
|
||||
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);
|
||||
}
|
||||
if (options.allowMirroringY) {
|
||||
// -Y/--mirror-y
|
||||
else if (options.allowMirroringY) {
|
||||
fputs("\tAllow deduplicating vertically mirrored tiles\n", stderr);
|
||||
}
|
||||
// -C/--color-curve
|
||||
if (options.useColorCurve) {
|
||||
fputs("\tUse color curve\n", stderr);
|
||||
}
|
||||
// -d/--depth
|
||||
fprintf(stderr, "\tBit depth: %" PRIu8 "bpp\n", options.bitDepth);
|
||||
// -x/--trim-end
|
||||
if (options.trim != 0) {
|
||||
fprintf(stderr, "\tTrim the last %" PRIu64 " tiles\n", options.trim);
|
||||
}
|
||||
// -n/--nb-palettes
|
||||
fprintf(stderr, "\tMaximum %" PRIu16 " palettes\n", options.nbPalettes);
|
||||
// -s/--palette-size
|
||||
fprintf(stderr, "\tPalettes contain %" PRIu8 " colors\n", options.nbColorsPerPal);
|
||||
// -c/--colors
|
||||
if (options.palSpecType != Options::NO_SPEC) {
|
||||
fprintf(stderr, "\t%s palette spec\n", [] {
|
||||
switch (options.palSpecType) {
|
||||
case Options::NO_SPEC:
|
||||
return "No";
|
||||
case Options::EXPLICIT:
|
||||
return "Explicit";
|
||||
case Options::EMBEDDED:
|
||||
return "Embedded";
|
||||
case Options::DMG:
|
||||
return "DMG";
|
||||
}
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}());
|
||||
}
|
||||
if (options.palSpecType == Options::EXPLICIT) {
|
||||
fputs("\t[\n", stderr);
|
||||
for (auto const &pal : options.palSpec) {
|
||||
@@ -682,6 +643,8 @@ static void verboseOutputConfig() {
|
||||
}
|
||||
fputs("\t]\n", stderr);
|
||||
}
|
||||
// -L/--slice
|
||||
if (options.inputSlice.specified()) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"\tInput image slice: %" PRIu16 "x%" PRIu16 " pixels starting at (%" PRIu16 ", %" PRIu16
|
||||
@@ -691,31 +654,54 @@ static void verboseOutputConfig() {
|
||||
options.inputSlice.left,
|
||||
options.inputSlice.top
|
||||
);
|
||||
}
|
||||
// -b/--base-tiles
|
||||
if (options.baseTileIDs[0] || options.baseTileIDs[1]) {
|
||||
fprintf(
|
||||
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[1]
|
||||
);
|
||||
}
|
||||
// -l/--base-palette
|
||||
if (options.basePalID) {
|
||||
fprintf(stderr, "\tBase palette ID: %" PRIu8 "\n", options.basePalID);
|
||||
}
|
||||
// -N/--nb-tiles
|
||||
fprintf(
|
||||
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[1]
|
||||
);
|
||||
// -O/--group-outputs (influences other options)
|
||||
auto printPath = [](char const *name, std::string const &path) {
|
||||
if (!path.empty()) {
|
||||
fprintf(stderr, "\t%s: %s\n", name, path.c_str());
|
||||
}
|
||||
};
|
||||
// file
|
||||
printPath("Input image", options.input);
|
||||
// -i/--input-tileset
|
||||
printPath("Input tileset", options.inputTileset);
|
||||
// -o/--output
|
||||
printPath("Output tile data", options.output);
|
||||
// -t/--tilemap or -T/--auto-tilemap
|
||||
printPath("Output tilemap", options.tilemap);
|
||||
// -a/--attrmap or -A/--auto-attrmap
|
||||
printPath("Output attrmap", options.attrmap);
|
||||
// -p/--palette or -P/--auto-palette
|
||||
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);
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
// Manual implementation of std::filesystem::path.replace_extension().
|
||||
// macOS <10.15 did not support std::filesystem::path.
|
||||
@@ -839,11 +825,7 @@ int main(int argc, char *argv[]) {
|
||||
parseExternalPalSpec(localOptions.externalPalSpec);
|
||||
}
|
||||
|
||||
// LCOV_EXCL_START
|
||||
if (options.verbosity >= Options::VERB_CFG) {
|
||||
verboseOutputConfig();
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
verboseOutputConfig(); // LCOV_EXCL_LINE
|
||||
|
||||
// Do not do anything if option parsing went wrong.
|
||||
requireZeroErrors();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "verbosity.hpp"
|
||||
|
||||
#include "gfx/color_set.hpp"
|
||||
#include "gfx/main.hpp"
|
||||
@@ -247,6 +248,10 @@ public:
|
||||
static void verboseOutputAssignments(
|
||||
std::vector<AssignedSets> const &assignments, std::vector<ColorSet> const &colorSets
|
||||
) {
|
||||
if (!checkVerbosity(VERB_INFO)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (AssignedSets const &assignment : assignments) {
|
||||
fputs("{ ", stderr);
|
||||
for (ColorSetAttrs const &attrs : assignment) {
|
||||
@@ -284,9 +289,7 @@ static void decant(std::vector<AssignedSets> &assignments, std::vector<ColorSet>
|
||||
}
|
||||
};
|
||||
|
||||
options.verbosePrint(
|
||||
Options::VERB_DEBUG, "%zu palettes before decanting\n", assignments.size()
|
||||
);
|
||||
verbosePrint(VERB_DEBUG, "%zu palettes before decanting\n", assignments.size());
|
||||
|
||||
// Decant on palettes
|
||||
decantOn([&colorSets](AssignedSets &to, AssignedSets &from) {
|
||||
@@ -298,9 +301,7 @@ static void decant(std::vector<AssignedSets> &assignments, std::vector<ColorSet>
|
||||
from.clear();
|
||||
}
|
||||
});
|
||||
options.verbosePrint(
|
||||
Options::VERB_DEBUG, "%zu palettes after decanting on palettes\n", assignments.size()
|
||||
);
|
||||
verbosePrint(VERB_DEBUG, "%zu palettes after decanting on palettes\n", assignments.size());
|
||||
|
||||
// Decant on "components" (color sets sharing colors)
|
||||
decantOn([&colorSets](AssignedSets &to, AssignedSets &from) {
|
||||
@@ -344,8 +345,8 @@ static void decant(std::vector<AssignedSets> &assignments, std::vector<ColorSet>
|
||||
}
|
||||
}
|
||||
});
|
||||
options.verbosePrint(
|
||||
Options::VERB_DEBUG, "%zu palettes after decanting on \"components\"\n", assignments.size()
|
||||
verbosePrint(
|
||||
VERB_DEBUG, "%zu palettes after decanting on \"components\"\n", assignments.size()
|
||||
);
|
||||
|
||||
// Decant on individual color sets
|
||||
@@ -357,15 +358,11 @@ static void decant(std::vector<AssignedSets> &assignments, std::vector<ColorSet>
|
||||
}
|
||||
}
|
||||
});
|
||||
options.verbosePrint(
|
||||
Options::VERB_DEBUG, "%zu palettes after decanting on color sets\n", assignments.size()
|
||||
);
|
||||
verbosePrint(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) {
|
||||
options.verbosePrint(
|
||||
Options::VERB_LOG_ACT, "Paginating palettes using \"overload-and-remove\" strategy...\n"
|
||||
);
|
||||
verbosePrint(VERB_NOTICE, "Paginating palettes using \"overload-and-remove\" strategy...\n");
|
||||
|
||||
// Sort the color sets by size, which improves the packing algorithm's efficiency
|
||||
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.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];
|
||||
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);
|
||||
options.verbosePrint(
|
||||
Options::VERB_TRACE,
|
||||
verbosePrint(
|
||||
VERB_TRACE,
|
||||
" Relative size to palette %zu (of %zu): %" PRIu32 " (size = %zu)\n",
|
||||
i,
|
||||
assignments.size(),
|
||||
@@ -420,8 +417,8 @@ std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet>
|
||||
|
||||
if (bestPalIndex == assignments.size()) {
|
||||
// Found nowhere to put it, create a new page containing just that one
|
||||
options.verbosePrint(
|
||||
Options::VERB_TRACE,
|
||||
verbosePrint(
|
||||
VERB_TRACE,
|
||||
"Assigning color set %zu to new palette %zu\n",
|
||||
attrs.colorSetIndex,
|
||||
bestPalIndex
|
||||
@@ -430,8 +427,8 @@ std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet>
|
||||
continue;
|
||||
}
|
||||
|
||||
options.verbosePrint(
|
||||
Options::VERB_TRACE,
|
||||
verbosePrint(
|
||||
VERB_TRACE,
|
||||
"Assigning color set %zu to palette %zu\n",
|
||||
attrs.colorSetIndex,
|
||||
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 relSize2 = bestPal.relSizeOf(colorSet2);
|
||||
|
||||
options.verbosePrint(
|
||||
Options::VERB_TRACE,
|
||||
verbosePrint(
|
||||
VERB_TRACE,
|
||||
" Color sets %zu <=> %zu: Efficiency: %zu / %" PRIu32 " <=> %zu / "
|
||||
"%" PRIu32 "\n",
|
||||
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)
|
||||
while (bestPal.volume() > options.maxOpaqueColors()) {
|
||||
options.verbosePrint(
|
||||
Options::VERB_TRACE,
|
||||
verbosePrint(
|
||||
VERB_TRACE,
|
||||
"Palette %zu is overloaded! (%zu > %" PRIu8 ")\n",
|
||||
bestPalIndex,
|
||||
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
|
||||
if (compareEfficiency(*minEfficiencyIter, *maxEfficiencyIter) == 0) {
|
||||
options.verbosePrint(Options::VERB_TRACE, " All efficiencies are identical\n");
|
||||
verbosePrint(VERB_TRACE, " All efficiencies are identical\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// Remove the color set with minimal efficiency
|
||||
options.verbosePrint(
|
||||
Options::VERB_TRACE, " Removing color set %zu\n", minEfficiencyIter->colorSetIndex
|
||||
verbosePrint(
|
||||
VERB_TRACE, " Removing color set %zu\n", minEfficiencyIter->colorSetIndex
|
||||
);
|
||||
queue.emplace(std::move(*minEfficiencyIter));
|
||||
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);
|
||||
});
|
||||
if (iter == assignments.end()) { // No such page, create a new one
|
||||
options.verbosePrint(
|
||||
Options::VERB_DEBUG,
|
||||
verbosePrint(
|
||||
VERB_DEBUG,
|
||||
"Adding new palette (%zu) for overflowing color set %zu\n",
|
||||
assignments.size(),
|
||||
attrs.colorSetIndex
|
||||
);
|
||||
assignments.emplace_back(colorSets, std::move(attrs));
|
||||
} else {
|
||||
options.verbosePrint(
|
||||
Options::VERB_DEBUG,
|
||||
verbosePrint(
|
||||
VERB_DEBUG,
|
||||
"Assigning overflowing color set %zu to palette %zu\n",
|
||||
attrs.colorSetIndex,
|
||||
iter - assignments.begin()
|
||||
@@ -544,21 +541,13 @@ std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet>
|
||||
}
|
||||
}
|
||||
|
||||
// LCOV_EXCL_START
|
||||
if (options.verbosity >= Options::VERB_INTERM) {
|
||||
verboseOutputAssignments(assignments, colorSets);
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
verboseOutputAssignments(assignments, colorSets); // LCOV_EXCL_LINE
|
||||
|
||||
// "Decant" the result
|
||||
decant(assignments, colorSets);
|
||||
// Note that the result does not contain any empty palettes
|
||||
|
||||
// LCOV_EXCL_START
|
||||
if (options.verbosity >= Options::VERB_INTERM) {
|
||||
verboseOutputAssignments(assignments, colorSets);
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
verboseOutputAssignments(assignments, colorSets); // LCOV_EXCL_LINE
|
||||
|
||||
std::vector<size_t> mappings(colorSets.size());
|
||||
for (size_t i = 0; i < assignments.size(); ++i) {
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "verbosity.hpp"
|
||||
|
||||
#include "gfx/main.hpp"
|
||||
|
||||
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) {
|
||||
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(
|
||||
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
|
||||
// we should only have a single palette.
|
||||
@@ -59,7 +60,7 @@ static unsigned int luminance(uint16_t color) {
|
||||
}
|
||||
|
||||
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) {
|
||||
// Sort from lightest to darkest
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <png.h>
|
||||
|
||||
#include "verbosity.hpp"
|
||||
|
||||
#include "gfx/main.hpp"
|
||||
#include "gfx/rgba.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) {
|
||||
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;
|
||||
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
|
||||
}
|
||||
|
||||
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_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";
|
||||
}
|
||||
};
|
||||
options.verbosePrint(
|
||||
Options::VERB_INTERM,
|
||||
verbosePrint(
|
||||
VERB_INFO,
|
||||
"PNG image: %" PRIu32 "x%" PRIu32 " pixels, %dbpp %s, %s\n",
|
||||
width,
|
||||
height,
|
||||
@@ -139,18 +141,15 @@ Png::Png(char const *filename, std::streambuf &file) {
|
||||
);
|
||||
}
|
||||
|
||||
options.verbosePrint(
|
||||
Options::VERB_INTERM, "Embedded PNG palette has %d colors: [", nbColors
|
||||
);
|
||||
if (checkVerbosity(VERB_INFO)) {
|
||||
fprintf(stderr, "Embedded PNG palette has %d colors: [", nbColors);
|
||||
for (int i = 0; i < nbColors; ++i) {
|
||||
if (i > 0) {
|
||||
options.verbosePrint(Options::VERB_INTERM, ", ");
|
||||
fprintf(stderr, "%s#%08x", i > 0 ? ", " : "", palette[i].toCSS());
|
||||
}
|
||||
options.verbosePrint(Options::VERB_INTERM, "#%08x", palette[i].toCSS());
|
||||
fprintf(stderr, "]\n");
|
||||
}
|
||||
options.verbosePrint(Options::VERB_INTERM, "]\n");
|
||||
} 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
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "file.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "itertools.hpp"
|
||||
#include "verbosity.hpp"
|
||||
|
||||
#include "gfx/color_set.hpp"
|
||||
#include "gfx/main.hpp"
|
||||
@@ -79,8 +80,8 @@ struct Image {
|
||||
bool isSuitableForGrayscale() const {
|
||||
// Check that all of the grays don't fall into the same "bin"
|
||||
if (colors.size() > options.maxOpaqueColors()) { // Apply the Pigeonhole Principle
|
||||
options.verbosePrint(
|
||||
Options::VERB_DEBUG,
|
||||
verbosePrint(
|
||||
VERB_DEBUG,
|
||||
"Too many colors for grayscale sorting (%zu > %" PRIu8 ")\n",
|
||||
colors.size(),
|
||||
options.maxOpaqueColors()
|
||||
@@ -93,8 +94,8 @@ struct Image {
|
||||
continue;
|
||||
}
|
||||
if (!color->isGray()) {
|
||||
options.verbosePrint(
|
||||
Options::VERB_DEBUG,
|
||||
verbosePrint(
|
||||
VERB_DEBUG,
|
||||
"Found non-gray color #%08x, not using grayscale sorting\n",
|
||||
color->toCSS()
|
||||
);
|
||||
@@ -102,8 +103,8 @@ struct Image {
|
||||
}
|
||||
uint8_t mask = 1 << color->grayIndex();
|
||||
if (bins & mask) { // Two in the same bin!
|
||||
options.verbosePrint(
|
||||
Options::VERB_DEBUG,
|
||||
verbosePrint(
|
||||
VERB_DEBUG,
|
||||
"Color #%08x conflicts with another one, not using grayscale sorting\n",
|
||||
color->toCSS()
|
||||
);
|
||||
@@ -336,7 +337,7 @@ static std::tuple<std::vector<size_t>, std::vector<Palette>>
|
||||
assume(mappings.size() == colorSets.size());
|
||||
|
||||
// LCOV_EXCL_START
|
||||
if (options.verbosity >= Options::VERB_INTERM) {
|
||||
if (checkVerbosity(VERB_INFO)) {
|
||||
fprintf(
|
||||
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) {
|
||||
// LCOV_EXCL_START
|
||||
if (options.verbosity >= Options::VERB_INTERM) {
|
||||
if (checkVerbosity(VERB_INFO)) {
|
||||
for (Palette const &palette : palettes) {
|
||||
fputs("{ ", stderr);
|
||||
for (uint16_t colorIndex : palette) {
|
||||
@@ -897,7 +898,7 @@ static void
|
||||
}
|
||||
|
||||
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<Palette> palettes;
|
||||
@@ -907,13 +908,13 @@ void processPalettes() {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// LCOV_EXCL_START
|
||||
if (options.verbosity >= Options::VERB_INTERM) {
|
||||
if (checkVerbosity(VERB_INFO)) {
|
||||
fputs("Image colors: [ ", stderr);
|
||||
for (std::optional<Rgba> const &slot : image.colors) {
|
||||
if (!slot.has_value()) {
|
||||
@@ -1025,14 +1026,14 @@ void process() {
|
||||
continue_visiting_tiles:;
|
||||
}
|
||||
|
||||
options.verbosePrint(
|
||||
Options::VERB_INTERM,
|
||||
verbosePrint(
|
||||
VERB_INFO,
|
||||
"Image contains %zu color set%s\n",
|
||||
colorSets.size(),
|
||||
colorSets.size() != 1 ? "s" : ""
|
||||
);
|
||||
// LCOV_EXCL_START
|
||||
if (options.verbosity >= Options::VERB_INTERM) {
|
||||
if (checkVerbosity(VERB_INFO)) {
|
||||
for (ColorSet const &colorSet : colorSets) {
|
||||
fputs("[ ", stderr);
|
||||
for (uint16_t color : colorSet) {
|
||||
@@ -1074,20 +1075,19 @@ continue_visiting_tiles:;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (!options.tilemap.empty() || !options.attrmap.empty() || !options.palmap.empty()) {
|
||||
options.verbosePrint(
|
||||
Options::VERB_LOG_ACT,
|
||||
"Generating unoptimized tilemap and/or attrmap and/or palmap...\n"
|
||||
verbosePrint(
|
||||
VERB_NOTICE, "Generating unoptimized tilemap and/or attrmap and/or palmap...\n"
|
||||
);
|
||||
outputUnoptimizedMaps(attrmap, mappings);
|
||||
}
|
||||
} else {
|
||||
// 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);
|
||||
|
||||
if (size_t nbTiles = tiles.size();
|
||||
@@ -1101,22 +1101,22 @@ continue_visiting_tiles:;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (!options.tilemap.empty()) {
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized tilemap...\n");
|
||||
verbosePrint(VERB_NOTICE, "Generating optimized tilemap...\n");
|
||||
outputTilemap(attrmap);
|
||||
}
|
||||
|
||||
if (!options.attrmap.empty()) {
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized attrmap...\n");
|
||||
verbosePrint(VERB_NOTICE, "Generating optimized attrmap...\n");
|
||||
outputAttrmap(attrmap, mappings);
|
||||
}
|
||||
|
||||
if (!options.palmap.empty()) {
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized palmap...\n");
|
||||
verbosePrint(VERB_NOTICE, "Generating optimized palmap...\n");
|
||||
outputPalmap(attrmap, mappings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "diagnostics.hpp"
|
||||
#include "file.hpp"
|
||||
#include "helpers.hpp" // assume
|
||||
#include "verbosity.hpp"
|
||||
|
||||
#include "gfx/main.hpp"
|
||||
#include "gfx/warning.hpp"
|
||||
@@ -98,7 +99,7 @@ static void printPalette(std::array<std::optional<Rgba>, 4> const &palette) {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
uint8_t tileSize = 8 * options.bitDepth;
|
||||
if (tiles.size() % tileSize != 0) {
|
||||
@@ -140,13 +141,13 @@ void reverse() {
|
||||
|
||||
// By default, assume tiles are not deduplicated, and add the (allegedly) trimmed tiles
|
||||
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
|
||||
std::optional<std::vector<uint8_t>> tilemap;
|
||||
if (!options.tilemap.empty()) {
|
||||
tilemap = readInto(options.tilemap);
|
||||
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) {
|
||||
@@ -172,7 +173,7 @@ void reverse() {
|
||||
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 (options.trim == 0 && !tilemap) {
|
||||
@@ -192,9 +193,7 @@ void reverse() {
|
||||
}
|
||||
height = mapSize / width;
|
||||
|
||||
options.verbosePrint(
|
||||
Options::VERB_INTERM, "Reversed image dimensions: %zux%zu tiles\n", width, height
|
||||
);
|
||||
verbosePrint(VERB_INFO, "Reversed image dimensions: %zux%zu tiles\n", width, height);
|
||||
|
||||
Rgba const grayColors[4] = {
|
||||
Rgba(0xFFFFFFFF), Rgba(0xAAAAAAFF), Rgba(0x555555FF), Rgba(0x000000FF)
|
||||
@@ -320,8 +319,8 @@ void reverse() {
|
||||
}
|
||||
}
|
||||
|
||||
options.verbosePrint(
|
||||
Options::VERB_INTERM,
|
||||
verbosePrint(
|
||||
VERB_INFO,
|
||||
"Number of tiles in bank {0: %" PRIu16 ", 1: %" PRIu16 "}\n",
|
||||
nbTilesInBank[0],
|
||||
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;
|
||||
if (!pngFile.open(options.input, std::ios::out | std::ios::binary)) {
|
||||
// LCOV_EXCL_START
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "itertools.hpp"
|
||||
#include "linkdefs.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "verbosity.hpp"
|
||||
|
||||
#include "link/main.hpp"
|
||||
#include "link/output.hpp"
|
||||
@@ -405,7 +406,7 @@ static std::vector<Section const *> checkOverlayCompat() {
|
||||
}
|
||||
|
||||
void assign_AssignSections() {
|
||||
verbosePrint("Beginning assignment...\n");
|
||||
verbosePrint(VERB_NOTICE, "Beginning assignment...\n");
|
||||
|
||||
// Initialize assignment
|
||||
initFreeSpace();
|
||||
@@ -443,7 +444,7 @@ void assign_AssignSections() {
|
||||
// Assign sections in decreasing constraint order
|
||||
for (uint8_t constraints = std::size(unassignedSections); constraints--;) {
|
||||
if (char const *constraintName = constraintNames[constraints]; constraintName) {
|
||||
verbosePrint("Assigning %sconstrained sections...\n", constraintName);
|
||||
verbosePrint(VERB_INFO, "Assigning %sconstrained sections...\n", constraintName);
|
||||
} else {
|
||||
assume(unassignedSections[constraints].empty());
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "script.hpp" // Generated from script.y
|
||||
#include "usage.hpp"
|
||||
#include "util.hpp" // UpperMap, printChar
|
||||
#include "verbosity.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
#include "link/assign.hpp"
|
||||
@@ -31,28 +32,7 @@
|
||||
|
||||
Options options;
|
||||
|
||||
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 char const *linkerScriptName = nullptr; // -l
|
||||
|
||||
// Short options
|
||||
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"
|
||||
" -p, --pad <value> set the value to pad between sections with\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"
|
||||
"For help, use `man rgblink' or go to https://rgbds.gbdev.io/docs/\n"
|
||||
);
|
||||
// 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) {
|
||||
// clang-format off: vertically align nested initializers
|
||||
static UpperMap<std::pair<uint16_t *, uint16_t>> scrambleSpecs{
|
||||
{"ROMX", std::pair{&options.scrambleROMX, 65535}},
|
||||
{"SRAM", std::pair{&options.scrambleSRAM, 255} },
|
||||
{"WRAMX", std::pair{&options.scrambleWRAMX, 7} },
|
||||
{"SRAM", std::pair{&options.scrambleSRAM, 255 }},
|
||||
{"WRAMX", std::pair{&options.scrambleWRAMX, 7 }},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// Skip leading whitespace before the regions.
|
||||
spec += strspn(spec, " \t");
|
||||
@@ -211,8 +304,6 @@ static void parseScrambleSpec(char *spec) {
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char const *linkerScriptName = nullptr; // -l
|
||||
|
||||
// Parse options
|
||||
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
|
||||
switch (ch) {
|
||||
@@ -283,7 +374,7 @@ int main(int argc, char *argv[]) {
|
||||
// LCOV_EXCL_STOP
|
||||
case 'v':
|
||||
// LCOV_EXCL_START
|
||||
options.beVerbose = true;
|
||||
incrementVerbosity();
|
||||
break;
|
||||
// LCOV_EXCL_STOP
|
||||
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 (curArgIndex == argc) {
|
||||
if (musl_optind == argc) {
|
||||
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,
|
||||
for (obj_Setup(argc - curArgIndex); curArgIndex < argc; ++curArgIndex) {
|
||||
obj_ReadFile(argv[curArgIndex], argc - curArgIndex - 1);
|
||||
obj_Setup(argc - musl_optind);
|
||||
for (int i = musl_optind; i < argc; ++i) {
|
||||
obj_ReadFile(argv[i], argc - i - 1);
|
||||
}
|
||||
|
||||
// apply the linker script's modifications,
|
||||
if (linkerScriptName) {
|
||||
verbosePrint("Reading linker script...\n");
|
||||
verbosePrint(VERB_NOTICE, "Reading linker script...\n");
|
||||
|
||||
if (lexer_Init(linkerScriptName)) {
|
||||
yy::parser parser;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "helpers.hpp"
|
||||
#include "linkdefs.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "verbosity.hpp"
|
||||
#include "version.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);
|
||||
}
|
||||
|
||||
verbosePrint("Reading object file %s\n", fileName);
|
||||
verbosePrint(VERB_NOTICE, "Reading object file %s\n", fileName);
|
||||
|
||||
uint32_t revNum;
|
||||
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;
|
||||
tryReadLong(nbNodes, file, "%s: Cannot read number of nodes: %s", fileName);
|
||||
nodes[fileID].resize(nbNodes);
|
||||
verbosePrint("Reading %u nodes...\n", nbNodes);
|
||||
verbosePrint(VERB_INFO, "Reading %u nodes...\n", nbNodes);
|
||||
for (uint32_t i = nbNodes; i--;) {
|
||||
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<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) {
|
||||
// Read symbol
|
||||
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
|
||||
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) {
|
||||
// Read section
|
||||
fileSections[i] = std::make_unique<Section>();
|
||||
@@ -543,7 +544,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
|
||||
|
||||
uint32_t nbAsserts;
|
||||
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) {
|
||||
Assertion &assertion = patch_AddAssertion();
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "helpers.hpp" // assume, clz, ctz
|
||||
#include "linkdefs.hpp"
|
||||
#include "opmath.hpp"
|
||||
#include "verbosity.hpp"
|
||||
|
||||
#include "link/main.hpp"
|
||||
#include "link/section.hpp"
|
||||
@@ -462,7 +463,7 @@ Assertion &patch_AddAssertion() {
|
||||
}
|
||||
|
||||
void patch_CheckAssertions() {
|
||||
verbosePrint("Checking assertions...\n");
|
||||
verbosePrint(VERB_NOTICE, "Checking assertions...\n");
|
||||
|
||||
for (Assertion &assert : assertions) {
|
||||
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
|
||||
static void applyFilePatches(Section §ion, 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) {
|
||||
int32_t value = computeRPNExpr(patch, *section.fileSymbols);
|
||||
uint16_t offset = patch.offset + section.offset;
|
||||
|
||||
69
src/verbosity.cpp
Normal file
69
src/verbosity.cpp
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user