Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8dfd3773f | ||
|
|
856ca06a5d | ||
|
|
7a1b683328 | ||
|
|
4b09168e7f | ||
|
|
c208663539 | ||
|
|
307d2a953c | ||
|
|
ce8eba8088 | ||
|
|
62dee62eb6 | ||
|
|
cf11cc86ce | ||
|
|
40e4ff2cf8 | ||
|
|
e901c92b41 | ||
|
|
dc52b7a592 | ||
|
|
93b67eb7d7 | ||
|
|
81a768bc81 | ||
|
|
1b61b52929 | ||
|
|
0110c16001 | ||
|
|
38ccb5f9a4 | ||
|
|
54de4909ef | ||
|
|
1797ecd9ef | ||
|
|
eb16cf4bdc | ||
|
|
52c1934dab | ||
|
|
bc37efd5da | ||
|
|
f9bfca84b0 | ||
|
|
453c24e6e0 | ||
|
|
b1d71f070f | ||
|
|
90ab2ff094 | ||
|
|
8488a161eb | ||
|
|
4c624fcbed | ||
|
|
8652d565da | ||
|
|
bc2344fb44 | ||
|
|
7abd376b85 | ||
|
|
e6d1e09bd7 | ||
|
|
e94255072e | ||
|
|
c527105b31 | ||
|
|
da38822634 | ||
|
|
90dd65cab4 | ||
|
|
efabb0d056 | ||
|
|
4ac609f0df | ||
|
|
be521e2621 | ||
|
|
75b2393ccf | ||
|
|
8592780212 | ||
|
|
861b8faca7 | ||
|
|
f9676b2962 | ||
|
|
0ca693e681 | ||
|
|
9fc9f4e7a4 | ||
|
|
8a899e7650 | ||
|
|
8413225a55 | ||
|
|
0792a3aecb | ||
|
|
3efe79a38d | ||
|
|
e393fbe716 | ||
|
|
5530d19769 | ||
|
|
603ad6b543 | ||
|
|
322146f3cb | ||
|
|
a31c482d5a | ||
|
|
e543fe53e0 | ||
|
|
76fbf67334 | ||
|
|
7524ba6168 | ||
|
|
5a1b8a4094 | ||
|
|
b718ad8468 | ||
|
|
af07c05d21 | ||
|
|
9a8382f1ae | ||
|
|
7da699b035 | ||
|
|
c5c604bb95 | ||
|
|
01797c0434 | ||
|
|
7059096e1b | ||
|
|
54c2d31c97 | ||
|
|
dd16971b6c | ||
|
|
7c942e9a57 | ||
|
|
25697bbaa1 | ||
|
|
a5e6102413 | ||
|
|
b9e393c00c | ||
|
|
c54185c088 | ||
|
|
6b214c63a7 | ||
|
|
19d84ce775 | ||
|
|
5f4beed534 | ||
|
|
17a4c84a0a | ||
|
|
2c849ca30c | ||
|
|
c794406c20 | ||
|
|
8662777f11 | ||
|
|
f80bd15210 | ||
|
|
4773c351d2 | ||
|
|
e24df8ead9 | ||
|
|
7a10a413c8 | ||
|
|
e7d334c66a | ||
|
|
f8b3eecb37 | ||
|
|
adccd47fe0 | ||
|
|
5e228ea9db | ||
|
|
7cf24ae589 | ||
|
|
e5e97c0526 | ||
|
|
2e376b992e | ||
|
|
ff2e07fac1 | ||
|
|
521c44f862 | ||
|
|
0f3ceb9eab | ||
|
|
39fcfdafaf | ||
|
|
4afb59660a | ||
|
|
1a98636fc0 | ||
|
|
14ec9fc4a2 | ||
|
|
73677cf26d | ||
|
|
b43c70168a | ||
|
|
a71a4ac135 | ||
|
|
1b25ea9a37 | ||
|
|
fccf773db3 | ||
|
|
201442d377 | ||
|
|
164026a7f1 | ||
|
|
606d0a2458 | ||
|
|
862c393cd8 | ||
|
|
09255f0e78 | ||
|
|
38fdd8cb24 | ||
|
|
25f719236d | ||
|
|
356a8d412c | ||
|
|
5e55d8f83a |
No files matched your search
@@ -371,7 +371,6 @@ jobs:
|
||||
with:
|
||||
sparse-checkout: |
|
||||
port/android
|
||||
port/third_party/extract-xiso
|
||||
tools
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
@@ -469,6 +468,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: write
|
||||
pages: write
|
||||
id-token: write
|
||||
concurrency:
|
||||
@@ -483,6 +483,45 @@ jobs:
|
||||
name: halo-web-release
|
||||
path: site
|
||||
|
||||
# The game's wasm, most of a build, from jsDelivr rather than GitHub
|
||||
# Pages, which serves 100 GB a month: the wasm alone in a commit of the
|
||||
# web-wasm branch (pushed over the last, so the repository keeps one),
|
||||
# which jsDelivr serves by its hash (with CORS; GitHub's release
|
||||
# downloads have none, so a page cannot fetch them), and version.json
|
||||
# names that copy and its SHA-256. The site's service worker takes the
|
||||
# wasm from there, checked, and from the site only when it cannot
|
||||
# (port/web/site/sw.js). A failure here leaves the site serving it.
|
||||
- name: The wasm on jsDelivr
|
||||
continue-on-error: true
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
sha256="$(sha256sum site/halo.wasm | cut -d' ' -f1)"
|
||||
mkdir wasm
|
||||
cp site/halo.wasm wasm/
|
||||
cd wasm
|
||||
git init -q -b web-wasm
|
||||
git -c user.name="github-actions[bot]" -c user.email="41898282+github-actions[bot]@users.noreply.github.com" \
|
||||
add halo.wasm
|
||||
git -c user.name="github-actions[bot]" -c user.email="41898282+github-actions[bot]@users.noreply.github.com" \
|
||||
commit -q -m "The web build's wasm (build $GITHUB_RUN_NUMBER, $GITHUB_SHA)"
|
||||
git push -q --force "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" HEAD:refs/heads/web-wasm
|
||||
url="https://cdn.jsdelivr.net/gh/${GITHUB_REPOSITORY}@$(git rev-parse HEAD)/halo.wasm"
|
||||
# (jsDelivr fetches it now, and it must be the same file: it serves
|
||||
# files of at most 20 MB from GitHub)
|
||||
curl -fsSL --retry 6 --retry-delay 10 --retry-all-errors -o served.wasm "$url"
|
||||
echo "$sha256 served.wasm" | sha256sum -c -
|
||||
cd ..
|
||||
python3 - "$url" "$sha256" <<'EOF'
|
||||
import json, sys
|
||||
with open("site/version.json") as file:
|
||||
stamp = json.load(file)
|
||||
stamp["wasm"] = {"url": sys.argv[1], "sha256": sys.argv[2]}
|
||||
with open("site/version.json", "w") as file:
|
||||
json.dump(stamp, file, indent=1)
|
||||
EOF
|
||||
echo "the wasm from $url"
|
||||
|
||||
- uses: actions/configure-pages@v5
|
||||
|
||||
- uses: actions/upload-pages-artifact@v3
|
||||
|
||||
@@ -15,8 +15,8 @@ end.
|
||||
| Path | Contents |
|
||||
| --- | --- |
|
||||
| `source/` | The game: 466 C files of the decompilation, compiled as they are with few changes (each marked, below) |
|
||||
| `port/linux/src/` | The platform layer the three ports share: Direct3D 8 on OpenGL (`d3d8_gl.c`), DirectSound and XInput on SDL3, files, threads, memory, sockets, settings (`port_config.c`), internet play (`p2p*.c`) |
|
||||
| `port/linux/game/` | The port's game-side code, compiled as the game's sources are: the distributed netcode (`network_*.c`), co-op, map sharing (`map_share*.c`), the menus (`menu_tags.c`, `menu_functions.c`), Custom Edition maps (`custom_edition_*.c`, `cache_file_formats.c`), the tag validator (`tag_validate.c`, `tag_schema_*.c`), touch, FOV and more |
|
||||
| `port/linux/src/` | The platform layer the three ports share: Direct3D 8 on OpenGL (`d3d8_gl.c`), DirectSound and XInput on SDL3, files, threads, memory, sockets, settings (`port_config.c`), the mods folder (`port_mods.c`), internet play (`p2p*.c`) |
|
||||
| `port/linux/game/` | The port's game-side code, compiled as the game's sources are: the distributed netcode (`network_*.c`), co-op, map sharing (`map_share*.c`), the menus (`menu_tags.c`, `menu_functions.c`), Custom Edition maps (`custom_edition_*.c`, `cache_file_formats.c`), the tag validator (`tag_validate.c`, `tag_schema_*.c`), mods (`mods.c`, `mod_*.c`), streamer mode's word filter (`text_filter*.c`), touch, FOV and more |
|
||||
| `port/linux/include/` | The port's headers: the limits (`halo_port_limits.h`, with `HALO_PORT_NETWORK_VERSION`), capacities (`halo_port_capacity.h`), the prefix header, `halo_linux_source_fixups.h` (port functions the game's sources call) |
|
||||
| `port/include/xdk/` | Stand-ins for the Xbox SDK's headers (declarations only) |
|
||||
| `port/windows/` | The Windows build's own files (`win32_*.c`, headers) |
|
||||
@@ -26,7 +26,7 @@ end.
|
||||
| `port/third_party/` | Vendored libraries, each with a README naming its upstream, version and checksum |
|
||||
| `port/tools/` | Stand-alone tools (`cache_file_report.c`) |
|
||||
| `tools/` | Build scripts (`linux_build.py`, `windows_build.py`, `android_build.py`, `web_build.py`, `ci_build.py`, `web_serve.py`), generators (`ce_menus.py`, `port_settings.py`, `hud_assets.py`, `title_assets.py`, `embed_assets.py`, `xdk_headers.py`) and tests |
|
||||
| `docs/` | Longer design notes (`custom_edition_caches.md`) |
|
||||
| `docs/` | Longer design notes (`custom_edition_caches.md`, `mods.md`) |
|
||||
| `pgo/` | Profile-guided optimisation profiles |
|
||||
| `assets/` | Game data for local runs (gitignored: `maps/`, `custom_maps/`) |
|
||||
| `.github/workflows/` | CI: builds every platform on each push, publishes releases from `main` |
|
||||
@@ -84,6 +84,7 @@ setting (`game.custom_edition`), checked at run time with
|
||||
```sh
|
||||
python -m pytest -q tools/harness tools/test_touch_menu.py # the engine's functions in a fake world
|
||||
python -m pytest -q tools/test_linux_port.py # the build, the maps' checks (needs assets/maps)
|
||||
python -m pytest -q tools/test_mods.py # mods played over a map (needs assets/maps)
|
||||
python -m pytest -q tools/test_cache_file_formats.py tools/test_bmp_files.py tools/test_profile.py
|
||||
python tools/test_touch_layout.py # the Android touch layout (JDK 17+)
|
||||
python tools/test_light_storage.py
|
||||
@@ -189,10 +190,18 @@ otherwise.
|
||||
|
||||
### Third-party code and licences
|
||||
|
||||
The repository is CC0. Vendored code goes in `port/third_party/<name>`,
|
||||
copied unchanged, with a README naming the upstream, the version or commit,
|
||||
the checksum and what uses it. GPL code (OpenSauce, Reclaimer) may be read
|
||||
as documentation of file formats, never copied.
|
||||
The repository is `AGPL-3.0-or-later` (`LICENSE`); the commits before the
|
||||
change, and the releases built from them, stay CC0 (`LICENSES/CC0-1.0.txt`).
|
||||
The README's "Licence" section says what each part is under. Vendored code
|
||||
goes in `port/third_party/<name>`, copied unchanged, with a README naming
|
||||
the upstream, the version or commit, the licence, the checksum and what uses
|
||||
it. Its licence must let it be combined with the AGPL 3.0: MIT, BSD (without
|
||||
an advertising clause), ISC, zlib, Apache 2.0, MPL 2.0, LGPL or GPL 3.0 or
|
||||
later, public domain. GPL-2.0-only code and the 4-clause BSD licence cannot
|
||||
be. Code taken from another project, GPL 3.0 code included, keeps its
|
||||
notices and its licence, and says where it came from.
|
||||
The decompilation's upstream, punpckhdq/halo, is AGPL 3.0 from 17 October
|
||||
2026; bnunu/halo-1 is CC0.
|
||||
|
||||
### Commits and pull requests
|
||||
|
||||
@@ -216,6 +225,8 @@ as documentation of file formats, never copied.
|
||||
| [port/web/README.md](port/web/README.md) | The web build, the page, the game data in the browser, limits, how it operates, hosting on GitHub Pages |
|
||||
| [port/include/xdk/README.md](port/include/xdk/README.md) | How the SDK declarations were written and are checked |
|
||||
| [docs/custom_edition_caches.md](docs/custom_edition_caches.md) | Loading and converting Custom Edition maps, and the evidence for each layout |
|
||||
| [docs/mods.md](docs/mods.md) | Mods: folders of Halo PC tag files whose sounds, bitmaps, text and models play over a map's on one machine |
|
||||
| [port/assets/text_filter/README.md](port/assets/text_filter/README.md) | Streamer mode's words: Surge AI's list, the review, the allowed words |
|
||||
| [port/assets/menus/README.md](port/assets/menus/README.md) | The menu files' format and how to write them again |
|
||||
| [port/assets/menus/NON_HANDDRAWN.md](port/assets/menus/NON_HANDDRAWN.md) | Menu pictures that are not redraws |
|
||||
| [port/assets/menus/UNWIRED.md](port/assets/menus/UNWIRED.md) | Menu functions that do nothing yet |
|
||||
|
||||
@@ -18,3 +18,6 @@
|
||||
[Lucide](https://lucide.dev) 1.53.0, copyright (c) 2026 Lucide Icons and
|
||||
Contributors, under the ISC licence
|
||||
(`port/assets/icons/lucide/LICENSE`).
|
||||
- **The Mods menu's warning icons** (a mod out of its requested order, and
|
||||
a mod that cannot be played): `triangle-alert` and `circle-x` from
|
||||
[Lucide](https://lucide.dev) 1.53.0, under the same licence.
|
||||
@@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
@@ -68,7 +68,8 @@ of the game.
|
||||
- A **Meta Quest 3 / 3S** or a **Steam Frame**.
|
||||
- A **disc image** (`.iso` or `.xiso`) of **Halo: Combat Evolved for the
|
||||
original Xbox**, made from **a disc you own**. Any region and version
|
||||
works.
|
||||
works. The game plays in the disc's language; on the European disc in
|
||||
Spanish and Italian it takes your headset's language (else the first).
|
||||
|
||||
This project does not include, link to or help find game files. Please
|
||||
do not ask for or share disc images in issues or discussions; such posts
|
||||
@@ -116,8 +117,9 @@ No computer and no keyboard needed.
|
||||
library and offers to switch back to Gaming Mode.
|
||||
6. Start **Ringfall: Mjolnir VR** from your library (under *Non-Steam*).
|
||||
|
||||
To update, open the same file in Desktop Mode again; your settings,
|
||||
saves and game data stay. More about the Steam Frame:
|
||||
The game updates itself: when a new version is out, it is downloaded
|
||||
while you play and used from the next start on. Your settings, saves and
|
||||
game data stay. More about the Steam Frame:
|
||||
[port/android/STEAM_FRAME.md](port/android/STEAM_FRAME.md).
|
||||
|
||||
## Playing
|
||||
@@ -168,8 +170,9 @@ Ringfall: Mjolnir VR stands on the work of others:
|
||||
- **[HaloCE-VR](https://github.com/astromaddie/HaloCE-VR)** by astromaddie
|
||||
(CC0): the Steam Frame work, whose findings (such as quieting Mesa's
|
||||
trace markers) are used here.
|
||||
- **[extract-xiso](https://github.com/XboxDev/extract-xiso)**: the disc
|
||||
image readers follow its format handling.
|
||||
- **[xboxdevwiki](https://xboxdevwiki.net/XDVDFS)**: its description of
|
||||
the Xbox's disc file system, which the disc image readers are written
|
||||
from.
|
||||
- The libraries and fonts in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
||||
|
||||
Thank you all.
|
||||
@@ -196,6 +199,26 @@ distributed. You need your own, legally purchased copy of the game. If
|
||||
you enjoy this mod, please support the creators of the original by buying
|
||||
their games.
|
||||
|
||||
This project's own code is released under [CC0 1.0](LICENSE.md), as the
|
||||
decompilation it builds on. Third-party code and fonts keep their own
|
||||
licenses: see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
||||
### Licence
|
||||
|
||||
Ringfall: Mjolnir VR is free software: you can give it to others and change
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, version 3 of the License or (at your option)
|
||||
any later version (`AGPL-3.0-or-later`), as OpenCE, which it builds on, is.
|
||||
It comes with no warranty. The full text is in [LICENSE](LICENSE).
|
||||
|
||||
- **From build 312 on.** The AGPL applies from the commit that took in
|
||||
OpenCE's change of licence (19 October 2026), and to each release built
|
||||
from it or a later one. Earlier releases, and the source at every earlier
|
||||
commit, were released under [CC0 1.0](LICENSES/CC0-1.0.txt) and stay
|
||||
that way.
|
||||
- **Hosts and servers.** If you change the game and let other persons play
|
||||
with your version over a network (for example, as the host of an
|
||||
internet game), the AGPL asks you to offer them the source of your
|
||||
version. The game as released has its source here, and each release's
|
||||
source is its tag (`build-<n>`).
|
||||
- **Third-party code and fonts** keep their own licences: see
|
||||
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
||||
- **Halo is not covered.** The licence covers the work of this project's
|
||||
and OpenCE's authors. It gives no rights in Halo, its maps or anything
|
||||
else that Microsoft owns.
|
||||
@@ -1,14 +1,16 @@
|
||||
# Third-party notices
|
||||
|
||||
Ringfall: Mjolnir VR's own code is released under [CC0 1.0](LICENSE.md).
|
||||
It includes or builds on the following, under their own licenses; each
|
||||
license's full text is in the file named.
|
||||
Ringfall: Mjolnir VR is released under the GNU Affero General Public
|
||||
License, version 3 or later ([LICENSE](LICENSE)); releases before build 312
|
||||
were released under [CC0 1.0](LICENSES/CC0-1.0.txt). It includes or builds
|
||||
on the following, under their own licenses; each license's full text is in
|
||||
the file named.
|
||||
|
||||
## Code
|
||||
|
||||
| Component | Used for | License | Text |
|
||||
| --- | --- | --- | --- |
|
||||
| [OpenCE](https://github.com/OpenCommunityEdition/OpenCE) and the decompilation ([bnunu/halo-1](https://github.com/bnunu/halo-1), [punpckhdq/halo](https://github.com/punpckhdq/halo)) | the game's code, the port | CC0 1.0 | [LICENSE.md](LICENSE.md) |
|
||||
| [OpenCE](https://github.com/OpenCommunityEdition/OpenCE) and the decompilation ([bnunu/halo-1](https://github.com/bnunu/halo-1), [punpckhdq/halo](https://github.com/punpckhdq/halo)) | the game's code, the port | AGPL 3.0 or later (OpenCE from 19 October 2026, punpckhdq/halo from 17 October 2026; CC0 1.0 before); bnunu/halo-1: CC0 1.0 | [LICENSE](LICENSE), [LICENSES/CC0-1.0.txt](LICENSES/CC0-1.0.txt) |
|
||||
| [HaloCE-Quest-VR](https://github.com/moistman42069/HaloCE-Quest-VR) | the arm IK, forearm twist and finger poses (`port/linux/game/vr_arms.c`) | CC0 1.0 | (its repository) |
|
||||
| [HaloCE-VR](https://github.com/astromaddie/HaloCE-VR) | Steam Frame findings | CC0 1.0 | (its repository) |
|
||||
| [SDL 3](https://libsdl.org) | windows, input, sound | zlib | (in SDL's source, `LICENSE.txt`) |
|
||||
@@ -22,11 +24,11 @@ license's full text is in the file named.
|
||||
| [tomlc17](https://github.com/cktan/tomlc17) | reading `config.toml` | MIT | [port/third_party/tomlc17/LICENSE](port/third_party/tomlc17/LICENSE) |
|
||||
| [stb](https://github.com/nothings/stb) | drawing the game's text sharply (stb_truetype) | MIT or public domain | [port/third_party/stb/LICENSE](port/third_party/stb/LICENSE) |
|
||||
| [SMAA](https://www.iryoku.com/smaa/) | anti-aliasing (flat screens; off in VR) | MIT | [port/third_party/smaa/LICENSE](port/third_party/smaa/LICENSE) |
|
||||
| [extract-xiso](https://github.com/XboxDev/extract-xiso) | reading disc images (followed by `XisoExtractor.java`, `steam_frame_xiso.py`, `xiso.c`) | BSD-style (in@fishtank.com) | [port/third_party/extract-xiso/LICENSE.TXT](port/third_party/extract-xiso/LICENSE.TXT) |
|
||||
|
||||
The disc image readers include this acknowledgement, as extract-xiso's
|
||||
license asks: *This product includes software developed by in
|
||||
<in@fishtank.com>.*
|
||||
The disc image readers (`XisoExtractor.java`, `steam_frame_xiso.py`,
|
||||
`xiso.c`) are written from a description of the Xbox's disc file system
|
||||
([xboxdevwiki's XDVDFS page](https://xboxdevwiki.net/XDVDFS)), not from
|
||||
extract-xiso, which earlier releases followed.
|
||||
|
||||
## Fonts
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ problems.
|
||||
|
||||
The game updates itself. At start-up it looks for a newer release, and asks
|
||||
if you want to install it. Refer to "Updates" in
|
||||
[port/linux/README.md](../port/linux/README.md#updates). The web site offers
|
||||
[port/linux/README.md](port/linux/README.md#updates). The web site offers
|
||||
each new build when it is out.
|
||||
|
||||
Each build of the `main` branch that passes on all four platforms is a new
|
||||
@@ -43,9 +43,13 @@ release, and the web site then plays it. The [Releases](https://github.com/OpenC
|
||||
page keeps the last five releases. If the latest build has a problem, get
|
||||
an older build from that page.
|
||||
|
||||
### Arch-based Linux Distributions
|
||||
|
||||
The above Linux binary release is available on the [AUR](https://aur.archlinux.org/packages/opence-bin).
|
||||
|
||||
## Game data
|
||||
|
||||
The port does not include the game data. Make an Xbox disc image from your own copy
|
||||
The port does not include the game data. Download an Xbox disc image
|
||||
(`.xiso` or `.iso`) of Halo: Combat Evolved. All versions of the game
|
||||
operate. The maps of the European (PAL) version were made for a slower
|
||||
console. The port changes them to play as the North American (NTSC) maps do,
|
||||
@@ -57,10 +61,19 @@ so players of the two versions can play together.
|
||||
|
||||
On Linux and Windows, the game puts `maps/` next to the executable. On
|
||||
Android, copy the disc image to the phone first. The app puts `maps/` in its
|
||||
data folder. Refer to [port/android/README.md](../port/android/README.md). On
|
||||
data folder. Refer to [port/android/README.md](port/android/README.md). On
|
||||
the web, the page copies `maps/` out of the disc image into the browser's
|
||||
storage for the site; nothing is uploaded. Refer to
|
||||
[port/web/README.md](../port/web/README.md).
|
||||
[port/web/README.md](port/web/README.md).
|
||||
|
||||
## Mods
|
||||
|
||||
Mods are folders of Halo PC tag files, as the Halo Editing Kit and Invader
|
||||
write them, in `mods/` next to `maps/`: texture, sound, text and model packs
|
||||
that play over the maps' on your machine only, in any game. Each has a
|
||||
`mod.toml`. Every mod in the folder is on; the main menu's Mods turns them
|
||||
off and on and sets the order they load in.
|
||||
Refer to [docs/mods.md](docs/mods.md).
|
||||
|
||||
## Platforms
|
||||
|
||||
@@ -68,10 +81,10 @@ Each platform has its own instructions:
|
||||
|
||||
| Platform | Instructions |
|
||||
| --- | --- |
|
||||
| Linux (32-bit x86 executable, OpenGL 4.5, SDL3) | [port/linux/README.md](../port/linux/README.md) |
|
||||
| Windows (32-bit x86 executable, OpenGL 4.5, SDL3) | [port/windows/README.md](../port/windows/README.md) |
|
||||
| Android (arm64 app, OpenGL ES 3, SDL3) | [port/android/README.md](../port/android/README.md) |
|
||||
| Web (WebAssembly, WebGL 2, SDL3; desktop Chrome, Edge and Firefox) | [port/web/README.md](../port/web/README.md) |
|
||||
| Linux (32-bit x86 executable, OpenGL 4.5, SDL3) | [port/linux/README.md](port/linux/README.md) |
|
||||
| Windows (32-bit x86 executable, OpenGL 4.5, SDL3) | [port/windows/README.md](port/windows/README.md) |
|
||||
| Android (arm64 app, OpenGL ES 3, SDL3) | [port/android/README.md](port/android/README.md) |
|
||||
| Web (WebAssembly, WebGL 2, SDL3; desktop Chrome, Edge and Firefox) | [port/web/README.md](port/web/README.md) |
|
||||
|
||||
The Linux README also gives the controls, the settings and the multiplayer
|
||||
functions. These are almost the same on all platforms.
|
||||
@@ -84,19 +97,19 @@ The game can play system link games on a local network and on the internet:
|
||||
- Linux, Windows and Android machines and browsers can play in the same
|
||||
game. On a local network, the native builds play system link; browsers
|
||||
play each other in rooms, over WebRTC
|
||||
([port/web/README.md](../port/web/README.md)).
|
||||
([port/web/README.md](port/web/README.md)).
|
||||
- An invite link, or the server browser, lets a machine join a game on the
|
||||
internet, browsers too: a native build takes a browser's WebRTC
|
||||
([port/linux/README.md](../port/linux/README.md), "Browsers"). No server of
|
||||
([port/linux/README.md](port/linux/README.md), "Browsers"). No server of
|
||||
this project is necessary.
|
||||
- The netcode is new. Each machine moves its own player at once,
|
||||
and the host makes the decisions for the game. Refer to
|
||||
[port/linux/NETCODE.md](../port/linux/NETCODE.md).
|
||||
[port/linux/NETCODE.md](port/linux/NETCODE.md).
|
||||
|
||||
## Build the game
|
||||
|
||||
You do not need the Xbox SDK. The port supplies the SDK declarations that
|
||||
the game uses. Refer to [port/include/xdk](../port/include/xdk/README.md).
|
||||
the game uses. Refer to [port/include/xdk](port/include/xdk/README.md).
|
||||
|
||||
To build the game:
|
||||
|
||||
@@ -127,7 +140,7 @@ Give these options to `configure.py`:
|
||||
| --- | --- |
|
||||
| (none) | A debug build. A failed assertion stops the game. |
|
||||
| `--release` | A release build. The game does not examine assertions, as in the retail game. |
|
||||
| `--portable` | The Linux and Windows builds operate on all x86-64 processors. The Linux build also operates on older distributions and on SteamOS: refer to "Portable build" in [port/linux/README.md](../port/linux/README.md#portable-build). Use this option for builds that you give to other persons. |
|
||||
| `--portable` | The Linux and Windows builds operate on all x86-64 processors. The Linux build also operates on older distributions and on SteamOS: refer to "Portable build" in [port/linux/README.md](port/linux/README.md#portable-build). Use this option for builds that you give to other persons. |
|
||||
| `--lto=thin`, `--lto=off` | Less link-time optimization. The link is faster. |
|
||||
| `--pgo=off` | No profile-guided optimization. |
|
||||
| `--pgo=train` | Records a new optimization profile. Refer to "Optimization profiles". |
|
||||
@@ -187,10 +200,41 @@ file.
|
||||
`debug.profile_memory` sets the memory of a recording in MB (default 256,
|
||||
from 4 to 1024). A recording has no length limit:
|
||||
the game writes a part each time the memory is full. Refer to "Settings" in
|
||||
[port/linux/README.md](../port/linux/README.md).
|
||||
[port/linux/README.md](port/linux/README.md).
|
||||
|
||||
On Android, the files are in `/sdcard/Android/data/com.halo.decomp/files/profiles/`.
|
||||
Enter `adb pull` to copy the recording parts to a computer.
|
||||
|
||||
A profiling build plays with normal builds. It does not use
|
||||
`--pgo=train`.
|
||||
|
||||
## Licence
|
||||
|
||||
OpenCE is free software: you can give it to others and change it under the
|
||||
terms of the GNU Affero General Public License as published by the Free
|
||||
Software Foundation, version 3 of the License or (at your option) any later
|
||||
version (`AGPL-3.0-or-later`). It comes with no warranty. The full text is in
|
||||
[LICENSE](LICENSE).
|
||||
|
||||
- **From the change.** The AGPL applies from the commit that made the change,
|
||||
and to each release built from that commit or a later one. Earlier
|
||||
releases, and the source at every earlier commit, were released under
|
||||
[CC0 1.0](LICENSES/CC0-1.0.txt) and stay that way.
|
||||
- **What OpenCE builds on.** The decompilation of
|
||||
[punpckhdq/halo](https://github.com/punpckhdq/halo) is under the AGPL 3.0
|
||||
from 17 October 2026, and under CC0 1.0 before that date.
|
||||
[bnunu/halo-1](https://github.com/bnunu/halo-1) is under CC0 1.0.
|
||||
- **Contributions** are under `AGPL-3.0-or-later`. A change taken from a
|
||||
project under GPL 3.0 keeps that licence and its notices; the GPL 3.0 and
|
||||
the AGPL 3.0 let the two be combined (section 13 of each).
|
||||
- **Hosts and servers.** If you change the game and let other persons play
|
||||
with your version over a network (for example, as the host of an internet
|
||||
game, or on a web site), the AGPL asks you to offer them the source of
|
||||
your version (section 13). The game as released has its source here, and
|
||||
each release's source is its tag (`build-<n>`).
|
||||
- **Third-party code, fonts and art** keep their own licences, which are
|
||||
next to them (`port/third_party`, `port/assets/fonts`,
|
||||
`port/assets/icons`, `port/assets/text_filter`). The release downloads and
|
||||
the web site's `licenses.txt` carry their notices.
|
||||
- **Halo is not covered.** The licence covers the work of OpenCE's authors.
|
||||
It gives no rights in Halo, its maps or anything else that Microsoft owns.
|
||||
@@ -493,9 +493,9 @@ file the same as the host's; with `network.map_download` off, or a host with
|
||||
but their model parts have its 0x68-byte layout and their shader types its
|
||||
numbering, which makes them a check on what the conversion produces.
|
||||
|
||||
OpenSauce and Reclaimer are GPL-3.0 and this repository is CC0: no code from
|
||||
either was copied. They were read as documentation of the file formats, and
|
||||
every layout used was then checked against the sample maps.
|
||||
No code from OpenSauce or Reclaimer (both GPL-3.0) was copied: they were
|
||||
read as documentation of the file formats, and every layout used was then
|
||||
checked against the sample maps.
|
||||
|
||||
### Documented facts (source, checked against the sample maps)
|
||||
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
# Mods
|
||||
|
||||
A mod is a folder of Halo PC tag files that the game plays over a map's own
|
||||
tags on your machine, as a resource pack does: sounds (a high-res audio
|
||||
pack), bitmaps (a texture pack, a HUD redraw), text (a translation) and
|
||||
models (higher-detail weapons, vehicles and scenery). Only the machine that
|
||||
has a mod plays it. Nothing of a mod is sent to another machine, and a mod
|
||||
changes nothing that the machines of a network game must agree on, so you
|
||||
can play with mods in any game, with players who have other mods or none. (A host's mod sounds of other lengths change how long
|
||||
the host's scripts and AI wait for dialogue to end; the host decides those
|
||||
for every machine, so they still agree.)
|
||||
|
||||
The game's code is `port/linux/game/mods.c` (with `mod_sounds.c`,
|
||||
`mod_bitmaps.c`, `mod_strings.c` and `mod_models.c`, one for each kind of
|
||||
tag) and `port/linux/src/port_mods.c` (the mods folder and its files).
|
||||
|
||||
## A mod's folder
|
||||
|
||||
The mods are in the `mods` folder of the data root (the folder with
|
||||
`maps/`; on Android the app's data folder, on the web the browser's storage
|
||||
for the site). Each mod is a folder in it, with a `mod.toml` and a `tags`
|
||||
folder:
|
||||
|
||||
```
|
||||
mods/
|
||||
hd_audio/
|
||||
mod.toml
|
||||
tags/
|
||||
sound/sfx/weapons/assault rifle/fire.sound
|
||||
sound/dialog/...
|
||||
german/
|
||||
mod.toml
|
||||
tags/
|
||||
ui/hud/hud_item_messages.unicode_string_list
|
||||
```
|
||||
|
||||
The files in `tags` have the paths of the tags they replace: a tag's name,
|
||||
then its group's extension, as the Halo Editing Kit and Invader keep them.
|
||||
`tags/sound/sfx/weapons/assault rifle/fire.sound` replaces the sound tag
|
||||
`sound\sfx\weapons\assault rifle\fire` of every map that has one. Names are
|
||||
not case-sensitive. The game only looks up the names of the map's tags among
|
||||
a mod's files: a mod cannot add tags, and no tag name of a map makes a path
|
||||
outside the mod.
|
||||
|
||||
`mod.toml` says what the mod is. Every key is optional:
|
||||
|
||||
```toml
|
||||
name = "High-res audio" # what the Mods menu shows (else the folder's name)
|
||||
id = "hd_audio" # what other mods name it by (else the folder's name)
|
||||
version = "1.0"
|
||||
authors = ["Someone", "Someone else"] # or one string
|
||||
description = "The game's sounds at 44 kHz"
|
||||
maps = ["a10", "bloodgulch"] # the maps it is for, by their files' names (else every map)
|
||||
load_after = ["hd_textures"] # mods it plays over: they load before it
|
||||
load_before = ["fixes"] # mods that play over it: they load after it
|
||||
incompatible = ["other_audio"] # mods it cannot be played with
|
||||
format = 1 # the mods' format it is made for
|
||||
```
|
||||
|
||||
`load_after`, `load_before` and `incompatible` name other mods by their `id`
|
||||
or their folder's name, not case-sensitive. A mod they name that is not in
|
||||
the mods folder is passed over. `maps = []` is every map.
|
||||
|
||||
A folder without `mod.toml` is not a mod. A mod made for a later `format`
|
||||
than the build's, or whose `maps` names no map, is not played. The game
|
||||
logs anything else wrong in `mod.toml` (an unknown key, a value of the
|
||||
wrong type) and uses the rest; a `mod.toml` that is not TOML at all is
|
||||
logged, and the mod played without what it says.
|
||||
|
||||
The game's log is its output: the terminal on Linux, `halo.log` beside
|
||||
`halo.exe` on Windows, logcat on Android, and the page's log on the web.
|
||||
|
||||
Limits: 256 mods, folder names of 63 bytes at most, 262144 files in all, 24
|
||||
folders deep under `tags`, and 64 maps in a mod's `maps`.
|
||||
|
||||
## The load order
|
||||
|
||||
Every mod in the mods folder is on until you turn it off. The mods load in
|
||||
an order, and where two mods have a file for the same tag, the one loaded
|
||||
later plays.
|
||||
|
||||
- The default order is the folders' names in alphabetical order (not
|
||||
case-sensitive), except that the mods a mod must load after (those it
|
||||
asks to load after, and those that ask to load before it) are taken to
|
||||
just before it. Mods that ask in a ring (a after b, b after a) load
|
||||
alphabetically, and the game's log says so.
|
||||
- The main menu's **Mods** (in the PC version's menus, `display.menus =
|
||||
"pc"`, the default) is laid out as the game lobby is: the mods on the
|
||||
left as a resource pack list has them, the one at the top playing over
|
||||
all those below it (the load order upside down: the mod loaded last is at
|
||||
the top), and the chosen mod's details in the frame on the right (its
|
||||
name, what is wrong with it, whether it is on, its priority, 1 the top,
|
||||
and its version, authors, folder, maps and description). Choosing a mod
|
||||
(A, or a click on the chosen one: the first click chooses it) turns it on
|
||||
or off (ON, OFF); left and right, or **Move up** and **Move down**, move
|
||||
the chosen mod (up: over one more mod). **OK** keeps the order and plays
|
||||
it at once (on a map that is loaded, too); **Cancel** leaves it as it was.
|
||||
- A warning sign beside a mod means it is on the wrong side of a mod it
|
||||
asks to load before or after (both on): a mod that loads after another is
|
||||
above it in the menu. A red cross means a mod that is on says it cannot be
|
||||
played with this one, or that it cannot be played at all (made for a later
|
||||
build, or its `maps` names none): it stays off. The details say which.
|
||||
|
||||
The menu keeps the order, and the mods turned off, in `mods/load_order.toml`,
|
||||
by the mods' folders' names:
|
||||
|
||||
```toml
|
||||
order = ["german", "hd_audio", "hd_textures"]
|
||||
disabled = ["hd_textures"]
|
||||
```
|
||||
|
||||
A mod added to the folder later is put where the default order puts it,
|
||||
after the mod before it there (but on the side it asks for of the mods it
|
||||
names), and is on. A mod gone from the folder is left out. When the file
|
||||
cannot be written, the game says so in its log and keeps the order until it
|
||||
quits.
|
||||
|
||||
### Mods that cannot be played together
|
||||
|
||||
When a mod that is on names another that is on as `incompatible`, the game
|
||||
asks as it starts, before the window opens:
|
||||
|
||||
- **Play anyway** plays them all (closing the box does too);
|
||||
- **Turn off ...** turns off the mods named as incompatible (if mod A says
|
||||
it cannot be played with mod B, B is turned off), and keeps that in
|
||||
`load_order.toml`;
|
||||
- **Quit** closes the game.
|
||||
|
||||
The Mods menu turns them on again. On the web, **Quit** starts the page
|
||||
again. A run nobody watches (`debug.hidden_window`, `debug.null_renderer`,
|
||||
`debug.exit_after` or `debug.network_test`) is not asked: it plays them all,
|
||||
and the game's log says which conflict.
|
||||
|
||||
At the console:
|
||||
|
||||
| Command | What it does |
|
||||
| --- | --- |
|
||||
| `mods_list` | Lists the mods in the mods folder in their load order, and which are on, then how many of the map's string lists, bitmaps, sounds and models the mods play. |
|
||||
| `mods_reload` | Finds the mods again and reads their files again: a changed file is read again (one that now fails a check leaves what was played before), a new one is played, and the map's tag comes back for a file that is gone. If a sound changed, all sounds stop. It does nothing while `mods_enable false`. |
|
||||
| `mods_enable false` | Plays the map's own tags until `mods_enable true` or the next map. |
|
||||
|
||||
These commands work in any game, a client's too: they change nothing of the
|
||||
game.
|
||||
|
||||
### Getting a mod onto each platform
|
||||
|
||||
- **Linux, Windows**: put the mod's folder in `mods/` next to `maps/`.
|
||||
- **Android**: put it in `mods/` in the app's data folder,
|
||||
`/sdcard/Android/data/com.halo.decomp/files/mods`, for example with
|
||||
`adb push hd_audio /sdcard/Android/data/com.halo.decomp/files/mods/`.
|
||||
- **Web**: on the page, **Mods**, **Add a mod folder…** copies a mod's
|
||||
`mod.toml` and `tags` folder into the browser's storage for the site.
|
||||
**Remove** deletes it again.
|
||||
|
||||
## What a mod can replace
|
||||
|
||||
The game plays these groups from a mod. It does not read the files of any
|
||||
other group (a `.weapon`, `.vehicle`, `.model_collision_geometry` or
|
||||
`.scenario`, for example): they would change what the machines of a network
|
||||
game must agree on.
|
||||
|
||||
| Group | Extension | What it is |
|
||||
| --- | --- | --- |
|
||||
| Sound | `.sound` | Sounds, music and dialogue |
|
||||
| Bitmap | `.bitmap` | Textures, sprites, the HUD's pictures |
|
||||
| Unicode string list | `.unicode_string_list` | The text the game shows |
|
||||
| String list | `.string_list` | Other text |
|
||||
| Model | `.gbxmodel` (Halo PC's), `.model` (the Xbox's) | What objects look like: weapons, first-person weapons and arms, vehicles, scenery, characters |
|
||||
|
||||
A file must be a Halo PC tag file of its group, as the Halo Editing Kit's
|
||||
tools and Invader write them (a model also as `invader-extract` writes an
|
||||
Xbox map's: `.model`). The game checks every count, size and offset
|
||||
in it against the file, and every value against what the game reads, before
|
||||
it uses the file. A file that fails a check is left out (the map's tag
|
||||
plays), and `debug.txt` and the console say why.
|
||||
|
||||
### Sounds
|
||||
|
||||
- The samples can be 16-bit PCM, Xbox ADPCM or Ogg Vorbis, mono or stereo,
|
||||
at 22 or 44 kHz. The game converts them to the Xbox ADPCM that it plays,
|
||||
as it does for Custom Edition maps: mono at 22 kHz, stereo at its own rate.
|
||||
- Empty values are filled in as a map's build fills them in: the defaults
|
||||
of the sound's class, the playback rates and the bend.
|
||||
- A sound must have permutations: a file with an empty pitch range (some
|
||||
Halo PC translations have them) is left out.
|
||||
- A permutation must fit in a block of the sound cache (one sound's samples:
|
||||
`MAXIMUM_SOUND_DATA_SIZE`).
|
||||
|
||||
### Bitmaps
|
||||
|
||||
- A bitmap's types and formats are those the game draws: 2D, 3D and cube
|
||||
maps; A8, Y8, AY8, A8Y8, R5G6B5, A1R5G5B5, A4R4G4B4, X8R8G8B8, A8R8G8B8,
|
||||
DXT1, DXT3, DXT5 and P8 bump. Sides are 4096 at most (512 for a 3D
|
||||
bitmap). A bitmap that is not a power of two on each side is drawn
|
||||
linear, as a Custom Edition map's is: it must be an uncompressed 2D
|
||||
bitmap.
|
||||
- One bitmap's pixels may take a quarter of the texture cache: 64 MB on
|
||||
Linux and Windows, 5.5 MB on Android and the web (a 2048 x 2048 DXT5 with
|
||||
its mipmaps fits on all of them).
|
||||
- A bitmap replaces the map's bitmap of its name, and the map's other tags
|
||||
use it as the map's: it must have at least as many bitmaps, of the same
|
||||
types, and as many sequences, each with at least as many sprites. It can
|
||||
be larger: a redraw at twice the size is drawn at the map's size, sharper.
|
||||
- The HUD draws a bitmap at its own size. So for each HUD element that draws
|
||||
a mod's bitmap, the game scales the element by the map's size of what it
|
||||
draws (its sprite, or with no sprites its bitmap) to the mod's, when that
|
||||
is the same ratio for all of them. A bitmap the HUD draws that is not the
|
||||
map's redrawn so (Halo PC's HUD bitmaps lay their sprites out to other
|
||||
sizes) is left out. The HUD's digits can be the map's size or twice it.
|
||||
- The menus draw a bitmap in their widgets' bounds, whatever its size.
|
||||
- Halo PC keeps the channels of a model shader's multipurpose maps and of
|
||||
the HUD's meters in another order than the Xbox. The game finds those
|
||||
bitmaps among the mod's and draws them in the Xbox's order, as it does for
|
||||
Custom Edition maps (`custom_edition_bitmaps.c`).
|
||||
- The pixels are read from the file when the texture cache loads them, not
|
||||
kept in memory.
|
||||
|
||||
### Text
|
||||
|
||||
- A list keeps the map's number of strings: a mod's strings past it are
|
||||
left out, and the map's own strings stay where the mod's list is shorter.
|
||||
- The game uses many strings as the format of what it prints
|
||||
(`%s was killed by %s`). A mod's string replaces the map's only if it has
|
||||
the same conversions in the same order (`%s`, `%d`, ...); otherwise the
|
||||
map's string stays. A translation keeps the conversions of the original.
|
||||
- Unicode strings are UTF-16, as the tool writes them.
|
||||
- The main menu is the port's own (the PC version's menus, `port/assets/menus`),
|
||||
whose text is in tags named `pc\...`. A mod's `ui\shell` lists change the
|
||||
Xbox's menus (`display.menus = "xbox"`) and the text shown while playing
|
||||
(HUD messages, the multiplayer messages, the pause menu).
|
||||
|
||||
### Models
|
||||
|
||||
A model is more than what is drawn: its nodes (its skeleton) are what its
|
||||
animations move, its markers are where projectiles leave a weapon, where a
|
||||
unit sits and where things attach, and its regions and permutations are what
|
||||
damage and an object's variant choose among. The machines of a network game
|
||||
agree on those, so a mod's model must have the map's:
|
||||
|
||||
- the same nodes: their names, hierarchy and default positions;
|
||||
- the same markers: their names, regions, permutations, nodes, positions
|
||||
and rotations (on the model's permutations, as the Halo Editing Kit keeps
|
||||
them, or on the model, as a map does);
|
||||
- the same regions and permutations: their names and flags.
|
||||
|
||||
Positions and rotations may differ from the map's by 0.001 at most (a model
|
||||
exported again is not always bit for bit the map's). Only what is drawn is
|
||||
the mod's: the geometries, their parts and vertices, which geometry each
|
||||
permutation draws at each detail level, the detail levels' cutoffs, the
|
||||
base map's scale, and which shaders each part is drawn with. A detail level
|
||||
the map draws must draw one of the mod's geometries, and one the map draws
|
||||
nothing at stays so. Every shader must be one the map has (a mod cannot add
|
||||
tags); a mod's bitmaps can redraw them.
|
||||
|
||||
The model is checked against each map's own model of its name as that map
|
||||
loads. On a map whose model of that name has other nodes, markers or
|
||||
regions (a custom map's own assault rifle, say), or lacks a shader the mod
|
||||
names, the mod's model is left out and the map's drawn; the mod's other
|
||||
files still play there. `maps` in `mod.toml` keeps a mod to the maps it is
|
||||
made for.
|
||||
|
||||
- Where a mod has both, its `.gbxmodel` plays; otherwise the file of the mod
|
||||
loaded later, whichever its format.
|
||||
- What the tool works out as it builds a map is worked out as it does: a
|
||||
part's strip is its triangles block, its centroid's nodes are its most
|
||||
weighted, its links of 0 to the parts drawn before and after it are none,
|
||||
a base map scale of 0 is 1, and a model whose tag asks for it has the
|
||||
normals of its vertices that share a position blended. The vertices are
|
||||
compressed as the game draws them.
|
||||
- A part's links to the parts drawn before and after it (for transparency)
|
||||
must not go round.
|
||||
- A model of 44 nodes or more (the renderer skins 43 at once; only Custom
|
||||
Edition maps' have so many) is drawn a part's own nodes at a time, as a
|
||||
Custom Edition map's is: a part's vertices may name 22 nodes at most.
|
||||
- The collision geometry, the animations and the object's tag are the map's:
|
||||
a larger weapon still hits and is hit where the map's does, and an object
|
||||
is still left undrawn by the map's bounds of it (a much larger model may
|
||||
vanish at the screen's edges).
|
||||
- Every model made from a file is kept until the map changes, a file edited
|
||||
and read again by `mods_reload` too, and counts against the mods' memory.
|
||||
|
||||
### Shaders
|
||||
|
||||
A model's parts can be drawn with a shader of every group Halo PC has but
|
||||
one: `shader_transparent_chicago_extended`, which Gearbox added for Halo PC.
|
||||
The Xbox game does not have it, so do not use it: use a
|
||||
`shader_transparent_chicago` shader instead. (A Custom Edition map's
|
||||
chicago extended shaders become chicago shaders as the map loads, keeping
|
||||
only their four-stage maps: see
|
||||
[custom_edition_caches.md](custom_edition_caches.md).) As for every
|
||||
shader, it must be one the map has.
|
||||
|
||||
## Making a mod
|
||||
|
||||
1. Get the tags you want to change as tag files. `invader-extract` extracts
|
||||
them from a map, and the Halo Editing Kit's tags are Halo PC's own.
|
||||
2. Change them with the Halo Editing Kit or Invader (`invader-bitmap`,
|
||||
`invader-sound` and `invader-model` build bitmaps, sounds and models from
|
||||
images, audio files and JMS files). Keep each tag's name.
|
||||
3. Put them in `tags/` of a folder in `mods/`, with a `mod.toml`.
|
||||
4. Load a map (a new mod is on). `mods_reload` at the console reads the
|
||||
files again as you change them, without loading the map again.
|
||||
|
||||
`debug.txt` has a line for every file the game leaves out, and why, and a
|
||||
summary of what each kind of tag plays from the mods:
|
||||
|
||||
```
|
||||
mods: 9 string lists from their files (9 new), 0 given back, 0 left out
|
||||
mods: 66 HUD placements scaled to draw larger bitmaps at the map's size
|
||||
mods: 354 bitmaps from their files (354 new), 0 given back, 6 left out
|
||||
mods: 313 sounds from their files (313 new), 0 given back, 2 left out
|
||||
mods: 4 models from their files (4 new), 0 given back, 1 left out
|
||||
```
|
||||
|
||||
## How it operates
|
||||
|
||||
- When a map's tags load, at `mods_reload` and at the Mods menu's **OK**,
|
||||
the game lists the files of the mods that are on, once, in a table of
|
||||
their tag paths. Each of the map's sound, bitmap, string list and model
|
||||
tags is looked up in it.
|
||||
- A tag replaced by a mod takes the file's values in place, so everything
|
||||
that holds the tag keeps it, and the map's own values come back when the
|
||||
file goes. A sound playing or a texture in the cache is never changed
|
||||
under the game: before a reload changes anything, the sounds stop and the
|
||||
caches let go of what they hold.
|
||||
- The mods' tags keep at most 512 MB in memory (192 MB on Android and the
|
||||
web): their files, their sounds' samples, their bitmaps' tags and their
|
||||
models' geometry.
|
||||
- A Custom Edition map takes mods as an Xbox map does.
|
||||
@@ -98,6 +98,7 @@ To install the data from a computer:
|
||||
| Item | Location in `/sdcard/Android/data/io.github.cesarus85.ringfall/files` |
|
||||
| --- | --- |
|
||||
| Saved games (`z:\` and `u:\`) | `save` |
|
||||
| Mods (`docs/mods.md`) | `mods`: `adb push <mod folder> /sdcard/Android/data/com.halo.decomp/files/mods/`; the main menu's Mods orders them and turns them off |
|
||||
| Log | `debug.txt` |
|
||||
| Settings | `config.toml` |
|
||||
|
||||
@@ -160,7 +161,7 @@ controller for player 1. With the default buttons of the profile:
|
||||
| Crouch, Zoom | left and right stick clicks |
|
||||
| Light, Gren. type | white, black |
|
||||
| Pause, Back | start, back |
|
||||
| Up, Down, Left, Right | D-pad |
|
||||
| Up, Down, Left, Right | D-pad (in a network game, Up opens the text chat's keyboard) |
|
||||
|
||||
The names on the buttons follow the profile's "Button layout" (Settings >
|
||||
Gamepads): with "Swap triggers", the right trigger's button says "Grenade".
|
||||
|
||||
@@ -16,8 +16,11 @@ On the Frame, in Desktop Mode (Steam menu > Power > Switch to Desktop):
|
||||
copies the game data out of it, adds "Ringfall: Mjolnir VR" to the Steam library
|
||||
and switches back to Gaming Mode. Start it from the library there.
|
||||
|
||||
Opening the same file again updates the game (settings, saves and game data
|
||||
stay).
|
||||
The game updates itself: while it runs, its launcher looks for a newer
|
||||
release, fetches it and puts it in place as the next start begins
|
||||
(`steam_frame_launch.sh`; `update.auto = false` in `config.toml` stops it;
|
||||
`launcher.log` says what it did). Opening the same file again updates it
|
||||
too (settings, saves and game data stay).
|
||||
|
||||
The desktop file (`steam_frame_installer.desktop`) downloads the latest
|
||||
release's `ringfall-mjolnir-vr-steam-frame.zip` (`tools/steam_frame_bundle.py`, made by
|
||||
|
||||
@@ -18,15 +18,17 @@ named where code is taken over.
|
||||
| --- | --- |
|
||||
| Headsets | Quest 3; Steam Frame (Lepton): its controllers' layout, a Steam library tile that installs, starts, updates and quits the game, data kept in `~/Documents/RingfallMjolnirVR` |
|
||||
| Rendering | The eyes drawn one by one (both at once, multiview, only with `debug.multiview`: its eyes did not always fuse, see the open points); lens flares at their depth in stereo; HUD, menus, scope, crosshair and names as layers of their own |
|
||||
| Aiming | The weapon in the hand, its shots from the muzzle, laser and crosshair options |
|
||||
| Aiming | The weapon in the hand, its shots from the muzzle, laser and crosshair options; each weapon held so that its model's hand lands on the real one, as the free hand's (it learns where each weapon's animations hold the hand; the pistol's had been 15 cm before the real hand, the assault rifle's 8 cm aside), the scope's disc moved with it; `vr.weapon_hand_*` to adjust (2026-10-17) |
|
||||
| Body | Physical crouch (`vr.crouch_limit`) beside the crouch button, leaning (`vr.lean_limit`), melee by swinging, grenades thrown with the left hand, two-handed weapons |
|
||||
| Vehicles | The Warthog's wheel turned with the hands, right trigger throttle, left brake; first-person seats; turrets with handles |
|
||||
| Scores | The menu button held shows the scores on the Quest (a press pauses, as it is let go); the Steam Frame's View button as before; the text chat's lines where the desktop has them (2026-10-18) |
|
||||
| Comfort | Snap or smooth turning, a comfort vignette in vehicles, VR options in the main and pause menus (Y), a page of every controller button |
|
||||
| Menus | The PC version's menus in VR (`vr.menus`), clicked with a laser; text typed on the on-screen keyboard |
|
||||
| Multiplayer | Up to date with OpenCE (network version 29, 2026-10-14): cross-play, its server browser, co-op campaign, password lobbies; join codes and the VR online list |
|
||||
| Bots | Bots in multiplayer: on foot (paths, items, objectives), in vehicles and at turrets; they make room for a joining person |
|
||||
| Bots | Bots in multiplayer: on foot (paths, items, objectives), in vehicles and at turrets; they make room for a joining person, on the LAN too (a lobby they fill is advertised open, 2026-10-18) |
|
||||
| Voice chat | Push to talk on any button (`vr.button_talk`, BUTTONS page; the free hand's grip by default, sending nothing while it reaches for the foregrip or the wheel), open mic, off (2026-10-13) |
|
||||
| Builds | The Android build's assembly conversion (`tools/android_asm_convert.py`) keeps string data as it is: strings and tables starting with "_" or holding \" lost a "_" (Opus's pulse cache among them: the Steam Frame's crashes in online games while talking), 2026-10-13 |
|
||||
| Languages | The European disc in Spanish and Italian (a maps folder for each, `maps_es` and `maps_it`, none called `maps`) imported on the Quest and the Steam Frame: the headset's language's, its language the game's and the menus' (`maps/language.txt`, unless `game.language` is set), 2026-10-17; not yet tried with that disc |
|
||||
| Controls | Left-handed mode (`vr.handedness`): the weapon in the left hand, the arms and grips mirrored, sticks and buttons mirrored on both controllers (`vr.mirror_controls`), the assault rifle's counter read the right way round (2026-10-06) |
|
||||
| Graphics | Presets low / medium / high / max (`vr.graphics`), refresh rate and resolution in the VR options, all kept over a restart (2026-10-06) |
|
||||
| Arms and hands | Both arms by IK from shoulders that follow the torso, the elbow led by the hand's roll; fingers from trigger, grip and touch; `vr.body` arms / hands / off, `vr.fingers` (2026-10-06) |
|
||||
|
||||
@@ -35,7 +35,7 @@ import java.nio.channels.FileChannel;
|
||||
*/
|
||||
public class LauncherActivity extends Activity {
|
||||
private static final int PICK_IMAGE = 1;
|
||||
private static final String SHARED_DATA_ROOT = "/home/steamos/Documents/RingfallMjolnirVR";
|
||||
static final String SHARED_DATA_ROOT = "/home/steamos/Documents/RingfallMjolnirVR";
|
||||
|
||||
private File dataRoot;
|
||||
private TextView status;
|
||||
|
||||
@@ -36,7 +36,8 @@ import java.util.zip.ZipInputStream;
|
||||
* the latest release when it starts, on a thread of its own, and if it is
|
||||
* newer asks the player whether to update:
|
||||
*
|
||||
* - Yes: the release's app (halo-android-release.zip or -debug.zip) is
|
||||
* - Yes: the release's app (ringfall-mjolnir-vr-android-release.zip or
|
||||
* -debug.zip) is
|
||||
* downloaded and handed to Android's package installer, which replaces the
|
||||
* game (closing it) and offers to open the new version.
|
||||
* - No: nothing, until the next start.
|
||||
@@ -45,6 +46,9 @@ import java.util.zip.ZipInputStream;
|
||||
*
|
||||
* Every build of main is signed with the same key (the workflow's), which an
|
||||
* app must keep for Android to install a new version over it.
|
||||
*
|
||||
* Not on the Steam Frame: Lepton installs the APK in the game's folder there
|
||||
* at every start, so its launcher updates that (steam_frame_launch.sh).
|
||||
*/
|
||||
final class Updater {
|
||||
/* the VR port's own releases: the original's, made without VR, must
|
||||
@@ -63,7 +67,8 @@ final class Updater {
|
||||
static void start(Activity activity) {
|
||||
File config = configFile(activity);
|
||||
|
||||
if (BuildConfig.HALO_BUILD_NUMBER <= 0 || config == null || !autoUpdate(config))
|
||||
if (BuildConfig.HALO_BUILD_NUMBER <= 0 || config == null || !autoUpdate(config) ||
|
||||
new File(LauncherActivity.SHARED_DATA_ROOT).isDirectory())
|
||||
return;
|
||||
new Thread(() -> {
|
||||
int latest = latestRelease();
|
||||
|
||||
@@ -4,94 +4,88 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Copies the maps folder out of an Xbox disc image (an "xiso", or a whole
|
||||
* disc's ".iso"), as the desktop games do (port/linux/src/xiso.c, which this
|
||||
* follows).
|
||||
* Copies the maps folder out of the player's Xbox disc image of Halo, for
|
||||
* LauncherActivity, as the desktop games do (port/linux/src/xiso.c; the web
|
||||
* page's is port/web/site/xiso-worker.js). The image is a document the
|
||||
* player picked, read through its FileChannel at absolute positions.
|
||||
*
|
||||
* The image's file system is XDVDFS, read as extract-xiso does
|
||||
* (https://github.com/XboxDev/extract-xiso, extract-xiso.c, whose format
|
||||
* handling this follows; its license is below and in
|
||||
* port/third_party/extract-xiso/LICENSE.TXT): 2048-byte sectors; a volume
|
||||
* descriptor at 0x10000 that starts and ends with "MICROSOFT*XBOX*MEDIA" and
|
||||
* gives the root directory's sector and size; and directories whose entries
|
||||
* form a binary tree (each entry: left and right subtree offsets in 4-byte
|
||||
* units, the start sector, the size, attributes, the name's length and the
|
||||
* name, 4-byte aligned). Images made from a whole disc put the game partition
|
||||
* further in, at one of the offsets below.
|
||||
* Written from a description of the Xbox's disc file system, XDVDFS
|
||||
* (xboxdevwiki's XDVDFS page, https://xboxdevwiki.net/XDVDFS, and the format
|
||||
* facts OpenCE's readers rely on). What this class relies on:
|
||||
*
|
||||
* The files are written to maps.partial first and moved into maps once they
|
||||
* all are, so an interrupted extraction never leaves maps/ui.map behind.
|
||||
* - Sectors are 2048 bytes and integers little-endian. Sector numbers count
|
||||
* from the start of the game partition: an image of the partition alone
|
||||
* has it at 0, an image of a whole disc further in (0x0FD90000, 0x02080000
|
||||
* or 0x18300000 for XGD2, XGD3 and XGD1 discs). They are tried in that
|
||||
* order.
|
||||
* - The volume descriptor is the partition's sector 32 (at 0x10000). It
|
||||
* holds "MICROSOFT*XBOX*MEDIA" at its start and again at 0x7EC, and the
|
||||
* root directory table's start sector (u32 at 0x14) and size in bytes (u32
|
||||
* at 0x18).
|
||||
* - A directory table is a binary tree of entries whose root is the entry at
|
||||
* offset 0. An entry is a left and a right subtree link (u16 each, in
|
||||
* 4-byte units from the table's start, 0 for none), the start sector and
|
||||
* the size of the file or of the subdirectory's table (u32 each), the
|
||||
* attributes (u8, 0x10 for a directory), the name's length (u8) and the
|
||||
* name. Unused bytes of a table are 0xFF, so a left link of 0xFFFF is
|
||||
* padding, not an entry.
|
||||
* - A file is one contiguous run of bytes from the start of its sector.
|
||||
*
|
||||
* Some parts of this code are copyright in@fishtank.com. This product
|
||||
* includes software developed by in <in@fishtank.com>.
|
||||
* The image is the player's file and nothing read from it is trusted. Only
|
||||
* the root's table and the maps folder's are read, each whole and at most
|
||||
* 4 MB; the walk of a table visits each offset once, goes at most 64 links
|
||||
* deep and looks at most 4096 entries; and a file is written only under a
|
||||
* plain name that no file before it had (ignoring case).
|
||||
*
|
||||
* Copyright (c) 2003 in <in@fishtank.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
*
|
||||
* This product includes software developed by in <in@fishtank.com>.
|
||||
*
|
||||
* 4. Neither the name of "in" nor the email address "in@fishtank.com"
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED `AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* The app starts the game once maps/ui.map exists, and makes the maps folder
|
||||
* itself, so the copy is written to maps.partial (emptied first of what an
|
||||
* earlier attempt left) and its files moved into maps once all are
|
||||
* complete, ui.map last.
|
||||
*/
|
||||
final class XisoExtractor {
|
||||
private static final int SECTOR_SIZE = 2048;
|
||||
private static final long VOLUME_DESCRIPTOR_OFFSET = 0x10000;
|
||||
private static final int ENTRY_HEADER_SIZE = 14;
|
||||
private static final int DESCRIPTOR_OFFSET = 0x10000;
|
||||
private static final byte[] MAGIC = "MICROSOFT*XBOX*MEDIA".getBytes(StandardCharsets.ISO_8859_1);
|
||||
private static final int SECOND_MAGIC_OFFSET = 0x7EC;
|
||||
private static final int ENTRY_FIXED_SIZE = 14;
|
||||
private static final int ATTRIBUTE_DIRECTORY = 0x10;
|
||||
/* directory tables are a few sectors; anything much larger is not one */
|
||||
private static final long MAXIMUM_DIRECTORY_SIZE = 4 << 20;
|
||||
private static final int MAXIMUM_FILES = 256;
|
||||
private static final int COPY_BUFFER_SIZE = 1 << 20;
|
||||
private static final byte[] VOLUME_MAGIC = "MICROSOFT*XBOX*MEDIA".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
/* where the game partition starts: a plain image, and whole-disc images
|
||||
(extract-xiso's GLOBAL, XGD3 and XGD1 offsets) */
|
||||
private static final long[] PARTITION_OFFSETS = { 0, 0x0FD90000L, 0x02080000L, 0x18300000L };
|
||||
private static final int PADDING_LINK = 0xFFFF;
|
||||
/** where the game partition may start, in the order they are tried */
|
||||
private static final long[] PARTITION_STARTS = {
|
||||
0x00000000L, // an image of the game partition alone
|
||||
0x0FD90000L, // a whole XGD2 disc
|
||||
0x02080000L, // a whole XGD3 disc
|
||||
0x18300000L, // a whole XGD1 disc (the original Xbox's)
|
||||
};
|
||||
/** directory tables are a few sectors; anything much larger is not one */
|
||||
private static final int MAXIMUM_TABLE_SIZE = 4 * 1024 * 1024;
|
||||
private static final int MAXIMUM_DEPTH = 64;
|
||||
private static final int MAXIMUM_VISITS = 4096;
|
||||
private static final int MAXIMUM_ENTRIES = 256;
|
||||
private static final int COPY_CHUNK = 1024 * 1024;
|
||||
private static final String PARTIAL_DIRECTORY = "maps.partial";
|
||||
|
||||
/** Told as the copy goes: the file being copied, and the bytes copied of all the files'. */
|
||||
interface Progress {
|
||||
void report(String file, long done, long total);
|
||||
}
|
||||
|
||||
/** a reason the player can act on */
|
||||
/** A reason the player can act on: its message is shown as it is. */
|
||||
static final class ExtractException extends IOException {
|
||||
ExtractException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
/** a file or folder found in a directory table */
|
||||
private static final class Entry {
|
||||
final String name;
|
||||
final long sector;
|
||||
@@ -104,187 +98,327 @@ final class XisoExtractor {
|
||||
}
|
||||
}
|
||||
|
||||
private final FileChannel image;
|
||||
private long partition;
|
||||
|
||||
private XisoExtractor(FileChannel image) {
|
||||
this.image = image;
|
||||
private XisoExtractor() {
|
||||
}
|
||||
|
||||
/** copies the image's maps folder to destination/maps */
|
||||
/** Copies the image's maps folder to destination/maps. */
|
||||
static void extractMaps(FileChannel image, File destination, Progress progress) throws IOException {
|
||||
new XisoExtractor(image).extract(destination, progress);
|
||||
}
|
||||
long imageSize = image.size();
|
||||
long partition = -1;
|
||||
long rootSector = 0, rootSize = 0;
|
||||
ByteBuffer descriptor = ByteBuffer.allocate(SECTOR_SIZE);
|
||||
|
||||
private void readAt(long offset, ByteBuffer buffer) throws IOException {
|
||||
while (buffer.hasRemaining()) {
|
||||
int count = image.read(buffer, offset);
|
||||
|
||||
if (count <= 0)
|
||||
throw new ExtractException("Could not read the disc image (is it complete?).");
|
||||
offset += count;
|
||||
}
|
||||
buffer.flip();
|
||||
}
|
||||
|
||||
private static boolean magicAt(ByteBuffer buffer, int offset) {
|
||||
for (int index = 0; index < VOLUME_MAGIC.length; index++) {
|
||||
if (buffer.get(offset + index) != VOLUME_MAGIC[index])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** whether a name is one to write in the maps folder: no path, no "." or
|
||||
* "..", and only printable ASCII that every file system takes as a plain
|
||||
* name (a disc's maps folder holds .map files and loading.tga) */
|
||||
static boolean isPlainName(String name) {
|
||||
if (name.isEmpty() || name.equals(".") || name.equals(".."))
|
||||
return false;
|
||||
for (int index = 0; index < name.length(); index++) {
|
||||
char c = name.charAt(index);
|
||||
if (c < 0x20 || c > 0x7E || "/\\:*?\"<>|".indexOf(c) >= 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** the partition's volume descriptor: the root directory's sector and size */
|
||||
private long[] findVolume() throws IOException {
|
||||
for (long offset : PARTITION_OFFSETS) {
|
||||
ByteBuffer descriptor = ByteBuffer.allocate(SECTOR_SIZE).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
try {
|
||||
readAt(offset + VOLUME_DESCRIPTOR_OFFSET, descriptor);
|
||||
} catch (IOException e) {
|
||||
for (long start : PARTITION_STARTS) {
|
||||
if (start + DESCRIPTOR_OFFSET + SECTOR_SIZE > imageSize)
|
||||
continue;
|
||||
}
|
||||
if (!magicAt(descriptor, 0) || !magicAt(descriptor, 0x7EC))
|
||||
descriptor.clear();
|
||||
if (!readFully(image, descriptor, start + DESCRIPTOR_OFFSET))
|
||||
continue;
|
||||
partition = offset;
|
||||
return new long[] {
|
||||
descriptor.getInt(20) & 0xFFFFFFFFL, descriptor.getInt(24) & 0xFFFFFFFFL,
|
||||
};
|
||||
byte[] bytes = descriptor.array();
|
||||
if (!hasMagic(bytes, 0) || !hasMagic(bytes, SECOND_MAGIC_OFFSET))
|
||||
continue;
|
||||
partition = start;
|
||||
rootSector = u32(bytes, 0x14);
|
||||
rootSize = u32(bytes, 0x18);
|
||||
break;
|
||||
}
|
||||
throw new ExtractException("This is not an Xbox disc image.");
|
||||
}
|
||||
if (partition < 0)
|
||||
throw new ExtractException("This is not an Xbox disc image.");
|
||||
|
||||
/** a directory's table, read whole */
|
||||
private ByteBuffer readDirectory(long sector, long size, String what) throws IOException {
|
||||
if (size <= 0 || size > MAXIMUM_DIRECTORY_SIZE)
|
||||
throw new ExtractException(what);
|
||||
ByteBuffer table = ByteBuffer.allocate((int) size).order(ByteOrder.LITTLE_ENDIAN);
|
||||
readAt(partition + sector * SECTOR_SIZE, table);
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* collects the entries of the subtree at offset (4-byte units), files or
|
||||
* directories as asked
|
||||
*/
|
||||
private static void walk(ByteBuffer table, long offset, int depth, boolean directories, List<Entry> out,
|
||||
int[] visited) {
|
||||
offset *= 4;
|
||||
/* a malformed table must not loop or run off its end */
|
||||
if (depth > 64 || ++visited[0] > 4096 || offset + ENTRY_HEADER_SIZE > table.limit())
|
||||
return;
|
||||
int at = (int) offset;
|
||||
int left = table.getShort(at) & 0xFFFF;
|
||||
int right = table.getShort(at + 2) & 0xFFFF;
|
||||
/* 0xFFFF: padding, an empty directory */
|
||||
if (left == 0xFFFF)
|
||||
return;
|
||||
int nameLength = table.get(at + 13) & 0xFF;
|
||||
if (left != 0)
|
||||
walk(table, left, depth + 1, directories, out, visited);
|
||||
boolean isDirectory = (table.get(at + 12) & ATTRIBUTE_DIRECTORY) != 0;
|
||||
if (at + ENTRY_HEADER_SIZE + nameLength <= table.limit() && nameLength > 0 && isDirectory == directories
|
||||
&& out.size() < MAXIMUM_FILES) {
|
||||
byte[] bytes = new byte[nameLength];
|
||||
for (int index = 0; index < nameLength; index++)
|
||||
bytes[index] = table.get(at + ENTRY_HEADER_SIZE + index);
|
||||
String name = new String(bytes, StandardCharsets.ISO_8859_1);
|
||||
/* (as extract-xiso refuses them: no name may leave the folder, and
|
||||
none but a plain file name is written) */
|
||||
if (isPlainName(name))
|
||||
out.add(new Entry(name, table.getInt(at + 4) & 0xFFFFFFFFL, table.getInt(at + 8) & 0xFFFFFFFFL));
|
||||
}
|
||||
if (right != 0)
|
||||
walk(table, right, depth + 1, directories, out, visited);
|
||||
}
|
||||
|
||||
private void extract(File destination, Progress progress) throws IOException {
|
||||
long[] root = findVolume();
|
||||
|
||||
/* the root's maps folder */
|
||||
ByteBuffer table = readDirectory(root[0], root[1], "The disc image's file system is damaged.");
|
||||
List<Entry> directories = new ArrayList<>();
|
||||
walk(table, 0, 0, true, directories, new int[1]);
|
||||
// the root's maps folder
|
||||
byte[] root = readTable(image, partition, rootSector, rootSize, "The disc image's file system is damaged.");
|
||||
Entry maps = null;
|
||||
for (Entry entry : directories) {
|
||||
if (entry.name.equalsIgnoreCase("maps"))
|
||||
for (Entry entry : walkTable(root, true)) {
|
||||
if (entry.name.equalsIgnoreCase("maps")) {
|
||||
maps = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// (Ringfall: a European disc in two languages, Spanish and Italian,
|
||||
// has no maps folder but one for each, maps_es and maps_it: the
|
||||
// device's language's is taken, else English's, else the first, and
|
||||
// its language written to maps/language.txt, which the game asks the
|
||||
// Xbox for while game.language is unset: port/linux/src/xbox_xapi.c)
|
||||
String language = null;
|
||||
if (maps == null) {
|
||||
String wanted = Locale.getDefault().getLanguage();
|
||||
Entry english = null, first = null;
|
||||
|
||||
for (Entry entry : walkTable(root, true)) {
|
||||
String code = mapsLanguage(entry.name);
|
||||
|
||||
if (code == null)
|
||||
continue;
|
||||
if (code.equals(wanted) && maps == null)
|
||||
maps = entry;
|
||||
if (code.equals("en") && english == null)
|
||||
english = entry;
|
||||
if (first == null)
|
||||
first = entry;
|
||||
}
|
||||
if (maps == null)
|
||||
maps = english != null ? english : first;
|
||||
if (maps != null)
|
||||
language = mapsLanguage(maps.name);
|
||||
}
|
||||
if (maps == null)
|
||||
throw new ExtractException("The disc image has no maps folder: it is not a Halo disc.");
|
||||
|
||||
/* its files */
|
||||
table = readDirectory(maps.sector, maps.size, "The disc image's maps folder is damaged.");
|
||||
List<Entry> files = new ArrayList<>();
|
||||
walk(table, 0, 0, false, files, new int[1]);
|
||||
long total = 0;
|
||||
boolean hasUi = false;
|
||||
// its files
|
||||
byte[] table = readTable(image, partition, maps.sector, maps.size, "The disc image's maps folder is damaged.");
|
||||
List<Entry> files = walkTable(table, false);
|
||||
Entry userInterface = null;
|
||||
for (Entry file : files) {
|
||||
total += file.size;
|
||||
hasUi |= file.name.equalsIgnoreCase("ui.map");
|
||||
if (file.name.equalsIgnoreCase("ui.map"))
|
||||
userInterface = file;
|
||||
}
|
||||
if (!hasUi)
|
||||
if (userInterface == null)
|
||||
throw new ExtractException("The disc image's maps folder has no ui.map: it is not a Halo disc.");
|
||||
|
||||
File partial = new File(destination, "maps.partial");
|
||||
File finished = new File(destination, "maps");
|
||||
if (!partial.isDirectory() && !partial.mkdirs())
|
||||
throw new ExtractException("Could not create " + partial + ".");
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(COPY_BUFFER_SIZE);
|
||||
long done = 0;
|
||||
// (an image that ends before a file does is refused before anything
|
||||
// is written; an empty file's sector need not be anywhere)
|
||||
long total = 0;
|
||||
for (Entry file : files) {
|
||||
File path = new File(partial, file.name);
|
||||
long offset = partition + file.sector * SECTOR_SIZE;
|
||||
long remaining = file.size;
|
||||
if (file.size > 0 && partition + file.sector * SECTOR_SIZE + file.size > imageSize)
|
||||
throw new ExtractException(readFailure(file));
|
||||
total += file.size;
|
||||
}
|
||||
|
||||
try (FileOutputStream out = new FileOutputStream(path)) {
|
||||
FileChannel output = out.getChannel();
|
||||
// the copy, to maps.partial
|
||||
File partial = new File(destination, PARTIAL_DIRECTORY);
|
||||
if (partial.isDirectory()) {
|
||||
File[] leftovers = partial.listFiles();
|
||||
if (leftovers != null) {
|
||||
for (File leftover : leftovers)
|
||||
leftover.delete();
|
||||
}
|
||||
} else {
|
||||
partial.delete();
|
||||
partial.mkdirs();
|
||||
}
|
||||
if (!partial.isDirectory())
|
||||
throw new ExtractException("Could not create " + partial.getPath() + ".");
|
||||
ByteBuffer buffer = ByteBuffer.allocate(COPY_CHUNK);
|
||||
long done = 0;
|
||||
for (Entry file : files)
|
||||
done = copyFile(image, partition, file, new File(partial, file.name), buffer, done, total, progress);
|
||||
|
||||
while (remaining > 0) {
|
||||
buffer.clear();
|
||||
buffer.limit((int) Math.min(remaining, COPY_BUFFER_SIZE));
|
||||
readAt(offset, buffer);
|
||||
int count = buffer.remaining();
|
||||
while (buffer.hasRemaining())
|
||||
output.write(buffer);
|
||||
offset += count;
|
||||
remaining -= count;
|
||||
done += count;
|
||||
progress.report(file.name, done, total);
|
||||
}
|
||||
} catch (ExtractException e) {
|
||||
throw new ExtractException("Could not read " + file.name + " from the disc image (is it complete?).");
|
||||
// then into maps, ui.map (the app's sign that the data is there) last
|
||||
File mapsFolder = new File(destination, "maps");
|
||||
if (!mapsFolder.isDirectory() && !mapsFolder.mkdirs() && !mapsFolder.isDirectory())
|
||||
throw new ExtractException("Could not create " + mapsFolder.getPath() + ".");
|
||||
// (the language of a language's maps folder: before ui.map is there)
|
||||
File languageFile = new File(mapsFolder, "language.txt");
|
||||
languageFile.delete();
|
||||
if (language != null) {
|
||||
try (FileOutputStream out = new FileOutputStream(languageFile)) {
|
||||
out.write((language + "\n").getBytes(StandardCharsets.US_ASCII));
|
||||
} catch (IOException e) {
|
||||
throw new ExtractException("Could not write " + path + " (is the storage full?).");
|
||||
throw new ExtractException("Could not write " + languageFile.getPath() + " (is the storage full?).");
|
||||
}
|
||||
}
|
||||
|
||||
/* (the maps folder may be there, empty: the app makes it for adb) */
|
||||
if (!finished.isDirectory() && !finished.mkdirs())
|
||||
throw new ExtractException("Could not create " + finished + ".");
|
||||
for (Entry file : files) {
|
||||
File from = new File(partial, file.name);
|
||||
File to = new File(finished, file.name);
|
||||
|
||||
to.delete();
|
||||
if (!from.renameTo(to))
|
||||
throw new ExtractException("Could not move " + file.name + " into " + finished + ".");
|
||||
List<Entry> order = new ArrayList<>(files);
|
||||
order.remove(userInterface);
|
||||
order.add(userInterface);
|
||||
for (Entry file : order) {
|
||||
File target = new File(mapsFolder, file.name);
|
||||
target.delete();
|
||||
if (!new File(partial, file.name).renameTo(target))
|
||||
throw new ExtractException("Could not move " + file.name + " into " + mapsFolder.getPath() + ".");
|
||||
}
|
||||
partial.delete();
|
||||
}
|
||||
|
||||
/** a language's maps folder's language ("maps_it": "it"), else null */
|
||||
static String mapsLanguage(String name) {
|
||||
if (name.length() != 7 || !name.regionMatches(true, 0, "maps_", 0, 5))
|
||||
return null;
|
||||
String code = name.substring(5).toLowerCase(Locale.ROOT);
|
||||
return Character.isLetter(code.charAt(0)) && Character.isLetter(code.charAt(1)) ? code : null;
|
||||
}
|
||||
|
||||
/** No path, no . or .., and only printable ASCII that every file system takes as a plain name. */
|
||||
static boolean isPlainName(String name) {
|
||||
if (name == null || name.isEmpty() || name.equals(".") || name.equals(".."))
|
||||
return false;
|
||||
for (int index = 0; index < name.length(); index++) {
|
||||
char character = name.charAt(index);
|
||||
|
||||
if (character < 0x20 || character > 0x7E || "/\\:*?\"<>|".indexOf(character) >= 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ---------- the image */
|
||||
|
||||
private static long u16(byte[] bytes, int offset) {
|
||||
return (bytes[offset] & 0xFF) | (bytes[offset + 1] & 0xFF) << 8;
|
||||
}
|
||||
|
||||
private static long u32(byte[] bytes, int offset) {
|
||||
return u16(bytes, offset) | u16(bytes, offset + 2) << 16;
|
||||
}
|
||||
|
||||
private static boolean hasMagic(byte[] bytes, int offset) {
|
||||
for (int index = 0; index < MAGIC.length; index++) {
|
||||
if (bytes[offset + index] != MAGIC[index])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Fills buffer from the image at position; false if the image ends first. */
|
||||
private static boolean readFully(FileChannel image, ByteBuffer buffer, long position) throws IOException {
|
||||
while (buffer.hasRemaining()) {
|
||||
int count = image.read(buffer, position);
|
||||
|
||||
if (count <= 0)
|
||||
return false;
|
||||
position += count;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static byte[] readTable(FileChannel image, long partition, long sector, long size, String damaged)
|
||||
throws IOException {
|
||||
if (size == 0 || size > MAXIMUM_TABLE_SIZE)
|
||||
throw new ExtractException(damaged);
|
||||
ByteBuffer table = ByteBuffer.allocate((int) size);
|
||||
if (!readFully(image, table, partition + sector * SECTOR_SIZE))
|
||||
throw new ExtractException("Could not read the disc image (is it complete?).");
|
||||
return table.array();
|
||||
}
|
||||
|
||||
/* ---------- directory tables */
|
||||
|
||||
private static final class Walk {
|
||||
final byte[] table;
|
||||
final boolean directories;
|
||||
final BitSet visited = new BitSet();
|
||||
final List<Entry> entries = new ArrayList<>();
|
||||
int visits;
|
||||
boolean stopped;
|
||||
|
||||
Walk(byte[] table, boolean directories) {
|
||||
this.table = table;
|
||||
this.directories = directories;
|
||||
}
|
||||
}
|
||||
|
||||
/** The folders (or the files) of a table with plain names, in the tree's order. */
|
||||
private static List<Entry> walkTable(byte[] table, boolean directories) {
|
||||
Walk walk = new Walk(table, directories);
|
||||
|
||||
walkEntry(walk, 0, 0);
|
||||
return walk.entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* The subtree whose root entry is at offset: its left subtree, the
|
||||
* entry, its right subtree. A link to padding, past the table or back to
|
||||
* an entry already visited ends that branch.
|
||||
*/
|
||||
private static void walkEntry(Walk walk, int offset, int depth) {
|
||||
byte[] table = walk.table;
|
||||
|
||||
if (walk.stopped || depth > MAXIMUM_DEPTH)
|
||||
return;
|
||||
if (offset >= table.length || table.length - offset < ENTRY_FIXED_SIZE)
|
||||
return;
|
||||
int left = (int) u16(table, offset);
|
||||
int right = (int) u16(table, offset + 2);
|
||||
if (left == PADDING_LINK || walk.visited.get(offset / 4))
|
||||
return;
|
||||
walk.visited.set(offset / 4);
|
||||
if (walk.visits >= MAXIMUM_VISITS) {
|
||||
walk.stopped = true;
|
||||
return;
|
||||
}
|
||||
walk.visits++;
|
||||
|
||||
if (left != 0)
|
||||
walkEntry(walk, left * 4, depth + 1);
|
||||
int nameLength = table[offset + 13] & 0xFF;
|
||||
if (nameLength != 0 && nameLength <= table.length - offset - ENTRY_FIXED_SIZE)
|
||||
collect(walk, offset, nameLength);
|
||||
if (right != 0)
|
||||
walkEntry(walk, right * 4, depth + 1);
|
||||
}
|
||||
|
||||
private static void collect(Walk walk, int offset, int nameLength) {
|
||||
byte[] table = walk.table;
|
||||
boolean directory = (table[offset + 12] & ATTRIBUTE_DIRECTORY) != 0;
|
||||
|
||||
if (directory != walk.directories || walk.entries.size() >= MAXIMUM_ENTRIES)
|
||||
return;
|
||||
String name = new String(table, offset + ENTRY_FIXED_SIZE, nameLength, StandardCharsets.ISO_8859_1);
|
||||
if (!isPlainName(name))
|
||||
return;
|
||||
// (the destination's file system may ignore case)
|
||||
for (Entry entry : walk.entries) {
|
||||
if (entry.name.equalsIgnoreCase(name))
|
||||
return;
|
||||
}
|
||||
walk.entries.add(new Entry(name, u32(table, offset + 4), u32(table, offset + 8)));
|
||||
}
|
||||
|
||||
/* ---------- the copy */
|
||||
|
||||
private static String readFailure(Entry file) {
|
||||
return "Could not read " + file.name + " from the disc image (is it complete?).";
|
||||
}
|
||||
|
||||
/** Copies one file of the image to output; returns the bytes copied of all the files so far. */
|
||||
private static long copyFile(FileChannel image, long partition, Entry file, File output, ByteBuffer buffer,
|
||||
long done, long total, Progress progress) throws IOException {
|
||||
String writeFailure = "Could not write " + output.getPath() + " (is the storage full?).";
|
||||
long position = partition + file.sector * SECTOR_SIZE;
|
||||
long remaining = file.size;
|
||||
FileOutputStream stream = null;
|
||||
|
||||
try {
|
||||
stream = new FileOutputStream(output);
|
||||
FileChannel channel = stream.getChannel();
|
||||
while (remaining > 0) {
|
||||
int count = (int) Math.min(remaining, COPY_CHUNK);
|
||||
boolean read;
|
||||
|
||||
buffer.clear();
|
||||
buffer.limit(count);
|
||||
try {
|
||||
read = readFully(image, buffer, position);
|
||||
} catch (IOException exception) {
|
||||
read = false;
|
||||
}
|
||||
if (!read)
|
||||
throw new ExtractException(readFailure(file));
|
||||
buffer.flip();
|
||||
while (buffer.hasRemaining()) {
|
||||
if (channel.write(buffer) <= 0)
|
||||
throw new ExtractException(writeFailure);
|
||||
}
|
||||
position += count;
|
||||
remaining -= count;
|
||||
done += count;
|
||||
if (progress != null)
|
||||
progress.report(file.name, done, total);
|
||||
}
|
||||
FileOutputStream closing = stream;
|
||||
stream = null;
|
||||
closing.close();
|
||||
} catch (ExtractException exception) {
|
||||
throw exception;
|
||||
} catch (IOException exception) {
|
||||
throw new ExtractException(writeFailure);
|
||||
} finally {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException ignored) {
|
||||
// (the copy failed already)
|
||||
}
|
||||
}
|
||||
}
|
||||
return done;
|
||||
}
|
||||
}
|
||||
@@ -75,6 +75,10 @@ void host_sdl_scancode_name(int scancode, char *buffer, unsigned int size);
|
||||
int host_sdl_scancode_from_name(const char *name);
|
||||
int host_sdl_show_toast(const char *message, int duration, int gravity, int x, int y);
|
||||
int host_sdl_show_simple_message_box(unsigned int flags, const char *title, const char *message);
|
||||
/* SDL_ShowMessageBox of up to three buttons (SDL_MessageBoxData has not the
|
||||
guest's layout): the index of the one chosen, else -1 */
|
||||
int host_sdl_show_message_box(unsigned int flags, const char *title, const char *message, int default_index,
|
||||
const char *button0, const char *button1, const char *button2);
|
||||
int host_sdl_get_gamepads(unsigned int *ids, int capacity);
|
||||
unsigned int host_sdl_open_gamepad(unsigned int id);
|
||||
unsigned int host_sdl_gamepad_from_id(unsigned int id);
|
||||
|
||||
@@ -95,6 +95,31 @@ bool SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags flags, const char *title, cons
|
||||
return host_sdl_show_simple_message_box((unsigned int)flags, title, message) != 0;
|
||||
}
|
||||
|
||||
/* (up to three buttons, their ids those of the guest's) */
|
||||
bool SDL_ShowMessageBox(const SDL_MessageBoxData *data, int *buttonid)
|
||||
{
|
||||
const char *labels[3] = { NULL, NULL, NULL };
|
||||
int default_index = -1;
|
||||
int index;
|
||||
int chosen;
|
||||
|
||||
if (!data || data->numbuttons < 1 || data->numbuttons > 3)
|
||||
return false;
|
||||
for (index = 0; index < data->numbuttons; index++)
|
||||
{
|
||||
labels[index] = data->buttons[index].text;
|
||||
if (data->buttons[index].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT)
|
||||
default_index = index;
|
||||
}
|
||||
chosen = host_sdl_show_message_box((unsigned int)data->flags, data->title, data->message, default_index,
|
||||
labels[0], labels[1], labels[2]);
|
||||
if (chosen < 0 || chosen >= data->numbuttons)
|
||||
return false;
|
||||
if (buttonid)
|
||||
*buttonid = data->buttons[chosen].buttonID;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDL_Delay(Uint32 milliseconds)
|
||||
{
|
||||
struct timespec duration;
|
||||
|
||||
@@ -514,3 +514,35 @@ int host_sdl_show_simple_message_box(uint32_t flags, const char *title, const ch
|
||||
{
|
||||
return SDL_ShowSimpleMessageBox((SDL_MessageBoxFlags)flags, title, message, NULL) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* ---------- a question for the player (the mods at startup: port_mods.c) */
|
||||
|
||||
int host_sdl_show_message_box(uint32_t flags, const char *title, const char *message, int default_index,
|
||||
const char *button0, const char *button1, const char *button2)
|
||||
{
|
||||
const char *labels[3] = { button0, button1, button2 };
|
||||
SDL_MessageBoxButtonData buttons[3];
|
||||
SDL_MessageBoxData question;
|
||||
int count = 0;
|
||||
int answer = -1;
|
||||
int index;
|
||||
|
||||
for (index = 0; index < 3; index++)
|
||||
{
|
||||
if (!labels[index])
|
||||
continue;
|
||||
buttons[count].flags = index == default_index ?
|
||||
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT | SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT : 0;
|
||||
buttons[count].buttonID = index;
|
||||
buttons[count].text = labels[index];
|
||||
count++;
|
||||
}
|
||||
question.flags = (SDL_MessageBoxFlags)flags;
|
||||
question.window = NULL;
|
||||
question.title = title;
|
||||
question.message = message;
|
||||
question.numbuttons = count;
|
||||
question.buttons = buttons;
|
||||
question.colorScheme = NULL;
|
||||
return SDL_ShowMessageBox(&question, &answer) ? answer : -1;
|
||||
}
|
||||
@@ -32,6 +32,7 @@ host_sdl_set_clipboard_text
|
||||
host_sdl_get_clipboard_text
|
||||
host_sdl_show_toast
|
||||
host_sdl_show_simple_message_box
|
||||
host_sdl_show_message_box
|
||||
host_sdl_get_gamepads
|
||||
host_sdl_open_gamepad
|
||||
host_sdl_gamepad_from_id
|
||||
|
||||
@@ -237,6 +237,10 @@ cp "$BUNDLE/$LAUNCHER" "$GAME_DIR/$LAUNCHER.tmp" && chmod 755 "$GAME_DIR/$LAUNCH
|
||||
cp "$BUNDLE/$APK_NAME" "$GAME_DIR/$APK_NAME.tmp" && chmod 666 "$GAME_DIR/$APK_NAME.tmp" &&
|
||||
mv -f "$GAME_DIR/$APK_NAME.tmp" "$GAME_DIR/$APK_NAME" && touch "$GAME_DIR/$APK_NAME" ||
|
||||
fail "Could not write $GAME_DIR/$APK_NAME (is the storage full?)."
|
||||
# (the build's number: the launcher updates from it, steam_frame_launch.sh;
|
||||
# an update fetched for an older build is dropped)
|
||||
cp "$BUNDLE/build.txt" "$GAME_DIR/build.txt" 2>/dev/null || rm -f "$GAME_DIR/build.txt"
|
||||
rm -rf "$GAME_DIR/update"
|
||||
|
||||
# --- the game data
|
||||
|
||||
|
||||
@@ -70,6 +70,75 @@ run()
|
||||
child=0
|
||||
}
|
||||
|
||||
# Updates (update.auto in config.toml; the game's own updater, the Quest's,
|
||||
# does nothing here: Lepton installs the APK in this folder each start).
|
||||
# While the game runs, the latest release is looked for; a newer one's
|
||||
# bundle is fetched into update/, checked, and put in place as the next
|
||||
# start begins, before Lepton runs the APK (it installs the newer one).
|
||||
# A build that knows no number of its own (build.txt: a local one) never
|
||||
# looks. Nothing changes without the internet, or when anything fails.
|
||||
REPOSITORY="@REPOSITORY@"
|
||||
UPDATE="$DATA/update"
|
||||
BUILD_FILE="$DATA/build.txt"
|
||||
SELF="$DATA/ringfall-mjolnir-vr.sh"
|
||||
|
||||
update_apply()
|
||||
{
|
||||
local new="$UPDATE/bundle" build
|
||||
[ -f "$UPDATE/ready" ] || { rm -rf "$UPDATE"; return 0; }
|
||||
build=$(cat "$new/build.txt" 2>/dev/null)
|
||||
# (the launcher renamed over this one: bash goes on reading its own copy)
|
||||
if cp "$new/ringfall-mjolnir-vr.sh" "$SELF.tmp" && chmod 755 "$SELF.tmp" &&
|
||||
cp "$new/RingfallMjolnirVR.apk" "$APK.tmp" && chmod 666 "$APK.tmp" &&
|
||||
mv -f "$SELF.tmp" "$SELF" && mv -f "$APK.tmp" "$APK" && touch "$APK" &&
|
||||
cp "$new/build.txt" "$BUILD_FILE"
|
||||
then
|
||||
log "update: build $build put in place"
|
||||
else
|
||||
log "update: build $build could not be put in place"
|
||||
rm -f "$SELF.tmp" "$APK.tmp"
|
||||
fi
|
||||
rm -rf "$UPDATE"
|
||||
}
|
||||
|
||||
update_wanted()
|
||||
{
|
||||
# (update.auto = false in config.toml's [update]: never)
|
||||
! awk '/^\[/ { section = $0 } section == "[update]" && /^[ \t]*auto[ \t]*=/ { print $3 }' \
|
||||
"$DATA/config.toml" 2>/dev/null | grep -q false
|
||||
}
|
||||
|
||||
update_check()
|
||||
{
|
||||
local installed latest
|
||||
installed=$(cat "$BUILD_FILE" 2>/dev/null)
|
||||
[[ "$installed" =~ ^[0-9]+$ ]] && (( installed > 0 )) || return 0
|
||||
update_wanted || return 0
|
||||
latest=$(curl -fsSL --max-time 20 -H "Accept: application/vnd.github+json" \
|
||||
"https://api.github.com/repos/$REPOSITORY/releases/latest" |
|
||||
python3 -I -c 'import json, sys; print(json.load(sys.stdin)["tag_name"].rpartition("-")[2])' 2>/dev/null)
|
||||
[[ "$latest" =~ ^[0-9]+$ ]] && (( latest > installed )) || return 0
|
||||
log "update: build $latest found (this is $installed), fetching it"
|
||||
rm -rf "$UPDATE" && mkdir -p "$UPDATE" || return 0
|
||||
if curl -fsSL --retry 2 --max-time 900 -o "$UPDATE/bundle.zip" \
|
||||
"https://github.com/$REPOSITORY/releases/download/build-$latest/ringfall-mjolnir-vr-steam-frame.zip" &&
|
||||
python3 -I -m zipfile -e "$UPDATE/bundle.zip" "$UPDATE/bundle" &&
|
||||
[ "$(cat "$UPDATE/bundle/build.txt" 2>/dev/null)" = "$latest" ] &&
|
||||
[ -s "$UPDATE/bundle/RingfallMjolnirVR.apk" ] && [ -s "$UPDATE/bundle/ringfall-mjolnir-vr.sh" ]
|
||||
then
|
||||
rm -f "$UPDATE/bundle.zip"
|
||||
touch "$UPDATE/ready"
|
||||
log "update: build $latest fetched, put in place at the next start"
|
||||
else
|
||||
log "update: build $latest could not be fetched"
|
||||
rm -rf "$UPDATE"
|
||||
fi
|
||||
}
|
||||
|
||||
update_apply
|
||||
update_check </dev/null >/dev/null 2>&1 &
|
||||
updater=$!
|
||||
|
||||
# (a new APK is only installed on its first start, not started; and a start
|
||||
# right after a game that was ended hard can fail in SteamVR, which then
|
||||
# needs a moment: again, up to three times, while it ends within 30 s)
|
||||
@@ -83,4 +152,7 @@ for attempt in 1 2 3; do
|
||||
sleep 5
|
||||
done
|
||||
rm -f "$QUIT"
|
||||
# (a fetch still under way is given up: the next start looks again)
|
||||
pkill -P "$updater" 2>/dev/null
|
||||
kill "$updater" 2>/dev/null
|
||||
log "end"
|
||||
@@ -1,66 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Copies the maps folder out of an Xbox disc image (an "xiso", or a whole
|
||||
disc's ".iso") on the Steam Frame's SteamOS side, for its installer
|
||||
(steam_frame_install.sh). It reads the image as the app's own import does
|
||||
Copies the maps folder out of the player's Xbox disc image of Halo (an
|
||||
"xiso", or a whole disc's ".iso") on the Steam Frame's SteamOS side, for
|
||||
its installer (steam_frame_install.sh), as the app's own import does
|
||||
(app/src/main/java/com/halo/decomp/XisoExtractor.java, which this follows;
|
||||
the desktop games' is port/linux/src/xiso.c).
|
||||
|
||||
steam_frame_xiso.py <image> <destination>
|
||||
|
||||
writes <destination>/maps (through maps.partial, so an interrupted copy
|
||||
never leaves maps/ui.map behind) and prints its progress for zenity
|
||||
--progress: a percentage on a line of its own, and "# <text>" lines. On
|
||||
failure it prints "error: <reason the player can act on>" to stderr and
|
||||
exits 1.
|
||||
writes <destination>/maps (through maps.partial, ui.map moved in last, so
|
||||
an interrupted copy never leaves maps/ui.map behind) and prints its
|
||||
progress for zenity --progress: a percentage on a line of its own, and
|
||||
"# <text>" lines. On failure it prints "error: <reason the player can act
|
||||
on>" to stderr and exits 1.
|
||||
|
||||
The image's file system is XDVDFS, read as extract-xiso does
|
||||
(https://github.com/XboxDev/extract-xiso, extract-xiso.c, whose format
|
||||
handling this follows; its license is below and in
|
||||
port/third_party/extract-xiso/LICENSE.TXT): 2048-byte sectors; a volume
|
||||
descriptor at 0x10000 that starts and ends with "MICROSOFT*XBOX*MEDIA" and
|
||||
gives the root directory's sector and size; and directories whose entries
|
||||
form a binary tree (each entry: left and right subtree offsets in 4-byte
|
||||
units, the start sector, the size, attributes, the name's length and the
|
||||
name). Images made from a whole disc put the game partition further in, at
|
||||
one of the offsets below.
|
||||
Written from a description of the Xbox's disc file system, XDVDFS
|
||||
(xboxdevwiki's XDVDFS page, https://xboxdevwiki.net/XDVDFS), as
|
||||
XisoExtractor.java is:
|
||||
|
||||
Some parts of this code are copyright in@fishtank.com. This product
|
||||
includes software developed by in <in@fishtank.com>.
|
||||
- Sectors are 2048 bytes and integers little-endian. Sector numbers count
|
||||
from the start of the game partition: an image of the partition alone has
|
||||
it at 0, an image of a whole disc further in (PARTITION_STARTS, tried in
|
||||
that order).
|
||||
- The volume descriptor is the partition's sector 32 (at 0x10000). It holds
|
||||
"MICROSOFT*XBOX*MEDIA" at its start and again at 0x7EC, and the root
|
||||
directory table's start sector (u32 at 0x14) and size in bytes (u32 at
|
||||
0x18).
|
||||
- A directory table is a binary tree of entries whose root is the entry at
|
||||
offset 0. An entry is a left and a right subtree link (u16 each, in 4-byte
|
||||
units from the table's start, 0 for none), the start sector and the size
|
||||
of the file or of the subdirectory's table (u32 each), the attributes (u8,
|
||||
0x10 for a directory), the name's length (u8) and the name. Unused bytes
|
||||
are 0xFF, so a left link of 0xFFFF is padding, not an entry.
|
||||
- A file is one contiguous run of bytes from the start of its sector.
|
||||
|
||||
Copyright (c) 2003 in <in@fishtank.com>
|
||||
All rights reserved.
|
||||
The image is the player's file and nothing read from it is trusted: only
|
||||
the root's table and the maps folder's are read, each whole and at most
|
||||
4 MB; a table's walk visits each offset once, goes at most 64 links deep
|
||||
and looks at most 4096 entries; and a file is written only under a plain
|
||||
name that no file before it had (ignoring case).
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. All advertising materials mentioning features or use of this software
|
||||
must display the following acknowledgement:
|
||||
|
||||
This product includes software developed by in <in@fishtank.com>.
|
||||
|
||||
4. Neither the name of "in" nor the email address "in@fishtank.com"
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED `AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
A European disc in two languages (Spanish and Italian) has no maps folder
|
||||
but one for each, maps_es and maps_it: the one for the language asked for
|
||||
(RINGFALL_LANGUAGE, else the system's) is taken, else English's, else the
|
||||
first, and its language written to maps/language.txt, which the game takes
|
||||
as the Xbox's language while game.language is unset.
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -68,117 +52,170 @@ import struct
|
||||
import sys
|
||||
|
||||
SECTOR_SIZE = 2048
|
||||
VOLUME_DESCRIPTOR_OFFSET = 0x10000
|
||||
ENTRY_HEADER_SIZE = 14
|
||||
DESCRIPTOR_OFFSET = 0x10000
|
||||
MAGIC = b"MICROSOFT*XBOX*MEDIA"
|
||||
SECOND_MAGIC_OFFSET = 0x7EC
|
||||
ENTRY_FIXED_SIZE = 14
|
||||
ATTRIBUTE_DIRECTORY = 0x10
|
||||
PADDING_LINK = 0xFFFF
|
||||
# where the game partition may start: an image of the partition alone, a
|
||||
# whole XGD2, XGD3 and XGD1 (the original Xbox's) disc
|
||||
PARTITION_STARTS = (0x00000000, 0x0FD90000, 0x02080000, 0x18300000)
|
||||
# directory tables are a few sectors; anything much larger is not one
|
||||
MAXIMUM_DIRECTORY_SIZE = 4 << 20
|
||||
MAXIMUM_FILES = 256
|
||||
COPY_BUFFER_SIZE = 1 << 20
|
||||
VOLUME_MAGIC = b"MICROSOFT*XBOX*MEDIA"
|
||||
# where the game partition starts: a plain image, and whole-disc images
|
||||
# (extract-xiso's GLOBAL, XGD3 and XGD1 offsets)
|
||||
PARTITION_OFFSETS = (0, 0x0FD90000, 0x02080000, 0x18300000)
|
||||
MAXIMUM_TABLE_SIZE = 4 << 20
|
||||
MAXIMUM_DEPTH = 64
|
||||
MAXIMUM_VISITS = 4096
|
||||
MAXIMUM_ENTRIES = 256
|
||||
COPY_CHUNK = 1 << 20
|
||||
PARTIAL_DIRECTORY = "maps.partial"
|
||||
|
||||
|
||||
class ExtractError(Exception):
|
||||
"""a reason the player can act on"""
|
||||
|
||||
|
||||
def read_at(image, offset, size):
|
||||
image.seek(offset)
|
||||
def read_exactly(image, position, size):
|
||||
"""size bytes of the image from position; None if it ends first"""
|
||||
image.seek(position)
|
||||
data = image.read(size)
|
||||
if len(data) != size:
|
||||
raise ExtractError("Could not read the disc image (is it complete?).")
|
||||
return data
|
||||
return data if len(data) == size else None
|
||||
|
||||
|
||||
def find_volume(image):
|
||||
"""the game partition's offset, and the root directory's sector and size"""
|
||||
for offset in PARTITION_OFFSETS:
|
||||
try:
|
||||
descriptor = read_at(image, offset + VOLUME_DESCRIPTOR_OFFSET, SECTOR_SIZE)
|
||||
except ExtractError:
|
||||
def find_partition(image, image_size):
|
||||
"""the game partition's start, and the root table's sector and size"""
|
||||
for start in PARTITION_STARTS:
|
||||
if start + DESCRIPTOR_OFFSET + SECTOR_SIZE > image_size:
|
||||
continue
|
||||
if descriptor[:20] != VOLUME_MAGIC or descriptor[0x7EC:0x7EC + 20] != VOLUME_MAGIC:
|
||||
descriptor = read_exactly(image, start + DESCRIPTOR_OFFSET, SECTOR_SIZE)
|
||||
if (descriptor is None or descriptor[:len(MAGIC)] != MAGIC or
|
||||
descriptor[SECOND_MAGIC_OFFSET:SECOND_MAGIC_OFFSET + len(MAGIC)] != MAGIC):
|
||||
continue
|
||||
sector, size = struct.unpack_from("<II", descriptor, 20)
|
||||
return offset, sector, size
|
||||
sector, size = struct.unpack_from("<II", descriptor, 0x14)
|
||||
return start, sector, size
|
||||
raise ExtractError("This is not an Xbox disc image.")
|
||||
|
||||
|
||||
def read_directory(image, partition, sector, size, what):
|
||||
if size <= 0 or size > MAXIMUM_DIRECTORY_SIZE:
|
||||
raise ExtractError(what)
|
||||
return read_at(image, partition + sector * SECTOR_SIZE, size)
|
||||
def read_table(image, partition, sector, size, damaged):
|
||||
if size == 0 or size > MAXIMUM_TABLE_SIZE:
|
||||
raise ExtractError(damaged)
|
||||
table = read_exactly(image, partition + sector * SECTOR_SIZE, size)
|
||||
if table is None:
|
||||
raise ExtractError("Could not read the disc image (is it complete?).")
|
||||
return table
|
||||
|
||||
|
||||
def walk(table, offset, depth, directories, out, visited):
|
||||
"""collects the entries (name, sector, size) of the subtree at offset
|
||||
(4-byte units), files or directories as asked"""
|
||||
at = offset * 4
|
||||
# a malformed table must not loop or run off its end
|
||||
visited[0] += 1
|
||||
if depth > 64 or visited[0] > 4096 or at + ENTRY_HEADER_SIZE > len(table):
|
||||
return
|
||||
left, right, sector, size, attributes, name_length = struct.unpack_from("<HHIIBB", table, at)
|
||||
# 0xFFFF: padding, an empty directory
|
||||
if left == 0xFFFF:
|
||||
return
|
||||
if left:
|
||||
walk(table, left, depth + 1, directories, out, visited)
|
||||
is_directory = bool(attributes & ATTRIBUTE_DIRECTORY)
|
||||
if (at + ENTRY_HEADER_SIZE + name_length <= len(table) and name_length > 0
|
||||
and is_directory == directories and len(out) < MAXIMUM_FILES):
|
||||
name = table[at + ENTRY_HEADER_SIZE:at + ENTRY_HEADER_SIZE + name_length].decode("latin-1")
|
||||
# (as extract-xiso refuses them: no name may leave the folder)
|
||||
if name not in (".", "..") and "/" not in name and "\\" not in name:
|
||||
out.append((name, sector, size))
|
||||
if right:
|
||||
walk(table, right, depth + 1, directories, out, visited)
|
||||
def is_plain_name(name):
|
||||
"""no path, no . or .., and only printable ASCII that every file system
|
||||
takes as a plain name"""
|
||||
if not name or name in (".", ".."):
|
||||
return False
|
||||
return all(0x20 <= ord(character) <= 0x7E and character not in '/\\:*?"<>|' for character in name)
|
||||
|
||||
|
||||
def walk_table(table, directories):
|
||||
"""the folders (or the files) of a table with plain names, as (name,
|
||||
sector, size), in the tree's order: each entry's left subtree, the entry,
|
||||
its right subtree; a link to padding, past the table or back to an entry
|
||||
already visited ends that branch"""
|
||||
entries, visited = [], set()
|
||||
visits = [0]
|
||||
|
||||
def walk(offset, depth):
|
||||
if depth > MAXIMUM_DEPTH or visits[0] >= MAXIMUM_VISITS:
|
||||
return
|
||||
if offset >= len(table) or len(table) - offset < ENTRY_FIXED_SIZE:
|
||||
return
|
||||
left, right = struct.unpack_from("<HH", table, offset)
|
||||
if left == PADDING_LINK or offset // 4 in visited:
|
||||
return
|
||||
visited.add(offset // 4)
|
||||
visits[0] += 1
|
||||
if left:
|
||||
walk(left * 4, depth + 1)
|
||||
name_length = table[offset + 13]
|
||||
if name_length and name_length <= len(table) - offset - ENTRY_FIXED_SIZE:
|
||||
is_directory = bool(table[offset + 12] & ATTRIBUTE_DIRECTORY)
|
||||
name = table[offset + ENTRY_FIXED_SIZE:offset + ENTRY_FIXED_SIZE + name_length].decode("latin-1")
|
||||
# (the destination's file system may ignore case)
|
||||
if (is_directory == directories and len(entries) < MAXIMUM_ENTRIES and is_plain_name(name) and
|
||||
all(entry[0].lower() != name.lower() for entry in entries)):
|
||||
sector, size = struct.unpack_from("<II", table, offset + 4)
|
||||
entries.append((name, sector, size))
|
||||
if right:
|
||||
walk(right * 4, depth + 1)
|
||||
|
||||
walk(0, 0)
|
||||
return entries
|
||||
|
||||
|
||||
def maps_language(name):
|
||||
"""a language's maps folder's language ("maps_it": "it"), else None"""
|
||||
if len(name) == 7 and name[:5].lower() == "maps_" and name[5:].isalpha():
|
||||
return name[5:].lower()
|
||||
return None
|
||||
|
||||
|
||||
def choose_maps(folders):
|
||||
"""the maps folder, and its language (None for a plain "maps")"""
|
||||
for folder in folders:
|
||||
if folder[0].lower() == "maps":
|
||||
return folder, None
|
||||
languages = [(maps_language(folder[0]), folder) for folder in folders if maps_language(folder[0])]
|
||||
wanted = (os.environ.get("RINGFALL_LANGUAGE") or os.environ.get("LC_ALL") or
|
||||
os.environ.get("LC_MESSAGES") or os.environ.get("LANG") or "")[:2].lower()
|
||||
for preferred in (wanted, "en"):
|
||||
for language, folder in languages:
|
||||
if language == preferred:
|
||||
return folder, language
|
||||
if languages:
|
||||
return languages[0][1], languages[0][0]
|
||||
raise ExtractError("The disc image has no maps folder: it is not a Halo disc.")
|
||||
|
||||
|
||||
def extract(image_path, destination):
|
||||
with open(image_path, "rb") as image:
|
||||
partition, root_sector, root_size = find_volume(image)
|
||||
image_size = os.fstat(image.fileno()).st_size
|
||||
partition, root_sector, root_size = find_partition(image, image_size)
|
||||
|
||||
# the root's maps folder
|
||||
table = read_directory(image, partition, root_sector, root_size,
|
||||
"The disc image's file system is damaged.")
|
||||
directories = []
|
||||
walk(table, 0, 0, True, directories, [0])
|
||||
maps = [entry for entry in directories if entry[0].lower() == "maps"]
|
||||
if not maps:
|
||||
raise ExtractError("The disc image has no maps folder: it is not a Halo disc.")
|
||||
root = read_table(image, partition, root_sector, root_size, "The disc image's file system is damaged.")
|
||||
(_, maps_sector, maps_size), language = choose_maps(walk_table(root, True))
|
||||
|
||||
# its files
|
||||
table = read_directory(image, partition, maps[-1][1], maps[-1][2],
|
||||
"The disc image's maps folder is damaged.")
|
||||
files = []
|
||||
walk(table, 0, 0, False, files, [0])
|
||||
if not any(name.lower() == "ui.map" for name, _, _ in files):
|
||||
table = read_table(image, partition, maps_sector, maps_size, "The disc image's maps folder is damaged.")
|
||||
files = walk_table(table, False)
|
||||
user_interface = next((file for file in files if file[0].lower() == "ui.map"), None)
|
||||
if user_interface is None:
|
||||
raise ExtractError("The disc image's maps folder has no ui.map: it is not a Halo disc.")
|
||||
# (an image that ends before a file does is refused before anything
|
||||
# is written; an empty file's sector need not be anywhere)
|
||||
for name, sector, size in files:
|
||||
if size and partition + sector * SECTOR_SIZE + size > image_size:
|
||||
raise ExtractError("Could not read %s from the disc image (is it complete?)." % name)
|
||||
total = sum(size for _, _, size in files) or 1
|
||||
|
||||
partial = os.path.join(destination, "maps.partial")
|
||||
finished = os.path.join(destination, "maps")
|
||||
# the copy, to maps.partial (emptied of what an earlier attempt left)
|
||||
partial = os.path.join(destination, PARTIAL_DIRECTORY)
|
||||
try:
|
||||
os.makedirs(partial, exist_ok=True)
|
||||
for leftover in os.listdir(partial):
|
||||
os.remove(os.path.join(partial, leftover))
|
||||
except OSError:
|
||||
raise ExtractError("Could not create %s." % partial)
|
||||
done = 0
|
||||
shown = -1
|
||||
done, shown = 0, -1
|
||||
for name, sector, size in files:
|
||||
path = os.path.join(partial, name)
|
||||
offset = partition + sector * SECTOR_SIZE
|
||||
remaining = size
|
||||
position, remaining = partition + sector * SECTOR_SIZE, size
|
||||
print("# Copying maps/%s" % name, flush=True)
|
||||
try:
|
||||
with open(path, "wb") as out:
|
||||
while remaining > 0:
|
||||
count = min(remaining, COPY_BUFFER_SIZE)
|
||||
out.write(read_at(image, offset, count))
|
||||
offset += count
|
||||
count = min(remaining, COPY_CHUNK)
|
||||
data = read_exactly(image, position, count)
|
||||
if data is None:
|
||||
raise ExtractError("Could not read %s from the disc image (is it complete?)." % name)
|
||||
out.write(data)
|
||||
position += count
|
||||
remaining -= count
|
||||
done += count
|
||||
# (zenity closes at 100: that is left to the caller)
|
||||
@@ -186,15 +223,22 @@ def extract(image_path, destination):
|
||||
if percent != shown:
|
||||
print(percent, flush=True)
|
||||
shown = percent
|
||||
except ExtractError:
|
||||
raise ExtractError("Could not read %s from the disc image (is it complete?)." % name)
|
||||
except OSError:
|
||||
raise ExtractError("Could not write %s (is the storage full?)." % path)
|
||||
|
||||
# (the maps folder may be there, empty)
|
||||
# then into maps (which may be there, empty), the language before and
|
||||
# ui.map (the sign that the data is there) last
|
||||
finished = os.path.join(destination, "maps")
|
||||
try:
|
||||
os.makedirs(finished, exist_ok=True)
|
||||
for name, _, _ in files:
|
||||
language_file = os.path.join(finished, "language.txt")
|
||||
if os.path.exists(language_file):
|
||||
os.remove(language_file)
|
||||
if language:
|
||||
with open(language_file, "w") as out:
|
||||
out.write(language + "\n")
|
||||
order = [file for file in files if file is not user_interface] + [user_interface]
|
||||
for name, _, _ in order:
|
||||
os.replace(os.path.join(partial, name), os.path.join(finished, name))
|
||||
os.rmdir(partial)
|
||||
except OSError as error:
|
||||
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
@@ -207,6 +207,320 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "black_butn__0",
|
||||
"tag": "ui\\shell\\bitmaps\\black_butn",
|
||||
"bitmap": 0,
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"format": "dxt3",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"svg": "black_butn.svg",
|
||||
"place": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "white_butn__0",
|
||||
"tag": "ui\\shell\\bitmaps\\white_butn",
|
||||
"bitmap": 0,
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"format": "dxt3",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"svg": "white_butn.svg",
|
||||
"place": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "classic_a__0",
|
||||
"tag": "ui\\hud\\buttons\\classic_a",
|
||||
"bitmap": 0,
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"format": "dxt3",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"svg": "a_butn.svg",
|
||||
"place": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"classic": "#40eb40",
|
||||
"letter": [
|
||||
0.1685,
|
||||
0.1119
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "classic_b__0",
|
||||
"tag": "ui\\hud\\buttons\\classic_b",
|
||||
"bitmap": 0,
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"format": "dxt3",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"svg": "b_butn.svg",
|
||||
"place": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"classic": "#fa5946",
|
||||
"letter": [
|
||||
0.696,
|
||||
0.1204
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "classic_x__0",
|
||||
"tag": "ui\\hud\\buttons\\classic_x",
|
||||
"bitmap": 0,
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"format": "dxt3",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"svg": "x_butn.svg",
|
||||
"place": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"classic": "#3998fa",
|
||||
"letter": [
|
||||
0.3363,
|
||||
2.4589
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "classic_y__0",
|
||||
"tag": "ui\\hud\\buttons\\classic_y",
|
||||
"bitmap": 0,
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"format": "dxt3",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"svg": "y_butn.svg",
|
||||
"place": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"classic": "#f9e928",
|
||||
"letter": [
|
||||
-0.4376,
|
||||
2.2898
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "classic_black__0",
|
||||
"tag": "ui\\hud\\buttons\\classic_black",
|
||||
"bitmap": 0,
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"format": "dxt3",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"svg": "black_butn.svg",
|
||||
"place": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"classic": "#000000"
|
||||
},
|
||||
{
|
||||
"name": "classic_white__0",
|
||||
"tag": "ui\\hud\\buttons\\classic_white",
|
||||
"bitmap": 0,
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"format": "dxt3",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"svg": "white_butn.svg",
|
||||
"place": [
|
||||
1,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"classic": "#ffffff"
|
||||
},
|
||||
{
|
||||
"name": "gem_a__0",
|
||||
"tag": "ui\\hud\\buttons\\gem_a",
|
||||
"bitmap": 0,
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"format": "dxt3",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"source_png": "source/a_gem.png"
|
||||
},
|
||||
{
|
||||
"name": "gem_b__0",
|
||||
"tag": "ui\\hud\\buttons\\gem_b",
|
||||
"bitmap": 0,
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"format": "dxt3",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"source_png": "source/b_gem.png"
|
||||
},
|
||||
{
|
||||
"name": "gem_x__0",
|
||||
"tag": "ui\\hud\\buttons\\gem_x",
|
||||
"bitmap": 0,
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"format": "dxt3",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"source_png": "source/x_gem.png"
|
||||
},
|
||||
{
|
||||
"name": "gem_y__0",
|
||||
"tag": "ui\\hud\\buttons\\gem_y",
|
||||
"bitmap": 0,
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"format": "dxt3",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"source_png": "source/y_gem.png"
|
||||
},
|
||||
{
|
||||
"name": "trigger_left__0",
|
||||
"tag": "ui\\hud\\buttons\\trigger_left",
|
||||
"bitmap": 0,
|
||||
"width": 64,
|
||||
"height": 64,
|
||||
"format": "a8r8g8b8",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"source_png": "source/trigger_left.png",
|
||||
"control_cell": [
|
||||
50,
|
||||
35
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "trigger_right__0",
|
||||
"tag": "ui\\hud\\buttons\\trigger_right",
|
||||
"bitmap": 0,
|
||||
"width": 64,
|
||||
"height": 64,
|
||||
"format": "a8r8g8b8",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"source_png": "source/trigger_right.png",
|
||||
"control_cell": [
|
||||
50,
|
||||
35
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "thumbstick_left__0",
|
||||
"tag": "ui\\hud\\buttons\\thumbstick_left",
|
||||
"bitmap": 0,
|
||||
"width": 64,
|
||||
"height": 64,
|
||||
"format": "a8r8g8b8",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"source_png": "source/thumbstick_left.png",
|
||||
"control_cell": [
|
||||
36,
|
||||
40
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "thumbstick_right__0",
|
||||
"tag": "ui\\hud\\buttons\\thumbstick_right",
|
||||
"bitmap": 0,
|
||||
"width": 64,
|
||||
"height": 64,
|
||||
"format": "a8r8g8b8",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"source_png": "source/thumbstick_right.png",
|
||||
"control_cell": [
|
||||
36,
|
||||
40
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "thumbstick-halfway_left__0",
|
||||
"tag": "ui\\hud\\buttons\\thumbstick-halfway_left",
|
||||
"bitmap": 0,
|
||||
"width": 64,
|
||||
"height": 64,
|
||||
"format": "a8r8g8b8",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"source_png": "source/thumbstick-halfway_left.png",
|
||||
"control_cell": [
|
||||
36,
|
||||
40
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "thumbstick-halfway_right__0",
|
||||
"tag": "ui\\hud\\buttons\\thumbstick-halfway_right",
|
||||
"bitmap": 0,
|
||||
"width": 64,
|
||||
"height": 64,
|
||||
"format": "a8r8g8b8",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"source_png": "source/thumbstick-halfway_right.png",
|
||||
"control_cell": [
|
||||
36,
|
||||
40
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "thumbstick-full_left__0",
|
||||
"tag": "ui\\hud\\buttons\\thumbstick-full_left",
|
||||
"bitmap": 0,
|
||||
"width": 64,
|
||||
"height": 64,
|
||||
"format": "a8r8g8b8",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"source_png": "source/thumbstick-full_left.png",
|
||||
"control_cell": [
|
||||
36,
|
||||
40
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "thumbstick-full_right__0",
|
||||
"tag": "ui\\hud\\buttons\\thumbstick-full_right",
|
||||
"bitmap": 0,
|
||||
"width": 64,
|
||||
"height": 64,
|
||||
"format": "a8r8g8b8",
|
||||
"scale": 8,
|
||||
"crc": 0,
|
||||
"source_png": "source/thumbstick-full_right.png",
|
||||
"control_cell": [
|
||||
36,
|
||||
40
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 420 KiB |
|
After Width: | Height: | Size: 393 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 523 KiB |
|
After Width: | Height: | Size: 522 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
|
||||
<!-- Original Xbox black button, derived from the OpenCE button redraw. -->
|
||||
<defs>
|
||||
|
||||
<linearGradient id="body" gradientUnits="userSpaceOnUse" x1="0" y1="-12.932" x2="0" y2="12.932">
|
||||
<stop offset="0" stop-color="#050505"/>
|
||||
<stop offset="0.5" stop-color="#202020"/>
|
||||
<stop offset="1" stop-color="#454545"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="rim" gradientUnits="userSpaceOnUse" x1="0" y1="-12.932" x2="0" y2="12.932">
|
||||
<stop offset="0" stop-color="#ffffff" stop-opacity="0.74"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity="0.73"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g transform="translate(10.341 12.879) rotate(-26.459)">
|
||||
<ellipse id="oval" rx="8.659" ry="12.932" fill="url(#body)"/>
|
||||
<ellipse rx="8.082" ry="12.355" fill="none" stroke="url(#rim)" stroke-width="1.154"/>
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 866 B |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
|
||||
<!-- Original Xbox white button, derived from the OpenCE button redraw. -->
|
||||
<defs>
|
||||
|
||||
<linearGradient id="body" gradientUnits="userSpaceOnUse" x1="0" y1="-12.932" x2="0" y2="12.932">
|
||||
<stop offset="0" stop-color="#999999"/>
|
||||
<stop offset="0.5" stop-color="#dddddd"/>
|
||||
<stop offset="1" stop-color="#ffffff"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="rim" gradientUnits="userSpaceOnUse" x1="0" y1="-12.932" x2="0" y2="12.932">
|
||||
<stop offset="0" stop-color="#ffffff" stop-opacity="0.74"/>
|
||||
<stop offset="1" stop-color="#ffffff" stop-opacity="0.73"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g transform="translate(10.341 12.879) rotate(-26.459)">
|
||||
<ellipse id="oval" rx="8.659" ry="12.932" fill="url(#body)"/>
|
||||
<ellipse rx="8.082" ry="12.355" fill="none" stroke="url(#rim)" stroke-width="1.154"/>
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 866 B |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 369 KiB After Width: | Height: | Size: 330 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 170 KiB |
@@ -302,7 +302,70 @@
|
||||
108
|
||||
],
|
||||
"kind": "meter",
|
||||
"score": 0.683
|
||||
"score": 0.683,
|
||||
"thresholds": [
|
||||
4,
|
||||
8,
|
||||
12,
|
||||
16,
|
||||
20,
|
||||
24,
|
||||
28,
|
||||
32,
|
||||
36,
|
||||
40,
|
||||
44,
|
||||
48,
|
||||
52,
|
||||
56,
|
||||
60,
|
||||
64,
|
||||
68,
|
||||
72,
|
||||
76,
|
||||
80,
|
||||
84,
|
||||
88,
|
||||
92,
|
||||
96,
|
||||
100,
|
||||
104,
|
||||
108,
|
||||
112,
|
||||
116,
|
||||
120,
|
||||
124,
|
||||
128,
|
||||
132,
|
||||
136,
|
||||
140,
|
||||
144,
|
||||
148,
|
||||
152,
|
||||
156,
|
||||
160,
|
||||
164,
|
||||
168,
|
||||
172,
|
||||
176,
|
||||
180,
|
||||
184,
|
||||
188,
|
||||
192,
|
||||
196,
|
||||
200,
|
||||
204,
|
||||
208,
|
||||
212,
|
||||
216,
|
||||
220,
|
||||
224,
|
||||
228,
|
||||
232,
|
||||
236,
|
||||
240
|
||||
],
|
||||
"quantize_thresholds": true
|
||||
},
|
||||
{
|
||||
"xbox": [
|
||||
@@ -324,7 +387,22 @@
|
||||
180
|
||||
],
|
||||
"kind": "meter",
|
||||
"score": 0.676
|
||||
"score": 0.676,
|
||||
"thresholds": [
|
||||
10,
|
||||
30,
|
||||
50,
|
||||
70,
|
||||
90,
|
||||
110,
|
||||
130,
|
||||
150,
|
||||
170,
|
||||
190,
|
||||
210,
|
||||
230
|
||||
],
|
||||
"quantize_thresholds": true
|
||||
},
|
||||
{
|
||||
"xbox": [
|
||||
@@ -346,7 +424,22 @@
|
||||
248
|
||||
],
|
||||
"kind": "meter",
|
||||
"score": 0.747
|
||||
"score": 0.747,
|
||||
"thresholds": [
|
||||
10,
|
||||
30,
|
||||
50,
|
||||
70,
|
||||
90,
|
||||
110,
|
||||
130,
|
||||
150,
|
||||
170,
|
||||
190,
|
||||
210,
|
||||
230
|
||||
],
|
||||
"quantize_thresholds": true
|
||||
},
|
||||
{
|
||||
"xbox": [
|
||||
@@ -434,7 +527,30 @@
|
||||
248
|
||||
],
|
||||
"kind": "meter",
|
||||
"score": 0.721
|
||||
"score": 0.721,
|
||||
"thresholds": [
|
||||
12,
|
||||
24,
|
||||
36,
|
||||
48,
|
||||
60,
|
||||
72,
|
||||
84,
|
||||
96,
|
||||
108,
|
||||
120,
|
||||
132,
|
||||
144,
|
||||
156,
|
||||
168,
|
||||
180,
|
||||
192,
|
||||
204,
|
||||
216,
|
||||
228,
|
||||
240
|
||||
],
|
||||
"quantize_thresholds": true
|
||||
},
|
||||
{
|
||||
"xbox": [
|
||||
@@ -456,7 +572,14 @@
|
||||
88
|
||||
],
|
||||
"kind": "meter",
|
||||
"score": 0.852
|
||||
"score": 0.852,
|
||||
"thresholds": [
|
||||
60,
|
||||
120,
|
||||
180,
|
||||
240
|
||||
],
|
||||
"quantize_thresholds": true
|
||||
},
|
||||
{
|
||||
"xbox": [
|
||||
@@ -478,7 +601,12 @@
|
||||
180
|
||||
],
|
||||
"kind": "meter",
|
||||
"score": 0.82
|
||||
"score": 0.82,
|
||||
"thresholds": [
|
||||
120,
|
||||
240
|
||||
],
|
||||
"quantize_thresholds": true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -3237,7 +3365,7 @@
|
||||
50,
|
||||
18
|
||||
],
|
||||
"svg": "hud2_left_reticles__reticle_18.svg",
|
||||
"svg": "sniper_diamond.svg",
|
||||
"source_scale": 1.365,
|
||||
"source": [
|
||||
-3.5831,
|
||||
@@ -3355,6 +3483,72 @@
|
||||
"score": 0.996
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "vehicle_wrench_outline__0",
|
||||
"tag": "ui\\hud\\redraws\\vehicle_wrench_outline",
|
||||
"bitmap": 0,
|
||||
"width": 128,
|
||||
"height": 32,
|
||||
"format": "ay8",
|
||||
"scale": 4,
|
||||
"crc": 0,
|
||||
"cells": [
|
||||
{
|
||||
"xbox": [
|
||||
0,
|
||||
0,
|
||||
128,
|
||||
32
|
||||
],
|
||||
"svg": "vehicle_wrench_outline.svg",
|
||||
"source_scale": 1,
|
||||
"source": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"clip": [
|
||||
0,
|
||||
0,
|
||||
128,
|
||||
32
|
||||
],
|
||||
"kind": "alpha"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "passenger_health_outline__0",
|
||||
"tag": "ui\\hud\\redraws\\passenger_health_outline",
|
||||
"bitmap": 0,
|
||||
"width": 128,
|
||||
"height": 32,
|
||||
"format": "ay8",
|
||||
"scale": 4,
|
||||
"crc": 0,
|
||||
"cells": [
|
||||
{
|
||||
"xbox": [
|
||||
0,
|
||||
0,
|
||||
128,
|
||||
32
|
||||
],
|
||||
"svg": "passenger_health_outline.svg",
|
||||
"source_scale": 1,
|
||||
"source": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"clip": [
|
||||
0,
|
||||
0,
|
||||
128,
|
||||
32
|
||||
],
|
||||
"kind": "alpha"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@@ -28,12 +28,12 @@
|
||||
<stop offset="0" stop-color="#ffffff"/><stop offset="1" stop-color="#000000"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="ab0" gradientUnits="userSpaceOnUse" x1="0" y1="322.996" x2="0" y2="339.002"><stop offset="0" stop-color="#b9b9b9"/><stop offset="1" stop-color="#b3b3b3"/></linearGradient>
|
||||
<mask id="mbx0" maskUnits="userSpaceOnUse" x="0" y="0" width="1024" height="512"><path d="M48.13,323 L416.49,323 L423.87,339 L55.51,339 Z" fill="url(#ab0)"/></mask>
|
||||
<mask id="mbx0" maskUnits="userSpaceOnUse" x="0" y="0" width="1024" height="512"><path d="M53.5,327 L408.5,327 L412.2,335 L57.2,335 Z" fill="url(#ab0)"/></mask>
|
||||
<linearGradient id="cb1" gradientUnits="userSpaceOnUse" x1="-88.616" y1="40.907" x2="242.068" y2="-111.744">
|
||||
<stop offset="0" stop-color="#ffffff"/><stop offset="1" stop-color="#000000"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="ab1" gradientUnits="userSpaceOnUse" x1="0" y1="363.996" x2="0" y2="392.003"><stop offset="0" stop-color="#b9b9b9"/><stop offset="1" stop-color="#aeaeae"/></linearGradient>
|
||||
<mask id="mbx1" maskUnits="userSpaceOnUse" x="0" y="0" width="1024" height="512"><path d="M63.33,364 L459.69,364 L472.66,392 L76.29,392 Z" fill="url(#ab1)"/></mask>
|
||||
<mask id="mbx1" maskUnits="userSpaceOnUse" x="0" y="0" width="1024" height="512"><path d="M68,368 L454,368 L463.3,388 L77.3,388 Z" fill="url(#ab1)"/></mask>
|
||||
</defs>
|
||||
<g transform="translate(0,0)" mask="url(#m1)"><rect x="564.89" y="6" width="393.11" height="60.03" fill="url(#c1)"/></g>
|
||||
<g transform="translate(0,-1)" mask="url(#m4)"><rect x="49.21" y="143" width="435.41" height="112.02" fill="url(#c4)"/></g>
|
||||
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="32" viewBox="0 0 128 32"><!-- Existing meter contour, aligned by the Xbox HUD tag offsets. --><path transform="translate(-.25,-55) scale(.25)" d="M327.21,274.16 L339.43,274.16 L334.08,285.74 L345.32,285.74 L340.46,296.25 L329.23,296.25 L323.88,307.82 L311.66,307.82 L317.01,296.25 L305.77,296.25 L310.63,285.74 L321.86,285.74 Z" fill="none" stroke="#fff" stroke-width="3.34"/></svg>
|
||||
|
After Width: | Height: | Size: 443 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path d="M8 2.7 C9.5 2.7 10.1 3.6 11.7 5.2 C13.8 7.3 13.8 8.7 11.7 10.8 C10.1 12.4 9.5 13.3 8 13.3 C6.5 13.3 5.9 12.4 4.3 10.8 C2.2 8.7 2.2 7.3 4.3 5.2 C5.9 3.6 6.5 2.7 8 2.7 Z" fill="none" stroke="#fff" stroke-width="1.3"/><circle cx="8" cy="8" r=".65" fill="#fff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 359 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="32" viewBox="0 0 128 32"><!-- Existing meter contour, aligned by the Xbox HUD tag offsets. --><path transform="translate(-1.25,0) scale(.25)" d="M328.14,53.64 C327.98,53.7 327.27,53.81 326.57,53.89 C324.82,54.06 324.04,54.24 323.36,54.57 C323.04,54.74 322.43,54.92 322,54.99 C321.54,55.06 321,55.26 320.64,55.47 C320.32,55.69 319.77,55.91 319.41,56 C319.06,56.07 318.59,56.29 318.35,56.46 C318.12,56.62 317.66,56.91 317.31,57.07 C316.4,57.52 315.11,58.26 314.55,58.66 C313.98,59.07 313.38,59.49 312.69,59.95 C311.82,60.54 309.75,62.41 308.05,64.14 C305.23,67.03 304.94,66.88 313.35,66.88 C320.88,66.88 320.89,66.88 321.69,67.78 C322.69,68.88 322.07,70.7 320.11,72.45 C318.82,73.6 318.44,73.88 317.94,74 C317.66,74.08 317.21,74.21 316.94,74.31 C316.51,74.47 315.23,74.5 309.1,74.5 L301.76,74.5 L301.41,74.8 L301.06,75.1 L301.06,80.78 C301.06,86 301.07,86.47 301.29,86.76 C301.4,86.92 301.65,87.29 301.84,87.56 C302.36,88.36 303.52,89.61 303.99,89.91 C304.23,90.05 304.6,90.33 304.8,90.51 C305.01,90.69 305.41,90.91 305.69,91 C305.96,91.09 306.44,91.31 306.75,91.51 C307.14,91.75 307.61,91.91 308.25,92 C308.76,92.08 309.52,92.22 309.95,92.33 C310.85,92.56 315.31,92.53 315.9,92.28 C316.09,92.2 316.8,92.08 317.5,92.01 C318.32,91.92 319.09,91.75 319.73,91.51 C320.25,91.31 321.02,91.05 321.44,90.94 C321.85,90.83 322.32,90.62 322.5,90.51 C322.68,90.39 323.15,90.17 323.56,90.04 C323.98,89.9 324.59,89.61 324.93,89.39 C325.27,89.17 325.61,89 325.7,89 C325.77,89 326.12,88.81 326.46,88.58 C326.79,88.35 327.19,88.12 327.35,88.08 C327.5,88.03 327.85,87.79 328.14,87.55 C328.43,87.31 328.86,87.03 329.1,86.94 C329.34,86.84 329.71,86.6 329.94,86.41 C330.16,86.21 330.7,85.78 331.12,85.44 C331.55,85.09 332.27,84.5 332.73,84.11 C333.19,83.72 333.69,83.35 333.84,83.28 C334.29,83.03 356.52,83.06 357,83.31 C357.52,83.59 357.85,84.08 357.94,84.72 C358.05,85.56 358.25,86.14 358.56,86.56 C358.73,86.76 358.96,87.16 359.1,87.44 C359.59,88.41 362.02,90.66 362.93,90.96 C363.2,91.06 363.73,91.31 364.07,91.51 C364.48,91.74 365.02,91.91 365.59,92 C366.06,92.06 366.82,92.2 367.26,92.31 C368.39,92.58 372.21,92.58 373.09,92.31 C373.45,92.2 374.31,92.06 375.01,92 C375.98,91.91 376.49,91.79 377.11,91.5 C377.56,91.3 378.21,91.08 378.56,91 C378.9,90.92 379.46,90.7 379.81,90.5 C380.15,90.31 380.68,90.09 380.95,90.01 C381.24,89.94 381.69,89.72 381.95,89.54 C382.23,89.36 382.73,89.1 383.06,88.97 C383.41,88.84 383.8,88.62 383.94,88.5 C384.07,88.38 384.41,88.16 384.69,88.03 C385.29,87.72 386.39,87.03 387.06,86.5 C387.34,86.29 387.93,85.84 388.38,85.5 C390.16,84.12 390.45,83.89 391.68,82.69 C392.38,82 393.1,81.24 393.27,81 C393.45,80.76 393.9,80.21 394.26,79.79 C395.91,77.88 396.15,77.44 395.73,77.16 C395.64,77.11 392.55,77.09 388.88,77.1 C380.81,77.15 381.38,77.22 379.48,75.75 C378.15,74.72 380.57,71.14 383.59,69.69 L384.75,69.12 L392.12,69.12 C400.81,69.12 399.93,69.31 400.06,67.42 C400.29,64.56 400,61.07 399.49,60.36 C399.36,60.19 399.16,59.79 399.04,59.49 C398.38,57.86 396.06,55.59 394.45,54.96 C394.1,54.84 393.69,54.64 393.51,54.51 C393.05,54.19 392.45,54.04 391.35,53.94 C390.81,53.9 390.15,53.79 389.86,53.69 C389.12,53.42 385.86,53.42 385.11,53.69 C384.81,53.8 383.94,53.94 383.15,54 C382.01,54.09 381.56,54.19 380.9,54.49 C380.44,54.69 379.69,54.94 379.23,55.04 C378.76,55.15 378.16,55.39 377.88,55.56 C377.6,55.74 377.19,55.91 376.96,55.96 C376.5,56.05 375.94,56.31 375.51,56.64 C375.35,56.77 375,56.94 374.74,57 C374.48,57.07 374.1,57.27 373.9,57.44 C373.7,57.61 373.26,57.86 372.94,58 C372.6,58.14 372.15,58.42 371.91,58.62 C371.69,58.84 371.44,59 371.38,59 C371.3,59 371.04,59.2 370.76,59.44 C370.5,59.67 370.01,60 369.68,60.15 L369.06,60.44 L355.8,60.47 L342.54,60.5 L342.31,60.24 C342.18,60.1 341.96,59.8 341.81,59.56 C341.15,58.47 340.79,58 340.07,57.26 C339.18,56.34 337.55,55 337.31,55 C337.23,55 336.74,54.8 336.24,54.56 C335.55,54.24 334.98,54.09 334,53.95 C333.27,53.85 332.43,53.7 332.12,53.64 C331.43,53.47 328.54,53.47 328.14,53.64 Z M327.19,57.01 C326.71,57.07 326.04,57.21 325.69,57.3 C323.85,57.82 322.59,58.25 322.23,58.47 C322,58.61 321.48,58.82 321.06,58.94 C320.65,59.06 320,59.34 319.62,59.56 C319.25,59.79 318.51,60.21 318,60.5 C315.26,62.07 314.46,62.82 314.99,63.3 C315.18,63.48 315.61,63.5 317.89,63.5 C321.77,63.5 322.84,63.8 324.21,65.22 C325.81,66.88 325.89,70.24 324.4,72.69 C323.9,73.5 321.79,75.65 321.19,75.96 C320.99,76.08 320.61,76.31 320.38,76.5 C320.14,76.69 319.71,76.91 319.44,77 C319.16,77.09 318.6,77.3 318.19,77.49 L317.44,77.81 L310.88,77.84 C304.71,77.86 304.3,77.88 304,78.1 C302.6,79.12 303.02,83.61 304.74,85.89 C305.91,87.45 307.93,88.69 309.79,89.01 C311.21,89.25 316.19,89.09 317.31,88.78 C319.38,88.19 320.96,87.66 321.25,87.46 C321.43,87.35 321.81,87.17 322.12,87.06 C322.43,86.95 322.94,86.71 323.25,86.51 C323.56,86.33 324.01,86.09 324.25,86 C324.49,85.9 324.85,85.69 325.05,85.53 C325.26,85.36 325.71,85.1 326.07,84.94 C326.43,84.79 326.76,84.59 326.81,84.51 C326.85,84.42 327.11,84Line truncated
|
||||
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
@@ -1,11 +1,17 @@
|
||||
# Lucide icons
|
||||
|
||||
Two icons of [Lucide](https://lucide.dev) 1.53.0 (ISC licence: `LICENSE`),
|
||||
Four icons of [Lucide](https://lucide.dev) 1.53.0 (ISC licence: `LICENSE`),
|
||||
as `icons/` has them at the `1.53.0` tag of
|
||||
https://github.com/lucide-icons/lucide:
|
||||
|
||||
- `volume-2.svg`: voice chat's speaker, of a player who talks;
|
||||
- `volume-x.svg`: ... of a player muted.
|
||||
- `volume-x.svg`: ... of a player muted;
|
||||
- `triangle-alert.svg`: the Mods menu's sign of a mod that loads on the
|
||||
wrong side of a mod it asks to load before or after;
|
||||
- `circle-x.svg`: ... of a mod that cannot be played, or that another mod
|
||||
says it cannot be played with.
|
||||
|
||||
`tools/ce_menus.py` draws them, white, as the menus' bitmap `voice/speaker`
|
||||
(port/linux/game/network_voice.c tints it). Credited in `AUTHORS.md`.
|
||||
`tools/ce_menus.py` draws the first two, white, as the menus' bitmap
|
||||
`voice/speaker` (port/linux/game/network_voice.c tints it), and the others,
|
||||
amber and red, as `mods/status` and the smaller `mods/status_small`
|
||||
(`tools/port_settings.py`). Credited in `AUTHORS.md`.
|
||||
@@ -0,0 +1,15 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="m15 9-6 6" />
|
||||
<path d="m9 9 6 6" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 293 B |
@@ -0,0 +1,15 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" />
|
||||
<path d="M12 9v4" />
|
||||
<path d="M12 17h.01" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 345 B |
@@ -62,7 +62,7 @@ until it is redrawn.
|
||||
| `ce/shell/main_menu/settings_select/player_setup/player_profile_edit/profile_options__6.png` | 512x256 | `shell/main_menu/settings_select/player_setup/player_profile_edit/profile_options__6.png` |
|
||||
| `ce/shell/main_menu/settings_select/player_setup/player_profile_edit/profile_options__8.png` | 512x256 | `shell/main_menu/settings_select/player_setup/player_profile_edit/profile_options__8.png` |
|
||||
|
||||
13 of 164 frames are placeholders.
|
||||
13 of 177 frames are placeholders.
|
||||
|
||||
The PC version's `ui\gamespy` and `ui\ticker` fonts are drawn with `ui\small_ui`, which
|
||||
the Xbox's map has.
|
||||
@@ -10,7 +10,7 @@ do (save a setting, join a game) does not happen; the screens open and close as
|
||||
|
||||
| Function | Uses | |
|
||||
| --- | --- | --- |
|
||||
| `common button bar update` | 27 | PC function |
|
||||
| `common button bar update` | 28 | PC function |
|
||||
| `direct ip connect init` | 1 | PC function |
|
||||
| `direct ip connect update` | 1 | PC function |
|
||||
| `direct ip edit field` | 4 | PC function |
|
||||
@@ -21,7 +21,7 @@ do (save a setting, join a game) does not happen; the screens open and close as
|
||||
| `gamespy update filter settings` | 2 | PC function |
|
||||
| `gt edit list update` | 1 | PC function |
|
||||
| `gt select list update` | 1 | PC function |
|
||||
| `mouse spinner 1wide click` | 82 | PC function |
|
||||
| `mouse spinner 1wide click` | 84 | PC function |
|
||||
| `mp map list update` | 1 | PC function |
|
||||
| `mp prof init teamplay options` | 1 | PC function |
|
||||
| `mp prof init vehicle options` | 1 | PC function |
|
||||
|
||||
@@ -314,6 +314,9 @@
|
||||
<bitmap name="main_menu/solo_level_select/header_load_level">
|
||||
<frame png="ce/shell/main_menu/solo_level_select/header_load_level__0.png" width="512" height="64"/>
|
||||
</bitmap>
|
||||
<bitmap name="main_menu/mods/header_mods">
|
||||
<frame png="ce/port/main_menu/mods/header_mods.png" width="512" height="64"/>
|
||||
</bitmap>
|
||||
<bitmap name="main_menu/multiplayer_type_select/coop/header_player_2">
|
||||
<frame png="ce/port/main_menu/multiplayer_type_select/coop/header_player_2.png" width="512" height="64"/>
|
||||
</bitmap>
|
||||
@@ -356,10 +359,33 @@
|
||||
<frame png="ce/port/pause/pausebox_right__1.png" width="16" height="256"/>
|
||||
<frame png="ce/port/pause/pausebox_right__2.png" width="16" height="256"/>
|
||||
</bitmap>
|
||||
<bitmap name="pause/objectives_left">
|
||||
<frame png="ce/port/pause/objectives_left__0.png" width="16" height="256"/>
|
||||
</bitmap>
|
||||
<bitmap name="pause/objectives_center">
|
||||
<frame png="ce/port/pause/objectives_center__0.png" width="4" height="256"/>
|
||||
</bitmap>
|
||||
<bitmap name="pause/objectives_right">
|
||||
<frame png="ce/port/pause/objectives_right__0.png" width="16" height="256"/>
|
||||
</bitmap>
|
||||
<bitmap name="voice/speaker">
|
||||
<frame png="ce/port/voice/speaker__0.png" width="24" height="24"/>
|
||||
<frame png="ce/port/voice/speaker__1.png" width="24" height="24"/>
|
||||
</bitmap>
|
||||
<bitmap name="main_menu/menu_mods">
|
||||
<frame png="ce/port/main_menu/menu_mods__0.png" width="256" height="64"/>
|
||||
<frame png="ce/port/main_menu/menu_mods__1.png" width="256" height="64"/>
|
||||
</bitmap>
|
||||
<bitmap name="mods/status">
|
||||
<frame png="ce/port/mods/status__0.png" width="24" height="24"/>
|
||||
<frame png="ce/port/mods/status__1.png" width="24" height="24"/>
|
||||
<frame png="ce/port/mods/status__2.png" width="24" height="24"/>
|
||||
</bitmap>
|
||||
<bitmap name="mods/status_small">
|
||||
<frame png="ce/port/mods/status_small__0.png" width="16" height="16"/>
|
||||
<frame png="ce/port/mods/status_small__1.png" width="16" height="16"/>
|
||||
<frame png="ce/port/mods/status_small__2.png" width="16" height="16"/>
|
||||
</bitmap>
|
||||
<bitmap name="blank">
|
||||
<frame png="ce/port/blank__0.png" width="4" height="4"/>
|
||||
</bitmap>
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- The port's Mods screen, in the PC version's style (tools/port_settings.py) -->
|
||||
<menus>
|
||||
<widget name="main_menu/main_menu_item_mods" type="text" width="256" height="33" bitmap="main_menu/menu_mods">
|
||||
<on event="a" open="main_menu/mods/mods_screen"/>
|
||||
<on event="start" open="main_menu/mods/mods_screen"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/mods_screen" width="640" height="480" flags="pass_unhandled_to_focused_child pause_game" bitmap="bitmaps/gradient">
|
||||
<on event="b" back="true"/>
|
||||
<on event="back" back="true"/>
|
||||
<child widget="main_menu/new_select/sel_list_desc_bkd"/>
|
||||
<child widget="main_menu/mods/mods_list"/>
|
||||
<child widget="main_menu/mods/header_mods"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/header_mods" controller="1" left="35" top="11" width="363" height="59" bitmap="main_menu/mods/header_mods">
|
||||
</widget>
|
||||
<widget name="main_menu/mods/mods_list" type="column_list" width="640" height="480" flags="pass_unhandled_to_focused_child up_down_tabs_children" description="main_menu/mods/mod_details">
|
||||
<data input="port mods update"/>
|
||||
<on event="created" run="port mods init"/>
|
||||
<on event="deleted" run="port mods close"/>
|
||||
<child widget="main_menu/mods/op_mod_1" x="20" y="73"/>
|
||||
<child widget="main_menu/mods/op_mod_2" x="20" y="103"/>
|
||||
<child widget="main_menu/mods/op_mod_3" x="20" y="133"/>
|
||||
<child widget="main_menu/mods/op_mod_4" x="20" y="163"/>
|
||||
<child widget="main_menu/mods/op_mod_5" x="20" y="193"/>
|
||||
<child widget="main_menu/mods/op_mod_6" x="20" y="223"/>
|
||||
<child widget="main_menu/mods/op_mod_7" x="20" y="253"/>
|
||||
<child widget="main_menu/mods/op_mod_8" x="20" y="283"/>
|
||||
<child widget="main_menu/mods/op_mod_9" x="20" y="313"/>
|
||||
<child widget="main_menu/mods/op_mod_10" x="20" y="343"/>
|
||||
<child widget="main_menu/mods/op_mod_11" x="20" y="373"/>
|
||||
<child widget="main_menu/mods/button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/op_mod_1" width="390" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/sel_list_item_bkd_top" color="#FF2896FF">
|
||||
<on event="a" run="port mods toggle"/>
|
||||
<on event="start" run="port mods toggle"/>
|
||||
<on event="left" run="port mods move up"/>
|
||||
<on event="right" run="port mods move down"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/mods/mod_status" x="8" y="2"/>
|
||||
<child widget="main_menu/mods/mod_name" x="34"/>
|
||||
<child widget="main_menu/mods/mod_state" x="318"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/op_mod_2" width="390" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/sel_list_item_bkd" color="#FF2896FF">
|
||||
<on event="a" run="port mods toggle"/>
|
||||
<on event="start" run="port mods toggle"/>
|
||||
<on event="left" run="port mods move up"/>
|
||||
<on event="right" run="port mods move down"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/mods/mod_status" x="8" y="2"/>
|
||||
<child widget="main_menu/mods/mod_name" x="34"/>
|
||||
<child widget="main_menu/mods/mod_state" x="318"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/op_mod_3" width="390" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/sel_list_item_bkd" color="#FF2896FF">
|
||||
<on event="a" run="port mods toggle"/>
|
||||
<on event="start" run="port mods toggle"/>
|
||||
<on event="left" run="port mods move up"/>
|
||||
<on event="right" run="port mods move down"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/mods/mod_status" x="8" y="2"/>
|
||||
<child widget="main_menu/mods/mod_name" x="34"/>
|
||||
<child widget="main_menu/mods/mod_state" x="318"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/op_mod_4" width="390" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/sel_list_item_bkd" color="#FF2896FF">
|
||||
<on event="a" run="port mods toggle"/>
|
||||
<on event="start" run="port mods toggle"/>
|
||||
<on event="left" run="port mods move up"/>
|
||||
<on event="right" run="port mods move down"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/mods/mod_status" x="8" y="2"/>
|
||||
<child widget="main_menu/mods/mod_name" x="34"/>
|
||||
<child widget="main_menu/mods/mod_state" x="318"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/op_mod_5" width="390" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/sel_list_item_bkd" color="#FF2896FF">
|
||||
<on event="a" run="port mods toggle"/>
|
||||
<on event="start" run="port mods toggle"/>
|
||||
<on event="left" run="port mods move up"/>
|
||||
<on event="right" run="port mods move down"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/mods/mod_status" x="8" y="2"/>
|
||||
<child widget="main_menu/mods/mod_name" x="34"/>
|
||||
<child widget="main_menu/mods/mod_state" x="318"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/op_mod_6" width="390" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/sel_list_item_bkd" color="#FF2896FF">
|
||||
<on event="a" run="port mods toggle"/>
|
||||
<on event="start" run="port mods toggle"/>
|
||||
<on event="left" run="port mods move up"/>
|
||||
<on event="right" run="port mods move down"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/mods/mod_status" x="8" y="2"/>
|
||||
<child widget="main_menu/mods/mod_name" x="34"/>
|
||||
<child widget="main_menu/mods/mod_state" x="318"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/op_mod_7" width="390" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/sel_list_item_bkd" color="#FF2896FF">
|
||||
<on event="a" run="port mods toggle"/>
|
||||
<on event="start" run="port mods toggle"/>
|
||||
<on event="left" run="port mods move up"/>
|
||||
<on event="right" run="port mods move down"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/mods/mod_status" x="8" y="2"/>
|
||||
<child widget="main_menu/mods/mod_name" x="34"/>
|
||||
<child widget="main_menu/mods/mod_state" x="318"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/op_mod_8" width="390" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/sel_list_item_bkd" color="#FF2896FF">
|
||||
<on event="a" run="port mods toggle"/>
|
||||
<on event="start" run="port mods toggle"/>
|
||||
<on event="left" run="port mods move up"/>
|
||||
<on event="right" run="port mods move down"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/mods/mod_status" x="8" y="2"/>
|
||||
<child widget="main_menu/mods/mod_name" x="34"/>
|
||||
<child widget="main_menu/mods/mod_state" x="318"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/op_mod_9" width="390" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/sel_list_item_bkd" color="#FF2896FF">
|
||||
<on event="a" run="port mods toggle"/>
|
||||
<on event="start" run="port mods toggle"/>
|
||||
<on event="left" run="port mods move up"/>
|
||||
<on event="right" run="port mods move down"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/mods/mod_status" x="8" y="2"/>
|
||||
<child widget="main_menu/mods/mod_name" x="34"/>
|
||||
<child widget="main_menu/mods/mod_state" x="318"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/op_mod_10" width="390" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/sel_list_item_bkd" color="#FF2896FF">
|
||||
<on event="a" run="port mods toggle"/>
|
||||
<on event="start" run="port mods toggle"/>
|
||||
<on event="left" run="port mods move up"/>
|
||||
<on event="right" run="port mods move down"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/mods/mod_status" x="8" y="2"/>
|
||||
<child widget="main_menu/mods/mod_name" x="34"/>
|
||||
<child widget="main_menu/mods/mod_state" x="318"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/op_mod_11" width="390" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/sel_list_item_bkd" color="#FF2896FF">
|
||||
<on event="a" run="port mods toggle"/>
|
||||
<on event="start" run="port mods toggle"/>
|
||||
<on event="left" run="port mods move up"/>
|
||||
<on event="right" run="port mods move down"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/mods/mod_status" x="8" y="2"/>
|
||||
<child widget="main_menu/mods/mod_name" x="34"/>
|
||||
<child widget="main_menu/mods/mod_state" x="318"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/mod_status" controller="1" width="24" height="24" bitmap="mods/status">
|
||||
</widget>
|
||||
<widget name="main_menu/mods/mod_name" type="text" controller="1" width="284" height="22" font="ui\large_ui" color="#FF2896FF" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/mods/mod_state" type="text" controller="1" width="66" height="22" font="ui\large_ui" color="#FF2896FF" align="center" text_y="4" text_flags="no_focus_test">
|
||||
</widget>
|
||||
<widget name="main_menu/mods/mod_details" width="640" height="480">
|
||||
<child widget="main_menu/mods/mod_details_name"/>
|
||||
<child widget="main_menu/mods/mod_details_status"/>
|
||||
<child widget="main_menu/mods/mod_details_text"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/mod_details_name" type="text" controller="1" left="412" top="84" width="200" height="44" font="ui\large_ui" color="#FFFFFFFF">
|
||||
</widget>
|
||||
<widget name="main_menu/mods/mod_details_status" controller="1" left="411" top="132" width="16" height="16" bitmap="mods/status_small">
|
||||
</widget>
|
||||
<widget name="main_menu/mods/mod_details_text" type="text" controller="1" left="412" top="130" width="200" height="272" font="ui\small_ui" color="#FF2896FF">
|
||||
</widget>
|
||||
<widget name="main_menu/mods/button_bar" type="column_list" width="640" height="28" flags="pass_unhandled_to_focused_child left_right_tabs_items">
|
||||
<child widget="main_menu/mods/button_up" y="1"/>
|
||||
<child widget="main_menu/mods/button_down" x="130" y="1"/>
|
||||
<child widget="main_menu/mods/button_ok" x="380" y="1"/>
|
||||
<child widget="common_button_cancel" x="510" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/button_up" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" text="MOVE UP" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port mods move up"/>
|
||||
<on event="start" run="port mods move up"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/button_down" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" text="MOVE DOWN" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port mods move down"/>
|
||||
<on event="start" run="port mods move down"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
<widget name="main_menu/mods/button_ok" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="1" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port mods save" back="true"/>
|
||||
<on event="start" run="port mods save" back="true"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
</menus>
|
||||
@@ -219,7 +219,7 @@
|
||||
<string text="Anyone can see and join your game: it is listed\nin everyone's Server Browser."/>
|
||||
<string text="Only players with your invite link can join."/>
|
||||
<string text="Friendly fire, the enemies and player collisions,\nfor this game."/>
|
||||
<string text="Voice chat and votes to kick a player, for this\ngame."/>
|
||||
<string text="Voice chat, text chat and votes to kick a player,\nfor this game."/>
|
||||
<string text="Players from the Server Browser must type it to\njoin (an invite link needs none). Enter sets it."/>
|
||||
<string text="Sending players who join without the game's\nCustom Edition map the map, and how fast."/>
|
||||
</strings>
|
||||
@@ -369,6 +369,7 @@
|
||||
<child widget="main_menu/multiplayer_type_select/server_settings/voice_options/op_voice_kbps" x="54" y="133"/>
|
||||
<child widget="main_menu/multiplayer_type_select/server_settings/voice_options/op_voice_proximity" x="54" y="163"/>
|
||||
<child widget="main_menu/multiplayer_type_select/server_settings/voice_options/op_votekick" x="54" y="193"/>
|
||||
<child widget="main_menu/multiplayer_type_select/server_settings/voice_options/op_text_chat" x="54" y="223"/>
|
||||
<child widget="main_menu/multiplayer_type_select/server_settings/voice_options/voice_options_button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/server_settings/voice_options/voice_options_button_bar" type="column_list" width="640" height="28" flags="pass_unhandled_to_focused_child left_right_tabs_items">
|
||||
@@ -458,12 +459,27 @@
|
||||
<string text="ON"/>
|
||||
<string text="OFF"/>
|
||||
</strings>
|
||||
<widget name="main_menu/multiplayer_type_select/server_settings/voice_options/op_text_chat" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/multiplayer_type_select/server_settings/voice_options/text_chat_label"/>
|
||||
<child widget="main_menu/multiplayer_type_select/server_settings/voice_options/text_chat_spinner" x="286" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/server_settings/voice_options/text_chat_label" type="text" controller="1" width="300" height="22" string_list="main_menu/multiplayer_type_select/server_settings/voice_options/voice_options_labels" string_index="5" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/server_settings/voice_options/text_chat_spinner" type="spinner" top="2" width="206" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" string_list="main_menu/multiplayer_type_select/server_settings/voice_options/var_text_chat" font="ui\large_ui" color="#FF2896FF" align="center" text_y="4" list_flags="items_from_strings" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -13 19 -7" footer_bounds="7 208 19 214">
|
||||
<on event="left_mouse" run="mouse spinner 1wide click"/>
|
||||
</widget>
|
||||
<strings name="main_menu/multiplayer_type_select/server_settings/voice_options/var_text_chat">
|
||||
<string text="ON"/>
|
||||
<string text="WITH LINKS"/>
|
||||
<string text="OFF"/>
|
||||
</strings>
|
||||
<strings name="main_menu/multiplayer_type_select/server_settings/voice_options/voice_options_labels">
|
||||
<string text="VOICE CHAT:"/>
|
||||
<string text="LOBBY VOICE CHAT:"/>
|
||||
<string text="VOICE QUALITY:"/>
|
||||
<string text="VOICE NEAR DISTANCE:"/>
|
||||
<string text="VOTE KICK:"/>
|
||||
<string text="TEXT CHAT:"/>
|
||||
</strings>
|
||||
<strings name="main_menu/multiplayer_type_select/server_settings/voice_options/cap_voice_options">
|
||||
<string text="No voice chat during the game."/>
|
||||
@@ -488,6 +504,9 @@
|
||||
<string text="Players 150 metres apart or less are near\nfor voice chat."/>
|
||||
<string text="Players can vote to kick a player, from the\nscoreboard."/>
|
||||
<string text="Players cannot vote to kick anyone."/>
|
||||
<string text="Players can type to each other; lines with links\nare refused. Off in a public internet game."/>
|
||||
<string text="Players can type to each other, links too. Off\nin a public internet game."/>
|
||||
<string text="No text chat in the game."/>
|
||||
</strings>
|
||||
<widget name="main_menu/multiplayer_type_select/server_settings/map_share_options/map_share_options_screen" width="640" height="480" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/gradient">
|
||||
<child widget="main_menu/multiplayer_type_select/server_settings/map_share_options/header_map_share_options"/>
|
||||
@@ -543,6 +562,8 @@
|
||||
<string text="2 MB/S"/>
|
||||
<string text="4 MB/S"/>
|
||||
<string text="8 MB/S"/>
|
||||
<string text="16 MB/S"/>
|
||||
<string text="20 MB/S"/>
|
||||
</strings>
|
||||
<strings name="main_menu/multiplayer_type_select/server_settings/map_share_options/map_share_options_labels">
|
||||
<string text="SHARE MAPS:"/>
|
||||
@@ -557,5 +578,7 @@
|
||||
<string text="The most you send of maps, so that the game\nkeeps the rest of your upload."/>
|
||||
<string text="The most you send of maps, so that the game\nkeeps the rest of your upload."/>
|
||||
<string text="The most you send of maps, so that the game\nkeeps the rest of your upload."/>
|
||||
<string text="The most you send of maps, so that the game\nkeeps the rest of your upload."/>
|
||||
<string text="The most you send of maps, so that the game\nkeeps the rest of your upload."/>
|
||||
</strings>
|
||||
</menus>
|
||||
@@ -60,13 +60,13 @@
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_create_internet_item" y="243"/>
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_create_lan_item" y="276"/>
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_coop_item" y="309"/>
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_gametypes_item" y="375"/>
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_gametypes_item" y="358"/>
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/multiplayer_type_select/multiplayer_type_select_screen" width="640" height="480" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/gradient">
|
||||
<on event="created" run="mp type menu initialize"/>
|
||||
<child widget="main_menu/multiplayer_type_select/header_multiplayer"/>
|
||||
<child widget="main_menu/multiplayer_type_select/multiplayer_type_select_list"/>
|
||||
<child widget="main_menu/blueline" x="64" y="358"/>
|
||||
<child widget="main_menu/blueline" x="64" y="349"/>
|
||||
</widget>
|
||||
</menus>
|
||||
@@ -78,4 +78,18 @@
|
||||
<string text="ON"/>
|
||||
<string text="OFF"/>
|
||||
</strings>
|
||||
<widget name="main_menu/settings_select/multiplayer_setup/teamplay_options_edit/op_text_chat" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/multiplayer_setup/teamplay_options_edit/text_chat_label"/>
|
||||
<child widget="main_menu/settings_select/multiplayer_setup/teamplay_options_edit/text_chat_spinner" x="286" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/multiplayer_setup/teamplay_options_edit/text_chat_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/multiplayer_setup/teamplay_options_edit/teamplay_options_labels" string_index="8" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/multiplayer_setup/teamplay_options_edit/text_chat_spinner" type="spinner" top="2" width="206" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" string_list="main_menu/settings_select/multiplayer_setup/teamplay_options_edit/var_text_chat" font="ui\large_ui" color="#FF2896FF" align="center" text_y="4" list_flags="items_from_strings" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -13 19 -7" footer_bounds="7 208 19 214">
|
||||
<on event="left_mouse" run="mouse spinner 1wide click"/>
|
||||
</widget>
|
||||
<strings name="main_menu/settings_select/multiplayer_setup/teamplay_options_edit/var_text_chat">
|
||||
<string text="ON"/>
|
||||
<string text="WITH LINKS"/>
|
||||
<string text="OFF"/>
|
||||
</strings>
|
||||
</menus>
|
||||
@@ -53,6 +53,7 @@
|
||||
<child widget="main_menu/settings_select/multiplayer_setup/teamplay_options_edit/op_voice_kbps" x="54" y="223"/>
|
||||
<child widget="main_menu/settings_select/multiplayer_setup/teamplay_options_edit/op_voice_proximity" x="54" y="253"/>
|
||||
<child widget="main_menu/settings_select/multiplayer_setup/teamplay_options_edit/op_votekick" x="54" y="283"/>
|
||||
<child widget="main_menu/settings_select/multiplayer_setup/teamplay_options_edit/op_text_chat" x="54" y="313"/>
|
||||
<child widget="main_menu/settings_select/multiplayer_setup/teamplay_options_edit/teamplay_button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/multiplayer_setup/teamplay_options_edit/teamplay_options_screen" width="640" height="480" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/gradient">
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- The port's settings screen, in the PC version's style (tools/port_settings.py) -->
|
||||
<menus>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/sound_processing_settings_screen" width="640" height="480" flags="pass_unhandled_to_focused_child pause_game" bitmap="bitmaps/gradient">
|
||||
<on event="b" back="true"/>
|
||||
<on event="back" back="true"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/header_profile_audio_settings"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/options_menu"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/header_profile_audio_settings" controller="1" left="35" top="11" width="605" height="59" bitmap="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/header_profile_audio_settings">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/help" type="text" controller="1" left="68" top="350" width="482" height="60" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/help_strings" font="ui\large_ui" color="#FFFFFFFF">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/options_menu" type="column_list" width="640" height="480" flags="pass_unhandled_to_focused_child up_down_tabs_children" description="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/help">
|
||||
<data input="port settings help"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/op_reverb" x="54" y="73"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/op_resampler" x="54" y="103"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/button_bar" type="column_list" width="640" height="28" flags="pass_unhandled_to_focused_child left_right_tabs_items">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/button_defaults" y="1"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/button_ok" x="380" y="1"/>
|
||||
<child widget="common_button_cancel" x="510" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/op_reverb" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/reverb_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/reverb_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/reverb_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/labels" string_index="0" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/reverb_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ON|OFF" setting="audio.reverb" values="true|false" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/op_resampler" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/resampler_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/resampler_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/resampler_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/labels" string_index="1" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/resampler_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="SINC|LINEAR" setting="audio.resampler" values="sinc|linear" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/button_defaults" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="3" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port settings defaults"/>
|
||||
<on event="start" run="port settings defaults"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/button_ok" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="1" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port settings save" back="true"/>
|
||||
<on event="start" run="port settings save" back="true"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/labels">
|
||||
<string text="REVERB:"/>
|
||||
<string text="RESAMPLER:"/>
|
||||
</strings>
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/help_strings">
|
||||
<string text=""/>
|
||||
<string text="Echo sounds as the place you are in does, and\nmuffle those behind walls, as the Xbox did."/>
|
||||
<string text="How sounds are converted to the output's rate:\nsinc is clean; linear is brighter, with aliasing."/>
|
||||
</strings>
|
||||
</menus>
|
||||
@@ -21,14 +21,14 @@
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_music_volume" x="54" y="103" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_effects_volume" x="54" y="193" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_effects_volume" x="54" y="133" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_reverb" x="54" y="223" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_reverb" x="54" y="163" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_enabled" x="54" y="253" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_enabled" x="54" y="193" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_voice_chat" x="54" y="283" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_voice_chat" x="54" y="223" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_voice_volume" x="54" y="313" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_voice_volume" x="54" y="253" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_enabled" x="54" y="223" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_enabled" x="54" y="163" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_voice_chat" x="54" y="253" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_voice_chat" x="54" y="193" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_voice_volume" x="54" y="283" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_voice_volume" x="54" y="223" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_sound_processing" x="54" y="313" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_sound_processing" x="54" y="253" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/button_bar" type="column_list" width="640" height="28" flags="pass_unhandled_to_focused_child left_right_tabs_items">
|
||||
@@ -81,20 +81,11 @@
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/effects_volume_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="0|1|2|3|4|5|6|7|8|9|10" setting="audio.effects_volume" values="0|0.1|0.2|0.3|0.4|0.5|0.6|0.7|0.8|0.9|1" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_reverb" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/reverb_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/reverb_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/reverb_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/labels" string_index="5" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/reverb_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ON|OFF" setting="audio.reverb" values="true|false" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_enabled" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/enabled_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/enabled_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/enabled_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/labels" string_index="6" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/enabled_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/labels" string_index="5" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/enabled_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ON|OFF" setting="audio.enabled" values="true|false" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
@@ -103,7 +94,7 @@
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_chat_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_chat_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_chat_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/labels" string_index="7" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_chat_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/labels" string_index="6" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_chat_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="PUSH TO TALK|OPEN MIC|OFF" setting="audio.voice_chat" values="push_to_talk|open_mic|off" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
@@ -112,11 +103,19 @@
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_volume_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_volume_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_volume_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/labels" string_index="8" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_volume_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/labels" string_index="7" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_volume_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="0|1|2|3|4|5|6|7|8|9|10" setting="audio.voice_volume" values="0|0.1|0.2|0.3|0.4|0.5|0.6|0.7|0.8|0.9|1" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_sound_processing" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<on event="a" open="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/sound_processing_settings_screen"/>
|
||||
<on event="start" open="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/sound_processing_settings_screen"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing_label"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing_label" type="text" controller="1" width="512" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/labels" string_index="8" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/button_defaults" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="3" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port settings defaults"/>
|
||||
<on event="start" run="port settings defaults"/>
|
||||
@@ -133,10 +132,10 @@
|
||||
<string text="MASTER VOLUME:"/>
|
||||
<string text="MUSIC VOLUME:"/>
|
||||
<string text="EFFECTS VOLUME:"/>
|
||||
<string text="REVERB:"/>
|
||||
<string text="SOUND:"/>
|
||||
<string text="VOICE CHAT:"/>
|
||||
<string text="VOICE VOLUME:"/>
|
||||
<string text="SOUND PROCESSING:"/>
|
||||
</strings>
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/help_strings">
|
||||
<string text=""/>
|
||||
@@ -145,9 +144,9 @@
|
||||
<string text="The volume of everything."/>
|
||||
<string text="The music's volume."/>
|
||||
<string text="The volume of every other sound: effects and\nspeech."/>
|
||||
<string text="Echo sounds as the place you are in does, and\nmuffle those behind walls, as the Xbox did."/>
|
||||
<string text="Play sound at all; from the next time the game\nstarts."/>
|
||||
<string text="Talk in network games: while PUSH TO TALK is held\n(Controls), whenever you speak, or never."/>
|
||||
<string text="The other players' voices."/>
|
||||
<string text="The reverb, and how sounds are converted to the\noutput's rate."/>
|
||||
</strings>
|
||||
</menus>
|
||||
@@ -35,7 +35,7 @@
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/controls_setup/group_label" type="text" controller="1" width="200" height="22" text="SHOW:" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/controls_setup/group_spinner" type="spinner" left="3" top="2" width="297" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="MOVEMENT|WEAPONS|ACTIONS" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 300 19 306">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/controls_setup/group_spinner" type="spinner" left="3" top="2" width="297" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="MOVEMENT|WEAPONS|ACTIONS|COMMUNICATION" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 300 19 306">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/controls_setup/op_command_1" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<on event="a" run="controls begin binding"/>
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- The port's settings screen, in the PC version's style (tools/port_settings.py) -->
|
||||
<menus>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/in_game_network_settings_screen" width="640" height="480" flags="pass_unhandled_to_focused_child pause_game" bitmap="bitmaps/gradient">
|
||||
<on event="b" back="true"/>
|
||||
<on event="back" back="true"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/header_profile_network_settings"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/options_menu"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/header_profile_network_settings" controller="1" left="35" top="11" width="605" height="59" bitmap="main_menu/settings_select/player_setup/player_profile_edit/network_setup/header_profile_network_settings">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/help" type="text" controller="1" left="68" top="350" width="482" height="60" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/help_strings" font="ui\large_ui" color="#FFFFFFFF">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/options_menu" type="column_list" width="640" height="480" flags="pass_unhandled_to_focused_child up_down_tabs_children" description="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/help">
|
||||
<data input="port settings help"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/op_map_sharing" x="54" y="73" platform="desktop android"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/op_multiplayer_hud" x="54" y="103" platform="desktop android"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/op_multiplayer_hud" x="54" y="73" platform="web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/op_streamer_mode" x="54" y="133" platform="desktop android"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/op_streamer_mode" x="54" y="103" platform="web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/button_bar" type="column_list" width="640" height="28" flags="pass_unhandled_to_focused_child left_right_tabs_items">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/button_defaults" y="1"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/button_ok" x="380" y="1"/>
|
||||
<child widget="common_button_cancel" x="510" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/op_map_sharing" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF" platform="desktop android">
|
||||
<on event="a" open="main_menu/settings_select/player_setup/player_profile_edit/network_setup/map_sharing/map_sharing_settings_screen"/>
|
||||
<on event="start" open="main_menu/settings_select/player_setup/player_profile_edit/network_setup/map_sharing/map_sharing_settings_screen"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/map_sharing_label"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/map_sharing_label" type="text" controller="1" width="512" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/labels" string_index="0" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/op_multiplayer_hud" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<on event="a" open="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/multiplayer_hud_settings_screen"/>
|
||||
<on event="start" open="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/multiplayer_hud_settings_screen"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/multiplayer_hud_label"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/multiplayer_hud_label" type="text" controller="1" width="512" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/labels" string_index="1" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/op_streamer_mode" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<on event="a" open="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/streamer_mode_settings_screen"/>
|
||||
<on event="start" open="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/streamer_mode_settings_screen"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/streamer_mode_label"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/streamer_mode_label" type="text" controller="1" width="512" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/labels" string_index="2" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/button_defaults" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="3" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port settings defaults"/>
|
||||
<on event="start" run="port settings defaults"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/button_ok" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="1" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port settings save" back="true"/>
|
||||
<on event="start" run="port settings save" back="true"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/labels">
|
||||
<string text="MAP SHARING:"/>
|
||||
<string text="MULTIPLAYER HUD:"/>
|
||||
<string text="STREAMER MODE:"/>
|
||||
</strings>
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/in_game/help_strings">
|
||||
<string text=""/>
|
||||
<string text="Downloading Custom Edition maps you do not have,\nand helping send them to other players."/>
|
||||
<string text="Players' names above their heads, the\nscoreboard, and the text chat."/>
|
||||
<string text="Hide offensive words in names and text from\nother players, maps and mods, as stars."/>
|
||||
</strings>
|
||||
</menus>
|
||||
@@ -47,7 +47,7 @@
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/map_sharing/map_seed_upload_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/map_sharing/labels" string_index="2" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/map_sharing/map_seed_upload_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="64 KB/S|128 KB/S|256 KB/S|512 KB/S|1 MB/S|2 MB/S" setting="network.map_seed_upload" values="64|128|256|512|1024|2048" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/map_sharing/map_seed_upload_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="64 KB/S|128 KB/S|256 KB/S|512 KB/S|1 MB/S|2 MB/S|4 MB/S|8 MB/S|10 MB/S" setting="network.map_seed_upload" values="64|128|256|512|1024|2048|4096|8192|10240" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/map_sharing/button_defaults" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="3" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- The port's settings screen, in the PC version's style (tools/port_settings.py) -->
|
||||
<menus>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/multiplayer_hud_settings_screen" width="640" height="480" flags="pass_unhandled_to_focused_child pause_game" bitmap="bitmaps/gradient">
|
||||
<on event="b" back="true"/>
|
||||
<on event="back" back="true"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/header_profile_network_settings"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/options_menu"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/header_profile_network_settings" controller="1" left="35" top="11" width="605" height="59" bitmap="main_menu/settings_select/player_setup/player_profile_edit/network_setup/header_profile_network_settings">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/help" type="text" controller="1" left="68" top="350" width="482" height="60" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/help_strings" font="ui\large_ui" color="#FFFFFFFF">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/options_menu" type="column_list" width="640" height="480" flags="pass_unhandled_to_focused_child up_down_tabs_children" description="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/help">
|
||||
<data input="port settings help"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/op_player_names" x="54" y="73"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/op_player_name_scale" x="54" y="103"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/op_scoreboard_team_layout" x="54" y="133"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/op_scoreboard_background" x="54" y="163"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/op_text_chat" x="54" y="193"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/op_chat_style" x="54" y="223"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/button_bar" type="column_list" width="640" height="28" flags="pass_unhandled_to_focused_child left_right_tabs_items">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/button_defaults" y="1"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/button_ok" x="380" y="1"/>
|
||||
<child widget="common_button_cancel" x="510" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/op_player_names" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/player_names_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/player_names_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/player_names_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/labels" string_index="0" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/player_names_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ALL|ALLIES|ENEMIES|NONE" setting="display.player_names" values="all|allies|enemies|none" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/op_player_name_scale" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/player_name_scale_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/player_name_scale_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/player_name_scale_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/labels" string_index="1" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/player_name_scale_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="0.5x|0.75x|1x|1.25x|1.5x|2x" setting="display.player_name_scale" values="0.5|0.75|1|1.25|1.5|2" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/op_scoreboard_team_layout" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/scoreboard_team_layout_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/scoreboard_team_layout_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/scoreboard_team_layout_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/labels" string_index="2" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/scoreboard_team_layout_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="TEAMS|BY SCORE" setting="display.scoreboard_team_layout" values="teams|score" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/op_scoreboard_background" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/scoreboard_background_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/scoreboard_background_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/scoreboard_background_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/labels" string_index="3" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/scoreboard_background_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ON|OFF" setting="display.scoreboard_background" values="true|false" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/op_text_chat" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/text_chat_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/text_chat_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/text_chat_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/labels" string_index="4" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/text_chat_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="SHOW|HIDE" setting="display.text_chat" values="true|false" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/op_chat_style" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/chat_style_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/chat_style_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/chat_style_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/labels" string_index="5" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/chat_style_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="COLORS|WHITE" setting="display.chat_style" values="colors|white" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/button_defaults" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="3" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port settings defaults"/>
|
||||
<on event="start" run="port settings defaults"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/button_ok" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="1" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port settings save" back="true"/>
|
||||
<on event="start" run="port settings save" back="true"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/labels">
|
||||
<string text="PLAYER NAMES:"/>
|
||||
<string text="PLAYER NAME SIZE:"/>
|
||||
<string text="SCOREBOARD LAYOUT:"/>
|
||||
<string text="SCOREBOARD PANEL:"/>
|
||||
<string text="TEXT CHAT:"/>
|
||||
<string text="CHAT TEXT:"/>
|
||||
</strings>
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/help_strings">
|
||||
<string text=""/>
|
||||
<string text="In multiplayer, whose names are drawn above their\nheads."/>
|
||||
<string text="How large the players' names are drawn."/>
|
||||
<string text="A team game's scoreboard: a column for each team,\nor every player in order of score."/>
|
||||
<string text="Draw a panel behind the scoreboard, for clearer\ntext."/>
|
||||
<string text="SHOW the text chat, or HIDE it: no lines, and\nits keys and D-pad Up open nothing."/>
|
||||
<string text="The text chat's colors: COLORS, names in their\nteam's color; WHITE, as Custom Edition."/>
|
||||
</strings>
|
||||
</menus>
|
||||
@@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- The port's settings screen, in the PC version's style (tools/port_settings.py) -->
|
||||
<menus>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/streamer_mode_settings_screen" width="640" height="480" flags="pass_unhandled_to_focused_child pause_game" bitmap="bitmaps/gradient">
|
||||
<on event="b" back="true"/>
|
||||
<on event="back" back="true"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/header_profile_network_settings"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/options_menu"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/header_profile_network_settings" controller="1" left="35" top="11" width="605" height="59" bitmap="main_menu/settings_select/player_setup/player_profile_edit/network_setup/header_profile_network_settings">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/help" type="text" controller="1" left="68" top="350" width="482" height="60" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/help_strings" font="ui\large_ui" color="#FFFFFFFF">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/options_menu" type="column_list" width="640" height="480" flags="pass_unhandled_to_focused_child up_down_tabs_children" description="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/help">
|
||||
<data input="port settings help"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/op_enabled" x="54" y="73"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/op_slurs" x="54" y="103"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/op_swearing" x="54" y="133"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/op_insults" x="54" y="163"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/button_bar" type="column_list" width="640" height="28" flags="pass_unhandled_to_focused_child left_right_tabs_items">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/button_defaults" y="1"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/button_ok" x="380" y="1"/>
|
||||
<child widget="common_button_cancel" x="510" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/op_enabled" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/enabled_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/enabled_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/enabled_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/labels" string_index="0" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/enabled_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ON|OFF" setting="streamer.enabled" values="true|false" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/op_slurs" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/slurs_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/slurs_spinner" x="286" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/slurs_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/labels" string_index="1" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/slurs_spinner" type="spinner" top="2" width="206" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="OFF|SEVERE|STRONG AND WORSE|MILD AND WORSE" setting="streamer.slurs" values="off|severe|strong|mild" font="ui\large_ui" color="#FF2896FF" align="center" text_y="4" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -13 19 -7" footer_bounds="7 208 19 214">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/op_swearing" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/swearing_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/swearing_spinner" x="286" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/swearing_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/labels" string_index="2" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/swearing_spinner" type="spinner" top="2" width="206" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="OFF|SEVERE|STRONG AND WORSE|MILD AND WORSE" setting="streamer.swearing" values="off|severe|strong|mild" font="ui\large_ui" color="#FF2896FF" align="center" text_y="4" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -13 19 -7" footer_bounds="7 208 19 214">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/op_insults" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/insults_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/insults_spinner" x="286" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/insults_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/labels" string_index="3" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/insults_spinner" type="spinner" top="2" width="206" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="OFF|SEVERE|STRONG AND WORSE|MILD AND WORSE" setting="streamer.insults" values="off|severe|strong|mild" font="ui\large_ui" color="#FF2896FF" align="center" text_y="4" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -13 19 -7" footer_bounds="7 208 19 214">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/button_defaults" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="3" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port settings defaults"/>
|
||||
<on event="start" run="port settings defaults"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/button_ok" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="1" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port settings save" back="true"/>
|
||||
<on event="start" run="port settings save" back="true"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/labels">
|
||||
<string text="STREAMER MODE:"/>
|
||||
<string text="CENSOR SLURS:"/>
|
||||
<string text="CENSOR SWEARING:"/>
|
||||
<string text="CENSOR INSULTS:"/>
|
||||
</strings>
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/help_strings">
|
||||
<string text=""/>
|
||||
<string text="Hide offensive words in names and text from\nother players, maps and mods, as stars."/>
|
||||
<string text="Slurs about race, sexuality, gender and\ndisability."/>
|
||||
<string text="Swearing and sexual words (STRONG AND WORSE\nleaves the mildest showing)."/>
|
||||
<string text="Name-calling and insults that are not slurs."/>
|
||||
</strings>
|
||||
</menus>
|
||||
@@ -17,15 +17,11 @@
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_allow_upnp" x="54" y="103" platform="desktop android"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_join_from_clipboard" x="54" y="133" platform="desktop android"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_auto" x="54" y="163" platform="desktop android"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_player_names" x="54" y="193" platform="desktop android"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_player_names" x="54" y="103" platform="web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_player_name_scale" x="54" y="223" platform="desktop android"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_player_name_scale" x="54" y="133" platform="web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_scoreboard_team_layout" x="54" y="253" platform="desktop android"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_scoreboard_team_layout" x="54" y="163" platform="web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_scoreboard_background" x="54" y="283" platform="desktop android"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_scoreboard_background" x="54" y="193" platform="web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_map_sharing" x="54" y="313" platform="desktop android"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_map_sharing" x="54" y="193" platform="desktop android"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_multiplayer_hud" x="54" y="223" platform="desktop android"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_multiplayer_hud" x="54" y="103" platform="web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_streamer_mode" x="54" y="253" platform="desktop android"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_streamer_mode" x="54" y="133" platform="web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/button_bar" type="column_list" width="640" height="28" flags="pass_unhandled_to_focused_child left_right_tabs_items">
|
||||
@@ -69,49 +65,29 @@
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/auto_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ON|OFF" setting="update.auto" values="true|false" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_player_names" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/player_names_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/player_names_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/player_names_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/labels" string_index="4" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/player_names_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ALL|ALLIES|ENEMIES|NONE" setting="display.player_names" values="all|allies|enemies|none" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_player_name_scale" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/player_name_scale_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/player_name_scale_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/player_name_scale_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/labels" string_index="5" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/player_name_scale_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="0.5x|0.75x|1x|1.25x|1.5x|2x" setting="display.player_name_scale" values="0.5|0.75|1|1.25|1.5|2" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_scoreboard_team_layout" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/scoreboard_team_layout_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/scoreboard_team_layout_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/scoreboard_team_layout_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/labels" string_index="6" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/scoreboard_team_layout_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="TEAMS|BY SCORE" setting="display.scoreboard_team_layout" values="teams|score" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_scoreboard_background" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/scoreboard_background_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/scoreboard_background_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/scoreboard_background_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/labels" string_index="7" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/scoreboard_background_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ON|OFF" setting="display.scoreboard_background" values="true|false" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_map_sharing" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF" platform="desktop android">
|
||||
<on event="a" open="main_menu/settings_select/player_setup/player_profile_edit/network_setup/map_sharing/map_sharing_settings_screen"/>
|
||||
<on event="start" open="main_menu/settings_select/player_setup/player_profile_edit/network_setup/map_sharing/map_sharing_settings_screen"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/map_sharing_label"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/map_sharing_label" type="text" controller="1" width="512" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/labels" string_index="8" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/map_sharing_label" type="text" controller="1" width="512" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/labels" string_index="4" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_multiplayer_hud" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<on event="a" open="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/multiplayer_hud_settings_screen"/>
|
||||
<on event="start" open="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud/multiplayer_hud_settings_screen"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud_label"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/multiplayer_hud_label" type="text" controller="1" width="512" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/labels" string_index="5" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/op_streamer_mode" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<on event="a" open="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/streamer_mode_settings_screen"/>
|
||||
<on event="start" open="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode/streamer_mode_settings_screen"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode_label"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/streamer_mode_label" type="text" controller="1" width="512" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/network_setup/labels" string_index="6" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/button_defaults" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="3" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port settings defaults"/>
|
||||
@@ -128,11 +104,9 @@
|
||||
<string text="UPNP PORT FORWARDING:"/>
|
||||
<string text="JOIN FROM CLIPBOARD:"/>
|
||||
<string text="CHECK FOR UPDATES:"/>
|
||||
<string text="PLAYER NAMES:"/>
|
||||
<string text="PLAYER NAME SIZE:"/>
|
||||
<string text="SCOREBOARD LAYOUT:"/>
|
||||
<string text="SCOREBOARD PANEL:"/>
|
||||
<string text="MAP SHARING:"/>
|
||||
<string text="MULTIPLAYER HUD:"/>
|
||||
<string text="STREAMER MODE:"/>
|
||||
</strings>
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/network_setup/help_strings">
|
||||
<string text=""/>
|
||||
@@ -140,10 +114,8 @@
|
||||
<string text="Let internet play ask the router to forward its\nport, for networks that stop connections."/>
|
||||
<string text="Join the game of an invite link copied before\nswitching to the game."/>
|
||||
<string text="Look for a new version when the game starts."/>
|
||||
<string text="In multiplayer, whose names are drawn above their\nheads."/>
|
||||
<string text="How large the players' names are drawn."/>
|
||||
<string text="A team game's scoreboard: a column for each team,\nor every player in order of score."/>
|
||||
<string text="Draw a panel behind the scoreboard, for clearer\ntext."/>
|
||||
<string text="Downloading Custom Edition maps you do not have,\nand helping send them to other players."/>
|
||||
<string text="Players' names above their heads, the\nscoreboard, and the text chat."/>
|
||||
<string text="Hide offensive words in names and text from\nother players, maps and mods, as stars."/>
|
||||
</strings>
|
||||
</menus>
|
||||
@@ -14,13 +14,14 @@
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/options_menu" type="column_list" width="640" height="480" flags="pass_unhandled_to_focused_child up_down_tabs_children" description="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/help">
|
||||
<data input="port settings help"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_high_res_hud" x="54" y="73"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_high_res_text" x="54" y="103"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_anti_aliasing" x="54" y="133" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_anti_aliasing_android" x="54" y="133" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_shadow_resolution" x="54" y="163"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_per_pixel_lighting" x="54" y="193"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_shield_glow" x="54" y="223"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_shield_glow_intensity" x="54" y="253"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_hud_button_style" x="54" y="103"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_high_res_text" x="54" y="133"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_anti_aliasing" x="54" y="163" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_anti_aliasing_android" x="54" y="163" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_shadow_resolution" x="54" y="193"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_per_pixel_lighting" x="54" y="223"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_shield_glow" x="54" y="253"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_shield_glow_intensity" x="54" y="283"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/button_bar" type="column_list" width="640" height="28" flags="pass_unhandled_to_focused_child left_right_tabs_items">
|
||||
@@ -37,11 +38,20 @@
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/high_res_hud_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ON|OFF" setting="display.high_res_hud" values="true|false" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_hud_button_style" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/hud_button_style_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/hud_button_style_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/hud_button_style_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="1" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/hud_button_style_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="CLASSIC|GEMS|DARK GEMS" setting="display.hud_button_style" values="classic|gems|menu_gems" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/op_high_res_text" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/high_res_text_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/high_res_text_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/high_res_text_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="1" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/high_res_text_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="2" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/high_res_text_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ON|OFF" setting="display.high_res_text" values="true|false" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
@@ -50,7 +60,7 @@
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/anti_aliasing_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/anti_aliasing_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/anti_aliasing_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="2" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/anti_aliasing_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="3" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/anti_aliasing_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="OFF|FXAA|SMAA|SSAA 2X|MSAA 2X|MSAA 4X|MSAA 8X" setting="display.anti_aliasing" values="off|fxaa|smaa|ssaa2x|msaa2x|msaa4x|msaa8x" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
@@ -59,7 +69,7 @@
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/anti_aliasing_android_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/anti_aliasing_android_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/anti_aliasing_android_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="3" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/anti_aliasing_android_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="4" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/anti_aliasing_android_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="OFF|FXAA|MSAA 2X|MSAA 4X" setting="display.anti_aliasing" values="off|fxaa|msaa2x|msaa4x" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
@@ -68,7 +78,7 @@
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shadow_resolution_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shadow_resolution_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shadow_resolution_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="4" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shadow_resolution_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="5" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shadow_resolution_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="128|256|512|1024" setting="display.shadow_resolution" values="128|256|512|1024" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
@@ -77,7 +87,7 @@
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/per_pixel_lighting_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/per_pixel_lighting_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/per_pixel_lighting_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="5" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/per_pixel_lighting_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="6" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/per_pixel_lighting_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ON|OFF" setting="display.per_pixel_lighting" values="true|false" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
@@ -86,7 +96,7 @@
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shield_glow_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shield_glow_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shield_glow_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="6" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shield_glow_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="7" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shield_glow_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ON|OFF" setting="display.shield_glow" values="true|false" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
@@ -95,7 +105,7 @@
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shield_glow_intensity_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shield_glow_intensity_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shield_glow_intensity_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="7" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shield_glow_intensity_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels" string_index="8" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/shield_glow_intensity_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="DEFAULT|LIGHT SHOW" setting="display.shield_glow_intensity" values="default|light_show" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
@@ -112,6 +122,7 @@
|
||||
</widget>
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/labels">
|
||||
<string text="HIGH-RES HUD:"/>
|
||||
<string text="BUTTON STYLE:"/>
|
||||
<string text="HIGH-RES TEXT:"/>
|
||||
<string text="ANTI-ALIASING:"/>
|
||||
<string text="ANTI-ALIASING:"/>
|
||||
@@ -123,6 +134,7 @@
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/graphics/help_strings">
|
||||
<string text=""/>
|
||||
<string text="Draw the HUD from the high-res redraws; off\ndraws the game's own pictures."/>
|
||||
<string text="Classic: plain Xbox buttons. Gems: shaded Xbox\nbuttons. Dark Gems: the menu's button artwork."/>
|
||||
<string text="Draw text and titles with high-res fonts; off\ndraws the game's own."/>
|
||||
<string text="Smooth jagged edges, which the Xbox did not. FXAA\nand SMAA are cheap; SSAA and MSAA are sharper."/>
|
||||
<string text="Smooth jagged edges, which the Xbox did not. FXAA\nis cheap; MSAA is sharper."/>
|
||||
|
||||
@@ -56,7 +56,8 @@
|
||||
<child widget="main_menu/main_menu_item_multiplayer" x="192" y="283"/>
|
||||
<child widget="main_menu/main_menu_item_profile" x="192" y="319"/>
|
||||
<child widget="main_menu/main_menu_item_settings" x="192" y="355"/>
|
||||
<child widget="main_menu/main_menu_item_quit_game" x="192" y="391"/>
|
||||
<child widget="main_menu/main_menu_item_mods" x="192" y="391"/>
|
||||
<child widget="main_menu/main_menu_item_quit_game" x="192" y="427"/>
|
||||
</widget>
|
||||
<widget name="main_menu/new_campaign_creating_profile" width="640" height="480" flags="pass_unhandled_to_focused_child no_history" auto_close="1">
|
||||
<on event="deleted" run="new campaign decision" open="main_menu/new_campaign_saving_profile"/>
|
||||
|
||||
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 116 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 96 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 418 B |
|
After Width: | Height: | Size: 2.7 KiB |