[libromdata/tests] GcnFstPrint, WiiUFstPrint: Use libgsvt.
Some checks are pending
Codecov / run (push) Waiting to run
CodeQL / Analyze (cpp) (push) Waiting to run

The Unicode text printing on Windows via _fputts() was causing the
console output to lock up on Windows 7. Switching to libgsvt fixes it.

Code size difference: (64-bit Gentoo Linux, gcc-15.1.0, release build, no LTO)

   text    data     bss     dec     hex filename
  16247    1032      48   17327    43af GcnFstPrint [before]
  19006    1160      80   20246    4f16 GcnFstPrint [after]
  +2759    +128     +32   +2919    +b67 Difference

   text    data     bss     dec     hex filename
  15417    1016      48   16481    4061 WiiUFstPrint [before]
  18210    1144      80   19434    4bea WiiUFstPrint [after]
  +2793    +128     +32   +2953    +b89 Difference
This commit is contained in:
David Korth 2025-05-11 04:13:24 -04:00
parent 665a9f878c
commit 43a7bd4a49
3 changed files with 54 additions and 65 deletions

View File

@ -40,6 +40,7 @@ ADD_EXECUTABLE(GcnFstPrint
disc/FstPrint.hpp disc/FstPrint.hpp
) )
TARGET_LINK_LIBRARIES(GcnFstPrint PRIVATE rpsecure romdata) TARGET_LINK_LIBRARIES(GcnFstPrint PRIVATE rpsecure romdata)
TARGET_LINK_LIBRARIES(GcnFstPrint PRIVATE gsvt)
IF(ENABLE_NLS) IF(ENABLE_NLS)
TARGET_LINK_LIBRARIES(GcnFstPrint PRIVATE i18n) TARGET_LINK_LIBRARIES(GcnFstPrint PRIVATE i18n)
ENDIF(ENABLE_NLS) ENDIF(ENABLE_NLS)
@ -87,6 +88,7 @@ ADD_EXECUTABLE(WiiUFstPrint
disc/FstPrint.hpp disc/FstPrint.hpp
) )
TARGET_LINK_LIBRARIES(WiiUFstPrint PRIVATE rpsecure romdata) TARGET_LINK_LIBRARIES(WiiUFstPrint PRIVATE rpsecure romdata)
TARGET_LINK_LIBRARIES(WiiUFstPrint PRIVATE gsvt)
IF(ENABLE_NLS) IF(ENABLE_NLS)
TARGET_LINK_LIBRARIES(WiiUFstPrint PRIVATE i18n) TARGET_LINK_LIBRARIES(WiiUFstPrint PRIVATE i18n)
ENDIF(ENABLE_NLS) ENDIF(ENABLE_NLS)

View File

@ -14,6 +14,9 @@ using LibRomData::GcnFst;
// i18n // i18n
#include "libi18n/i18n.h" #include "libi18n/i18n.h"
// libgsvt for VT handling
#include "gsvtpp.hpp"
// C includes (C++ namespace) // C includes (C++ namespace)
#include <cerrno> #include <cerrno>
#include <cstdio> #include <cstdio>
@ -68,14 +71,19 @@ int RP_C_API main(int argc, char *argv[])
setlocale(LC_CTYPE, "C"); setlocale(LC_CTYPE, "C");
#endif /* _WIN32 */ #endif /* _WIN32 */
// Detect console information.
// NOTE: Technically not needed, since Gsvt::Console access
// will call this for us...
gsvt_init();
// Initialize i18n. // Initialize i18n.
rp_i18n_init(); rp_i18n_init();
if (argc < 2 || argc > 3) { if (argc < 2 || argc > 3) {
fmt::print(stderr, FRUN(C_("GcnFstPrint", "Syntax: {:s} fst.bin [offsetShift]")), argv[0]); Gsvt::StdErr.fputs(fmt::format(FRUN(C_("GcnFstPrint", "Syntax: {:s} fst.bin [offsetShift]")), argv[0]));
fputc('\n', stderr); Gsvt::StdErr.newline();
fputs(C_("GcnFstPrint", "offsetShift should be 0 for GameCube, 2 for Wii. (default is 0)"), stderr); Gsvt::StdErr.fputs(C_("GcnFstPrint", "offsetShift should be 0 for GameCube, 2 for Wii. (default is 0)"));
fputc('\n', stderr); Gsvt::StdErr.newline();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -85,10 +93,10 @@ int RP_C_API main(int argc, char *argv[])
char *endptr = nullptr; char *endptr = nullptr;
long ltmp = strtol(argv[2], &endptr, 10); long ltmp = strtol(argv[2], &endptr, 10);
if (*endptr != '\0' || (ltmp != 0 && ltmp != 2)) { if (*endptr != '\0' || (ltmp != 0 && ltmp != 2)) {
fmt::print(stderr, FRUN(C_("GcnFstPrint", "Invalid offset shift '{:s}' specified.")), argv[2]); Gsvt::StdErr.fputs(fmt::format(FRUN(C_("GcnFstPrint", "Invalid offset shift '{:s}' specified.")), argv[2]));
fputc('\n', stderr); Gsvt::StdErr.newline();
fputs(C_("GcnFstPrint", "offsetShift should be 0 for GameCube, 2 for Wii. (default is 0)"), stderr); Gsvt::StdErr.fputs(C_("GcnFstPrint", "offsetShift should be 0 for GameCube, 2 for Wii. (default is 0)"));
fputc('\n', stderr); Gsvt::StdErr.newline();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
offsetShift = static_cast<uint8_t>(ltmp); offsetShift = static_cast<uint8_t>(ltmp);
@ -98,8 +106,8 @@ int RP_C_API main(int argc, char *argv[])
FILE *f = fopen(argv[1], "rb"); FILE *f = fopen(argv[1], "rb");
if (!f) { if (!f) {
// tr: {0:s} == filename, {1:s} == error message // tr: {0:s} == filename, {1:s} == error message
fmt::print(stderr, FRUN(C_("GcnFstPrint", "Error opening '{0:s}': '{1:s}'")), argv[1], strerror(errno)); Gsvt::StdErr.fputs(fmt::format(FRUN(C_("GcnFstPrint", "Error opening '{0:s}': '{1:s}'")), argv[1], strerror(errno)));
fputc('\n', stderr); Gsvt::StdErr.newline();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -107,8 +115,8 @@ int RP_C_API main(int argc, char *argv[])
fseeko(f, 0, SEEK_END); fseeko(f, 0, SEEK_END);
const off64_t fileSize_o = ftello(f); const off64_t fileSize_o = ftello(f);
if (fileSize_o > (16*1024*1024)) { if (fileSize_o > (16*1024*1024)) {
fputs(C_("GcnFstPrint", "ERROR: FST is too big. (Maximum of 16 MB.)"), stderr); Gsvt::StdErr.fputs(C_("GcnFstPrint", "ERROR: FST is too big. (Maximum of 16 MB.)"));
fputc('\n', stderr); Gsvt::StdErr.newline();
fclose(f); fclose(f);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -121,9 +129,9 @@ int RP_C_API main(int argc, char *argv[])
fclose(f); fclose(f);
if (rd_size != fileSize) { if (rd_size != fileSize) {
// tr: {0:d} == number of bytes read, {1:d} == number of bytes expected to read // tr: {0:d} == number of bytes read, {1:d} == number of bytes expected to read
fmt::print(stderr, FRUN(C_("GcnFstPrint", "ERROR: Read {0:Ld} bytes, expected {1:Ld} bytes.")), Gsvt::StdErr.fputs(fmt::format(FRUN(C_("GcnFstPrint", "ERROR: Read {0:Ld} bytes, expected {1:Ld} bytes.")),
rd_size, fileSize); rd_size, fileSize));
fputc('\n', stderr); Gsvt::StdErr.newline();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -145,35 +153,20 @@ int RP_C_API main(int argc, char *argv[])
unique_ptr<IFst> fst(new GcnFst(&fstData[fst_start_offset], unique_ptr<IFst> fst(new GcnFst(&fstData[fst_start_offset],
static_cast<uint32_t>(fileSize - fst_start_offset), offsetShift)); static_cast<uint32_t>(fileSize - fst_start_offset), offsetShift));
if (!fst->isOpen()) { if (!fst->isOpen()) {
fmt::print(stderr, FRUN(C_("GcnFstPrint", "*** ERROR: Could not parse '{:s}' as GcnFst.")), argv[1]); Gsvt::StdErr.fputs(fmt::format(FRUN(C_("GcnFstPrint", "*** ERROR: Could not parse '{:s}' as GcnFst.")), argv[1]));
fputc('\n', stderr); Gsvt::StdErr.newline();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// Print the FST to an ostringstream. // Print the FST to an ostringstream.
ostringstream oss; ostringstream oss;
LibRomData::fstPrint(fst.get(), oss); LibRomData::fstPrint(fst.get(), oss);
const string fst_str = oss.str(); Gsvt::StdOut.fputs(oss.str());
#ifdef _WIN32
// FIXME: isatty() might not work properly on Win8+ with MinGW.
// Reference: https://lists.gnu.org/archive/html/bug-gnulib/2013-01/msg00007.html
if (isatty(fileno(stdout))) {
// Convert to wchar_t, then print it.
_fputts(U82T_s(fst_str), stdout);
} else {
// Writing to file. Print the original UTF-8.
fputs(fst_str.c_str(), stdout);
}
#else /* !_WIN32 */
// Print the FST.
fputs(fst_str.c_str(), stdout);
#endif
if (fst->hasErrors()) { if (fst->hasErrors()) {
fputc('\n', stderr); Gsvt::StdErr.newline();
fputs(C_("GcnFstPrint", "*** WARNING: FST has errors and may be unusable."), stderr); Gsvt::StdErr.fputs(C_("GcnFstPrint", "*** WARNING: FST has errors and may be unusable."));
fputc('\n', stderr); Gsvt::StdErr.newline();
} }
// Cleanup. // Cleanup.

View File

@ -16,6 +16,9 @@ using LibRomData::WiiUFst;
// i18n // i18n
#include "libi18n/i18n.h" #include "libi18n/i18n.h"
// libgsvt for VT handling
#include "gsvtpp.hpp"
// C includes (C++ namespace) // C includes (C++ namespace)
#include <cerrno> #include <cerrno>
#include <cstdio> #include <cstdio>
@ -69,12 +72,17 @@ int RP_C_API main(int argc, char *argv[])
setlocale(LC_CTYPE, "C"); setlocale(LC_CTYPE, "C");
#endif /* _WIN32 */ #endif /* _WIN32 */
// Detect console information.
// NOTE: Technically not needed, since Gsvt::Console access
// will call this for us...
gsvt_init();
// Initialize i18n. // Initialize i18n.
rp_i18n_init(); rp_i18n_init();
if (argc < 2 || argc > 3) { if (argc < 2 || argc > 3) {
fmt::print(stderr, FRUN(C_("WiiUFstPrint", "Syntax: {:s} fst.bin")), argv[0]); Gsvt::StdErr.fputs(fmt::format(FRUN(C_("WiiUFstPrint", "Syntax: {:s} fst.bin")), argv[0]));
fputc('\n', stderr); Gsvt::StdErr.newline();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -82,8 +90,8 @@ int RP_C_API main(int argc, char *argv[])
FILE *f = fopen(argv[1], "rb"); FILE *f = fopen(argv[1], "rb");
if (!f) { if (!f) {
// tr: {0:s} == filename, {1:s} == error message // tr: {0:s} == filename, {1:s} == error message
fmt::print(stderr, FRUN(C_("GcnFstPrint", "Error opening '{0:s}': '{1:s}'")), argv[1], strerror(errno)); Gsvt::StdErr.fputs(fmt::format(FRUN(C_("GcnFstPrint", "Error opening '{0:s}': '{1:s}'")), argv[1], strerror(errno)));
fputc('\n', stderr); Gsvt::StdErr.newline();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -91,8 +99,8 @@ int RP_C_API main(int argc, char *argv[])
fseeko(f, 0, SEEK_END); fseeko(f, 0, SEEK_END);
const off64_t fileSize_o = ftello(f); const off64_t fileSize_o = ftello(f);
if (fileSize_o > (16*1024*1024)) { if (fileSize_o > (16*1024*1024)) {
fputs(C_("GcnFstPrint", "ERROR: FST is too big. (Maximum of 16 MB.)"), stderr); Gsvt::StdErr.fputs(C_("GcnFstPrint", "ERROR: FST is too big. (Maximum of 16 MB.)"));
fputc('\n', stderr); Gsvt::StdErr.newline();
fclose(f); fclose(f);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -105,9 +113,9 @@ int RP_C_API main(int argc, char *argv[])
fclose(f); fclose(f);
if (rd_size != fileSize) { if (rd_size != fileSize) {
// tr: {0:Ld} == number of bytes read, {1:Ld} == number of bytes expected to read // tr: {0:Ld} == number of bytes read, {1:Ld} == number of bytes expected to read
fmt::print(stderr, FRUN(C_("GcnFstPrint", "ERROR: Read {0:Ld} bytes, expected {1:Ld} bytes.")), Gsvt::StdErr.fputs(fmt::format(FRUN(C_("GcnFstPrint", "ERROR: Read {0:Ld} bytes, expected {1:Ld} bytes.")),
rd_size, fileSize); rd_size, fileSize));
fputc('\n', stderr); Gsvt::StdErr.newline();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -116,8 +124,8 @@ int RP_C_API main(int argc, char *argv[])
// "look" like an FST? // "look" like an FST?
unique_ptr<IFst> fst(new WiiUFst(fstData.get(), static_cast<uint32_t>(fileSize))); unique_ptr<IFst> fst(new WiiUFst(fstData.get(), static_cast<uint32_t>(fileSize)));
if (!fst->isOpen()) { if (!fst->isOpen()) {
fmt::print(stderr, FRUN(C_("WiiUFstPrint", "*** ERROR: Could not parse '{:s}' as WiiUFst.")), argv[1]); Gsvt::StdErr.fputs(fmt::format(FRUN(C_("WiiUFstPrint", "*** ERROR: Could not parse '{:s}' as WiiUFst.")), argv[1]));
fputc('\n', stderr); Gsvt::StdErr.newline();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -125,26 +133,12 @@ int RP_C_API main(int argc, char *argv[])
ostringstream oss; ostringstream oss;
LibRomData::fstPrint(fst.get(), oss, true); LibRomData::fstPrint(fst.get(), oss, true);
const string fst_str = oss.str(); const string fst_str = oss.str();
Gsvt::StdOut.fputs(oss.str());
#ifdef _WIN32
// FIXME: isatty() might not work properly on Win8+ with MinGW.
// Reference: https://lists.gnu.org/archive/html/bug-gnulib/2013-01/msg00007.html
if (isatty(fileno(stdout))) {
// Convert to wchar_t, then print it.
_fputts(U82T_s(fst_str), stdout);
} else {
// Writing to file. Print the original UTF-8.
fputs(fst_str.c_str(), stdout);
}
#else /* !_WIN32 */
// Print the FST.
fputs(fst_str.c_str(), stdout);
#endif
if (fst->hasErrors()) { if (fst->hasErrors()) {
fputc('\n', stderr); Gsvt::StdErr.newline();
fputs(C_("WiiUFstPrint", "*** WARNING: FST has errors and may be unusable."), stderr); Gsvt::StdErr.fputs(C_("WiiUFstPrint", "*** WARNING: FST has errors and may be unusable."));
fputc('\n', stderr); Gsvt::StdErr.newline();
} }
// Cleanup. // Cleanup.