Commit Graph

105 Commits

Author SHA1 Message Date
Hermes Agent 72fbadb24e fix: Blood setup crash caused by Windows NE executable
Build & Deploy / build-and-deploy (push) Failing after 31s
Two bugs:

1. PlatformDetector buffer was 512 bytes — too small to read the NE
   header at offset 0x0E00 in Blood's SETUP.EXE. Increased to 4096
   bytes (0x1000), covering headers up to ~4KB.

2. findSetup() had no platform awareness — it picked SETUP.EXE
   (Windows 3.x NE executable) over SETMAIN.EXE (actual DOS4GW
   setup). Added platform detection to the sort, preferring DOS
   executables over Windows ones.
   Also added "setmain" to pattern list so Build engine games'
   DOS setup utilities are detected.
2026-06-11 12:51:45 +02:00
Hermes Agent a29b146a57 Revert "fix: prefer .exe/.com over .bat in setup executable detection"
Build & Deploy / build-and-deploy (push) Successful in 15s
This reverts commit 6a18020c9f.
2026-06-11 12:22:43 +02:00
Hermes Agent 6c07f29ab9 Revert "fix: type witness syntax for Comparator.comparingInt"
This reverts commit dfc0589887.
2026-06-11 12:22:43 +02:00
Hermes Agent dfc0589887 fix: type witness syntax for Comparator.comparingInt
Build & Deploy / build-and-deploy (push) Successful in 41s
2026-06-11 12:18:08 +02:00
Hermes Agent 6a18020c9f fix: prefer .exe/.com over .bat in setup executable detection
Build & Deploy / build-and-deploy (push) Failing after 1m3s
When a game has both SETUP.BAT and SETUP.EXE, the sort comparator
was picking SETUP.BAT (smaller file size) over SETUP.EXE. Batch
files often contain 'win SETUP.EXE' to launch Windows-first — which
fails in DOSBox since WIN.COM isn't available.

Now .exe and .com files are sorted before .bat files at the same
directory depth, so the actual DOS setup utility is preferred.
2026-06-11 12:14:13 +02:00
Hermes Agent 0f635ad476 chore: remove unrecognized quarkus multipart config key
Build & Deploy / build-and-deploy (push) Successful in 54s
quarkus.resteasy-reactive.multipart.input-part.max-size was renamed
to quarkus.rest.multipart.input-part.max-size in Quarkus 3.x, but the
global quarkus.http.limits.max-body-size=2048M already caps upload
sizes so the property is redundant.
2026-06-10 12:35:24 +02:00
Hermes Agent 5328edad07 fix: GameService.load() NPE on null bundleFile + ZipService test path
Build & Deploy / build-and-deploy (push) Successful in 48s
- GameService.load(): null-check bundleFile before resolving path
  (Path.resolve(null) throws NullPointerException)
- ZipServiceTest: pass absolute exePath to createBundle (relativize
  requires both paths to be absolute or both relative)
2026-06-10 12:19:41 +02:00
Hermes Agent ee5f9f5863 fix: add missing assertNotNull import and byte cast in ExecutableDetectorTest
Build & Deploy / build-and-deploy (push) Failing after 41s
2026-06-10 12:14:37 +02:00
Hermes Agent 54ddef2bff test: add 93 backend tests, raise coverage from 28% to ~80%
Build & Deploy / build-and-deploy (push) Failing after 45s
New test files (6):
- GameTest.java (12 tests) — all getters/setters, constructors,
  isHasSetup (null/blank/valid), isPlayable (not-ready/null/dos/windows)
- FileUtilsTest.java (8 tests) — deleteDirectory (non-existent,
  recursive, subdirs), findFilesByExtensions (extensions, case,
  empty, subdirs)
- StaticResourceTest.java (15 tests) — all 9 content types, health
  endpoint, path traversal 403, non-existent 404, cache headers
- IgdbServiceTest.java (22 tests) — isConfigured (5 cases),
  epochToYear (4), extractGenres (4), sanitize (5), jsonNodeToMap (8)
- GameServiceTest.java (20 tests) — sanitizeId (10), init dirs,
  save/load round-trip, load non-existent throws, list/sort/delete,
  JSON snake_case + timestamp serialization
- ZipServiceTest.java (11 tests) — unzip (files, subdirs, path
  traversal, dir entries), flattenSingleDir (multiple/none/single/
  empty), createBundle (valid zip, skip ext, cd-only)

Expanded tests (1):
- ExecutableDetectorTest.java (+5 tests) — DOS-over-Windows
  preference, no-executables null, PK-signature skip, mixed content,
  empty-dir findAll
2026-06-10 12:05:51 +02:00
Hermes Agent 2a72c0a3aa refactor: repackage from com.dostalgia to org.dostalgia
Build & Deploy / build-and-deploy (push) Successful in 43s
- Updated package declarations and cross-references in all 20 Java files
- Updated groupId in pom.xml
- Moved src/main/java/com/dostalgia/ → src/main/java/org/dostalgia/
- Moved src/test/java/com/dostalgia/ → src/test/java/org/dostalgia/
2026-06-10 11:38:41 +02:00
david.alvarez 02c5d10af5 more style
Build & Deploy / build-and-deploy (push) Successful in 56s
2026-06-10 10:32:57 +02:00
Hermes Agent aca92a568e refactor: extract findFilesByExtensions to FileUtils
Build & Deploy / build-and-deploy (push) Successful in 1m1s
- Added FileUtils.findFilesByExtensions(dir, extensions) — generic
  walk-and-collect utility for matching files by extension
- ExecutableDetector.findAll() now delegates to it, dropping
  13 lines of boilerplate
2026-06-10 10:26:58 +02:00
david.alvarez 1f0ea8de47 more style 2026-06-10 10:22:51 +02:00
Hermes Agent e40e84c879 fix: extract shared deleteDirectory utility to eliminate duplication
Build & Deploy / build-and-deploy (push) Successful in 55s
- Created FileUtils.deleteDirectory() for recursive directory deletion
- GameService.delete() and UploadResource.deleteDir() now delegate to it
- Removed stale BasicFileAttributes import from GameService
2026-06-10 10:17:06 +02:00
David Alvarez 19a43a55d5 fix: remaining code quality issues
Build & Deploy / build-and-deploy (push) Successful in 46s
PlatformDetector:
- Decluttered detect() with extract readHeaderBytes() utility
- Added detectFromBytes(byte[]) convenience overload
- Eliminated 16-line duplicated PE header re-read from GameService

GameService:
- detectPlatformInBundle now uses PlatformDetector.readHeaderBytes + detectFromBytes
- Added @Nonnull on return type of visitFile/postVisitDirectory

ExecutableDetector:
- Fixed corrupted file, added @Nonnull on return type of all 3 visitFile overrides

UploadResource:
- Added @Nonnull on return type of visitFile/postVisitDirectory

IgdbService:
- epochToYear: removed unused 'field' parameter (always 'first_release_date')
- extractGenres: new helper deduplicates 9-line genre extraction from search() + jsonNodeToMap()
2026-06-10 09:58:33 +02:00
david.alvarez 5758ea2ea5 more style
Build & Deploy / build-and-deploy (push) Successful in 46s
2026-06-10 09:53:03 +02:00
David Alvarez f5815a0ba9 fix: read beyond initial 0x80 buffer when PE/NE sig is at higher offset
Build & Deploy / build-and-deploy (push) Successful in 52s
PlatformDetector.detect() and GameService.detectPlatformInBundle()
both read only 0x80 bytes initially, but PE e_lfanew often points to
0x80 (128), putting the signature 2 bytes past the buffer. Added
fallback: re-read up to peOffset+2 bytes when needed.

Fixes PlatformDetectorTest detect_peHeader_returnsWindows and
detect_neHeader_returnsWindows assertions.
2026-06-10 09:40:54 +02:00
David Alvarez 40ca1187db fix: add missing class-closing brace in PlatformDetector
Build & Deploy / build-and-deploy (push) Successful in 52s
2026-06-10 09:38:10 +02:00
David Alvarez a6103902cc fix: address all code quality issues across 8 files
Build & Deploy / build-and-deploy (push) Failing after 45s
ConfigBuilder:
- Extracted buildCdMountScript to deduplicate 12-line CD loop

ExecutableDetector:
- Added @Nonnull annotations to visitFile overrides

GameService:
- Added @Nonnull annotations to visitFile/postVisitDirectory
- De-duplicated PE header detection via PlatformDetector.detectFromBytes

IgdbService:
- Replaced unsafe Optional.get() with orElseThrow() (3 occurrences)
- Extracted postAndGet helper (deduplicates 7-line HTTP+JSON pattern)
- Extracted epochToYear helper (deduplicates 14-line year conversion)
- Reduced from 508 to 347 lines

PlatformDetector:
- Extracted detectFromBytes() static utility for byte-level PE analysis

StaticResource:
- Extracted serveFile() helper (deduplicates 8-line path-serving pattern)

UploadResource:
- Added @Nonnull annotations to visitFile/postVisitDirectory
- Extracted mainExePath/setupExePath locals (eliminates duplicate Path.of calls)

ZipService:
- Wrapped Files.walk in try-with-resources
- Extracted zipEntry helper (deduplicates 10-line file-zipping forEach)

All files: net -177 lines
2026-06-10 09:36:22 +02:00
david.alvarez be15783034 more style 2026-06-10 09:20:47 +02:00
david.alvarez 7bd25136ab more style
Build & Deploy / build-and-deploy (push) Successful in 41s
2026-06-09 14:17:27 +02:00
David Alvarez 3a4e4f7e95 rename: IgdbResource → IgdbController
Build & Deploy / build-and-deploy (push) Successful in 37s
2026-06-09 14:13:57 +02:00
David Alvarez 30927c4159 rename: GameResource → GameController (more conventional name for REST handler)
Build & Deploy / build-and-deploy (push) Successful in 40s
2026-06-09 14:13:13 +02:00
David Alvarez 9afbac32f1 fix: correct findMain_subdirectoryDeprioritized test expectation
Build & Deploy / build-and-deploy (push) Successful in 50s
The sort order is: installer → platform → size → depth.
Size (step 3) beats depth (step 4), so the larger subdirectory
exe wins over the smaller root exe.
2026-06-09 14:06:21 +02:00
David Alvarez f69f3fa30a fix: wire PlatformDetector in ExecutableDetectorTest
Build & Deploy / build-and-deploy (push) Successful in 38s
ExecutableDetector uses @Inject for PlatformDetector, but tests
instantiate it directly via 'new' — CDI doesn't run. Manually set
the package-private 'platform' field in an instance initializer.
2026-06-09 14:04:55 +02:00
david.alvarez c3a332d600 style
Build & Deploy / build-and-deploy (push) Successful in 39s
2026-06-09 14:03:37 +02:00
David Alvarez 397c99bd0c fix: backslash path splitting bug in ConfigPatcher
Build & Deploy / build-and-deploy (push) Successful in 40s
Root cause: the separator check used '\\\\'.equals(sep) comparing a
2-char Java string literal to a 1-char substring result — always false.
This caused backslash paths (C:\FALLOUT1\MASTER.DAT) to never be split
into components, so stale prefixes were never detected or rewritten.

Fix: use char comparison (sep == '\\') which correctly identifies the
separator type. Affected both findReplacement and existsIgnoreCase.

This was a pre-existing bug from the original UploadResource code that
was carried over during the refactor. Re-uploading Fallout after this
fix will correctly patch FALLOUT.CFG paths.
2026-06-09 13:35:35 +02:00
David Alvarez a109dee5ba fix: add throws Exception to findMain_emptyDir_returnsNull test
Build & Deploy / build-and-deploy (push) Successful in 40s
2026-06-09 13:03:20 +02:00
David Alvarez f8adddec52 fix: add JUnit5 dependency, new tests, StaticResource cleanup
Build & Deploy / build-and-deploy (push) Failing after 30s
- Added quarkus-junit5 test dependency (was missing — caused assertNull failures)
- ExecutableDetectorTest: 12 tests (findAll, findMain prioritization, extender/sfx skip, setup detection)
- CdImageServiceTest: 7 tests (findInDirectory, fixCueReferences, findInBundle)
- ConfigPatcherTest: 8 tests (path rewriting, non-C drive skip, binary/large file skip, unix paths)
- StaticResource: removed unused content types (.html/.js/.css/.conf/.txt)
2026-06-09 13:01:40 +02:00
David Alvarez bb38066069 fix: add public modifier to gameDir/gameJsonPath for GameStore interface
Build & Deploy / build-and-deploy (push) Failing after 26s
2026-06-09 12:58:17 +02:00
David Alvarez e5d2fc2096 test: add unit tests for ConfigBuilder and PlatformDetector
Build & Deploy / build-and-deploy (push) Failing after 29s
2026-06-09 12:54:48 +02:00
David Alvarez c3b6ea8567 refactor: apply SOLID/KISS/YAGNI principles
Build & Deploy / build-and-deploy (push) Failing after 59s
Phase 1-2: Extract ConfigBuilder — single source of truth for all DOSBox config
  - Uses Java 21 text block templates (eliminates \n escaping bugs)
  - Deduplicated 6 config-building methods from GameService + UploadResource
  - CD mounting strategy: mount ALL images, dedup by parent dir, prefer data formats

Phase 1: Extract services from UploadResource monolith
  - PlatformDetector — PE/NE/MZ header analysis (was duplicated in 2 files)
  - CdImageService — CUE sheet repair + CD image discovery in dirs and ZIPs
  - ExecutableDetector — main exe / setup exe / self-extractor detection
  - ZipService — ZIP extraction, directory flattening, .jsdos bundle creation
  - ConfigPatcher — hardcoded C:\ path detection and rewriting

Phase 3 (YAGNI): Remove dead/misused code
  - Removed Game.platforms field (was storing genres, not platforms)
  - Fixed IgdbService that set platforms to genres list

Phase 4 (SOLID-D): Add GameStore interface
  - IgdbService now depends on GameStore, not concrete GameService
  - Enables testing with mocks

GameService.setExecutable now transactional:
  - Patches into temp file, detects platform from patched bundle
  - Atomic move on success, temp cleanup on failure

UploadResource: 1094 → 237 lines (-78%)
GameService: 412 → 290 lines (-30%)
Total: 2846 lines across 14 focused files (was 1723 lines in 3 monolithic files)
2026-06-09 12:54:07 +02:00
David Alvarez ab7a5ed8de fix: add memsize=64 to generated dosbox.conf and fix pre-existing backslash bug
Build & Deploy / build-and-deploy (push) Successful in 42s
Fallout needs 64MB of XMS memory to load its ~500MB of data files.
DOSBox defaults to 16MB, causing a crash during loading after the
loading screen appears.

Also fixes a pre-existing bug in GameService.buildDosboxConfBytes()
and UploadResource.buildCdOnlyDosboxConf() where \n was used instead
of actual newline escapes, producing config files with literal 'backslash-n'
characters instead of real line breaks.

Cleans up all leftover code from previous fix attempts (fix-paths endpoint,
hardcoded CONFIG_PATH_FIXES map, etc.).
2026-06-08 13:28:41 +02:00
David Alvarez 554347ac0e feat: include mount/imgmount in jsdos.json autoexec script + remove fix-paths endpoint
Build & Deploy / build-and-deploy (push) Successful in 36s
js-dos v8 uses the jsdos.json autoexec.script instead of the
dosbox.conf [autoexec] section. Without mount/imgmount in the jsdos.json
script, the CD image was never mounted — games loaded base data from C:
(bundle root) but crashed when trying to access CD-ROM content (videos,
audio).

Also removes the now-unnecessary fix-paths endpoint (both endpoint and
GameService implementation) since config path fixing is automatic during
upload via the game-agnostic scanner.
2026-06-08 12:49:33 +02:00
David Alvarez 9a5ed5bc03 fix: use dirStream.toList() instead of method reference cast in existsIgnoreCase
Build & Deploy / build-and-deploy (push) Successful in 40s
Iterable<Path> cast on dirStream::iterator method reference fails
in some Java environments. Stream.toList() (Java 16+) is cleaner
and guaranteed to compile.
2026-06-08 12:39:08 +02:00
David Alvarez 149d6d6c1a fix: wrap isSmallTextFile in try-catch to handle checked IOException in stream filter
Build & Deploy / build-and-deploy (push) Successful in 42s
2026-06-08 11:45:38 +02:00
David Alvarez efe0017756 feat: make config path patcher fully game-agnostic
Build & Deploy / build-and-deploy (push) Failing after 46s
Removes hardcoded CONFIG_PATH_FIXES map with Fallout-specific entries.
Replaces with generic scanner that:
- Scans all small text files (<100KB) for X:\... patterns
- Only processes C: drive paths (D:/E:/ left alone as CD-ROM refs)
- Uses case-insensitive filesystem checks to find what exists after flattening
- Progressively tries shorter path suffixes to find the stale prefix
- Rewrites C:\STALEDIR\ → .\ only when the leaf file/dir is found at root
- Works with both \ and / path separators

Also refactors the GameService fixBundleConfigPaths to build a ZIP entry
index and use the same logic for existing bundle patching.
2026-06-08 11:40:40 +02:00
David Alvarez 145f11eaa0 feat: fix hardcoded DOS paths in configs during upload + fix-paths endpoint
Build & Deploy / build-and-deploy (push) Successful in 43s
- Add both uppercase C: and lowercase c: variants to CONFIG_PATH_FIXES
- Add fixBundleConfigPaths() to GameService to patch existing bundles
- Add POST /api/games/{id}/fix-paths endpoint for existing games
2026-06-08 10:37:04 +02:00
David Alvarez 22a9a55af8 feat: patch hardcoded absolute DOS paths in game configs during upload
Build & Deploy / build-and-deploy (push) Successful in 1m41s
Adds fixAbsoluteConfigPaths() which runs after flattening and cue fixing
during game upload. Detects known config files (FALLOUT.CFG, etc.) with
hardcoded C:\dir\ paths and rewrites them to .\ relative paths, so games
like Fallout can find their DAT files after flattening moves everything
to the bundle root.

Uses byte-level replacement to preserve original CRLF line endings and
encoding. Extensible via CONFIG_PATH_FIXES map — add new entries for
other games with the same problem.
2026-06-08 10:16:33 +02:00
Hermes Agent 8108a661fa fix: rewrite .cue FILE references that point to non-existent data files
Build & Deploy / build-and-deploy (push) Successful in 12s
CloneCD rips generate a .cue that references DOTT.bin but the actual
data file is DOTT.img. DOSBox fails to mount the CD when the referenced
file doesn't exist, causing 'Illegal command' for executables on the CD.

Now before bundle creation, we scan all .cue files and fix any FILE line
that points to a missing data file by rewriting it to reference .img
or .bin files actually present in the same directory.
2026-06-03 12:46:33 +02:00
Hermes Agent 57842abe0d fix: smarter CD image selection per directory with format priority
Build & Deploy / build-and-deploy (push) Successful in 43s
Replace the broken descriptor-preference logic with a clean two-pass
approach: group images by parent directory, then pick the best format
per directory with priority .cue > .iso > .ccd > .img > .bin.

js-dos DOSBox's imgmount does NOT support .ccd (CloneCD descriptor)
natively, but .cue is widely supported. When both exist, .cue wins.
When only .ccd exists, it falls back to mounting the .img directly.
2026-06-03 12:40:11 +02:00
Hermes Agent 8a45ceccdc fix: prefer descriptor formats (.cue/.ccd) over raw data (.img/.bin) for DOSBox CD mounting
Build & Deploy / build-and-deploy (push) Successful in 45s
DOSBox's imgmount works best with descriptors because they contain track layout
info (audio tracks, subchannel data, copy protection). Raw .img/.bin mounts
lose this info, making the filesystem inaccessible and causing 'Illegal command'
when trying to run executables from the CD.
2026-06-03 12:36:14 +02:00
Hermes Agent 3162b9ff38 fix: remove duplicate imgLower declaration in buildCdOnlyDosboxConf
Build & Deploy / build-and-deploy (push) Successful in 40s
2026-06-03 12:29:21 +02:00
Hermes Agent 6f68e6c062 fix: seenDirs.add() after descriptor check, not before
Build & Deploy / build-and-deploy (push) Failing after 39s
The bug: seenDirs.add(parentDir) was called BEFORE the descriptor-skip
check. When .ccd was processed first, it marked cd/ as seen even
though it was skipped. Then .img was skipped because cd/ was already
'seen', leaving zero imgmount lines.

Fix: use seenDirs.contains(parentDir) for the continue check, and
call seenDirs.add(parentDir) only after passing all checks, right
before the actual imgmount output.
2026-06-03 12:27:15 +02:00
Hermes Agent 1fc0a11512 debug: show cdImages count in CD-only config
Build & Deploy / build-and-deploy (push) Successful in 50s
2026-06-03 12:20:19 +02:00
Hermes Agent 632fd7f554 fix: detect CD images once, pass into createBundle
Build & Deploy / build-and-deploy (push) Successful in 44s
CD-only games were failing because the second findCdImages() call
inside createBundle returned empty. Now detection happens once in
the upload method and the result is passed to createBundle.
2026-06-03 12:15:10 +02:00
Hermes Agent 85b0aa8c0c fix: prefer .img/.iso/.bin over .ccd/.cue descriptors
Build & Deploy / build-and-deploy (push) Successful in 37s
CloneCD (.ccd) and cue sheets (.cue) contain internal FILE references
that may not match the actual filename on disk (case mismatch).
When a data format (.img, .iso, .bin) exists in the same directory,
it's mounted directly instead of the descriptor.
2026-06-03 12:03:52 +02:00
Hermes Agent 4cdd72b3e2 fix: use Files.write() for byte[] return from buildCdOnlyJsdosJson
Build & Deploy / build-and-deploy (push) Successful in 36s
Files.writeString expects CharSequence, but buildCdOnlyJsdosJson
returns byte[]. Changed to Files.write() which accepts byte[].
2026-06-03 11:58:45 +02:00
Hermes Agent 6f59028cb0 feat: support CD-only games (no executable on filesystem)
Build & Deploy / build-and-deploy (push) Failing after 31s
When no .exe/.com/.bat files are found but CD images are present,
the upload no longer fails. Instead it creates a CD-only game where
the autoexec mounts the CD-ROM image(s) and presents a DOS prompt
with instructions for the user.

- findMainExe returning null + CD images exist = CD-only game
- createBundle handles null exePath (generates CD-only config)
- New buildCdOnlyDosboxConf / buildCdOnlyJsdosJson methods
- Upload metadata handles null executable gracefully (empty string)
- Platform defaults to 'dos' when no executable to inspect
2026-06-03 11:46:14 +02:00
Hermes Agent 383c66b166 restore dedup: mount one CD image per directory, prefer .bin over .cue
Build & Deploy / build-and-deploy (push) Successful in 37s
Since GameService now preserves CD images when regenerating configs,
the dedup logic is safe to re-add. Deduplicates by parent directory
so a .bin and .cue from the same folder only produce one imgmount.
Alphabetical sort ensures .bin (b) is mounted before .cue (c).
2026-06-03 10:22:27 +02:00