crimsonland 0.1.0.dev13__tar.gz → 0.1.0.dev15__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. crimsonland-0.1.0.dev15/PKG-INFO +197 -0
  2. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/pyproject.toml +6 -1
  3. crimsonland-0.1.0.dev15/readme.md +184 -0
  4. crimsonland-0.1.0.dev15/src/crimson/.DS_Store +0 -0
  5. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/cli.py +2 -0
  6. crimsonland-0.1.0.dev15/src/crimson/creatures/.DS_Store +0 -0
  7. crimsonland-0.1.0.dev15/src/crimson/debug.py +16 -0
  8. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/game.py +248 -2
  9. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/gameplay.py +16 -0
  10. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/modes/quest_mode.py +37 -0
  11. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/modes/survival_mode.py +42 -6
  12. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/player_damage.py +2 -0
  13. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/ui/quest_results.py +16 -6
  14. crimsonland-0.1.0.dev15/src/grim/.DS_Store +0 -0
  15. crimsonland-0.1.0.dev13/PKG-INFO +0 -9
  16. crimsonland-0.1.0.dev13/src/crimson/debug.py +0 -7
  17. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/__init__.py +0 -0
  18. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/assets_fetch.py +0 -0
  19. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/atlas.py +0 -0
  20. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/audio_router.py +0 -0
  21. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/bonuses.py +0 -0
  22. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/camera.py +0 -0
  23. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/creatures/__init__.py +0 -0
  24. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/creatures/ai.py +0 -0
  25. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/creatures/anim.py +0 -0
  26. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/creatures/damage.py +0 -0
  27. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/creatures/runtime.py +0 -0
  28. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/creatures/spawn.py +0 -0
  29. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/demo.py +0 -0
  30. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/demo_trial.py +0 -0
  31. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/effects.py +0 -0
  32. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/effects_atlas.py +0 -0
  33. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/__init__.py +0 -0
  34. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/assets.py +0 -0
  35. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/boot.py +0 -0
  36. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/high_scores_layout.py +0 -0
  37. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/menu.py +0 -0
  38. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/panels/__init__.py +0 -0
  39. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/panels/base.py +0 -0
  40. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/panels/controls.py +0 -0
  41. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/panels/credits.py +0 -0
  42. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/panels/databases.py +0 -0
  43. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/panels/mods.py +0 -0
  44. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/panels/options.py +0 -0
  45. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/panels/play_game.py +0 -0
  46. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/panels/stats.py +0 -0
  47. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/pause_menu.py +0 -0
  48. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/frontend/transitions.py +0 -0
  49. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/game_modes.py +0 -0
  50. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/game_world.py +0 -0
  51. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/input_codes.py +0 -0
  52. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/modes/__init__.py +0 -0
  53. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/modes/base_gameplay_mode.py +0 -0
  54. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/modes/rush_mode.py +0 -0
  55. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/modes/tutorial_mode.py +0 -0
  56. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/modes/typo_mode.py +0 -0
  57. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/paths.py +0 -0
  58. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/perks.py +0 -0
  59. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/persistence/__init__.py +0 -0
  60. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/persistence/highscores.py +0 -0
  61. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/persistence/save_status.py +0 -0
  62. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/projectiles.py +0 -0
  63. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/quests/__init__.py +0 -0
  64. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/quests/helpers.py +0 -0
  65. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/quests/registry.py +0 -0
  66. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/quests/results.py +0 -0
  67. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/quests/runtime.py +0 -0
  68. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/quests/tier1.py +0 -0
  69. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/quests/tier2.py +0 -0
  70. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/quests/tier3.py +0 -0
  71. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/quests/tier4.py +0 -0
  72. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/quests/tier5.py +0 -0
  73. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/quests/timeline.py +0 -0
  74. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/quests/types.py +0 -0
  75. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/render/__init__.py +0 -0
  76. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/render/terrain_fx.py +0 -0
  77. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/render/world_renderer.py +0 -0
  78. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/sim/__init__.py +0 -0
  79. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/sim/world_defs.py +0 -0
  80. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/sim/world_state.py +0 -0
  81. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/terrain_assets.py +0 -0
  82. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/tutorial/__init__.py +0 -0
  83. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/tutorial/timeline.py +0 -0
  84. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/typo/__init__.py +0 -0
  85. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/typo/names.py +0 -0
  86. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/typo/player.py +0 -0
  87. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/typo/spawns.py +0 -0
  88. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/typo/typing.py +0 -0
  89. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/ui/__init__.py +0 -0
  90. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/ui/cursor.py +0 -0
  91. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/ui/demo_trial_overlay.py +0 -0
  92. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/ui/game_over.py +0 -0
  93. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/ui/hud.py +0 -0
  94. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/ui/menu_panel.py +0 -0
  95. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/ui/perk_menu.py +0 -0
  96. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/ui/shadow.py +0 -0
  97. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/__init__.py +0 -0
  98. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/aim_debug.py +0 -0
  99. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/animations.py +0 -0
  100. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/arsenal_debug.py +0 -0
  101. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/audio_bootstrap.py +0 -0
  102. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/bonuses.py +0 -0
  103. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/camera_debug.py +0 -0
  104. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/camera_shake.py +0 -0
  105. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/corpse_stamp_debug.py +0 -0
  106. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/decals_debug.py +0 -0
  107. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/empty.py +0 -0
  108. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/fonts.py +0 -0
  109. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/game_over.py +0 -0
  110. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/ground.py +0 -0
  111. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/lighting_debug.py +0 -0
  112. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/particles.py +0 -0
  113. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/perk_menu_debug.py +0 -0
  114. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/perks.py +0 -0
  115. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/player.py +0 -0
  116. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/player_sprite_debug.py +0 -0
  117. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/projectile_fx.py +0 -0
  118. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/projectile_render_debug.py +0 -0
  119. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/projectiles.py +0 -0
  120. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/quest_title_overlay.py +0 -0
  121. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/registry.py +0 -0
  122. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/rush.py +0 -0
  123. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/small_font_debug.py +0 -0
  124. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/spawn_plan.py +0 -0
  125. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/sprites.py +0 -0
  126. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/survival.py +0 -0
  127. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/terrain.py +0 -0
  128. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/ui.py +0 -0
  129. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/views/wicons.py +0 -0
  130. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/weapon_sfx.py +0 -0
  131. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/crimson/weapons.py +0 -0
  132. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/__init__.py +0 -0
  133. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/app.py +0 -0
  134. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/assets.py +0 -0
  135. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/audio.py +0 -0
  136. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/config.py +0 -0
  137. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/console.py +0 -0
  138. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/fonts/__init__.py +0 -0
  139. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/fonts/grim_mono.py +0 -0
  140. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/fonts/small.py +0 -0
  141. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/input.py +0 -0
  142. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/jaz.py +0 -0
  143. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/math.py +0 -0
  144. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/music.py +0 -0
  145. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/paq.py +0 -0
  146. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/rand.py +0 -0
  147. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/sfx.py +0 -0
  148. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/sfx_map.py +0 -0
  149. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/terrain_render.py +0 -0
  150. {crimsonland-0.1.0.dev13 → crimsonland-0.1.0.dev15}/src/grim/view.py +0 -0
@@ -0,0 +1,197 @@
1
+ Metadata-Version: 2.3
2
+ Name: crimsonland
3
+ Version: 0.1.0.dev15
4
+ Requires-Dist: construct>=2.10.70
5
+ Requires-Dist: pillow>=12.1.0
6
+ Requires-Dist: platformdirs>=4.5.1
7
+ Requires-Dist: raylib>=5.5.0.4
8
+ Requires-Dist: typer>=0.21.1
9
+ Requires-Python: >=3.13
10
+ Project-URL: Documentation, https://crimson.banteg.xyz/
11
+ Project-URL: Repository, https://github.com/banteg/crimson
12
+ Description-Content-Type: text/markdown
13
+
14
+ # Crimsonland 1.9.93 decompilation + rewrite
15
+
16
+ This repository is a **reverse engineering + high‑fidelity reimplementation** of **Crimsonland 1.9.93 (2003)**.
17
+
18
+ - **Target build:** `v1.9.93` (GOG "Crimsonland Classic") — see [docs/provenance.md](docs/provenance.md) for exact hashes.
19
+ - **Rewrite:** a runnable reference implementation in **Python + raylib** under `src/`.
20
+ - **Analysis:** decompiles, name/type maps, and runtime evidence under `analysis/`.
21
+ - **Docs:** long-form notes and parity tracking under `docs/` (start at [docs/index.md](docs/index.md)).
22
+
23
+ The north star is **behavioral parity** with the original Windows build: timings, RNG, UI/layout quirks, asset decoding, and gameplay rules should match as closely as practical.
24
+
25
+ **[Read the full story](https://banteg.xyz/posts/crimsonland/)** of how this project came together: reverse engineering workflow, custom asset formats, AI-assisted decompilation, and game preservation philosophy.
26
+
27
+ ---
28
+
29
+ ## Quick start
30
+
31
+ Install [uv](https://docs.astral.sh/uv/getting-started/installation/) package manager.
32
+
33
+ ### Run the latest packaged build
34
+
35
+ If you just want to play the rewrite:
36
+
37
+ ```bash
38
+ uvx crimsonland@latest
39
+ ```
40
+
41
+ ### Run from a checkout
42
+
43
+ ```bash
44
+ gh repo clone banteg/crimson
45
+ cd crimson
46
+ uv run crimson
47
+ ```
48
+
49
+ ### Keep runtime files local to the repo
50
+
51
+ By default, runtime files (e.g. `crimson.cfg`, `game.cfg`, highscores, logs, downloaded PAQs) live in your per-user data dir.
52
+ To keep everything under this checkout:
53
+
54
+ ```bash
55
+ export CRIMSON_RUNTIME_DIR="$PWD/artifacts/runtime"
56
+ mkdir -p artifacts/runtime
57
+ uv run crimson
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Assets + binaries
63
+
64
+ There are two separate “inputs” to this repo:
65
+
66
+ 1. **Runtime assets for the rewrite** (PAQ archives)
67
+ 2. **Original Windows binaries for reverse engineering** (`crimsonland.exe`, `grim.dll`, …)
68
+
69
+ We keep them out of git and expect a local layout like:
70
+
71
+ ```text
72
+ game_bins/
73
+ crimsonland/
74
+ 1.9.93-gog/
75
+ crimsonland.exe
76
+ grim.dll
77
+ crimson.paq
78
+ music.paq
79
+ sfx.paq
80
+ artifacts/
81
+ runtime/ # optional: where you run the rewrite (cfg/status/paqs)
82
+ assets/ # optional: extracted PAQs for inspection/tools
83
+ ```
84
+
85
+ ### Running the rewrite
86
+
87
+ The rewrite loads the assets from original archives:
88
+
89
+ - `crimson.paq`
90
+ - `music.paq`
91
+ - `sfx.paq`
92
+
93
+ ### Extracted assets
94
+
95
+ For inspection/diffs/tools, you can extract PAQs into a filesystem tree:
96
+
97
+ ```bash
98
+ uv run crimson extract crimsonland_1.9.93 artifacts/assets
99
+ ```
100
+
101
+ Same as the original, many loaders can work from either:
102
+
103
+ - **PAQ-backed assets** (preferred when available), or
104
+ - the **extracted filesystem layout** under `artifacts/assets/`.
105
+
106
+ ---
107
+
108
+ ## CLI cheat sheet
109
+
110
+ Everything is exposed via the `crimson` CLI (alias: `crimsonland`):
111
+
112
+ ```bash
113
+ uv run crimson # run the game (default command)
114
+ uv run crimson view ui # debug views / sandboxes
115
+ uv run crimson quests 1.1 # print quest spawn script
116
+ uv run crimson config # inspect crimson.cfg
117
+ uv run crimson extract <game_dir> artifacts/assets
118
+ ```
119
+
120
+ Useful flags:
121
+
122
+ - `--base-dir PATH` / `CRIMSON_RUNTIME_DIR=...` — where saves/config/logs live
123
+ - `--assets-dir PATH` — where `.paq` archives (or extracted assets) are loaded from
124
+ - `--seed N` — deterministic runs for parity testing
125
+ - `--demo` — enable shareware/demo paths
126
+ - `--no-intro` — skip logos/intro music
127
+
128
+ ---
129
+
130
+ ## Docs
131
+
132
+ Docs are authored in `docs/` and built as a static site at https://crimson.banteg.xyz/
133
+
134
+ For development, it's useful to have a live local build:
135
+
136
+ ```
137
+ uv tool install zensical
138
+ zensical serve
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Development
144
+
145
+ ### Tests
146
+
147
+ ```bash
148
+ uv run pytest
149
+ ```
150
+
151
+ ### Lint / checks
152
+
153
+ ```bash
154
+ uv run lint-imports
155
+ uv run python scripts/check_asset_loader_usage.py
156
+ ```
157
+
158
+ ### `justfile` shortcuts
159
+
160
+ If you have `just` installed:
161
+
162
+ ```bash
163
+ just --list
164
+ just test
165
+ just docs-build
166
+ just ghidra-exe
167
+ just ghidra-grim
168
+ ```
169
+
170
+ ---
171
+
172
+ ## Reverse engineering workflow
173
+
174
+ High level:
175
+
176
+ - **Static analysis is the source of truth.**
177
+ - Update names/types in [analysis/ghidra/maps/](analysis/ghidra/maps/).
178
+ - Treat [analysis/ghidra/raw/](analysis/ghidra/raw/) as generated output (regenerate; do not hand-edit).
179
+ - **Runtime tooling** (Frida / WinDbg) validates ambiguous behavior and captures ground truth.
180
+ - Evidence summaries live under [analysis/frida/](analysis/frida/).
181
+
182
+ ---
183
+
184
+ ## Contributing notes
185
+
186
+ - Keep changes small and reviewable (one subsystem/feature at a time).
187
+ - Prefer *measured parity* (captures/logs/deterministic tests) over “looks right”.
188
+ - When porting float constants from decompilation, prefer the intended value
189
+ (e.g. `0.6` instead of `0.6000000238418579` when it’s clearly a float32 artifact).
190
+
191
+ ---
192
+
193
+ ## Legal
194
+
195
+ This project is an independent reverse engineering and reimplementation effort for preservation, research, and compatibility.
196
+
197
+ No original assets or binaries are included. Use your own legally obtained copy.
@@ -1,6 +1,7 @@
1
1
  [project]
2
2
  name = "crimsonland"
3
- version = "0.1.0.dev13"
3
+ version = "0.1.0.dev15"
4
+ readme = { file = "readme.md", content-type = "text/markdown" }
4
5
  requires-python = ">=3.13"
5
6
  dependencies = [
6
7
  "construct>=2.10.70",
@@ -10,6 +11,10 @@ dependencies = [
10
11
  "typer>=0.21.1",
11
12
  ]
12
13
 
14
+ [project.urls]
15
+ Documentation = "https://crimson.banteg.xyz/"
16
+ Repository = "https://github.com/banteg/crimson"
17
+
13
18
  [project.scripts]
14
19
  crimsonland = "crimson.cli:main"
15
20
  crimson = "crimson.cli:main"
@@ -0,0 +1,184 @@
1
+ # Crimsonland 1.9.93 decompilation + rewrite
2
+
3
+ This repository is a **reverse engineering + high‑fidelity reimplementation** of **Crimsonland 1.9.93 (2003)**.
4
+
5
+ - **Target build:** `v1.9.93` (GOG "Crimsonland Classic") — see [docs/provenance.md](docs/provenance.md) for exact hashes.
6
+ - **Rewrite:** a runnable reference implementation in **Python + raylib** under `src/`.
7
+ - **Analysis:** decompiles, name/type maps, and runtime evidence under `analysis/`.
8
+ - **Docs:** long-form notes and parity tracking under `docs/` (start at [docs/index.md](docs/index.md)).
9
+
10
+ The north star is **behavioral parity** with the original Windows build: timings, RNG, UI/layout quirks, asset decoding, and gameplay rules should match as closely as practical.
11
+
12
+ **[Read the full story](https://banteg.xyz/posts/crimsonland/)** of how this project came together: reverse engineering workflow, custom asset formats, AI-assisted decompilation, and game preservation philosophy.
13
+
14
+ ---
15
+
16
+ ## Quick start
17
+
18
+ Install [uv](https://docs.astral.sh/uv/getting-started/installation/) package manager.
19
+
20
+ ### Run the latest packaged build
21
+
22
+ If you just want to play the rewrite:
23
+
24
+ ```bash
25
+ uvx crimsonland@latest
26
+ ```
27
+
28
+ ### Run from a checkout
29
+
30
+ ```bash
31
+ gh repo clone banteg/crimson
32
+ cd crimson
33
+ uv run crimson
34
+ ```
35
+
36
+ ### Keep runtime files local to the repo
37
+
38
+ By default, runtime files (e.g. `crimson.cfg`, `game.cfg`, highscores, logs, downloaded PAQs) live in your per-user data dir.
39
+ To keep everything under this checkout:
40
+
41
+ ```bash
42
+ export CRIMSON_RUNTIME_DIR="$PWD/artifacts/runtime"
43
+ mkdir -p artifacts/runtime
44
+ uv run crimson
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Assets + binaries
50
+
51
+ There are two separate “inputs” to this repo:
52
+
53
+ 1. **Runtime assets for the rewrite** (PAQ archives)
54
+ 2. **Original Windows binaries for reverse engineering** (`crimsonland.exe`, `grim.dll`, …)
55
+
56
+ We keep them out of git and expect a local layout like:
57
+
58
+ ```text
59
+ game_bins/
60
+ crimsonland/
61
+ 1.9.93-gog/
62
+ crimsonland.exe
63
+ grim.dll
64
+ crimson.paq
65
+ music.paq
66
+ sfx.paq
67
+ artifacts/
68
+ runtime/ # optional: where you run the rewrite (cfg/status/paqs)
69
+ assets/ # optional: extracted PAQs for inspection/tools
70
+ ```
71
+
72
+ ### Running the rewrite
73
+
74
+ The rewrite loads the assets from original archives:
75
+
76
+ - `crimson.paq`
77
+ - `music.paq`
78
+ - `sfx.paq`
79
+
80
+ ### Extracted assets
81
+
82
+ For inspection/diffs/tools, you can extract PAQs into a filesystem tree:
83
+
84
+ ```bash
85
+ uv run crimson extract crimsonland_1.9.93 artifacts/assets
86
+ ```
87
+
88
+ Same as the original, many loaders can work from either:
89
+
90
+ - **PAQ-backed assets** (preferred when available), or
91
+ - the **extracted filesystem layout** under `artifacts/assets/`.
92
+
93
+ ---
94
+
95
+ ## CLI cheat sheet
96
+
97
+ Everything is exposed via the `crimson` CLI (alias: `crimsonland`):
98
+
99
+ ```bash
100
+ uv run crimson # run the game (default command)
101
+ uv run crimson view ui # debug views / sandboxes
102
+ uv run crimson quests 1.1 # print quest spawn script
103
+ uv run crimson config # inspect crimson.cfg
104
+ uv run crimson extract <game_dir> artifacts/assets
105
+ ```
106
+
107
+ Useful flags:
108
+
109
+ - `--base-dir PATH` / `CRIMSON_RUNTIME_DIR=...` — where saves/config/logs live
110
+ - `--assets-dir PATH` — where `.paq` archives (or extracted assets) are loaded from
111
+ - `--seed N` — deterministic runs for parity testing
112
+ - `--demo` — enable shareware/demo paths
113
+ - `--no-intro` — skip logos/intro music
114
+
115
+ ---
116
+
117
+ ## Docs
118
+
119
+ Docs are authored in `docs/` and built as a static site at https://crimson.banteg.xyz/
120
+
121
+ For development, it's useful to have a live local build:
122
+
123
+ ```
124
+ uv tool install zensical
125
+ zensical serve
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Development
131
+
132
+ ### Tests
133
+
134
+ ```bash
135
+ uv run pytest
136
+ ```
137
+
138
+ ### Lint / checks
139
+
140
+ ```bash
141
+ uv run lint-imports
142
+ uv run python scripts/check_asset_loader_usage.py
143
+ ```
144
+
145
+ ### `justfile` shortcuts
146
+
147
+ If you have `just` installed:
148
+
149
+ ```bash
150
+ just --list
151
+ just test
152
+ just docs-build
153
+ just ghidra-exe
154
+ just ghidra-grim
155
+ ```
156
+
157
+ ---
158
+
159
+ ## Reverse engineering workflow
160
+
161
+ High level:
162
+
163
+ - **Static analysis is the source of truth.**
164
+ - Update names/types in [analysis/ghidra/maps/](analysis/ghidra/maps/).
165
+ - Treat [analysis/ghidra/raw/](analysis/ghidra/raw/) as generated output (regenerate; do not hand-edit).
166
+ - **Runtime tooling** (Frida / WinDbg) validates ambiguous behavior and captures ground truth.
167
+ - Evidence summaries live under [analysis/frida/](analysis/frida/).
168
+
169
+ ---
170
+
171
+ ## Contributing notes
172
+
173
+ - Keep changes small and reviewable (one subsystem/feature at a time).
174
+ - Prefer *measured parity* (captures/logs/deterministic tests) over “looks right”.
175
+ - When porting float constants from decompilation, prefer the intended value
176
+ (e.g. `0.6` instead of `0.6000000238418579` when it’s clearly a float32 artifact).
177
+
178
+ ---
179
+
180
+ ## Legal
181
+
182
+ This project is an independent reverse engineering and reimplementation effort for preservation, research, and compatibility.
183
+
184
+ No original assets or binaries are included. Use your own legally obtained copy.
@@ -211,6 +211,7 @@ def cmd_game(
211
211
  seed: int | None = typer.Option(None, help="rng seed"),
212
212
  demo: bool = typer.Option(False, "--demo", help="enable shareware demo mode"),
213
213
  no_intro: bool = typer.Option(False, "--no-intro", help="skip company splashes and intro music"),
214
+ debug: bool = typer.Option(False, "--debug", help="enable debug cheats and overlays"),
214
215
  base_dir: Path = typer.Option(
215
216
  default_runtime_dir(),
216
217
  "--base-dir",
@@ -236,6 +237,7 @@ def cmd_game(
236
237
  seed=seed,
237
238
  demo_enabled=demo,
238
239
  no_intro=no_intro,
240
+ debug=debug,
239
241
  )
240
242
  run_game(config)
241
243
 
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+ _DEBUG_OVERRIDE: bool | None = None
6
+
7
+
8
+ def set_debug_enabled(enabled: bool) -> None:
9
+ global _DEBUG_OVERRIDE
10
+ _DEBUG_OVERRIDE = bool(enabled)
11
+
12
+
13
+ def debug_enabled() -> bool:
14
+ if _DEBUG_OVERRIDE is not None:
15
+ return bool(_DEBUG_OVERRIDE)
16
+ return os.environ.get("CRIMSON_DEBUG") == "1"