Commit Graph

141 Commits

Author SHA1 Message Date
david.alvarez df5ac3d87b more style
Build & Deploy / build-and-deploy (push) Successful in 56s
2026-06-10 10:19:44 +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 dfb46ef032 Merge remote-tracking branch 'origin/main'
Build & Deploy / build-and-deploy (push) Successful in 50s
2026-06-10 09:21:02 +02:00
david.alvarez be15783034 more style 2026-06-10 09:20:47 +02:00
david.alvarez e452df00e4 more style
Build & Deploy / build-and-deploy (push) Successful in 1m37s
2026-06-10 09:19:11 +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 82b379d211 ci: use docker run with correct volume path instead of compose file
Build & Deploy / build-and-deploy (push) Successful in 11s
The runner container can't access the compose file path on the host.
Use the compose file's parameters (network: dockernet, volume:
/mnt/cache/appdata/dostalgia) directly in docker run instead.
2026-06-08 11:10:46 +02:00
David Alvarez 9801e37bec ci: fix COMPOSE_DIR to runner-internal path /unraid/dostalgia
Build & Deploy / build-and-deploy (push) Failing after 17s
The Gitea Actions runner is a container; it can't see host paths.
The dostalgia compose dir is mounted inside the runner at /unraid/dostalgia,
so that's what the workflow must reference.
2026-06-08 11:09:01 +02:00
droideparanoico f2fb1c035d Actualizar .gitea/workflows/build.yaml
Build & Deploy / build-and-deploy (push) Failing after 6s
2026-06-08 10:58:49 +02:00
droideparanoico fc6f44b2ce Actualizar .gitea/workflows/build.yaml
Build & Deploy / build-and-deploy (push) Failing after 21s
2026-06-08 10:55:17 +02:00
droideparanoico ee05a7f031 Actualizar .gitea/workflows/build.yaml
Build & Deploy / build-and-deploy (push) Failing after 6s
2026-06-08 10:53:04 +02:00
David Alvarez 106cb5bf83 ci: deploy via docker-compose instead of raw docker run
Build & Deploy / build-and-deploy (push) Failing after 39s
Uses docker compose -f /appdata/dockhand/stacks/unraid/dostalgia/compose.yaml
so the container matches the user's Unraid stack configuration (correct volume
mount at /mnt/cache/appdata/dostalgia, proper network, labels, etc.)
2026-06-08 10:49:44 +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
David Alvarez 9fceeb4b78 fix: remove unrecognized G1HeapUncommitPercent flag, keep Min/MaxHeapFreeRatio
Build & Deploy / build-and-deploy (push) Successful in 14s
G1HeapUncommitPercent is not available in this JRE build. The
MinHeapFreeRatio=10 and MaxHeapFreeRatio=20 flags are sufficient
to keep JVM committed heap from growing unbounded after uploads.
2026-06-04 11:27:02 +02:00
David Alvarez e9c4d963ad feat: add G1GC heap uncommit flags so container memory shrinks after uploads
Build & Deploy / build-and-deploy (push) Failing after 49s
-XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:G1HeapUncommitPercent=5
After large game uploads, the JVM committed heap stays high. These flags tell
G1GC to release free heap pages back to the OS after GC cycles, so Docker
memory usage drops back toward baseline instead of staying at peak.
2026-06-04 11:25:02 +02:00
david.alvarez d119b3ccb9 Revert "fix: limit JVM heap to 128MB with G1GC periodic GC container"
Build & Deploy / build-and-deploy (push) Successful in 12s
This reverts commit d652f3f94b.
2026-06-03 21:29:36 +02:00
Hermes Agent d652f3f94b fix: limit JVM heap to 128MB with G1GC periodic GC container
Build & Deploy / build-and-deploy (push) Successful in 14s
Revert to commit 8108a66 (cue file fix + CD mounting fixes) and
apply only the Dockerfile JVM memory limit. All other changes
(StaticResource Range support, FileChannel transferTo) reverted.
2026-06-03 16:15:20 +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