Skip to content

Commit 0ed41d9

Browse files
committed
Fix non-ASCII Windows path handling (platform-layer file reads + UTF-8 code-page manifest); fix watch-window replay double-launch; iPad trackpad momentum scrolling.
1 parent 2d86ddd commit 0ed41d9

10 files changed

Lines changed: 104 additions & 43 deletions

File tree

.efrocachemap

Lines changed: 28 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
### 1.8.0 (build 22972, api 9, 2026-08-11)
1+
### 1.8.0 (build 22973, api 9, 2026-08-12)
22
- Fully implemented asset packages (more on this soon)
33
- Upgraded to Python 3.14. This gives us a few nice useful bits such as zstd
44
compression to help speed up online stuff and also means we can get rid of all

ballisticakit-windows/Directory.Build.props

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,15 @@
88
per-project edits.
99
-->
1010
<Project>
11+
<!--
12+
Merge the UTF-8 active-code-page manifest into every exe so
13+
narrow-string APIs treat char* paths as UTF-8 on Windows 10 1903+
14+
(see utf8.manifest). Static-lib projects ignore Manifest items, so
15+
applying this unconditionally is harmless there.
16+
-->
17+
<ItemDefinitionGroup>
18+
<Manifest>
19+
<AdditionalManifestFiles>$(MSBuildThisFileDirectory)utf8.manifest;%(AdditionalManifestFiles)</AdditionalManifestFiles>
20+
</Manifest>
21+
</ItemDefinitionGroup>
1122
</Project>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<!--
3+
Opts the process into the UTF-8 active code page (Windows 10 1903+),
4+
so narrow-string CRT/Win32 APIs treat char* as UTF-8. The engine uses
5+
UTF-8 std::strings throughout; without this, any narrow file API that
6+
bypasses our Platform wrappers resolves paths through the legacy ANSI
7+
code page and fails on non-ASCII paths (e.g. user-profile dirs).
8+
Merged into every BallisticaKit exe via Directory.Build.props.
9+
-->
10+
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
11+
<application>
12+
<windowsSettings>
13+
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
14+
</windowsSettings>
15+
</application>
16+
</assembly>

pconfig/projectconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"bauiv1lib": "buil"
2424
},
2525
"efrocache_repository_url": "https://files.ballistica.net/cache/ba1",
26-
"engine_build_number": 22972,
26+
"engine_build_number": 22973,
2727
"name": "BallisticaKit",
2828
"public": true,
2929
"python_paths": [
@@ -43,5 +43,5 @@
4343
"tests",
4444
"config"
4545
],
46-
"version": "1.8.0a80"
46+
"version": "1.8.0a81"
4747
}

pconfig/spinoffconfig.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@
274274
'.hlsl',
275275
'.gradle',
276276
'.xml',
277+
'.manifest',
277278
'.java',
278279
'.kt',
279280
'.pro',

src/assets/ba_data/python/baenv.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@
5555

5656
# Build number and version of the ballistica binary we expect to be
5757
# using.
58-
TARGET_BALLISTICA_BUILD = 22972
59-
TARGET_BALLISTICA_VERSION = '1.8.0a80'
58+
TARGET_BALLISTICA_BUILD = 22973
59+
TARGET_BALLISTICA_VERSION = '1.8.0a81'
6060

6161

6262
@dataclass

src/assets/ba_data/python/bauiv1lib/watch.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def __init__(
3939
self._scrollwidget: bui.Widget | None = None
4040
self._columnwidget: bui.Widget | None = None
4141
self._my_replay_selected: str | None = None
42+
self._replay_play_in_flight = False
4243
self._my_replays_rename_window: bui.Widget | None = None
4344
self._my_replay_rename_text: bui.Widget | None = None
4445
self._r = 'watchWindow'
@@ -394,6 +395,13 @@ def _on_my_replay_play_press(self) -> None:
394395
self._start_replay_playback()
395396

396397
def _start_replay_playback(self) -> None:
398+
# A double press (or a second confirm) would otherwise spawn two
399+
# concurrent launch tasks: two launch_replay calls and a second
400+
# transition-out on our root widget (seen in the field as
401+
# 'ContainerWidget was set to transition out twice' reports).
402+
if self._replay_play_in_flight:
403+
return
404+
self._replay_play_in_flight = True
397405
bui.app.create_async_task(self._prepare_and_play_replay())
398406

399407
async def _prepare_and_play_replay(self) -> None:
@@ -406,6 +414,9 @@ async def _prepare_and_play_replay(self) -> None:
406414
# stranding them on a torn-down UI (mirrors how connect_to_party
407415
# preps with the gather UI still present).
408416
if not await bs.prepare_replay(path):
417+
# Prep failed/cancelled; we're staying here, so allow
418+
# another play attempt.
419+
self._replay_play_in_flight = False
409420
return
410421

411422
# Content is ready; now do the fade-out -> launch -> fade-in and
@@ -431,7 +442,11 @@ def do_it() -> None:
431442
bs.new_host_session(mainmenu.MainMenuSession)
432443

433444
bui.fade_screen(False, endcall=bui.CallStrict(bui.pushcall, do_it))
434-
bui.containerwidget(edit=self._root_widget, transition='out_left')
445+
# The user may have navigated away during the async prep above,
446+
# in which case our window is already gone or on its way out;
447+
# transitioning it out again logs a warning.
448+
if self._root_widget and not self._root_widget.transitioning_out:
449+
bui.containerwidget(edit=self._root_widget, transition='out_left')
435450

436451
def _on_my_replay_rename_press(self) -> None:
437452
if self._my_replay_selected is None:

src/ballistica/shared/ballistica.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ auto main(int argc, char** argv) -> int {
5151
namespace ballistica {
5252

5353
// These are set automatically via script; don't modify them here.
54-
const int kEngineBuildNumber = 22972;
55-
const char* kEngineVersion = "1.8.0a80";
54+
const int kEngineBuildNumber = 22973;
55+
const char* kEngineVersion = "1.8.0a81";
5656
const int kEngineApiVersion = 9;
5757

5858
#if BA_MONOLITHIC_BUILD

src/ballistica/shared/generic/utils.cc

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "ballistica/core/core.h"
1414
#include "ballistica/core/logging/logging.h"
15+
#include "ballistica/core/platform/platform.h"
1516
#include "ballistica/core/support/base_soft.h"
1617
#include "ballistica/shared/foundation/exception.h"
1718
#include "ballistica/shared/generic/json_facade.h"
@@ -324,16 +325,33 @@ auto Utils::Sphrand(float radius) -> Vector3f {
324325
}
325326

326327
auto Utils::FileToString(const std::string& file_name) -> std::string {
327-
std::ifstream file_stream{file_name};
328-
if (file_stream.fail()) {
328+
// Open through the platform layer: on Windows a bare narrow
329+
// std::ifstream/fopen resolves paths via the legacy ANSI code page,
330+
// so UTF-8 paths containing non-ASCII characters (e.g. a user-profile
331+
// dir) fail to open. Platform::FOpen widens to the wide-char APIs
332+
// there. (Field case: language-blob reads under C:\Users\<non-ascii>
333+
// permanently failing construct-mode bring-up.)
334+
FILE* file = (g_core != nullptr && g_core->platform != nullptr)
335+
? g_core->platform->FOpen(file_name.c_str(), "rb")
336+
: fopen(file_name.c_str(), "rb"); // Pre-core fallback.
337+
if (file == nullptr) {
329338
throw Exception("Error opening file for reading: '" + file_name + "'");
330339
}
331-
std::ostringstream str_stream{};
332-
file_stream >> str_stream.rdbuf();
333-
if (file_stream.fail() && !file_stream.eof()) {
334-
throw Exception("Error reading file: '" + file_name + "'");
340+
std::string out;
341+
char buffer[16384];
342+
while (true) {
343+
size_t amt = fread(buffer, 1, sizeof(buffer), file);
344+
out.append(buffer, amt);
345+
if (amt < sizeof(buffer)) {
346+
bool had_error = ferror(file) != 0;
347+
fclose(file);
348+
if (had_error) {
349+
throw Exception("Error reading file: '" + file_name + "'");
350+
}
351+
break;
352+
}
335353
}
336-
return str_stream.str();
354+
return out;
337355
}
338356

339357
auto Utils::BaseName(const std::string& val) -> std::string {

0 commit comments

Comments
 (0)