exp-runtime 0.5.0__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 (224) hide show
  1. exp_runtime-0.5.0/.github/workflows/desktop-builds.yml +259 -0
  2. exp_runtime-0.5.0/.github/workflows/ios-builds.yml +103 -0
  3. exp_runtime-0.5.0/.gitignore +68 -0
  4. exp_runtime-0.5.0/AGENTS.md +75 -0
  5. exp_runtime-0.5.0/CHANGELOG.md +359 -0
  6. exp_runtime-0.5.0/LICENSE +21 -0
  7. exp_runtime-0.5.0/MAINTAINERS.md +265 -0
  8. exp_runtime-0.5.0/MANIFEST.in +41 -0
  9. exp_runtime-0.5.0/PKG-INFO +287 -0
  10. exp_runtime-0.5.0/README.md +229 -0
  11. exp_runtime-0.5.0/RELEASE.md +16 -0
  12. exp_runtime-0.5.0/docs/COD.md +421 -0
  13. exp_runtime-0.5.0/docs/DEVELOPMENT.md +135 -0
  14. exp_runtime-0.5.0/docs/DISTRIBUTION.md +224 -0
  15. exp_runtime-0.5.0/docs/ENGINE_ABI.md +507 -0
  16. exp_runtime-0.5.0/docs/EPISODE_CATALOG.md +242 -0
  17. exp_runtime-0.5.0/docs/FOOTBALL_UI.md +237 -0
  18. exp_runtime-0.5.0/docs/GRID_UI.md +220 -0
  19. exp_runtime-0.5.0/docs/IOS_APP.md +291 -0
  20. exp_runtime-0.5.0/docs/IPA.md +318 -0
  21. exp_runtime-0.5.0/docs/MAIN_MENU.md +531 -0
  22. exp_runtime-0.5.0/docs/MINIGAMES.md +484 -0
  23. exp_runtime-0.5.0/docs/NAME_INPUT.md +136 -0
  24. exp_runtime-0.5.0/docs/PROVENANCE.md +83 -0
  25. exp_runtime-0.5.0/docs/RUNTIME.md +785 -0
  26. exp_runtime-0.5.0/docs/SCHEMA.md +340 -0
  27. exp_runtime-0.5.0/docs/SDK.md +178 -0
  28. exp_runtime-0.5.0/docs/STORY_SERVICES.md +959 -0
  29. exp_runtime-0.5.0/docs/TITLE_SCREENS.md +196 -0
  30. exp_runtime-0.5.0/docs/UI_ASSETS.md +293 -0
  31. exp_runtime-0.5.0/docs/UI_FIDELITY.md +1254 -0
  32. exp_runtime-0.5.0/docs/VM_EXECUTION.md +103 -0
  33. exp_runtime-0.5.0/docs/VM_SPEC.md +484 -0
  34. exp_runtime-0.5.0/exp_runtime.egg-info/SOURCES.txt +221 -0
  35. exp_runtime-0.5.0/ios/EXPRuntime/EXPRuntimeApp.swift +446 -0
  36. exp_runtime-0.5.0/ios/EXPRuntime/Info.plist +24 -0
  37. exp_runtime-0.5.0/ios/EXPRuntime/NativeCanvas.swift +271 -0
  38. exp_runtime-0.5.0/ios/EXPRuntime/NativeFonts.h +2 -0
  39. exp_runtime-0.5.0/ios/EXPRuntime/NativeFonts.m +148 -0
  40. exp_runtime-0.5.0/ios/EXPRuntime/PythonBridge.h +12 -0
  41. exp_runtime-0.5.0/ios/EXPRuntime/PythonBridge.m +101 -0
  42. exp_runtime-0.5.0/ios/EXPRuntime.xcodeproj/project.pbxproj +250 -0
  43. exp_runtime-0.5.0/ios/Signing.local.xcconfig.example +5 -0
  44. exp_runtime-0.5.0/ios/Signing.xcconfig +6 -0
  45. exp_runtime-0.5.0/ios/dependencies.json +18 -0
  46. exp_runtime-0.5.0/ios/probe/exp_ios_probe.py +630 -0
  47. exp_runtime-0.5.0/ios/probe/rar_fixture.py +36 -0
  48. exp_runtime-0.5.0/packages/cod/LICENSE +21 -0
  49. exp_runtime-0.5.0/packages/cod/MANIFEST.in +4 -0
  50. exp_runtime-0.5.0/packages/cod/README.md +15 -0
  51. exp_runtime-0.5.0/packages/cod/pyproject.toml +20 -0
  52. exp_runtime-0.5.0/packages/cod/src/exp_runtime/cod/__init__.py +1 -0
  53. exp_runtime-0.5.0/packages/cod/src/exp_runtime/cod/assets.py +76 -0
  54. exp_runtime-0.5.0/packages/cod/src/exp_runtime/cod/audio.py +6 -0
  55. exp_runtime-0.5.0/packages/cod/src/exp_runtime/cod/content.py +7 -0
  56. exp_runtime-0.5.0/packages/cod/src/exp_runtime/cod/profile.py +36 -0
  57. exp_runtime-0.5.0/packages/cod/src/exp_runtime/cod/services.py +56 -0
  58. exp_runtime-0.5.0/packages/engine/LICENSE +21 -0
  59. exp_runtime-0.5.0/packages/engine/MANIFEST.in +4 -0
  60. exp_runtime-0.5.0/packages/engine/README.md +15 -0
  61. exp_runtime-0.5.0/packages/engine/pyproject.toml +23 -0
  62. exp_runtime-0.5.0/packages/engine/src/exp_runtime/audio.py +13 -0
  63. exp_runtime-0.5.0/packages/engine/src/exp_runtime/background.py +58 -0
  64. exp_runtime-0.5.0/packages/engine/src/exp_runtime/character_picker.py +92 -0
  65. exp_runtime-0.5.0/packages/engine/src/exp_runtime/choice.py +116 -0
  66. exp_runtime-0.5.0/packages/engine/src/exp_runtime/choice_hints.py +153 -0
  67. exp_runtime-0.5.0/packages/engine/src/exp_runtime/content.py +742 -0
  68. exp_runtime-0.5.0/packages/engine/src/exp_runtime/dialogue.py +151 -0
  69. exp_runtime-0.5.0/packages/engine/src/exp_runtime/dialogue_animation.py +345 -0
  70. exp_runtime-0.5.0/packages/engine/src/exp_runtime/dialogue_notice.py +51 -0
  71. exp_runtime-0.5.0/packages/engine/src/exp_runtime/engine.py +609 -0
  72. exp_runtime-0.5.0/packages/engine/src/exp_runtime/episode_catalog.py +171 -0
  73. exp_runtime-0.5.0/packages/engine/src/exp_runtime/game_state.py +9 -0
  74. exp_runtime-0.5.0/packages/engine/src/exp_runtime/games.py +35 -0
  75. exp_runtime-0.5.0/packages/engine/src/exp_runtime/ios_fonts.py +228 -0
  76. exp_runtime-0.5.0/packages/engine/src/exp_runtime/launcher.py +92 -0
  77. exp_runtime-0.5.0/packages/engine/src/exp_runtime/loading.py +37 -0
  78. exp_runtime-0.5.0/packages/engine/src/exp_runtime/menu.py +177 -0
  79. exp_runtime-0.5.0/packages/engine/src/exp_runtime/message_panel.py +36 -0
  80. exp_runtime-0.5.0/packages/engine/src/exp_runtime/minigames.py +76 -0
  81. exp_runtime-0.5.0/packages/engine/src/exp_runtime/randomness.py +61 -0
  82. exp_runtime-0.5.0/packages/engine/src/exp_runtime/rar.py +145 -0
  83. exp_runtime-0.5.0/packages/engine/src/exp_runtime/relationships.py +131 -0
  84. exp_runtime-0.5.0/packages/engine/src/exp_runtime/runtime.py +879 -0
  85. exp_runtime-0.5.0/packages/engine/src/exp_runtime/scene_badge.py +65 -0
  86. exp_runtime-0.5.0/packages/engine/src/exp_runtime/speaker_names.py +258 -0
  87. exp_runtime-0.5.0/packages/engine/src/exp_runtime/text_input.py +55 -0
  88. exp_runtime-0.5.0/packages/engine/src/exp_runtime/title_screen.py +104 -0
  89. exp_runtime-0.5.0/packages/engine/src/exp_runtime/trace.py +76 -0
  90. exp_runtime-0.5.0/packages/engine/src/exp_runtime/vm.py +391 -0
  91. exp_runtime-0.5.0/packages/formats/LICENSE +21 -0
  92. exp_runtime-0.5.0/packages/formats/MANIFEST.in +4 -0
  93. exp_runtime-0.5.0/packages/formats/README.md +15 -0
  94. exp_runtime-0.5.0/packages/formats/pyproject.toml +27 -0
  95. exp_runtime-0.5.0/packages/formats/src/exp_runtime/__main__.py +5 -0
  96. exp_runtime-0.5.0/packages/formats/src/exp_runtime/atlas.py +203 -0
  97. exp_runtime-0.5.0/packages/formats/src/exp_runtime/cli.py +186 -0
  98. exp_runtime-0.5.0/packages/formats/src/exp_runtime/decode/__init__.py +1 -0
  99. exp_runtime-0.5.0/packages/formats/src/exp_runtime/decode/bytecode.py +119 -0
  100. exp_runtime-0.5.0/packages/formats/src/exp_runtime/exp.py +121 -0
  101. exp_runtime-0.5.0/packages/formats/src/exp_runtime/fonts.py +280 -0
  102. exp_runtime-0.5.0/packages/formats/src/exp_runtime/formats.py +18 -0
  103. exp_runtime-0.5.0/packages/formats/src/exp_runtime/languages.py +23 -0
  104. exp_runtime-0.5.0/packages/formats/src/exp_runtime/menu_assets.py +85 -0
  105. exp_runtime-0.5.0/packages/formats/src/exp_runtime/ui_assets.py +285 -0
  106. exp_runtime-0.5.0/packages/player/LICENSE +21 -0
  107. exp_runtime-0.5.0/packages/player/MANIFEST.in +5 -0
  108. exp_runtime-0.5.0/packages/player/README.md +15 -0
  109. exp_runtime-0.5.0/packages/player/pyproject.toml +30 -0
  110. exp_runtime-0.5.0/packages/player/src/exp_runtime/app_icon.py +12 -0
  111. exp_runtime-0.5.0/packages/player/src/exp_runtime/app_info.py +16 -0
  112. exp_runtime-0.5.0/packages/player/src/exp_runtime/application.py +825 -0
  113. exp_runtime-0.5.0/packages/player/src/exp_runtime/assets/kiwi.svg +49 -0
  114. exp_runtime-0.5.0/packages/player/src/exp_runtime/desktop.py +726 -0
  115. exp_runtime-0.5.0/packages/player/src/exp_runtime/desktop_choice.py +202 -0
  116. exp_runtime-0.5.0/packages/player/src/exp_runtime/desktop_dialogue.py +271 -0
  117. exp_runtime-0.5.0/packages/player/src/exp_runtime/desktop_input.py +176 -0
  118. exp_runtime-0.5.0/packages/player/src/exp_runtime/desktop_loading.py +45 -0
  119. exp_runtime-0.5.0/packages/player/src/exp_runtime/desktop_menu.py +448 -0
  120. exp_runtime-0.5.0/packages/player/src/exp_runtime/desktop_message.py +123 -0
  121. exp_runtime-0.5.0/packages/player/src/exp_runtime/desktop_pause.py +105 -0
  122. exp_runtime-0.5.0/packages/player/src/exp_runtime/desktop_picker.py +68 -0
  123. exp_runtime-0.5.0/packages/player/src/exp_runtime/desktop_text.py +88 -0
  124. exp_runtime-0.5.0/packages/player/src/exp_runtime/desktop_title.py +82 -0
  125. exp_runtime-0.5.0/packages/player/src/exp_runtime/graphics.py +79 -0
  126. exp_runtime-0.5.0/packages/player/src/exp_runtime/platforms/__init__.py +1 -0
  127. exp_runtime-0.5.0/packages/player/src/exp_runtime/platforms/drawing.py +612 -0
  128. exp_runtime-0.5.0/packages/player/src/exp_runtime/platforms/ios.py +225 -0
  129. exp_runtime-0.5.0/packages/player/src/exp_runtime/pointer.py +25 -0
  130. exp_runtime-0.5.0/packages/player/src/exp_runtime/projection.py +68 -0
  131. exp_runtime-0.5.0/packages/player/src/exp_runtime/settings.py +46 -0
  132. exp_runtime-0.5.0/packages/player/src/exp_runtime/updates.py +144 -0
  133. exp_runtime-0.5.0/packages/shs/LICENSE +21 -0
  134. exp_runtime-0.5.0/packages/shs/MANIFEST.in +4 -0
  135. exp_runtime-0.5.0/packages/shs/README.md +15 -0
  136. exp_runtime-0.5.0/packages/shs/pyproject.toml +20 -0
  137. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/__init__.py +1 -0
  138. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/audio.py +33 -0
  139. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/builtin_episode.py +50 -0
  140. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/content.py +25 -0
  141. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/desktop_football.py +319 -0
  142. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/desktop_grid.py +341 -0
  143. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/football.py +517 -0
  144. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/football_layout.py +164 -0
  145. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/grid_layout.py +78 -0
  146. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/grid_motion.py +41 -0
  147. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/ios_assets.py +100 -0
  148. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/profile.py +37 -0
  149. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/relationships.py +14 -0
  150. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/services.py +114 -0
  151. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/survey.py +45 -0
  152. exp_runtime-0.5.0/packages/shs/src/exp_runtime/shs/word_grid.py +592 -0
  153. exp_runtime-0.5.0/pyproject.toml +47 -0
  154. exp_runtime-0.5.0/setup.cfg +4 -0
  155. exp_runtime-0.5.0/tests/test_atlas.py +61 -0
  156. exp_runtime-0.5.0/tests/test_audio.py +407 -0
  157. exp_runtime-0.5.0/tests/test_background.py +272 -0
  158. exp_runtime-0.5.0/tests/test_build_query.py +147 -0
  159. exp_runtime-0.5.0/tests/test_builtin_episode.py +188 -0
  160. exp_runtime-0.5.0/tests/test_bytecode.py +51 -0
  161. exp_runtime-0.5.0/tests/test_character_picker.py +169 -0
  162. exp_runtime-0.5.0/tests/test_choice.py +323 -0
  163. exp_runtime-0.5.0/tests/test_choice_hints.py +199 -0
  164. exp_runtime-0.5.0/tests/test_choice_shuffle.py +248 -0
  165. exp_runtime-0.5.0/tests/test_cod_services.py +302 -0
  166. exp_runtime-0.5.0/tests/test_components.py +104 -0
  167. exp_runtime-0.5.0/tests/test_content.py +221 -0
  168. exp_runtime-0.5.0/tests/test_desktop.py +408 -0
  169. exp_runtime-0.5.0/tests/test_dialogue.py +356 -0
  170. exp_runtime-0.5.0/tests/test_dialogue_lifecycle.py +310 -0
  171. exp_runtime-0.5.0/tests/test_dialogue_notice.py +164 -0
  172. exp_runtime-0.5.0/tests/test_engine.py +94 -0
  173. exp_runtime-0.5.0/tests/test_episode_catalog.py +450 -0
  174. exp_runtime-0.5.0/tests/test_episode_exit.py +311 -0
  175. exp_runtime-0.5.0/tests/test_episode_rar.py +104 -0
  176. exp_runtime-0.5.0/tests/test_episode_zip.py +256 -0
  177. exp_runtime-0.5.0/tests/test_fonts.py +150 -0
  178. exp_runtime-0.5.0/tests/test_football.py +143 -0
  179. exp_runtime-0.5.0/tests/test_football_layout.py +257 -0
  180. exp_runtime-0.5.0/tests/test_games.py +328 -0
  181. exp_runtime-0.5.0/tests/test_grid_layout.py +166 -0
  182. exp_runtime-0.5.0/tests/test_ios_fonts.py +76 -0
  183. exp_runtime-0.5.0/tests/test_ios_host.py +144 -0
  184. exp_runtime-0.5.0/tests/test_ipa.py +407 -0
  185. exp_runtime-0.5.0/tests/test_ipa_music.py +282 -0
  186. exp_runtime-0.5.0/tests/test_label_layout.py +454 -0
  187. exp_runtime-0.5.0/tests/test_loading.py +191 -0
  188. exp_runtime-0.5.0/tests/test_menu.py +459 -0
  189. exp_runtime-0.5.0/tests/test_message_panel.py +169 -0
  190. exp_runtime-0.5.0/tests/test_minigames.py +127 -0
  191. exp_runtime-0.5.0/tests/test_named_dialogue.py +267 -0
  192. exp_runtime-0.5.0/tests/test_native_drawing.py +191 -0
  193. exp_runtime-0.5.0/tests/test_packaging.py +104 -0
  194. exp_runtime-0.5.0/tests/test_panel_lifecycle.py +108 -0
  195. exp_runtime-0.5.0/tests/test_pause_feedback.py +141 -0
  196. exp_runtime-0.5.0/tests/test_rar_library.py +81 -0
  197. exp_runtime-0.5.0/tests/test_relationships.py +262 -0
  198. exp_runtime-0.5.0/tests/test_runtime.py +195 -0
  199. exp_runtime-0.5.0/tests/test_shared_dialogue.py +192 -0
  200. exp_runtime-0.5.0/tests/test_shs_services.py +223 -0
  201. exp_runtime-0.5.0/tests/test_simulator_verification.py +351 -0
  202. exp_runtime-0.5.0/tests/test_story_services.py +106 -0
  203. exp_runtime-0.5.0/tests/test_survey.py +259 -0
  204. exp_runtime-0.5.0/tests/test_text_input.py +264 -0
  205. exp_runtime-0.5.0/tests/test_title_screen.py +246 -0
  206. exp_runtime-0.5.0/tests/test_trace.py +64 -0
  207. exp_runtime-0.5.0/tests/test_ui_assets.py +111 -0
  208. exp_runtime-0.5.0/tests/test_updates.py +317 -0
  209. exp_runtime-0.5.0/tests/test_vm.py +126 -0
  210. exp_runtime-0.5.0/tests/test_word_grid.py +264 -0
  211. exp_runtime-0.5.0/tools/audit_format.py +167 -0
  212. exp_runtime-0.5.0/tools/audit_runtime.py +58 -0
  213. exp_runtime-0.5.0/tools/audit_ui_assets.py +80 -0
  214. exp_runtime-0.5.0/tools/audit_vm.py +30 -0
  215. exp_runtime-0.5.0/tools/build_desktop.py +97 -0
  216. exp_runtime-0.5.0/tools/build_ios.py +233 -0
  217. exp_runtime-0.5.0/tools/build_packages.py +92 -0
  218. exp_runtime-0.5.0/tools/build_rar.py +90 -0
  219. exp_runtime-0.5.0/tools/components.py +40 -0
  220. exp_runtime-0.5.0/tools/package_desktop.py +166 -0
  221. exp_runtime-0.5.0/tools/package_ios.py +119 -0
  222. exp_runtime-0.5.0/tools/test_ios.py +224 -0
  223. exp_runtime-0.5.0/tools/test_packages.py +206 -0
  224. exp_runtime-0.5.0/uv.lock +406 -0
@@ -0,0 +1,259 @@
1
+ name: Builds
2
+
3
+ on:
4
+ push:
5
+ branches: ['**']
6
+ tags: ['v*']
7
+ pull_request:
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ concurrency:
14
+ group: desktop-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
15
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }}
16
+
17
+ jobs:
18
+ packages:
19
+ name: Python wheels (${{ matrix.python }})
20
+ runs-on: ubuntu-22.04
21
+ timeout-minutes: 15
22
+ strategy:
23
+ fail-fast: false
24
+ matrix:
25
+ python: ['3.10', '3.14']
26
+ steps:
27
+ - name: Check out source
28
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
29
+ with:
30
+ persist-credentials: false
31
+
32
+ - name: Install uv and select Python
33
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
34
+ with:
35
+ version: '0.12.13'
36
+ python-version: ${{ matrix.python }}
37
+ enable-cache: true
38
+ cache-suffix: python-${{ matrix.python }}
39
+
40
+ - name: Install locked build dependencies
41
+ run: uv sync --locked --extra build
42
+
43
+ - name: Build and audit wheels and source archives
44
+ shell: bash
45
+ run: |
46
+ flags=()
47
+ if [[ "$GITHUB_REF_TYPE" == tag ]]; then
48
+ flags+=(--tag "$GITHUB_REF_NAME")
49
+ fi
50
+ uv run --locked --extra build python tools/build_packages.py "${flags[@]}"
51
+
52
+ - name: Verify isolated pip installs and component removal
53
+ run: uv run --locked --extra build python tools/test_packages.py
54
+
55
+ - name: Upload Python distributions and checksums
56
+ if: matrix.python == '3.14'
57
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
58
+ with:
59
+ name: python-packages
60
+ path: dist/packages/*
61
+ if-no-files-found: error
62
+ retention-days: 30
63
+
64
+ ios:
65
+ name: iOS
66
+ uses: ./.github/workflows/ios-builds.yml
67
+
68
+ build:
69
+ name: ${{ matrix.target }}
70
+ runs-on: ${{ matrix.runner }}
71
+ timeout-minutes: 30
72
+ strategy:
73
+ fail-fast: false
74
+ matrix:
75
+ include:
76
+ - target: windows-x64
77
+ runner: windows-2022
78
+ - target: linux-x64
79
+ runner: ubuntu-22.04
80
+ - target: macos-arm64
81
+ runner: macos-14
82
+ - target: macos-x64
83
+ runner: macos-15-intel
84
+ defaults:
85
+ run:
86
+ shell: bash
87
+ env:
88
+ SDL_VIDEODRIVER: dummy
89
+ SDL_AUDIODRIVER: dummy
90
+ PYGAME_HIDE_SUPPORT_PROMPT: '1'
91
+ EXP_TARGET: ${{ matrix.target }}
92
+ EXP_BUILD_LABEL: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || github.sha }}
93
+ steps:
94
+ - name: Check out source
95
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
96
+ with:
97
+ persist-credentials: false
98
+
99
+ - name: Install uv and select Python
100
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
101
+ with:
102
+ version: '0.12.13'
103
+ python-version: '3.14'
104
+ enable-cache: true
105
+ cache-suffix: ${{ matrix.target }}
106
+
107
+ - name: Install locked dependencies
108
+ run: uv sync --locked --extra build
109
+
110
+ - name: Identify native build image
111
+ if: runner.os != 'macOS'
112
+ id: rar-image
113
+ # The hosted image version covers compiler, SDK and CMake updates.
114
+ # If unavailable, skip caching and build normally.
115
+ run: echo "version=${ImageVersion:-}" >> "$GITHUB_OUTPUT"
116
+
117
+ - name: Restore RAR decoder
118
+ if: runner.os != 'macOS' && steps.rar-image.outputs.version != ''
119
+ id: rar-cache
120
+ uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
121
+ with:
122
+ path: |
123
+ build/rar/install
124
+ build/rar/libarchive-*.tar.xz
125
+ key: rar-v1-${{ matrix.runner }}-${{ runner.arch }}-${{ steps.rar-image.outputs.version }}-${{ hashFiles('tools/build_rar.py') }}
126
+
127
+ - name: Prepare RAR decoder
128
+ run: uv run --locked --extra build python tools/build_rar.py
129
+
130
+ # Persist a successful dependency build even if later tests fail.
131
+ - name: Save RAR decoder
132
+ if: steps.rar-cache.outputs.cache-primary-key != '' && steps.rar-cache.outputs.cache-hit != 'true'
133
+ uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
134
+ with:
135
+ path: |
136
+ build/rar/install
137
+ build/rar/libarchive-*.tar.xz
138
+ key: ${{ steps.rar-cache.outputs.cache-primary-key }}
139
+
140
+ - name: Run tests without game content
141
+ run: uv run --locked --extra build python -m unittest discover -s tests
142
+
143
+ - name: Build desktop application
144
+ run: uv run --locked --extra build python tools/build_desktop.py
145
+
146
+ - name: Package, audit and test the extracted download
147
+ run: >-
148
+ uv run --locked --extra build python tools/package_desktop.py
149
+ --target "$EXP_TARGET" --label "$EXP_BUILD_LABEL" --smoke-test
150
+
151
+ - name: Upload downloadable archive and checksum
152
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
153
+ with:
154
+ name: desktop-${{ matrix.target }}
155
+ path: dist/downloads/*
156
+ if-no-files-found: error
157
+ compression-level: 0
158
+ retention-days: 30
159
+
160
+ publish-pypi:
161
+ name: Publish Python distributions to PyPI
162
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
163
+ needs: [build, ios, packages]
164
+ runs-on: ubuntu-22.04
165
+ timeout-minutes: 10
166
+ environment:
167
+ name: pypi
168
+ url: https://pypi.org/p/exp-runtime
169
+ permissions:
170
+ id-token: write
171
+ env:
172
+ EXP_TAG: ${{ github.ref_name }}
173
+ steps:
174
+ # Reuse the audited artifacts; do not build or install project code in
175
+ # the job that can request PyPI credentials.
176
+ - name: Download tested Python distributions and checksums
177
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
178
+ with:
179
+ name: python-packages
180
+ path: dist/packages
181
+
182
+ - name: Verify checksums and stage only publishable distributions
183
+ working-directory: dist/packages
184
+ shell: bash
185
+ run: |
186
+ version="${EXP_TAG#v}"
187
+ mkdir ../pypi
188
+ for name in exp_runtime exp_runtime_formats exp_runtime_engine exp_runtime_player exp_runtime_shs exp_runtime_cod; do
189
+ for suffix in -py3-none-any.whl .tar.gz; do
190
+ file="$name-$version$suffix"
191
+ sha256sum --check --strict "$file.sha256"
192
+ cp -- "$file" ../pypi/
193
+ done
194
+ done
195
+
196
+ - name: Publish wheels and source archives with Trusted Publishing
197
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
198
+ with:
199
+ packages-dir: dist/pypi/
200
+
201
+ release:
202
+ name: Publish versioned downloads
203
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
204
+ needs: [build, ios, packages]
205
+ runs-on: ubuntu-22.04
206
+ timeout-minutes: 10
207
+ permissions:
208
+ contents: write
209
+ env:
210
+ EXP_TAG: ${{ github.ref_name }}
211
+ steps:
212
+ - name: Check out release notes
213
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
214
+ with:
215
+ persist-credentials: false
216
+
217
+ - name: Download desktop archives and checksums
218
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
219
+ with:
220
+ pattern: desktop-*
221
+ path: dist/release
222
+ merge-multiple: true
223
+
224
+ - name: Download unsigned IPA and checksum
225
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
226
+ with:
227
+ name: ios-arm64-unsigned
228
+ path: dist/release
229
+
230
+ - name: Verify downloaded checksums
231
+ working-directory: dist/release
232
+ shell: bash
233
+ run: |
234
+ for target in windows-x64.zip linux-x64.tar.gz macos-arm64.zip macos-x64.zip ios-arm64-unsigned.ipa; do
235
+ test -s "exp-runtime-$EXP_TAG-$target"
236
+ test -s "exp-runtime-$EXP_TAG-$target.sha256"
237
+ done
238
+ sha256sum --check ./*.sha256
239
+
240
+ - name: Create release or update its downloads
241
+ env:
242
+ GH_TOKEN: ${{ github.token }}
243
+ GH_REPO: ${{ github.repository }}
244
+ shell: bash
245
+ run: |
246
+ if draft=$(gh release view "$EXP_TAG" --repo "$GH_REPO" --json isDraft --jq .isDraft 2>/dev/null); then
247
+ gh release upload "$EXP_TAG" dist/release/* --repo "$GH_REPO" --clobber
248
+ if [[ "$draft" == true ]]; then
249
+ gh release edit "$EXP_TAG" --repo "$GH_REPO" --draft=false
250
+ fi
251
+ else
252
+ flags=()
253
+ if [[ "$EXP_TAG" == *-* ]]; then
254
+ flags+=(--prerelease)
255
+ fi
256
+ gh release create "$EXP_TAG" dist/release/* \
257
+ --repo "$GH_REPO" --verify-tag --title "EXP Runtime $EXP_TAG" \
258
+ --notes-file RELEASE.md "${flags[@]}"
259
+ fi
@@ -0,0 +1,103 @@
1
+ name: iOS builds
2
+
3
+ on:
4
+ workflow_call:
5
+ workflow_dispatch:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ concurrency:
11
+ group: ios-${{ github.workflow }}-${{ github.ref }}
12
+ cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }}
13
+
14
+ jobs:
15
+ build:
16
+ name: Unsigned iPhone app
17
+ runs-on: macos-26
18
+ timeout-minutes: 30
19
+ env:
20
+ PYTHONDONTWRITEBYTECODE: '1'
21
+ SDL_VIDEODRIVER: dummy
22
+ SDL_AUDIODRIVER: dummy
23
+ PYGAME_HIDE_SUPPORT_PROMPT: '1'
24
+ EXP_BUILD_LABEL: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || github.sha }}
25
+ steps:
26
+ - name: Check out source
27
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
28
+ with:
29
+ persist-credentials: false
30
+
31
+ - name: Install uv and select Python
32
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
33
+ with:
34
+ version: '0.12.13'
35
+ python-version: '3.14'
36
+ enable-cache: true
37
+ cache-suffix: ios
38
+
39
+ - name: Install locked host dependencies
40
+ run: uv sync --locked --extra desktop
41
+
42
+ - name: Restore iOS dependencies
43
+ id: ios-dependencies-cache
44
+ uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
45
+ with:
46
+ path: build/ios/dependencies
47
+ key: ios-deps-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('ios/dependencies.json', 'tools/build_ios.py') }}
48
+
49
+ - name: Prepare iOS dependencies
50
+ run: |
51
+ uv run --locked --extra desktop python tools/build_ios.py --prepare --sdk iphonesimulator
52
+ uv run --locked --extra desktop python tools/build_ios.py --prepare --sdk iphoneos
53
+
54
+ # Cache both SDKs before tests or simulator boot can fail.
55
+ - name: Save iOS dependencies
56
+ if: steps.ios-dependencies-cache.outputs.cache-primary-key != '' && steps.ios-dependencies-cache.outputs.cache-hit != 'true'
57
+ uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
58
+ with:
59
+ path: build/ios/dependencies
60
+ key: ${{ steps.ios-dependencies-cache.outputs.cache-primary-key }}
61
+
62
+ - name: Run authored engine and packaging tests
63
+ run: uv run --locked --extra desktop python -m unittest discover -s tests
64
+
65
+ - name: Build simulator app
66
+ run: uv run --locked --extra desktop python tools/build_ios.py --sdk iphonesimulator
67
+
68
+ - name: Verify shared engine and native presentation on iOS
69
+ run: uv run --locked --extra desktop python tools/test_ios.py --boot-timeout 600 --install-timeout 300
70
+
71
+ - name: Upload simulator failure diagnostics
72
+ if: failure()
73
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
74
+ with:
75
+ name: ios-simulator-diagnostics
76
+ path: build/ios/simulator-diagnostics/
77
+ if-no-files-found: warn
78
+ retention-days: 7
79
+
80
+ - name: Build unsigned device app
81
+ run: uv run --locked --extra desktop python tools/build_ios.py --sdk iphoneos
82
+
83
+ - name: Audit and package unsigned IPA
84
+ run: uv run --locked --extra desktop python tools/package_ios.py --label "$EXP_BUILD_LABEL"
85
+
86
+ - name: Upload unsigned IPA and checksum
87
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
88
+ with:
89
+ name: ios-arm64-unsigned
90
+ path: |
91
+ dist/downloads/*-ios-arm64-unsigned.ipa
92
+ dist/downloads/*-ios-arm64-unsigned.ipa.sha256
93
+ if-no-files-found: error
94
+ compression-level: 0
95
+ retention-days: 30
96
+
97
+ - name: Upload simulator verification report
98
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
99
+ with:
100
+ name: ios-simulator-verification
101
+ path: build/ios/verification-simulator.json
102
+ if-no-files-found: error
103
+ retention-days: 30
@@ -0,0 +1,68 @@
1
+ # Python environments, caches and generated packages.
2
+ .venv/
3
+ venv/
4
+ __pycache__/
5
+ *.py[cod]
6
+ *.egg-info/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ .mypy_cache/
10
+ .coverage*
11
+ htmlcov/
12
+ build/
13
+ dist/
14
+ *.spec
15
+
16
+ # User-provided game files and imported content.
17
+ *.[aA][pP][kK]
18
+ *.[iI][pP][aA]
19
+ *.[eE][xX][pP]
20
+ *.[zZ][iI][pP]
21
+ *.[rR][aA][rR]
22
+ *.[kK][iI][wW]
23
+ *.[sS][aA][vV]
24
+ *.shs-save.json
25
+ *.shs-auto.json
26
+ .shs-library/
27
+ .shs-import-*/
28
+ .exp-import-*/
29
+ .exp-library/
30
+ /library/
31
+ /Episodes/
32
+ /extract/
33
+ /game-assets/
34
+ /converted_assets/
35
+ /debug_images/
36
+ /original-screenshots/
37
+ /output/
38
+ /renpy-*-sdk/
39
+ /decomp/
40
+
41
+ # Private native research and generated reports.
42
+ *.so
43
+ *.dylib
44
+ *.dll
45
+ *.gpr
46
+ *.rep/
47
+ *.gzf
48
+ /docs/native-*
49
+ /docs/*-audit.json
50
+ /docs/*-trace.json
51
+ /local-reports/
52
+ *.log
53
+
54
+ # Editor and machine-local state.
55
+ .DS_Store
56
+ .idea/
57
+ .vscode/
58
+ xcuserdata/
59
+ *.xcuserstate
60
+ DerivedData/
61
+ *.xcarchive/
62
+ /ios/Signing.local.xcconfig
63
+ *.p12
64
+ *.p8
65
+ *.mobileprovision
66
+ *.provisionprofile
67
+ .env
68
+ .env.*
@@ -0,0 +1,75 @@
1
+ # EXP Runtime
2
+
3
+ This is an experimental compatible engine for Surviving High School and Cause
4
+ of Death. Users supply SHS Android 1.0.9 APK / SHS IPA or CoD IPA assets and
5
+ additional EXP episodes. `exp_runtime` executes
6
+ original KiWi bytecode and renders user-supplied resources. It is not a static
7
+ ARM recompilation or a Ren'Py conversion.
8
+
9
+ ## Build and verify
10
+
11
+ ```sh
12
+ uv sync --locked --extra desktop
13
+ uv run --locked --extra desktop python -m unittest discover -s tests
14
+ uv run --locked --extra desktop exp-runtime
15
+ uv run --locked --extra build python tools/build_desktop.py
16
+ ```
17
+
18
+ Tests use authored fixtures. Optional original-content checks skip when a
19
+ player's local content is absent. Do not add original assets as test fixtures.
20
+
21
+ ## Architecture and contracts
22
+
23
+ One uv workspace produces the `exp-runtime` umbrella and five component wheels.
24
+ Source lives under `packages/{formats,engine,player,shs,cod}/src/exp_runtime`.
25
+ Keep the shared `exp_runtime` namespace free of a root `__init__.py`, and give
26
+ each source file one wheel owner. `tools/build_packages.py` audits the wheels;
27
+ `tools/test_packages.py` checks isolated pip installs. See docs/SDK.md.
28
+
29
+ - `shs/`, `cod/`: game-specific import, asset/audio bindings and host services.
30
+ - `content.py`: validated EXP/APK/IPA import, exact resource IDs, relocatable
31
+ per-game libraries and separation of base versus episode resource banks.
32
+ - `decode/bytecode.py`, `vm.py`: lossless KiWi representation and real VM
33
+ execution, including pending argument frames and callbacks.
34
+ - `engine.py`, `runtime.py`: host services, panel state, clocks, choices,
35
+ mini games and versioned JSON saves.
36
+ - `application.py`, `desktop*.py`: shared launcher, menu, rendering and input.
37
+ - `graphics.py`, `platforms/`, `ios/`: pygame/native drawing primitives and the
38
+ Swift iOS host; keep game layout and behavior in the shared Python code.
39
+ - `fonts.py`, `ui_assets.py`, `atlas.py`: native asset and layout contracts.
40
+
41
+ Follow SCHEMA.md, VM_SPEC.md and docs/ENGINE_ABI.md. Unknown services must
42
+ remain explicit stops with their pending arguments intact. Never invent a
43
+ score, result, scene link or random value to bypass them. The heuristic AST
44
+ parser and Ren'Py experiments belong to the original research project.
45
+
46
+ Preserve the 1:1 target: original positions, font metrics, portrait masking,
47
+ pagination, panel lifecycle, input gates, timers, randomness and script-visible
48
+ side effects. Recover behavior and presentation together. Use Android 1.0.9
49
+ as the available reference for the reported identical iOS/Android interface.
50
+ The owner selected v0.1.3's SHS skin as the common presentation reference,
51
+ then supplied original screenshots and requested native verification of the
52
+ general title fix. Keep a common dialogue renderer; adapters supply each
53
+ game’s assets and native font metrics. Do not introduce a separate IPA layout.
54
+ The owner subsequently selected the verified iOS dialogue-box opening
55
+ animation for both games/platforms (120 ms travel, then 200 ms expansion).
56
+ This overrides Android's immediate box display; retain the common final
57
+ geometry. See docs/UI_FIDELITY.md for recovered paths and remaining limits.
58
+ Ghidra confirms that both iOS games wrap names at the native region width,
59
+ select a normal/tall header, and keep ordinary body text in its own region.
60
+ Honor those outline-font rules and Android's distinct bitmap-label rules.
61
+ Long titles may straddle the box border: do not force every name wholly above
62
+ it into the normal-height header. Bound fallback ink against the selected
63
+ header, skin, body and portrait; keep the drawing fit separate from saved
64
+ font history. Resolve text colors per game. See docs/IPA.md for evidence.
65
+ For other CoD-specific differences use its
66
+ supplied iOS native reference and docs/COD.md.
67
+ Keep unverified details explicit in the specifications.
68
+
69
+ Do not bundle APKs, EXPs, extracted art/audio/fonts/scripts, native
70
+ decompilations, screenshots, generated games, player libraries or saves.
71
+ Keep MANIFEST.in and package discovery restrictive. Preserve the inherited
72
+ MIT license and attribution in docs/PROVENANCE.md.
73
+
74
+ The owner handles Git initialization, commits, remotes and pushes. Creating
75
+ this directory does not authorize publishing it.