diff --git a/.gitignore b/.gitignore index 3c6f9cc7..6e7ad76b 100644 --- a/.gitignore +++ b/.gitignore @@ -387,4 +387,6 @@ dreamcast/aud2adpcm* dreamcast/repack-data dreamcast/output.map dreamcast/dca3.ds.iso +dreamcast/git-version.h +dreamcast/git-version.tmp .DS_Store diff --git a/dreamcast/Makefile b/dreamcast/Makefile index 0617a240..9930fbfa 100644 --- a/dreamcast/Makefile +++ b/dreamcast/Makefile @@ -40,6 +40,8 @@ AUDIO_STREAM_OPTION=-q MKDCDISC_PAD_OPTION= endif +all: $(TARGET) + include common.mk OBJS = $(RE3_OBJS) $(RW_OBJS) \ @@ -100,10 +102,6 @@ OBJS_NO_FAST_MATH = \ KOS_CPPFLAGS += -fbuiltin -ffast-math -ffp-contract=fast \ -mfsrra -mfsca -# The rm-elf step is to remove the target before building, to force the -# re-creation of the rom disk. -all: $(TARGET) - ifdef KOS_BASE include $(KOS_BASE)/Makefile.rules else @@ -157,7 +155,7 @@ $(OBJS_NO_FAST_MATH): %.o: %.cpp $(TARGET): $(OBJS) kos-c++ -o $(TARGET) $(OBJS) -Wl,--gc-sections -Wl,--as-needed -Wl,-Map,output.map \ - -flto=auto $(if $(WITH_IDE),-lkosfat) $(if $(WITH_SD),-lkosfat) + -flto=auto $(if $(WITH_IDE),-lkosfat) $(if $(WITH_SD),-lkosfat) -Wl,--build-id=sha1 @echo && echo && echo "*** Build Completed Successfully ***" && echo && echo run: $(TARGET) @@ -355,6 +353,7 @@ STREAM_WAV_DECODED = $(addprefix $(REPACK_STREAM_DECODED_DIR)/, $(STREAM_WAV)) $(REPACK_DIR)/repacked: $(REPACK_GTA_DIR)/models/gta3.img $(REPACK_GTA_DIR)/models/gta3.dir $(LOOSE_FILES_DC) $(STREAM_ADPCM_DC) $(SFX_DC_RAW) $(SFX_DC_DSC) mkdir -p $(@D) + @git archive --format zip --output "$(REPACK_GTA_DIR)/DCA3-$(GIT_VERSION).zip" HEAD @touch $@ @echo && echo && echo "*** Repack Completed Successfully ***" && echo && echo diff --git a/dreamcast/common.mk b/dreamcast/common.mk index 2cacf927..13fccece 100644 --- a/dreamcast/common.mk +++ b/dreamcast/common.mk @@ -1,3 +1,28 @@ +GIT_VERSION := $(shell git describe --always --tags --long --dirty 2>/dev/null || echo "NO_GIT") +CI_JOB_ID ?= NO_CI + + +git-version.tmp: + @echo "Generating git-version.tmp with GIT_VERSION = \"$(GIT_VERSION)\"" + @echo "#pragma once" > git-version.tmp + @echo "#ifndef VERSION_H" >> git-version.tmp + @echo "#define VERSION_H" >> git-version.tmp + @echo "#define GIT_VERSION \"$(GIT_VERSION)\"" >> git-version.tmp + @echo "#define CI_JOB_ID \"$(CI_JOB_ID)\"" >> git-version.tmp + @echo "#endif // VERSION_H" >> git-version.tmp + +git-version.h: git-version.tmp + @if [ ! -f git-version.h ] || ! cmp -s git-version.tmp git-version.h; then \ + echo "Updating git-version.h"; \ + cp git-version.tmp git-version.h; \ + else \ + echo "git-version.h is up to date. No change."; \ + fi + +.PHONY: git-version.tmp + +../src/skel/dc/dc.cpp: git-version.h + # List all of your C files here, but change the extension to ".o" # Include "romdisk.o" if you want a rom disk. diff --git a/dreamcast/sim.mk b/dreamcast/sim.mk index 883fd032..9a2b78f4 100644 --- a/dreamcast/sim.mk +++ b/dreamcast/sim.mk @@ -3,6 +3,8 @@ TARGET ?= dca3-sim.elf IS_MAC := $(shell uname -s | grep -i "darwin" > /dev/null && echo "yes" || echo "no") +all: $(TARGET) + include common.mk OBJS = $(RE3_OBJS) $(RW_OBJS) \ @@ -52,8 +54,6 @@ else $(CXX) -msse2 -mfpmath=sse -c -O3 -g -fno-pic -no-pie -o $@ $(CXXFLAGS) -I../vendor/koshle -I../vendor/emu -m32 -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -D_FILE_OFFSET_BITS=64 $< endif -all: $(TARGET) - clean: -rm -f $(OBJS_SIM) $(TARGET) diff --git a/src/core/Frontend.cpp b/src/core/Frontend.cpp index 2045c34b..78b57467 100644 --- a/src/core/Frontend.cpp +++ b/src/core/Frontend.cpp @@ -35,6 +35,7 @@ #include "FileLoader.h" #include "frontendoption.h" #include "IniFile.h" +#include "../skel/dc/dc.h" // Game has colors inlined in code. // For easier modification we collect them here: @@ -1930,6 +1931,15 @@ CMenuManager::Draw() CSprite2d::DrawRect(CRect(MENU_X_LEFT_ALIGNED(181), MENU_Y(99), MENU_X_LEFT_ALIGNED(229), MENU_Y(122)), CRGBA(m_PrefsPlayerRed, m_PrefsPlayerGreen, m_PrefsPlayerBlue, FadeIn(255))); } + char strver[200]; + wchar ustr[200]; + snprintf(strver, sizeof(strver), "DCA3: %s", getExecutableTag()); + AsciiToUnicode(strver, ustr); + + CFont::SetScale(MENU_X(MENU_TEXT_SIZE_X), MENU_Y(MENU_TEXT_SIZE_Y)); + CFont::SetColor(CRGBA(MENUOPTION_COLOR.r, MENUOPTION_COLOR.g, MENUOPTION_COLOR.b, FadeIn(255))); + CFont::PrintString(MENU_X_LEFT_ALIGNED(BUILDID_TEXT_LEFT_MARGIN), SCREEN_SCALE_FROM_BOTTOM(BUILDID_TEXT_BOTTOM_MARGIN), ustr); + } int diff --git a/src/core/Frontend.h b/src/core/Frontend.h index 18f0f246..dbb86a8d 100644 --- a/src/core/Frontend.h +++ b/src/core/Frontend.h @@ -40,6 +40,9 @@ #define HELPER_TEXT_LEFT_MARGIN 320.0f #define HELPER_TEXT_BOTTOM_MARGIN 120.0f +#define BUILDID_TEXT_LEFT_MARGIN 320.0f +#define BUILDID_TEXT_BOTTOM_MARGIN 20.0f + #define PLAYERSETUP_LIST_TOP 28.0f #define PLAYERSETUP_LIST_BOTTOM 125.0f #define PLAYERSETUP_LIST_LEFT 200.0f diff --git a/src/core/re3.cpp b/src/core/re3.cpp index cf5036a7..0a1f8629 100644 --- a/src/core/re3.cpp +++ b/src/core/re3.cpp @@ -1196,8 +1196,8 @@ void re3_assert(const char *expr, const char *filename, unsigned int lineno, con stacktrace(); dbgio_dev_select("fb"); sleep(1); - dbgio_printf("RE3 ASSERT FAILED\n\tFile: %s\n\tLine: %d\n\tFunction: %s\n\tExpression: %s\n",filename,lineno,func,expr); - dbgio_printf("POSIX error (may not be relevant): %s\n", strerror(errno)); + dbglog(DBG_CRITICAL, "RE3 ASSERT FAILED\n\tFile: %s\n\tLine: %d\n\tFunction: %s\n\tExpression: %s\n",filename,lineno,func,expr); + dbglog(DBG_CRITICAL, "POSIX error (may not be relevant): %s\n", strerror(errno)); stacktrace(); dbgio_flush(); abort(); diff --git a/src/skel/dc/dc.cpp b/src/skel/dc/dc.cpp index b3c8ac38..4f2e8ccb 100644 --- a/src/skel/dc/dc.cpp +++ b/src/skel/dc/dc.cpp @@ -62,6 +62,10 @@ long _dwOperatingSystemVersion; #include "AnimViewer.h" #include "Font.h" #include "MemoryMgr.h" +#include "../../dreamcast/git-version.h" +#include "dc.h" + +#include // This is defined on project-level, via premake5 or cmake #ifdef GET_KEYBOARD_INPUT_FROM_X11 @@ -1977,13 +1981,14 @@ __attribute__((noinline)) void stacktrace() { : "+r" (sp), "+r" (pr) : : ); - dbgio_printf("Stack trace: %p ", (void*)pr); + dbglog(DBG_CRITICAL, "DCA3: %s\n", getExecutableTag()); + dbglog(DBG_CRITICAL, "Stack trace: %p ", (void*)pr); int found = 0; if(!(sp & 3) && sp > 0x8c000000 && sp < _arch_mem_top) { char** sp_ptr = (char**)sp; for (int so = 0; so < 16384; so++) { if (uintptr_t(&sp_ptr[so]) >= _arch_mem_top) { - dbgio_printf("(@@%p) ", &sp_ptr[so]); + dbglog(DBG_CRITICAL, "(@@%p) ", &sp_ptr[so]); break; } if (sp_ptr[so] > (char*)0x8c000000 && sp_ptr[so] < etext) { @@ -1999,9 +2004,9 @@ __attribute__((noinline)) void stacktrace() { uint16_t instr = instrp[-2]; // BSR or BSRF or JSR @Rn ? if (((instr & 0xf000) == 0xB000) || ((instr & 0xf0ff) == 0x0003) || ((instr & 0xf0ff) == 0x400B)) { - dbgio_printf("%p ", instrp); + dbglog(DBG_CRITICAL, "%p ", instrp); if (found++ > 24) { - dbgio_printf("(@%p) ", &sp_ptr[so]); + dbglog(DBG_CRITICAL, "(@%p) ", &sp_ptr[so]); break; } } else { @@ -2011,15 +2016,83 @@ __attribute__((noinline)) void stacktrace() { // dbglog(DBG_CRITICAL, "Stack trace: %p (@%p): out of range\n", (void*)sp_ptr[so], &sp_ptr[so]); } } - dbgio_printf("end\n"); + dbglog(DBG_CRITICAL, "end\n"); } else { - dbgio_printf("(@%p)\n", (void*)sp); + dbglog(DBG_CRITICAL, "(@%p)\n", (void*)sp); } } + +#include +#include +#include +#include +#include + +extern "C" { + extern const unsigned char _build_id_start[]; + extern const unsigned char _build_id_end[]; +} + +std::string getBuildId() +{ + // Pointer to the start of the .note.gnu.build-id section + const unsigned char *p = _build_id_start; + + // Parse the ELF note header + struct NoteHeader { + uint32_t n_namesz; + uint32_t n_descsz; + uint32_t n_type; + }; + + // Read header fields (be careful with endianness if needed) + const auto* note = reinterpret_cast(p); + + // Move p beyond the note header + p += sizeof(NoteHeader); + + // Skip the "name" field + alignment (e.g. "GNU\0") + // name is note->n_namesz bytes, then align up to 4 bytes + auto nameBytes = (note->n_namesz + 3u) & ~3u; + p += nameBytes; + + // Now p should point to the actual build-id bytes, which are note->n_descsz in length. + const unsigned char* buildId = p; + auto buildIdSize = note->n_descsz; + + // Convert it to a hex string + std::ostringstream oss; + oss << std::hex << std::setfill('0'); + for (uint32_t i = 0; i < buildIdSize; i++) { + oss << std::setw(2) << static_cast(buildId[i]); + } + + return oss.str(); +} +#else +std::string getBuildId() { + return "non-dreamcast-build"; +} #endif + +const char* getSourceId() { + return GIT_VERSION; +} + +const char* getCIJobId() { + return CI_JOB_ID; +} + +static std::string executableTag = getBuildId().substr(0, 10) + ":" + getSourceId() + ":" + getCIJobId(); + +const char* getExecutableTag() { + return executableTag.c_str(); +} + int main(int argc, char *argv[]) { + dbglog(DBG_CRITICAL, "DCA3: %s\n", getExecutableTag()); #if !defined(DC_SIM) std::set_terminate([]() { fflush(stdout); @@ -2029,8 +2102,8 @@ main(int argc, char *argv[]) stacktrace(); dbgio_dev_select("fb"); sleep(1); - dbgio_printf("std::terminate() called\n"); - dbgio_printf("POSIX error (may not be relevant): %s\n", strerror(errno)); + dbglog(DBG_CRITICAL, "std::terminate() called\n"); + dbglog(DBG_CRITICAL, "POSIX error (may not be relevant): %s\n", strerror(errno)); stacktrace(); dbgio_flush(); diff --git a/src/skel/dc/dc.h b/src/skel/dc/dc.h new file mode 100644 index 00000000..a843f115 --- /dev/null +++ b/src/skel/dc/dc.h @@ -0,0 +1,7 @@ +#pragma once +#include + +std::string getBuildId(); +const char* getSourceId(); +const char* getCIJobId(); +const char* getExecutableTag(); \ No newline at end of file diff --git a/vendor/dca3-kos b/vendor/dca3-kos index 0b2de922..397acb31 160000 --- a/vendor/dca3-kos +++ b/vendor/dca3-kos @@ -1 +1 @@ -Subproject commit 0b2de9228be7debb7f5d37ecf6341a9a78bbf3af +Subproject commit 397acb31b1bdd702344081caccd0865fec21332b