diff --git a/emulators/openbor.yml b/emulators/openbor.yml index 53226b81..8b1d6d01 100644 --- a/emulators/openbor.yml +++ b/emulators/openbor.yml @@ -4,7 +4,7 @@ core_classification: game_engine source: "https://github.com/DCurrent/openbor" upstream: "https://github.com/DCurrent/openbor" profiled_date: "2026-08-12" -source_commit: "0ece95d1c113c1ce86f2da826b3c25b521ee116d" +source_commit: "9d81480f8481fbb9e76b0b5f2a5dfa408376761a" core_version: "4.0 Build 7949" display_name: "OpenBOR Game Engine" cores: @@ -35,18 +35,18 @@ notes: | such argument the built in menu lists Paks/ and the player picks one (engine/sdl/sdlport.c:25,124-135, engine/sdl/menu.c:172-203,802). packfile_supported accepts any name containing .pak and skips menu.pak - (engine/source/gamelib/packfile.c:1577-1584). + (engine/source/gamelib/packfile.c:1595-1602). Everything read for a running game comes out of that module. buffer_pakfile tries the real filesystem first through fopen, then the same path inside the - archive (engine/openbor.c:921-965,969-1018), so data/models.txt, + archive (engine/openbor.c:935-979,983-1032), so data/models.txt, data/levels.txt, data/video.txt, data/menu.txt, the scene scripts and the sprites and sounds under data/ resolve either from an unpacked data/ directory beside the binary (engine/source/gamelib/packfile.c:781-810) or from the pak. Four names take precedence over their in-pak copy when they exist on disk: translation/translation.txt and translation/menu.txt over data/translation.txt - (engine/source/gamelib/translation.c:102-106, engine/openbor.c:5975,53240), - saves/lifebar.txt over data/lifebar.txt (engine/openbor.c:4965-4972), and on + (engine/source/gamelib/translation.c:102-106, engine/openbor.c:6744,54707), + saves/lifebar.txt over data/lifebar.txt (engine/openbor.c:5734-5741), and on Android Saves//touch.txt then Saves/touch.txt over data/touch.txt (engine/android/app/jni/openbor/video.c:210-218). Each holds text or a control layout written for one module. The engine/translation.txt copied into the @@ -67,12 +67,12 @@ notes: | Files the engine writes rather than expects: Paks, Saves, Logs and ScreenShots are created at startup when missing (engine/sdl/sdlport.c:30-33,119-122), settings and progress land in Saves as .cfg, .sav, .hi, .scr and .inp - alongside default.cfg (engine/source/utils.c:284-312, - engine/openbor.c:2675-3040), and the viewer reads back Logs/OpenBorLog.txt and + alongside default.cfg (engine/source/utils.c:324-352, + engine/openbor.c:2940-3482), and the viewer reads back Logs/OpenBorLog.txt and Logs/ScriptLog.txt (engine/source/utils.c:47-62, engine/sdl/menu.c:652-700). paks/logoff.txt is a marker whose existence alone skips the startup logo, so the player creates it empty (engine/sdl/menu.c:707-716). Module script can open any path it names through openfilestream - (engine/openborscript.c:10505-10600). + (engine/openborscript.c:10573-10668). files: [] diff --git a/scripts/profile_sync.py b/scripts/profile_sync.py index 2b029f00..b427e170 100644 --- a/scripts/profile_sync.py +++ b/scripts/profile_sync.py @@ -45,7 +45,12 @@ WIDEN_STEPS = (0, 3, 6, 12, 25, 50) # A 30k-line driver diffs in about five seconds, and the biggest files the # corpus cites sit just above that. The cap stays as a guard against # pathological inputs, not as a limit on real source files. -MAX_MATCH_LINES = 40000 +# The fallback mapping is a full sequence diff, and it only runs once exact +# anchoring has failed, which is rare. openbor.c is the largest file any +# profile cites, 55k lines against 57k, and diffing the pair takes nine +# seconds; the ceiling sits above it so that one resolves, and still stops a +# pathological pair from stalling a sweep of every profile. +MAX_MATCH_LINES = 60000 PIN = "pin" HEAD = "head" diff --git a/tests/test_profile_sync.py b/tests/test_profile_sync.py index 659af9e3..9ed005f9 100644 --- a/tests/test_profile_sync.py +++ b/tests/test_profile_sync.py @@ -2160,6 +2160,34 @@ class TestWriteDryRun(unittest.TestCase): self.assertEqual(self.path.read_text(), before) +class TestLargeFileCeiling(unittest.TestCase): + """The fallback mapping bails above a ceiling, and it sits high enough. + + openbor.c is the largest file any profile cites, 55k lines against 57k + at HEAD, and the ref that mattered mapped only once the ceiling cleared + it. The guard stays so a pathological pair cannot stall a sweep. + """ + + def test_a_pair_over_the_ceiling_is_not_diffed(self): + big = ["x"] * (profile_sync.MAX_MATCH_LINES + 1) + result = profile_sync._map_changed(big, big, 1, 2) + self.assertEqual(result.status, "CHANGED") + self.assertIn("over", result.reason) + + def test_a_pair_under_the_ceiling_is_mapped(self): + pin = ["a", "subject", "b"] + head = ["pad", "pad", "a", "subject", "b"] + result = profile_sync._map_changed(pin, head, 2, 2) + self.assertIsNotNone(result.start, "a small pair has to be diffed") + self.assertNotIn("over", result.reason or "") + + def test_the_ceiling_clears_the_largest_file_any_profile_cites(self): + self.assertGreater( + profile_sync.MAX_MATCH_LINES, 57000, + "openbor.c is 57k lines at HEAD and its ref has to map", + ) + + class TestRebaseWaitsForThePin(unittest.TestCase): """Recaling refs the pin cannot follow manufactures the desync.