Compare commits

...

88 Commits

Author SHA1 Message Date
duhow 49992be60a disable pr build trigger
Docker / Build (push) Has been cancelled
2024-08-24 08:31:53 -07:00
duhow 7414b6ce8e trigger workflow build on pr 2024-08-24 08:31:53 -07:00
duhow 591f3c7b36 fix Werror maybe-uninitialized 2024-08-24 08:31:53 -07:00
duhow de2df5f6cf bump to ubuntu 24.04 and remove cmake downgrade version 2024-08-24 08:31:53 -07:00
David Girón 4a40dfd361 remove static folder copy (unneeded) 2024-08-24 08:31:53 -07:00
duhow b760bf5066 fix registry 2024-08-24 08:31:53 -07:00
duhow 8e85167cb6 run after main workflow finishes 2024-08-24 08:31:53 -07:00
duhow af27ea080f add GitHub Actions Workflow 2024-08-24 08:31:53 -07:00
duhow 65de5d0060 add Dockerfile 2024-08-24 08:31:53 -07:00
Martin Michelsen a9b816c548 allow battle param lookups from other episodes 2024-08-23 19:48:56 -07:00
duhow 075c576116 add nproc for macos 2024-08-23 13:05:19 -07:00
duhow f9986f5ac5 build in parallel jobs 2024-08-23 13:05:19 -07:00
Martin Michelsen a9a28aa71b swap ep4 rare boss drops; closes #557 2024-08-23 08:55:21 -07:00
Martin Michelsen c6bbd5daa3 remove debug comment 2024-08-23 08:49:05 -07:00
Martin Michelsen c89c3c27ad fix Ep1 normal Falz item location; fixes #555 2024-08-18 17:39:29 -07:00
Martin Michelsen 3205afbcdb save invalid fields when assigning certain npc skins; fixes #551 2024-08-18 17:19:35 -07:00
Martin Michelsen 61003b509a add $killcount command 2024-08-18 11:01:48 -07:00
Martin Michelsen ce3f25be7b update comment in example config 2024-08-17 15:33:27 -07:00
Martin Michelsen a8fd1bdada use new CMake configs from phosg and resource_dasm 2024-08-17 15:33:06 -07:00
Martin Michelsen 4426476a15 clean up patch enable quest logic 2024-08-17 10:39:10 -07:00
Martin Michelsen 7d775a38d1 remove invalid image data command from q211; fixes #549 2024-08-11 09:27:19 -07:00
Martin Michelsen a7d3720050 always null-terminate 81 command contents 2024-08-11 09:12:56 -07:00
Martin Michelsen 596ea40bc0 minor cleanup in notes 2024-08-11 09:12:39 -07:00
Martin Michelsen f8f194e19b port AllCards to all Ep3 versions 2024-08-10 18:04:29 -07:00
Martin Michelsen 170111422b move BB patches into ar-codes.txt 2024-08-10 15:25:49 -07:00
Martin Michelsen 81969fc91b add BB cheat patch notes 2024-08-10 00:33:21 -07:00
Martin Michelsen f0366a3550 add BB stack limits patch 2024-08-10 00:29:24 -07:00
Martin Michelsen d676e9bb38 add 07DF note 2024-08-10 00:28:51 -07:00
Martin Michelsen 188aac48eb fix LockStatusRegister 2024-08-07 10:34:19 -07:00
Martin Michelsen 24be0d8195 move default keyboard and joystick config into files 2024-08-06 22:47:04 -07:00
Martin Michelsen fbc5cd5967 fix print_bank 2024-08-02 18:07:25 -07:00
Martin Michelsen d11329b2c9 assign item IDs chen changing banks; fixes #546 2024-08-02 17:54:14 -07:00
Martin Michelsen 3a74dbf04e use aliases for subordinate boss entities; closes #545 2024-08-02 17:47:10 -07:00
Martin Michelsen 299e187380 fix edge cases in drop table area computation 2024-07-28 19:41:31 -07:00
Martin Michelsen 0f29b1801d split all material reset into two cases 2024-07-28 12:44:54 -07:00
Martin Michelsen f8162d442a add material reset to $edit 2024-07-28 12:42:13 -07:00
Martin Michelsen cd09bfa7e8 add common LE/BE type declarations 2024-07-28 12:01:56 -07:00
Martin Michelsen 1bfbf09891 use phosg namespace 2024-07-28 11:54:41 -07:00
Martin Michelsen 5523388ad4 disable rare notifs for client drops by default 2024-07-20 11:58:18 -07:00
Martin Michelsen a3cc0bd13f use ResourceDASM namespace where needed 2024-07-13 16:26:33 -07:00
Martin Michelsen 70ada6669d fix formatting in readme 2024-07-11 07:56:35 -07:00
Martin Michelsen 4d76229527 fix typo in comment 2024-07-10 22:06:24 -07:00
Martin Michelsen 5ea3d0ad4b add missing quest files 2024-07-10 20:41:18 -07:00
Martin Michelsen 90efde7aa9 handle rare shell i/o error 2024-07-10 19:48:51 -07:00
Martin Michelsen 55f1869125 add replace sound effects code 2024-07-08 01:08:43 -07:00
Martin Michelsen b4efd90fdc replace q050 and q052 with direct backports 2024-07-07 16:39:52 -07:00
Martin Michelsen 87dd554592 remove offset comments in reassembly mode 2024-07-07 15:19:18 -07:00
Martin Michelsen 58974ae1be use JP version as default sub_version 2024-07-06 12:57:30 -07:00
Martin Michelsen 21c8bab91c handle one 6x63 data race 2024-07-06 09:54:07 -07:00
Martin Michelsen c58b37be23 add GC-GJAM quest opcodes in handler-tables 2024-07-05 21:55:16 -07:00
Martin Michelsen d3d98c44b8 clear ep3 media when switching to proxy server 2024-07-04 16:25:21 -07:00
Martin Michelsen dc2e73d198 fix item notifications for mags on GC 2024-07-04 16:24:36 -07:00
Martin Michelsen 774f9649da fix binary operator bind order 2024-07-04 16:24:18 -07:00
Martin Michelsen 093287af75 fix condition icons in enemy HP bars patch 2024-06-29 22:25:32 -07:00
Martin Michelsen 0126189cbd fix 3OJ2 and 3OJ3 item loss patches 2024-06-29 12:22:27 -07:00
Martin Michelsen c250a2dbc4 update enemy HP bars patch 2024-06-28 20:57:16 -07:00
Martin Michelsen 2ff9df19c8 don't allow language fallback for q88500 2024-06-28 14:10:06 -07:00
Martin Michelsen 528593651b update TODO.md 2024-06-28 10:13:24 -07:00
Martin Michelsen 9f073d07cd don't use member initialization 2024-06-28 10:10:39 -07:00
Martin Michelsen 4bd6ef12a9 implement $savechar on Episode 3 2024-06-28 09:48:09 -07:00
Martin Michelsen 52644695a3 fix grind limit overdraft 2024-06-26 20:00:22 -07:00
Martin Michelsen 45e619718c fix patch menu on BB 2024-06-26 19:37:21 -07:00
nolrinale 43fd979763 Fixed Coren rewards item names causing parsing issues 2024-06-26 09:36:21 -07:00
Martin Michelsen 082bc49a4d add customization segregation test 2024-06-24 00:01:09 -07:00
Martin Michelsen 4adcaa7bee skip max grind check on v3 2024-06-24 00:00:48 -07:00
Martin Michelsen 630ae0beb4 fix stack limits on DC NTE 2024-06-23 22:38:46 -07:00
Martin Michelsen 246dfd9fe0 update notes on DC NTE quest commands 2024-06-23 22:38:46 -07:00
Martin Michelsen 6f056cb1bd update proxy options 2024-06-23 22:38:46 -07:00
Martin Michelsen 9322c023da fix missing sub_version check 2024-06-23 22:38:46 -07:00
Martin Michelsen fd4719f8ec clean up comments in q88500 2024-06-23 08:38:51 -07:00
Martin Michelsen 3a22a5c489 add Ep3 codepaths to B2 enabler 2024-06-23 00:24:01 -07:00
Martin Michelsen 862b3d27da add B2 patch support on PSO Plus 2024-06-22 21:42:30 -07:00
Martin Michelsen 998664d2fb add loading screen AR code 2024-06-22 17:22:02 -07:00
Martin Michelsen 0bf2d950ac fix offsets on DCv1 item loss patches 2024-06-22 15:23:38 -07:00
Martin Michelsen 3ae5e875a1 fix comments on some quest opcodes 2024-06-22 15:23:23 -07:00
Martin Michelsen a88795d8b9 fix edge case in quest episode detection 2024-06-22 15:22:52 -07:00
Martin Michelsen 9ca1b79409 add .include directives in quest assembler 2024-06-22 15:22:32 -07:00
Martin Michelsen ce8277b96a describe 6x51 command 2024-06-22 15:20:48 -07:00
Martin Michelsen 25731eb71f add comments about UDP subcommands 2024-06-22 15:20:39 -07:00
Martin Michelsen e55963b82b specify types on some quest handlers 2024-06-22 14:22:29 -07:00
Martin Michelsen b9d9b38351 add US v1.2 quest opcodes to handler-tables 2024-06-22 09:29:40 -07:00
Martin Michelsen 782babf3ae add quest opcode names in handler-tables 2024-06-22 09:07:37 -07:00
Martin Michelsen 9869fa03c2 only send notifs for client-generated items if the game drop mode is CLIENT 2024-06-21 11:02:59 -07:00
Martin Michelsen 0ae02b0191 add websocket endpoint for rare drop stream 2024-06-21 10:59:01 -07:00
Martin Michelsen c0ea976fdc fix typo in VersionDetectGC 2024-06-21 10:59:01 -07:00
nolrinale c4bf9e7d5b Adjusted Coren rewards to match Retail server ones 2024-06-20 20:24:05 -07:00
Martin Michelsen 2e5d95d612 fix data race in 6xCA command 2024-06-19 23:31:51 -07:00
nolrinale 75b2827da9 Corrected BB english client files 2024-06-18 19:37:37 -07:00
283 changed files with 19661 additions and 4777 deletions
+13 -4
View File
@@ -27,14 +27,23 @@ jobs:
- name: Install libraries (macOS)
if: ${{ matrix.os == 'macos-latest' }}
run: brew install libevent
run: |
brew install libevent
cat << EOF > nproc
#!/bin/sh
sysctl -n hw.logicalcpu
EOF
chmod a+x nproc
sudo cp nproc /usr/local/bin/nproc
rm -f nproc
- name: Install phosg
run: |
git clone https://github.com/fuzziqersoftware/phosg.git
cd phosg
cmake .
make
make -j $(nproc)
sudo make install
- name: Install resource_file
@@ -43,14 +52,14 @@ jobs:
git clone https://github.com/fuzziqersoftware/resource_dasm.git
cd resource_dasm
cmake .
make
make -j $(nproc)
sudo make install
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j $(nproc)
- name: Test
working-directory: ${{github.workspace}}/build
+55
View File
@@ -0,0 +1,55 @@
name: Docker
on:
# After build passes with tests
workflow_run:
workflows: [CMake]
types: [completed]
branches:
- master
push:
tags:
- 'v**'
jobs:
build:
runs-on: ubuntu-latest
name: Build
permissions:
contents: read
id-token: write
packages: write
steps:
- uses: actions/checkout@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=sha
type=ref,event=tag
type=semver,pattern={{version}}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
uses: docker/build-push-action@v6
with:
push: ${{ github.event_name != 'pull_request' }}
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+10 -15
View File
@@ -14,12 +14,6 @@ else()
add_compile_options(-Wall -Wextra -Werror -Wno-address-of-packed-member)
endif()
set(LOCAL_INCLUDE_DIR "/usr/local/include")
set(LOCAL_LIB_DIR "/usr/local/lib")
list(APPEND CMAKE_PREFIX_PATH ${LOCAL_LIB_DIR})
include_directories(${LOCAL_INCLUDE_DIR})
link_directories(${LOCAL_LIB_DIR})
# Library search
@@ -141,20 +135,19 @@ endif()
add_executable(newserv ${SOURCES})
target_include_directories(newserv PUBLIC ${LIBEVENT_INCLUDE_DIR} ${Iconv_INCLUDE_DIRS})
target_link_libraries(newserv phosg ${LIBEVENT_LIBRARIES} ${Iconv_LIBRARIES} pthread)
target_link_libraries(newserv phosg::phosg ${LIBEVENT_LIBRARIES} ${Iconv_LIBRARIES} pthread)
if(resource_file_FOUND)
target_compile_definitions(newserv PUBLIC HAVE_RESOURCE_FILE)
target_link_libraries(newserv resource_file::resource_file)
message(STATUS "resource_file found; enabling patch support")
else()
message(WARNING "resource_file not found; disabling patch support")
endif()
add_dependencies(newserv newserv-Revision-cc)
# target_compile_options(newserv PRIVATE -fsanitize=address)
# target_link_options(newserv PRIVATE -fsanitize=address)
if(resource_file_FOUND)
target_compile_definitions(newserv PUBLIC HAVE_RESOURCE_FILE)
target_link_libraries(newserv resource_file)
message(STATUS "libresource_file found; enabling patch support")
else()
message(WARNING "libresource_file not found; disabling patch support")
endif()
# Test configuration
@@ -179,6 +172,8 @@ foreach(ScriptTestCase IN ITEMS ${ScriptTestCases})
COMMAND ${ScriptTestCase} ${CMAKE_BINARY_DIR}/newserv)
endforeach()
# Installation configuration
install(TARGETS newserv DESTINATION bin)
+87
View File
@@ -0,0 +1,87 @@
# syntax=docker/dockerfile:1
ARG BASE_IMAGE=ubuntu:24.04
FROM ${BASE_IMAGE} AS builder
RUN apt update && apt install -y --no-install-recommends \
python3 \
git \
ca-certificates \
sudo \
make \
cmake \
g++ \
libevent-dev \
zlib1g-dev
# ---
FROM builder AS deps
ARG PHOSG_TARGET=master
ARG RESOURCE_DASM_TARGET=master
ARG BUILD_RESOURCE_DASM=true
RUN git clone --depth 1 -b ${PHOSG_TARGET} https://github.com/fuzziqersoftware/phosg.git && \
cd phosg && \
cmake . && \
make -j$(nproc) && \
sudo make install
RUN \
if [ "$BUILD_RESOURCE_DASM" = "true" ] ; then \
git clone --depth 1 -b ${RESOURCE_DASM_TARGET} https://github.com/fuzziqersoftware/resource_dasm.git && \
cd resource_dasm && \
cmake . && \
make -j$(nproc) && \
sudo make install \
; fi
# ---
FROM builder AS newserv
ARG BUILD_TYPE=Release
ARG BUILD_STRIP=true
WORKDIR /usr/src/newserv
COPY . .
COPY --from=deps /usr/local /usr/local
RUN cmake -B $PWD/build -DCMAKE_BUILD_TYPE=${BUILD_TYPE} && \
cmake --build $PWD/build --config ${BUILD_TYPE} -j $(nproc) && \
sudo make -C build install
RUN \
if [ "$BUILD_STRIP" = "true" ] ; then \
strip /usr/local/lib/*.a && \
strip /usr/local/bin/* \
; fi
# ---
FROM ${BASE_IMAGE} AS data
WORKDIR /newserv
COPY system/ ./system
RUN cp -f system/config.example.json system/config.json && \
sed -i 's/"ExternalAddress": "[^"]*"/"ExternalAddress": "0.0.0.0"/' system/config.json
# ---
FROM ${BASE_IMAGE} AS final
RUN apt update && apt install -y --no-install-recommends \
libevent-dev \
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/*
WORKDIR /newserv
COPY --from=data /newserv .
COPY --from=newserv /usr/local /usr/local
USER root
VOLUME /newserv/system
# does not allow receiving any signal at the moment, so force kill the app
STOPSIGNAL SIGKILL
CMD ["newserv"]
+42 -4
View File
@@ -140,8 +140,8 @@ newserv implements a patch server for PSO PC and PSO BB game data. Any file or d
For Blue Burst set up, the below is mandatory for a smooth experience:
1. Browse to your chosen client's data directory.
2. Copy all the map_*.dat files, unitxt_* files and the data.gsl file and place them in `system/patch-bb/data`.
3. If you're using game files from the Tethealla client, make a copy of unitxt_j.prs inside system/patch-bb/data and name it unitxt_e.prs. (If unitxt_e.prs already exists, replace it with the copied file.)
2. Copy all the `map_*.dat` files, `unitxt_*` files and the `data.gsl` file and place them in `system/patch-bb/data`.
3. If you're using game files from the Tethealla client, make a copy of `unitxt_j.prs` inside system/patch-bb/data and name it `unitxt_e.prs`. (If `unitxt_e.prs` already exists, replace it with the copied file.)
If you do not have a BB client, or using a Tethealla client from another source, Tethealla clients that are compatible with newserv can be found here: [English](https://web.archive.org/web/20240402011115/https://ragol.org/files/bb/TethVer12513_English.zip) / [Japanese](https://web.archive.org/web/20240402013127/https://ragol.org/files/bb/TethVer12513_Japanese.zip). These clients connect to 127.0.0.1 (localhost) automatically.
@@ -357,10 +357,14 @@ Exactly which data is saved and loaded depends on the game version:
| PSO PC (v2) | Yes | Yes | No | No | No | Save only |
| PSO GC NTE | Yes | Yes | Yes | Yes | Yes | Yes |
| PSO GC (not Plus) | Yes | Yes | Yes | Yes | Yes | Yes |
| PSO GC Plus | Save only | Save only | No | No | No | Save only |
| PSO GC Plus (1) | Save only | Save only | No | No | No | Save only |
| PSO GC Ep3 (1) | No | Save only | No | No | No | Save only |
| PSO Xbox | Yes | Yes | Yes | Yes | Yes | Yes |
| PSO BB | Yes | Yes | Yes | Yes | Yes | Yes |
*Notes*:
1. *If EnableSendFunctionCallQuestNumber is enabled in config.json, then $savechar and $loadchar can save and restore all character data on these versions, just like on GC non-Plus. Episode 3 characters exist in a separate namespace; that is, you can't use $savechar and $loadchar to convert an Ep3 character to non-Ep3, or vice versa.*
## Episode 3 features
newserv supports many features unique to Episode 3:
@@ -502,6 +506,7 @@ Some commands only work on the game server and not on the proxy server. The chat
* `$si` (game server only): Show basic information about the server.
* `$ping`: Show round-trip ping time from the server to you. On the proxy server, show the ping time from you to the proxy and from the proxy to the server.
* `$matcount` (game server only): Show how many of each type of material you've used.
* `$killcount` (game server only): Show the kill count on your currently-equipped weapon. If you're in a game and not on BB, the value is only accurate at the time the item enters the game.
* `$itemnotifs <mode>`: Enable item drop notification messages. If the game has private drops enabled, you will only see a notification if the dropped item is visible to you; you won't be notified of other players' drops. The modes are:
* `off`: No notifications are shown.
* `rare`: You are notified when a rare item drops.
@@ -549,7 +554,7 @@ Some commands only work on the game server and not on the proxy server. The chat
* `$savechar <slot>`: Save your current character data on the server in the specified slot. See the "Server-side saves" section for more details.
* `$loadchar <slot>`: Save your current character data on the server in the specified slot. See the "Server-side saves" section for more details.
* `$bbchar <username> <password> <slot>`: Save your current character data on the server in a different account's BB character slots. See the "Server-side saves" section for more details.
* `$edit <stat> <value>`: Modify your character data. If you are on V3 (GameCube/Xbox), this command does nothing. If you are on V1 or V2 (DC or PC, not BB), your changes will be undone if you join a game - to save your changes, disconnect from the lobby. If cheats are allowed on the server, `<stat>` can be any of `atp`, `mst`, `evp`, `hp`, `dfp`, `ata`, `lck`, `meseta`, `exp`, `level`, `namecolor`, `secid`, `name`, `language`, `npc`, or `tech`. If cheats are not allowed, only `namecolor`, `name`, `language`, and `npc` can be used. Changing your character's language is only useful on BB; to do so, use a single-character language code (e.g. to switch your character to English, use `$edit language E`; for Japanese, use `$edit language J`).
* `$edit <stat> <value>`: Modify your character data. See "Using $edit" below for details.
* Blue Burst player commands (game server only)
* `$bank [number]`: Switch your current bank, so you can access your other character's banks (if `number` is 1-4) or your shared account bank (if `number` is 0). If `number` is not given, switch back to your current character's bank.
@@ -595,6 +600,39 @@ Some commands only work on the game server and not on the proxy server. The chat
* `$kick <identifier>`: Disconnect a player. The identifier may be the player's name or Guild Card number.
* `$ban <duration> <identifier>`: Ban a player. The duration should be of the form `10m` (minutes), `10h` (hours), `10d` (days), `10w` (weeks), `10M` (months), or `10y` (years). (Numbers other than 10 may be used, of course.) As with `$kick`, the identifier may be the player's name or Guild Card number.
### Using $edit
The $edit command modifies your character data. This command doesn't work on V3 (GameCube/Xbox). If you are on V1 or V2 (DC or PC, not BB), your changes will be undone if you join a game - to save your changes, disconnect from the lobby.
Some subcommands are always available. They are:
* `$edit mat reset power`: Clear your usage of power materials (BB only)
* `$edit mat reset mind`: Clear your usage of mind materials (BB only)
* `$edit mat reset evade`: Clear your usage of evade materials (BB only)
* `$edit mat reset def`: Clear your usage of def materials (BB only)
* `$edit mat reset luck`: Clear your usage of luck materials (BB only)
* `$edit mat reset hp`: Clear your usage of HP materials (BB only)
* `$edit mat reset tp`: Clear your usage of TP materials (BB only)
* `$edit mat reset all`: Clear your usage of all materials except HP and TP (BB only)
* `$edit mat reset every`: Clear your usage of all materials including HP and TP (BB only)
* `$edit namecolor AARRGGBB`: Set your name color (AARRGGBB specified in hex)
* `$edit language L`: Set your language (Generally only useful on BB; values for L: J = Japanese, E = English, G = German, F = French, S = Spanish, B = Simplified Chinese, T = Traditional Chinese, K = Korean)
* `$edit name NAME`: Set your character name
* `$edit npc NPC-NAME`: Set or remove an NPC skin on your character (NPC-NAME can be ninja, rico, sonic, knuckles, tails, flowen, elly, or none)
The remaining subcommands are only available if cheat mode is enabled on the server. They are:
* `$edit atp N`: Set your ATP to N until stats are updated (e.g. by leveling up)
* `$edit mst N`: Set your MST to N until stats are updated
* `$edit evp N`: Set your EVP to N until stats are updated
* `$edit dfp N`: Set your DFP to N until stats are updated
* `$edit ata N`: Set your ATA to N until stats are updated
* `$edit lck N`: Set your LCK to N until stats are updated
* `$edit hp N`: Set your MST to N until stats are updated
* `$edit meseta N`: Set the amount of Meseta in your inventory
* `$edit exp N`: Set your total amount of EXP (does not affect level)
* `$edit level N`: Set your current level (recomputes stats, but does not affect EXP)
* `$edit secid SECID-NAME`: Set your section ID
* `$edit tech TECH-NAME LEVEL`: Set the level of one of your techniques
# Non-server features
newserv has many CLI options, which can be used to access functionality other than the game and proxy server. Run `newserv help` to see a full list of the options and how to use each one.
+1 -3
View File
@@ -1,11 +1,10 @@
## General
- Make reloading happen on separate threads so compression doesn't block active clients
- Implement decrypt/encrypt actions for VMS files
- Make UI strings localizable (e.g. entries in menus, welcome message, etc.)
- Add an idle connection timeout for proxy sessions
- Clean up ItemParameterTable implementation (see comment at the top of the class definition)
- Handle MeetUserExtensions properly in 41 and C4 commands on the proxy (rewrite the embedded 19 command and store a map of )
- Handle MeetUserExtensions properly in 41 and C4 commands on the proxy (rewrite the embedded 19 command and store a map of received destinations)
## PSO DC
@@ -26,7 +25,6 @@
## PSOBB
- Test all quest item subcommands
- Figure out why Pouilly Slime EXP doesn't work
- Make server-specified rare enemies work with maps loaded by the proxy
- Implement serialization for various table types (ItemPMT, ItemPT, etc.)
+25
View File
@@ -1,3 +1,16 @@
This file contains client patches I've made for various versions of PSO.
All BB patches are for the JP 1.25.13 version (Tethealla client).
See also https://github.com/Solybum/Blue-Burst-Patch-Project
(DCv2-US) Disable serial number validation (untested)
8C1E743E 01E0
8C2670B6 01E0
(BB) Disable item equip restrictions ("God of equip")
Memory: 005C9F31 E9A7000000
File: 001C9331 E9A7000000
Unlock all songs in BGM test
(Note: sadly, there are no secret/unused ones)
Ep12-JP12 => 04367A68 38600001
@@ -79,6 +92,11 @@ Ep3-US => 042F9AC0 60000000
Ep3-NTE => 040C2C48 60000000
Ep3-JP => 042F8B74 60000000
(Ep1&2 USA v1.1) Change type of all loading screens
0401CA04 3BE0000X
0401CA08 48000038
Values for X: 0 = lobby/game join, 1 = quest load, 3 = pipe up, 4 = pipe down, anything else = silent black screen
(Ep3 USA) Replace loading screen A button sounds with random sounds
042F9B18 4804BB19
042F9B1C 5463063E
@@ -259,6 +277,7 @@ Ep3-EU => 041A1C84 38600001
Ep3-US => 041A16FC 38600001
(Ep3 USA) Full dressing room v1
Original Ep1&2 code by Ralf @ GC-Forever
Can't change your class, but you start with your existing appearance
Go online with this code on after using the dressing room to fully save changes
0418EB5C 60000000
@@ -266,6 +285,7 @@ Go online with this code on after using the dressing room to fully save changes
042A0188 387E2120
(Ep3 USA) Full dressing room v2
Original Ep1&2 code by Ralf @ GC-Forever
Can change your class, but you start with the default appearance
Go online with this code on after using the dressing room to fully save changes
04186ECC 4BFFFFD8
@@ -350,3 +370,8 @@ Note: Without a TextEnglish.pr2/pr3 patch, the menu items for these sounds will
0408E448 38000001
0408E44C 900DA62C
0408E450 4E800020
(v1.1 USA) Replace all sound effects with specified sound effect
042256E4 3F40XXXX
042256E8 635AYYYY
042256EC 4800000C
File diff suppressed because one or more lines are too long
+573 -573
View File
File diff suppressed because it is too large Load Diff
+41 -11
View File
@@ -73,9 +73,9 @@ ItemLossPrevention
*** desc=Don't lose items if\nyou don't log off\nnormally
JP12------------- JP13------------- JP14------------- JP15------------- US10------------- US11------------- US12------------- EU--------------- DISASSEMBLY (US10)
801D33E4 4800004C 801D38EC 4800004C 801D3CC4 4800004C 801D39B8 4800004C 801D381C 4800004C 801D381C 4800004C 801D3A1C 4800004C 801D3ED8 4800004C b +0x0000004C /* 801D3868 */
8020010C 60000000 801FF710 60000000 801FF0FC 60000000 801FF0FC 60000000 801FFA44 60000000 801FF9E0 60000000 nop
802016CC 60000000 80200C9C 60000000 80200658 60000000 80200658 60000000 80200FD0 60000000 80200F3C 60000000 nop
801FD944 38000000 80202860 38000000 802021C4 38000000 802021C4 38000000 80202B94 38000000 80202AA8 38000000 li r0, 0x0000
801FE900 60000000 801FF174 60000000 8020010C 60000000 801FF710 60000000 801FF0FC 60000000 801FF0FC 60000000 801FFA44 60000000 801FF9E0 60000000 nop
801FFE5C 60000000 802006D0 60000000 802016CC 60000000 80200C9C 60000000 80200658 60000000 80200658 60000000 80200FD0 60000000 80200F3C 60000000 nop
802019C8 38000000 8020223C 38000000 801FD944 38000000 80202860 38000000 802021C4 38000000 802021C4 38000000 80202B94 38000000 80202AA8 38000000 li r0, 0x0000
802C2060 4800004C 802C2F98 4800004C 802C42E4 4800004C 802C3E78 4800004C 802C2A40 4800004C 802C2A84 4800004C 802C402C 4800004C 802C37C0 4800004C b +0x0000004C /* 802C2A8C */
802D0AA0 48000020 802D1A58 48000020 802D2C10 48000020 802D2938 48000020 802D1480 48000020 802D14C4 48000020 802D2AEC 48000020 802D2280 48000020 b +0x00000020 /* 802D14A0 */
@@ -754,15 +754,45 @@ Show Enemy HP Bars
EnemyHPBars
*** name=Enemy HP bars
*** desc=Show HP bars in\nenemy info windows
JP12------------- JP13------------- JP14------------- JP15------------- US10------------- US11------------- US12------------- EU--------------- DISASSEMBLY (US10)
JP12------------- JP13------------- JP14------------- JP15------------- US10------------- US11------------- US12------------- EU--------------- DISASSEMBLY (US12)
802612C4 4BFE1541 80261E9C 4BFE1349 80262EE4 4BFE0665 80262C98 4BFE1241 80261B9C 4BFE1545 80261B9C 4BFE1545 80262F5C 4BFE12B1 802627A4 4BFE12B1 bl -0x0001EABC /* 802430E0 */
804CAF00 42300000 804CE650 42300000 804D0BA0 42300000 804D0940 42300000 804CB6D0 42300000 804CBBB0 42300000 804D0218 42300000 804D0608 42300000 bdnz cr4, +0x00000000 /* 804CB6D0 */
804CAF1C FF00FF15 804CE66C FF00FF15 804D0BBC FF00FF15 804D095C FF00FF15 804CB6EC FF00FF15 804CBBCC FF00FF15 804D0234 FF00FF15 804D0624 FF00FF15 .invalid FC, 0
805CBFBC 42A00000 805D65BC 42A00000 805DDA5C 42A00000 805DD7FC 42A00000 805CC8C4 42A00000 805D38E4 42A00000 805DD104 42A00000 805D9344 42A00000 b +0x00000000 /* 805CC8C4 */
804CAE40 42640000 804CE590 42640000 804D0AE0 42640000 804D0880 42640000 804CB610 42640000 804CBAF0 42640000 804D0158 42640000 804D0548 42640000 bc 19, 4, +0x00000000 /* 804CB610 */
804CAE4C 42640000 804CE59C 42640000 804D0AEC 42640000 804D088C 42640000 804CB61C 42640000 804CBAFC 42640000 804D0164 42640000 804D0554 42640000 bc 19, 4, +0x00000000 /* 804CB61C */
804CAE58 42640000 804CE5A8 42640000 804D0AF8 42640000 804D0898 42640000 804CB628 42640000 804CBB08 42640000 804D0170 42640000 804D0560 42640000 bc 19, 4, +0x00000000 /* 804CB628 */
804CAE64 42640000 804CE5B4 42640000 804D0B04 42640000 804D08A4 42640000 804CB634 42640000 804CBB14 42640000 804D017C 42640000 804D056C 42640000 bc 19, 4, +0x00000000 /* 804CB634 */
804CAF00 42780000 804CE650 42780000 804D0BA0 42780000 804D0940 42780000 804CB6D0 42780000 804CBBB0 42780000 804D0218 42780000 804D0608 42780000
804CAF1C FF00FF15 804CE66C FF00FF15 804D0BBC FF00FF15 804D095C FF00FF15 804CB6EC FF00FF15 804CBBCC FF00FF15 804D0234 FF00FF15 804D0624 FF00FF15
805CBFBC 42C00000 805D65BC 42C00000 805DDA5C 42C00000 805DD7FC 42C00000 805CC8C4 42C00000 805D38E4 42C00000 805DD104 42C00000 805D9344 42C00000
804CAE40 42960000 804CE590 42960000 804D0AE0 42960000 804D0880 42960000 804CB610 42960000 804CBAF0 42960000 804D0158 42960000 804D0548 42960000
804CAE4C 42960000 804CE59C 42960000 804D0AEC 42960000 804D088C 42960000 804CB61C 42960000 804CBAFC 42960000 804D0164 42960000 804D0554 42960000
804CAE58 42960000 804CE5A8 42960000 804D0AF8 42960000 804D0898 42960000 804CB628 42960000 804CBB08 42960000 804D0170 42960000 804D0560 42960000
804CAE64 42960000 804CE5B4 42960000 804D0B04 42960000 804D08A4 42960000 804CB634 42960000 804CBB14 42960000 804D017C 42960000 804D056C 42960000
804CAE70 42960000 804CE5C0 42960000 804D0B10 42960000 804D08B0 42960000 804CB640 42960000 804CBB20 42960000 804D0188 42960000 804D0578 42960000
80261260 4BDAA3F1 80261E38 4BDA9819 80262E80 4BDA87D1 80262C34 4BDA8A1D 80261B38 4BDA9B19 80261B38 4BDA9B19 80262EF8 4BDA8759 80262740 4BDA8F11 bl -0x002578A8 /* 8000B650 */
80261420 4BDAA245 80261FF8 4BDA966D 80263040 4BDA8625 80262DF4 4BDA8871 80261CF8 4BDA996D 80261CF8 4BDA996D 802630B8 4BDA85AD 80262900 4BDA8D65 bl -0x00257A54 /* 8000B664 */
8000B650 3CA08001 8000B650 3CA08001 8000B650 3CA08001 8000B650 3CA08001 8000B650 3CA08001 8000B650 3CA08001 8000B650 3CA08001 8000B650 3CA08001 lis r5, 0x8001
8000B654 8065B6BC 8000B654 8065B6BC 8000B654 8065B6BC 8000B654 8065B6BC 8000B654 8065B6BC 8000B654 8065B6BC 8000B654 8065B6BC 8000B654 8065B6BC lwz r3, [r5 - 0x4944]
8000B658 7FFEFB78 8000B658 7FFEFB78 8000B658 7FFEFB78 8000B658 7FFEFB78 8000B658 7FFEFB78 8000B658 7FFEFB78 8000B658 7FFEFB78 8000B658 7FFEFB78 mr r30, r31
8000B65C A8DE032C 8000B65C A8DE032C 8000B65C A8DE032C 8000B65C A8DE032C 8000B65C A8DE032C 8000B65C A8DE032C 8000B65C A8DE032C 8000B65C A8DE032C lha r6, [r30 + 0x032C]
8000B660 48000010 8000B660 48000010 8000B660 48000010 8000B660 48000010 8000B660 48000010 8000B660 48000010 8000B660 48000010 8000B660 48000010 b +0x00000010 /* 8000B670 */
8000B664 A8DE02B8 8000B664 A8DE02B8 8000B664 A8DE02B8 8000B664 A8DE02B8 8000B664 A8DE02B8 8000B664 A8DE02B8 8000B664 A8DE02B8 8000B664 A8DE02B8 lha r6, [r30 + 0x02B8]
8000B668 3CA08001 8000B668 3CA08001 8000B668 3CA08001 8000B668 3CA08001 8000B668 3CA08001 8000B668 3CA08001 8000B668 3CA08001 8000B668 3CA08001 lis r5, 0x8001
8000B66C 9065B6BC 8000B66C 9065B6BC 8000B66C 9065B6BC 8000B66C 9065B6BC 8000B66C 9065B6BC 8000B66C 9065B6BC 8000B66C 9065B6BC 8000B66C 9065B6BC stw [r5 - 0x4944], r3
8000B670 7C0802A6 8000B670 7C0802A6 8000B670 7C0802A6 8000B670 7C0802A6 8000B670 7C0802A6 8000B670 7C0802A6 8000B670 7C0802A6 8000B670 7C0802A6 mflr r0
8000B674 9005B6C0 8000B674 9005B6C0 8000B674 9005B6C0 8000B674 9005B6C0 8000B674 9005B6C0 8000B674 9005B6C0 8000B674 9005B6C0 8000B674 9005B6C0 stw [r5 - 0x4940], r0
8000B678 7C651B78 8000B678 7C651B78 8000B678 7C651B78 8000B678 7C651B78 8000B678 7C651B78 8000B678 7C651B78 8000B678 7C651B78 8000B678 7C651B78 mr r5, r3
8000B67C A8FE02B8 8000B67C A8FE02B8 8000B67C A8FE02B8 8000B67C A8FE02B8 8000B67C A8FE02B8 8000B67C A8FE02B8 8000B67C A8FE02B8 8000B67C A8FE02B8 lha r7, [r30 + 0x02B8]
8000B680 3C808000 8000B680 3C808000 8000B680 3C808000 8000B680 3C808000 8000B680 3C808000 8000B680 3C808000 8000B680 3C808000 8000B680 3C808000 lis r4, 0x8000
8000B684 6084B6AC 8000B684 6084B6AC 8000B684 6084B6AC 8000B684 6084B6AC 8000B684 6084B6AC 8000B684 6084B6AC 8000B684 6084B6AC 8000B684 6084B6AC ori r4, r4, 0xB6AC
8000B688 38640018 8000B688 38640018 8000B688 38640018 8000B688 38640018 8000B688 38640018 8000B688 38640018 8000B688 38640018 8000B688 38640018 addi r3, r4, 0x0018
8000B68C 4CC63182 8000B68C 4CC63182 8000B68C 4CC63182 8000B68C 4CC63182 8000B68C 4CC63182 8000B68C 4CC63182 8000B68C 4CC63182 8000B68C 4CC63182 crxor crb6, crb6, crb6
8000B690 4838A86D 8000B690 4838D275 8000B690 4838F115 8000B690 4838EEC5 8000B690 4838BB3D 8000B690 4838BB95 8000B690 4838F295 8000B690 4838DD85 bl sprintf /* 8039A924 */
8000B694 3C808000 8000B694 3C808000 8000B694 3C808000 8000B694 3C808000 8000B694 3C808000 8000B694 3C808000 8000B694 3C808000 8000B694 3C808000 lis r4, 0x8000
8000B698 6084B6C4 8000B698 6084B6C4 8000B698 6084B6C4 8000B698 6084B6C4 8000B698 6084B6C4 8000B698 6084B6C4 8000B698 6084B6C4 8000B698 6084B6C4 ori r4, r4, 0xB6C4
8000B69C 7F83E378 8000B69C 7F83E378 8000B69C 7F83E378 8000B69C 7F83E378 8000B69C 7F83E378 8000B69C 7F83E378 8000B69C 7F83E378 8000B69C 7F83E378 mr r3, r28
8000B6A0 8004FFFC 8000B6A0 8004FFFC 8000B6A0 8004FFFC 8000B6A0 8004FFFC 8000B6A0 8004FFFC 8000B6A0 8004FFFC 8000B6A0 8004FFFC 8000B6A0 8004FFFC lwz r0, [r4 - 0x0004]
8000B6A4 7C0803A6 8000B6A4 7C0803A6 8000B6A4 7C0803A6 8000B6A4 7C0803A6 8000B6A4 7C0803A6 8000B6A4 7C0803A6 8000B6A4 7C0803A6 8000B6A4 7C0803A6 mtlr r0
8000B6A8 4E800020 8000B6A8 4E800020 8000B6A8 4E800020 8000B6A8 4E800020 8000B6A8 4E800020 8000B6A8 4E800020 8000B6A8 4E800020 8000B6A8 4E800020 blr
8000B6AC 25730A0A 8000B6AC 25730A0A 8000B6AC 25730A0A 8000B6AC 25730A0A 8000B6AC 25730A0A 8000B6AC 25730A0A 8000B6AC 25730A0A 8000B6AC 25730A0A .invalid
8000B6B0 48503A25 8000B6B0 48503A25 8000B6B0 48503A25 8000B6B0 48503A25 8000B6B0 48503A25 8000B6B0 48503A25 8000B6B0 48503A25 8000B6B0 48503A25 bl +0x00503A24 /* 8050F0D4 */
8000B6B4 642F2564 8000B6B4 642F2564 8000B6B4 642F2564 8000B6B4 642F2564 8000B6B4 642F2564 8000B6B4 642F2564 8000B6B4 642F2564 8000B6B4 642F2564 oris r15, r1, 0x2564
8000B6B8 00000000 8000B6B8 00000000 8000B6B8 00000000 8000B6B8 00000000 8000B6B8 00000000 8000B6B8 00000000 8000B6B8 00000000 8000B6B8 00000000 .invalid
PSO DC Reticle Colours
DCReticleColors
Binary file not shown.
Binary file not shown.
Binary file not shown.
View File
Binary file not shown.
View File
+8 -10
View File
@@ -23,7 +23,7 @@ AFSArchive::AFSArchive(shared_ptr<const string> data)
le_uint32_t size;
} __packed_ws__(FileEntry, 8);
StringReader r(*this->data);
phosg::StringReader r(*this->data);
const auto& header = r.get<FileHeader>();
if (header.magic != 0x41465300) { // 'AFS\0'
throw runtime_error("file is not an AFS archive");
@@ -52,29 +52,27 @@ string AFSArchive::get_copy(size_t index) const {
return string(reinterpret_cast<const char*>(ret.first), ret.second);
}
StringReader AFSArchive::get_reader(size_t index) const {
phosg::StringReader AFSArchive::get_reader(size_t index) const {
auto ret = this->get(index);
return StringReader(ret.first, ret.second);
return phosg::StringReader(ret.first, ret.second);
}
string AFSArchive::generate(const vector<string>& files, bool big_endian) {
return big_endian ? AFSArchive::generate_t<true>(files) : AFSArchive::generate_t<false>(files);
}
template <bool IsBigEndian>
template <bool BE>
string AFSArchive::generate_t(const vector<string>& files) {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
StringWriter w;
phosg::StringWriter w;
w.put_u32b(0x41465300); // 'AFS\0'
w.put<U32T>(files.size());
w.put<U32T<BE>>(files.size());
// It seems entries are aligned to 0x800-byte boundaries, and the file's
// header is always 0x80000 (!) bytes, most of which is unused
uint32_t data_offset = 0x80000;
for (const auto& file : files) {
w.put<U32T>(data_offset);
w.put<U32T>(file.size());
w.put<U32T<BE>>(data_offset);
w.put<U32T<BE>>(file.size());
data_offset = (data_offset + file.size() + 0x7FF) & (~0x7FF);
}
+4 -2
View File
@@ -8,6 +8,8 @@
#include <string>
#include <unordered_map>
#include "Types.hh"
class AFSArchive {
public:
AFSArchive(std::shared_ptr<const std::string> data);
@@ -23,12 +25,12 @@ public:
std::pair<const void*, size_t> get(size_t index) const;
std::string get_copy(size_t index) const;
StringReader get_reader(size_t index) const;
phosg::StringReader get_reader(size_t index) const;
static std::string generate(const std::vector<std::string>& files, bool big_endian);
private:
template <bool IsBigEndian>
template <bool BE>
static std::string generate_t(const std::vector<std::string>& files);
std::shared_ptr<const std::string> data;
+48 -48
View File
@@ -11,7 +11,7 @@
using namespace std;
shared_ptr<DCNTELicense> DCNTELicense::from_json(const JSON& json) {
shared_ptr<DCNTELicense> DCNTELicense::from_json(const phosg::JSON& json) {
auto ret = make_shared<DCNTELicense>();
ret->serial_number = json.get_string("SerialNumber");
ret->access_key = json.get_string("AccessKey");
@@ -30,14 +30,14 @@ shared_ptr<DCNTELicense> DCNTELicense::from_json(const JSON& json) {
return ret;
}
JSON DCNTELicense::json() const {
return JSON::dict({
phosg::JSON DCNTELicense::json() const {
return phosg::JSON::dict({
{"SerialNumber", this->serial_number},
{"AccessKey", this->access_key},
});
}
shared_ptr<V1V2License> V1V2License::from_json(const JSON& json) {
shared_ptr<V1V2License> V1V2License::from_json(const phosg::JSON& json) {
auto ret = make_shared<V1V2License>();
ret->serial_number = json.get_int("SerialNumber");
ret->access_key = json.get_string("AccessKey");
@@ -50,14 +50,14 @@ shared_ptr<V1V2License> V1V2License::from_json(const JSON& json) {
return ret;
}
JSON V1V2License::json() const {
return JSON::dict({
phosg::JSON V1V2License::json() const {
return phosg::JSON::dict({
{"SerialNumber", this->serial_number},
{"AccessKey", this->access_key},
});
}
shared_ptr<GCLicense> GCLicense::from_json(const JSON& json) {
shared_ptr<GCLicense> GCLicense::from_json(const phosg::JSON& json) {
auto ret = make_shared<GCLicense>();
ret->serial_number = json.get_int("SerialNumber");
ret->access_key = json.get_string("AccessKey");
@@ -74,15 +74,15 @@ shared_ptr<GCLicense> GCLicense::from_json(const JSON& json) {
return ret;
}
JSON GCLicense::json() const {
return JSON::dict({
phosg::JSON GCLicense::json() const {
return phosg::JSON::dict({
{"SerialNumber", this->serial_number},
{"AccessKey", this->access_key},
{"Password", this->password},
});
}
shared_ptr<XBLicense> XBLicense::from_json(const JSON& json) {
shared_ptr<XBLicense> XBLicense::from_json(const phosg::JSON& json) {
auto ret = make_shared<XBLicense>();
ret->gamertag = json.get_string("GamerTag");
ret->user_id = json.get_int("UserID");
@@ -99,15 +99,15 @@ shared_ptr<XBLicense> XBLicense::from_json(const JSON& json) {
return ret;
}
JSON XBLicense::json() const {
return JSON::dict({
phosg::JSON XBLicense::json() const {
return phosg::JSON::dict({
{"GamerTag", this->gamertag},
{"UserID", this->user_id},
{"AccountID", this->account_id},
});
}
shared_ptr<BBLicense> BBLicense::from_json(const JSON& json) {
shared_ptr<BBLicense> BBLicense::from_json(const phosg::JSON& json) {
auto ret = make_shared<BBLicense>();
ret->username = json.get_string("UserName");
ret->password = json.get_string("Password");
@@ -126,14 +126,14 @@ shared_ptr<BBLicense> BBLicense::from_json(const JSON& json) {
return ret;
}
JSON BBLicense::json() const {
return JSON::dict({
phosg::JSON BBLicense::json() const {
return phosg::JSON::dict({
{"UserName", this->username},
{"Password", this->password},
});
}
Account::Account(const JSON& json)
Account::Account(const phosg::JSON& json)
: account_id(0),
flags(0),
ban_end_time(0),
@@ -237,38 +237,38 @@ Account::Account(const JSON& json)
}
}
JSON Account::json() const {
JSON dc_nte_json = JSON::list();
phosg::JSON Account::json() const {
phosg::JSON dc_nte_json = phosg::JSON::list();
for (const auto& it : this->dc_nte_licenses) {
dc_nte_json.emplace_back(it.second->json());
}
JSON dc_json = JSON::list();
phosg::JSON dc_json = phosg::JSON::list();
for (const auto& it : this->dc_licenses) {
dc_json.emplace_back(it.second->json());
}
JSON pc_json = JSON::list();
phosg::JSON pc_json = phosg::JSON::list();
for (const auto& it : this->pc_licenses) {
pc_json.emplace_back(it.second->json());
}
JSON gc_json = JSON::list();
phosg::JSON gc_json = phosg::JSON::list();
for (const auto& it : this->gc_licenses) {
gc_json.emplace_back(it.second->json());
}
JSON xb_json = JSON::list();
phosg::JSON xb_json = phosg::JSON::list();
for (const auto& it : this->xb_licenses) {
xb_json.emplace_back(it.second->json());
}
JSON bb_json = JSON::list();
phosg::JSON bb_json = phosg::JSON::list();
for (const auto& it : this->bb_licenses) {
bb_json.emplace_back(it.second->json());
}
JSON auto_patches_json = JSON::list();
phosg::JSON auto_patches_json = phosg::JSON::list();
for (const auto& it : this->auto_patches_enabled) {
auto_patches_json.emplace_back(it);
}
return JSON::dict({
return phosg::JSON::dict({
{"FormatVersion", 1},
{"AccountID", this->account_id},
{"DCNTELicenses", std::move(dc_nte_json)},
@@ -333,14 +333,14 @@ void Account::print(FILE* stream) const {
}
if (flags_str.empty()) {
flags_str = "none";
} else if (ends_with(flags_str, ",")) {
} else if (phosg::ends_with(flags_str, ",")) {
flags_str.pop_back();
}
fprintf(stream, " Flags: %08" PRIX32 " (%s)\n", this->flags, flags_str.c_str());
}
if (this->ban_end_time) {
string time_str = format_time(this->ban_end_time);
string time_str = phosg::format_time(this->ban_end_time);
fprintf(stream, " Banned until: %" PRIu64 " (%s)\n", this->ban_end_time, time_str.c_str());
}
if (this->ep3_current_meseta || this->ep3_total_meseta_earned) {
@@ -388,14 +388,14 @@ void Account::print(FILE* stream) const {
void Account::save() const {
if (!this->is_temporary) {
auto json = this->json();
string json_data = json.serialize(JSON::SerializeOption::FORMAT | JSON::SerializeOption::HEX_INTEGERS);
string filename = string_printf("system/licenses/%010" PRIu32 ".json", this->account_id);
save_file(filename, json_data);
string json_data = json.serialize(phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::HEX_INTEGERS);
string filename = phosg::string_printf("system/licenses/%010" PRIu32 ".json", this->account_id);
phosg::save_file(filename, json_data);
}
}
void Account::delete_file() const {
string filename = string_printf("system/licenses/%010" PRIu32 ".json", this->account_id);
string filename = phosg::string_printf("system/licenses/%010" PRIu32 ".json", this->account_id);
remove(filename.c_str());
}
@@ -420,7 +420,7 @@ shared_ptr<Login> AccountIndex::from_dc_nte_credentials_locked(const string& ser
if (login->dc_nte_license->access_key != access_key) {
throw incorrect_access_key();
}
if (login->account->ban_end_time && (login->account->ban_end_time >= now())) {
if (login->account->ban_end_time && (login->account->ban_end_time >= phosg::now())) {
throw invalid_argument("user is banned");
}
return login;
@@ -448,7 +448,7 @@ shared_ptr<Login> AccountIndex::from_dc_nte_credentials(
auto login = make_shared<Login>();
login->account_was_created = true;
login->account = make_shared<Account>();
login->account->account_id = fnv1a32(serial_number) & 0x7FFFFFFF;
login->account->account_id = phosg::fnv1a32(serial_number) & 0x7FFFFFFF;
auto lic = make_shared<DCNTELicense>();
lic->serial_number = serial_number;
lic->access_key = access_key;
@@ -470,7 +470,7 @@ shared_ptr<Login> AccountIndex::from_dc_credentials_locked(
if (!is_shared && (login->dc_license->access_key != access_key)) {
throw incorrect_access_key();
}
if (login->account->ban_end_time && (login->account->ban_end_time >= now())) {
if (login->account->ban_end_time && (login->account->ban_end_time >= phosg::now())) {
throw invalid_argument("user is banned");
}
if (is_shared) {
@@ -519,7 +519,7 @@ shared_ptr<Login> AccountIndex::from_pc_nte_credentials(uint32_t guild_card_numb
throw missing_account();
}
if (guild_card_number == 0xFFFFFFFF) {
guild_card_number = random_object<uint32_t>() & 0x7FFFFFFF;
guild_card_number = phosg::random_object<uint32_t>() & 0x7FFFFFFF;
}
auto login = make_shared<Login>();
login->account_was_created = true;
@@ -543,7 +543,7 @@ shared_ptr<Login> AccountIndex::from_pc_credentials_locked(
if (!is_shared && (login->pc_license->access_key != access_key)) {
throw incorrect_access_key();
}
if (login->account->ban_end_time && (login->account->ban_end_time >= now())) {
if (login->account->ban_end_time && (login->account->ban_end_time >= phosg::now())) {
throw invalid_argument("user is banned");
}
if (is_shared) {
@@ -599,7 +599,7 @@ shared_ptr<Login> AccountIndex::from_gc_credentials_locked(
if (password && (login->gc_license->password != *password)) {
throw incorrect_password();
}
if (login->account->ban_end_time && (login->account->ban_end_time >= now())) {
if (login->account->ban_end_time && (login->account->ban_end_time >= phosg::now())) {
throw invalid_argument("user is banned");
}
if (is_shared) {
@@ -652,7 +652,7 @@ shared_ptr<Login> AccountIndex::from_xb_credentials_locked(const string& gamerta
(login->xb_license->account_id && (login->xb_license->account_id != account_id))) {
throw incorrect_access_key();
}
if (login->account->ban_end_time && (login->account->ban_end_time >= now())) {
if (login->account->ban_end_time && (login->account->ban_end_time >= phosg::now())) {
throw invalid_argument("user is banned");
}
return login;
@@ -680,7 +680,7 @@ shared_ptr<Login> AccountIndex::from_xb_credentials(
auto login = make_shared<Login>();
login->account_was_created = true;
login->account = make_shared<Account>();
login->account->account_id = fnv1a32(gamertag) & 0x7FFFFFFF;
login->account->account_id = phosg::fnv1a32(gamertag) & 0x7FFFFFFF;
auto lic = make_shared<XBLicense>();
lic->gamertag = gamertag;
lic->user_id = user_id;
@@ -701,7 +701,7 @@ shared_ptr<Login> AccountIndex::from_bb_credentials_locked(const string& usernam
if (password && (login->bb_license->password != *password)) {
throw incorrect_password();
}
if (login->account->ban_end_time && (login->account->ban_end_time >= now())) {
if (login->account->ban_end_time && (login->account->ban_end_time >= phosg::now())) {
throw invalid_argument("user is banned");
}
return login;
@@ -728,7 +728,7 @@ shared_ptr<Login> AccountIndex::from_bb_credentials(const string& username, cons
auto login = make_shared<Login>();
login->account_was_created = true;
login->account = make_shared<Account>();
login->account->account_id = fnv1a32(username) & 0x7FFFFFFF;
login->account->account_id = phosg::fnv1a32(username) & 0x7FFFFFFF;
auto lic = make_shared<BBLicense>();
lic->username = username;
lic->password = *password;
@@ -976,24 +976,24 @@ shared_ptr<Account> AccountIndex::create_temporary_account_for_shared_account(
shared_ptr<const Account> src_a, const string& variation_data) const {
auto ret = make_shared<Account>(*src_a);
ret->is_temporary = true;
ret->account_id = fnv1a32(&src_a->account_id, sizeof(src_a->account_id));
ret->account_id = fnv1a32(variation_data, ret->account_id);
ret->account_id = phosg::fnv1a32(&src_a->account_id, sizeof(src_a->account_id));
ret->account_id = phosg::fnv1a32(variation_data, ret->account_id);
return ret;
}
AccountIndex::AccountIndex(bool force_all_temporary)
: force_all_temporary(force_all_temporary) {
if (!this->force_all_temporary) {
if (!isdir("system/licenses")) {
if (!phosg::isdir("system/licenses")) {
mkdir("system/licenses", 0755);
} else {
for (const auto& item : list_directory("system/licenses")) {
if (ends_with(item, ".json")) {
for (const auto& item : phosg::list_directory("system/licenses")) {
if (phosg::ends_with(item, ".json")) {
try {
JSON json = JSON::parse(load_file("system/licenses/" + item));
phosg::JSON json = phosg::JSON::parse(phosg::load_file("system/licenses/" + item));
this->add(make_shared<Account>(json));
} catch (const exception& e) {
log_error("Failed to index account %s", item.c_str());
phosg::log_error("Failed to index account %s", item.c_str());
throw;
}
}
+12 -12
View File
@@ -16,16 +16,16 @@ struct DCNTELicense {
std::string serial_number;
std::string access_key;
static std::shared_ptr<DCNTELicense> from_json(const JSON& json);
JSON json() const;
static std::shared_ptr<DCNTELicense> from_json(const phosg::JSON& json);
phosg::JSON json() const;
};
struct V1V2License {
uint32_t serial_number = 0;
std::string access_key;
static std::shared_ptr<V1V2License> from_json(const JSON& json);
JSON json() const;
static std::shared_ptr<V1V2License> from_json(const phosg::JSON& json);
phosg::JSON json() const;
};
struct GCLicense {
@@ -33,8 +33,8 @@ struct GCLicense {
std::string access_key;
std::string password;
static std::shared_ptr<GCLicense> from_json(const JSON& json);
JSON json() const;
static std::shared_ptr<GCLicense> from_json(const phosg::JSON& json);
phosg::JSON json() const;
};
struct XBLicense {
@@ -42,16 +42,16 @@ struct XBLicense {
uint64_t user_id = 0;
uint64_t account_id = 0;
static std::shared_ptr<XBLicense> from_json(const JSON& json);
JSON json() const;
static std::shared_ptr<XBLicense> from_json(const phosg::JSON& json);
phosg::JSON json() const;
};
struct BBLicense {
std::string username;
std::string password;
static std::shared_ptr<BBLicense> from_json(const JSON& json);
JSON json() const;
static std::shared_ptr<BBLicense> from_json(const phosg::JSON& json);
phosg::JSON json() const;
};
struct Account {
@@ -101,10 +101,10 @@ struct Account {
std::unordered_map<std::string, std::shared_ptr<BBLicense>> bb_licenses;
Account() = default;
explicit Account(const JSON& json);
explicit Account(const phosg::JSON& json);
virtual ~Account() = default;
JSON json() const;
phosg::JSON json() const;
virtual void save() const;
virtual void delete_file() const;
+25 -25
View File
@@ -108,32 +108,32 @@ public:
: log("[addr-trans] "),
directory(directory),
enable_ppc(false) {
while (ends_with(this->directory, "/")) {
while (phosg::ends_with(this->directory, "/")) {
this->directory.pop_back();
}
for (const auto& filename : list_directory(this->directory)) {
if (ends_with(filename, ".dol")) {
for (const auto& filename : phosg::list_directory(this->directory)) {
if (phosg::ends_with(filename, ".dol")) {
string name = filename.substr(0, filename.size() - 4);
string path = directory + "/" + filename;
DOLFile dol(path.c_str());
auto mem = make_shared<MemoryContext>();
ResourceDASM::DOLFile dol(path.c_str());
auto mem = make_shared<ResourceDASM::MemoryContext>();
dol.load_into(mem);
this->mems.emplace(name, mem);
this->enable_ppc = true;
this->log.info("Loaded %s", name.c_str());
} else if (ends_with(filename, ".xbe")) {
} else if (phosg::ends_with(filename, ".xbe")) {
string name = filename.substr(0, filename.size() - 4);
string path = directory + "/" + filename;
XBEFile xbe(path.c_str());
auto mem = make_shared<MemoryContext>();
ResourceDASM::XBEFile xbe(path.c_str());
auto mem = make_shared<ResourceDASM::MemoryContext>();
xbe.load_into(mem);
this->mems.emplace(name, mem);
this->log.info("Loaded %s", name.c_str());
} else if (ends_with(filename, ".bin")) {
} else if (phosg::ends_with(filename, ".bin")) {
string name = filename.substr(0, filename.size() - 4);
string path = directory + "/" + filename;
string data = load_file(path);
auto mem = make_shared<MemoryContext>();
string data = phosg::load_file(path);
auto mem = make_shared<ResourceDASM::MemoryContext>();
mem->allocate_at(0x8C010000, data.size());
mem->memcpy(0x8C010000, data.data(), data.size());
this->mems.emplace(name, mem);
@@ -160,7 +160,7 @@ public:
uint32_t r2 = 0;
uint32_t r13 = 0;
for (const auto& block : it.second->allocated_blocks()) {
StringReader r = it.second->reader(block.first, block.second);
phosg::StringReader r = it.second->reader(block.first, block.second);
while (!r.eof() && r.where()) {
uint32_t opcode = r.get_u32b();
if ((opcode & 0xFFFF0000) == 0x3DA00000) {
@@ -204,7 +204,7 @@ public:
}
uint32_t find_match(
shared_ptr<const MemoryContext> dest_mem,
shared_ptr<const ResourceDASM::MemoryContext> dest_mem,
uint32_t src_addr,
uint32_t src_size,
ExpandMethod expand_method) const {
@@ -243,13 +243,13 @@ public:
size_t num_matches = 0;
size_t last_match_address = 0;
size_t match_length = match_bytes_before + match_bytes_after + 4;
StringReader src_r = this->src_mem->reader(src_section.first + src_offset - match_bytes_before, match_length);
phosg::StringReader src_r = this->src_mem->reader(src_section.first + src_offset - match_bytes_before, match_length);
for (const auto& dest_section : dest_mem->allocated_blocks()) {
for (size_t dest_match_offset = 0;
dest_match_offset + match_length < dest_section.second;
dest_match_offset += (is_ppc ? 4 : 1)) {
src_r.go(0);
StringReader dest_r = dest_mem->reader(dest_section.first + dest_match_offset, match_length);
phosg::StringReader dest_r = dest_mem->reader(dest_section.first + dest_match_offset, match_length);
size_t z;
if (is_ppc) {
for (z = 0; z < match_length; z += 4) {
@@ -429,11 +429,11 @@ public:
}
void handle_command(const string& command) {
auto tokens = split(command, ' ');
auto tokens = phosg::split(command, ' ');
if (tokens.empty()) {
throw runtime_error("no command given");
}
strip_trailing_whitespace(tokens[tokens.size() - 1]);
phosg::strip_trailing_whitespace(tokens[tokens.size() - 1]);
if (tokens[0] == "use") {
this->set_source_file(tokens.at(1));
@@ -457,7 +457,7 @@ public:
}
fflush(stdout);
string command = fgets(stdin);
string command = phosg::fgets(stdin);
try {
this->handle_command(command);
} catch (const exception& e) {
@@ -468,11 +468,11 @@ public:
}
private:
PrefixedLogger log;
phosg::PrefixedLogger log;
string directory;
unordered_map<string, shared_ptr<const MemoryContext>> mems;
unordered_map<string, shared_ptr<const ResourceDASM::MemoryContext>> mems;
string src_filename;
shared_ptr<const MemoryContext> src_mem;
shared_ptr<const ResourceDASM::MemoryContext> src_mem;
bool enable_ppc;
};
@@ -490,10 +490,10 @@ void run_address_translator(const std::string& directory, const std::string& use
}
vector<pair<uint32_t, string>> diff_dol_files(const string& a_filename, const string& b_filename) {
DOLFile a(a_filename.c_str());
DOLFile b(b_filename.c_str());
auto a_mem = make_shared<MemoryContext>();
auto b_mem = make_shared<MemoryContext>();
ResourceDASM::DOLFile a(a_filename.c_str());
ResourceDASM::DOLFile b(b_filename.c_str());
auto a_mem = make_shared<ResourceDASM::MemoryContext>();
auto b_mem = make_shared<ResourceDASM::MemoryContext>();
a.load_into(a_mem);
b.load_into(b_mem);
+14 -17
View File
@@ -5,15 +5,14 @@
#include <stdexcept>
#include "Text.hh"
#include "Types.hh"
using namespace std;
template <bool IsBigEndian>
template <bool BE>
struct BMLHeaderT {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
parray<uint8_t, 0x04> unknown_a1;
U32T num_entries;
U32T<BE> num_entries;
parray<uint8_t, 0x38> unknown_a2;
} __packed__;
@@ -22,16 +21,14 @@ using BMLHeaderBE = BMLHeaderT<true>;
check_struct_size(BMLHeader, 0x40);
check_struct_size(BMLHeaderBE, 0x40);
template <bool IsBigEndian>
template <bool BE>
struct BMLHeaderEntryT {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
pstring<TextEncoding::ASCII, 0x20> filename;
U32T compressed_size;
U32T<BE> compressed_size;
parray<uint8_t, 0x04> unknown_a1;
U32T decompressed_size;
U32T compressed_gvm_size;
U32T decompressed_gvm_size;
U32T<BE> decompressed_size;
U32T<BE> compressed_gvm_size;
U32T<BE> decompressed_gvm_size;
parray<uint8_t, 0x0C> unknown_a2;
} __packed__;
@@ -40,15 +37,15 @@ using BMLHeaderEntryBE = BMLHeaderEntryT<true>;
check_struct_size(BMLHeaderEntry, 0x40);
check_struct_size(BMLHeaderEntryBE, 0x40);
template <bool IsBigEndian>
template <bool BE>
void BMLArchive::load_t() {
StringReader r(*this->data);
phosg::StringReader r(*this->data);
const auto& header = r.get<BMLHeaderT<IsBigEndian>>();
const auto& header = r.get<BMLHeaderT<BE>>();
size_t offset = 0x800;
while (this->entries.size() < header.num_entries) {
const auto& entry = r.get<BMLHeaderEntryT<IsBigEndian>>();
const auto& entry = r.get<BMLHeaderEntryT<BE>>();
if (offset + entry.compressed_size > this->data->size()) {
throw runtime_error("BML data entry extends beyond end of data");
@@ -106,10 +103,10 @@ string BMLArchive::get_copy(const string& name) const {
}
}
StringReader BMLArchive::get_reader(const string& name) const {
phosg::StringReader BMLArchive::get_reader(const string& name) const {
try {
const auto& entry = this->entries.at(name);
return StringReader(this->data->data() + entry.offset, entry.size);
return phosg::StringReader(this->data->data() + entry.offset, entry.size);
} catch (const out_of_range&) {
throw out_of_range("BML does not contain file: " + name);
}
+2 -2
View File
@@ -24,10 +24,10 @@ public:
std::pair<const void*, size_t> get(const std::string& name) const;
std::pair<const void*, size_t> get_gvm(const std::string& name) const;
std::string get_copy(const std::string& name) const;
StringReader get_reader(const std::string& name) const;
phosg::StringReader get_reader(const std::string& name) const;
private:
template <bool IsBigEndian>
template <bool BE>
void load_t();
std::shared_ptr<const std::string> data;
+1 -1
View File
@@ -54,7 +54,7 @@ BattleParamsIndex::BattleParamsIndex(
for (uint8_t episode = 0; episode < 3; episode++) {
auto& file = this->files[is_solo][episode];
if (file.data->size() < sizeof(Table)) {
throw runtime_error(string_printf(
throw runtime_error(phosg::string_printf(
"battle params table size is incorrect (expected %zX bytes, have %zX bytes; is_solo=%hhu, episode=%hhu)",
sizeof(Table), file.data->size(), is_solo, episode));
}
+7 -8
View File
@@ -38,7 +38,7 @@ CatSession::CatSession(
const struct sockaddr_storage& remote,
Version version,
shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file)
: log(string_printf("[CatSession:%s] ", name_for_enum(version)), proxy_server_log.min_level),
: log(phosg::string_printf("[CatSession:%s] ", phosg::name_for_enum(version)), proxy_server_log.min_level),
base(base),
read_event(event_new(this->base.get(), 0, EV_READ | EV_PERSIST, CatSession::dispatch_read_stdin, this), event_free),
channel(version, 1, CatSession::dispatch_on_channel_input, CatSession::dispatch_on_channel_error, this, "CatSession"),
@@ -48,19 +48,19 @@ CatSession::CatSession(
throw runtime_error("remote is not AF_INET");
}
string netloc_str = render_sockaddr_storage(remote);
string netloc_str = phosg::render_sockaddr_storage(remote);
this->log.info("Connecting to %s", netloc_str.c_str());
struct bufferevent* bev = bufferevent_socket_new(
this->base.get(), -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
if (!bev) {
throw runtime_error(string_printf("failed to open socket (%d)", EVUTIL_SOCKET_ERROR()));
throw runtime_error(phosg::string_printf("failed to open socket (%d)", EVUTIL_SOCKET_ERROR()));
}
this->channel.set_bufferevent(bev, 0);
if (bufferevent_socket_connect(this->channel.bev.get(),
reinterpret_cast<const sockaddr*>(&remote), sizeof(struct sockaddr_in)) != 0) {
throw runtime_error(string_printf("failed to connect (%d)", EVUTIL_SOCKET_ERROR()));
throw runtime_error(phosg::string_printf("failed to connect (%d)", EVUTIL_SOCKET_ERROR()));
}
event_add(this->read_event.get(), nullptr);
@@ -68,7 +68,7 @@ CatSession::CatSession(
}
void CatSession::execute_command(const std::string& command) {
string full_cmd = parse_data_string(command, nullptr, ParseDataFlags::ALLOW_FILES);
string full_cmd = phosg::parse_data_string(command, nullptr, phosg::ParseDataFlags::ALLOW_FILES);
send_command_with_header(this->channel, full_cmd.data(), full_cmd.size());
}
@@ -109,9 +109,8 @@ void CatSession::on_channel_input(
// TODO: Use the iovec form of print_data here instead of
// prepend_command_header (which copies the string)
string full_cmd = prepend_command_header(
this->channel.version, this->channel.crypt_in.get(), command, flag, data);
print_data(stdout, full_cmd, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::OFFSET_16_BITS);
string full_cmd = prepend_command_header(this->channel.version, this->channel.crypt_in.get(), command, flag, data);
phosg::print_data(stdout, full_cmd, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::OFFSET_16_BITS);
}
void CatSession::dispatch_on_channel_error(Channel& ch, short events) {
+2 -2
View File
@@ -29,10 +29,10 @@ public:
virtual ~CatSession() = default;
protected:
PrefixedLogger log;
phosg::PrefixedLogger log;
std::shared_ptr<struct event_base> base;
std::unique_ptr<struct event, void (*)(struct event*)> read_event;
Poll poll;
phosg::Poll poll;
Channel channel;
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file;
+19 -19
View File
@@ -29,8 +29,8 @@ Channel::Channel(
on_error_t on_error,
void* context_obj,
const string& name,
TerminalFormat terminal_send_color,
TerminalFormat terminal_recv_color)
phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color)
: bev(nullptr, flush_and_free_bufferevent),
virtual_network_id(0),
version(version),
@@ -52,8 +52,8 @@ Channel::Channel(
on_error_t on_error,
void* context_obj,
const string& name,
TerminalFormat terminal_send_color,
TerminalFormat terminal_recv_color)
phosg::TerminalFormat terminal_send_color,
phosg::TerminalFormat terminal_recv_color)
: bev(nullptr, flush_and_free_bufferevent),
version(version),
language(language),
@@ -98,7 +98,7 @@ void Channel::set_bufferevent(struct bufferevent* bev, uint64_t virtual_network_
memset(&this->local_addr, 0, sizeof(this->local_addr));
memset(&this->remote_addr, 0, sizeof(this->remote_addr));
} else {
get_socket_addresses(fd, &this->local_addr, &this->remote_addr);
phosg::get_socket_addresses(fd, &this->local_addr, &this->remote_addr);
}
bufferevent_setcb(this->bev.get(), &Channel::dispatch_on_input, nullptr, &Channel::dispatch_on_error, this);
@@ -206,9 +206,9 @@ Channel::Message Channel::recv() {
}
command_data.resize(command_logical_size - header_size);
if (command_data_log.should_log(LogLevel::INFO) && (this->terminal_recv_color != TerminalFormat::END)) {
if (use_terminal_colors && this->terminal_recv_color != TerminalFormat::NORMAL) {
print_color_escape(stderr, this->terminal_recv_color, TerminalFormat::BOLD, TerminalFormat::END);
if (command_data_log.should_log(phosg::LogLevel::INFO) && (this->terminal_recv_color != phosg::TerminalFormat::END)) {
if (use_terminal_colors && this->terminal_recv_color != phosg::TerminalFormat::NORMAL) {
print_color_escape(stderr, this->terminal_recv_color, phosg::TerminalFormat::BOLD, phosg::TerminalFormat::END);
}
if (version == Version::BB_V4) {
@@ -221,7 +221,7 @@ Channel::Message Channel::recv() {
command_data_log.info(
"Received from %s (version=%s command=%02hX flag=%02" PRIX32 ")",
this->name.c_str(),
name_for_enum(this->version),
phosg::name_for_enum(this->version),
header.command(this->version),
header.flag(this->version));
}
@@ -229,10 +229,10 @@ Channel::Message Channel::recv() {
vector<struct iovec> iovs;
iovs.emplace_back(iovec{.iov_base = header_data.data(), .iov_len = header_data.size()});
iovs.emplace_back(iovec{.iov_base = command_data.data(), .iov_len = command_data.size()});
print_data(stderr, iovs, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
phosg::print_data(stderr, iovs, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
if (use_terminal_colors && this->terminal_recv_color != TerminalFormat::NORMAL) {
print_color_escape(stderr, TerminalFormat::NORMAL, TerminalFormat::END);
if (use_terminal_colors && this->terminal_recv_color != phosg::TerminalFormat::NORMAL) {
phosg::print_color_escape(stderr, phosg::TerminalFormat::NORMAL, phosg::TerminalFormat::END);
}
}
@@ -342,20 +342,20 @@ void Channel::send(uint16_t cmd, uint32_t flag, const std::vector<std::pair<cons
}
send_data.resize(send_data_size, '\0');
if (!silent && (command_data_log.should_log(LogLevel::INFO)) && (this->terminal_send_color != TerminalFormat::END)) {
if (use_terminal_colors && this->terminal_send_color != TerminalFormat::NORMAL) {
print_color_escape(stderr, TerminalFormat::FG_YELLOW, TerminalFormat::BOLD, TerminalFormat::END);
if (!silent && (command_data_log.should_log(phosg::LogLevel::INFO)) && (this->terminal_send_color != phosg::TerminalFormat::END)) {
if (use_terminal_colors && this->terminal_send_color != phosg::TerminalFormat::NORMAL) {
print_color_escape(stderr, phosg::TerminalFormat::FG_YELLOW, phosg::TerminalFormat::BOLD, phosg::TerminalFormat::END);
}
if (version == Version::BB_V4) {
command_data_log.info("Sending to %s (version=BB command=%04hX flag=%08" PRIX32 ")",
this->name.c_str(), cmd, flag);
} else {
command_data_log.info("Sending to %s (version=%s command=%02hX flag=%02" PRIX32 ")",
this->name.c_str(), name_for_enum(version), cmd, flag);
this->name.c_str(), phosg::name_for_enum(version), cmd, flag);
}
print_data(stderr, send_data.data(), logical_size, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
if (use_terminal_colors && this->terminal_send_color != TerminalFormat::NORMAL) {
print_color_escape(stderr, TerminalFormat::NORMAL, TerminalFormat::END);
phosg::print_data(stderr, send_data.data(), logical_size, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
if (use_terminal_colors && this->terminal_send_color != phosg::TerminalFormat::NORMAL) {
print_color_escape(stderr, phosg::TerminalFormat::NORMAL, phosg::TerminalFormat::END);
}
}
+6 -6
View File
@@ -21,8 +21,8 @@ struct Channel {
std::shared_ptr<PSOEncryption> crypt_out;
std::string name;
TerminalFormat terminal_send_color;
TerminalFormat terminal_recv_color;
phosg::TerminalFormat terminal_send_color;
phosg::TerminalFormat terminal_recv_color;
struct Message {
uint16_t command;
@@ -45,8 +45,8 @@ struct Channel {
on_error_t on_error,
void* context_obj,
const std::string& name,
TerminalFormat terminal_send_color = TerminalFormat::END,
TerminalFormat terminal_recv_color = TerminalFormat::END);
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
phosg::TerminalFormat terminal_recv_color = phosg::TerminalFormat::END);
// Creates a connected channel
Channel(
struct bufferevent* bev,
@@ -57,8 +57,8 @@ struct Channel {
on_error_t on_error,
void* context_obj,
const std::string& name = "",
TerminalFormat terminal_send_color = TerminalFormat::END,
TerminalFormat terminal_recv_color = TerminalFormat::END);
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
phosg::TerminalFormat terminal_recv_color = phosg::TerminalFormat::END);
Channel(const Channel& other) = delete;
Channel(Channel&& other) = delete;
Channel& operator=(const Channel& other) = delete;
+181 -63
View File
@@ -103,7 +103,7 @@ static void check_is_leader(shared_ptr<Lobby> l, shared_ptr<Client> c) {
static void server_command_server_info(shared_ptr<Client> c, const std::string&) {
auto s = c->require_server_state();
string uptime_str = format_duration(now() - s->creation_time);
string uptime_str = phosg::format_duration(phosg::now() - s->creation_time);
send_text_message_printf(c,
"Uptime: $C6%s$C7\nLobbies: $C6%zu$C7\nClients: $C6%zu$C7(g) $C6%zu$C7(p)",
uptime_str.c_str(),
@@ -123,11 +123,11 @@ static void server_command_lobby_info(shared_ptr<Client> c, const std::string&)
if (l->is_game()) {
if (!l->is_ep3()) {
if (l->max_level == 0xFFFFFFFF) {
lines.emplace_back(string_printf("$C6%08X$C7 L$C6%d+$C7", l->lobby_id, l->min_level + 1));
lines.emplace_back(phosg::string_printf("$C6%08X$C7 L$C6%d+$C7", l->lobby_id, l->min_level + 1));
} else {
lines.emplace_back(string_printf("$C6%08X$C7 L$C6%d-%d$C7", l->lobby_id, l->min_level + 1, l->max_level + 1));
lines.emplace_back(phosg::string_printf("$C6%08X$C7 L$C6%d-%d$C7", l->lobby_id, l->min_level + 1, l->max_level + 1));
}
lines.emplace_back(string_printf("$C7Section ID: $C6%s$C7", name_for_section_id(l->effective_section_id())));
lines.emplace_back(phosg::string_printf("$C7Section ID: $C6%s$C7", name_for_section_id(l->effective_section_id())));
switch (l->drop_mode) {
case Lobby::DropMode::DISABLED:
@@ -153,45 +153,45 @@ static void server_command_lobby_info(shared_ptr<Client> c, const std::string&)
}
} else {
lines.emplace_back(string_printf("$C7State seed: $C6%08X$C7", l->random_seed));
lines.emplace_back(phosg::string_printf("$C7State seed: $C6%08X$C7", l->random_seed));
}
} else {
lines.emplace_back(string_printf("$C7Lobby ID: $C6%08X$C7", l->lobby_id));
lines.emplace_back(phosg::string_printf("$C7Lobby ID: $C6%08X$C7", l->lobby_id));
}
string slots_str = "Slots: ";
for (size_t z = 0; z < l->clients.size(); z++) {
if (!l->clients[z]) {
slots_str += string_printf("$C0%zX$C7", z);
slots_str += phosg::string_printf("$C0%zX$C7", z);
} else {
bool is_self = l->clients[z] == c;
bool is_leader = z == l->leader_id;
if (is_self && is_leader) {
slots_str += string_printf("$C6%zX$C7", z);
slots_str += phosg::string_printf("$C6%zX$C7", z);
} else if (is_self) {
slots_str += string_printf("$C2%zX$C7", z);
slots_str += phosg::string_printf("$C2%zX$C7", z);
} else if (is_leader) {
slots_str += string_printf("$C4%zX$C7", z);
slots_str += phosg::string_printf("$C4%zX$C7", z);
} else {
slots_str += string_printf("%zX", z);
slots_str += phosg::string_printf("%zX", z);
}
}
}
lines.emplace_back(std::move(slots_str));
}
send_text_message(c, join(lines, "\n"));
send_text_message(c, phosg::join(lines, "\n"));
}
static void server_command_ping(shared_ptr<Client> c, const std::string&) {
c->ping_start_time = now();
c->ping_start_time = phosg::now();
send_command(c, 0x1D, 0x00);
}
static void proxy_command_ping(shared_ptr<ProxyServer::LinkedSession> ses, const std::string&) {
ses->client_ping_start_time = now();
ses->server_ping_start_time = now();
ses->client_ping_start_time = phosg::now();
ses->server_ping_start_time = ses->client_ping_start_time;
C_GuildCardSearch_40 cmd = {0x00010000, ses->remote_guild_card_number, ses->remote_guild_card_number};
ses->client_channel.send(0x1D, 0x00);
@@ -204,7 +204,7 @@ static void proxy_command_lobby_info(shared_ptr<ProxyServer::LinkedSession> ses,
// don't show it. (The user can see it in the pause menu, unlike in masked-GC
// sessions like GC.)
if (ses->remote_guild_card_number >= 0) {
msg = string_printf("$C7GC: $C6%" PRId64 "$C7\n", ses->remote_guild_card_number);
msg = phosg::string_printf("$C7GC: $C6%" PRId64 "$C7\n", ses->remote_guild_card_number);
}
msg += "Slots: ";
@@ -212,15 +212,15 @@ static void proxy_command_lobby_info(shared_ptr<ProxyServer::LinkedSession> ses,
bool is_self = z == ses->lobby_client_id;
bool is_leader = z == ses->leader_client_id;
if (ses->lobby_players[z].guild_card_number == 0) {
msg += string_printf("$C0%zX$C7", z);
msg += phosg::string_printf("$C0%zX$C7", z);
} else if (is_self && is_leader) {
msg += string_printf("$C6%zX$C7", z);
msg += phosg::string_printf("$C6%zX$C7", z);
} else if (is_self) {
msg += string_printf("$C2%zX$C7", z);
msg += phosg::string_printf("$C2%zX$C7", z);
} else if (is_leader) {
msg += string_printf("$C4%zX$C7", z);
msg += phosg::string_printf("$C4%zX$C7", z);
} else {
msg += string_printf("%zX", z);
msg += phosg::string_printf("%zX", z);
}
}
@@ -233,7 +233,7 @@ static void proxy_command_lobby_info(shared_ptr<ProxyServer::LinkedSession> ses,
}
if (!cheats_tokens.empty()) {
msg += "\n$C7Cheats: $C6";
msg += join(cheats_tokens, ",");
msg += phosg::join(cheats_tokens, ",");
}
vector<const char*> behaviors_tokens;
@@ -251,7 +251,7 @@ static void proxy_command_lobby_info(shared_ptr<ProxyServer::LinkedSession> ses,
}
if (!behaviors_tokens.empty()) {
msg += "\n$C7Flags: $C6";
msg += join(behaviors_tokens, ",");
msg += phosg::join(behaviors_tokens, ",");
}
if (ses->config.override_section_id != 0xFF) {
@@ -353,7 +353,7 @@ static void server_command_swset_swclear(shared_ptr<Client> c, const std::string
return;
}
auto tokens = split(args, ' ');
auto tokens = phosg::split(args, ' ');
uint8_t floor, flag_num;
if (tokens.size() == 1) {
floor = c->floor;
@@ -391,7 +391,7 @@ static void proxy_command_swset_swclear(shared_ptr<ProxyServer::LinkedSession> s
return;
}
auto tokens = split(args, ' ');
auto tokens = phosg::split(args, ' ');
uint8_t floor, flag_num;
if (tokens.size() == 1) {
floor = ses->floor;
@@ -589,7 +589,7 @@ static void server_command_qgwrite(shared_ptr<Client> c, const std::string& args
return;
}
auto tokens = split(args, ' ');
auto tokens = phosg::split(args, ' ');
if (tokens.size() != 2) {
send_text_message(c, "$C6Incorrect number\nof arguments");
return;
@@ -616,7 +616,7 @@ static void server_command_qsync_qsyncall(shared_ptr<Client> c, const std::strin
return;
}
auto tokens = split(args, ' ');
auto tokens = phosg::split(args, ' ');
if (tokens.size() != 2) {
send_text_message(c, "$C6Incorrect number of\narguments");
return;
@@ -655,7 +655,7 @@ static void proxy_command_qsync_qsyncall(shared_ptr<ProxyServer::LinkedSession>
return;
}
auto tokens = split(args, ' ');
auto tokens = phosg::split(args, ' ');
if (tokens.size() != 2) {
send_text_message(ses->client_channel, "$C6Incorrect number of\narguments");
return;
@@ -724,6 +724,35 @@ static void server_command_show_material_counts(shared_ptr<Client> c, const std:
}
}
static void server_command_show_kill_count(shared_ptr<Client> c, const std::string&) {
auto p = c->character();
size_t item_index;
try {
item_index = p->inventory.find_equipped_item(EquipSlot::WEAPON);
} catch (const out_of_range&) {
send_text_message(c, "No weapon equipped");
return;
}
const auto& item = p->inventory.items.at(item_index);
if (!item.data.has_kill_count()) {
send_text_message(c, "Weapon does not\nhave a kill count");
return;
}
// Kill counts are only accurate on the server side at all times on BB. On
// other versions, we update the server's view of the client's inventory
// during games, but we can't track kills because the client doesn't inform
// the server whether it counted a kill for any individual enemy. So, on
// non-BB versions, the kill count is accurate at all times in the lobby
// (since kills can't occur there), or at the beginning of a game.
if ((c->version() == Version::BB_V4) || !c->require_lobby()->is_game()) {
send_text_message_printf(c, "%hu kills", item.data.get_kill_count());
} else {
send_text_message_printf(c, "%hu kills as of\ngame join", item.data.get_kill_count());
}
}
static void server_command_auction(shared_ptr<Client> c, const std::string&) {
check_account_flag(c, Account::Flag::DEBUG);
auto l = c->require_lobby();
@@ -896,26 +925,26 @@ static void proxy_command_get_player_card(shared_ptr<ProxyServer::LinkedSession>
static void server_command_send_client(shared_ptr<Client> c, const std::string& args) {
check_debug_enabled(c);
string data = parse_data_string(args);
string data = phosg::parse_data_string(args);
data.resize((data.size() + 3) & (~3));
c->channel.send(data);
}
static void server_command_send_server(shared_ptr<Client> c, const std::string& args) {
check_debug_enabled(c);
string data = parse_data_string(args);
string data = phosg::parse_data_string(args);
data.resize((data.size() + 3) & (~3));
on_command_with_header(c, data);
}
static void proxy_command_send_client(shared_ptr<ProxyServer::LinkedSession> ses, const std::string& args) {
string data = parse_data_string(args);
string data = phosg::parse_data_string(args);
data.resize((data.size() + 3) & (~3));
ses->client_channel.send(data);
}
static void proxy_command_send_server(shared_ptr<ProxyServer::LinkedSession> ses, const std::string& args) {
string data = parse_data_string(args);
string data = phosg::parse_data_string(args);
data.resize((data.size() + 3) & (~3));
ses->server_channel.send(data);
}
@@ -1044,7 +1073,7 @@ static string file_path_for_recording(const std::string& args, uint32_t account_
throw runtime_error("invalid recording name");
}
}
return string_printf("system/ep3/battle-records/%010" PRIu32 "_%s.mzrd", account_id, args.c_str());
return phosg::string_printf("system/ep3/battle-records/%010" PRIu32 "_%s.mzrd", account_id, args.c_str());
}
static void server_command_saverec(shared_ptr<Client> c, const std::string& args) {
@@ -1054,7 +1083,7 @@ static void server_command_saverec(shared_ptr<Client> c, const std::string& args
}
string file_path = file_path_for_recording(args, c->login->account->account_id);
string data = c->ep3_prev_battle_record->serialize();
save_file(file_path, data);
phosg::save_file(file_path, data);
send_text_message(c, "$C7Recording saved");
c->ep3_prev_battle_record.reset();
}
@@ -1080,8 +1109,8 @@ static void server_command_playrec(shared_ptr<Client> c, const std::string& args
string data;
try {
data = load_file(file_path);
} catch (const cannot_open_file&) {
data = phosg::load_file(file_path);
} catch (const phosg::cannot_open_file&) {
send_text_message(c, "$C4The recording does\nnot exist");
return;
}
@@ -1307,8 +1336,10 @@ static void server_command_edit(shared_ptr<Client> c, const std::string& args) {
bool cheats_allowed = ((s->cheat_mode_behavior != ServerState::BehaviorSwitch::OFF) ||
c->login->account->check_flag(Account::Flag::CHEAT_ANYWHERE));
string encoded_args = tolower(args);
vector<string> tokens = split(encoded_args, ' ');
string encoded_args = phosg::tolower(args);
vector<string> tokens = phosg::split(encoded_args, ' ');
using MatType = PSOBBCharacterFile::MaterialType;
try {
auto p = c->character();
@@ -1331,10 +1362,48 @@ static void server_command_edit(shared_ptr<Client> c, const std::string& args) {
} else if (tokens.at(0) == "exp" && cheats_allowed) {
p->disp.stats.experience = stoul(tokens.at(1));
} else if (tokens.at(0) == "level" && cheats_allowed) {
uint32_t level = stoul(tokens.at(1)) - 1;
auto level_table = s->level_table(c->version());
level_table->reset_to_base(p->disp.stats, p->disp.visual.char_class);
level_table->advance_to_level(p->disp.stats, level, p->disp.visual.char_class);
p->disp.stats.level = stoul(tokens.at(1)) - 1;
p->recompute_stats(s->level_table(c->version()));
} else if (((tokens.at(0) == "material") || (tokens.at(0) == "mat")) && !is_v1_or_v2(c->version())) {
if (tokens.at(1) == "reset") {
const auto& which = tokens.at(2);
if (which == "power") {
p->set_material_usage(MatType::POWER, 0);
} else if (which == "mind") {
p->set_material_usage(MatType::MIND, 0);
} else if (which == "evade") {
p->set_material_usage(MatType::EVADE, 0);
} else if (which == "def") {
p->set_material_usage(MatType::DEF, 0);
} else if (which == "luck") {
p->set_material_usage(MatType::LUCK, 0);
} else if (which == "hp") {
p->set_material_usage(MatType::HP, 0);
} else if (which == "tp") {
p->set_material_usage(MatType::TP, 0);
} else if (which == "all") {
p->set_material_usage(MatType::POWER, 0);
p->set_material_usage(MatType::MIND, 0);
p->set_material_usage(MatType::EVADE, 0);
p->set_material_usage(MatType::DEF, 0);
p->set_material_usage(MatType::LUCK, 0);
} else if (which == "every") {
p->set_material_usage(MatType::POWER, 0);
p->set_material_usage(MatType::MIND, 0);
p->set_material_usage(MatType::EVADE, 0);
p->set_material_usage(MatType::DEF, 0);
p->set_material_usage(MatType::LUCK, 0);
p->set_material_usage(MatType::HP, 0);
p->set_material_usage(MatType::TP, 0);
} else {
send_text_message(c, "$C6Invalid subcommand");
return;
}
} else {
send_text_message(c, "$C6Invalid subcommand");
return;
}
p->recompute_stats(s->level_table(c->version()));
} else if (tokens.at(0) == "namecolor") {
uint32_t new_color;
sscanf(tokens.at(1).c_str(), "%8X", &new_color);
@@ -1360,18 +1429,52 @@ static void server_command_edit(shared_ptr<Client> c, const std::string& args) {
p->disp.visual.section_id = secid;
}
} else if (tokens.at(0) == "name") {
vector<string> orig_tokens = split(args, ' ');
vector<string> orig_tokens = phosg::split(args, ' ');
p->disp.name.encode(orig_tokens.at(1), p->inventory.language);
} else if (tokens.at(0) == "npc") {
if (tokens.at(1) == "none") {
p->disp.visual.extra_model = 0;
p->disp.visual.validation_flags &= 0xFD;
// Restore saved fields, if any
if (p->disp.visual.unused[0] == 0x8D) {
p->disp.visual.char_class = p->disp.visual.unused[1];
p->disp.visual.head = p->disp.visual.unused[2];
p->disp.visual.hair = p->disp.visual.unused[3];
p->disp.visual.unused.clear(0);
}
} else {
uint8_t npc = npc_for_name(tokens.at(1));
if (npc == 0xFF) {
send_text_message(c, "$C6No such NPC");
return;
}
// Some NPCs can crash the client if the character's class is
// incorrect. To handle this, we save the affected fields in the unused
// bytes after extra_model.
int8_t replacement_class = -1;
switch (npc) {
case 1: // Rico (replace with HUnewearl)
case 6: // Elly (replace with HUnewearl)
replacement_class = 0x01;
break;
case 0: // Ninja (replace with HUmar)
case 5: // Flowen (replace with HUmar)
replacement_class = 0x00;
break;
}
if (replacement_class >= 0) {
if (p->disp.visual.unused[0] != 0x8D) {
p->disp.visual.unused[0] = 0x8D;
p->disp.visual.unused[1] = p->disp.visual.char_class;
p->disp.visual.unused[2] = p->disp.visual.head;
p->disp.visual.unused[3] = p->disp.visual.hair;
}
p->disp.visual.char_class = replacement_class;
p->disp.visual.head = 0x00;
p->disp.visual.hair = 0x00;
}
p->disp.visual.extra_model = npc;
p->disp.visual.validation_flags |= 0x02;
}
@@ -1440,7 +1543,11 @@ static void server_command_change_bank(shared_ptr<Client> c, const std::string&
throw runtime_error("invalid bank number");
}
const auto& bank = c->current_bank();
auto& bank = c->current_bank();
bank.assign_ids(0x99000000 + (c->lobby_client_id << 20));
c->log.info("Assigned bank item IDs");
c->print_bank(stderr);
send_text_message_printf(c, "%" PRIu32 " items\n%" PRIu32 " Meseta", bank.num_items.load(), bank.meseta.load());
}
@@ -1449,10 +1556,15 @@ static void server_command_bbchar_savechar(shared_ptr<Client> c, const std::stri
auto l = c->require_lobby();
check_is_game(l, false);
if (is_bb_conversion && is_ep3(c->version())) {
send_text_message(c, "$C6Episode 3 players\ncannot be converted\nto BB format");
return;
}
auto pending_export = make_unique<Client::PendingCharacterExport>();
if (is_bb_conversion) {
vector<string> tokens = split(args, ' ');
vector<string> tokens = phosg::split(args, ' ');
if (tokens.size() != 3) {
send_text_message(c, "$C6Incorrect argument count");
return;
@@ -1503,14 +1615,6 @@ static void server_command_savechar(shared_ptr<Client> c, const std::string& arg
}
static void server_command_loadchar(shared_ptr<Client> c, const std::string& args) {
if (!is_v1_or_v2(c->version()) &&
(c->version() != Version::GC_V3) &&
(c->version() != Version::GC_NTE) &&
(c->version() != Version::XB_V3) &&
(c->version() != Version::BB_V4)) {
send_text_message(c, "$C7This command cannot\nbe used on your\ngame version");
return;
}
if (c->login->account->check_flag(Account::Flag::IS_SHARED_ACCOUNT)) {
send_text_message(c, "$C7This command cannot\nbe used on a shared\naccount");
return;
@@ -1523,7 +1627,13 @@ static void server_command_loadchar(shared_ptr<Client> c, const std::string& arg
send_text_message(c, "$C6Player index must\nbe in range 1-16");
return;
}
c->load_backup_character(c->login->account->account_id, index);
shared_ptr<PSOGCEp3CharacterFile::Character> ep3_char;
if (is_ep3(c->version())) {
ep3_char = c->load_ep3_backup_character(c->login->account->account_id, index);
} else {
c->load_backup_character(c->login->account->account_id, index);
}
if (c->version() == Version::BB_V4) {
// On BB, it suffices to simply send the character file again
@@ -1535,10 +1645,12 @@ static void server_command_loadchar(shared_ptr<Client> c, const std::string& arg
} else if ((c->version() == Version::DC_V2) ||
(c->version() == Version::GC_NTE) ||
(c->version() == Version::GC_V3) ||
(c->version() == Version::GC_EP3_NTE) ||
(c->version() == Version::GC_EP3) ||
(c->version() == Version::XB_V3)) {
// TODO: Support extended player info on other versions
auto s = c->require_server_state();
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL) ||
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) ||
c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
send_text_message_printf(c, "Can\'t load character\ndata on this game\nversion");
return;
@@ -1582,6 +1694,11 @@ static void server_command_loadchar(shared_ptr<Client> c, const std::string& arg
} else if (c->version() == Version::GC_V3) {
auto gc_char = make_shared<PSOGCCharacterFile::Character>(c->character()->to_gc());
send_set_extended_player_info.operator()<PSOGCCharacterFile::Character>(c, gc_char);
} else if (c->version() == Version::GC_EP3_NTE) {
auto nte_char = make_shared<PSOGCEp3NTECharacter>(*ep3_char);
send_set_extended_player_info.operator()<PSOGCEp3NTECharacter>(c, nte_char);
} else if (c->version() == Version::GC_EP3) {
send_set_extended_player_info.operator()<PSOGCEp3CharacterFile::Character>(c, ep3_char);
} else if (c->version() == Version::XB_V3) {
if (!c->login || !c->login->xb_license) {
throw runtime_error("XB client is not logged in");
@@ -1622,7 +1739,7 @@ static string name_for_client(shared_ptr<Client> c) {
}
if (c->login) {
return string_printf("SN:%" PRIu32, c->login->account->account_id);
return phosg::string_printf("SN:%" PRIu32, c->login->account->account_id);
}
return "Player";
@@ -1717,7 +1834,7 @@ static void server_command_ban(shared_ptr<Client> c, const std::string& args) {
usecs *= 60 * 60 * 24 * 365;
}
target->login->account->ban_end_time = now() + usecs;
target->login->account->ban_end_time = phosg::now() + usecs;
target->login->account->save();
send_message_box(target, "$C6You have been banned.");
target->should_disconnect = true;
@@ -2125,7 +2242,7 @@ static void proxy_command_item(shared_ptr<ProxyServer::LinkedSession> ses, const
bool set_drop = (!args.empty() && (args[0] == '!'));
ItemData item = s->parse_item_description(ses->version(), (set_drop ? args.substr(1) : args));
item.id = random_object<uint32_t>() | 0x80000000;
item.id = phosg::random_object<uint32_t>() | 0x80000000;
if (set_drop) {
ses->next_drop_item = item;
@@ -2214,7 +2331,7 @@ static void server_command_ep3_set_dice_range(shared_ptr<Client> c, const std::s
}
auto parse_dice_range = +[](const string& spec) -> uint8_t {
auto tokens = split(spec, '-');
auto tokens = phosg::split(spec, '-');
if (tokens.size() == 1) {
uint8_t v = stoull(spec);
return (v << 4) | (v & 0x0F);
@@ -2228,8 +2345,8 @@ static void server_command_ep3_set_dice_range(shared_ptr<Client> c, const std::s
uint8_t def_dice_range = 0;
uint8_t atk_dice_range_2v1 = 0;
uint8_t def_dice_range_2v1 = 0;
for (const auto& spec : split(args, ' ')) {
auto tokens = split(spec, ':');
for (const auto& spec : phosg::split(args, ' ')) {
auto tokens = phosg::split(spec, ':');
if (tokens.size() != 2) {
send_text_message(c, "$C6Invalid dice spec\nformat");
return;
@@ -2295,7 +2412,7 @@ static void server_command_ep3_replace_assist_card(shared_ptr<Client> c, const s
size_t client_id;
string card_name;
if (isdigit(args[0])) {
auto tokens = split(args, ' ', 1);
auto tokens = phosg::split(args, ' ', 1);
client_id = stoul(tokens.at(0), nullptr, 0) - 1;
card_name = tokens.at(1);
} else {
@@ -2398,7 +2515,7 @@ static void server_command_get_ep3_battle_stat(shared_ptr<Client> c, const std::
const char* rank_name = ps->stats.name_for_rank(rank);
send_text_message_printf(c, "$C7Score: %g\nRank: %hhu (%s)", score, rank, rank_name);
} else if (args == "duration") {
string s = format_duration(now() - l->ep3_server->battle_start_usecs);
string s = phosg::format_duration(phosg::now() - l->ep3_server->battle_start_usecs);
send_text_message_printf(c, "$C7Duration: %s", s.c_str());
} else if (args == "fcs-destroyed") {
send_text_message_printf(c, "$C7Team FCs destroyed:\n%" PRIu32, l->ep3_server->team_num_ally_fcs_destroyed[team_id]);
@@ -2482,6 +2599,7 @@ static const unordered_map<string, ChatCommandDefinition> chat_commands({
{"$itemnotifs", {server_command_item_notifs, proxy_command_item_notifs}},
{"$i", {server_command_item, proxy_command_item}},
{"$kick", {server_command_kick, nullptr}},
{"$killcount", {server_command_show_kill_count, nullptr}},
{"$li", {server_command_lobby_info, proxy_command_lobby_info}},
{"$ln", {server_command_lobby_type, proxy_command_lobby_type}},
{"$loadchar", {server_command_loadchar, nullptr}},
+7 -9
View File
@@ -7,18 +7,16 @@
#include <vector>
#include "Text.hh"
#include "Types.hh"
class Client;
template <bool IsBigEndian>
template <bool BE>
struct ChoiceSearchConfigT {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
U32T disabled = 1; // 0 = enabled, 1 = disabled. Unused in command C3
U32T<BE> disabled = 1; // 0 = enabled, 1 = disabled. Unused in command C3
struct Entry {
U16T parent_choice_id = 0;
U16T choice_id = 0;
U16T<BE> parent_choice_id = 0;
U16T<BE> choice_id = 0;
} __packed_ws__(Entry, 4);
parray<Entry, 5> entries;
@@ -31,8 +29,8 @@ struct ChoiceSearchConfigT {
return -1;
}
operator ChoiceSearchConfigT<!IsBigEndian>() const {
ChoiceSearchConfigT<!IsBigEndian> ret;
operator ChoiceSearchConfigT<!BE>() const {
ChoiceSearchConfigT<!BE> ret;
ret.disabled = this->disabled.load();
for (size_t z = 0; z < this->entries.size(); z++) {
auto& ret_e = ret.entries[z];
+109 -73
View File
@@ -31,6 +31,7 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
if (version == Version::BB_V4) {
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::SAVE_ENABLED);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
return;
}
@@ -41,32 +42,40 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
case Version::PC_PATCH:
case Version::BB_PATCH:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
break;
case Version::DC_NTE:
case Version::DC_V1_11_2000_PROTOTYPE:
case Version::DC_V1:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
break;
case Version::DC_V2:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case Version::PC_NTE:
case Version::PC_V2:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case Version::GC_NTE:
case Version::GC_V3:
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
break;
case Version::GC_EP3_NTE:
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
break;
case Version::GC_V3:
case Version::GC_EP3:
// Some of these versions have send_function_call and some don't; we
// have to set these flags later when we get sub_version
break;
case Version::XB_V3:
// TODO: Do all versions of XB need this flag? US does, at least.
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
default:
@@ -76,36 +85,37 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
case 0x20: // DCNTE, possibly also DCv1 JP
case 0x21: // DCv1 US
case 0x22: // DCv1 EU, 12/2000, and 01/2001, at 50Hz (presumably)
case 0x23: // DCv1 EU, 12/2000, and 01/2001, at 60Hz (presumably)
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
break;
case 0x22: // DCv1 EU 50Hz (presumably)
case 0x23: // DCv1 EU 60Hz (presumably)
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
break;
case 0x25: // DCv2 JP
case 0x26: // DCv2 US
case 0x27: // DCv2 EU 50Hz (presumably)
case 0x28: // DCv2 EU 60Hz (presumably)
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case 0x29: // PC
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case 0x30: // GC Ep1&2 GameJam demo, GC Ep1&2 Trial Edition, GC Ep1&2 JP v1.2, at least one version of XB
case 0x31: // GC Ep1&2 US v1.0, GC US v1.1, XB US
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
break;
case 0x32: // GC Ep1&2 EU 50Hz
case 0x33: // GC Ep1&2 EU 60Hz
case 0x34: // GC Ep1&2 JP v1.3
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
break;
case 0x35: // GC Ep1&2 JP v1.4 (Plus)
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
@@ -115,27 +125,24 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
case 0x36: // GC Ep1&2 US v1.2 (Plus)
case 0x39: // GC Ep1&2 JP v1.5 (Plus)
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
this->set_flag(Flag::CAN_RECEIVE_ENABLE_B2_QUEST);
break;
case 0x40: // GC Ep3 JP and Trial Edition (and BB)
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
// sub_version can't be used to tell JP final and Trial Edition apart; we
// instead look at header.flag in the 61 command and set the version then.
break;
case 0x41: // GC Ep3 US (and BB)
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case 0x42: // GC Ep3 EU 50Hz
case 0x43: // GC Ep3 EU 60Hz
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
this->set_flag(Flag::CAN_RECEIVE_ENABLE_B2_QUEST);
break;
default:
throw runtime_error(string_printf("unknown sub_version %" PRIX64, sub_version));
throw runtime_error(phosg::string_printf("unknown sub_version %" PRIX64, sub_version));
}
}
@@ -179,8 +186,8 @@ Client::Client(
ServerBehavior server_behavior)
: server(server),
id(next_id++),
log(string_printf("[C-%" PRIX64 "] ", this->id), client_log.min_level),
channel(bev, virtual_network_id, version, 1, nullptr, nullptr, this, "", TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN),
log(phosg::string_printf("[C-%" PRIX64 "] ", this->id), client_log.min_level),
channel(bev, virtual_network_id, version, 1, nullptr, nullptr, this, "", phosg::TerminalFormat::FG_YELLOW, phosg::TerminalFormat::FG_GREEN),
server_behavior(server_behavior),
should_disconnect(false),
should_send_to_lobby_server(false),
@@ -239,8 +246,8 @@ Client::Client(
// more annoying than helpful at this point.
if ((s->hide_download_commands) &&
((this->channel.version == Version::PC_PATCH) || (this->channel.version == Version::BB_PATCH))) {
this->channel.terminal_recv_color = TerminalFormat::END;
this->channel.terminal_send_color = TerminalFormat::END;
this->channel.terminal_recv_color = phosg::TerminalFormat::END;
this->channel.terminal_send_color = phosg::TerminalFormat::END;
}
this->log.info("Created");
@@ -267,24 +274,24 @@ void Client::update_channel_name() {
auto player = this->character(false, false);
if (player) {
string name_str = player->disp.name.decode(this->language());
this->channel.name = string_printf("C-%" PRIX64 " (%s) @ %s", this->id, name_str.c_str(), ip_str.c_str());
this->channel.name = phosg::string_printf("C-%" PRIX64 " (%s) @ %s", this->id, name_str.c_str(), ip_str.c_str());
} else {
this->channel.name = string_printf("C-%" PRIX64 " @ %s", this->id, ip_str.c_str());
this->channel.name = phosg::string_printf("C-%" PRIX64 " @ %s", this->id, ip_str.c_str());
}
}
void Client::reschedule_save_game_data_event() {
if (this->version() == Version::BB_V4) {
struct timeval tv = usecs_to_timeval(60000000); // 1 minute
struct timeval tv = phosg::usecs_to_timeval(60000000); // 1 minute
event_add(this->save_game_data_event.get(), &tv);
}
}
void Client::reschedule_ping_and_timeout_events() {
auto s = this->require_server_state();
struct timeval ping_tv = usecs_to_timeval(s->client_ping_interval_usecs);
struct timeval ping_tv = phosg::usecs_to_timeval(s->client_ping_interval_usecs);
event_add(this->send_ping_event.get(), &ping_tv);
struct timeval idle_tv = usecs_to_timeval(s->client_idle_timeout_usecs);
struct timeval idle_tv = phosg::usecs_to_timeval(s->client_idle_timeout_usecs);
event_add(this->idle_timeout_event.get(), &idle_tv);
}
@@ -384,7 +391,7 @@ bool Client::evaluate_quest_availability_expression(
.v1_present = v1_present,
};
int64_t ret = expr->evaluate(env);
if (this->log.should_log(LogLevel::INFO)) {
if (this->log.should_log(phosg::LogLevel::INFO)) {
string expr_str = expr->str();
this->log.info("Evaluated integral expression %s => %s", expr_str.c_str(), ret ? "TRUE" : "FALSE");
}
@@ -442,7 +449,7 @@ void Client::send_ping() {
if (!is_patch(this->version())) {
this->log.info("Sending ping command");
// The game doesn't use this timestamp; we only use it for debugging purposes
be_uint64_t timestamp = now();
be_uint64_t timestamp = phosg::now();
try {
this->channel.send(0x1D, 0x00, &timestamp, sizeof(be_uint64_t));
} catch (const exception& e) {
@@ -634,7 +641,7 @@ string Client::system_filename() const {
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return string_printf("system/players/system_%s.psosys", this->login->bb_license->username.c_str());
return phosg::string_printf("system/players/system_%s.psosys", this->login->bb_license->username.c_str());
}
string Client::character_filename(const std::string& bb_username, int8_t index) {
@@ -644,11 +651,12 @@ string Client::character_filename(const std::string& bb_username, int8_t index)
if (index < 0) {
throw logic_error("character index is not set");
}
return string_printf("system/players/player_%s_%hhd.psochar", bb_username.c_str(), index);
return phosg::string_printf("system/players/player_%s_%hhd.psochar", bb_username.c_str(), index);
}
string Client::backup_character_filename(uint32_t account_id, size_t index) {
return string_printf("system/players/backup_player_%" PRIu32 "_%zu.psochar", account_id, index);
string Client::backup_character_filename(uint32_t account_id, size_t index, bool is_ep3) {
return phosg::string_printf("system/players/backup_player_%" PRIu32 "_%zu.%s",
account_id, index, is_ep3 ? "pso3char" : "psochar");
}
string Client::character_filename(int8_t index) const {
@@ -668,7 +676,7 @@ string Client::guild_card_filename() const {
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return string_printf("system/players/guild_cards_%s.psocard", this->login->bb_license->username.c_str());
return phosg::string_printf("system/players/guild_cards_%s.psocard", this->login->bb_license->username.c_str());
}
string Client::shared_bank_filename() const {
@@ -678,7 +686,7 @@ string Client::shared_bank_filename() const {
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return string_printf("system/players/shared_bank_%s.psobank", this->login->bb_license->username.c_str());
return phosg::string_printf("system/players/shared_bank_%s.psobank", this->login->bb_license->username.c_str());
}
string Client::legacy_account_filename() const {
@@ -688,7 +696,7 @@ string Client::legacy_account_filename() const {
if (!this->login || !this->login->bb_license) {
throw logic_error("client is not logged in");
}
return string_printf("system/players/account_%s.nsa", this->login->bb_license->username.c_str());
return phosg::string_printf("system/players/account_%s.nsa", this->login->bb_license->username.c_str());
}
string Client::legacy_player_filename() const {
@@ -701,7 +709,7 @@ string Client::legacy_player_filename() const {
if (this->bb_character_index < 0) {
throw logic_error("character index is not set");
}
return string_printf(
return phosg::string_printf(
"system/players/player_%s_%hhd.nsc",
this->login->bb_license->username.c_str(),
static_cast<int8_t>(this->bb_character_index + 1));
@@ -737,8 +745,8 @@ void Client::load_all_files() {
this->system_data = files_manager->get_system(sys_filename);
if (this->system_data) {
player_data_log.info("Using loaded system file %s", sys_filename.c_str());
} else if (isfile(sys_filename)) {
this->system_data = make_shared<PSOBBBaseSystemFile>(load_object_file<PSOBBBaseSystemFile>(sys_filename, true));
} else if (phosg::isfile(sys_filename)) {
this->system_data = make_shared<PSOBBBaseSystemFile>(phosg::load_object_file<PSOBBBaseSystemFile>(sys_filename, true));
files_manager->set_system(sys_filename, this->system_data);
player_data_log.info("Loaded system data from %s", sys_filename.c_str());
} else {
@@ -750,9 +758,9 @@ void Client::load_all_files() {
this->character_data = files_manager->get_character(char_filename);
if (this->character_data) {
player_data_log.info("Using loaded character file %s", char_filename.c_str());
} else if (isfile(char_filename)) {
auto f = fopen_unique(char_filename, "rb");
auto header = freadx<PSOCommandHeaderBB>(f.get());
} else if (phosg::isfile(char_filename)) {
auto f = phosg::fopen_unique(char_filename, "rb");
auto header = phosg::freadx<PSOCommandHeaderBB>(f.get());
if (header.size != 0x399C) {
throw runtime_error("incorrect size in character file header");
}
@@ -763,14 +771,14 @@ void Client::load_all_files() {
throw runtime_error("incorrect flag in character file header");
}
static_assert(sizeof(PSOBBCharacterFile) + sizeof(PSOBBFullSystemFile) == 0x3994, ".psochar size is incorrect");
this->character_data = make_shared<PSOBBCharacterFile>(freadx<PSOBBCharacterFile>(f.get()));
this->character_data = make_shared<PSOBBCharacterFile>(phosg::freadx<PSOBBCharacterFile>(f.get()));
files_manager->set_character(char_filename, this->character_data);
player_data_log.info("Loaded character data from %s", char_filename.c_str());
// If there was no .psosys file, load the system file from the .psochar
// file instead
if (!this->system_data) {
this->system_data = make_shared<PSOBBBaseSystemFile>(freadx<PSOBBBaseSystemFile>(f.get()));
this->system_data = make_shared<PSOBBBaseSystemFile>(phosg::freadx<PSOBBBaseSystemFile>(f.get()));
files_manager->set_system(sys_filename, this->system_data);
player_data_log.info("Loaded system data from %s", char_filename.c_str());
}
@@ -787,8 +795,8 @@ void Client::load_all_files() {
this->guild_card_data = files_manager->get_guild_card(card_filename);
if (this->guild_card_data) {
player_data_log.info("Using loaded Guild Card file %s", card_filename.c_str());
} else if (isfile(card_filename)) {
this->guild_card_data = make_shared<PSOBBGuildCardFile>(load_object_file<PSOBBGuildCardFile>(card_filename));
} else if (phosg::isfile(card_filename)) {
this->guild_card_data = make_shared<PSOBBGuildCardFile>(phosg::load_object_file<PSOBBGuildCardFile>(card_filename));
files_manager->set_guild_card(card_filename, this->guild_card_data);
player_data_log.info("Loaded Guild Card data from %s", card_filename.c_str());
} else {
@@ -799,8 +807,8 @@ void Client::load_all_files() {
if (!this->system_data || (!this->character_data && (this->bb_character_index >= 0)) || !this->guild_card_data) {
string nsa_filename = this->legacy_account_filename();
shared_ptr<LegacySavedAccountDataBB> nsa_data;
if (isfile(nsa_filename)) {
nsa_data = make_shared<LegacySavedAccountDataBB>(load_object_file<LegacySavedAccountDataBB>(nsa_filename));
if (phosg::isfile(nsa_filename)) {
nsa_data = make_shared<LegacySavedAccountDataBB>(phosg::load_object_file<LegacySavedAccountDataBB>(nsa_filename));
if (!nsa_data->signature.eq(LegacySavedAccountDataBB::SIGNATURE)) {
throw runtime_error("account data header is incorrect");
}
@@ -818,6 +826,13 @@ void Client::load_all_files() {
if (!this->system_data) {
this->system_data = make_shared<PSOBBBaseSystemFile>();
auto s = this->require_server_state();
if (s->bb_default_keyboard_config) {
this->system_data->key_config = *s->bb_default_keyboard_config;
}
if (s->bb_default_joystick_config) {
this->system_data->joystick_config = *s->bb_default_joystick_config;
}
files_manager->set_system(sys_filename, this->system_data);
player_data_log.info("Created new system data");
}
@@ -829,7 +844,7 @@ void Client::load_all_files() {
if (!this->character_data && (this->bb_character_index >= 0)) {
string nsc_filename = this->legacy_player_filename();
auto nsc_data = load_object_file<LegacySavedPlayerDataBB>(nsc_filename);
auto nsc_data = phosg::load_object_file<LegacySavedPlayerDataBB>(nsc_filename);
if (nsc_data.signature == LegacySavedPlayerDataBB::SIGNATURE_V0) {
nsc_data.signature = LegacySavedPlayerDataBB::SIGNATURE_V0;
nsc_data.unused.clear();
@@ -885,7 +900,7 @@ void Client::load_all_files() {
this->character_data->disp.name.clear_after_bytes(0x18);
this->login->account->auto_reply_message = this->character_data->auto_reply.decode();
this->login->account->save();
this->last_play_time_update = now();
this->last_play_time_update = phosg::now();
}
}
@@ -910,7 +925,7 @@ void Client::save_all() {
}
if (this->external_bank) {
string filename = this->shared_bank_filename();
save_object_file<PlayerBank200>(filename, *this->external_bank);
phosg::save_object_file<PlayerBank200>(filename, *this->external_bank);
player_data_log.info("Saved shared bank file %s", filename.c_str());
}
if (this->external_bank_character) {
@@ -926,7 +941,7 @@ void Client::save_system_file() const {
throw logic_error("no system file loaded");
}
string filename = this->system_filename();
save_object_file(filename, *this->system_data);
phosg::save_object_file(filename, *this->system_data);
player_data_log.info("Saved system file %s", filename.c_str());
}
@@ -934,11 +949,11 @@ void Client::save_character_file(
const string& filename,
shared_ptr<const PSOBBBaseSystemFile> system,
shared_ptr<const PSOBBCharacterFile> character) {
auto f = fopen_unique(filename, "wb");
auto f = phosg::fopen_unique(filename, "wb");
PSOCommandHeaderBB header = {sizeof(PSOCommandHeaderBB) + sizeof(PSOBBCharacterFile) + sizeof(PSOBBBaseSystemFile) + sizeof(PSOBBTeamMembership), 0x00E7, 0x00000000};
fwritex(f.get(), header);
fwritex(f.get(), *character);
fwritex(f.get(), *system);
phosg::fwritex(f.get(), header);
phosg::fwritex(f.get(), *character);
phosg::fwritex(f.get(), *system);
// TODO: Technically, we should write the actual team membership struct to the
// file here, but that would cause Client to depend on Account, which
// it currently does not. This data doesn't matter at all for correctness
@@ -950,10 +965,17 @@ void Client::save_character_file(
// of teams with a different set of team IDs anyway, so the membership struct
// here would be useless either way.
static const PSOBBTeamMembership empty_membership;
fwritex(f.get(), empty_membership);
phosg::fwritex(f.get(), empty_membership);
player_data_log.info("Saved character file %s", filename.c_str());
}
void Client::save_ep3_character_file(
const string& filename,
const PSOGCEp3CharacterFile::Character& character) {
phosg::save_file(filename, &character, sizeof(character));
player_data_log.info("Saved Episode 3 character file %s", filename.c_str());
}
void Client::save_character_file() {
if (!this->system_data.get()) {
throw logic_error("no system file loaded");
@@ -964,7 +986,7 @@ void Client::save_character_file() {
if (this->should_update_play_time) {
// This is slightly inaccurate, since fractions of a second are truncated
// off each time we save. I'm lazy, so insert shrug emoji here.
uint64_t t = now();
uint64_t t = phosg::now();
uint64_t seconds = (t - this->last_play_time_update) / 1000000;
this->character_data->play_time_seconds += seconds;
player_data_log.info("Added %" PRIu64 " seconds to play time", seconds);
@@ -979,14 +1001,14 @@ void Client::save_guild_card_file() const {
throw logic_error("no Guild Card file loaded");
}
string filename = this->guild_card_filename();
save_object_file(filename, *this->guild_card_data);
phosg::save_object_file(filename, *this->guild_card_data);
player_data_log.info("Saved Guild Card file %s", filename.c_str());
}
void Client::load_backup_character(uint32_t account_id, size_t index) {
string filename = this->backup_character_filename(account_id, index);
auto f = fopen_unique(filename, "rb");
auto header = freadx<PSOCommandHeaderBB>(f.get());
string filename = this->backup_character_filename(account_id, index, false);
auto f = phosg::fopen_unique(filename, "rb");
auto header = phosg::freadx<PSOCommandHeaderBB>(f.get());
if (header.size != 0x399C) {
throw runtime_error("incorrect size in character file header");
}
@@ -996,11 +1018,21 @@ void Client::load_backup_character(uint32_t account_id, size_t index) {
if (header.flag != 0x00000000) {
throw runtime_error("incorrect flag in character file header");
}
this->character_data = make_shared<PSOBBCharacterFile>(freadx<PSOBBCharacterFile>(f.get()));
this->character_data = make_shared<PSOBBCharacterFile>(phosg::freadx<PSOBBCharacterFile>(f.get()));
this->update_character_data_after_load(this->character_data);
this->v1_v2_last_reported_disp.reset();
}
shared_ptr<PSOGCEp3CharacterFile::Character> Client::load_ep3_backup_character(uint32_t account_id, size_t index) {
string filename = this->backup_character_filename(account_id, index, true);
auto ch = make_shared<PSOGCEp3CharacterFile::Character>(phosg::load_object_file<PSOGCEp3CharacterFile::Character>(filename));
this->character_data = PSOBBCharacterFile::create_from_ep3(*ch);
this->ep3_config = make_shared<Episode3::PlayerConfig>(ch->ep3_config);
this->update_character_data_after_load(this->character_data);
this->v1_v2_last_reported_disp.reset();
return ch;
}
void Client::save_and_unload_character() {
if (this->character_data) {
this->save_character_file();
@@ -1018,6 +1050,10 @@ PlayerBank200& Client::current_bank() {
return this->character()->bank;
}
const PlayerBank200& Client::current_bank() const {
return const_cast<Client*>(this)->current_bank();
}
std::shared_ptr<PSOBBCharacterFile> Client::current_bank_character() {
return this->external_bank_character ? this->external_bank_character : this->character();
}
@@ -1025,7 +1061,7 @@ std::shared_ptr<PSOBBCharacterFile> Client::current_bank_character() {
void Client::use_default_bank() {
if (this->external_bank) {
string filename = this->shared_bank_filename();
save_object_file<PlayerBank200>(filename, *this->external_bank);
phosg::save_object_file<PlayerBank200>(filename, *this->external_bank);
this->external_bank.reset();
player_data_log.info("Detached shared bank %s", filename.c_str());
}
@@ -1046,8 +1082,8 @@ bool Client::use_shared_bank() {
if (this->external_bank) {
player_data_log.info("Using loaded shared bank %s", filename.c_str());
return true;
} else if (isfile(filename)) {
this->external_bank = make_shared<PlayerBank200>(load_object_file<PlayerBank200>(filename));
} else if (phosg::isfile(filename)) {
this->external_bank = make_shared<PlayerBank200>(phosg::load_object_file<PlayerBank200>(filename));
files_manager->set_bank(filename, this->external_bank);
player_data_log.info("Loaded shared bank %s", filename.c_str());
return true;
@@ -1069,9 +1105,9 @@ void Client::use_character_bank(int8_t index) {
if (this->external_bank_character) {
this->external_bank_character_index = index;
player_data_log.info("Using loaded character file %s for external bank", filename.c_str());
} else if (isfile(filename)) {
auto f = fopen_unique(filename, "rb");
auto header = freadx<PSOCommandHeaderBB>(f.get());
} else if (phosg::isfile(filename)) {
auto f = phosg::fopen_unique(filename, "rb");
auto header = phosg::freadx<PSOCommandHeaderBB>(f.get());
if (header.size != 0x399C) {
throw runtime_error("incorrect size in character file header");
}
@@ -1081,7 +1117,7 @@ void Client::use_character_bank(int8_t index) {
if (header.flag != 0x00000000) {
throw runtime_error("incorrect flag in character file header");
}
this->external_bank_character = make_shared<PSOBBCharacterFile>(freadx<PSOBBCharacterFile>(f.get()));
this->external_bank_character = make_shared<PSOBBCharacterFile>(phosg::freadx<PSOBBCharacterFile>(f.get()));
this->update_character_data_after_load(this->external_bank_character);
this->external_bank_character_index = index;
files_manager->set_character(filename, this->external_bank_character);
@@ -1107,11 +1143,11 @@ void Client::print_inventory(FILE* stream) const {
void Client::print_bank(FILE* stream) const {
auto s = this->require_server_state();
auto p = this->character();
fprintf(stream, "[PlayerBank] Meseta: %" PRIu32 "\n", p->bank.meseta.load());
fprintf(stream, "[PlayerBank] %" PRIu32 " items\n", p->bank.num_items.load());
for (size_t x = 0; x < p->bank.num_items; x++) {
const auto& item = p->bank.items[x];
auto bank = this->current_bank();
fprintf(stream, "[PlayerBank] Meseta: %" PRIu32 "\n", bank.meseta.load());
fprintf(stream, "[PlayerBank] %" PRIu32 " items\n", bank.num_items.load());
for (size_t x = 0; x < bank.num_items; x++) {
const auto& item = bank.items[x];
const char* present_token = item.present ? "" : " (missing present flag)";
auto hex = item.data.hex();
auto name = s->describe_item(this->version(), item.data, false);
+14 -7
View File
@@ -35,7 +35,7 @@ public:
// TODO: It'd be nice to use a pattern here (e.g. all server-side flags are
// in the high bits) but that would require re-recording or manually
// rewriting all the tests
CLIENT_SIDE_MASK = 0xFF3CFFFF7C0FFFFB,
CLIENT_SIDE_MASK = 0xFF3CFFFF7C0BFFFB,
// Version-related flags
CHECKED_FOR_DC_V1_PROTOTYPE = 0x0000000000000002,
@@ -44,11 +44,12 @@ public:
FORCE_ENGLISH_LANGUAGE_BB = 0x0000000000000400,
// Flags describing the behavior for send_function_call
NO_SEND_FUNCTION_CALL = 0x0000000000001000,
HAS_SEND_FUNCTION_CALL = 0x0000000000001000,
ENCRYPTED_SEND_FUNCTION_CALL = 0x0000000000002000,
SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x0000000000004000,
SEND_FUNCTION_CALL_NO_CACHE_PATCH = 0x0000000000008000,
USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x0000000000010000,
CAN_RECEIVE_ENABLE_B2_QUEST = 0x0000000000020000,
AWAITING_ENABLE_B2_QUEST = 0x0000000000040000, // Server-side only
// State flags
LOADING = 0x0000000000100000, // Server-side only
@@ -93,6 +94,7 @@ public:
PROXY_BLANK_NAME_ENABLED = 0x0000400000000000,
PROXY_BLOCK_FUNCTION_CALLS = 0x0000800000000000,
PROXY_EP3_UNMASK_WHISPERS = 0x0008000000000000,
PROXY_VIRTUAL_CLIENT = 0x0400000000000000,
// clang-format on
};
enum class ItemDropNotificationMode {
@@ -145,7 +147,7 @@ public:
template <size_t Bytes>
void parse_from(const parray<uint8_t, Bytes>& data) {
StringReader r(data.data(), data.size());
phosg::StringReader r(data.data(), data.size());
if (r.get_u32l() != CLIENT_CONFIG_MAGIC) {
throw std::invalid_argument("config signature is incorrect");
}
@@ -161,7 +163,7 @@ public:
template <size_t Bytes>
void serialize_into(parray<uint8_t, Bytes>& data) const {
StringWriter w;
phosg::StringWriter w;
w.put_u32l(CLIENT_CONFIG_MAGIC);
w.put_u32l(this->specific_version);
w.put_u64l(this->enabled_flags & static_cast<uint64_t>(Flag::CLIENT_SIDE_MASK));
@@ -182,7 +184,7 @@ public:
std::weak_ptr<Server> server;
uint64_t id;
PrefixedLogger log;
phosg::PrefixedLogger log;
std::shared_ptr<Login> login;
@@ -362,7 +364,7 @@ public:
std::string system_filename() const;
static std::string character_filename(const std::string& bb_username, int8_t index);
static std::string backup_character_filename(uint32_t account_id, size_t index);
static std::string backup_character_filename(uint32_t account_id, size_t index, bool is_ep3);
std::string character_filename(int8_t index = -1) const;
std::string guild_card_filename() const;
std::string shared_bank_filename() const;
@@ -376,14 +378,19 @@ public:
const std::string& filename,
std::shared_ptr<const PSOBBBaseSystemFile> sys,
std::shared_ptr<const PSOBBCharacterFile> character);
static void save_ep3_character_file(
const std::string& filename,
const PSOGCEp3CharacterFile::Character& character);
// Note: This function is not const because it updates the player's play time.
void save_character_file();
void save_guild_card_file() const;
void load_backup_character(uint32_t account_id, size_t index);
std::shared_ptr<PSOGCEp3CharacterFile::Character> load_ep3_backup_character(uint32_t account_id, size_t index);
void save_and_unload_character();
PlayerBank200& current_bank();
const PlayerBank200& current_bank() const;
std::shared_ptr<PSOBBCharacterFile> current_bank_character();
bool use_shared_bank(); // Returns true if the bank exists; false if it was created
void use_character_bank(int8_t bb_character_index);
+72 -48
View File
@@ -430,8 +430,7 @@ struct C_LegacyLogin_BB_04 {
// Any other nonzero value = Generic failure (101)
// The client config field in this command is ignored by pre-V3 clients as well
// as Episodes 1&2 Trial Edition. All other V3 clients save it as opaque data to
// be returned in a 9E or 9F command later. newserv sends the client config
// anyway to clients that ignore it.
// be returned in a 9E or 9F command later.
// The client will respond with a 96 command, but only the first time it
// receives this command - for later 04 commands, the client will still update
// its client config but will not respond. Changing the security data at any
@@ -718,16 +717,19 @@ check_struct_size(C_MenuSelection_PC_BB_10_Flag03, 0x48);
// Internal name: RcvDownLoad
// Used for downloading online quests. For download quests (to be saved to the
// memory card), use A7 instead.
// This command exists on DC NTE, but it does nothing. DC NTE does not have the
// 44 command, which would also be required for loading quests, so online
// quests canot be loaded on DC NTE.
// All chunks except the last must have 0x400 data bytes. When downloading an
// online quest, the .bin and .dat chunks may be interleaved (although newserv
// currently sends them sequentially). There is a client bug in BB (and
// probably all other versions) where if the quest file's size is a multiple
// of 0x400, the last chunk will have size 0x400, and the client will never
// consider the download complete since it only checks if the last chunk has
// size < 0x400; it does not check if all expected bytes have been received.
// To work around this, newserv appends an extra zero byte if the quest file's
// size is a multiple of 0x400; this byte will be ignored since the PRS
// decompression algorithm contains a stop command, so it will never read it.
// online quest, the .bin and .dat chunks may be interleaved. There is a client
// bug in BB (and probably all other versions) where if the quest file's size
// is a multiple of 0x400, the last chunk will have size 0x400, and the client
// will never consider the download complete since it only checks if the last
// chunk has size < 0x400; it does not check if all expected bytes have been
// received. To work around this, newserv appends an extra zero byte if the
// quest file's size is a multiple of 0x400; this byte will be ignored since
// the PRS decompression algorithm contains a stop command, so it will never
// read it.
// header.flag = file chunk index (start offset / 0x400)
struct S_WriteFile_13_A7 {
@@ -975,6 +977,8 @@ check_struct_size(S_GuildCardSearchResult_BB_41, 0x128);
// Internal name: RcvDownLoadHead
// Used for downloading online quests. The client will react to a 44 command if
// the filename ends in .bin or .dat.
// This command is not implemented on DC NTE, so DC NTE cannot receive online
// quest files.
// For download quests (to be saved to the memory card) and GBA games, the A6
// command is used instead. The client will react to A6 if the filename ends in
// .bin/.dat (quests), .pvr (textures), or .gba (GameBoy Advance games).
@@ -1456,26 +1460,33 @@ struct SC_SimpleMail_PC_81 {
// replied to.
le_uint32_t player_tag = 0x00010000;
le_uint32_t from_guild_card_number = 0;
pstring<TextEncoding::UTF16, 0x10> from_name;
pstring<TextEncoding::UTF16, 0x0F> from_name;
le_uint16_t from_name_term = 0;
le_uint32_t to_guild_card_number = 0;
pstring<TextEncoding::UTF16, 0x200> text;
pstring<TextEncoding::UTF16, 0x1FF> text;
le_uint16_t text_term = 0;
} __packed_ws__(SC_SimpleMail_PC_81, 0x42C);
struct SC_SimpleMail_DC_V3_81 {
le_uint32_t player_tag = 0x00010000;
le_uint32_t from_guild_card_number = 0;
pstring<TextEncoding::MARKED, 0x10> from_name;
pstring<TextEncoding::MARKED, 0x0F> from_name;
uint8_t from_name_term = 0;
le_uint32_t to_guild_card_number = 0;
pstring<TextEncoding::MARKED, 0x200> text;
pstring<TextEncoding::MARKED, 0x1FF> text;
uint8_t text_term = 0;
} __packed_ws__(SC_SimpleMail_DC_V3_81, 0x21C);
struct SC_SimpleMail_BB_81 {
le_uint32_t player_tag = 0x00010000;
le_uint32_t from_guild_card_number = 0;
pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> from_name;
pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x0F> from_name;
le_uint16_t from_name_term = 0;
le_uint32_t to_guild_card_number = 0;
pstring<TextEncoding::UTF16, 0x14> received_date;
pstring<TextEncoding::UTF16, 0x200> text;
pstring<TextEncoding::UTF16, 0x13> received_date;
le_uint16_t received_date_term = 0;
pstring<TextEncoding::UTF16, 0x1FF> text;
le_uint16_t text_term = 0;
} __packed_ws__(SC_SimpleMail_BB_81, 0x454);
// 82: Invalid command
@@ -1660,7 +1671,8 @@ struct C_LoginV1_DC_PC_V3_90 {
// 92 (C->S): Register (DC)
struct C_RegisterV1_DC_92 {
parray<uint8_t, 0x0C> unknown_a1;
parray<uint8_t, 0x08> unknown_a1;
le_uint32_t sub_version;
uint8_t is_extended = 0; // TODO: This is a guess
uint8_t language = 0; // TODO: This is a guess; verify it
parray<uint8_t, 2> unknown_a3;
@@ -2224,10 +2236,9 @@ struct S_ExecuteCode_B2 {
// The code immediately follows, ending with an S_ExecuteCode_Footer_B2
} __packed_ws__(S_ExecuteCode_B2, 0x0C);
template <bool IsBigEndian>
template <bool BE>
struct S_ExecuteCode_FooterT_B2 {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
static constexpr bool IsBE = BE; // Needed by generate_client_command_t
// Relocations is a list of words (le_uint16_t on DC/PC/XB/BB, be_uint16_t on
// GC) containing the number of doublewords (uint32_t) to skip for each
@@ -2247,14 +2258,14 @@ struct S_ExecuteCode_FooterT_B2 {
// relocations_offset points there, so those 12 bytes may also be omitted from
// the command entirely (without changing code_size - so code_size would
// technically extend beyond the end of the B2 command).
U32T relocations_offset = 0; // Relative to code base (after checksum_size)
U32T num_relocations = 0;
parray<U32T, 2> unused1;
U32T<BE> relocations_offset = 0; // Relative to code base (after checksum_size)
U32T<BE> num_relocations = 0;
parray<U32T<BE>, 2> unused1;
// entrypoint_offset is doubly indirect - it points to a pointer to a 32-bit
// value that itself is the actual entrypoint. This is presumably done so the
// entrypoint can be optionally relocated.
U32T entrypoint_addr_offset = 0; // Relative to code base (after checksum_size).
parray<U32T, 3> unused2;
U32T<BE> entrypoint_addr_offset = 0; // Relative to code base (after checksum_size).
parray<U32T<BE>, 3> unused2;
} __packed__;
using S_ExecuteCode_Footer_GC_B2 = S_ExecuteCode_FooterT_B2<true>;
using S_ExecuteCode_Footer_DC_PC_XB_BB_B2 = S_ExecuteCode_FooterT_B2<false>;
@@ -3929,6 +3940,7 @@ struct G_SendGuildCard_BB_6x06 {
} __packed_ws__(G_SendGuildCard_BB_6x06, 0x10C);
// 6x07: Symbol chat
// If UDP mode is enabled, this command is sent via UDP.
struct G_SymbolChat_6x07 {
G_UnusedHeader header;
@@ -3946,7 +3958,7 @@ struct G_Unknown_6x09 {
// 6x0A: Update enemy state
template <bool IsBigEndian>
template <bool BE>
struct G_UpdateEnemyStateT_6x0A {
G_EnemyIDHeader header;
le_uint16_t enemy_index = 0; // [0, 0xB50)
@@ -3954,7 +3966,7 @@ struct G_UpdateEnemyStateT_6x0A {
// Flags:
// 00000400 - should play hit animation
// 00000800 - is dead
typename std::conditional_t<IsBigEndian, be_uint32_t, le_uint32_t> flags = 0;
typename std::conditional_t<BE, be_uint32_t, le_uint32_t> flags = 0;
} __packed__;
using G_UpdateEnemyState_GC_6x0A = G_UpdateEnemyStateT_6x0A<true>;
using G_UpdateEnemyState_DC_PC_XB_BB_6x0A = G_UpdateEnemyStateT_6x0A<false>;
@@ -4000,15 +4012,14 @@ struct G_Unknown_6x10_6x11_6x12_6x14 {
// 6x12: Dragon boss actions (not valid on Episode 3)
template <bool IsBigEndian>
template <bool BE>
struct G_DragonBossActionsT_6x12 {
using F32T = typename std::conditional<IsBigEndian, be_float, le_float>::type;
G_EnemyIDHeader header;
le_uint16_t unknown_a2 = 0;
le_uint16_t unknown_a3 = 0;
le_uint32_t unknown_a4 = 0;
F32T x = 0.0f;
F32T z = 0.0f;
F32T<BE> x = 0.0f;
F32T<BE> z = 0.0f;
} __packed__;
using G_DragonBossActions_DC_PC_XB_BB_6x12 = G_DragonBossActionsT_6x12<false>;
using G_DragonBossActions_GC_6x12 = G_DragonBossActionsT_6x12<true>;
@@ -4351,6 +4362,7 @@ struct G_SetPosition_6x3F {
} __packed_ws__(G_SetPosition_6x3F, 0x18);
// 6x40: Walk (protected on V3/V4)
// If UDP mode is enabled, this command is sent via UDP.
struct G_WalkToPosition_6x40 {
G_ClientIDHeader header;
@@ -4369,6 +4381,7 @@ struct G_Unknown_6x41 {
} __packed_ws__(G_Unknown_6x41, 0x0C);
// 6x42: Run (protected on V3/V4)
// If UDP mode is enabled, this command is sent via UDP.
struct G_RunToPosition_6x42 {
G_ClientIDHeader header;
@@ -4379,6 +4392,7 @@ struct G_RunToPosition_6x42 {
// 6x43: First attack (protected on V3/V4)
// 6x44: Second attack (protected on V3/V4)
// 6x45: Third attack (protected on V3/V4)
// If UDP mode is enabled, these commands are sent via UDP.
struct G_Attack_6x43_6x44_6x45 {
G_ClientIDHeader header;
@@ -4483,13 +4497,24 @@ struct G_PlayerRevived_6x4F {
} __packed_ws__(G_PlayerRevived_6x4F, 4);
// 6x50: Switch interaction (protected on V3/V4)
// If UDP mode is enabled, this command is sent via UDP.
struct G_SwitchInteraction_6x50 {
G_ClientIDHeader header;
le_uint32_t unknown_a1 = 0;
} __packed_ws__(G_SwitchInteraction_6x50, 8);
// 6x51: Invalid subcommand
// 6x51: Set player angle
// If UDP mode is enabled, this command is sent via UDP.
// This command appears to be vestigial - no version of the game has a handler
// for it (it is always ignored), but PSO GC has a function that sends it. It's
// not known if this function is ever called, or how to trigger it.
struct G_SetPlayerAngle_6x51 {
G_ClientIDHeader header;
le_uint16_t angle = 0;
parray<uint8_t, 2> unused;
} __packed_ws__(G_SetPlayerAngle_6x51, 8);
// 6x52: Set animation state (protected on V3/V4)
@@ -4543,6 +4568,7 @@ struct G_Unknown_6x57 {
} __packed_ws__(G_Unknown_6x57, 4);
// 6x58: Lobby animation (protected on V3/V4)
// If UDP mode is enabled, this command is sent via UDP.
struct G_LobbyAnimation_6x58 {
G_ClientIDHeader header;
@@ -4656,7 +4682,9 @@ struct G_ActivateMagEffect_6x61 {
// 6x62: Unknown
// This command has a handler, but it does nothing even on DC NTE.
// 6x63: Destroy floor item (used when too many items have been dropped)
// 6x63: Destroy floor item
// This is sent by the leader to destroy a floor item when there are 50 or more
// items already on the ground on the current floor.
struct G_DestroyFloorItem_6x5C_6x63 {
G_UnusedHeader header;
@@ -5021,12 +5049,11 @@ struct G_ExitQuest_6x73 {
// There is a bug in PSO GC with regard to this command: the client does not
// byteswap the header, which means the client_id field is big-endian.
template <bool IsBigEndian>
template <bool BE>
struct G_WordSelectT_6x74 {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
uint8_t subcommand = 0;
uint8_t size = 0;
U16T client_id = 0;
U16T<BE> client_id = 0;
WordSelectMessage message;
} __packed__;
using G_WordSelect_6x74 = G_WordSelectT_6x74<false>;
@@ -5148,14 +5175,12 @@ struct G_SetBattleModeData_6x7D {
// 6x7F: Battle scores and places (not valid on Episode 3)
template <bool IsBigEndian>
template <bool BE>
struct G_BattleScoresT_6x7F {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
struct Entry {
U16T client_id = 0;
U16T place = 0;
U32T score = 0;
U16T<BE> client_id = 0;
U16T<BE> place = 0;
U32T<BE> score = 0;
} __packed_ws__(Entry, 8);
G_UnusedHeader header;
parray<Entry, 4> entries;
@@ -5491,15 +5516,14 @@ struct G_ModifyTradeProposal_6xA6 {
// 6xA8: Gol Dragon boss actions (not valid on pre-V3 or Episode 3)
template <bool IsBigEndian>
template <bool BE>
struct G_GolDragonBossActionsT_6xA8 {
using F32T = typename std::conditional<IsBigEndian, be_float, le_float>::type;
G_EnemyIDHeader header;
le_uint16_t unknown_a2 = 0;
le_uint16_t unknown_a3 = 0;
le_uint32_t unknown_a4 = 0;
F32T x = 0.0f;
F32T z = 0.0f;
F32T<BE> x = 0.0f;
F32T<BE> z = 0.0f;
uint8_t unknown_a5 = 0;
parray<uint8_t, 3> unused;
} __packed__;
+43 -45
View File
@@ -4,12 +4,13 @@
#include "EnemyType.hh"
#include "GSLArchive.hh"
#include "StaticGameData.hh"
#include "Types.hh"
using namespace std;
template <typename IntT, size_t Count>
JSON to_json(const parray<IntT, Count>& v) {
auto ret = JSON::list();
phosg::JSON to_json(const parray<IntT, Count>& v) {
auto ret = phosg::JSON::list();
for (size_t z = 0; z < Count; z++) {
ret.emplace_back(v[z]);
}
@@ -17,7 +18,7 @@ JSON to_json(const parray<IntT, Count>& v) {
}
template <typename IntT, size_t Count>
void from_json_into(const JSON& json, parray<IntT, Count>& ret) {
void from_json_into(const phosg::JSON& json, parray<IntT, Count>& ret) {
if (json.size() != Count) {
throw runtime_error("incorrect array length");
}
@@ -27,8 +28,8 @@ void from_json_into(const JSON& json, parray<IntT, Count>& ret) {
}
template <typename IntT, size_t Count>
JSON to_json(const parray<CommonItemSet::Table::Range<IntT>, Count>& v) {
auto ret = JSON::list();
phosg::JSON to_json(const parray<CommonItemSet::Table::Range<IntT>, Count>& v) {
auto ret = phosg::JSON::list();
for (size_t z = 0; z < Count; z++) {
ret.emplace_back(to_json(v[z]));
}
@@ -36,7 +37,7 @@ JSON to_json(const parray<CommonItemSet::Table::Range<IntT>, Count>& v) {
}
template <typename IntT, size_t Count>
void from_json_into(const JSON& json, parray<CommonItemSet::Table::Range<IntT>, Count>& ret) {
void from_json_into(const phosg::JSON& json, parray<CommonItemSet::Table::Range<IntT>, Count>& ret) {
if (json.size() != Count) {
throw runtime_error("incorrect array length");
}
@@ -46,16 +47,16 @@ void from_json_into(const JSON& json, parray<CommonItemSet::Table::Range<IntT>,
}
template <typename IntT>
JSON to_json(const CommonItemSet::Table::Range<IntT>& v) {
phosg::JSON to_json(const CommonItemSet::Table::Range<IntT>& v) {
if (v.min == v.max) {
return JSON(v.min);
return phosg::JSON(v.min);
} else {
return JSON::list({v.min, v.max});
return phosg::JSON::list({v.min, v.max});
}
}
template <typename IntT>
void from_json_into(const JSON& json, CommonItemSet::Table::Range<IntT>& ret) {
void from_json_into(const phosg::JSON& json, CommonItemSet::Table::Range<IntT>& ret) {
if (json.is_int()) {
IntT v = json.as_int();
ret.min = v;
@@ -71,8 +72,8 @@ void from_json_into(const JSON& json, CommonItemSet::Table::Range<IntT>& ret) {
}
template <typename IntT, size_t Count1, size_t Count2>
JSON to_json(const parray<parray<IntT, Count2>, Count1>& v) {
auto ret = JSON::list();
phosg::JSON to_json(const parray<parray<IntT, Count2>, Count1>& v) {
auto ret = phosg::JSON::list();
for (size_t z = 0; z < Count1; z++) {
ret.emplace_back(to_json(v[z]));
}
@@ -80,7 +81,7 @@ JSON to_json(const parray<parray<IntT, Count2>, Count1>& v) {
}
template <typename IntT, size_t Count1, size_t Count2>
void from_json_into(const JSON& json, parray<parray<IntT, Count2>, Count1>& ret) {
void from_json_into(const phosg::JSON& json, parray<parray<IntT, Count2>, Count1>& ret) {
if (json.size() != Count1) {
throw runtime_error("incorrect array length");
}
@@ -90,7 +91,7 @@ void from_json_into(const JSON& json, parray<parray<IntT, Count2>, Count1>& ret)
}
template <typename IntT, size_t Count1, size_t Count2>
void from_json_into(const JSON& json, parray<parray<CommonItemSet::Table::Range<IntT>, Count2>, Count1>& ret) {
void from_json_into(const phosg::JSON& json, parray<parray<CommonItemSet::Table::Range<IntT>, Count2>, Count1>& ret) {
if (json.size() != Count1) {
throw runtime_error("incorrect array length");
}
@@ -99,7 +100,7 @@ void from_json_into(const JSON& json, parray<parray<CommonItemSet::Table::Range<
}
}
CommonItemSet::Table::Table(const JSON& json, Episode episode)
CommonItemSet::Table::Table(const phosg::JSON& json, Episode episode)
: episode(episode) {
from_json_into(json.at("BaseWeaponTypeProbTable"), this->base_weapon_type_prob_table);
from_json_into(json.at("SubtypeBaseTable"), this->subtype_base_table);
@@ -128,7 +129,7 @@ CommonItemSet::Table::Table(const JSON& json, Episode episode)
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
for (Episode episode : episodes) {
for (auto type : enemy_types_for_rare_table_index(episode, z)) {
string name = string_printf("%s:%s", abbreviation_for_episode(episode), name_for_enum(type));
string name = phosg::string_printf("%s:%s", abbreviation_for_episode(episode), phosg::name_for_enum(type));
from_json_into(*enemy_meseta_ranges_json.at(name), this->enemy_meseta_ranges[z]);
this->enemy_type_drop_probs[z] = enemy_type_drop_probs_json.at(name)->as_int();
this->enemy_item_classes[z] = enemy_item_classes_json.at(name)->as_int();
@@ -170,7 +171,7 @@ void CommonItemSet::Table::print(FILE* stream) const {
if (!enemies_str.empty()) {
enemies_str += ", ";
}
enemies_str += name_for_enum(enemy_type);
enemies_str += phosg::name_for_enum(enemy_type);
}
if (drop_probs[z]) {
fprintf(stream, " %02zX %5hu %5hu %3hhu%% %02hX:%s %s\n",
@@ -335,22 +336,22 @@ void CommonItemSet::Table::print(FILE* stream) const {
}
}
JSON CommonItemSet::Table::json() const {
JSON enemy_meseta_ranges_json = JSON::dict();
JSON enemy_type_drop_probs_json = JSON::dict();
JSON enemy_item_classes_json = JSON::dict();
phosg::JSON CommonItemSet::Table::json() const {
phosg::JSON enemy_meseta_ranges_json = phosg::JSON::dict();
phosg::JSON enemy_type_drop_probs_json = phosg::JSON::dict();
phosg::JSON enemy_item_classes_json = phosg::JSON::dict();
for (size_t z = 0; z < 0x64; z++) {
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
for (Episode episode : episodes) {
for (auto type : enemy_types_for_rare_table_index(episode, z)) {
string name = string_printf("%s:%s", abbreviation_for_episode(episode), name_for_enum(type));
string name = phosg::string_printf("%s:%s", abbreviation_for_episode(episode), phosg::name_for_enum(type));
enemy_meseta_ranges_json.emplace(name, to_json(this->enemy_meseta_ranges[z]));
enemy_type_drop_probs_json.emplace(name, this->enemy_type_drop_probs[z]);
enemy_item_classes_json.emplace(name, this->enemy_item_classes[z]);
}
}
}
return JSON::dict({
return phosg::JSON::dict({
{"BaseWeaponTypeProbTable", to_json(this->base_weapon_type_prob_table)},
{"SubtypeBaseTable", to_json(this->subtype_base_table)},
{"SubtypeAreaLengthTable", to_json(this->subtype_area_length_table)},
@@ -376,16 +377,16 @@ JSON CommonItemSet::Table::json() const {
});
}
JSON CommonItemSet::json() const {
auto modes_dict = JSON::dict();
phosg::JSON CommonItemSet::json() const {
auto modes_dict = phosg::JSON::dict();
static const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
for (const auto& mode : modes) {
auto episodes_dict = JSON::dict();
auto episodes_dict = phosg::JSON::dict();
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
for (const auto& episode : episodes) {
auto difficulty_dict = JSON::dict();
auto difficulty_dict = phosg::JSON::dict();
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
auto section_id_dict = JSON::dict();
auto section_id_dict = phosg::JSON::dict();
for (uint8_t section_id = 0; section_id < 10; section_id++) {
try {
auto table = this->get_table(episode, mode, difficulty, section_id);
@@ -423,7 +424,7 @@ void CommonItemSet::print(FILE* stream) const {
}
}
CommonItemSet::Table::Table(const StringReader& r, bool is_big_endian, bool is_v3, Episode episode)
CommonItemSet::Table::Table(const phosg::StringReader& r, bool is_big_endian, bool is_v3, Episode episode)
: episode(episode) {
if (is_big_endian) {
this->parse_itempt_t<true>(r, is_v3);
@@ -432,12 +433,9 @@ CommonItemSet::Table::Table(const StringReader& r, bool is_big_endian, bool is_v
}
}
template <bool IsBigEndian>
void CommonItemSet::Table::parse_itempt_t(const StringReader& r, bool is_v3) {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
const auto& offsets = r.pget<OffsetsT<IsBigEndian>>(r.pget<U32T>(r.size() - 0x10));
template <bool BE>
void CommonItemSet::Table::parse_itempt_t(const phosg::StringReader& r, bool is_v3) {
const auto& offsets = r.pget<OffsetsT<BE>>(r.pget<U32T<BE>>(r.size() - 0x10));
this->base_weapon_type_prob_table = r.pget<parray<uint8_t, 0x0C>>(offsets.base_weapon_type_prob_table_offset);
this->subtype_base_table = r.pget<parray<int8_t, 0x0C>>(offsets.subtype_base_table_offset);
@@ -445,14 +443,14 @@ void CommonItemSet::Table::parse_itempt_t(const StringReader& r, bool is_v3) {
this->grind_prob_table = r.pget<parray<parray<uint8_t, 4>, 9>>(offsets.grind_prob_table_offset);
this->armor_shield_type_index_prob_table = r.pget<parray<uint8_t, 0x05>>(offsets.armor_shield_type_index_prob_table_offset);
this->armor_slot_count_prob_table = r.pget<parray<uint8_t, 0x05>>(offsets.armor_slot_count_prob_table_offset);
const auto& data = r.pget<parray<Range<U16T>, 0x64>>(offsets.enemy_meseta_ranges_offset);
const auto& data = r.pget<parray<Range<U16T<BE>>, 0x64>>(offsets.enemy_meseta_ranges_offset);
for (size_t z = 0; z < data.size(); z++) {
this->enemy_meseta_ranges[z] = Range<uint16_t>{data[z].min, data[z].max};
}
this->enemy_type_drop_probs = r.pget<parray<uint8_t, 0x64>>(offsets.enemy_type_drop_probs_offset);
this->enemy_item_classes = r.pget<parray<uint8_t, 0x64>>(offsets.enemy_item_classes_offset);
{
const auto& data = r.pget<parray<Range<U16T>, 0x0A>>(offsets.box_meseta_ranges_offset);
const auto& data = r.pget<parray<Range<U16T<BE>>, 0x0A>>(offsets.box_meseta_ranges_offset);
for (size_t z = 0; z < data.size(); z++) {
this->box_meseta_ranges[z] = Range<uint16_t>{data[z].min, data[z].max};
}
@@ -466,7 +464,7 @@ void CommonItemSet::Table::parse_itempt_t(const StringReader& r, bool is_v3) {
}
}
} else { // V3
const auto& data = r.pget<parray<parray<U16T, 6>, 0x17>>(offsets.bonus_value_prob_table_offset);
const auto& data = r.pget<parray<parray<U16T<BE>, 6>, 0x17>>(offsets.bonus_value_prob_table_offset);
for (size_t z = 0; z < data.size(); z++) {
for (size_t x = 0; x < data[z].size(); x++) {
this->bonus_value_prob_table[z][x] = data[z][x];
@@ -478,7 +476,7 @@ void CommonItemSet::Table::parse_itempt_t(const StringReader& r, bool is_v3) {
this->special_mult = r.pget<parray<uint8_t, 0x0A>>(offsets.special_mult_offset);
this->special_percent = r.pget<parray<uint8_t, 0x0A>>(offsets.special_percent_offset);
{
const auto& data = r.pget<parray<parray<U16T, 0x0A>, 0x1C>>(offsets.tool_class_prob_table_offset);
const auto& data = r.pget<parray<parray<U16T<BE>, 0x0A>, 0x1C>>(offsets.tool_class_prob_table_offset);
for (size_t z = 0; z < data.size(); z++) {
for (size_t x = 0; x < data[z].size(); x++) {
this->tool_class_prob_table[z][x] = data[z][x];
@@ -505,7 +503,7 @@ shared_ptr<const CommonItemSet::Table> CommonItemSet::get_table(
try {
return this->tables.at(this->key_for_table(episode, mode, difficulty, secid));
} catch (const out_of_range&) {
throw runtime_error(string_printf("common item table not available for episode=%s, mode=%s, difficulty=%hu, secid=%hu",
throw runtime_error(phosg::string_printf("common item table not available for episode=%s, mode=%s, difficulty=%hu, secid=%hu",
name_for_episode(episode), name_for_mode(mode), difficulty, secid));
}
}
@@ -518,7 +516,7 @@ AFSV2CommonItemSet::AFSV2CommonItemSet(
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
for (size_t section_id = 0; section_id < 10; section_id++) {
auto entry = pt_afs.get(difficulty * 10 + section_id);
StringReader r(entry.first, entry.second);
phosg::StringReader r(entry.first, entry.second);
auto table = make_shared<Table>(r, false, false, Episode::EP1);
this->tables.emplace(this->key_for_table(Episode::EP1, GameMode::NORMAL, difficulty, section_id), table);
this->tables.emplace(this->key_for_table(Episode::EP1, GameMode::BATTLE, difficulty, section_id), table);
@@ -556,7 +554,7 @@ GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gs
default:
throw runtime_error("invalid episode");
}
return string_printf(
return phosg::string_printf(
"ItemPT%s%s%c%1hhu.rel",
is_challenge ? "c" : "",
episode_token,
@@ -568,7 +566,7 @@ GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gs
for (Episode episode : episodes) {
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
for (size_t section_id = 0; section_id < 10; section_id++) {
StringReader r;
phosg::StringReader r;
try {
r = gsl.get_reader(filename_for_table(episode, difficulty, section_id, false));
} catch (const exception&) {
@@ -602,7 +600,7 @@ GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gs
}
}
JSONCommonItemSet::JSONCommonItemSet(const JSON& json) {
JSONCommonItemSet::JSONCommonItemSet(const phosg::JSON& json) {
for (const auto& mode_it : json.as_dict()) {
static const unordered_map<string, GameMode> mode_keys(
{{"Normal", GameMode::NORMAL}, {"Battle", GameMode::BATTLE}, {"Challenge", GameMode::CHALLENGE}, {"Solo", GameMode::SOLO}});
@@ -791,7 +789,7 @@ const ProbabilityTable<uint8_t, 100>& TekkerAdjustmentSet::get_bonus_delta_prob_
}
int8_t TekkerAdjustmentSet::get_luck(uint32_t start_offset, uint8_t delta_index) const {
StringReader sub_r = r.sub(start_offset);
phosg::StringReader sub_r = r.sub(start_offset);
while (!sub_r.eof()) {
const auto& entry = sub_r.get<LuckTableEntry>();
if (entry.delta_index == 0xFF) {
+31 -33
View File
@@ -8,14 +8,15 @@
#include "PSOEncryption.hh"
#include "StaticGameData.hh"
#include "Text.hh"
#include "Types.hh"
class CommonItemSet {
public:
class Table {
public:
Table() = delete;
Table(const JSON& json, Episode episode);
Table(const StringReader& r, bool big_endian, bool is_v3, Episode episode);
Table(const phosg::JSON& json, Episode episode);
Table(const phosg::StringReader& r, bool big_endian, bool is_v3, Episode episode);
template <typename IntT>
struct Range {
@@ -47,18 +48,15 @@ public:
parray<uint8_t, 0x0A> unit_max_stars_table;
parray<parray<uint8_t, 10>, 7> box_item_class_prob_table;
JSON json() const;
phosg::JSON json() const;
void print(FILE* stream) const;
private:
template <bool IsBigEndian>
void parse_itempt_t(const StringReader& r, bool is_v3);
template <bool BE>
void parse_itempt_t(const phosg::StringReader& r, bool is_v3);
template <bool IsBigEndian>
template <bool BE>
struct OffsetsT {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
// This data structure uses index probability tables in multiple places. An
// index probability table is a table where each entry holds the probability
// that that entry's index is used. For example, if the armor slot count
@@ -80,7 +78,7 @@ public:
// The indexes in this table correspond to the non-rare weapon types 01
// through 0C (Saber through Wand).
// V2/V3: -> parray<uint8_t, 0x0C>
/* 00 */ U32T base_weapon_type_prob_table_offset;
/* 00 */ U32T<BE> base_weapon_type_prob_table_offset;
// This table specifies the base subtype for each weapon type. Negative
// values here mean that the weapon cannot be found in the first N areas (so
@@ -90,7 +88,7 @@ public:
// of weapon that actually appears depends on this value and a value from
// the following table.
// V2/V3: -> parray<int8_t, 0x0C>
/* 04 */ U32T subtype_base_table_offset;
/* 04 */ U32T<BE> subtype_base_table_offset;
// This table specifies how many areas each weapon subtype appears in. For
// example, if Sword (subtype 02, which is index 1 in this table and the
@@ -99,7 +97,7 @@ public:
// through Mine 1), and Gigush (the next sword subtype) can be found in Mine
// 1 through Ruins 3.
// V2/V3: -> parray<uint8_t, 0x0C>
/* 08 */ U32T subtype_area_length_table_offset;
/* 08 */ U32T<BE> subtype_area_length_table_offset;
// This index probability table specifies how likely each possible grind
// value is. The table is indexed as [grind][subtype_area_index], where the
@@ -114,28 +112,28 @@ public:
// ...
// C1 C2 C3 M1 // (Episode 1 area values from the example for reference)
// V2/V3: -> parray<parray<uint8_t, 4>, 9>
/* 0C */ U32T grind_prob_table_offset;
/* 0C */ U32T<BE> grind_prob_table_offset;
// TODO: Figure out exactly how this table is used. Anchor: 80106D34
// V2/V3: -> parray<uint8_t, 0x05>
/* 10 */ U32T armor_shield_type_index_prob_table_offset;
/* 10 */ U32T<BE> armor_shield_type_index_prob_table_offset;
// This index probability table specifies how common each possible slot
// count is for armor drops.
// V2/V3: -> parray<uint8_t, 0x05>
/* 14 */ U32T armor_slot_count_prob_table_offset;
/* 14 */ U32T<BE> armor_slot_count_prob_table_offset;
// This array (indexed by enemy_type) specifies the range of meseta values
// that each enemy can drop.
// V2/V3: -> parray<Range<U16T>, 0x64>
/* 18 */ U32T enemy_meseta_ranges_offset;
/* 18 */ U32T<BE> enemy_meseta_ranges_offset;
// Each byte in this table (indexed by enemy_type) represents the percent
// chance that the enemy drops anything at all. (This check is done before
// the rare drop check, so the chance of getting a rare item from an enemy
// is essentially this probability multiplied by the rare drop rate.)
// V2/V3: -> parray<uint8_t, 0x64>
/* 1C */ U32T enemy_type_drop_probs_offset;
/* 1C */ U32T<BE> enemy_type_drop_probs_offset;
// Each byte in this table (indexed by enemy_type) represents the class of
// item that the enemy can drop. The values are:
@@ -147,12 +145,12 @@ public:
// 05 = meseta
// Anything else = no item
// V2/V3: -> parray<uint8_t, 0x64>
/* 20 */ U32T enemy_item_classes_offset;
/* 20 */ U32T<BE> enemy_item_classes_offset;
// This table (indexed by area - 1) specifies the ranges of meseta values
// that can drop from boxes.
// V2/V3: -> parray<Range<U16T>, 0x0A>
/* 24 */ U32T box_meseta_ranges_offset;
/* 24 */ U32T<BE> box_meseta_ranges_offset;
// This array specifies the chance that a rare weapon will have each
// possible bonus value. This is indexed as [(bonus_value - 10 / 5)][spec],
@@ -162,7 +160,7 @@ public:
// for rare items, spec is always 5.
// V2: -> parray<parray<uint8_t, 5>, 0x17>
// V3: -> parray<parray<U16T, 6>, 0x17>
/* 28 */ U32T bonus_value_prob_table_offset;
/* 28 */ U32T<BE> bonus_value_prob_table_offset;
// This array specifies the value of spec to be used in the above lookup for
// non-rare items. This is NOT an index probability table; this is a direct
@@ -178,7 +176,7 @@ public:
// bonus; in all other areas except Ruins 3, they can have at most two
// bonuses, and in Ruins 3, they can have up to three bonuses.
// V2/V3: // -> parray<parray<uint8_t, 10>, 3>
/* 2C */ U32T nonrare_bonus_prob_spec_offset;
/* 2C */ U32T<BE> nonrare_bonus_prob_spec_offset;
// This array specifies the chance that a weapon will have each bonus type.
// The table is indexed as [bonus_type][area - 1] for non-rare items; for
@@ -193,37 +191,37 @@ public:
// [00 00 00 00 00 01 01 01 01 01] // Chance of getting Hit bonus
// F1 F2 C1 C2 C3 M1 M2 R1 R2 R3 // (Episode 1 areas, for reference)
// V2/V3: -> parray<parray<uint8_t, 10>, 6>
/* 30 */ U32T bonus_type_prob_table_offset;
/* 30 */ U32T<BE> bonus_type_prob_table_offset;
// This array (indexed by area - 1) specifies a multiplier of used in
// special ability determination. It seems this uses the star values from
// ItemPMT, but not yet clear exactly in what way.
// TODO: Figure out exactly what this does. Anchor: 80106FEC
// V2/V3: -> parray<uint8_t, 0x0A>
/* 34 */ U32T special_mult_offset;
/* 34 */ U32T<BE> special_mult_offset;
// This array (indexed by area - 1) specifies the probability that any
// non-rare weapon will have a special ability.
// V2/V3: -> parray<uint8_t, 0x0A>
/* 38 */ U32T special_percent_offset;
/* 38 */ U32T<BE> special_percent_offset;
// This index probability table is indexed by [tool_class][area - 1]. The
// tool class refers to an entry in ItemPMT, which links it to the actual
// item code.
// V2/V3: -> parray<parray<U16T, 0x0A>, 0x1C>
/* 3C */ U32T tool_class_prob_table_offset;
/* 3C */ U32T<BE> tool_class_prob_table_offset;
// This index probability table determines how likely each technique is to
// appear. The table is indexed as [technique_num][area - 1].
// V2/V3: -> parray<parray<uint8_t, 0x0A>, 0x13>
/* 40 */ U32T technique_index_prob_table_offset;
/* 40 */ U32T<BE> technique_index_prob_table_offset;
// This table specifies the ranges for technique disk levels. The table is
// indexed as [technique_num][area - 1]. If either min or max in the range
// is 0xFF, or if max < min, technique disks are not dropped for that
// technique and area pair.
// V2/V3: -> parray<parray<Range<uint8_t>, 0x0A>, 0x13>
/* 44 */ U32T technique_level_ranges_offset;
/* 44 */ U32T<BE> technique_level_ranges_offset;
/* 48 */ uint8_t armor_or_shield_type_bias;
/* 49 */ parray<uint8_t, 3> unused1;
@@ -234,7 +232,7 @@ public:
// game uniformly chooses a random number of stars in the acceptable
// range, then uniformly chooses a random unit with that many stars.
// V2/V3: -> parray<uint8_t, 0x0A>
/* 4C */ U32T unit_max_stars_offset;
/* 4C */ U32T<BE> unit_max_stars_offset;
// This index probability table determines which type of items drop from
// boxes. The table is indexed as [item_class][area - 1], with item_class
@@ -253,7 +251,7 @@ public:
// [16 16 11 11 11 11 11 0F 0C 0B] // Chances per area of an empty box
// F1 F2 C1 C2 C3 M1 M2 R1 R2 R3 // (Episode 1 areas, for reference)
// V2/V3: -> parray<parray<uint8_t, 10>, 7>
/* 50 */ U32T box_item_class_prob_table_offset;
/* 50 */ U32T<BE> box_item_class_prob_table_offset;
// There are several unused fields here.
} __packed__;
@@ -264,7 +262,7 @@ public:
};
std::shared_ptr<const Table> get_table(Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) const;
JSON json() const;
phosg::JSON json() const;
void print(FILE* stream) const;
protected:
@@ -287,7 +285,7 @@ public:
class JSONCommonItemSet : public CommonItemSet {
public:
explicit JSONCommonItemSet(const JSON& json);
explicit JSONCommonItemSet(const phosg::JSON& json);
};
// Note: There are clearly better ways of doing this, but this implementation
@@ -348,7 +346,7 @@ public:
protected:
std::shared_ptr<const std::string> data;
StringReader r;
phosg::StringReader r;
struct TableSpec {
be_uint32_t offset;
@@ -463,7 +461,7 @@ private:
int8_t get_luck(uint32_t start_offset, uint8_t delta_index) const;
std::shared_ptr<const std::string> data;
StringReader r;
phosg::StringReader r;
struct DeltaProbabilityEntry {
uint8_t delta_index;
+12 -12
View File
@@ -14,7 +14,7 @@
using namespace std;
template <>
const char* name_for_enum<CompressPhase>(CompressPhase v) {
const char* phosg::name_for_enum<CompressPhase>(CompressPhase v) {
switch (v) {
case CompressPhase::INDEX:
return "INDEX";
@@ -118,7 +118,7 @@ struct WindowIndex {
};
struct LZSSInterleavedWriter {
StringWriter w;
phosg::StringWriter w;
size_t buf_offset;
uint8_t next_control_bit;
uint8_t buf[0x19];
@@ -166,7 +166,7 @@ struct LZSSInterleavedWriter {
class ControlStreamReader {
public:
ControlStreamReader(StringReader& r)
ControlStreamReader(phosg::StringReader& r)
: r(r),
bits(0x0000) {}
@@ -188,7 +188,7 @@ public:
}
private:
StringReader& r;
phosg::StringReader& r;
uint16_t bits;
};
@@ -493,7 +493,7 @@ void PRSCompressor::add(const void* data, size_t size) {
throw logic_error("compressor is closed");
}
StringReader r(data, size);
phosg::StringReader r(data, size);
while (!r.eof()) {
this->add_byte(r.get_u8());
}
@@ -874,8 +874,8 @@ PRSDecompressResult prs_decompress_with_meta(
// is encountered partway through an opcode, we throw instead, because it's
// likely the input has been truncated or is malformed in some way.
StringWriter w;
StringReader r(data, size);
phosg::StringWriter w;
phosg::StringReader r(data, size);
ControlStreamReader cr(r);
while (!r.eof()) {
@@ -959,7 +959,7 @@ string prs_decompress(const string& data, size_t max_output_size, bool allow_unt
size_t prs_decompress_size(const void* data, size_t size, size_t max_output_size, bool allow_unterminated) {
size_t ret = 0;
StringReader r(data, size);
phosg::StringReader r(data, size);
ControlStreamReader cr(r);
while (!r.eof()) {
@@ -1011,7 +1011,7 @@ size_t prs_decompress_size(const string& data, size_t max_output_size, bool allo
void prs_disassemble(FILE* stream, const void* data, size_t size) {
size_t output_bytes = 0;
StringReader r(data, size);
phosg::StringReader r(data, size);
ControlStreamReader cr(r);
while (!r.eof()) {
@@ -1249,8 +1249,8 @@ string bc0_decompress(const string& data) {
}
string bc0_decompress(const void* data, size_t size) {
StringReader r(data, size);
StringWriter w;
phosg::StringReader r(data, size);
phosg::StringWriter w;
// Unlike PRS, BC0 uses a memo which "rolls over" every 0x1000 bytes. The
// boundaries of these "memo pages" are offset by -0x12 bytes for some reason,
@@ -1322,7 +1322,7 @@ void bc0_disassemble(FILE* stream, const string& data) {
}
void bc0_disassemble(FILE* stream, const void* data, size_t size) {
StringReader r(data, size);
phosg::StringReader r(data, size);
uint16_t control_stream_bits = 0x0000;
size_t output_bytes = 0;
+2 -2
View File
@@ -18,7 +18,7 @@ enum class CompressPhase {
};
template <>
const char* name_for_enum<CompressPhase>(CompressPhase v);
const char* phosg::name_for_enum<CompressPhase>(CompressPhase v);
typedef std::function<void(CompressPhase phase, size_t input_progress, size_t input_size, size_t output_size)> ProgressCallback;
@@ -146,7 +146,7 @@ private:
WrappedLog<0x101> forward_log;
IndexedLog<0x2000> reverse_log;
StringWriter output;
phosg::StringWriter output;
};
// These functions use PRSCompressor to compress a buffer of data. This is
+20 -20
View File
@@ -1174,7 +1174,7 @@ static uint64_t decode_dc_serial_number_str(const string& s) {
if (new_ch == '\0') {
return INVALID_PRODUCT;
}
serial_number = (serial_number << 4) | value_for_hex_char(new_ch);
serial_number = (serial_number << 4) | phosg::value_for_hex_char(new_ch);
}
return serial_number;
}
@@ -1291,12 +1291,12 @@ string generate_dc_serial_number(uint8_t domain, uint8_t subdomain) {
throw runtime_error("invalid domain");
}
size_t det1 = (subdomain == 0xFF) ? random_object<uint32_t>() : subdomain;
size_t det1 = (subdomain == 0xFF) ? phosg::random_object<uint32_t>() : subdomain;
size_t index1 = offset1 + (det1 % (limit1 - offset1));
size_t index2 = random_object<uint32_t>() % (sizeof(primes2) / sizeof(primes2[0]));
size_t index3 = random_object<uint32_t>() % (sizeof(primes3) / sizeof(primes3[0]));
size_t index2 = phosg::random_object<uint32_t>() % (sizeof(primes2) / sizeof(primes2[0]));
size_t index3 = phosg::random_object<uint32_t>() % (sizeof(primes3) / sizeof(primes3[0]));
uint32_t value = primes1[index1] * primes2[index2] * primes3[index3];
string s = string_printf("%08X", value);
string s = phosg::string_printf("%08X", value);
string ret;
for (char ch : s) {
@@ -1355,7 +1355,7 @@ unordered_map<uint32_t, string> generate_all_dc_serial_numbers(uint8_t domain, u
}
void dc_serial_number_speed_test(uint64_t seed) {
uint32_t effective_seed = (seed & 0xFFFFFFFF00000000) ? random_object<uint32_t>() : seed;
uint32_t effective_seed = (seed & 0xFFFFFFFF00000000) ? phosg::random_object<uint32_t>() : seed;
fprintf(stderr, "Product speed test with seed=%08" PRIX32 "\n", effective_seed);
PSOV2Encryption crypt(effective_seed);
uint64_t time_slow = 0;
@@ -1363,15 +1363,15 @@ void dc_serial_number_speed_test(uint64_t seed) {
size_t num_disagreements = 0;
static constexpr size_t count = 0x1000;
for (size_t z = 0; z < count; z++) {
string s = string_printf("%08X", crypt.next());
string s = phosg::string_printf("%08X", crypt.next());
uint64_t start = now();
uint64_t start = phosg::now();
bool is_valid_fast = dc_serial_number_is_valid_fast(s, 1, 0xFF);
time_fast += now() - start;
time_fast += phosg::now() - start;
start = now();
start = phosg::now();
bool is_valid_slow = dc_serial_number_is_valid_slow(s, 1, 0xFF);
time_slow += now() - start;
time_slow += phosg::now() - start;
if (((z & 0xF) == 0) || is_valid_slow || is_valid_fast) {
fprintf(stderr, "... %02zX: %s => %s %s%s\n", z, s.c_str(), is_valid_slow ? "SLOW" : "----", is_valid_fast ? "FAST" : "----", is_valid_slow != is_valid_fast ? " !!!" : "");
@@ -1391,13 +1391,13 @@ string decrypt_dp_address_jpn(
const string& executable,
const string& values,
const string& indexes) {
StringReader values_r(values);
StringReader indexes_r(indexes);
phosg::StringReader values_r(values);
phosg::StringReader indexes_r(indexes);
size_t fixup_values_offset = values_r.pget_u32l(0x3FFC) - 0x8C004000;
size_t fixup_steps_offset = indexes_r.pget_u32l(0x3BFC) - 0x8C008400;
StringReader fixup_values_r = values_r.sub(fixup_values_offset);
StringReader fixup_steps_r = indexes_r.sub(fixup_steps_offset);
phosg::StringReader fixup_values_r = values_r.sub(fixup_values_offset);
phosg::StringReader fixup_steps_r = indexes_r.sub(fixup_steps_offset);
auto decrypted = decrypt_pr2_data<false>(executable);
size_t fixup_offset = 0;
@@ -1417,9 +1417,9 @@ EncryptedDCv2Executables encrypt_dp_address_jpn(const string& executable, const
EncryptedDCv2Executables ret;
string compressed = prs_compress(executable);
ret.executable = encrypt_pr2_data<false>(compressed, executable.size(), random_object<uint32_t>() & 0x7FFFFF7F);
ret.executable = encrypt_pr2_data<false>(compressed, executable.size(), phosg::random_object<uint32_t>() & 0x7FFFFF7F);
StringReader indexes_r(indexes);
phosg::StringReader indexes_r(indexes);
size_t fixup_steps_offset = indexes_r.pget_u32l(0x3BFC) - 0x8C008400;
ret.indexes = indexes;
ret.indexes.at(fixup_steps_offset) = 0;
@@ -1431,7 +1431,7 @@ std::string crypt_dp_address_jpn_simple(const std::string& data, int64_t mask_ke
throw runtime_error("size is not a multiple of 4");
}
StringReader r(data);
phosg::StringReader r(data);
if (mask_key < 0) {
unordered_map<uint32_t, size_t> key_freq;
while (!r.eof()) {
@@ -1447,11 +1447,11 @@ std::string crypt_dp_address_jpn_simple(const std::string& data, int64_t mask_ke
if (mask_key < 0) {
throw runtime_error("cannot determine mask key");
}
log_info("Determined %08" PRIX64 " to be the most likely mask key", mask_key);
phosg::log_info("Determined %08" PRIX64 " to be the most likely mask key", mask_key);
r.go(0);
}
StringWriter w;
phosg::StringWriter w;
while (!r.eof()) {
w.put_u32l(r.get_u32l() ^ mask_key);
}
+5 -5
View File
@@ -34,15 +34,15 @@ DNSServer::~DNSServer() {
}
void DNSServer::listen(const std::string& socket_path) {
this->add_socket(::listen(socket_path, 0, 0));
this->add_socket(phosg::listen(socket_path, 0, 0));
}
void DNSServer::listen(const std::string& addr, int port) {
this->add_socket(::listen(addr, port, 0));
this->add_socket(phosg::listen(addr, port, 0));
}
void DNSServer::listen(int port) {
this->add_socket(::listen("", port, 0));
this->add_socket(phosg::listen("", port, 0));
}
void DNSServer::add_socket(int fd) {
@@ -66,7 +66,7 @@ string DNSServer::response_for_query(const void* vdata, size_t size, uint32_t re
const char* data = reinterpret_cast<const char*>(vdata);
size_t name_len = strlen(&data[12]) + 1;
be_uint32_t be_resolved_address = resolved_address;
phosg::be_uint32_t be_resolved_address = resolved_address;
string response;
response.append(data, 2);
@@ -104,7 +104,7 @@ void DNSServer::on_receive_message(int fd, short) {
} else if (bytes < 0x0C) {
dns_server_log.warning("input query too small");
print_data(stderr, input.data(), bytes);
phosg::print_data(stderr, input.data(), bytes);
} else if (!this->banned_ipv4_ranges->check(remote)) {
input.resize(bytes);
+9 -6
View File
@@ -10,7 +10,7 @@
using namespace std;
template <>
const char* name_for_enum<EnemyType>(EnemyType type) {
const char* phosg::name_for_enum<EnemyType>(EnemyType type) {
switch (type) {
case EnemyType::UNKNOWN:
return "UNKNOWN";
@@ -274,7 +274,7 @@ const char* name_for_enum<EnemyType>(EnemyType type) {
}
template <>
EnemyType enum_for_name<EnemyType>(const char* name) {
EnemyType phosg::enum_for_name<EnemyType>(const char* name) {
static const unordered_map<string, EnemyType> names({
{"UNKNOWN", EnemyType::UNKNOWN},
{"NONE", EnemyType::NONE},
@@ -672,7 +672,8 @@ uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type)
case EnemyType::SO_DIMENIAN:
return 0x55;
default:
throw runtime_error(string_printf("%s does not have battle parameters in Episode 1", name_for_enum(enemy_type)));
throw out_of_range(phosg::string_printf(
"%s does not have battle parameters in Episode 1", phosg::name_for_enum(enemy_type)));
}
break;
case Episode::EP2:
@@ -787,7 +788,8 @@ uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type)
case EnemyType::SO_DIMENIAN:
return 0x55;
default:
throw runtime_error(string_printf("%s does not have battle parameters in Episode 2", name_for_enum(enemy_type)));
throw out_of_range(phosg::string_printf(
"%s does not have battle parameters in Episode 2", phosg::name_for_enum(enemy_type)));
}
break;
case Episode::EP4:
@@ -847,7 +849,8 @@ uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type)
case EnemyType::KONDRIEU:
return 0x2A;
default:
throw runtime_error(string_printf("%s does not have battle parameters in Episode 4", name_for_enum(enemy_type)));
throw out_of_range(phosg::string_printf(
"%s does not have battle parameters in Episode 4", phosg::name_for_enum(enemy_type)));
}
break;
default:
@@ -1072,7 +1075,7 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
case EnemyType::ZU_ALT:
return 0x4A;
default:
throw runtime_error(string_printf("%s does not have a rare table entry", name_for_enum(enemy_type)));
throw runtime_error(phosg::string_printf("%s does not have a rare table entry", phosg::name_for_enum(enemy_type)));
}
}
+3 -2
View File
@@ -5,6 +5,7 @@
#include <phosg/Tools.hh>
#include "StaticGameData.hh"
#include "Types.hh"
enum class EnemyType {
UNKNOWN = -1,
@@ -138,9 +139,9 @@ enum class EnemyType {
};
template <>
const char* name_for_enum<EnemyType>(EnemyType type);
const char* phosg::name_for_enum<EnemyType>(EnemyType type);
template <>
EnemyType enum_for_name<EnemyType>(const char* name);
EnemyType phosg::enum_for_name<EnemyType>(const char* name);
bool enemy_type_valid_for_episode(Episode episode, EnemyType enemy_type);
uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type);
+25 -26
View File
@@ -12,10 +12,10 @@ namespace Episode3 {
void BattleRecord::PlayerEntry::print(FILE* stream) const {
// TODO: Format this nicely somehow. Maybe factor out the functions in
// QuestScript that format some of these structures
print_data(stream, this, sizeof(*this), 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
phosg::print_data(stream, this, sizeof(*this), 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
}
BattleRecord::Event::Event(StringReader& r) {
BattleRecord::Event::Event(phosg::StringReader& r) {
this->type = r.get<Event::Type>();
this->timestamp = r.get_u64l();
switch (this->type) {
@@ -46,7 +46,7 @@ BattleRecord::Event::Event(StringReader& r) {
}
}
void BattleRecord::Event::serialize(StringWriter& w) const {
void BattleRecord::Event::serialize(phosg::StringWriter& w) const {
w.put(this->type);
w.put_u64l(this->timestamp);
switch (this->type) {
@@ -81,7 +81,7 @@ void BattleRecord::Event::serialize(StringWriter& w) const {
}
void BattleRecord::Event::print(FILE* stream) const {
string time_str = format_time(this->timestamp);
string time_str = phosg::format_time(this->timestamp);
fprintf(stream, "Event @%016" PRIX64 " (%s) ", this->timestamp, time_str.c_str());
switch (this->type) {
case Type::PLAYER_JOIN:
@@ -103,23 +103,23 @@ void BattleRecord::Event::print(FILE* stream) const {
break;
case Type::BATTLE_COMMAND:
fprintf(stream, "BATTLE_COMMAND\n");
print_data(stream, this->data, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
break;
case Type::GAME_COMMAND:
fprintf(stream, "GAME_COMMAND\n");
print_data(stream, this->data, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
break;
case Type::EP3_GAME_COMMAND:
fprintf(stream, "EP3_GAME_COMMAND\n");
print_data(stream, this->data, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
break;
case Type::CHAT_MESSAGE:
fprintf(stream, "CHAT_MESSAGE %08" PRIX32 "\n", this->guild_card_number);
print_data(stream, this->data, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
break;
case Type::SERVER_DATA_COMMAND:
fprintf(stream, "SERVER_DATA_COMMAND\n");
print_data(stream, this->data, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
phosg::print_data(stream, this->data, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
break;
default:
throw runtime_error("unknown event type in battle record");
@@ -137,7 +137,7 @@ BattleRecord::BattleRecord(const string& data)
behavior_flags(0),
battle_start_timestamp(0),
battle_end_timestamp(0) {
StringReader r(data);
phosg::StringReader r(data);
uint64_t signature = r.get_u64l();
bool has_random_stream;
@@ -161,7 +161,7 @@ BattleRecord::BattleRecord(const string& data)
}
string BattleRecord::serialize() const {
StringWriter w;
phosg::StringWriter w;
w.put_u64l(this->SIGNATURE_V2);
w.put_u64l(this->battle_start_timestamp);
w.put_u64l(this->battle_end_timestamp);
@@ -202,7 +202,7 @@ void BattleRecord::add_player(
}
Event& ev = this->events.emplace_back();
ev.type = Event::Type::PLAYER_JOIN;
ev.timestamp = now();
ev.timestamp = phosg::now();
auto& player = ev.players.emplace_back();
player.lobby_data = lobby_data;
player.inventory = inventory;
@@ -216,7 +216,7 @@ void BattleRecord::delete_player(uint8_t client_id) {
}
Event& ev = this->events.emplace_back();
ev.type = Event::Type::PLAYER_LEAVE;
ev.timestamp = now();
ev.timestamp = phosg::now();
ev.leaving_client_id = client_id;
}
@@ -226,7 +226,7 @@ void BattleRecord::add_command(Event::Type type, const void* data, size_t size)
}
Event& ev = this->events.emplace_back();
ev.type = type;
ev.timestamp = now();
ev.timestamp = phosg::now();
ev.data.assign(reinterpret_cast<const char*>(data), size);
}
@@ -236,7 +236,7 @@ void BattleRecord::add_command(Event::Type type, string&& data) {
}
Event& ev = this->events.emplace_back();
ev.type = type;
ev.timestamp = now();
ev.timestamp = phosg::now();
ev.data = std::move(data);
}
@@ -247,7 +247,7 @@ void BattleRecord::add_chat_message(
}
Event& ev = this->events.emplace_back();
ev.type = Event::Type::CHAT_MESSAGE;
ev.timestamp = now();
ev.timestamp = phosg::now();
ev.guild_card_number = guild_card_number;
ev.data = std::move(data);
}
@@ -287,7 +287,7 @@ void BattleRecord::set_battle_start_timestamp() {
if (this->battle_start_timestamp != 0) {
throw logic_error("battle start timestamp is already set");
}
this->battle_start_timestamp = now();
this->battle_start_timestamp = phosg::now();
// First, find the correct map definition subcommand to keep, and execute
// player join/leave events to get the present players
@@ -357,12 +357,12 @@ void BattleRecord::set_battle_start_timestamp() {
}
void BattleRecord::set_battle_end_timestamp() {
this->battle_end_timestamp = now();
this->battle_end_timestamp = phosg::now();
}
void BattleRecord::print(FILE* stream) const {
string start_str = format_time(this->battle_start_timestamp);
string end_str = format_time(this->battle_end_timestamp);
string start_str = phosg::format_time(this->battle_start_timestamp);
string end_str = phosg::format_time(this->battle_end_timestamp);
fprintf(stream, "BattleRecord %s behavior_flags=%08" PRIX32 " start=%016" PRIX64 " (%s) end=%016" PRIX64 " (%s); %zu events\n",
this->is_writable ? "writable" : "read-only",
this->behavior_flags,
@@ -394,13 +394,12 @@ void BattleRecordPlayer::set_lobby(shared_ptr<Lobby> l) {
void BattleRecordPlayer::start() {
if (this->play_start_timestamp == 0) {
this->play_start_timestamp = now();
this->play_start_timestamp = phosg::now();
this->schedule_events();
}
}
void BattleRecordPlayer::dispatch_schedule_events(
evutil_socket_t, short, void* ctx) {
void BattleRecordPlayer::dispatch_schedule_events(evutil_socket_t, short, void* ctx) {
reinterpret_cast<BattleRecordPlayer*>(ctx)->schedule_events();
}
@@ -413,7 +412,7 @@ void BattleRecordPlayer::schedule_events() {
}
for (;;) {
uint64_t relative_ts = now() - this->play_start_timestamp + this->record->battle_start_timestamp;
uint64_t relative_ts = phosg::now() - this->play_start_timestamp + this->record->battle_start_timestamp;
if (this->event_it == this->record->events.end()) {
if (relative_ts >= this->record->battle_end_timestamp) {
@@ -426,7 +425,7 @@ void BattleRecordPlayer::schedule_events() {
} else {
// There are no more events to play, but the battle has not officially
// ended yet - reschedule the event for the end time
auto tv = usecs_to_timeval(this->record->battle_end_timestamp - relative_ts);
auto tv = phosg::usecs_to_timeval(this->record->battle_end_timestamp - relative_ts);
event_add(this->next_command_ev.get(), &tv);
}
break;
@@ -467,7 +466,7 @@ void BattleRecordPlayer::schedule_events() {
} else {
// The next event should not occur yet, so reschedule for the time when
// it should occur
auto tv = usecs_to_timeval(this->event_it->timestamp - relative_ts);
auto tv = phosg::usecs_to_timeval(this->event_it->timestamp - relative_ts);
event_add(this->next_command_ev.get(), &tv);
break;
}
+3 -3
View File
@@ -53,8 +53,8 @@ public:
std::string data;
Event() = default;
explicit Event(StringReader& r);
void serialize(StringWriter& w) const;
explicit Event(phosg::StringReader& r);
void serialize(phosg::StringWriter& w) const;
void print(FILE* stream) const;
};
@@ -126,7 +126,7 @@ private:
std::shared_ptr<struct event_base> base;
std::weak_ptr<Lobby> lobby;
std::shared_ptr<struct event> next_command_ev;
StringReader random_r;
phosg::StringReader random_r;
};
} // namespace Episode3
+9 -9
View File
@@ -123,7 +123,7 @@ ssize_t Card::apply_abnormal_condition(
int8_t dice_roll_value,
int8_t random_percent) {
auto s = this->server();
auto log = s->log_stack(string_printf("apply_abnormal_condition(%02hhX, @%04X, @%04X, %hd, %hhd, %hhd): ", def_effect_index, target_card_ref, sc_card_ref, value, dice_roll_value, random_percent));
auto log = s->log_stack(phosg::string_printf("apply_abnormal_condition(%02hhX, @%04X, @%04X, %hd, %hhd, %hhd): ", def_effect_index, target_card_ref, sc_card_ref, value, dice_roll_value, random_percent));
bool is_nte = s->options.is_nte();
ssize_t existing_cond_index;
@@ -298,7 +298,7 @@ void Card::commit_attack(
size_t strike_number,
int16_t* out_effective_damage) {
auto s = this->server();
auto log = s->log_stack(string_printf("commit_attack(@%04hX #%04hX, @%04hX #%04hX => %hd (str%zu)): ", this->get_card_ref(), this->get_card_id(), attacker_card->get_card_ref(), attacker_card->get_card_id(), damage, strike_number));
auto log = s->log_stack(phosg::string_printf("commit_attack(@%04hX #%04hX, @%04hX #%04hX => %hd (str%zu)): ", this->get_card_ref(), this->get_card_id(), attacker_card->get_card_ref(), attacker_card->get_card_id(), damage, strike_number));
bool is_nte = s->options.is_nte();
int16_t effective_damage = damage;
@@ -507,7 +507,7 @@ void Card::execute_attack(shared_ptr<Card> attacker_card) {
}
auto s = this->server();
auto log = s->log_stack(string_printf("execute_attack(@%04X #%04X, @%04X #%04X): ", this->get_card_ref(), this->get_card_id(), attacker_card->get_card_ref(), attacker_card->get_card_id()));
auto log = s->log_stack(phosg::string_printf("execute_attack(@%04X #%04X, @%04X #%04X): ", this->get_card_ref(), this->get_card_id(), attacker_card->get_card_ref(), attacker_card->get_card_id()));
bool is_nte = s->options.is_nte();
this->card_flags &= 0xFFFFFFF3;
@@ -905,7 +905,7 @@ void Card::clear_action_chain_and_metadata_and_most_flags() {
void Card::compute_action_chain_results(bool apply_action_conditions, bool ignore_this_card_ap_tp) {
auto s = this->server();
auto log = s->log_stack(string_printf("compute_action_chain_results(@%04hX #%04hX): ", this->get_card_ref(), this->get_card_id()));
auto log = s->log_stack(phosg::string_printf("compute_action_chain_results(@%04hX #%04hX): ", this->get_card_ref(), this->get_card_id()));
bool is_nte = s->options.is_nte();
this->action_chain.compute_attack_medium(s);
@@ -914,7 +914,7 @@ void Card::compute_action_chain_results(bool apply_action_conditions, bool ignor
this->action_chain.chain.tp_effect_bonus = 0;
log.debug("(initial) medium=%s, strike_count=%hhu, ap_effect_bonus=%hhd, tp_effect_bonus=%hhd",
name_for_enum(this->action_chain.chain.attack_medium),
phosg::name_for_enum(this->action_chain.chain.attack_medium),
this->action_chain.chain.strike_count,
this->action_chain.chain.ap_effect_bonus,
this->action_chain.chain.tp_effect_bonus);
@@ -1220,7 +1220,7 @@ void Card::move_phase_before() {
void Card::unknown_80236374(shared_ptr<Card> other_card, const ActionState* as) {
auto s = this->server();
auto log = s->log_stack(string_printf("unknown_80236374(@%04hX #%04hX, @%04hX #%04hX): ", this->get_card_ref(), this->get_card_id(), other_card->get_card_ref(), other_card->get_card_id()));
auto log = s->log_stack(phosg::string_printf("unknown_80236374(@%04hX #%04hX, @%04hX #%04hX): ", this->get_card_ref(), this->get_card_id(), other_card->get_card_ref(), other_card->get_card_id()));
auto check_card = [&](shared_ptr<Card> card) -> void {
if (card) {
@@ -1361,8 +1361,8 @@ bool Card::is_guard_item() const {
bool Card::unknown_80236554(shared_ptr<Card> other_card, const ActionState* as) {
auto s = this->server();
auto log = s->log_stack(other_card
? string_printf("unknown_80236554(@%04hX #%04hX, @%04hX #%04hX): ", this->get_card_ref(), this->get_card_id(), other_card->get_card_ref(), other_card->get_card_id())
: string_printf("unknown_80236554(@%04hX #%04hX, null): ", this->get_card_ref(), this->get_card_id()));
? phosg::string_printf("unknown_80236554(@%04hX #%04hX, @%04hX #%04hX): ", this->get_card_ref(), this->get_card_id(), other_card->get_card_ref(), other_card->get_card_id())
: phosg::string_printf("unknown_80236554(@%04hX #%04hX, null): ", this->get_card_ref(), this->get_card_id()));
if (as) {
string as_str = as->str(s);
log.debug("as = %s", as_str.c_str());
@@ -1439,7 +1439,7 @@ void Card::apply_attack_result() {
auto ps = this->player_state();
bool is_nte = s->options.is_nte();
auto log = s->log_stack(string_printf("apply_attack_result(@%04hX #%04hX): ", this->get_card_ref(), this->get_card_id()));
auto log = s->log_stack(phosg::string_printf("apply_attack_result(@%04hX #%04hX): ", this->get_card_ref(), this->get_card_id()));
if (!this->action_chain.can_apply_attack()) {
return;
}
+20 -21
View File
@@ -21,7 +21,7 @@ static string refs_str_for_cards_vector(const vector<shared_ptr<T>>& cards) {
if (!ret.empty()) {
ret += ", ";
}
ret += string_printf("@%04hX", ref_for_card(card));
ret += phosg::string_printf("@%04hX", ref_for_card(card));
}
return ret;
}
@@ -453,7 +453,7 @@ bool CardSpecial::apply_stat_deltas_to_all_cards_from_all_conditions_with_card_r
bool CardSpecial::apply_stat_deltas_to_card_from_condition_and_clear_cond(Condition& cond, shared_ptr<Card> card) {
auto s = this->server();
auto log = s->log_stack(string_printf("apply_stat_deltas_to_card_from_condition_and_clear_cond(@%04hX #%04hX): ", card->get_card_ref(), card->get_card_id()));
auto log = s->log_stack(phosg::string_printf("apply_stat_deltas_to_card_from_condition_and_clear_cond(@%04hX #%04hX): ", card->get_card_ref(), card->get_card_id()));
bool is_nte = s->options.is_nte();
string cond_str = cond.str(s);
@@ -585,7 +585,7 @@ bool CardSpecial::apply_stat_deltas_to_card_from_condition_and_clear_cond(Condit
break;
trial_unimplemented:
default:
log.debug("%s: no adjustments for condition type", name_for_enum(cond_type));
log.debug("%s: no adjustments for condition type", phosg::name_for_enum(cond_type));
break;
}
@@ -1189,7 +1189,7 @@ shared_ptr<Card> CardSpecial::compute_replaced_target_based_on_conditions(
StatSwapType CardSpecial::compute_stat_swap_type(shared_ptr<const Card> card) const {
auto s = this->server();
auto log = s->log_stack(string_printf("compute_stat_swap_type(@%04hX #%04hX): ", card->get_card_ref(), card->get_card_id()));
auto log = s->log_stack(phosg::string_printf("compute_stat_swap_type(@%04hX #%04hX): ", card->get_card_ref(), card->get_card_id()));
if (!card) {
log.debug("card is missing");
return StatSwapType::NONE;
@@ -1199,7 +1199,7 @@ StatSwapType CardSpecial::compute_stat_swap_type(shared_ptr<const Card> card) co
for (size_t cond_index = 0; cond_index < 9; cond_index++) {
auto& cond = card->action_chain.conditions[cond_index];
if (cond.type != ConditionType::NONE) {
auto cond_log = log.sub(string_printf("(%zu) ", cond_index));
auto cond_log = log.sub(phosg::string_printf("(%zu) ", cond_index));
string cond_str = cond.str(s);
cond_log.debug("%s", cond_str.c_str());
if (!this->card_ref_has_ability_trap(cond)) {
@@ -1675,7 +1675,7 @@ int32_t CardSpecial::evaluate_effect_expr(
const char* expr,
DiceRoll& dice_roll) const {
auto log = this->server()->log_stack("evaluate_effect_expr: ");
if (log.min_level == LogLevel::DEBUG) {
if (log.min_level == phosg::LogLevel::DEBUG) {
log.debug("ast, expr=\"%s\", dice_roll=(client_id=%02hhX, a2=%02hhX, value=%02hhX, value_used_in_expr=%s, a5=%04hX)", expr, dice_roll.client_id, dice_roll.unknown_a2, dice_roll.value, dice_roll.value_used_in_expr ? "true" : "false", dice_roll.unknown_a5);
ast.print(stderr);
}
@@ -1769,10 +1769,10 @@ bool CardSpecial::execute_effect(
uint32_t unknown_p7,
uint16_t attacker_card_ref) {
auto s = this->server();
auto log = s->log_stack(string_printf("execute_effect(@%04hX #%04hX): ", card->get_card_ref(), card->get_card_id()));
auto log = s->log_stack(phosg::string_printf("execute_effect(@%04hX #%04hX): ", card->get_card_ref(), card->get_card_id()));
{
string cond_str = cond.str(s);
log.debug("cond=%s, card=@%04hX, expr_value=%hd, unknown_p5=%hd, cond_type=%s, unknown_p7=%" PRIu32 ", attacker_card_ref=@%04hX", cond_str.c_str(), ref_for_card(card), expr_value, unknown_p5, name_for_enum(cond_type), unknown_p7, attacker_card_ref);
log.debug("cond=%s, card=@%04hX, expr_value=%hd, unknown_p5=%hd, cond_type=%s, unknown_p7=%" PRIu32 ", attacker_card_ref=@%04hX", cond_str.c_str(), ref_for_card(card), expr_value, unknown_p5, phosg::name_for_enum(cond_type), unknown_p7, attacker_card_ref);
}
bool is_nte = s->options.is_nte();
@@ -2804,7 +2804,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
int16_t p_target_type,
bool apply_usability_filters) const {
auto s = this->server();
auto log = s->log_stack(string_printf("get_targeted_cards_for_condition(@%04hX, %hhu, @%04hX): ", card_ref, def_effect_index, setter_card_ref));
auto log = s->log_stack(phosg::string_printf("get_targeted_cards_for_condition(@%04hX, %hhu, @%04hX): ", card_ref, def_effect_index, setter_card_ref));
log.debug("card_ref=@%04hX, def_effect_index=%02hhX, setter_card_ref=@%04hX, as, p_target_type=%hd, apply_usability_filters=%s", card_ref, def_effect_index, setter_card_ref, p_target_type, apply_usability_filters ? "true" : "false");
vector<shared_ptr<const Card>> ret;
@@ -2836,7 +2836,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
AttackMedium attack_medium = card2
? card2->action_chain.chain.attack_medium
: AttackMedium::UNKNOWN;
log.debug("attack_medium=%s", name_for_enum(attack_medium));
log.debug("attack_medium=%s", phosg::name_for_enum(attack_medium));
auto add_card_refs = [&](const vector<uint16_t>& result_card_refs) -> void {
for (uint16_t result_card_ref : result_card_refs) {
@@ -3944,13 +3944,13 @@ void CardSpecial::evaluate_and_apply_effects(
bool apply_defense_condition_to_all_cards,
uint16_t apply_defense_condition_to_card_ref) {
auto s = this->server();
auto log = s->log_stack(string_printf("evaluate_and_apply_effects(%s, @%04hX, @%04hX): ", name_for_enum(when), set_card_ref, sc_card_ref));
auto log = s->log_stack(phosg::string_printf("evaluate_and_apply_effects(%s, @%04hX, @%04hX): ", phosg::name_for_enum(when), set_card_ref, sc_card_ref));
bool is_nte = s->options.is_nte();
{
string as_str = as.str(s);
log.debug("when=%s, set_card_ref=@%04hX, as=%s, sc_card_ref=@%04hX, apply_defense_condition_to_all_cards=%s, apply_defense_condition_to_card_ref=@%04hX",
name_for_enum(when), set_card_ref, as_str.c_str(), sc_card_ref, apply_defense_condition_to_all_cards ? "true" : "false", apply_defense_condition_to_card_ref);
phosg::name_for_enum(when), set_card_ref, as_str.c_str(), sc_card_ref, apply_defense_condition_to_all_cards ? "true" : "false", apply_defense_condition_to_card_ref);
}
if (!is_nte) {
@@ -4010,12 +4010,12 @@ void CardSpecial::evaluate_and_apply_effects(
log.debug("inputs: dice_roll=%02hhX, random_percent=%hhu, unknown_v1=%s", dice_roll.value, random_percent, unknown_v1 ? "true" : "false");
for (size_t def_effect_index = 0; (def_effect_index < 3) && !unknown_v1 && (ce->def.effects[def_effect_index].type != ConditionType::NONE); def_effect_index++) {
auto effect_log = log.sub(string_printf("(effect:%zu) ", def_effect_index));
auto effect_log = log.sub(phosg::string_printf("(effect:%zu) ", def_effect_index));
const auto& card_effect = ce->def.effects[def_effect_index];
string card_effect_str = card_effect.str();
effect_log.debug("effect: %s", card_effect_str.c_str());
if (card_effect.when != when) {
effect_log.debug("does not apply (effect.when=%s, when=%s)", name_for_enum(card_effect.when), name_for_enum(when));
effect_log.debug("does not apply (effect.when=%s, when=%s)", phosg::name_for_enum(card_effect.when), phosg::name_for_enum(when));
continue;
}
@@ -4067,7 +4067,7 @@ void CardSpecial::evaluate_and_apply_effects(
}
for (size_t z = 0; z < targeted_cards.size(); z++) {
auto target_log = effect_log.sub(string_printf("(target:@%04hX) ", targeted_cards[z]->get_card_ref()));
auto target_log = effect_log.sub(phosg::string_printf("(target:@%04hX) ", targeted_cards[z]->get_card_ref()));
dice_roll.value_used_in_expr = false;
string arg2_str = card_effect.arg2.decode();
target_log.debug("arg2_str = %s", arg2_str.c_str());
@@ -4075,8 +4075,7 @@ void CardSpecial::evaluate_and_apply_effects(
this->evaluate_effect_arg2_condition(
as, targeted_cards[z], arg2_str.c_str(), dice_roll, set_card_ref, sc_card_ref, random_percent, when)) {
target_log.debug("arg2 condition passed");
auto env_stats = this->compute_attack_env_stats(
as, targeted_cards[z], dice_roll, set_card_ref, sc_card_ref);
auto env_stats = this->compute_attack_env_stats(as, targeted_cards[z], dice_roll, set_card_ref, sc_card_ref);
string expr_str = card_effect.expr.decode();
int16_t value = this->evaluate_effect_expr(env_stats, expr_str.c_str(), dice_roll);
target_log.debug("expr = %s, value = %hd", expr_str.c_str(), value);
@@ -4767,7 +4766,7 @@ void CardSpecial::dice_phase_before_for_card(shared_ptr<Card> card) {
template <EffectWhen When1, EffectWhen When2>
void CardSpecial::apply_effects_on_phase_change_t(shared_ptr<Card> unknown_p2, const ActionState* existing_as) {
auto s = this->server();
auto log = s->log_stack(string_printf("apply_effects_on_phase_change_t<%s, %s>(@%04hX #%04hX): ", name_for_enum(When1), name_for_enum(When2), unknown_p2->get_card_ref(), unknown_p2->get_card_id()));
auto log = s->log_stack(phosg::string_printf("apply_effects_on_phase_change_t<%s, %s>(@%04hX #%04hX): ", phosg::name_for_enum(When1), phosg::name_for_enum(When2), unknown_p2->get_card_ref(), unknown_p2->get_card_id()));
bool is_nte = s->options.is_nte();
ActionState as;
@@ -4818,7 +4817,7 @@ void CardSpecial::unknown_8024945C(shared_ptr<Card> unknown_p2, const ActionStat
}
void CardSpecial::unknown_8024966C(shared_ptr<Card> unknown_p2, const ActionState* existing_as) {
auto log = this->server()->log_stack(string_printf("unknown_8024966C(@%04hX #%04hX): ", unknown_p2->get_card_ref(), unknown_p2->get_card_id()));
auto log = this->server()->log_stack(phosg::string_printf("unknown_8024966C(@%04hX #%04hX): ", unknown_p2->get_card_ref(), unknown_p2->get_card_id()));
ActionState as;
if (!existing_as) {
@@ -4969,8 +4968,8 @@ template <
EffectWhen WhenTargetsAndActionCards>
void CardSpecial::apply_effects_before_or_after_attack(shared_ptr<Card> unknown_p2) {
auto s = this->server();
auto log = s->log_stack(string_printf("apply_effects_before_or_after_attack<%s, %s, %s, %s>(@%04hX #%04hX): ",
name_for_enum(WhenAllCards), name_for_enum(WhenAttackerAndActionCards), name_for_enum(WhenAttackerOrHunterSCCard), name_for_enum(WhenTargetsAndActionCards), unknown_p2->get_card_ref(), unknown_p2->get_card_id()));
auto log = s->log_stack(phosg::string_printf("apply_effects_before_or_after_attack<%s, %s, %s, %s>(@%04hX #%04hX): ",
phosg::name_for_enum(WhenAllCards), phosg::name_for_enum(WhenAttackerAndActionCards), phosg::name_for_enum(WhenAttackerOrHunterSCCard), phosg::name_for_enum(WhenTargetsAndActionCards), unknown_p2->get_card_ref(), unknown_p2->get_card_id()));
ActionState as = this->create_attack_state_from_card_action_chain(unknown_p2);
File diff suppressed because it is too large Load Diff
+67 -32
View File
@@ -16,6 +16,7 @@
#include "../PlayerSubordinates.hh"
#include "../Text.hh"
#include "../TextIndex.hh"
#include "../Types.hh"
namespace Episode3 {
@@ -506,7 +507,7 @@ struct CardDefinition {
void decode_code();
std::string str() const;
JSON json() const;
phosg::JSON json() const;
} __packed_ws__(Stat, 4);
struct Effect {
@@ -543,7 +544,7 @@ struct CardDefinition {
bool is_empty() const;
static std::string str_for_arg(const std::string& arg);
std::string str(const char* separator = ", ", const TextSet* text_archive = nullptr) const;
JSON json() const;
phosg::JSON json() const;
} __packed_ws__(Effect, 0x20);
/* 0000 */ be_uint32_t card_id;
@@ -810,7 +811,7 @@ struct CardDefinition {
void decode_range();
std::string str(bool single_line = true, const TextSet* text_archive = nullptr) const;
JSON json() const;
phosg::JSON json() const;
} __packed_ws__(CardDefinition, 0x128);
struct CardDefinitionsFooter {
@@ -907,12 +908,12 @@ struct PlayerConfig {
// card counts array is encrypted in memory most of the time, and they went
// out of their way to ensure the game uses an area of memory that almost no
// other game uses, which is also used by the Action Replay.)
/* 05A4:0450 */ parray<be_uint64_t, 0x1C2> rare_tokens;
/* 05A4:0450 */ parray<be_uint64_t, 450> rare_tokens;
/* 13B4:1260 */ parray<uint8_t, 0x80> unknown_a7;
/* 1434:12E0 */ parray<DeckDefinition, 25> decks;
/* 2118:1FC4 */ parray<uint8_t, 0x08> unknown_a8;
/* 2120:1FCC */ be_uint32_t offline_clv_exp; // CLvOff = this / 100
/* 2124:1FD0 */ be_uint32_t online_clv_exp; // CLvOn = this / 100
/* 2120:1FCC */ be_uint32_t offline_clv_exp; // CLvOff = (this / 100) + 1
/* 2124:1FD0 */ be_uint32_t online_clv_exp; // CLvOn = (this / 100) + 1
struct PlayerReference {
/* 00 */ be_uint32_t guild_card_number;
/* 04 */ pstring<TextEncoding::MARKED, 0x18> name;
@@ -941,6 +942,40 @@ struct PlayerConfig {
void encrypt(uint8_t basis);
} __packed_ws__(PlayerConfig, 0x2350);
struct PlayerConfigNTE {
/* 0000 */ pstring<TextEncoding::MARKED, 12> rank_text;
/* 000C */ parray<uint8_t, 0x1C> unknown_a1;
/* 0028 */ parray<be_uint16_t, 20> tech_menu_shortcut_entries;
/* 0050 */ parray<be_uint32_t, 10> choice_search_config;
/* 0078 */ parray<be_uint32_t, 0x10> scenario_progress; // Final has 0x30 entries here
/* 00B8 */ PlayerRecordsBattleBE unused_offline_records;
/* 00D0 */ parray<uint8_t, 4> unknown_a4;
/* 00D4 */ uint8_t is_encrypted;
/* 00D5 */ uint8_t basis;
/* 00D6 */ parray<uint8_t, 2> unused;
/* 00D8 */ parray<uint8_t, 1000> card_counts;
/* 04C0 */ parray<be_uint16_t, 50> card_count_checksums;
/* 0524 */ parray<be_uint64_t, 300> rare_tokens;
/* 0E84 */ parray<DeckDefinition, 25> decks;
/* 1B68 */ parray<uint8_t, 0x08> unknown_a8;
/* 1B70 */ be_uint32_t offline_clv_exp;
/* 1B74 */ be_uint32_t online_clv_exp;
/* 1B78 */ parray<PlayerConfig::PlayerReference, 10> recent_human_opponents;
/* 1C90 */ parray<uint8_t, 0x28> unknown_a10;
/* 1CB8 */ be_uint32_t init_timestamp;
/* 1CBC */ be_uint32_t last_online_battle_start_timestamp;
/* 1CC0 */ be_uint32_t unknown_t3;
/* 1CC4 */ parray<uint8_t, 0x94> unknown_a14;
/* 1D58 */
PlayerConfigNTE() = default;
explicit PlayerConfigNTE(const PlayerConfig& config);
operator PlayerConfig() const;
void decrypt();
void encrypt(uint8_t basis);
} __packed_ws__(PlayerConfigNTE, 0x1D58);
enum class HPType : uint8_t {
DEFEAT_PLAYER = 0,
DEFEAT_TEAM = 1,
@@ -995,8 +1030,8 @@ struct Rules {
// likely be more work than it's worth.
Rules() = default;
explicit Rules(const JSON& json);
JSON json() const;
explicit Rules(const phosg::JSON& json);
phosg::JSON json() const;
bool operator==(const Rules& other) const = default;
bool operator!=(const Rules& other) const = default;
void clear();
@@ -1191,7 +1226,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
/* 48 */
std::string str() const;
JSON json() const;
phosg::JSON json() const;
} __packed_ws__(CameraSpec, 0x48);
// This array specifies the camera zone maps. A camera zone map is a subset of
@@ -1249,7 +1284,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
/* 00 */ pstring<TextEncoding::MARKED, 0x18> deck_name;
/* 18 */ parray<be_uint16_t, 0x20> card_ids; // Last one appears to always be FFFF
/* 58 */
JSON json(uint8_t language) const;
phosg::JSON json(uint8_t language) const;
} __packed_ws__(NPCDeck, 0x58);
/* 1FE8 */ parray<NPCDeck, 3> npc_decks; // Unused if name[0] == 0
@@ -1265,7 +1300,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
// TODO: Figure out exactly how these are used and document here.
/* 0018 */ parray<be_uint16_t, 0x7E> params;
/* 0114 */
JSON json(uint8_t language) const;
phosg::JSON json(uint8_t language) const;
} __packed_ws__(AIParams, 0x114);
/* 20F0 */ parray<AIParams, 3> npc_ai_params; // Unused if name[0] == 0
@@ -1318,7 +1353,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
// strings, excluding any that are empty or begin with the character '^'.
/* 0004 */ parray<pstring<TextEncoding::MARKED, 0x40>, 4> strings;
/* 0104 */
JSON json(uint8_t language) const;
phosg::JSON json(uint8_t language) const;
} __packed_ws__(DialogueSet, 0x104);
// There are up to 0x10 of these per valid NPC, but only the first 13 of them
@@ -1401,7 +1436,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
bool operator==(const EntryState& other) const = default;
bool operator!=(const EntryState& other) const = default;
JSON json() const;
phosg::JSON json() const;
} __packed_ws__(EntryState, 2);
/* 5A10 */ parray<EntryState, 4> entry_states;
/* 5A18 */
@@ -1417,7 +1452,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
void assert_semantically_equivalent(const MapDefinition& other) const;
std::string str(const CardIndex* card_index, uint8_t language) const;
JSON json(uint8_t language) const;
phosg::JSON json(uint8_t language) const;
} __packed_ws__(MapDefinition, 0x5A18);
struct MapDefinitionTrial {
@@ -1500,7 +1535,7 @@ public:
std::shared_ptr<const CardEntry> definition_for_name_normalized(const std::string& name) const;
std::set<uint32_t> all_ids() const;
uint64_t definitions_mtime() const;
JSON definitions_json() const;
phosg::JSON definitions_json() const;
private:
static std::string normalize_card_name(const std::string& name);
@@ -1581,37 +1616,37 @@ private:
// TODO: Figure out how to declare these inside the Episode3 namespace.
template <>
Episode3::HPType enum_for_name<Episode3::HPType>(const char* name);
Episode3::HPType phosg::enum_for_name<Episode3::HPType>(const char* name);
template <>
const char* name_for_enum<Episode3::HPType>(Episode3::HPType hp_type);
const char* phosg::name_for_enum<Episode3::HPType>(Episode3::HPType hp_type);
template <>
Episode3::DiceExchangeMode enum_for_name<Episode3::DiceExchangeMode>(const char* name);
Episode3::DiceExchangeMode phosg::enum_for_name<Episode3::DiceExchangeMode>(const char* name);
template <>
const char* name_for_enum<Episode3::DiceExchangeMode>(Episode3::DiceExchangeMode dice_exchange_mode);
const char* phosg::name_for_enum<Episode3::DiceExchangeMode>(Episode3::DiceExchangeMode dice_exchange_mode);
template <>
Episode3::AllowedCards enum_for_name<Episode3::AllowedCards>(const char* name);
Episode3::AllowedCards phosg::enum_for_name<Episode3::AllowedCards>(const char* name);
template <>
const char* name_for_enum<Episode3::AllowedCards>(Episode3::AllowedCards allowed_cards);
const char* phosg::name_for_enum<Episode3::AllowedCards>(Episode3::AllowedCards allowed_cards);
template <>
const char* name_for_enum<Episode3::BattlePhase>(Episode3::BattlePhase phase);
const char* phosg::name_for_enum<Episode3::BattlePhase>(Episode3::BattlePhase phase);
template <>
const char* name_for_enum<Episode3::SetupPhase>(Episode3::SetupPhase phase);
const char* phosg::name_for_enum<Episode3::SetupPhase>(Episode3::SetupPhase phase);
template <>
const char* name_for_enum<Episode3::RegistrationPhase>(Episode3::RegistrationPhase phase);
const char* phosg::name_for_enum<Episode3::RegistrationPhase>(Episode3::RegistrationPhase phase);
template <>
const char* name_for_enum<Episode3::ActionSubphase>(Episode3::ActionSubphase phase);
const char* phosg::name_for_enum<Episode3::ActionSubphase>(Episode3::ActionSubphase phase);
template <>
const char* name_for_enum<Episode3::AttackMedium>(Episode3::AttackMedium medium);
const char* phosg::name_for_enum<Episode3::AttackMedium>(Episode3::AttackMedium medium);
template <>
const char* name_for_enum<Episode3::CriterionCode>(Episode3::CriterionCode code);
const char* phosg::name_for_enum<Episode3::CriterionCode>(Episode3::CriterionCode code);
template <>
const char* name_for_enum<Episode3::CardType>(Episode3::CardType type);
const char* phosg::name_for_enum<Episode3::CardType>(Episode3::CardType type);
template <>
const char* name_for_enum<Episode3::CardClass>(Episode3::CardClass cc);
const char* phosg::name_for_enum<Episode3::CardClass>(Episode3::CardClass cc);
template <>
const char* name_for_enum<Episode3::ConditionType>(Episode3::ConditionType cond_type);
const char* phosg::name_for_enum<Episode3::ConditionType>(Episode3::ConditionType cond_type);
template <>
const char* name_for_enum<Episode3::EffectWhen>(Episode3::EffectWhen when);
const char* phosg::name_for_enum<Episode3::EffectWhen>(Episode3::EffectWhen when);
template <>
const char* name_for_enum<Episode3::Direction>(Episode3::Direction d);
const char* phosg::name_for_enum<Episode3::Direction>(Episode3::Direction d);
+4 -4
View File
@@ -1780,7 +1780,7 @@ bool PlayerState::set_action_cards_for_action_state(const ActionState& pa) {
auto card = s->card_for_set_card_ref(pa.attacker_card_ref);
if (card) {
card->loc.direction = pa.facing_direction;
log.debug("set facing direction to %s", name_for_enum(card->loc.direction));
log.debug("set facing direction to %s", phosg::name_for_enum(card->loc.direction));
G_AddToSetCardlog_Ep3_6xB4x4A cmd;
cmd.card_refs.clear(0xFFFF);
@@ -1789,7 +1789,7 @@ bool PlayerState::set_action_cards_for_action_state(const ActionState& pa) {
cmd.entry_count = 0;
size_t z = 0;
do {
if (log.should_log(LogLevel::DEBUG)) {
if (log.should_log(phosg::LogLevel::DEBUG)) {
string ref_str = s->debug_str_for_card_ref(pa.action_card_refs[z]);
log.debug("on action card ref %s", ref_str.c_str());
}
@@ -1826,7 +1826,7 @@ bool PlayerState::set_action_cards_for_action_state(const ActionState& pa) {
for (size_t z = 0; (z < 4 * 9) && (pa.target_card_refs[z] != 0xFFFF); z++) {
auto target_card = s->card_for_set_card_ref(pa.target_card_refs[z]);
if (target_card) {
if (log.should_log(LogLevel::DEBUG)) {
if (log.should_log(phosg::LogLevel::DEBUG)) {
string ref_str = s->debug_str_for_card_ref(pa.target_card_refs[z]);
log.debug("on target card ref %s", ref_str.c_str());
}
@@ -1856,7 +1856,7 @@ bool PlayerState::set_action_cards_for_action_state(const ActionState& pa) {
this->subtract_or_check_atk_or_def_points_for_action(pa, 1);
}
for (size_t z = 0; (z < pa.action_card_refs.size()) && (pa.action_card_refs[z] != 0xFFFF); z++) {
if (log.should_log(LogLevel::DEBUG)) {
if (log.should_log(phosg::LogLevel::DEBUG)) {
string ref_str = s->debug_str_for_card_ref(pa.action_card_refs[z]);
log.debug("discarding %s from hand", ref_str.c_str());
}
+15 -15
View File
@@ -64,11 +64,11 @@ void Condition::clear_FF() {
std::string Condition::str(shared_ptr<const Server> s) const {
auto card_ref_str = s->debug_str_for_card_ref(this->card_ref);
auto giver_ref_str = s->debug_str_for_card_ref(this->condition_giver_card_ref);
return string_printf(
return phosg::string_printf(
"Condition[type=%s, turns=%hhu, a_arg=%hhd, dice=%hhu, flags=%02hhX, "
"def_eff_index=%hhu, ref=%s, value=%hd, giver_ref=%s "
"percent=%hhu value8=%hd order=%hu a8=%hu]",
name_for_enum(this->type),
phosg::name_for_enum(this->type),
this->remaining_turns,
this->a_arg_value,
this->dice_roll_value,
@@ -103,7 +103,7 @@ void EffectResult::clear() {
std::string EffectResult::str(shared_ptr<const Server> s) const {
string attacker_ref_str = s->debug_str_for_card_ref(this->attacker_card_ref);
string target_ref_str = s->debug_str_for_card_ref(this->target_card_ref);
return string_printf(
return phosg::string_printf(
"EffectResult[att_ref=%s, target_ref=%s, value=%hhd, "
"cur_hp=%hhd, ap=%hhd, tp=%hhd, flags=%02hhX, op=%hhd, "
"cond_index=%hhu, dice=%hhu]",
@@ -139,7 +139,7 @@ bool CardShortStatus::operator!=(const CardShortStatus& other) const {
std::string CardShortStatus::str(shared_ptr<const Server> s) const {
string loc_s = this->loc.str();
string ref_str = s->debug_str_for_card_ref(this->card_ref);
return string_printf(
return phosg::string_printf(
"CardShortStatus[ref=%s, cur_hp=%hd, flags=%08" PRIX32 ", loc=%s, "
"u1=%04hX, max_hp=%hhd, u2=%hhu]",
ref_str.c_str(),
@@ -193,13 +193,13 @@ std::string ActionState::str(shared_ptr<const Server> s) const {
string original_attacker_ref_s = s->debug_str_for_card_ref(this->original_attacker_card_ref);
string target_refs_s = s->debug_str_for_card_refs(this->target_card_refs);
string action_refs_s = s->debug_str_for_card_refs(this->action_card_refs);
return string_printf(
return phosg::string_printf(
"ActionState[client=%hu, u=%hhu, facing=%s, attacker_ref=%s, "
"def_ref=%s, target_refs=%s, action_refs=%s, "
"orig_attacker_ref=%s]",
this->client_id.load(),
this->unused,
name_for_enum(this->facing_direction),
phosg::name_for_enum(this->facing_direction),
attacker_ref_s.c_str(),
defense_ref_s.c_str(),
target_refs_s.c_str(),
@@ -243,7 +243,7 @@ std::string ActionChain::str(shared_ptr<const Server> s) const {
string unknown_card_ref_a3_s = s->debug_str_for_card_ref(this->unknown_card_ref_a3);
string attack_action_card_refs_s = s->debug_str_for_card_refs(this->attack_action_card_refs);
string target_card_refs_s = s->debug_str_for_card_refs(this->target_card_refs);
return string_printf(
return phosg::string_printf(
"ActionChain[eff_ap=%hhd, eff_tp=%hhd, ap_bonus=%hhd, damage=%hhd, "
"acting_ref=%s, unknown_ref_a3=%s, attack_action_refs=%s, "
"attack_action_ref_count=%hhu, medium=%s, target_ref_count=%hhu, "
@@ -258,9 +258,9 @@ std::string ActionChain::str(shared_ptr<const Server> s) const {
unknown_card_ref_a3_s.c_str(),
attack_action_card_refs_s.c_str(),
this->attack_action_card_ref_count,
name_for_enum(this->attack_medium),
phosg::name_for_enum(this->attack_medium),
this->target_card_ref_count,
name_for_enum(this->action_subphase),
phosg::name_for_enum(this->action_subphase),
this->strike_count,
this->damage_multiplier,
this->attack_number,
@@ -341,7 +341,7 @@ std::string ActionChainWithConds::str(shared_ptr<const Server> s) const {
if (ret.back() != '[') {
ret += ", ";
}
ret += string_printf("%zu:", z);
ret += phosg::string_printf("%zu:", z);
ret += this->conditions[z].str(s);
}
}
@@ -580,7 +580,7 @@ std::string ActionMetadata::str(shared_ptr<const Server> s) const {
string target_card_refs_s = s->debug_str_for_card_refs(this->target_card_refs);
string defense_card_refs_s = s->debug_str_for_card_refs(this->defense_card_refs);
string original_attacker_card_refs_s = s->debug_str_for_card_refs(this->original_attacker_card_refs);
return string_printf(
return phosg::string_printf(
"ActionMetadata[ref=%s, target_ref_count=%hhu, def_ref_count=%hhu, "
"subphase=%s, def_power=%hhd, def_bonus=%hhd, "
"att_bonus=%hhd, flags=%08" PRIX32 ", target_refs=%s, "
@@ -588,7 +588,7 @@ std::string ActionMetadata::str(shared_ptr<const Server> s) const {
card_ref_s.c_str(),
this->target_card_ref_count,
this->defense_card_ref_count,
name_for_enum(this->action_subphase),
phosg::name_for_enum(this->action_subphase),
this->defense_power,
this->defense_bonus,
this->attack_bonus,
@@ -683,7 +683,7 @@ std::string HandAndEquipState::str(shared_ptr<const Server> s) const {
string set_card_refs_s = s->debug_str_for_card_refs(this->set_card_refs);
string hand_card_refs2_s = s->debug_str_for_card_refs(this->hand_card_refs2);
string set_card_refs2_s = s->debug_str_for_card_refs(this->set_card_refs2);
return string_printf(
return phosg::string_printf(
"HandAndEquipState[dice=[%hhu, %hhu], atk=%hhu, def=%hhu, atk2=%hhu, "
"a1=%hhu, total_set_cost=%hhu, is_cpu=%hhu, assist_flags=%08" PRIX32 ", "
"hand_refs=%s, assist_ref=%s, set_refs=%s, sc_ref=%s, hand_refs2=%s, "
@@ -858,7 +858,7 @@ static bool is_card_within_range(
const parray<uint8_t, 9 * 9>& range,
const Location& anchor_loc,
const CardShortStatus& ss,
PrefixedLogger* log) {
phosg::PrefixedLogger* log) {
if (ss.card_ref == 0xFFFF) {
if (log) {
log->debug("is_card_within_range: (false) ss.card_ref missing");
@@ -899,7 +899,7 @@ vector<uint16_t> get_card_refs_within_range(
const parray<uint8_t, 9 * 9>& range,
const Location& loc,
const parray<CardShortStatus, 0x10>& short_statuses,
PrefixedLogger* log) {
phosg::PrefixedLogger* log) {
vector<uint16_t> ret;
if (is_card_within_range(range, loc, short_statuses[0], log)) {
if (log) {
+1 -1
View File
@@ -329,6 +329,6 @@ std::vector<uint16_t> get_card_refs_within_range(
const parray<uint8_t, 9 * 9>& range,
const Location& loc,
const parray<CardShortStatus, 0x10>& short_statuses,
PrefixedLogger* log = nullptr);
phosg::PrefixedLogger* log = nullptr);
} // namespace Episode3
+6 -6
View File
@@ -15,8 +15,8 @@ void compute_effective_range(
uint16_t card_id,
const Location& loc,
shared_ptr<const MapAndRulesState> map_and_rules,
PrefixedLogger* log) {
if (log && log->should_log(LogLevel::DEBUG)) {
phosg::PrefixedLogger* log) {
if (log && log->should_log(phosg::LogLevel::DEBUG)) {
string loc_str = loc.str();
log->debug("compute_effective_range: card_id=#%04hX, loc=%s", card_id, loc_str.c_str());
log->debug("compute_effective_range: map_and_rules->map:");
@@ -941,7 +941,7 @@ bool RulerServer::check_usability_or_condition_apply(
AttackMedium attack_medium) const {
auto s = this->server();
bool is_nte = s->options.is_nte();
auto log = s->log_stack(string_printf("check_usability_or_condition_apply(%02hhX, #%04hX, %02hhX, #%04hX, #%04hX, %02hhX, %s, %s): ", client_id1, card_id1, client_id2, card_id2, card_id3, def_effect_index, is_item_usability_check ? "true" : "false", name_for_enum(attack_medium)));
auto log = s->log_stack(phosg::string_printf("check_usability_or_condition_apply(%02hhX, #%04hX, %02hhX, #%04hX, #%04hX, %02hhX, %s, %s): ", client_id1, card_id1, client_id2, card_id2, card_id3, def_effect_index, is_item_usability_check ? "true" : "false", phosg::name_for_enum(attack_medium)));
if (static_cast<uint8_t>(attack_medium) & 0x80) {
attack_medium = AttackMedium::UNKNOWN;
@@ -969,7 +969,7 @@ bool RulerServer::check_usability_or_condition_apply(
}
criterion_code = ce1->def.effects[def_effect_index].apply_criterion;
}
log.debug("criterion_code=%s", name_for_enum(criterion_code));
log.debug("criterion_code=%s", phosg::name_for_enum(criterion_code));
// For item usability checks, prevent criteria that depend on player
// positioning/team setup
@@ -1865,7 +1865,7 @@ int32_t RulerServer::error_code_for_client_setting_card(
return -0x7E;
}
int32_t x_offset, y_offset;
int32_t x_offset = 0, y_offset = 0;
this->offsets_for_direction(summon_area_loc, &x_offset, &y_offset);
if (x_offset == 0) {
if ((loc->x < 1) && (loc->x >= this->map_and_rules->map.width - 1)) {
@@ -2059,7 +2059,7 @@ shared_ptr<const CardIndex::CardEntry> RulerServer::definition_for_card_id(uint3
uint32_t RulerServer::get_card_id_with_effective_range(
uint16_t card_ref, uint16_t card_id_override, TargetMode* out_target_mode) const {
auto log = this->server()->log_stack(string_printf("get_card_id_with_effective_range(@%04hX, #%04hX): ", card_ref, card_id_override));
auto log = this->server()->log_stack(phosg::string_printf("get_card_id_with_effective_range(@%04hX, #%04hX): ", card_ref, card_id_override));
uint16_t card_id = (card_id_override == 0xFFFF)
? this->card_id_for_card_ref(card_ref)
+1 -1
View File
@@ -19,7 +19,7 @@ void compute_effective_range(
uint16_t card_id,
const Location& loc,
std::shared_ptr<const MapAndRulesState> map_and_rules,
PrefixedLogger* log = nullptr);
phosg::PrefixedLogger* log = nullptr);
bool card_linkage_is_valid(
std::shared_ptr<const CardIndex::CardEntry> right_def,
+24 -24
View File
@@ -82,7 +82,7 @@ Server::Server(shared_ptr<Lobby> lobby, Options&& options)
Server::~Server() noexcept(false) {
if (this->logger_stack.size() != 1) {
throw logic_error(string_printf("incorrect logger stack size: expected 1, received %zu", this->logger_stack.size()));
throw logic_error(phosg::string_printf("incorrect logger stack size: expected 1, received %zu", this->logger_stack.size()));
}
delete this->logger_stack.back();
}
@@ -125,7 +125,7 @@ Server::StackLogger::StackLogger(const Server* s, const std::string& prefix)
s->logger_stack.push_back(this);
}
Server::StackLogger::StackLogger(const Server* s, const std::string& prefix, LogLevel min_level)
Server::StackLogger::StackLogger(const Server* s, const std::string& prefix, phosg::LogLevel min_level)
: PrefixedLogger(prefix, min_level),
server(s) {
s->logger_stack.push_back(this);
@@ -172,9 +172,9 @@ std::string Server::debug_str_for_card_ref(uint16_t card_ref) const {
auto ce = this->definition_for_card_ref(card_ref);
if (ce) {
string name = ce->def.en_name.decode();
return string_printf("@%04hX (#%04" PRIX32 " %s)", card_ref, ce->def.card_id.load(), name.c_str());
return phosg::string_printf("@%04hX (#%04" PRIX32 " %s)", card_ref, ce->def.card_id.load(), name.c_str());
} else {
return string_printf("@%04hX (missing)", card_ref);
return phosg::string_printf("@%04hX (missing)", card_ref);
}
}
@@ -185,9 +185,9 @@ std::string Server::debug_str_for_card_id(uint16_t card_id) const {
auto ce = this->definition_for_card_id(card_id);
if (ce) {
string name = ce->def.en_name.decode();
return string_printf("#%04hX (%s)", card_id, name.c_str());
return phosg::string_printf("#%04hX (%s)", card_id, name.c_str());
} else {
return string_printf("#%04hX (missing)", card_id);
return phosg::string_printf("#%04hX (missing)", card_id);
}
}
@@ -240,7 +240,7 @@ void Server::send(const void* data, size_t size, uint8_t command, bool enable_ma
!(this->options.behavior_flags & BehaviorFlag::DISABLE_MASKING) &&
(size >= 8)) {
masked_data.assign(reinterpret_cast<const char*>(data), size);
uint8_t mask_key = (random_object<uint32_t>() % 0xFF) + 1;
uint8_t mask_key = (phosg::random_object<uint32_t>() % 0xFF) + 1;
set_mask_for_ep3_game_command(masked_data.data(), masked_data.size(), mask_key);
data = masked_data.data();
size = masked_data.size();
@@ -260,7 +260,7 @@ void Server::send(const void* data, size_t size, uint8_t command, bool enable_ma
} else if ((this->options.behavior_flags & BehaviorFlag::LOG_COMMANDS_IF_LOBBY_MISSING) &&
this->log().info("Generated command")) {
print_data(stderr, data, size, 0, nullptr, PrintDataFlags::PRINT_ASCII | PrintDataFlags::DISABLE_COLOR | PrintDataFlags::OFFSET_16_BITS);
phosg::print_data(stderr, data, size, 0, nullptr, phosg::PrintDataFlags::PRINT_ASCII | phosg::PrintDataFlags::DISABLE_COLOR | phosg::PrintDataFlags::OFFSET_16_BITS);
}
}
@@ -272,9 +272,9 @@ void Server::send_6xB4x46() const {
// debugging easier.
G_ServerVersionStrings_Ep3_6xB4x46 cmd;
cmd.version_signature.encode(this->options.is_nte() ? VERSION_SIGNATURE_NTE : VERSION_SIGNATURE, 1);
cmd.date_str1.encode(format_time(this->options.card_index->definitions_mtime() * 1000000), 1);
string build_date = format_time(BUILD_TIMESTAMP);
cmd.date_str2.encode(string_printf("newserv %s compiled at %s", GIT_REVISION_HASH, build_date.c_str()), 1);
cmd.date_str1.encode(phosg::format_time(this->options.card_index->definitions_mtime() * 1000000), 1);
string build_date = phosg::format_time(BUILD_TIMESTAMP);
cmd.date_str2.encode(phosg::string_printf("newserv %s compiled at %s", GIT_REVISION_HASH, build_date.c_str()), 1);
this->send(cmd);
}
@@ -283,7 +283,7 @@ string Server::prepare_6xB6x41_map_definition(shared_ptr<const MapIndex::Map> ma
const auto& compressed = vm->compressed(is_nte);
StringWriter w;
phosg::StringWriter w;
uint32_t subcommand_size = (compressed.size() + sizeof(G_MapData_Ep3_6xB6x41) + 3) & (~3);
w.put<G_MapData_Ep3_6xB6x41>({{{{0xB6, 0, 0}, subcommand_size}, 0x41, {}}, vm->map->map_number.load(), compressed.size(), 0});
w.write(compressed);
@@ -347,7 +347,7 @@ __attribute__((format(printf, 2, 3))) void Server::send_debug_message_printf(con
if (l && (this->options.behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
va_list va;
va_start(va, fmt);
std::string buf = string_vprintf(fmt, va);
std::string buf = phosg::string_vprintf(fmt, va);
va_end(va);
send_text_message(l, buf);
}
@@ -358,7 +358,7 @@ __attribute__((format(printf, 2, 3))) void Server::send_info_message_printf(cons
if (l) {
va_list va;
va_start(va, fmt);
std::string buf = string_vprintf(fmt, va);
std::string buf = phosg::string_vprintf(fmt, va);
va_end(va);
send_text_message(l, buf);
}
@@ -862,7 +862,7 @@ void Server::draw_phase_after() {
// facilities used are different.
uint64_t limit_5mins = this->map_and_rules->rules.overall_time_limit;
uint64_t end_usecs = this->battle_start_usecs + (limit_5mins * 300 * 1000 * 1000);
if (now() >= end_usecs) {
if (phosg::now() >= end_usecs) {
this->overall_time_expired = true;
}
}
@@ -972,7 +972,7 @@ void Server::end_action_phase() {
bool Server::enqueue_attack_or_defense(uint8_t client_id, ActionState* pa) {
auto log = this->log_stack("enqueue_attack_or_defense: ");
if (log.should_log(LogLevel::DEBUG)) {
if (log.should_log(phosg::LogLevel::DEBUG)) {
string s = pa->str(this->shared_from_this());
log.debug("input: %s", s.c_str());
}
@@ -1022,7 +1022,7 @@ bool Server::enqueue_attack_or_defense(uint8_t client_id, ActionState* pa) {
size_t attack_index = this->num_pending_attacks++;
this->pending_attacks[attack_index] = *pa;
if (log.should_log(LogLevel::DEBUG)) {
if (log.should_log(phosg::LogLevel::DEBUG)) {
string pa_str = this->pending_attacks[attack_index].str(this->shared_from_this());
log.debug("set pending attack %zu: %s", attack_index, pa_str.c_str());
}
@@ -1711,7 +1711,7 @@ void Server::setup_and_start_battle() {
cmd.start_battle = 1;
this->send(cmd);
}
this->battle_start_usecs = now();
this->battle_start_usecs = phosg::now();
this->send_6xB4x46();
@@ -1831,8 +1831,8 @@ void Server::on_server_data_input(shared_ptr<Client> sender_c, const string& dat
auto header = check_size_t<G_CardBattleCommandHeader>(data, 0xFFFF);
size_t expected_size = header.size * 4;
if (expected_size < data.size()) {
print_data(stderr, data);
throw runtime_error(string_printf("command is incomplete: expected %zX bytes, received %zX bytes", expected_size, data.size()));
phosg::print_data(stderr, data);
throw runtime_error(phosg::string_printf("command is incomplete: expected %zX bytes, received %zX bytes", expected_size, data.size()));
}
if (header.subcommand != 0xB3) {
throw runtime_error("server data command is not 6xB3");
@@ -2259,7 +2259,7 @@ void Server::handle_CAx14_update_deck_during_setup(shared_ptr<Client>, const str
}
}
if (verify_error) {
throw runtime_error(string_printf("invalid deck: -0x%" PRIX32, verify_error));
throw runtime_error(phosg::string_printf("invalid deck: -0x%" PRIX32, verify_error));
}
if (!this->options.is_nte() && !(this->options.behavior_flags & BehaviorFlag::SKIP_D1_D2_REPLACE)) {
this->ruler_server->replace_D1_D2_rank_cards_with_Attack(entry.card_ids);
@@ -2579,7 +2579,7 @@ void Server::handle_CAx40_map_list_request(shared_ptr<Client> sender_c, const st
uint8_t language = sender_c ? sender_c->language() : 1;
const auto& list_data = this->options.map_index->get_compressed_list(num_players, language);
StringWriter w;
phosg::StringWriter w;
uint32_t subcommand_size = (list_data.size() + sizeof(G_MapList_Ep3_6xB6x40) + 3) & (~3);
w.put<G_MapList_Ep3_6xB6x40>(G_MapList_Ep3_6xB6x40{{{{0xB6, 0, 0}, subcommand_size}, 0x40, {}}, list_data.size(), 0});
w.write(list_data);
@@ -2828,7 +2828,7 @@ void Server::unknown_8023EEF4() {
auto card = this->attack_cards[this->unknown_a14];
if (this->get_current_team_turn() == card->get_team_id()) {
ActionState as = this->pending_attacks_with_cards[this->unknown_a14];
if (log.should_log(LogLevel::DEBUG)) {
if (log.should_log(phosg::LogLevel::DEBUG)) {
log.debug("card @%04hX #%04hX can attack", card->get_card_ref(), card->get_card_id());
string as_str = as.str(this->shared_from_this());
log.debug("as: %s", as_str.c_str());
@@ -2838,7 +2838,7 @@ void Server::unknown_8023EEF4() {
} else {
this->replace_targets_due_to_destruction_or_conditions(&as);
}
if (log.should_log(LogLevel::DEBUG)) {
if (log.should_log(phosg::LogLevel::DEBUG)) {
string as_str = as.str(this->shared_from_this());
log.debug("as after target replacement: %s", as_str.c_str());
}
+4 -4
View File
@@ -72,7 +72,7 @@ public:
std::shared_ptr<const CardIndex> card_index;
std::shared_ptr<const MapIndex> map_index;
uint32_t behavior_flags;
std::shared_ptr<StringReader> opt_rand_stream;
std::shared_ptr<phosg::StringReader> opt_rand_stream;
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt;
std::shared_ptr<const Tournament> tournament;
std::array<std::vector<uint16_t>, 5> trap_card_ids;
@@ -85,10 +85,10 @@ public:
~Server() noexcept(false);
void init();
class StackLogger : public PrefixedLogger {
class StackLogger : public phosg::PrefixedLogger {
public:
StackLogger(const Server* s, const std::string& prefix);
StackLogger(const Server* s, const std::string& prefix, LogLevel min_level);
StackLogger(const Server* s, const std::string& prefix, phosg::LogLevel min_level);
StackLogger(const StackLogger&) = delete;
StackLogger(StackLogger&&);
StackLogger& operator=(const StackLogger&) = delete;
@@ -109,7 +109,7 @@ public:
for (size_t z = 0; z < count; z++) {
if (refs[z] != 0xFFFF) {
std::string ref_str = this->debug_str_for_card_ref(refs[z]);
ret += string_printf("%zu:%s ", z, ref_str.c_str());
ret += phosg::string_printf("%zu:%s ", z, ref_str.c_str());
}
}
if (ret.size() > 1) {
+26 -26
View File
@@ -49,16 +49,16 @@ string Tournament::Team::str() const {
num_com_players += player.is_com();
}
string ret = string_printf("[Team/%zu %s %zuH/%zuC/%zuP name=%s pass=%s rounds=%zu",
string ret = phosg::string_printf("[Team/%zu %s %zuH/%zuC/%zuP name=%s pass=%s rounds=%zu",
this->index, this->is_active ? "active" : "inactive",
num_human_players, num_com_players, this->max_players, this->name.c_str(),
this->password.c_str(), this->num_rounds_cleared);
for (const auto& player : this->players) {
if (player.is_human()) {
if (player.player_name.empty()) {
ret += string_printf(" %08" PRIX32, player.account_id);
ret += phosg::string_printf(" %08" PRIX32, player.account_id);
} else {
ret += string_printf(" %08" PRIX32 " (%s)", player.account_id, player.player_name.c_str());
ret += phosg::string_printf(" %08" PRIX32 " (%s)", player.account_id, player.player_name.c_str());
}
}
}
@@ -206,7 +206,7 @@ Tournament::Match::Match(
string Tournament::Match::str() const {
string winner_str = this->winner_team ? this->winner_team->str() : "(none)";
return string_printf("[Match round=%zu winner=%s]", this->round_num, winner_str.c_str());
return phosg::string_printf("[Match round=%zu winner=%s]", this->round_num, winner_str.c_str());
}
bool Tournament::Match::resolve_if_skippable() {
@@ -230,7 +230,7 @@ bool Tournament::Match::resolve_if_skippable() {
// entirely and just make one team advance arbitrarily (note that this also
// handles the case where both preceding winner teams are empty)
if (!winner_a->has_any_human_players() && !winner_b->has_any_human_players()) {
this->set_winner_team((random_object<uint8_t>() & 1) ? winner_b : winner_a);
this->set_winner_team((phosg::random_object<uint8_t>() & 1) ? winner_b : winner_a);
return true;
}
@@ -318,7 +318,7 @@ Tournament::Tournament(
const Rules& rules,
size_t num_teams,
uint8_t flags)
: log(string_printf("[Tournament:%s] ", name.c_str())),
: log(phosg::string_printf("[Tournament:%s] ", name.c_str())),
map_index(map_index),
com_deck_index(com_deck_index),
name(name),
@@ -342,8 +342,8 @@ Tournament::Tournament(
Tournament::Tournament(
shared_ptr<const MapIndex> map_index,
shared_ptr<const COMDeckIndex> com_deck_index,
const JSON& json)
: log(string_printf("[Tournament:%s] ", json.get_string("name").c_str())),
const phosg::JSON& json)
: log(phosg::string_printf("[Tournament:%s] ", json.get_string("name").c_str())),
map_index(map_index),
com_deck_index(com_deck_index),
source_json(json),
@@ -504,14 +504,14 @@ void Tournament::create_bracket_matches() {
this->final_match = current_round_matches.at(0);
}
JSON Tournament::json() const {
auto teams_list = JSON::list();
phosg::JSON Tournament::json() const {
auto teams_list = phosg::JSON::list();
for (auto team : this->teams) {
auto players_list = JSON::list();
auto players_list = phosg::JSON::list();
for (const auto& player : team->players) {
if (player.is_human()) {
if (!player.player_name.empty()) {
players_list.emplace_back(JSON::list({player.account_id, player.player_name}));
players_list.emplace_back(phosg::JSON::list({player.account_id, player.player_name}));
} else {
players_list.emplace_back(player.account_id);
}
@@ -519,7 +519,7 @@ JSON Tournament::json() const {
players_list.emplace_back(player.com_deck->deck_name);
}
}
teams_list.emplace_back(JSON::dict({
teams_list.emplace_back(phosg::JSON::dict({
{"max_players", team->max_players},
{"player_specs", std::move(players_list)},
{"name", team->name},
@@ -527,7 +527,7 @@ JSON Tournament::json() const {
{"num_rounds_cleared", team->num_rounds_cleared},
}));
}
return JSON::dict({
return phosg::JSON::dict({
{"name", this->name},
{"map_number", this->map->map_number},
{"rules", this->rules.json()},
@@ -649,7 +649,7 @@ void Tournament::start() {
if (this->flags & Flag::SHUFFLE_ENTRIES) {
// Shuffle all the tournament entries
for (size_t z = this->teams.size(); z > 0; z--) {
size_t index = random_object<uint32_t>() % z;
size_t index = phosg::random_object<uint32_t>() % z;
if (index != z - 1) {
this->teams[z - 1].swap(this->teams[index]);
}
@@ -665,7 +665,7 @@ void Tournament::start() {
auto m = this->zero_round_matches[z];
auto t = m->winner_team;
if (t->name.empty()) {
t->name = has_com_teams ? string_printf("COM:%zu", z) : "(no entrant)";
t->name = has_com_teams ? phosg::string_printf("COM:%zu", z) : "(no entrant)";
}
for (const auto& player : t->players) {
if (player.is_com()) {
@@ -791,16 +791,16 @@ TournamentIndex::TournamentIndex(
return;
}
JSON json;
phosg::JSON json;
try {
json = JSON::parse(load_file(this->state_filename));
} catch (const cannot_open_file&) {
json = JSON::list();
json = phosg::JSON::parse(phosg::load_file(this->state_filename));
} catch (const phosg::cannot_open_file&) {
json = phosg::JSON::list();
}
if (json.is_list()) {
if (json.size() > 0x20) {
throw runtime_error("tournament JSON list length is incorrect");
throw runtime_error("tournament phosg::JSON list length is incorrect");
}
for (size_t z = 0; z < min<size_t>(json.size(), 0x20); z++) {
if (!json.at(z).is_null()) {
@@ -815,13 +815,13 @@ TournamentIndex::TournamentIndex(
}
} else if (json.is_dict()) {
if (json.size() > 0x20) {
throw runtime_error("tournament JSON dict length is incorrect");
throw runtime_error("tournament phosg::JSON dict length is incorrect");
}
for (const auto& it : json.as_dict()) {
auto tourn = make_shared<Tournament>(this->map_index, this->com_deck_index, *it.second);
tourn->init();
if (!this->name_to_tournament.emplace(tourn->get_name(), tourn).second) {
// This is logic_error instead of runtime_error because JSON dicts are
// This is logic_error instead of runtime_error because phosg::JSON dicts are
// supposed to already have unique keys
throw logic_error("multiple tournaments have the same name: " + tourn->get_name());
}
@@ -829,7 +829,7 @@ TournamentIndex::TournamentIndex(
this->menu_item_id_to_tournament.emplace_back(tourn);
}
} else {
throw runtime_error("tournament state root JSON is not a list or dict");
throw runtime_error("tournament state root phosg::JSON is not a list or dict");
}
}
@@ -838,11 +838,11 @@ void TournamentIndex::save() const {
return;
}
auto json = JSON::dict();
auto json = phosg::JSON::dict();
for (const auto& it : this->name_to_tournament) {
json.emplace(it.second->get_name(), it.second->json());
}
save_file(this->state_filename, json.serialize(JSON::SerializeOption::FORMAT | JSON::SerializeOption::HEX_INTEGERS | JSON::SerializeOption::ESCAPE_CONTROLS_ONLY));
phosg::save_file(this->state_filename, json.serialize(phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::HEX_INTEGERS | phosg::JSON::SerializeOption::ESCAPE_CONTROLS_ONLY));
}
shared_ptr<Tournament> TournamentIndex::create_tournament(
+5 -5
View File
@@ -49,7 +49,7 @@ public:
bool is_com() const;
bool is_human() const;
JSON json() const;
phosg::JSON json() const;
};
struct Team : public std::enable_shared_from_this<Team> {
@@ -115,11 +115,11 @@ public:
Tournament(
std::shared_ptr<const MapIndex> map_index,
std::shared_ptr<const COMDeckIndex> com_deck_index,
const JSON& json);
const phosg::JSON& json);
~Tournament() = default;
void init();
JSON json() const;
phosg::JSON json() const;
inline const std::string& get_name() const {
return this->name;
@@ -165,11 +165,11 @@ public:
private:
void create_bracket_matches();
PrefixedLogger log;
phosg::PrefixedLogger log;
std::shared_ptr<const MapIndex> map_index;
std::shared_ptr<const COMDeckIndex> com_deck_index;
JSON source_json;
phosg::JSON source_json;
std::string name;
std::shared_ptr<const MapIndex::Map> map;
Rules rules;
+3 -3
View File
@@ -20,7 +20,7 @@ FileContentsCache::File::File(
shared_ptr<const FileContentsCache::File> FileContentsCache::replace(
const string& name, string&& data, uint64_t t) {
if (t == 0) {
t = now();
t = phosg::now();
}
auto new_file = make_shared<File>(name, std::move(data), t);
auto emplace_ret = this->name_to_file.emplace(name, new_file);
@@ -37,7 +37,7 @@ shared_ptr<const FileContentsCache::File> FileContentsCache::replace(
}
FileContentsCache::GetResult FileContentsCache::get_or_load(const std::string& name) {
return this->get(name, load_file);
return this->get(name, phosg::load_file);
}
FileContentsCache::GetResult FileContentsCache::get_or_load(const char* name) {
@@ -59,7 +59,7 @@ shared_ptr<const FileContentsCache::File> FileContentsCache::get_or_throw(
FileContentsCache::GetResult FileContentsCache::get(const std::string& name,
std::function<std::string(const std::string&)> generate) {
uint64_t t = now();
uint64_t t = phosg::now();
try {
auto& entry = this->name_to_file.at(name);
if (this->ttl_usecs && (t - entry->load_time < this->ttl_usecs)) {
+1 -1
View File
@@ -78,7 +78,7 @@ public:
}
template <typename T, typename NameT>
GetObjResult<T> get_obj(NameT name, std::function<T(const std::string&)> generate) {
uint64_t t = now();
uint64_t t = phosg::now();
try {
auto& f = this->name_to_file.at(name);
if (f->data->size() != sizeof(T)) {
+54 -54
View File
@@ -59,7 +59,7 @@ string CompiledFunctionCode::generate_client_command_t(
footer.entrypoint_addr_offset = this->entrypoint_offset_offset;
footer.unused2.clear(0);
StringWriter w;
phosg::StringWriter w;
if (!label_writes.empty()) {
string modified_code = this->code;
for (const auto& it : label_writes) {
@@ -91,7 +91,7 @@ string CompiledFunctionCode::generate_client_command_t(
footer.relocations_offset = override_relocations_offset;
} else {
for (uint16_t delta : this->relocation_deltas) {
w.put<typename FooterT::U16T>(delta);
w.put<U16T<FooterT::IsBE>>(delta);
}
if (this->relocation_deltas.size() & 1) {
w.put_u16(0);
@@ -161,24 +161,24 @@ shared_ptr<CompiledFunctionCode> compile_function_code(
}
// Look in the function directory first, then the system directory
string asm_filename = string_printf("%s/%s.%s.inc.s", function_directory.c_str(), name.c_str(), arch_name_token);
if (!isfile(asm_filename)) {
asm_filename = string_printf("%s/%s.%s.inc.s", system_directory.c_str(), name.c_str(), arch_name_token);
string asm_filename = phosg::string_printf("%s/%s.%s.inc.s", function_directory.c_str(), name.c_str(), arch_name_token);
if (!phosg::isfile(asm_filename)) {
asm_filename = phosg::string_printf("%s/%s.%s.inc.s", system_directory.c_str(), name.c_str(), arch_name_token);
}
if (isfile(asm_filename)) {
if (phosg::isfile(asm_filename)) {
if (!get_include_stack.emplace(name).second) {
throw runtime_error("mutual recursion between includes: " + name);
}
EmulatorBase::AssembleResult ret;
ResourceDASM::EmulatorBase::AssembleResult ret;
switch (arch) {
case CompiledFunctionCode::Architecture::POWERPC:
ret = PPC32Emulator::assemble(load_file(asm_filename), get_include);
ret = ResourceDASM::PPC32Emulator::assemble(phosg::load_file(asm_filename), get_include);
break;
case CompiledFunctionCode::Architecture::X86:
ret = X86Emulator::assemble(load_file(asm_filename), get_include);
ret = ResourceDASM::X86Emulator::assemble(phosg::load_file(asm_filename), get_include);
break;
case CompiledFunctionCode::Architecture::SH4:
ret = SH4Emulator::assemble(load_file(asm_filename), get_include);
ret = ResourceDASM::SH4Emulator::assemble(phosg::load_file(asm_filename), get_include);
break;
default:
throw runtime_error("unknown architecture");
@@ -188,23 +188,23 @@ shared_ptr<CompiledFunctionCode> compile_function_code(
}
string bin_filename = function_directory + "/" + name + ".inc.bin";
if (isfile(bin_filename)) {
return load_file(bin_filename);
if (phosg::isfile(bin_filename)) {
return phosg::load_file(bin_filename);
}
bin_filename = system_directory + "/" + name + ".inc.bin";
if (isfile(bin_filename)) {
return load_file(bin_filename);
if (phosg::isfile(bin_filename)) {
return phosg::load_file(bin_filename);
}
throw runtime_error("data not found for include: " + name + " (from " + asm_filename + " or " + bin_filename + ")");
};
EmulatorBase::AssembleResult assembled;
ResourceDASM::EmulatorBase::AssembleResult assembled;
if (arch == CompiledFunctionCode::Architecture::POWERPC) {
assembled = PPC32Emulator::assemble(text, get_include);
assembled = ResourceDASM::PPC32Emulator::assemble(text, get_include);
} else if (arch == CompiledFunctionCode::Architecture::X86) {
assembled = X86Emulator::assemble(text, get_include);
assembled = ResourceDASM::X86Emulator::assemble(text, get_include);
} else if (arch == CompiledFunctionCode::Architecture::SH4) {
assembled = SH4Emulator::assemble(text, get_include);
assembled = ResourceDASM::SH4Emulator::assemble(text, get_include);
} else {
throw runtime_error("invalid architecture");
}
@@ -229,7 +229,7 @@ shared_ptr<CompiledFunctionCode> compile_function_code(
set<uint32_t> reloc_indexes;
for (const auto& it : ret->label_offsets) {
if (starts_with(it.first, "reloc")) {
if (phosg::starts_with(it.first, "reloc")) {
reloc_indexes.emplace(it.second / 4);
}
}
@@ -260,28 +260,28 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) {
return;
}
string system_dir_path = ends_with(directory, "/") ? (directory + "System") : (directory + "/System");
string system_dir_path = phosg::ends_with(directory, "/") ? (directory + "System") : (directory + "/System");
uint32_t next_menu_item_id = 1;
for (const auto& subdir_name : list_directory_sorted(directory)) {
string subdir_path = ends_with(directory, "/") ? (directory + subdir_name) : (directory + "/" + subdir_name);
if (!isdir(subdir_path)) {
for (const auto& subdir_name : phosg::list_directory_sorted(directory)) {
string subdir_path = phosg::ends_with(directory, "/") ? (directory + subdir_name) : (directory + "/" + subdir_name);
if (!phosg::isdir(subdir_path)) {
function_compiler_log.warning("Skipping %s (not a directory)", subdir_name.c_str());
continue;
}
for (const auto& filename : list_directory_sorted(subdir_path)) {
for (const auto& filename : phosg::list_directory_sorted(subdir_path)) {
try {
if (!ends_with(filename, ".s")) {
if (!phosg::ends_with(filename, ".s")) {
continue;
}
string name = filename.substr(0, filename.size() - 2);
if (ends_with(name, ".inc")) {
if (phosg::ends_with(name, ".inc")) {
continue;
}
bool is_patch = ends_with(name, ".patch");
bool is_patch = phosg::ends_with(name, ".patch");
if (is_patch) {
name.resize(name.size() - 6);
}
@@ -290,15 +290,15 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) {
CompiledFunctionCode::Architecture arch = CompiledFunctionCode::Architecture::UNKNOWN;
uint32_t specific_version = 0;
string short_name = name;
if (ends_with(name, ".ppc")) {
if (phosg::ends_with(name, ".ppc")) {
arch = CompiledFunctionCode::Architecture::POWERPC;
name.resize(name.size() - 4);
short_name = name;
} else if (ends_with(name, ".x86")) {
} else if (phosg::ends_with(name, ".x86")) {
arch = CompiledFunctionCode::Architecture::X86;
name.resize(name.size() - 4);
short_name = name;
} else if (ends_with(name, ".sh4")) {
} else if (phosg::ends_with(name, ".sh4")) {
arch = CompiledFunctionCode::Architecture::SH4;
name.resize(name.size() - 4);
short_name = name;
@@ -321,11 +321,11 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) {
}
string path = subdir_path + "/" + filename;
string text = load_file(path);
string text = phosg::load_file(path);
auto code = compile_function_code(arch, subdir_path, system_dir_path, name, text);
if (code->index != 0) {
if (!this->index_to_function.emplace(code->index, code).second) {
throw runtime_error(string_printf(
throw runtime_error(phosg::string_printf(
"duplicate function index: %08" PRIX32, code->index));
}
}
@@ -338,11 +338,11 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) {
this->menu_item_id_and_specific_version_to_patch_function.emplace(
static_cast<uint64_t>(code->menu_item_id) << 32 | specific_version, code);
this->name_and_specific_version_to_patch_function.emplace(
string_printf("%s-%08" PRIX32, short_name.c_str(), specific_version), code);
phosg::string_printf("%s-%08" PRIX32, short_name.c_str(), specific_version), code);
}
string index_prefix = code->index ? string_printf("%02X => ", code->index) : "";
string patch_prefix = is_patch ? string_printf("[%08" PRIX32 "/%08" PRIX32 "] ", code->menu_item_id, code->specific_version) : "";
string index_prefix = code->index ? phosg::string_printf("%02X => ", code->index) : "";
string patch_prefix = is_patch ? phosg::string_printf("[%08" PRIX32 "/%08" PRIX32 "] ", code->menu_item_id, code->specific_version) : "";
function_compiler_log.info("Compiled function %s%s%s (%s)",
index_prefix.c_str(), patch_prefix.c_str(), name.c_str(), name_for_architecture(code->arch));
@@ -354,13 +354,13 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) {
}
shared_ptr<const Menu> FunctionCodeIndex::patch_menu(uint32_t specific_version) const {
auto suffix = string_printf("-%08" PRIX32, specific_version);
auto suffix = phosg::string_printf("-%08" PRIX32, specific_version);
auto ret = make_shared<Menu>(MenuID::PATCHES, "Patches");
ret->items.emplace_back(PatchesMenuItemID::GO_BACK, "Go back", "Return to the\nmain menu", 0);
for (const auto& it : this->name_and_specific_version_to_patch_function) {
const auto& fn = it.second;
if (fn->hide_from_patches_menu || !ends_with(it.first, suffix)) {
if (fn->hide_from_patches_menu || !phosg::ends_with(it.first, suffix)) {
continue;
}
ret->items.emplace_back(
@@ -374,13 +374,13 @@ shared_ptr<const Menu> FunctionCodeIndex::patch_menu(uint32_t specific_version)
shared_ptr<const Menu> FunctionCodeIndex::patch_switches_menu(
uint32_t specific_version, const std::unordered_set<std::string>& auto_patches_enabled) const {
auto suffix = string_printf("-%08" PRIX32, specific_version);
auto suffix = phosg::string_printf("-%08" PRIX32, specific_version);
auto ret = make_shared<Menu>(MenuID::PATCH_SWITCHES, "Patch switches");
ret->items.emplace_back(PatchesMenuItemID::GO_BACK, "Go back", "Return to the\nmain menu", 0);
for (const auto& it : this->name_and_specific_version_to_patch_function) {
const auto& fn = it.second;
if (fn->hide_from_patches_menu || !ends_with(it.first, suffix)) {
if (fn->hide_from_patches_menu || !phosg::ends_with(it.first, suffix)) {
continue;
}
string name;
@@ -404,7 +404,7 @@ bool FunctionCodeIndex::patch_menu_empty(uint32_t specific_version) const {
std::shared_ptr<const CompiledFunctionCode> FunctionCodeIndex::get_patch(
const std::string& name, uint32_t specific_version) const {
return this->name_and_specific_version_to_patch_function.at(
string_printf("%s-%08" PRIX32, name.c_str(), specific_version));
phosg::string_printf("%s-%08" PRIX32, name.c_str(), specific_version));
}
DOLFileIndex::DOLFileIndex(const string& directory) {
@@ -412,7 +412,7 @@ DOLFileIndex::DOLFileIndex(const string& directory) {
function_compiler_log.info("Function compiler is not available");
return;
}
if (!isdir(directory)) {
if (!phosg::isdir(directory)) {
function_compiler_log.info("DOL file directory is missing");
return;
}
@@ -422,9 +422,9 @@ DOLFileIndex::DOLFileIndex(const string& directory) {
menu->items.emplace_back(ProgramsMenuItemID::GO_BACK, "Go back", "Return to the\nmain menu", 0);
uint32_t next_menu_item_id = 0;
for (const auto& filename : list_directory_sorted(directory)) {
bool is_dol = ends_with(filename, ".dol");
bool is_compressed_dol = ends_with(filename, ".dol.prs");
for (const auto& filename : phosg::list_directory_sorted(directory)) {
bool is_dol = phosg::ends_with(filename, ".dol");
bool is_compressed_dol = phosg::ends_with(filename, ".dol.prs");
if (!is_dol && !is_compressed_dol) {
continue;
}
@@ -436,13 +436,13 @@ DOLFileIndex::DOLFileIndex(const string& directory) {
dol->name = name;
string path = directory + "/" + filename;
string file_data = load_file(path);
string file_data = phosg::load_file(path);
string description;
if (is_compressed_dol) {
size_t decompressed_size = prs_decompress_size(file_data);
StringWriter w;
phosg::StringWriter w;
w.put_u32b(file_data.size());
w.put_u32b(decompressed_size);
w.write(file_data);
@@ -451,15 +451,15 @@ DOLFileIndex::DOLFileIndex(const string& directory) {
}
dol->data = std::move(w.str());
string compressed_size_str = format_size(file_data.size());
string decompressed_size_str = format_size(decompressed_size);
string compressed_size_str = phosg::format_size(file_data.size());
string decompressed_size_str = phosg::format_size(decompressed_size);
function_compiler_log.info("Loaded compressed DOL file %s (%s -> %s)",
dol->name.c_str(), compressed_size_str.c_str(), decompressed_size_str.c_str());
description = string_printf("$C6%s$C7\n%s\n%s (orig)",
description = phosg::string_printf("$C6%s$C7\n%s\n%s (orig)",
dol->name.c_str(), compressed_size_str.c_str(), decompressed_size_str.c_str());
} else {
StringWriter w;
phosg::StringWriter w;
w.put_u32b(0);
w.put_u32b(file_data.size());
w.write(file_data);
@@ -468,9 +468,9 @@ DOLFileIndex::DOLFileIndex(const string& directory) {
}
dol->data = std::move(w.str());
string size_str = format_size(dol->data.size());
string size_str = phosg::format_size(dol->data.size());
function_compiler_log.info("Loaded DOL file %s (%s)", filename.c_str(), size_str.c_str());
description = string_printf("$C6%s$C7\n%s", dol->name.c_str(), size_str.c_str());
description = phosg::string_printf("$C6%s$C7\n%s", dol->name.c_str(), size_str.c_str());
}
this->name_to_file.emplace(dol->name, dol);
@@ -503,7 +503,7 @@ uint32_t specific_version_for_gc_header_checksum(uint32_t header_checksum) {
data.region_code = *region_code;
for (uint8_t version_code = 0; version_code < 8; version_code++) {
data.version_code = version_code;
uint32_t checksum = crc32(&data, sizeof(data));
uint32_t checksum = phosg::crc32(&data, sizeof(data));
uint32_t specific_version = 0x33000030 | (*game_code2 << 16) | (*region_code << 8) | version_code;
if (!checksum_to_specific_version.emplace(checksum, specific_version).second) {
throw logic_error("multiple specific_versions have same header checksum");
@@ -515,7 +515,7 @@ uint32_t specific_version_for_gc_header_checksum(uint32_t header_checksum) {
data.region_code = 'J';
data.system_code = 'D';
data.version_code = 0;
uint32_t checksum = crc32(&data, sizeof(data));
uint32_t checksum = phosg::crc32(&data, sizeof(data));
uint32_t specific_version = 0x33004A54 | (*game_code2 << 16);
if (!checksum_to_specific_version.emplace(checksum, specific_version).second) {
throw logic_error("multiple specific_versions have same header checksum");
+13 -14
View File
@@ -5,16 +5,15 @@
#include <stdexcept>
#include "Text.hh"
#include "Types.hh"
using namespace std;
template <bool IsBigEndian>
template <bool BE>
struct GSLHeaderEntryT {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
pstring<TextEncoding::ASCII, 0x20> filename;
U32T offset; // In pages, so actual offset is this * 0x800
U32T size;
U32T<BE> offset; // In pages, so actual offset is this * 0x800
U32T<BE> size;
uint64_t unused;
} __packed__;
@@ -23,12 +22,12 @@ using GSLHeaderEntryBE = GSLHeaderEntryT<true>;
check_struct_size(GSLHeaderEntry, 0x30);
check_struct_size(GSLHeaderEntryBE, 0x30);
template <bool IsBigEndian>
template <bool BE>
void GSLArchive::load_t() {
StringReader r(*this->data);
phosg::StringReader r(*this->data);
uint64_t min_data_offset = 0xFFFFFFFFFFFFFFFF;
while (r.where() < min_data_offset) {
const auto& entry = r.get<GSLHeaderEntryT<IsBigEndian>>();
const auto& entry = r.get<GSLHeaderEntryT<BE>>();
if (entry.filename.empty()) {
break;
}
@@ -71,10 +70,10 @@ string GSLArchive::get_copy(const string& name) const {
}
}
StringReader GSLArchive::get_reader(const string& name) const {
phosg::StringReader GSLArchive::get_reader(const string& name) const {
try {
const auto& entry = this->entries.at(name);
return StringReader(this->data->data() + entry.offset, entry.size);
return phosg::StringReader(this->data->data() + entry.offset, entry.size);
} catch (const out_of_range&) {
throw out_of_range("GSL does not contain file: " + name);
}
@@ -84,16 +83,16 @@ string GSLArchive::generate(const unordered_map<string, string>& files, bool big
return big_endian ? GSLArchive::generate_t<true>(files) : GSLArchive::generate_t<false>(files);
}
template <bool IsBigEndian>
template <bool BE>
string GSLArchive::generate_t(const unordered_map<string, string>& files) {
StringWriter w;
phosg::StringWriter w;
// Make sure there's enough space for a blank header entry before any file's
// data pages begin
uint32_t data_start_offset = ((sizeof(GSLHeaderEntryT<IsBigEndian>) * (files.size() + 1)) + 0x7FF) & (~0x7FF);
uint32_t data_start_offset = ((sizeof(GSLHeaderEntryT<BE>) * (files.size() + 1)) + 0x7FF) & (~0x7FF);
uint32_t data_offset = data_start_offset;
for (const auto& file : files) {
GSLHeaderEntryT<IsBigEndian> entry;
GSLHeaderEntryT<BE> entry;
entry.filename.encode(file.first);
entry.offset = data_offset >> 11;
entry.size = file.second.size();
+3 -3
View File
@@ -21,14 +21,14 @@ public:
std::pair<const void*, size_t> get(const std::string& name) const;
std::string get_copy(const std::string& name) const;
StringReader get_reader(const std::string& name) const;
phosg::StringReader get_reader(const std::string& name) const;
static std::string generate(const std::unordered_map<std::string, std::string>& files, bool big_endian);
private:
template <bool IsBigEndian>
template <bool BE>
void load_t();
template <bool IsBigEndian>
template <bool BE>
static std::string generate_t(const std::unordered_map<std::string, std::string>& files);
std::shared_ptr<const std::string> data;
+2 -2
View File
@@ -34,7 +34,7 @@ struct GVRHeader {
be_uint16_t height;
} __packed_ws__(GVRHeader, 0x10);
string encode_gvm(const Image& img, GVRDataFormat data_format, const std::string& internal_name, uint32_t global_index) {
string encode_gvm(const phosg::Image& img, GVRDataFormat data_format, const std::string& internal_name, uint32_t global_index) {
int8_t dimensions_field = -2;
{
size_t h = img.get_height();
@@ -66,7 +66,7 @@ string encode_gvm(const Image& img, GVRDataFormat data_format, const std::string
throw invalid_argument("cannot encode pixel format");
}
StringWriter w;
phosg::StringWriter w;
w.put<GVMFileHeader>({.signature = 0x47564D48, .header_size = 0x48, .flags = 0x000F, .num_files = 1});
GVMFileEntry file_entry;
file_entry.file_num = 0;
+1 -1
View File
@@ -19,7 +19,7 @@ enum class GVRDataFormat : uint8_t {
DXT1 = 0x0E,
};
std::string encode_gvm(const Image& img, GVRDataFormat data_format, const std::string& internal_name, uint32_t global_index);
std::string encode_gvm(const phosg::Image& img, GVRDataFormat data_format, const std::string& internal_name, uint32_t global_index);
constexpr uint16_t encode_rgb565(uint8_t r, uint8_t g, uint8_t b) {
return ((r << 8) & 0xF800) | ((g << 3) & 0x07E0) | ((b >> 3) & 0x001F);
+384 -146
View File
@@ -110,7 +110,7 @@ unordered_multimap<string, string> HTTPServer::parse_url_params(const string& qu
if (query.empty()) {
return params;
}
for (auto it : split(query, '&')) {
for (auto it : phosg::split(query, '&')) {
size_t first_equals = it.find('=');
if (first_equals != string::npos) {
string value(it, first_equals + 1);
@@ -119,8 +119,8 @@ unordered_multimap<string, string> HTTPServer::parse_url_params(const string& qu
for (; read_offset < value.size(); write_offset++) {
if ((value[read_offset] == '%') && (read_offset < value.size() - 2)) {
value[write_offset] =
static_cast<char>(value_for_hex_char(value[read_offset + 1]) << 4) |
static_cast<char>(value_for_hex_char(value[read_offset + 2]));
static_cast<char>(phosg::value_for_hex_char(value[read_offset + 1]) << 4) |
static_cast<char>(phosg::value_for_hex_char(value[read_offset + 2]));
read_offset += 3;
} else if (value[write_offset] == '+') {
value[write_offset] = ' ';
@@ -132,8 +132,7 @@ unordered_multimap<string, string> HTTPServer::parse_url_params(const string& qu
}
value.resize(write_offset);
params.emplace(piecewise_construct, forward_as_tuple(it, 0, first_equals),
forward_as_tuple(value));
params.emplace(piecewise_construct, forward_as_tuple(it, 0, first_equals), forward_as_tuple(value));
} else {
params.emplace(it, "");
}
@@ -172,7 +171,7 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
}
void HTTPServer::listen(const string& socket_path) {
int fd = ::listen(socket_path, 0, SOMAXCONN);
int fd = phosg::listen(socket_path, 0, SOMAXCONN);
server_log.info("Listening on Unix socket %s on fd %d (HTTP)", socket_path.c_str(), fd);
this->add_socket(fd);
}
@@ -181,8 +180,8 @@ void HTTPServer::listen(const string& addr, int port) {
if (port == 0) {
this->listen(addr);
} else {
int fd = ::listen(addr, port, SOMAXCONN);
string netloc_str = render_netloc(addr, port);
int fd = phosg::listen(addr, port, SOMAXCONN);
string netloc_str = phosg::render_netloc(addr, port);
server_log.info("Listening on TCP interface %s on fd %d (HTTP)", netloc_str.c_str(), fd);
this->add_socket(fd);
}
@@ -204,30 +203,257 @@ void HTTPServer::wait_for_stop() {
this->th.join();
}
HTTPServer::WebsocketClient::WebsocketClient(struct evhttp_connection* conn)
: conn(conn),
bev(evhttp_connection_get_bufferevent(this->conn)),
pending_opcode(0xFF),
last_communication_time(phosg::now()) {}
HTTPServer::WebsocketClient::~WebsocketClient() {
evhttp_connection_free(this->conn);
}
void HTTPServer::WebsocketClient::reset_pending_frame() {
this->pending_opcode = 0xFF;
this->pending_data.clear();
}
shared_ptr<HTTPServer::WebsocketClient> HTTPServer::enable_websockets(struct evhttp_request* req) {
if (evhttp_request_get_command(req) != EVHTTP_REQ_GET) {
return nullptr;
}
struct evkeyvalq* in_headers = evhttp_request_get_input_headers(req);
const char* connection_header = evhttp_find_header(in_headers, "Connection");
if (!connection_header || strcasecmp(connection_header, "upgrade")) {
return nullptr;
}
const char* upgrade_header = evhttp_find_header(in_headers, "Upgrade");
if (!upgrade_header || strcasecmp(upgrade_header, "websocket")) {
return nullptr;
}
const char* sec_websocket_key_header = evhttp_find_header(in_headers, "Sec-WebSocket-Key");
if (!sec_websocket_key_header) {
return nullptr;
}
// Note: it's important that we make a copy of this header's value since
// we're about to free the original
string sec_websocket_key = sec_websocket_key_header;
string sec_websocket_accept_data = sec_websocket_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
string sec_websocket_accept = phosg::base64_encode(phosg::sha1(sec_websocket_accept_data));
// Hijack the bufferevent since it's no longer handling HTTP at all
struct evhttp_connection* conn = evhttp_request_get_connection(req);
struct bufferevent* bev = evhttp_connection_get_bufferevent(conn);
bufferevent_setcb(bev, &this->dispatch_on_websocket_read, NULL, &this->dispatch_on_websocket_error, this);
bufferevent_enable(bev, EV_READ | EV_WRITE);
// Send the HTTP reply, which enables websockets
struct evbuffer* out_buf = bufferevent_get_output(bev);
evbuffer_add_printf(out_buf, "HTTP/1.1 101 Switching Protocols\r\n\
Upgrade: websocket\r\n\
Connection: upgrade\r\n\
Sec-WebSocket-Accept: %s\r\n\
\r\n",
sec_websocket_accept.c_str());
return this->bev_to_websocket_client.emplace(bev, new WebsocketClient(conn)).first->second;
}
void HTTPServer::dispatch_on_websocket_read(struct bufferevent* bev, void* ctx) {
reinterpret_cast<HTTPServer*>(ctx)->on_websocket_read(bev);
}
void HTTPServer::dispatch_on_websocket_error(struct bufferevent* bev, short events, void* ctx) {
reinterpret_cast<HTTPServer*>(ctx)->on_websocket_error(bev, events);
}
void HTTPServer::on_websocket_read(struct bufferevent* bev) {
struct evbuffer* in_buf = bufferevent_get_input(bev);
for (;;) {
// We need at most 10 bytes to determine if there's a valid frame, or as
// little as 2
string header_data(10, '\0');
ssize_t bytes_read = evbuffer_copyout(in_buf, const_cast<char*>(header_data.data()), header_data.size());
if (bytes_read < 2) {
break; // Full header not yet available
}
// Get the payload size
bool has_mask = header_data[1] & 0x80;
size_t header_size = 2;
size_t payload_size = header_data[1] & 0x7F;
if (payload_size == 0x7F) {
if (bytes_read < 10) {
break; // Full 64-bit header not yet available
}
payload_size = phosg::bswap64(*reinterpret_cast<const uint64_t*>(&header_data[2]));
header_size = 10;
} else if (payload_size == 0x7E) {
if (bytes_read < 4) {
break; // Full 16-bit size header not yet available
}
payload_size = phosg::bswap16(*reinterpret_cast<const uint16_t*>(&header_data[2]));
header_size = 4;
}
if (evbuffer_get_length(in_buf) < header_size + payload_size) {
break; // Full message not yet available
}
// Full message is available; skip the header bytes (we already read them)
// and read the masking key if needed
evbuffer_drain(in_buf, header_size);
uint8_t mask_key[4];
if (has_mask) {
evbuffer_remove(in_buf, mask_key, 4);
}
shared_ptr<WebsocketClient> c = this->bev_to_websocket_client.at(bev);
c->last_communication_time = phosg::now();
// Read and unmask message data
string payload(payload_size, '\0');
evbuffer_remove(in_buf, const_cast<char*>(payload.data()), payload_size);
if (has_mask) {
for (size_t x = 0; x < payload_size; x++) {
payload[x] ^= mask_key[x & 3];
}
}
// If the current message is a control message, respond appropriately
// (these can be sent in the middle of fragmented messages)
uint8_t opcode = header_data[0] & 0x0F;
if (opcode & 0x08) {
if (opcode == 0x0A) {
// Ping response; ignore it
} else if (opcode == 0x08) {
// Close message
this->send_websocket_message(bev, payload, 0x08);
this->disconnect_websocket_client(bev);
} else if (opcode == 0x09) {
// Ping message
this->send_websocket_message(bev, payload, 0x0A);
} else {
// Unknown control message type
this->disconnect_websocket_client(bev);
}
break;
}
// If there's an existing pending message, the current message's opcode
// should be zero; if there's no pending message, it must not be zero
if ((c->pending_opcode != 0xFF) == (opcode != 0)) {
this->disconnect_websocket_client(bev);
break;
}
// At this point, we have read a full message; we must not break out of
// this loop in case there are further messages available.
// Save the message opcode, if present, and append the frame data
if (opcode) {
c->pending_opcode = opcode;
}
c->pending_data += payload;
// If the FIN bit is set, then the frame is complete - append the payload
// to any pending payloads and call the message handler. If the FIN bit
// isn't set, we need to receive at least one continuation frame to
// complete the message.
if (header_data[0] & 0x80) {
this->handle_websocket_message(c, c->pending_opcode, c->pending_data);
c->reset_pending_frame();
}
}
}
void HTTPServer::on_websocket_error(struct bufferevent* bev, short events) {
if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF)) {
this->disconnect_websocket_client(bev);
}
}
void HTTPServer::disconnect_websocket_client(struct bufferevent* bev) {
auto it = this->bev_to_websocket_client.find(bev);
this->handle_websocket_disconnect(it->second);
this->bev_to_websocket_client.erase(it);
}
void HTTPServer::send_websocket_message(struct bufferevent* bev,
const string& message, uint8_t opcode) {
string header;
header.push_back(0x80 | (opcode & 0x0F));
if (message.size() > 65535) {
header.push_back(0x7F);
header.resize(10);
*reinterpret_cast<uint64_t*>(const_cast<char*>(header.data() + 2)) = phosg::bswap64(message.size());
} else if (message.size() > 0x7D) {
header.push_back(0x7E);
header.resize(4);
*reinterpret_cast<uint16_t*>(const_cast<char*>(header.data() + 2)) = phosg::bswap16(message.size());
} else {
header.push_back(message.size());
}
struct evbuffer* out_buf = bufferevent_get_output(bev);
evbuffer_add(out_buf, header.data(), header.size());
evbuffer_add(out_buf, message.data(), message.size());
}
void HTTPServer::send_websocket_message(shared_ptr<WebsocketClient> c, const string& message, uint8_t opcode) {
this->send_websocket_message(c->bev, message, opcode);
}
void HTTPServer::handle_websocket_message(shared_ptr<WebsocketClient>, uint8_t, const string&) {
// Currently we just ignore any messages from the client
}
void HTTPServer::handle_websocket_disconnect(shared_ptr<WebsocketClient> c) {
this->rare_drop_subscribers.erase(c);
}
void HTTPServer::send_rare_drop_notification(shared_ptr<const phosg::JSON> message) {
forward_to_event_thread(this->base, [this, message]() -> void {
if (this->rare_drop_subscribers.empty()) {
return;
}
string serialized = message->serialize();
for (const auto& c : this->rare_drop_subscribers) {
this->send_websocket_message(c, serialized);
}
});
}
void HTTPServer::dispatch_handle_request(struct evhttp_request* req, void* ctx) {
reinterpret_cast<HTTPServer*>(ctx)->handle_request(req);
}
JSON HTTPServer::generate_quest_json_st(shared_ptr<const Quest> q) {
phosg::JSON HTTPServer::generate_quest_json_st(shared_ptr<const Quest> q) {
if (!q) {
return nullptr;
}
auto battle_rules_json = q->battle_rules ? q->battle_rules->json() : nullptr;
auto challenge_template_index_json = (q->challenge_template_index >= 0)
? q->challenge_template_index
: JSON(nullptr);
return JSON::dict({
: phosg::JSON(nullptr);
return phosg::JSON::dict({
{"Number", q->quest_number},
{"Episode", name_for_episode(q->episode)},
{"Joinable", q->joinable},
{"LockStatusRegister", (q->lock_status_register >= 0) ? q->lock_status_register : JSON(nullptr)},
{"LockStatusRegister", (q->lock_status_register >= 0) ? q->lock_status_register : phosg::JSON(nullptr)},
{"Name", q->name},
{"BattleRules", std::move(battle_rules_json)},
{"ChallengeTemplateIndex", std::move(challenge_template_index_json)},
});
}
JSON HTTPServer::generate_client_config_json_st(const Client::Config& config) {
phosg::JSON HTTPServer::generate_client_config_json_st(const Client::Config& config) {
const char* drop_notifications_mode = "unknown";
switch (config.get_drop_notification_mode()) {
case Client::ItemDropNotificationMode::NOTHING:
@@ -244,7 +470,7 @@ JSON HTTPServer::generate_client_config_json_st(const Client::Config& config) {
break;
}
auto ret = JSON::dict({
auto ret = phosg::JSON::dict({
{"SpecificVersion", config.specific_version},
{"SwitchAssistEnabled", (config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED) ? true : false)},
{"InfiniteHPEnabled", (config.check_flag(Client::Flag::INFINITE_HP_ENABLED) ? true : false)},
@@ -260,46 +486,46 @@ JSON HTTPServer::generate_client_config_json_st(const Client::Config& config) {
{"ProxyBlockFunctionCalls", (config.check_flag(Client::Flag::PROXY_BLOCK_FUNCTION_CALLS) ? true : false)},
{"ProxyEp3UnmaskWhispers", (config.check_flag(Client::Flag::PROXY_EP3_UNMASK_WHISPERS) ? true : false)},
});
ret.emplace("OverrideRandomSeed", config.check_flag(Client::Flag::USE_OVERRIDE_RANDOM_SEED) ? config.override_random_seed : JSON(nullptr));
ret.emplace("OverrideSectionID", (config.override_section_id != 0xFF) ? config.override_section_id : JSON(nullptr));
ret.emplace("OverrideLobbyEvent", (config.override_lobby_event != 0xFF) ? config.override_lobby_event : JSON(nullptr));
ret.emplace("OverrideLobbyNumber", (config.override_lobby_number != 0x80) ? config.override_lobby_number : JSON(nullptr));
ret.emplace("OverrideRandomSeed", config.check_flag(Client::Flag::USE_OVERRIDE_RANDOM_SEED) ? config.override_random_seed : phosg::JSON(nullptr));
ret.emplace("OverrideSectionID", (config.override_section_id != 0xFF) ? config.override_section_id : phosg::JSON(nullptr));
ret.emplace("OverrideLobbyEvent", (config.override_lobby_event != 0xFF) ? config.override_lobby_event : phosg::JSON(nullptr));
ret.emplace("OverrideLobbyNumber", (config.override_lobby_number != 0x80) ? config.override_lobby_number : phosg::JSON(nullptr));
return ret;
}
JSON HTTPServer::generate_account_json_st(shared_ptr<const Account> a) {
auto dc_nte_licenses_json = JSON::list();
phosg::JSON HTTPServer::generate_account_json_st(shared_ptr<const Account> a) {
auto dc_nte_licenses_json = phosg::JSON::list();
for (const auto& it : a->dc_nte_licenses) {
dc_nte_licenses_json.emplace_back(it.first);
}
auto dc_licenses_json = JSON::list();
auto dc_licenses_json = phosg::JSON::list();
for (const auto& it : a->dc_licenses) {
dc_licenses_json.emplace_back(it.first);
}
auto pc_licenses_json = JSON::list();
auto pc_licenses_json = phosg::JSON::list();
for (const auto& it : a->pc_licenses) {
pc_licenses_json.emplace_back(it.first);
}
auto gc_licenses_json = JSON::list();
auto gc_licenses_json = phosg::JSON::list();
for (const auto& it : a->gc_licenses) {
gc_licenses_json.emplace_back(it.first);
}
auto xb_licenses_json = JSON::list();
auto xb_licenses_json = phosg::JSON::list();
for (const auto& it : a->xb_licenses) {
xb_licenses_json.emplace_back(it.first);
}
auto bb_licenses_json = JSON::list();
auto bb_licenses_json = phosg::JSON::list();
for (const auto& it : a->bb_licenses) {
bb_licenses_json.emplace_back(it.first);
}
auto auto_patches_json = JSON::list();
auto auto_patches_json = phosg::JSON::list();
for (const auto& it : a->auto_patches_enabled) {
auto_patches_json.emplace_back(it);
}
return JSON::dict({
return phosg::JSON::dict({
{"AccountID", a->account_id},
{"Flags", a->flags},
{"BanEndTime", a->ban_end_time ? a->ban_end_time : JSON(nullptr)},
{"BanEndTime", a->ban_end_time ? a->ban_end_time : phosg::JSON(nullptr)},
{"Ep3CurrentMeseta", a->ep3_current_meseta},
{"Ep3TotalMesetaEarned", a->ep3_total_meseta_earned},
{"BBTeamID", a->bb_team_id},
@@ -316,11 +542,11 @@ JSON HTTPServer::generate_account_json_st(shared_ptr<const Account> a) {
});
};
JSON HTTPServer::generate_game_client_json_st(shared_ptr<const Client> c, shared_ptr<const ItemNameIndex> item_name_index) {
auto ret = JSON::dict({
phosg::JSON HTTPServer::generate_game_client_json_st(shared_ptr<const Client> c, shared_ptr<const ItemNameIndex> item_name_index) {
auto ret = phosg::JSON::dict({
{"ID", c->id},
{"RemoteAddress", render_sockaddr_storage(c->channel.remote_addr)},
{"Version", name_for_enum(c->version())},
{"RemoteAddress", phosg::render_sockaddr_storage(c->channel.remote_addr)},
{"Version", phosg::name_for_enum(c->version())},
{"SubVersion", c->sub_version},
{"Config", HTTPServer::generate_client_config_json_st(c->config)},
{"Language", name_for_language_code(c->language())},
@@ -329,7 +555,7 @@ JSON HTTPServer::generate_game_client_json_st(shared_ptr<const Client> c, shared
{"LocationFloor", c->floor},
{"CanChat", c->can_chat},
});
ret.emplace("Account", c->login ? HTTPServer::generate_account_json_st(c->login->account) : JSON(nullptr));
ret.emplace("Account", c->login ? HTTPServer::generate_account_json_st(c->login->account) : phosg::JSON(nullptr));
auto l = c->lobby.lock();
if (l) {
ret.emplace("LobbyID", l->lobby_id);
@@ -354,10 +580,10 @@ JSON HTTPServer::generate_game_client_json_st(shared_ptr<const Client> c, shared
ret.emplace("NumLuckMaterialsUsed", p->get_material_usage(PSOBBCharacterFile::MaterialType::LUCK));
}
}
JSON items_json = JSON::list();
phosg::JSON items_json = phosg::JSON::list();
for (size_t z = 0; z < p->inventory.num_items; z++) {
const auto& item = p->inventory.items[z];
auto item_dict = JSON::dict({
auto item_dict = phosg::JSON::dict({
{"Flags", item.flags.load()},
{"Data", item.data.hex()},
{"ItemID", item.data.id.load()},
@@ -376,17 +602,17 @@ JSON HTTPServer::generate_game_client_json_st(shared_ptr<const Client> c, shared
ret.emplace("LCK", p->disp.stats.char_stats.lck.load());
ret.emplace("EXP", p->disp.stats.experience.load());
ret.emplace("Meseta", p->disp.stats.meseta.load());
auto tech_levels_json = JSON::dict();
auto tech_levels_json = phosg::JSON::dict();
for (size_t z = 0; z < 0x13; z++) {
auto level = p->get_technique_level(z);
tech_levels_json.emplace(name_for_technique(z), (level != 0xFF) ? level : JSON(nullptr));
tech_levels_json.emplace(name_for_technique(z), (level != 0xFF) ? level : phosg::JSON(nullptr));
}
ret.emplace("TechniqueLevels", std::move(tech_levels_json));
}
ret.emplace("Height", p->disp.stats.height.load());
ret.emplace("Level", p->disp.stats.level.load());
ret.emplace("NameColor", p->disp.visual.name_color.load());
ret.emplace("ExtraModel", (p->disp.visual.validation_flags & 2) ? p->disp.visual.extra_model : JSON(nullptr));
ret.emplace("ExtraModel", (p->disp.visual.validation_flags & 2) ? p->disp.visual.extra_model : phosg::JSON(nullptr));
ret.emplace("SectionID", name_for_section_id(p->disp.visual.section_id));
ret.emplace("CharClass", name_for_char_class(p->disp.visual.section_id));
ret.emplace("Costume", p->disp.visual.costume.load());
@@ -405,7 +631,7 @@ JSON HTTPServer::generate_game_client_json_st(shared_ptr<const Client> c, shared
ret.emplace("AutoReply", p->auto_reply.decode(c->language()));
ret.emplace("InfoBoard", p->info_board.decode(c->language()));
auto battle_place_counts = JSON::list({
auto battle_place_counts = phosg::JSON::list({
p->battle_records.place_counts[0].load(),
p->battle_records.place_counts[1].load(),
p->battle_records.place_counts[2].load(),
@@ -415,8 +641,8 @@ JSON HTTPServer::generate_game_client_json_st(shared_ptr<const Client> c, shared
ret.emplace("BattleDisconnectCount", p->battle_records.disconnect_count.load());
if (!is_ep3(c->version())) {
auto json_for_challenge_times = []<size_t Count>(const parray<ChallengeTime, Count>& times) -> JSON {
auto times_json = JSON::list();
auto json_for_challenge_times = []<size_t Count>(const parray<ChallengeTime, Count>& times) -> phosg::JSON {
auto times_json = phosg::JSON::list();
for (size_t z = 0; z < times.size(); z++) {
times_json.emplace_back(times[z].decode());
}
@@ -436,7 +662,7 @@ JSON HTTPServer::generate_game_client_json_st(shared_ptr<const Client> c, shared
uint8_t day = (p->challenge_records.grave_time >> 16) & 0xFF;
uint8_t hour = (p->challenge_records.grave_time >> 8) & 0xFF;
uint8_t minute = p->challenge_records.grave_time & 0xFF;
ret.emplace("ChallengeGraveTime", string_printf("%04hu-%02hhu-%02hhu %02hhu:%02hhu:00", year, month, day, hour, minute));
ret.emplace("ChallengeGraveTime", phosg::string_printf("%04hu-%02hhu-%02hhu %02hhu:%02hhu:00", year, month, day, hour, minute));
}
string grave_enemy_types;
if (p->challenge_records.grave_defeated_by_enemy_rt_index) {
@@ -444,7 +670,7 @@ JSON HTTPServer::generate_game_client_json_st(shared_ptr<const Client> c, shared
if (!grave_enemy_types.empty()) {
grave_enemy_types += "/";
}
grave_enemy_types += name_for_enum(type);
grave_enemy_types += phosg::name_for_enum(type);
}
}
ret.emplace("ChallengeGraveDefeatedByEnemy", std::move(grave_enemy_types));
@@ -465,7 +691,7 @@ JSON HTTPServer::generate_game_client_json_st(shared_ptr<const Client> c, shared
return ret;
}
JSON HTTPServer::generate_proxy_client_json_st(shared_ptr<const ProxyServer::LinkedSession> ses) {
phosg::JSON HTTPServer::generate_proxy_client_json_st(shared_ptr<const ProxyServer::LinkedSession> ses) {
struct LobbyPlayer {
uint32_t guild_card_number = 0;
uint64_t xb_user_id = 0;
@@ -476,35 +702,35 @@ JSON HTTPServer::generate_proxy_client_json_st(shared_ptr<const ProxyServer::Lin
};
std::vector<LobbyPlayer> lobby_players;
auto lobby_players_json = JSON::list();
auto lobby_players_json = phosg::JSON::list();
for (size_t z = 0; z < ses->lobby_players.size(); z++) {
const auto& p = ses->lobby_players[z];
if (p.guild_card_number) {
lobby_players_json.emplace_back(JSON::dict({
lobby_players_json.emplace_back(phosg::JSON::dict({
{"GuildCardNumber", p.guild_card_number},
{"Name", p.name},
{"Language", name_for_language_code(p.language)},
{"SectionID", name_for_section_id(p.section_id)},
{"CharClass", name_for_char_class(p.char_class)},
}));
lobby_players_json.back().emplace("XBUserID", p.xb_user_id ? p.xb_user_id : JSON(nullptr));
lobby_players_json.back().emplace("XBUserID", p.xb_user_id ? p.xb_user_id : phosg::JSON(nullptr));
} else {
lobby_players_json.emplace_back(nullptr);
}
}
auto ret = JSON::dict({
auto ret = phosg::JSON::dict({
{"ID", ses->id},
{"RemoteClientAddress", render_sockaddr_storage(ses->client_channel.remote_addr)},
{"RemoteServerAddress", render_sockaddr_storage(ses->server_channel.remote_addr)},
{"RemoteClientAddress", phosg::render_sockaddr_storage(ses->client_channel.remote_addr)},
{"RemoteServerAddress", phosg::render_sockaddr_storage(ses->server_channel.remote_addr)},
{"LocalPort", ses->local_port},
{"NextDestination", render_sockaddr_storage(ses->next_destination)},
{"Version", name_for_enum(ses->version())},
{"NextDestination", phosg::render_sockaddr_storage(ses->next_destination)},
{"Version", phosg::name_for_enum(ses->version())},
{"SubVersion", ses->sub_version},
{"Name", ses->character_name},
{"DCHardwareID", ses->hardware_id},
{"RemoteGuildCardNumber", ses->remote_guild_card_number},
{"RemoteClientConfigData", format_data_string(&ses->remote_client_config_data[0], ses->remote_client_config_data.size())},
{"RemoteClientConfigData", phosg::format_data_string(&ses->remote_client_config_data[0], ses->remote_client_config_data.size())},
{"Config", HTTPServer::generate_client_config_json_st(ses->config)},
{"Language", name_for_language_code(ses->language())},
{"LobbyClientID", ses->lobby_client_id},
@@ -533,19 +759,19 @@ JSON HTTPServer::generate_proxy_client_json_st(shared_ptr<const ProxyServer::Lin
ret.emplace("DropMode", "proxy");
break;
}
ret.emplace("Account", ses->login ? HTTPServer::generate_account_json_st(ses->login->account) : JSON(nullptr));
ret.emplace("Account", ses->login ? HTTPServer::generate_account_json_st(ses->login->account) : phosg::JSON(nullptr));
return ret;
}
JSON HTTPServer::generate_lobby_json_st(shared_ptr<const Lobby> l, shared_ptr<const ItemNameIndex> item_name_index) {
phosg::JSON HTTPServer::generate_lobby_json_st(shared_ptr<const Lobby> l, shared_ptr<const ItemNameIndex> item_name_index) {
std::array<std::shared_ptr<Client>, 12> clients;
auto client_ids_json = JSON::list();
auto client_ids_json = phosg::JSON::list();
for (size_t z = 0; z < l->max_clients; z++) {
client_ids_json.emplace_back(l->clients[z] ? l->clients[z]->id : JSON(nullptr));
client_ids_json.emplace_back(l->clients[z] ? l->clients[z]->id : phosg::JSON(nullptr));
}
auto ret = JSON::dict({
auto ret = phosg::JSON::dict({
{"ID", l->lobby_id},
{"AllowedVersions", l->allowed_versions},
{"Event", l->event},
@@ -570,7 +796,7 @@ JSON HTTPServer::generate_lobby_json_st(shared_ptr<const Lobby> l, shared_ptr<co
ret.emplace("QuestSelectionInProgress", l->check_flag(Lobby::Flag::QUEST_SELECTION_IN_PROGRESS));
ret.emplace("QuestInProgress", l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS));
ret.emplace("JoinableQuestInProgress", l->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS));
auto variations_json = JSON::list();
auto variations_json = phosg::JSON::list();
for (size_t z = 0; z < l->variations.size(); z++) {
variations_json.emplace_back(l->variations[z].load());
}
@@ -613,11 +839,11 @@ JSON HTTPServer::generate_lobby_json_st(shared_ptr<const Lobby> l, shared_ptr<co
}
}
auto floor_items_json = JSON::list();
auto floor_items_json = phosg::JSON::list();
for (size_t floor = 0; floor < l->floor_item_managers.size(); floor++) {
for (const auto& it : l->floor_item_managers[floor].items) {
const auto& item = it.second;
auto item_dict = JSON::dict({
auto item_dict = phosg::JSON::dict({
{"LocationFloor", floor},
{"LocationX", item->x},
{"LocationZ", item->z},
@@ -642,7 +868,7 @@ JSON HTTPServer::generate_lobby_json_st(shared_ptr<const Lobby> l, shared_ptr<co
auto ep3s = l->ep3_server;
if (ep3s) {
auto players_json = JSON::list();
auto players_json = phosg::JSON::list();
for (size_t z = 0; z < 4; z++) {
if (!ep3s->name_entries[z].present) {
players_json.emplace_back(nullptr);
@@ -650,9 +876,9 @@ JSON HTTPServer::generate_lobby_json_st(shared_ptr<const Lobby> l, shared_ptr<co
auto lc = l->clients[z];
auto deck_entry = ep3s->deck_entries[z];
JSON deck_json = nullptr;
phosg::JSON deck_json = nullptr;
if (deck_entry) {
auto cards_json = JSON::list();
auto cards_json = phosg::JSON::list();
for (size_t w = 0; w < deck_entry->card_ids.size(); w++) {
try {
const auto& ce = ep3s->options.card_index->definition_for_id(deck_entry->card_ids[w]);
@@ -671,7 +897,7 @@ JSON HTTPServer::generate_lobby_json_st(shared_ptr<const Lobby> l, shared_ptr<co
cards_json.emplace_back(deck_entry->card_ids[w].load());
}
}
deck_json = JSON::dict({
deck_json = phosg::JSON::dict({
{"Name", deck_entry->name.decode(lc ? lc->language() : 1)},
{"TeamID", deck_entry->team_id.load()},
{"Cards", std::move(cards_json)},
@@ -680,7 +906,7 @@ JSON HTTPServer::generate_lobby_json_st(shared_ptr<const Lobby> l, shared_ptr<co
});
}
auto player_json = JSON::dict({
auto player_json = phosg::JSON::dict({
{"PlayerName", ep3s->name_entries[z].name.decode(lc ? lc->language() : 1)},
{"ClientID", ep3s->name_entries[z].client_id},
{"IsCOM", !!ep3s->name_entries[z].is_cpu_player},
@@ -689,13 +915,13 @@ JSON HTTPServer::generate_lobby_json_st(shared_ptr<const Lobby> l, shared_ptr<co
players_json.emplace_back(std::move(player_json));
}
}
auto battle_state_json = JSON::dict({
auto battle_state_json = phosg::JSON::dict({
{"BehaviorFlags", ep3s->options.behavior_flags},
{"RandomSeed", ep3s->options.opt_rand_crypt ? ep3s->options.opt_rand_crypt->seed() : JSON(nullptr)},
{"RandomOffset", ep3s->options.opt_rand_crypt ? ep3s->options.opt_rand_crypt->absolute_offset() : JSON(nullptr)},
{"RandomSeed", ep3s->options.opt_rand_crypt ? ep3s->options.opt_rand_crypt->seed() : phosg::JSON(nullptr)},
{"RandomOffset", ep3s->options.opt_rand_crypt ? ep3s->options.opt_rand_crypt->absolute_offset() : phosg::JSON(nullptr)},
{"Tournament", ep3s->options.tournament ? ep3s->options.tournament->json() : nullptr},
{"MapNumber", ep3s->last_chosen_map ? ep3s->last_chosen_map->map_number : JSON(nullptr)},
{"EnvironmentNumber", ep3s->map_and_rules ? ep3s->map_and_rules->environment_number : JSON(nullptr)},
{"MapNumber", ep3s->last_chosen_map ? ep3s->last_chosen_map->map_number : phosg::JSON(nullptr)},
{"EnvironmentNumber", ep3s->map_and_rules ? ep3s->map_and_rules->environment_number : phosg::JSON(nullptr)},
{"Rules", ep3s->map_and_rules ? ep3s->map_and_rules->rules.json() : nullptr},
{"Players", std::move(players_json)},
{"IsBattleFinished", ep3s->battle_finished},
@@ -703,13 +929,13 @@ JSON HTTPServer::generate_lobby_json_st(shared_ptr<const Lobby> l, shared_ptr<co
{"RoundNumber", ep3s->round_num},
{"FirstTeamTurn", ep3s->first_team_turn},
{"CurrentTeamTurn", ep3s->current_team_turn1},
{"BattlePhase", name_for_enum(ep3s->battle_phase)},
{"BattlePhase", phosg::name_for_enum(ep3s->battle_phase)},
{"SetupPhase", ep3s->setup_phase},
{"RegistrationPhase", ep3s->registration_phase},
{"ActionSubphase", ep3s->action_subphase},
{"BattleStartTimeUsecs", ep3s->battle_start_usecs},
{"TeamEXP", JSON::list({ep3s->team_exp[0], ep3s->team_exp[1]})},
{"TeamDiceBonus", JSON::list({ep3s->team_dice_bonus[0], ep3s->team_dice_bonus[1]})},
{"TeamEXP", phosg::JSON::list({ep3s->team_exp[0], ep3s->team_exp[1]})},
{"TeamDiceBonus", phosg::JSON::list({ep3s->team_dice_bonus[0], ep3s->team_dice_bonus[1]})},
});
// std::shared_ptr<StateFlags> state_flags;
// std::array<std::shared_ptr<PlayerState>, 4> player_states;
@@ -721,7 +947,7 @@ JSON HTTPServer::generate_lobby_json_st(shared_ptr<const Lobby> l, shared_ptr<co
if (watched_lobby) {
ret.emplace("WatchedLobbyID", watched_lobby->lobby_id);
}
auto watcher_lobby_ids_json = JSON::list();
auto watcher_lobby_ids_json = phosg::JSON::list();
for (const auto& watcher_lobby : l->watcher_lobbies) {
watcher_lobby_ids_json.emplace_back(watcher_lobby->lobby_id);
}
@@ -738,9 +964,9 @@ JSON HTTPServer::generate_lobby_json_st(shared_ptr<const Lobby> l, shared_ptr<co
return ret;
}
JSON HTTPServer::generate_game_server_clients_json() const {
return call_on_event_thread<JSON>(this->state->base, [&]() {
auto res = JSON::list();
phosg::JSON HTTPServer::generate_game_server_clients_json() const {
return call_on_event_thread<phosg::JSON>(this->state->base, [&]() {
auto res = phosg::JSON::list();
for (const auto& it : this->state->channel_to_client) {
res.emplace_back(this->generate_game_client_json_st(it.second, this->state->item_name_index_opt(it.second->version())));
}
@@ -748,9 +974,9 @@ JSON HTTPServer::generate_game_server_clients_json() const {
});
}
JSON HTTPServer::generate_proxy_server_clients_json() const {
return call_on_event_thread<JSON>(this->state->base, [&]() {
JSON res = JSON::list();
phosg::JSON HTTPServer::generate_proxy_server_clients_json() const {
return call_on_event_thread<phosg::JSON>(this->state->base, [&]() {
phosg::JSON res = phosg::JSON::list();
if (this->state->proxy_server) {
for (const auto& it : this->state->proxy_server->all_sessions()) {
res.emplace_back(this->generate_proxy_client_json_st(it.second));
@@ -760,8 +986,8 @@ JSON HTTPServer::generate_proxy_server_clients_json() const {
});
}
JSON HTTPServer::generate_server_info_json() const {
return call_on_event_thread<JSON>(this->state->base, [&]() {
phosg::JSON HTTPServer::generate_server_info_json() const {
return call_on_event_thread<phosg::JSON>(this->state->base, [&]() {
size_t game_count = 0;
size_t lobby_count = 0;
for (const auto& it : this->state->id_to_lobby) {
@@ -771,12 +997,12 @@ JSON HTTPServer::generate_server_info_json() const {
lobby_count++;
}
}
uint64_t uptime_usecs = now() - this->state->creation_time;
return JSON::dict({
uint64_t uptime_usecs = phosg::now() - this->state->creation_time;
return phosg::JSON::dict({
{"StartTimeUsecs", this->state->creation_time},
{"StartTime", format_time(this->state->creation_time)},
{"StartTime", phosg::format_time(this->state->creation_time)},
{"UptimeUsecs", uptime_usecs},
{"Uptime", format_duration(uptime_usecs)},
{"Uptime", phosg::format_duration(uptime_usecs)},
{"LobbyCount", lobby_count},
{"GameCount", game_count},
{"ClientCount", this->state->channel_to_client.size()},
@@ -786,9 +1012,9 @@ JSON HTTPServer::generate_server_info_json() const {
});
}
JSON HTTPServer::generate_lobbies_json() const {
return call_on_event_thread<JSON>(this->state->base, [&]() {
JSON res = JSON::list();
phosg::JSON HTTPServer::generate_lobbies_json() const {
return call_on_event_thread<phosg::JSON>(this->state->base, [&]() {
phosg::JSON res = phosg::JSON::list();
for (const auto& it : this->state->id_to_lobby) {
res.emplace_back(this->generate_lobby_json_st(it.second, this->state->item_name_index_opt(it.second->base_version)));
}
@@ -796,46 +1022,46 @@ JSON HTTPServer::generate_lobbies_json() const {
});
}
JSON HTTPServer::generate_summary_json() const {
auto ret = call_on_event_thread<JSON>(this->state->base, [&]() {
auto clients_json = JSON::list();
phosg::JSON HTTPServer::generate_summary_json() const {
auto ret = call_on_event_thread<phosg::JSON>(this->state->base, [&]() {
auto clients_json = phosg::JSON::list();
for (const auto& it : this->state->channel_to_client) {
auto c = it.second;
auto p = c->character(false, false);
auto l = c->lobby.lock();
clients_json.emplace_back(JSON::dict({
clients_json.emplace_back(phosg::JSON::dict({
{"ID", c->id},
{"AccountID", c->login ? c->login->account->account_id : JSON(nullptr)},
{"Name", p ? p->disp.name.decode(it.second->language()) : JSON(nullptr)},
{"Version", name_for_enum(it.second->version())},
{"AccountID", c->login ? c->login->account->account_id : phosg::JSON(nullptr)},
{"Name", p ? p->disp.name.decode(it.second->language()) : phosg::JSON(nullptr)},
{"Version", phosg::name_for_enum(it.second->version())},
{"Language", name_for_language_code(it.second->language())},
{"Level", p ? p->disp.stats.level + 1 : JSON(nullptr)},
{"Class", p ? name_for_char_class(p->disp.visual.char_class) : JSON(nullptr)},
{"SectionID", p ? name_for_section_id(p->disp.visual.section_id) : JSON(nullptr)},
{"LobbyID", l ? l->lobby_id : JSON(nullptr)},
{"Level", p ? p->disp.stats.level + 1 : phosg::JSON(nullptr)},
{"Class", p ? name_for_char_class(p->disp.visual.char_class) : phosg::JSON(nullptr)},
{"SectionID", p ? name_for_section_id(p->disp.visual.section_id) : phosg::JSON(nullptr)},
{"LobbyID", l ? l->lobby_id : phosg::JSON(nullptr)},
}));
}
auto proxy_clients_json = JSON::list();
auto proxy_clients_json = phosg::JSON::list();
if (this->state->proxy_server) {
for (const auto& it : this->state->proxy_server->all_sessions()) {
proxy_clients_json.emplace_back(JSON::dict({
{"AccountID", it.second->login ? it.second->login->account->account_id : JSON(nullptr)},
proxy_clients_json.emplace_back(phosg::JSON::dict({
{"AccountID", it.second->login ? it.second->login->account->account_id : phosg::JSON(nullptr)},
{"Name", it.second->character_name},
{"Version", name_for_enum(it.second->version())},
{"Version", phosg::name_for_enum(it.second->version())},
{"Language", name_for_language_code(it.second->language())},
}));
}
}
auto games_json = JSON::list();
auto games_json = phosg::JSON::list();
for (const auto& it : this->state->id_to_lobby) {
auto l = it.second;
if (l->is_game()) {
auto game_json = JSON::dict({
auto game_json = phosg::JSON::dict({
{"ID", l->lobby_id},
{"Name", l->name},
{"BaseVersion", name_for_enum(l->base_version)},
{"BaseVersion", phosg::name_for_enum(l->base_version)},
{"Players", l->count_clients()},
{"CheatsEnabled", l->check_flag(Lobby::Flag::CHEATS_ENABLED)},
{"Episode", name_for_episode(l->episode)},
@@ -845,7 +1071,7 @@ JSON HTTPServer::generate_summary_json() const {
auto ep3s = l->ep3_server;
game_json.emplace("BattleInProgress", l->check_flag(Lobby::Flag::BATTLE_IN_PROGRESS));
game_json.emplace("IsSpectatorTeam", l->check_flag(Lobby::Flag::IS_SPECTATOR_TEAM));
game_json.emplace("MapNumber", (ep3s && ep3s->last_chosen_map) ? ep3s->last_chosen_map->map_number : JSON(nullptr));
game_json.emplace("MapNumber", (ep3s && ep3s->last_chosen_map) ? ep3s->last_chosen_map->map_number : phosg::JSON(nullptr));
game_json.emplace("Rules", (ep3s && ep3s->map_and_rules) ? ep3s->map_and_rules->rules.json() : nullptr);
} else {
game_json.emplace("QuestSelectionInProgress", l->check_flag(Lobby::Flag::QUEST_SELECTION_IN_PROGRESS));
@@ -860,7 +1086,7 @@ JSON HTTPServer::generate_summary_json() const {
}
}
return JSON::dict({
return phosg::JSON::dict({
{"Clients", std::move(clients_json)},
{"ProxyClients", std::move(proxy_clients_json)},
{"Games", std::move(games_json)},
@@ -870,8 +1096,8 @@ JSON HTTPServer::generate_summary_json() const {
return ret;
}
JSON HTTPServer::generate_all_json() const {
return JSON::dict({
phosg::JSON HTTPServer::generate_all_json() const {
return phosg::JSON::dict({
{"Clients", this->generate_game_server_clients_json()},
{"ProxyClients", this->generate_proxy_server_clients_json()},
{"Lobbies", this->generate_lobbies_json()},
@@ -879,43 +1105,43 @@ JSON HTTPServer::generate_all_json() const {
});
}
JSON HTTPServer::generate_ep3_cards_json(bool trial) const {
phosg::JSON HTTPServer::generate_ep3_cards_json(bool trial) const {
auto index = call_on_event_thread<shared_ptr<const Episode3::CardIndex>>(this->state->base, [&]() {
return trial ? this->state->ep3_card_index_trial : this->state->ep3_card_index;
});
return index->definitions_json();
}
JSON HTTPServer::generate_common_tables_json() const {
phosg::JSON HTTPServer::generate_common_tables_json() const {
auto [set_v2, set_v3_v4] = call_on_event_thread<pair<shared_ptr<const CommonItemSet>, shared_ptr<const CommonItemSet>>>(this->state->base, [&]() {
return make_pair(this->state->common_item_set_v2, this->state->common_item_set_v3_v4);
});
return JSON::dict({{"v1_v2", set_v2->json()}, {"v3_v4", set_v3_v4->json()}});
return phosg::JSON::dict({{"v1_v2", set_v2->json()}, {"v3_v4", set_v3_v4->json()}});
}
JSON HTTPServer::generate_rare_tables_json() const {
phosg::JSON HTTPServer::generate_rare_tables_json() const {
auto sets = call_on_event_thread<unordered_map<string, shared_ptr<const RareItemSet>>>(this->state->base, [&]() {
return this->state->rare_item_sets;
});
JSON ret = JSON::list();
phosg::JSON ret = phosg::JSON::list();
for (const auto& it : sets) {
ret.emplace_back(it.first);
}
return ret;
}
JSON HTTPServer::generate_rare_table_json(const std::string& table_name) const {
phosg::JSON HTTPServer::generate_rare_table_json(const std::string& table_name) const {
try {
auto colls = call_on_event_thread<pair<shared_ptr<const RareItemSet>, shared_ptr<const ItemNameIndex>>>(this->state->base, [&]() {
const auto& table = this->state->rare_item_sets.at(table_name);
shared_ptr<const ItemNameIndex> name_index;
if (ends_with(table_name, "-v1")) {
if (phosg::ends_with(table_name, "-v1")) {
name_index = this->state->item_name_index_opt(Version::DC_V1);
} else if (ends_with(table_name, "-v2")) {
} else if (phosg::ends_with(table_name, "-v2")) {
name_index = this->state->item_name_index_opt(Version::PC_V2);
} else if (ends_with(table_name, "-v3")) {
} else if (phosg::ends_with(table_name, "-v3")) {
name_index = this->state->item_name_index_opt(Version::GC_V3);
} else if (ends_with(table_name, "-v4")) {
} else if (phosg::ends_with(table_name, "-v4")) {
name_index = this->state->item_name_index_opt(Version::BB_V4);
}
return make_pair(table, name_index);
@@ -927,9 +1153,9 @@ JSON HTTPServer::generate_rare_table_json(const std::string& table_name) const {
}
void HTTPServer::handle_request(struct evhttp_request* req) {
shared_ptr<const JSON> ret;
shared_ptr<const phosg::JSON> ret;
uint32_t serialize_options = 0;
uint64_t start_time = now();
uint64_t start_time = phosg::now();
string uri = evhttp_request_get_uri(req);
try {
@@ -942,14 +1168,14 @@ void HTTPServer::handle_request(struct evhttp_request* req) {
static const string default_format_option = "false";
if (this->get_url_param(query, "format", &default_format_option) == "true") {
serialize_options |= JSON::SerializeOption::FORMAT | JSON::SerializeOption::SORT_DICT_KEYS;
serialize_options |= phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::SORT_DICT_KEYS;
}
if (this->get_url_param(query, "hex", &default_format_option) == "true") {
serialize_options |= JSON::SerializeOption::HEX_INTEGERS;
serialize_options |= phosg::JSON::SerializeOption::HEX_INTEGERS;
}
if (uri == "/") {
auto endpoints_json = JSON::list({
auto endpoints_json = phosg::JSON::list({
"/y/data/ep3-cards",
"/y/data/ep3-cards-trial",
"/y/data/common-tables",
@@ -960,35 +1186,47 @@ void HTTPServer::handle_request(struct evhttp_request* req) {
"/y/proxy-clients",
"/y/lobbies",
"/y/server",
"/y/rare-drops/stream",
"/y/summary",
"/y/all",
});
ret = make_shared<JSON>(JSON::dict({{"endpoints", std::move(endpoints_json)}}));
ret = make_shared<phosg::JSON>(phosg::JSON::dict({{"endpoints", std::move(endpoints_json)}}));
} else if (uri == "/y/rare-drops/stream") {
auto c = this->enable_websockets(req);
if (!c) {
throw http_error(400, "this path requires a websocket connection");
} else {
this->rare_drop_subscribers.emplace(c);
auto version_message = phosg::JSON::dict({{"ServerType", "newserv"}});
this->send_websocket_message(c, version_message.serialize());
return;
}
} else if (uri == "/y/data/ep3-cards") {
ret = make_shared<JSON>(this->generate_ep3_cards_json(false));
ret = make_shared<phosg::JSON>(this->generate_ep3_cards_json(false));
} else if (uri == "/y/data/ep3-cards-trial") {
ret = make_shared<JSON>(this->generate_ep3_cards_json(true));
ret = make_shared<phosg::JSON>(this->generate_ep3_cards_json(true));
} else if (uri == "/y/data/common-tables") {
ret = make_shared<JSON>(this->generate_common_tables_json());
ret = make_shared<phosg::JSON>(this->generate_common_tables_json());
} else if (uri == "/y/data/rare-tables") {
ret = make_shared<JSON>(this->generate_rare_tables_json());
ret = make_shared<phosg::JSON>(this->generate_rare_tables_json());
} else if (!strncmp(uri.c_str(), "/y/data/rare-tables/", 20)) {
ret = make_shared<JSON>(this->generate_rare_table_json(uri.substr(20)));
ret = make_shared<phosg::JSON>(this->generate_rare_table_json(uri.substr(20)));
} else if (uri == "/y/data/config") {
ret = call_on_event_thread<shared_ptr<const JSON>>(this->state->base, [this]() { return this->state->config_json; });
ret = call_on_event_thread<shared_ptr<const phosg::JSON>>(this->state->base, [this]() { return this->state->config_json; });
} else if (uri == "/y/clients") {
ret = make_shared<JSON>(this->generate_game_server_clients_json());
ret = make_shared<phosg::JSON>(this->generate_game_server_clients_json());
} else if (uri == "/y/proxy-clients") {
ret = make_shared<JSON>(this->generate_proxy_server_clients_json());
ret = make_shared<phosg::JSON>(this->generate_proxy_server_clients_json());
} else if (uri == "/y/lobbies") {
ret = make_shared<JSON>(this->generate_lobbies_json());
ret = make_shared<phosg::JSON>(this->generate_lobbies_json());
} else if (uri == "/y/server") {
ret = make_shared<JSON>(this->generate_server_info_json());
ret = make_shared<phosg::JSON>(this->generate_server_info_json());
} else if (uri == "/y/summary") {
ret = make_shared<JSON>(this->generate_summary_json());
ret = make_shared<phosg::JSON>(this->generate_summary_json());
} else if (uri == "/y/all") {
ret = make_shared<JSON>(this->generate_all_json());
ret = make_shared<phosg::JSON>(this->generate_all_json());
} else {
throw http_error(404, "unknown action");
@@ -1008,20 +1246,20 @@ void HTTPServer::handle_request(struct evhttp_request* req) {
return;
}
uint64_t handler_end = now();
uint64_t handler_end = phosg::now();
unique_ptr<struct evbuffer, void (*)(struct evbuffer*)> out_buffer(evbuffer_new(), evbuffer_free);
string* serialized = new string(ret->serialize(JSON::SerializeOption::ESCAPE_CONTROLS_ONLY | serialize_options));
string* serialized = new string(ret->serialize(phosg::JSON::SerializeOption::ESCAPE_CONTROLS_ONLY | serialize_options));
size_t size = serialized->size();
uint64_t serialize_end = now();
uint64_t serialize_end = phosg::now();
auto cleanup = +[](const void*, size_t, void* s) -> void {
delete reinterpret_cast<string*>(s);
};
evbuffer_add_reference(out_buffer.get(), serialized->data(), serialized->size(), cleanup, serialized);
this->send_response(req, 200, "application/json", out_buffer.get());
string handler_time = format_duration(handler_end - start_time);
string serialize_time = format_duration(serialize_end - handler_end);
string size_str = format_size(size);
string handler_time = phosg::format_duration(handler_end - start_time);
string serialize_time = phosg::format_duration(serialize_end - handler_end);
string size_str = phosg::format_size(size);
server_log.info("[HTTPServer] %s in [handler: %s, serialize: %s, size: %s]",
uri.c_str(), handler_time.c_str(), serialize_time.c_str(), size_str.c_str());
}
+54 -16
View File
@@ -28,6 +28,8 @@ public:
void schedule_stop();
void wait_for_stop();
void send_rare_drop_notification(std::shared_ptr<const phosg::JSON> message);
protected:
class http_error : public std::runtime_error {
public:
@@ -35,11 +37,47 @@ protected:
int code;
};
struct WebsocketClient {
struct evhttp_connection* conn;
struct bufferevent* bev;
uint8_t pending_opcode;
std::string pending_data;
uint64_t last_communication_time;
void* context;
WebsocketClient(struct evhttp_connection* conn);
~WebsocketClient();
void reset_pending_frame();
};
std::shared_ptr<ServerState> state;
std::shared_ptr<struct event_base> base;
std::shared_ptr<struct evhttp> http;
std::thread th;
std::unordered_set<std::shared_ptr<WebsocketClient>> rare_drop_subscribers;
std::unordered_map<struct bufferevent*, std::shared_ptr<WebsocketClient>> bev_to_websocket_client;
std::shared_ptr<WebsocketClient> enable_websockets(struct evhttp_request* req);
static void dispatch_on_websocket_read(struct bufferevent* bev, void* ctx);
static void dispatch_on_websocket_error(struct bufferevent* bev, short events, void* ctx);
void on_websocket_read(struct bufferevent* bev);
void on_websocket_error(struct bufferevent* bev, short events);
void disconnect_websocket_client(struct bufferevent* bev);
void send_websocket_message(struct bufferevent* bev, const std::string& message, uint8_t opcode = 0x01);
void send_websocket_message(std::shared_ptr<WebsocketClient> c, const std::string& message, uint8_t opcode = 0x01);
virtual void handle_websocket_message(std::shared_ptr<WebsocketClient> c, uint8_t opcode, const std::string& message);
virtual void handle_websocket_disconnect(std::shared_ptr<WebsocketClient> c);
void thread_fn();
static void dispatch_handle_request(struct evhttp_request* req, void* ctx);
@@ -56,21 +94,21 @@ protected:
const std::string& key,
const std::string* _default = nullptr);
static JSON generate_quest_json_st(std::shared_ptr<const Quest> q);
static JSON generate_client_config_json_st(const Client::Config& config);
static JSON generate_account_json_st(std::shared_ptr<const Account> a);
static JSON generate_game_client_json_st(std::shared_ptr<const Client> c, std::shared_ptr<const ItemNameIndex> item_name_index);
static JSON generate_proxy_client_json_st(std::shared_ptr<const ProxyServer::LinkedSession> ses);
static JSON generate_lobby_json_st(std::shared_ptr<const Lobby> l, std::shared_ptr<const ItemNameIndex> item_name_index);
JSON generate_game_server_clients_json() const;
JSON generate_proxy_server_clients_json() const;
JSON generate_server_info_json() const;
JSON generate_lobbies_json() const;
JSON generate_summary_json() const;
JSON generate_all_json() const;
static phosg::JSON generate_quest_json_st(std::shared_ptr<const Quest> q);
static phosg::JSON generate_client_config_json_st(const Client::Config& config);
static phosg::JSON generate_account_json_st(std::shared_ptr<const Account> a);
static phosg::JSON generate_game_client_json_st(std::shared_ptr<const Client> c, std::shared_ptr<const ItemNameIndex> item_name_index);
static phosg::JSON generate_proxy_client_json_st(std::shared_ptr<const ProxyServer::LinkedSession> ses);
static phosg::JSON generate_lobby_json_st(std::shared_ptr<const Lobby> l, std::shared_ptr<const ItemNameIndex> item_name_index);
phosg::JSON generate_game_server_clients_json() const;
phosg::JSON generate_proxy_server_clients_json() const;
phosg::JSON generate_server_info_json() const;
phosg::JSON generate_lobbies_json() const;
phosg::JSON generate_summary_json() const;
phosg::JSON generate_all_json() const;
JSON generate_ep3_cards_json(bool trial) const;
JSON generate_common_tables_json() const;
JSON generate_rare_tables_json() const;
JSON generate_rare_table_json(const std::string& table_name) const;
phosg::JSON generate_ep3_cards_json(bool trial) const;
phosg::JSON generate_common_tables_json() const;
phosg::JSON generate_rare_tables_json() const;
phosg::JSON generate_rare_table_json(const std::string& table_name) const;
};
+10 -10
View File
@@ -23,7 +23,7 @@ FrameInfo::FrameInfo(LinkType link_type, const void* header_start, size_t size)
this->total_size = size;
this->payload_size = size;
StringReader r(header_start, size);
phosg::StringReader r(header_start, size);
// Parse link-layer header
Protocol proto = Protocol::NONE;
@@ -126,35 +126,35 @@ string FrameInfo::header_str() const {
string ret;
if (this->ether) {
ret = string_printf(
ret = phosg::string_printf(
"ETHER:%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX->%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
this->ether->src_mac[0], this->ether->src_mac[1], this->ether->src_mac[2],
this->ether->src_mac[3], this->ether->src_mac[4], this->ether->src_mac[5],
this->ether->dest_mac[0], this->ether->dest_mac[1], this->ether->dest_mac[2],
this->ether->dest_mac[3], this->ether->dest_mac[4], this->ether->dest_mac[5]);
} else if (this->hdlc) {
ret = string_printf("HDLC:%02hhX/%02hhX", this->hdlc->address, this->hdlc->control);
ret = phosg::string_printf("HDLC:%02hhX/%02hhX", this->hdlc->address, this->hdlc->control);
} else {
return "<invalid-frame-info>";
}
if (this->arp) {
ret += string_printf(
ret += phosg::string_printf(
",ARP,hw_type=%04hX,proto_type=%04hX,hw_addr_len=%02hhX,proto_addr_len=%02hhX,op=%04hX",
this->arp->hardware_type.load(), this->arp->protocol_type.load(), this->arp->hwaddr_len, this->arp->paddr_len, this->arp->operation.load());
} else if (this->ipv4) {
ret += string_printf(
ret += phosg::string_printf(
",IPv4,size=%04hX,src=%08" PRIX32 ",dest=%08" PRIX32,
this->ipv4->size.load(), this->ipv4->src_addr.load(), this->ipv4->dest_addr.load());
if (this->udp) {
ret += string_printf(
ret += phosg::string_printf(
",UDP,src_port=%04hX,dest_port=%04hX,size=%04hX",
this->udp->src_port.load(), this->udp->dest_port.load(), this->udp->size.load());
} else if (this->tcp) {
ret += string_printf(
ret += phosg::string_printf(
",TCP,src_port=%04hX,dest_port=%04hX,seq=%08" PRIX32 ",ack=%08" PRIX32 ",flags=%04hX(",
this->tcp->src_port.load(), this->tcp->dest_port.load(), this->tcp->seq_num.load(), this->tcp->ack_num.load(), this->tcp->flags.load());
if (this->tcp->flags & TCPHeader::Flag::FIN) {
@@ -175,14 +175,14 @@ string FrameInfo::header_str() const {
ret += ')';
} else {
ret += string_printf(",proto=%02hhX", this->ipv4->protocol);
ret += phosg::string_printf(",proto=%02hhX", this->ipv4->protocol);
}
} else {
if (this->ether) {
ret += string_printf(",proto=%04hX", this->ether->protocol.load());
ret += phosg::string_printf(",proto=%04hX", this->ether->protocol.load());
} else if (this->hdlc) {
ret += string_printf(",proto=%04hX", this->hdlc->protocol.load());
ret += phosg::string_printf(",proto=%04hX", this->hdlc->protocol.load());
}
}
+2 -2
View File
@@ -161,8 +161,8 @@ struct FrameInfo {
std::string header_str() const;
inline StringReader read_payload() const {
return StringReader(this->payload, this->payload_size);
inline phosg::StringReader read_payload() const {
return phosg::StringReader(this->payload, this->payload_size);
}
void truncate(size_t new_total_size);
+50 -51
View File
@@ -22,7 +22,7 @@ using namespace std;
static const size_t DEFAULT_RESEND_PUSH_USECS = 200000; // 200ms
static string unescape_hdlc_frame(const void* data, size_t size) {
StringReader r(data, size);
phosg::StringReader r(data, size);
if (r.get_u8(data) != 0x7E) {
throw runtime_error("HDLC frame does not begin with 7E");
}
@@ -53,7 +53,7 @@ static string escape_hdlc_frame(const void* data, size_t size, uint32_t escape_c
throw runtime_error("HDLC frame too small for start and end sentinels");
}
StringReader r(data, size);
phosg::StringReader r(data, size);
if (r.pget_u8(size - 1) != 0x7E) {
throw runtime_error("HDLC frame does not end with 7E");
}
@@ -104,9 +104,9 @@ string IPStackSimulator::str_for_ipv4_netloc(uint32_t addr, uint16_t port) {
be_uint32_t be_addr = addr;
char addr_str[INET_ADDRSTRLEN];
if (!inet_ntop(AF_INET, &be_addr, addr_str, INET_ADDRSTRLEN)) {
return string_printf("<UNKNOWN>:%hu", port);
return phosg::string_printf("<UNKNOWN>:%hu", port);
} else {
return string_printf("%s:%hu", addr_str, port);
return phosg::string_printf("%s:%hu", addr_str, port);
}
}
@@ -115,7 +115,7 @@ string IPStackSimulator::str_for_tcp_connection(shared_ptr<const IPClient> c, co
string server_netloc_str = str_for_ipv4_netloc(conn.server_addr, conn.server_port);
string client_netloc_str = str_for_ipv4_netloc(c->ipv4_addr, conn.client_port);
int fd = bufferevent_getfd(c->bev.get());
return string_printf("%d+%016" PRIX64 " (%s -> %s)",
return phosg::string_printf("%d+%016" PRIX64 " (%s -> %s)",
fd, key, client_netloc_str.c_str(), server_netloc_str.c_str());
}
@@ -137,7 +137,7 @@ IPStackSimulator::~IPStackSimulator() {
}
void IPStackSimulator::listen(const string& name, const string& socket_path, Protocol proto) {
int fd = ::listen(socket_path, 0, SOMAXCONN);
int fd = phosg::listen(socket_path, 0, SOMAXCONN);
ip_stack_simulator_log.info("Listening on Unix socket %s on fd %d as %s", socket_path.c_str(), fd, name.c_str());
this->add_socket(name, fd, proto);
}
@@ -146,8 +146,8 @@ void IPStackSimulator::listen(const string& name, const string& addr, int port,
if (port == 0) {
this->listen(name, addr, proto);
} else {
int fd = ::listen(addr, port, SOMAXCONN);
string netloc_str = render_netloc(addr, port);
int fd = phosg::listen(addr, port, SOMAXCONN);
string netloc_str = phosg::render_netloc(addr, port);
ip_stack_simulator_log.info("Listening on TCP interface %s on fd %d as %s", netloc_str.c_str(), fd, name.c_str());
this->add_socket(name, fd, proto);
}
@@ -195,7 +195,7 @@ IPStackSimulator::IPClient::IPClient(
ipv4_addr(0),
idle_timeout_event(event_new(sim->base.get(), -1, EV_TIMEOUT, &IPStackSimulator::IPClient::dispatch_on_idle_timeout, this), event_free) {
uint64_t idle_timeout_usecs = sim->state->client_idle_timeout_usecs;
struct timeval tv = usecs_to_timeval(idle_timeout_usecs);
struct timeval tv = phosg::usecs_to_timeval(idle_timeout_usecs);
event_add(this->idle_timeout_event.get(), &tv);
}
@@ -247,7 +247,7 @@ void IPStackSimulator::dispatch_on_listen_accept(
void IPStackSimulator::on_listen_accept(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr*, int) {
struct sockaddr_storage remote_addr;
get_socket_addresses(fd, nullptr, &remote_addr);
phosg::get_socket_addresses(fd, nullptr, &remote_addr);
if (this->state->banned_ipv4_ranges->check(remote_addr)) {
close(fd);
return;
@@ -304,7 +304,7 @@ void IPStackSimulator::IPClient::on_client_input(struct bufferevent* bev) {
}
uint64_t idle_timeout_usecs = sim ? sim->state->client_idle_timeout_usecs : 60000000;
struct timeval tv = usecs_to_timeval(idle_timeout_usecs);
struct timeval tv = phosg::usecs_to_timeval(idle_timeout_usecs);
event_add(this->idle_timeout_event.get(), &tv);
switch (this->protocol) {
@@ -325,7 +325,7 @@ void IPStackSimulator::IPClient::on_client_input(struct bufferevent* bev) {
sim->on_client_frame(this->shared_from_this(), frame);
} catch (const exception& e) {
if (ip_stack_simulator_log.warning("Failed to process frame: %s", e.what())) {
print_data(stderr, frame);
phosg::print_data(stderr, frame);
}
}
}
@@ -360,7 +360,7 @@ void IPStackSimulator::IPClient::on_client_input(struct bufferevent* bev) {
sim->on_client_frame(this->shared_from_this(), frame);
} catch (const exception& e) {
if (ip_stack_simulator_log.warning("Failed to process frame: %s", e.what())) {
print_data(stderr, frame);
phosg::print_data(stderr, frame);
}
}
}
@@ -416,7 +416,7 @@ void IPStackSimulator::send_layer3_frame(shared_ptr<IPClient> c, FrameInfo::Prot
evbuffer_add(out_buf, &ether, sizeof(ether));
evbuffer_add(out_buf, data, size);
if (this->pcap_text_log_file) {
StringWriter w;
phosg::StringWriter w;
w.write(&ether, sizeof(ether));
w.write(data, size);
this->log_frame(w.str());
@@ -451,7 +451,7 @@ void IPStackSimulator::send_layer3_frame(shared_ptr<IPClient> c, FrameInfo::Prot
throw logic_error("unknown layer 3 protocol");
}
StringWriter w;
phosg::StringWriter w;
w.put(hdlc);
w.write(data, size);
w.put_u16l(FrameInfo::computed_hdlc_checksum(w.str().data() + 1, w.size() - 1));
@@ -459,7 +459,7 @@ void IPStackSimulator::send_layer3_frame(shared_ptr<IPClient> c, FrameInfo::Prot
string escaped = escape_hdlc_frame(w.str(), c->hdlc_escape_control_character_flags);
if (ip_stack_simulator_log.debug("Sending HDLC frame to virtual network (escaped to %zX bytes)", escaped.size())) {
print_data(stderr, w.str());
phosg::print_data(stderr, w.str());
}
if (c->protocol == Protocol::HDLC_TAPSERVER) {
@@ -490,12 +490,12 @@ void IPStackSimulator::on_client_frame(shared_ptr<IPClient> c, const string& fra
effective_data = &hdlc_unescaped_data;
}
if (ip_stack_simulator_log.debug("Virtual network sent frame")) {
print_data(stderr, *effective_data);
phosg::print_data(stderr, *effective_data);
}
this->log_frame(*effective_data);
FrameInfo fi(link_type, *effective_data);
if (ip_stack_simulator_log.should_log(LogLevel::DEBUG)) {
if (ip_stack_simulator_log.should_log(phosg::LogLevel::DEBUG)) {
string fi_header = fi.header_str();
ip_stack_simulator_log.debug("Frame header: %s", fi_header.c_str());
}
@@ -510,7 +510,7 @@ void IPStackSimulator::on_client_frame(shared_ptr<IPClient> c, const string& fra
uint16_t expected_checksum = fi.computed_hdlc_checksum();
uint16_t stored_checksum = fi.stored_hdlc_checksum();
if (expected_checksum != stored_checksum) {
throw runtime_error(string_printf(
throw runtime_error(phosg::string_printf(
"HDLC checksum is incorrect (%04hX expected, %04hX received)",
expected_checksum, stored_checksum));
}
@@ -533,7 +533,7 @@ void IPStackSimulator::on_client_frame(shared_ptr<IPClient> c, const string& fra
} else if (fi.ipv4) {
uint16_t expected_ipv4_checksum = fi.computed_ipv4_header_checksum();
if (fi.ipv4->checksum != expected_ipv4_checksum) {
throw runtime_error(string_printf(
throw runtime_error(phosg::string_printf(
"IPv4 header checksum is incorrect (%04hX expected, %04hX received)",
expected_ipv4_checksum, fi.ipv4->checksum.load()));
}
@@ -545,7 +545,7 @@ void IPStackSimulator::on_client_frame(shared_ptr<IPClient> c, const string& fra
if (fi.udp) {
uint16_t expected_udp_checksum = fi.computed_udp4_checksum();
if (fi.udp->checksum != expected_udp_checksum) {
throw runtime_error(string_printf(
throw runtime_error(phosg::string_printf(
"UDP checksum is incorrect (%04hX expected, %04hX received)",
expected_udp_checksum, fi.udp->checksum.load()));
}
@@ -554,7 +554,7 @@ void IPStackSimulator::on_client_frame(shared_ptr<IPClient> c, const string& fra
} else if (fi.tcp) {
uint16_t expected_tcp_checksum = fi.computed_tcp4_checksum();
if (fi.tcp->checksum != expected_tcp_checksum) {
throw runtime_error(string_printf(
throw runtime_error(phosg::string_printf(
"TCP checksum is incorrect (%04hX expected, %04hX received)",
expected_tcp_checksum, fi.tcp->checksum.load()));
}
@@ -576,7 +576,7 @@ void IPStackSimulator::on_client_lcp_frame(shared_ptr<IPClient> c, const FrameIn
while (!opts_r.eof()) {
uint8_t opt = opts_r.get_u8();
string opt_data = opts_r.read(opts_r.get_u8() - 2);
StringReader opt_data_r(opt_data);
phosg::StringReader opt_data_r(opt_data);
switch (opt) {
case 0x01: // Maximum receive unit
// TODO: Currently we ignore this, but we probably should use it.
@@ -593,7 +593,7 @@ void IPStackSimulator::on_client_lcp_frame(shared_ptr<IPClient> c, const FrameIn
case 0x04: // Quality protocol
case 0x07: // Protocol field compression
case 0x08: // Address and control field compression
throw runtime_error(string_printf("unimplemented LCP option %02hhX (%zu bytes)", opt, opt_data.size()));
throw runtime_error(phosg::string_printf("unimplemented LCP option %02hhX (%zu bytes)", opt, opt_data.size()));
default:
throw runtime_error("unknown LCP option");
}
@@ -602,7 +602,7 @@ void IPStackSimulator::on_client_lcp_frame(shared_ptr<IPClient> c, const FrameIn
// lazy to do this right now. In our situation, it should suffice to
// simply always send a Configure-Request to the client with a magic
// number not equal to the one we received.
StringWriter opts_w;
phosg::StringWriter opts_w;
opts_w.put_u8(0x01); // Maximum receive unit
opts_w.put_u8(0x04);
opts_w.put_u16b(1500);
@@ -615,7 +615,7 @@ void IPStackSimulator::on_client_lcp_frame(shared_ptr<IPClient> c, const FrameIn
opts_w.put_u8(0x05); // Magic number (bitwise inverse of the remote end's)
opts_w.put_u8(0x06);
opts_w.put_u32b(~c->hdlc_remote_magic_number);
StringWriter request_w;
phosg::StringWriter request_w;
request_w.put<LCPHeader>(LCPHeader{
.command = 0x01, // Configure-Request
.request_id = fi.lcp->request_id,
@@ -624,7 +624,7 @@ void IPStackSimulator::on_client_lcp_frame(shared_ptr<IPClient> c, const FrameIn
request_w.write(opts_w.str());
this->send_layer3_frame(c, FrameInfo::Protocol::LCP, request_w.str());
StringWriter ack_w;
phosg::StringWriter ack_w;
ack_w.put<LCPHeader>(LCPHeader{
.command = 0x02, // Configure-Ack
.request_id = fi.lcp->request_id,
@@ -679,7 +679,7 @@ void IPStackSimulator::on_client_pap_frame(shared_ptr<IPClient> c, const FrameIn
ip_stack_simulator_log.info("Client logged in with username \"%s\" and password", username.c_str());
static const string login_message = "newserv PPP simulator";
StringWriter w;
phosg::StringWriter w;
w.put<PAPHeader>(PAPHeader{
.command = 0x02, // Authenticate-Ack
.request_id = fi.pap->request_id,
@@ -698,11 +698,11 @@ void IPStackSimulator::on_client_ipcp_frame(shared_ptr<IPClient> c, const FrameI
uint32_t remote_ip = 0;
uint32_t remote_primary_dns = 0;
uint32_t remote_secondary_dns = 0;
StringWriter rejected_opts_w;
phosg::StringWriter rejected_opts_w;
while (!opts_r.eof()) {
uint8_t opt = opts_r.get_u8();
string opt_data = opts_r.read(opts_r.get_u8() - 2);
StringReader opt_data_r(opt_data);
phosg::StringReader opt_data_r(opt_data);
switch (opt) {
case 0x01: // IP addresses (deprecated as of 1992; we don't support it at all)
throw runtime_error("IPCP client sent IP-Addresses option");
@@ -723,7 +723,7 @@ void IPStackSimulator::on_client_ipcp_frame(shared_ptr<IPClient> c, const FrameI
case 0x82: // Primary NBNS server address
case 0x84: // Secondary NBNS server address
case 0x04: // Mobile IP address
throw runtime_error(string_printf("unimplemented IPCP option %02hhX (%zu bytes)", opt, opt_data.size()));
throw runtime_error(phosg::string_printf("unimplemented IPCP option %02hhX (%zu bytes)", opt, opt_data.size()));
default:
throw runtime_error("unknown IPCP option");
}
@@ -731,7 +731,7 @@ void IPStackSimulator::on_client_ipcp_frame(shared_ptr<IPClient> c, const FrameI
if (!rejected_opts_w.str().empty()) {
// Send a Configure-Reject if the client specified IP header compression
StringWriter reject_w;
phosg::StringWriter reject_w;
reject_w.put<IPCPHeader>(IPCPHeader{
.command = 0x04, // Configure-Reject
.request_id = fi.ipcp->request_id,
@@ -745,7 +745,7 @@ void IPStackSimulator::on_client_ipcp_frame(shared_ptr<IPClient> c, const FrameI
(remote_secondary_dns != 0x24242424)) {
// Send a Configure-Nak if the client's request doesn't exactly match
// what we want them to use.
StringWriter opts_w;
phosg::StringWriter opts_w;
opts_w.put_u8(0x03); // IP address
opts_w.put_u8(0x06);
opts_w.put_u32b(0x1E1E1E1E);
@@ -756,7 +756,7 @@ void IPStackSimulator::on_client_ipcp_frame(shared_ptr<IPClient> c, const FrameI
opts_w.put_u8(0x06);
opts_w.put_u32b(0x24242424);
StringWriter nak_w;
phosg::StringWriter nak_w;
nak_w.put<IPCPHeader>(IPCPHeader{
.command = 0x03, // Configure-Nak
.request_id = fi.ipcp->request_id,
@@ -770,7 +770,7 @@ void IPStackSimulator::on_client_ipcp_frame(shared_ptr<IPClient> c, const FrameI
// As with LCP, we technically should implement the state machine, but I
// continue to be lazy.
StringWriter opts_w;
phosg::StringWriter opts_w;
opts_w.put_u8(0x03); // IP address
opts_w.put_u8(0x06);
opts_w.put_u32b(0x39393939);
@@ -781,7 +781,7 @@ void IPStackSimulator::on_client_ipcp_frame(shared_ptr<IPClient> c, const FrameI
opts_w.put_u8(0x06);
opts_w.put_u32b(0x24242424);
StringWriter request_w;
phosg::StringWriter request_w;
request_w.put<IPCPHeader>(IPCPHeader{
.command = 0x01, // Configure-Request
.request_id = fi.ipcp->request_id,
@@ -790,7 +790,7 @@ void IPStackSimulator::on_client_ipcp_frame(shared_ptr<IPClient> c, const FrameI
request_w.write(opts_w.str());
this->send_layer3_frame(c, FrameInfo::Protocol::IPCP, request_w.str());
StringWriter ack_w;
phosg::StringWriter ack_w;
ack_w.put<IPCPHeader>(IPCPHeader{
.command = 0x02, // Configure-Ack
.request_id = fi.ipcp->request_id,
@@ -841,7 +841,7 @@ void IPStackSimulator::on_client_arp_frame(
reinterpret_cast<const uint8_t*>(fi.payload) + 6);
}
StringWriter w;
phosg::StringWriter w;
w.put<ARPHeader>(ARPHeader{
.hardware_type = fi.arp->hardware_type,
.protocol_type = fi.arp->protocol_type,
@@ -942,7 +942,7 @@ void IPStackSimulator::on_client_udp_frame(shared_ptr<IPClient> c, const FrameIn
throw runtime_error("client sent unknown DHCP command option");
}
StringWriter w;
phosg::StringWriter w;
DHCPHeader r_dhcp;
r_dhcp.opcode = 2; // Response
r_dhcp.hardware_type = 1; // Ethernet
@@ -1027,13 +1027,13 @@ void IPStackSimulator::on_client_udp_frame(shared_ptr<IPClient> c, const FrameIn
r_udp.checksum = FrameInfo::computed_udp4_checksum(
r_ipv4, r_udp, r_data.data(), r_data.size());
if (ip_stack_simulator_log.should_log(LogLevel::DEBUG)) {
if (ip_stack_simulator_log.should_log(phosg::LogLevel::DEBUG)) {
string remote_str = this->str_for_ipv4_netloc(fi.ipv4->src_addr, fi.udp->src_port);
ip_stack_simulator_log.debug("Sending UDP response to %s", remote_str.c_str());
print_data(stderr, r_data);
phosg::print_data(stderr, r_data);
}
StringWriter w;
phosg::StringWriter w;
w.put(r_ipv4);
w.put(r_udp);
w.write(r_data);
@@ -1079,7 +1079,7 @@ void IPStackSimulator::on_client_tcp_frame(
throw runtime_error("TCP SYN contains extra flags");
}
StringReader options_r(fi.tcp + 1, fi.tcp_options_size);
phosg::StringReader options_r(fi.tcp + 1, fi.tcp_options_size);
size_t max_frame_size = 1400;
while (!options_r.eof()) {
uint8_t option = options_r.get_u8();
@@ -1135,7 +1135,7 @@ void IPStackSimulator::on_client_tcp_frame(
conn.server_port = fi.tcp->dest_port;
conn.client_port = fi.tcp->src_port;
conn.next_client_seq = fi.tcp->seq_num + 1;
conn.acked_server_seq = random_object<uint32_t>();
conn.acked_server_seq = phosg::random_object<uint32_t>();
conn.resend_push_usecs = DEFAULT_RESEND_PUSH_USECS;
conn.next_push_max_frame_size = max_frame_size;
conn.awaiting_first_ack = true;
@@ -1240,7 +1240,7 @@ void IPStackSimulator::on_client_tcp_frame(
// server immediately.
} else if (fi.payload_size != 0) {
string conn_str = ip_stack_simulator_log.should_log(LogLevel::WARNING)
string conn_str = ip_stack_simulator_log.should_log(phosg::LogLevel::WARNING)
? this->str_for_tcp_connection(c, *conn)
: "";
@@ -1285,7 +1285,7 @@ void IPStackSimulator::on_client_tcp_frame(
conn_str.c_str());
}
if (was_logged) {
print_data(stderr, payload, payload_size);
phosg::print_data(stderr, payload, payload_size);
}
// Send the new data to the server
@@ -1388,7 +1388,7 @@ void IPStackSimulator::send_pending_push_frame(
conn.acked_server_seq, bytes_to_send, pending_bytes);
this->send_tcp_frame(c, conn, TCPHeader::Flag::PSH, conn.pending_data.get(), bytes_to_send);
struct timeval resend_push_timeout = usecs_to_timeval(conn.resend_push_usecs);
struct timeval resend_push_timeout = phosg::usecs_to_timeval(conn.resend_push_usecs);
event_add(conn.resend_push_event.get(), &resend_push_timeout);
// If the client isn't responding to our PSHes, back off exponentially up to
@@ -1441,7 +1441,7 @@ void IPStackSimulator::send_tcp_frame(
const void* linear_data = src_bytes ? evbuffer_pullup(src_buf, src_bytes) : nullptr;
tcp.checksum = FrameInfo::computed_tcp4_checksum(ipv4, tcp, linear_data, src_bytes);
StringWriter w;
phosg::StringWriter w;
w.put(ipv4);
w.put(tcp);
if (src_bytes) {
@@ -1488,7 +1488,7 @@ void IPStackSimulator::on_server_input(shared_ptr<IPClient> c, IPClient::TCPConn
auto sim = c->sim.lock();
uint64_t idle_timeout_usecs = sim ? sim->state->client_idle_timeout_usecs : 60000000;
struct timeval tv = usecs_to_timeval(idle_timeout_usecs);
struct timeval tv = phosg::usecs_to_timeval(idle_timeout_usecs);
event_add(c->idle_timeout_event.get(), &tv);
evbuffer_add_buffer(conn.pending_data.get(), buf);
@@ -1534,8 +1534,7 @@ void IPStackSimulator::on_server_error(
void IPStackSimulator::log_frame(const string& data) const {
if (this->pcap_text_log_file) {
print_data(this->pcap_text_log_file, data, 0, nullptr,
PrintDataFlags::SKIP_SEPARATOR);
phosg::print_data(this->pcap_text_log_file, data, 0, nullptr, phosg::PrintDataFlags::SKIP_SEPARATOR);
fputc('\n', this->pcap_text_log_file);
fflush(this->pcap_text_log_file);
}
+6 -6
View File
@@ -4,10 +4,10 @@
using namespace std;
IPV4RangeSet::IPV4RangeSet(const JSON& json) {
IPV4RangeSet::IPV4RangeSet(const phosg::JSON& json) {
for (const auto& it : json.as_list()) {
// String should be of the form a.b.c.d or a.b.c.d/e
auto tokens = split(it->as_string(), '/');
auto tokens = phosg::split(it->as_string(), '/');
size_t mask_bits;
if (tokens.size() == 1) {
@@ -21,7 +21,7 @@ IPV4RangeSet::IPV4RangeSet(const JSON& json) {
throw runtime_error("invalid IPv4 address range");
}
auto addr_tokens = split(tokens[0], '.');
auto addr_tokens = phosg::split(tokens[0], '.');
if (addr_tokens.size() != 4) {
throw runtime_error("invalid IPv4 address");
}
@@ -40,12 +40,12 @@ IPV4RangeSet::IPV4RangeSet(const JSON& json) {
}
}
JSON IPV4RangeSet::json() const {
auto ret = JSON::list();
phosg::JSON IPV4RangeSet::json() const {
auto ret = phosg::JSON::list();
for (const auto& it : this->ranges) {
uint32_t addr = it.first;
uint8_t mask_bits = it.second;
ret.emplace_back(string_printf("%hhu.%hhu.%hhu.%hhu/%hhu",
ret.emplace_back(phosg::string_printf("%hhu.%hhu.%hhu.%hhu/%hhu",
static_cast<uint8_t>((addr >> 24) & 0xFF),
static_cast<uint8_t>((addr >> 16) & 0xFF),
static_cast<uint8_t>((addr >> 8) & 0xFF),
+2 -2
View File
@@ -6,9 +6,9 @@
class IPV4RangeSet {
public:
IPV4RangeSet() = default;
explicit IPV4RangeSet(const JSON& json);
explicit IPV4RangeSet(const phosg::JSON& json);
JSON json() const;
phosg::JSON json() const;
bool check(uint32_t addr) const;
bool check(const struct sockaddr_storage& ss) const;
+12 -12
View File
@@ -184,7 +184,7 @@ int64_t IntegralExpression::FlagLookupNode::evaluate(const Env& env) const {
}
string IntegralExpression::FlagLookupNode::str() const {
return string_printf("F_%04hX", this->flag_index);
return phosg::string_printf("F_%04hX", this->flag_index);
}
IntegralExpression::ChallengeCompletionLookupNode::ChallengeCompletionLookupNode(
@@ -214,7 +214,7 @@ int64_t IntegralExpression::ChallengeCompletionLookupNode::evaluate(const Env& e
}
string IntegralExpression::ChallengeCompletionLookupNode::str() const {
return string_printf("CC_%s_%hhu", abbreviation_for_episode(this->episode), static_cast<uint8_t>(this->stage_index + 1));
return phosg::string_printf("CC_%s_%hhu", abbreviation_for_episode(this->episode), static_cast<uint8_t>(this->stage_index + 1));
}
IntegralExpression::TeamRewardLookupNode::TeamRewardLookupNode(const string& reward_name)
@@ -296,7 +296,7 @@ int64_t IntegralExpression::ConstantNode::evaluate(const Env&) const {
}
string IntegralExpression::ConstantNode::str() const {
return string_printf("%" PRId64, this->value);
return phosg::string_printf("%" PRId64, this->value);
}
unique_ptr<const IntegralExpression::Node> IntegralExpression::parse_expr(string_view text) {
@@ -340,16 +340,16 @@ unique_ptr<const IntegralExpression::Node> IntegralExpression::parse_expr(string
// Check for binary operators at the root level
using BinType = BinaryOperatorNode::Type;
static const vector<vector<pair<std::string, BinaryOperatorNode::Type>>> binary_operator_levels = {
{{make_pair("*", BinType::MULTIPLY)}, {make_pair("/", BinType::DIVIDE)}, {make_pair("%", BinType::MODULUS)}},
{{make_pair("+", BinType::ADD)}, {make_pair("-", BinType::SUBTRACT)}},
{{make_pair("<<", BinType::LEFT_SHIFT)}, {make_pair(">>", BinType::RIGHT_SHIFT)}},
{{make_pair("<=", BinType::LESS_OR_EQUAL)}, {make_pair(">=", BinType::GREATER_OR_EQUAL)}, {make_pair("<", BinType::LESS_THAN)}, {make_pair(">", BinType::GREATER_THAN)}},
{{make_pair("==", BinType::EQUAL)}, {make_pair("!=", BinType::NOT_EQUAL)}},
{{make_pair("&", BinType::BITWISE_AND)}},
{{make_pair("^", BinType::BITWISE_XOR)}},
{{make_pair("|", BinType::BITWISE_OR)}},
{{make_pair("&&", BinType::LOGICAL_AND)}},
{{make_pair("||", BinType::LOGICAL_OR)}},
{{make_pair("&&", BinType::LOGICAL_AND)}},
{{make_pair("|", BinType::BITWISE_OR)}},
{{make_pair("^", BinType::BITWISE_XOR)}},
{{make_pair("&", BinType::BITWISE_AND)}},
{{make_pair("==", BinType::EQUAL)}, {make_pair("!=", BinType::NOT_EQUAL)}},
{{make_pair("<=", BinType::LESS_OR_EQUAL)}, {make_pair(">=", BinType::GREATER_OR_EQUAL)}, {make_pair("<", BinType::LESS_THAN)}, {make_pair(">", BinType::GREATER_THAN)}},
{{make_pair("<<", BinType::LEFT_SHIFT)}, {make_pair(">>", BinType::RIGHT_SHIFT)}},
{{make_pair("+", BinType::ADD)}, {make_pair("-", BinType::SUBTRACT)}},
{{make_pair("*", BinType::MULTIPLY)}, {make_pair("/", BinType::DIVIDE)}, {make_pair("%", BinType::MODULUS)}},
};
for (const auto& operators : binary_operator_levels) {
size_t paren_level = 0;
+25 -22
View File
@@ -36,7 +36,7 @@ ItemCreator::ItemCreator(
uint8_t section_id,
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt,
shared_ptr<const BattleRules> restrictions)
: log(string_printf("[ItemCreator:%s/%s/%s/%c/%hhu] ", name_for_enum(stack_limits->version), abbreviation_for_episode(episode), abbreviation_for_mode(mode), abbreviation_for_difficulty(difficulty), section_id), lobby_log.min_level),
: log(phosg::string_printf("[ItemCreator:%s/%s/%s/%c/%hhu] ", phosg::name_for_enum(stack_limits->version), abbreviation_for_episode(episode), abbreviation_for_mode(mode), abbreviation_for_difficulty(difficulty), section_id), lobby_log.min_level),
logic_version(stack_limits->version),
stack_limits(stack_limits),
episode(episode),
@@ -63,8 +63,8 @@ void ItemCreator::set_random_crypt(shared_ptr<PSOLFGEncryption> new_random_crypt
void ItemCreator::set_section_id(uint8_t new_section_id) {
if (this->section_id != new_section_id) {
this->section_id = new_section_id;
this->log.prefix = string_printf("[ItemCreator:%s/%s/%s/%c/%hhu] ",
name_for_enum(stack_limits->version),
this->log.prefix = phosg::string_printf("[ItemCreator:%s/%s/%s/%c/%hhu] ",
phosg::name_for_enum(stack_limits->version),
abbreviation_for_episode(episode),
abbreviation_for_mode(mode),
abbreviation_for_difficulty(difficulty),
@@ -85,27 +85,33 @@ bool ItemCreator::are_rare_drops_allowed() const {
}
uint8_t ItemCreator::normalize_area_number(uint8_t area) const {
if (!this->restrictions || (this->restrictions->box_drop_area == 0) || (area < 0x10) || (area > 0x11)) {
if (this->restrictions && (this->restrictions->box_drop_area != 0)) {
return this->restrictions->box_drop_area - 1;
} else {
switch (this->episode) {
case Episode::EP1:
if (area >= 0x0F) {
if (area >= 0x11) {
throw runtime_error("invalid Episode 1 area number");
}
switch (area) {
case 11:
return 2; // Dragon -> Cave 1
case 12:
return 5; // De Rol Le -> Mine 1
case 13:
return 7; // Vol Opt -> Ruins 1
case 14:
return 9; // Dark Falz -> Ruins 3
case 0x0B: // Dragon -> Cave 1
return 2;
case 0x0C: // De Rol Le -> Mine 1
return 5;
case 0x0D: // Vol Opt -> Ruins 1
return 7;
case 0x0E: // Dark Falz -> Ruins 3
case 0x10: // Palace -> Ruins 3
case 0x11: // Spaceship -> Ruins 3
return 9;
case 0x0F: // Lobby
throw runtime_error("visual lobby does not have item drop tables");
default:
return area - 1;
}
throw logic_error("this should be impossible");
case Episode::EP2: {
static const vector<uint8_t> area_subs = {
static const array<uint8_t, 0x11> area_subs = {
0x00, // 13 (VR Temple Alpha)
0x01, // 14 (VR Temple Beta)
0x02, // 15 (VR Spaceship Alpha)
@@ -127,7 +133,7 @@ uint8_t ItemCreator::normalize_area_number(uint8_t area) const {
if ((area >= 0x13) && (area < 0x24)) {
return area_subs.at(area - 0x13);
}
return area - 1;
throw runtime_error("invalid Episode 2 area number");
}
case Episode::EP4:
if (area >= 0x24 && area < 0x2D) {
@@ -137,9 +143,6 @@ uint8_t ItemCreator::normalize_area_number(uint8_t area) const {
default:
throw logic_error("invalid episode number");
}
} else {
return this->restrictions->box_drop_area - 1;
}
}
@@ -298,13 +301,13 @@ ItemData ItemCreator::check_rare_specs_and_create_rare_box_item(uint8_t area_nor
for (const auto& spec : rare_specs) {
item = this->check_rate_and_create_rare_item(spec, area_norm);
if (!item.empty()) {
if (this->log.should_log(LogLevel::INFO)) {
if (this->log.should_log(phosg::LogLevel::INFO)) {
auto hex = spec.data.hex();
this->log.info("Box spec %08" PRIX32 " produced item %s", spec.probability, hex.c_str());
}
break;
}
if (this->log.should_log(LogLevel::INFO)) {
if (this->log.should_log(phosg::LogLevel::INFO)) {
auto hex = spec.data.hex();
this->log.info("Box spec %08" PRIX32 " did not produce item %s", spec.probability, hex.c_str());
}
@@ -359,13 +362,13 @@ ItemData ItemCreator::check_rare_spec_and_create_rare_enemy_item(uint32_t enemy_
for (const auto& spec : rare_specs) {
item = this->check_rate_and_create_rare_item(spec, area_norm);
if (!item.empty()) {
if (this->log.should_log(LogLevel::INFO)) {
if (this->log.should_log(phosg::LogLevel::INFO)) {
auto hex = spec.data.hex();
this->log.info("Enemy spec %08" PRIX32 " produced item %s", spec.probability, hex.c_str());
}
break;
}
if (this->log.should_log(LogLevel::INFO)) {
if (this->log.should_log(phosg::LogLevel::INFO)) {
auto hex = spec.data.hex();
this->log.info("Enemy spec %08" PRIX32 " did not produce item %s", spec.probability, hex.c_str());
}
+1 -1
View File
@@ -58,7 +58,7 @@ public:
void set_section_id(uint8_t new_section_id);
private:
PrefixedLogger log;
phosg::PrefixedLogger log;
Version logic_version;
std::shared_ptr<const ItemData::StackLimits> stack_limits;
Episode episode;
+7 -7
View File
@@ -21,7 +21,7 @@ ItemData::StackLimits::StackLimits(
max_tool_stack_sizes_by_data1_1(max_tool_stack_sizes_by_data1_1),
max_meseta_stack_size(max_meseta_stack_size) {}
ItemData::StackLimits::StackLimits(Version version, const JSON& json)
ItemData::StackLimits::StackLimits(Version version, const phosg::JSON& json)
: version(version) {
this->max_tool_stack_sizes_by_data1_1.clear();
for (const auto& limit_json : json.at("ToolLimits").as_list()) {
@@ -53,8 +53,8 @@ ItemData::ItemData(const ItemData& other) {
ItemData::ItemData(uint64_t first, uint64_t second) {
*reinterpret_cast<be_uint64_t*>(&this->data1[0]) = first;
this->data1d[2] = bswap32((second >> 32) & 0xFFFFFFFF);
this->data2d = bswap32(second & 0xFFFFFFFF);
this->data1d[2] = phosg::bswap32((second >> 32) & 0xFFFFFFFF);
this->data2d = phosg::bswap32(second & 0xFFFFFFFF);
}
ItemData& ItemData::operator=(const ItemData& other) {
@@ -400,7 +400,7 @@ void ItemData::decode_for_version(Version from_version) {
// data2d field, since internally it's actually a uint32_t. We treat it
// as individual bytes instead, so we correct for the client's
// byteswapping here.
this->data2d = bswap32(this->data2d);
this->data2d = phosg::bswap32(this->data2d);
}
break;
@@ -482,7 +482,7 @@ void ItemData::encode_for_version(Version to_version, shared_ptr<const ItemParam
this->data2w[1] = this->data2[0] | ((this->data2[2] << 15) & 0x8000);
this->data2w[0] = this->data2[1];
} else if (is_big_endian(to_version)) {
this->data2d = bswap32(this->data2d);
this->data2d = phosg::bswap32(this->data2d);
}
break;
@@ -743,12 +743,12 @@ ItemData ItemData::from_primary_identifier(const StackLimits& limits, uint32_t p
}
string ItemData::hex() const {
return string_printf("%08" PRIX32 " %08" PRIX32 " %08" PRIX32 " (%08" PRIX32 ") %08" PRIX32,
return phosg::string_printf("%08" PRIX32 " %08" PRIX32 " %08" PRIX32 " (%08" PRIX32 ") %08" PRIX32,
this->data1db[0].load(), this->data1db[1].load(), this->data1db[2].load(), this->id.load(), this->data2db.load());
}
string ItemData::short_hex() const {
auto ret = string_printf("%08" PRIX32 "%08" PRIX32 "%08" PRIX32 "%08" PRIX32,
auto ret = phosg::string_printf("%08" PRIX32 "%08" PRIX32 "%08" PRIX32 "%08" PRIX32,
this->data1db[0].load(), this->data1db[1].load(), this->data1db[2].load(), this->data2db.load());
size_t offset = ret.find_last_not_of('0');
if (offset != string::npos) {
+1 -2
View File
@@ -62,7 +62,7 @@ struct ItemData {
uint32_t max_meseta_stack_size;
StackLimits(Version version, const std::vector<uint8_t>& max_tool_stack_sizes_by_data1_1, uint32_t max_meseta_stack_size);
StackLimits(Version version, const JSON& json);
StackLimits(Version version, const phosg::JSON& json);
StackLimits(const StackLimits& other) = default;
StackLimits(StackLimits&& other) = default;
StackLimits& operator=(const StackLimits& other) = default;
@@ -84,7 +84,6 @@ struct ItemData {
// Mag: 02ZZLLWW HHHHIIII JJJJKKKK YYQQPPVV
// Tool: 03ZZZZFF 00CC0000 00000000 00000000
// Meseta: 04000000 00000000 00000000 MMMMMMMM
// 01034D00 00000000 204E0000
// A = attribute type (for S-ranks, custom name)
// B = attribute amount (for S-ranks, custom name)
// C = stack size (for tools)
+76 -70
View File
@@ -24,7 +24,7 @@ ItemNameIndex::ItemNameIndex(
meta->primary_identifier = primary_identifier;
meta->name = *name;
this->primary_identifier_index.emplace(meta->primary_identifier, meta);
this->name_index.emplace(tolower(meta->name), meta);
this->name_index.emplace(phosg::tolower(meta->name), meta);
}
}
}
@@ -99,7 +99,7 @@ const array<const char*, 0x11> name_for_s_rank_special = {
std::string ItemNameIndex::describe_item(const ItemData& item, bool include_color_escapes) const {
if (item.data1[0] == 0x04) {
return string_printf("%s%" PRIu32 " Meseta", include_color_escapes ? "$C7" : "", item.data2d.load());
return phosg::string_printf("%s%" PRIu32 " Meseta", include_color_escapes ? "$C7" : "", item.data2d.load());
}
vector<string> ret_tokens;
@@ -120,7 +120,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo
try {
ret_tokens.emplace_back(name_for_weapon_special.at(special_id));
} catch (const out_of_range&) {
ret_tokens.emplace_back(string_printf("!SP:%02hhX", special_id));
ret_tokens.emplace_back(phosg::string_printf("!SP:%02hhX", special_id));
}
}
}
@@ -128,7 +128,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo
try {
ret_tokens.emplace_back(name_for_s_rank_special.at(item.data1[2]));
} catch (const out_of_range&) {
ret_tokens.emplace_back(string_printf("!SSP:%02hhX", item.data1[2]));
ret_tokens.emplace_back(phosg::string_printf("!SSP:%02hhX", item.data1[2]));
}
}
@@ -149,34 +149,34 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo
technique_name = tech_id_to_name.at(item.data1[4]);
technique_name[0] = toupper(technique_name[0]);
} catch (const out_of_range&) {
technique_name = string_printf("!TD:%02hhX", item.data1[4]);
technique_name = phosg::string_printf("!TD:%02hhX", item.data1[4]);
}
// Hide the level for Reverser and Ryuker, unless the level isn't 1
if ((item.data1[2] == 0) && ((item.data1[4] == 0x0E) || (item.data1[4] == 0x11))) {
ret_tokens.emplace_back(string_printf("Disk:%s", technique_name.c_str()));
ret_tokens.emplace_back(phosg::string_printf("Disk:%s", technique_name.c_str()));
} else {
ret_tokens.emplace_back(string_printf("Disk:%s Lv.%d", technique_name.c_str(), item.data1[2] + 1));
ret_tokens.emplace_back(phosg::string_printf("Disk:%s Lv.%d", technique_name.c_str(), item.data1[2] + 1));
}
} else {
try {
auto meta = this->primary_identifier_index.at(primary_identifier);
ret_tokens.emplace_back(meta->name);
} catch (const out_of_range&) {
ret_tokens.emplace_back(string_printf("!ID:%08" PRIX32, primary_identifier));
ret_tokens.emplace_back(phosg::string_printf("!ID:%08" PRIX32, primary_identifier));
}
}
// For weapons, add the grind and bonuses, or S-rank name if applicable
if (item.data1[0] == 0x00) {
if (item.data1[3] > 0) {
ret_tokens.emplace_back(string_printf("+%hhu", item.data1[3]));
ret_tokens.emplace_back(phosg::string_printf("+%hhu", item.data1[3]));
}
if (item.is_s_rank_weapon()) {
// S-rank (has name instead of percent bonuses)
uint16_t be_data1w3 = bswap16(item.data1w[3]);
uint16_t be_data1w4 = bswap16(item.data1w[4]);
uint16_t be_data1w5 = bswap16(item.data1w[5]);
uint16_t be_data1w3 = phosg::bswap16(item.data1w[3]);
uint16_t be_data1w4 = phosg::bswap16(item.data1w[4]);
uint16_t be_data1w5 = phosg::bswap16(item.data1w[5]);
uint8_t char_indexes[8] = {
static_cast<uint8_t>((be_data1w3 >> 5) & 0x1F),
static_cast<uint8_t>(be_data1w3 & 0x1F),
@@ -210,9 +210,9 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo
}
if (which & 0x80) {
uint16_t kill_count = ((which << 8) & 0x7F00) | (value & 0xFF);
ret_tokens.emplace_back(string_printf("K:%hu", kill_count));
ret_tokens.emplace_back(phosg::string_printf("K:%hu", kill_count));
} else if (which > 5) {
ret_tokens.emplace_back(string_printf("!PC:%02hhX%02hhX", which, value));
ret_tokens.emplace_back(phosg::string_printf("!PC:%02hhX%02hhX", which, value));
} else {
bonuses[which - 1] = value;
}
@@ -222,11 +222,11 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo
bool should_highlight_hit = include_color_escapes && (bonuses[4] > 0);
const char* color_prefix = include_color_escapes ? "$C7" : "";
if (should_include_hit) {
ret_tokens.emplace_back(string_printf("%s%hhd/%hhd/%hhd/%hhd/%s%hhd",
ret_tokens.emplace_back(phosg::string_printf("%s%hhd/%hhd/%hhd/%hhd/%s%hhd",
color_prefix, bonuses[0], bonuses[1], bonuses[2], bonuses[3],
(should_highlight_hit ? "$C6" : ""), bonuses[4]));
} else {
ret_tokens.emplace_back(string_printf("%s%hhd/%hhd/%hhd/%hhd",
ret_tokens.emplace_back(phosg::string_printf("%s%hhd/%hhd/%hhd/%hhd",
color_prefix, bonuses[0], bonuses[1], bonuses[2], bonuses[3]));
}
}
@@ -245,7 +245,7 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo
} else if (modifier <= -3) {
ret_tokens.back().append("--");
} else if (modifier != 0) {
ret_tokens.emplace_back(string_printf("!MD:%04hX", modifier));
ret_tokens.emplace_back(phosg::string_printf("!MD:%04hX", modifier));
}
} else { // Armor/shields
@@ -253,22 +253,22 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo
if (item.data1[5] == 1) {
ret_tokens.emplace_back("(1 slot)");
} else {
ret_tokens.emplace_back(string_printf("(%hhu slots)", item.data1[5]));
ret_tokens.emplace_back(phosg::string_printf("(%hhu slots)", item.data1[5]));
}
}
if (item.data1w[3] != 0) {
ret_tokens.emplace_back(string_printf("+%hdDEF",
ret_tokens.emplace_back(phosg::string_printf("+%hdDEF",
static_cast<int16_t>(item.data1w[3].load())));
}
if (item.data1w[4] != 0) {
ret_tokens.emplace_back(string_printf("+%hdEVP",
ret_tokens.emplace_back(phosg::string_printf("+%hdEVP",
static_cast<int16_t>(item.data1w[4].load())));
}
}
// For mags, add tons of info
} else if (item.data1[0] == 0x02) {
ret_tokens.emplace_back(string_printf("LV%hhu", item.data1[2]));
ret_tokens.emplace_back(phosg::string_printf("LV%hhu", item.data1[2]));
uint16_t def = item.data1w[2];
uint16_t pow = item.data1w[3];
@@ -278,16 +278,16 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo
uint16_t level = stat / 100;
uint8_t partial = stat % 100;
if (partial == 0) {
return string_printf("%hu", level);
return phosg::string_printf("%hu", level);
} else if (partial % 10 == 0) {
return string_printf("%hu.%hhu", level, static_cast<uint8_t>(partial / 10));
return phosg::string_printf("%hu.%hhu", level, static_cast<uint8_t>(partial / 10));
} else {
return string_printf("%hu.%02hhu", level, partial);
return phosg::string_printf("%hu.%02hhu", level, partial);
}
};
ret_tokens.emplace_back(format_stat(def) + "/" + format_stat(pow) + "/" + format_stat(dex) + "/" + format_stat(mind));
ret_tokens.emplace_back(string_printf("%hhu%%", item.data2[0]));
ret_tokens.emplace_back(string_printf("%hhuIQ", item.data2[1]));
ret_tokens.emplace_back(phosg::string_printf("%hhu%%", item.data2[0]));
ret_tokens.emplace_back(phosg::string_printf("%hhuIQ", item.data2[1]));
uint8_t flags = item.data2[2];
if (flags & 7) {
@@ -322,19 +322,19 @@ std::string ItemNameIndex::describe_item(const ItemData& item, bool include_colo
}
try {
ret_tokens.emplace_back(string_printf("(%s)", name_for_mag_color.at(item.data2[3])));
ret_tokens.emplace_back(phosg::string_printf("(%s)", name_for_mag_color.at(item.data2[3])));
} catch (const out_of_range&) {
ret_tokens.emplace_back(string_printf("(!CL:%02hhX)", item.data2[3]));
ret_tokens.emplace_back(phosg::string_printf("(!CL:%02hhX)", item.data2[3]));
}
// For tools, add the amount (if applicable)
} else if (item.data1[0] == 0x03) {
if (item.max_stack_size(*this->limits) > 1) {
ret_tokens.emplace_back(string_printf("x%hhu", item.data1[5]));
ret_tokens.emplace_back(phosg::string_printf("x%hhu", item.data1[5]));
}
}
string ret = join(ret_tokens, " ");
string ret = phosg::join(ret_tokens, " ");
if (include_color_escapes) {
if (is_unidentified) {
return "$C3" + ret;
@@ -361,13 +361,13 @@ ItemData ItemNameIndex::parse_item_description(const std::string& desc) const {
ret = this->parse_item_description_phase(desc, true);
} catch (const exception& e2) {
try {
ret = ItemData::from_data(parse_data_string(desc));
ret = ItemData::from_data(phosg::parse_data_string(desc));
} catch (const exception& ed) {
if (strcmp(e1.what(), e2.what())) {
throw runtime_error(string_printf("cannot parse item description \"%s\" (as text 1: %s) (as text 2: %s) (as data: %s)",
throw runtime_error(phosg::string_printf("cannot parse item description \"%s\" (as text 1: %s) (as text 2: %s) (as data: %s)",
desc.c_str(), e1.what(), e2.what(), ed.what()));
} else {
throw runtime_error(string_printf("cannot parse item description \"%s\" (as text: %s) (as data: %s)",
throw runtime_error(phosg::string_printf("cannot parse item description \"%s\" (as text: %s) (as data: %s)",
desc.c_str(), e1.what(), ed.what()));
}
}
@@ -383,15 +383,15 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
ret.id = 0xFFFFFFFF;
ret.data2d = 0;
string desc = tolower(description);
if (ends_with(desc, " meseta")) {
string desc = phosg::tolower(description);
if (phosg::ends_with(desc, " meseta")) {
ret.data1[0] = 0x04;
ret.data2d = stol(desc, nullptr, 10);
return ret;
}
if (starts_with(desc, "disk:")) {
auto tokens = split(desc, ' ');
if (phosg::starts_with(desc, "disk:")) {
auto tokens = phosg::split(desc, ' ');
tokens[0] = tokens[0].substr(5); // Trim off "disk:"
if ((tokens[0] == "reverser") || (tokens[0] == "ryuker")) {
uint8_t tech = technique_for_name(tokens[0]);
@@ -403,7 +403,7 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
if (tokens.size() != 2) {
throw runtime_error("invalid tech disk format");
}
if (!starts_with(tokens[1], "lv.")) {
if (!phosg::starts_with(tokens[1], "lv.")) {
throw runtime_error("invalid tech disk level");
}
uint8_t tech = technique_for_name(tokens[0]);
@@ -416,11 +416,11 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
return ret;
}
bool is_wrapped = starts_with(desc, "wrapped ");
bool is_wrapped = phosg::starts_with(desc, "wrapped ");
if (is_wrapped) {
desc = desc.substr(8);
}
bool is_unidentified = starts_with(desc, "?");
bool is_unidentified = phosg::starts_with(desc, "?");
if (is_unidentified) {
size_t z;
for (z = 1; z < desc.size(); z++) {
@@ -438,9 +438,9 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
if (!name_for_weapon_special[z]) {
continue;
}
string prefix = tolower(name_for_weapon_special[z]);
string prefix = phosg::tolower(name_for_weapon_special[z]);
prefix += ' ';
if (starts_with(desc, prefix)) {
if (phosg::starts_with(desc, prefix)) {
weapon_special = z;
desc = desc.substr(prefix.size());
break;
@@ -454,7 +454,7 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
// then we'll see Sange & Yasha first, which we should skip.
size_t lookback = 0;
while (lookback < 4) {
if (name_it != this->name_index.end() && desc.starts_with(name_it->first)) {
if (name_it != this->name_index.end() && phosg::starts_with(desc, name_it->first)) {
break;
} else if (name_it == this->name_index.begin()) {
throw runtime_error("no such item");
@@ -468,7 +468,7 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
}
desc = desc.substr(name_it->first.size());
if (starts_with(desc, " ")) {
if (phosg::starts_with(desc, " ")) {
desc = desc.substr(1);
}
@@ -480,15 +480,16 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
ret.data1[2] = (primary_identifier >> 8) & 0xFF;
if (ret.data1[0] == 0x00) {
// Weapons: add special, grind and percentages (or name, if S-rank)
// Weapons: add special, grind and percentages (or name, if S-rank) and
// kill count if unsealable
ret.data1[4] = weapon_special | (is_wrapped ? 0x40 : 0x00) | (is_unidentified ? 0x80 : 0x00);
auto tokens = split(desc, ' ');
auto tokens = phosg::split(desc, ' ');
for (auto& token : tokens) {
if (token.empty()) {
continue;
}
if (starts_with(token, "+")) {
if (phosg::starts_with(token, "+")) {
token = token.substr(1);
ret.data1[3] = stoul(token, nullptr, 10);
@@ -502,27 +503,28 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
char ch = toupper(token[z]);
const char* pos = strchr(s_rank_name_characters, ch);
if (!pos) {
throw runtime_error(string_printf("s-rank name contains invalid character %02hhX (%c)", ch, ch));
throw runtime_error(phosg::string_printf("s-rank name contains invalid character %02hhX (%c)", ch, ch));
}
char_indexes[z] = (pos - s_rank_name_characters);
}
ret.data1w[3] = bswap16(0x8000 | (char_indexes[1] & 0x1F) | ((char_indexes[0] & 0x1F) << 5));
ret.data1w[4] = bswap16(0x8000 | (char_indexes[4] & 0x1F) | ((char_indexes[3] & 0x1F) << 5) | ((char_indexes[2] & 0x1F) << 10));
ret.data1w[5] = bswap16(0x8000 | (char_indexes[7] & 0x1F) | ((char_indexes[6] & 0x1F) << 5) | ((char_indexes[5] & 0x1F) << 10));
ret.data1w[3] = phosg::bswap16(0x8000 | (char_indexes[1] & 0x1F) | ((char_indexes[0] & 0x1F) << 5));
ret.data1w[4] = phosg::bswap16(0x8000 | (char_indexes[4] & 0x1F) | ((char_indexes[3] & 0x1F) << 5) | ((char_indexes[2] & 0x1F) << 10));
ret.data1w[5] = phosg::bswap16(0x8000 | (char_indexes[7] & 0x1F) | ((char_indexes[6] & 0x1F) << 5) | ((char_indexes[5] & 0x1F) << 10));
} else {
auto p_tokens = split(token, '/');
auto p_tokens = phosg::split(token, '/');
if (p_tokens.size() > 5) {
throw runtime_error("invalid bonuses token");
}
uint8_t max_bonuses = this->item_parameter_table->is_unsealable_item(ret) ? 2 : 3;
uint8_t bonus_index = 0;
for (size_t z = 0; z < p_tokens.size(); z++) {
int8_t bonus_value = stol(p_tokens[z], nullptr, 10);
if (bonus_value == 0) {
continue;
}
if (bonus_index >= 3) {
if (bonus_index >= max_bonuses) {
throw runtime_error("weapon has too many bonuses");
}
ret.data1[6 + (2 * bonus_index)] = z + 1;
@@ -532,6 +534,10 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
}
}
if (this->item_parameter_table->is_unsealable_item(ret)) {
ret.set_kill_count(0);
}
} else if (ret.data1[0] == 0x01) {
if (ret.data1[1] == 0x03) { // Unit
static const unordered_map<string, uint16_t> modifiers({
@@ -544,15 +550,15 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
ret.data1w[3] = modifiers.at(desc);
} else { // Armor/shield
for (const auto& token : split(desc, ' ')) {
for (const auto& token : phosg::split(desc, ' ')) {
if (token.empty()) {
continue;
} else if (!starts_with(token, "+")) {
} else if (!phosg::starts_with(token, "+")) {
throw runtime_error("invalid armor/shield modifier");
}
if (ends_with(token, "def")) {
if (phosg::ends_with(token, "def")) {
ret.data1w[3] = static_cast<uint16_t>(stol(token.substr(1, token.size() - 4), nullptr, 10));
} else if (ends_with(token, "evp")) {
} else if (phosg::ends_with(token, "evp")) {
ret.data1w[4] = static_cast<uint16_t>(stol(token.substr(1, token.size() - 4), nullptr, 10));
} else {
ret.data1[5] = stoul(token.substr(1), nullptr, 10);
@@ -565,11 +571,11 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
}
} else if (ret.data1[0] == 0x02) {
for (const auto& token : split(desc, ' ')) {
for (const auto& token : phosg::split(desc, ' ')) {
if (token.empty()) {
continue;
} else if (starts_with(token, "pb:")) { // Photon blasts
auto pb_tokens = split(token.substr(3), ',');
} else if (phosg::starts_with(token, "pb:")) { // Photon blasts
auto pb_tokens = phosg::split(token.substr(3), ',');
if (pb_tokens.size() > 3) {
throw runtime_error("too many photon blasts specified");
}
@@ -586,17 +592,17 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
for (const auto& pb_token : pb_tokens) {
ret.add_mag_photon_blast(name_to_pb_num.at(pb_token));
}
} else if (ends_with(token, "%")) { // Synchro
} else if (phosg::ends_with(token, "%")) { // Synchro
ret.data2[0] = stoul(token.substr(0, token.size() - 1), nullptr, 10);
} else if (ends_with(token, "iq")) { // IQ
} else if (phosg::ends_with(token, "iq")) { // IQ
ret.data2[1] = stoul(token.substr(0, token.size() - 2), nullptr, 10);
} else if (!token.empty() && isdigit(token[0])) { // Stats
auto s_tokens = split(token, '/');
auto s_tokens = phosg::split(token, '/');
if (s_tokens.size() != 4) {
throw runtime_error("incorrect stat count");
}
for (size_t z = 0; z < 4; z++) {
auto n_tokens = split(s_tokens[z], '.');
auto n_tokens = phosg::split(s_tokens[z], '.');
if (n_tokens.size() == 0 || n_tokens.size() > 2) {
throw logic_error("incorrect stats argument format");
} else if ((n_tokens.size() == 1) || (n_tokens[1].size() == 0)) {
@@ -620,7 +626,7 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
}
} else if (ret.data1[0] == 0x03) {
if (ret.max_stack_size(*this->limits) > 1) {
if (starts_with(desc, "x")) {
if (phosg::starts_with(desc, "x")) {
ret.data1[5] = stoul(desc.substr(1), nullptr, 10);
} else {
ret.data1[5] = 1;
@@ -650,7 +656,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
for (size_t data1_1 = 0; data1_1 < pmt->num_weapon_classes; data1_1++) {
uint8_t v1_replacement = pmt->get_weapon_v1_replacement(data1_1);
float sale_divisor = pmt->get_sale_divisor(0x00, data1_1);
string divisor_str = string_printf("%g", sale_divisor);
string divisor_str = phosg::string_printf("%g", sale_divisor);
divisor_str.resize(13, ' ');
size_t data1_2_limit = pmt->num_weapons_in_class(data1_1);
@@ -708,7 +714,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
fprintf(stream, "ARMOR => ---ID--- TYPE SKIN POINTS -DFP- -EVP- BP BE FLAG LVL EFR ETH EIC EDK ELT DFR EVR SB TB -A2- ST* ---DIVISOR--- NAME\n");
for (size_t data1_1 = 1; data1_1 < 3; data1_1++) {
float sale_divisor = pmt->get_sale_divisor(0x01, data1_1);
string divisor_str = string_printf("%g", sale_divisor);
string divisor_str = phosg::string_printf("%g", sale_divisor);
divisor_str.resize(13, ' ');
size_t data1_2_limit = pmt->num_armors_or_shields_in_class(data1_1);
@@ -754,7 +760,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
fprintf(stream, "UNIT => ---ID--- TYPE SKIN POINTS STAT COUNT ST-MOD ST* ---DIVISOR--- NAME\n");
{
float sale_divisor = pmt->get_sale_divisor(0x01, 0x03);
string divisor_str = string_printf("%g", sale_divisor);
string divisor_str = phosg::string_printf("%g", sale_divisor);
divisor_str.resize(13, ' ');
size_t data1_2_limit = pmt->num_units();
@@ -791,7 +797,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
uint8_t stars = pmt->get_item_stars(m.base.id);
float sale_divisor = pmt->get_sale_divisor(0x02, data1_1);
string divisor_str = string_printf("%g", sale_divisor);
string divisor_str = phosg::string_printf("%g", sale_divisor);
divisor_str.resize(13, ' ');
ItemData item;
@@ -827,7 +833,7 @@ void ItemNameIndex::print_table(FILE* stream) const {
fprintf(stream, "TOOL => ---ID--- TYPE SKIN POINTS COUNT TECH -COST- ITEMFLAG ST* ---DIVISOR--- NAME\n");
for (size_t data1_1 = 0; data1_1 < pmt->num_tool_classes; data1_1++) {
float sale_divisor = pmt->get_sale_divisor(0x03, data1_1);
string divisor_str = string_printf("%g", sale_divisor);
string divisor_str = phosg::string_printf("%g", sale_divisor);
divisor_str.resize(13, ' ');
size_t data1_2_limit = pmt->num_tools_in_class(data1_1);
+29 -31
View File
@@ -206,8 +206,8 @@ ItemParameterTable::WeaponV4 ItemParameterTable::WeaponGCNTE::to_v4() const {
return ret;
}
template <bool IsBigEndian>
ItemParameterTable::WeaponV4 ItemParameterTable::WeaponV3T<IsBigEndian>::to_v4() const {
template <bool BE>
ItemParameterTable::WeaponV4 ItemParameterTable::WeaponV3T<BE>::to_v4() const {
WeaponV4 ret;
ret.base.id = this->base.id.load();
ret.base.type = this->base.type.load();
@@ -281,8 +281,8 @@ ItemParameterTable::ArmorOrShieldV4 ItemParameterTable::ArmorOrShieldV1V2::to_v4
return ret;
}
template <bool IsBigEndian>
ItemParameterTable::ArmorOrShieldV4 ItemParameterTable::ArmorOrShieldV3T<IsBigEndian>::to_v4() const {
template <bool BE>
ItemParameterTable::ArmorOrShieldV4 ItemParameterTable::ArmorOrShieldV3T<BE>::to_v4() const {
ArmorOrShieldV4 ret;
ret.base.id = this->base.id.load();
ret.base.type = this->base.type.load();
@@ -323,8 +323,8 @@ ItemParameterTable::UnitV4 ItemParameterTable::UnitV1V2::to_v4() const {
return ret;
}
template <bool IsBigEndian>
ItemParameterTable::UnitV4 ItemParameterTable::UnitV3T<IsBigEndian>::to_v4() const {
template <bool BE>
ItemParameterTable::UnitV4 ItemParameterTable::UnitV3T<BE>::to_v4() const {
UnitV4 ret;
ret.base.id = this->base.id.load();
ret.base.type = this->base.type.load();
@@ -370,8 +370,8 @@ ItemParameterTable::MagV4 ItemParameterTable::MagV2::to_v4() const {
return ret;
}
template <bool IsBigEndian>
ItemParameterTable::MagV4 ItemParameterTable::MagV3T<IsBigEndian>::to_v4() const {
template <bool BE>
ItemParameterTable::MagV4 ItemParameterTable::MagV3T<BE>::to_v4() const {
MagV4 ret;
ret.base.id = this->base.id.load();
ret.base.type = this->base.type.load();
@@ -401,8 +401,8 @@ ItemParameterTable::ToolV4 ItemParameterTable::ToolV1V2::to_v4() const {
return ret;
}
template <bool IsBigEndian>
ItemParameterTable::ToolV4 ItemParameterTable::ToolV3T<IsBigEndian>::to_v4() const {
template <bool BE>
ItemParameterTable::ToolV4 ItemParameterTable::ToolV3T<BE>::to_v4() const {
ToolV4 ret;
ret.base.id = this->base.id.load();
ret.base.type = this->base.type.load();
@@ -414,15 +414,15 @@ ItemParameterTable::ToolV4 ItemParameterTable::ToolV3T<IsBigEndian>::to_v4() con
return ret;
}
template <bool IsBigEndian>
size_t indirect_lookup_2d_count(const StringReader& r, size_t root_offset, size_t co_index) {
using ArrayRefT = typename std::conditional_t<IsBigEndian, ItemParameterTable::ArrayRefBE, ItemParameterTable::ArrayRef>;
template <bool BE>
size_t indirect_lookup_2d_count(const phosg::StringReader& r, size_t root_offset, size_t co_index) {
using ArrayRefT = typename std::conditional_t<BE, ItemParameterTable::ArrayRefBE, ItemParameterTable::ArrayRef>;
return r.pget<ArrayRefT>(root_offset + sizeof(ArrayRefT) * co_index).count;
}
template <typename T, bool IsBigEndian>
const T& indirect_lookup_2d(const StringReader& r, size_t root_offset, size_t co_index, size_t item_index) {
using ArrayRefT = typename std::conditional_t<IsBigEndian, ItemParameterTable::ArrayRefBE, ItemParameterTable::ArrayRef>;
template <typename T, bool BE>
const T& indirect_lookup_2d(const phosg::StringReader& r, size_t root_offset, size_t co_index, size_t item_index) {
using ArrayRefT = typename std::conditional_t<BE, ItemParameterTable::ArrayRefBE, ItemParameterTable::ArrayRef>;
const auto& co = r.pget<ArrayRefT>(root_offset + sizeof(ArrayRefT) * co_index);
if (item_index >= co.count) {
@@ -706,10 +706,10 @@ const ItemParameterTable::ToolV4& ItemParameterTable::get_tool(uint8_t data1_1,
}
}
template <typename ToolDefT, bool IsBigEndian>
template <typename ToolDefT, bool BE>
pair<uint8_t, uint8_t> ItemParameterTable::find_tool_by_id_t(uint32_t tool_table_offset, uint32_t item_id) const {
const auto* cos = &this->r.pget<ArrayRefT<IsBigEndian>>(
tool_table_offset, this->num_tool_classes * sizeof(ArrayRefT<IsBigEndian>));
const auto* cos = &this->r.pget<ArrayRefT<BE>>(
tool_table_offset, this->num_tool_classes * sizeof(ArrayRefT<BE>));
for (size_t z = 0; z < this->num_tool_classes; z++) {
const auto& co = cos[z];
const auto* defs = &this->r.pget<ToolDefT>(co.offset, sizeof(ToolDefT) * co.count);
@@ -719,7 +719,7 @@ pair<uint8_t, uint8_t> ItemParameterTable::find_tool_by_id_t(uint32_t tool_table
}
}
}
throw out_of_range(string_printf("invalid tool class %08" PRIX32, item_id));
throw out_of_range(phosg::string_printf("invalid tool class %08" PRIX32, item_id));
}
pair<uint8_t, uint8_t> ItemParameterTable::find_tool_by_id(uint32_t item_id) const {
@@ -740,19 +740,17 @@ pair<uint8_t, uint8_t> ItemParameterTable::find_tool_by_id(uint32_t item_id) con
}
}
template <bool IsBigEndian, typename OffsetsT>
template <bool BE, typename OffsetsT>
float ItemParameterTable::get_sale_divisor_t(const OffsetsT* offsets, uint8_t data1_0, uint8_t data1_1) const {
using FloatT = typename std::conditional<IsBigEndian, be_float, le_float>::type;
switch (data1_0) {
case 0:
if (data1_1 >= this->num_weapon_classes) {
return 0.0f;
}
return this->r.pget<FloatT>(offsets->weapon_sale_divisor_table + data1_1 * sizeof(FloatT));
return this->r.pget<F32T<BE>>(offsets->weapon_sale_divisor_table + data1_1 * sizeof(F32T<BE>));
case 1: {
const auto& divisors = this->r.pget<NonWeaponSaleDivisorsT<IsBigEndian>>(offsets->sale_divisor_table);
const auto& divisors = this->r.pget<NonWeaponSaleDivisorsT<BE>>(offsets->sale_divisor_table);
switch (data1_1) {
case 1:
return divisors.armor_divisor;
@@ -765,7 +763,7 @@ float ItemParameterTable::get_sale_divisor_t(const OffsetsT* offsets, uint8_t da
}
case 2: {
const auto& divisors = this->r.pget<NonWeaponSaleDivisorsT<IsBigEndian>>(offsets->sale_divisor_table);
const auto& divisors = this->r.pget<NonWeaponSaleDivisorsT<BE>>(offsets->sale_divisor_table);
return divisors.mag_divisor;
}
@@ -1136,9 +1134,9 @@ const std::map<uint32_t, std::vector<ItemParameterTable::ItemCombination>>& Item
return this->item_combination_index;
}
template <bool IsBigEndian>
template <bool BE>
size_t ItemParameterTable::num_events_t(uint32_t base_offset) const {
return this->r.pget<ArrayRefT<IsBigEndian>>(base_offset).count;
return this->r.pget<ArrayRefT<BE>>(base_offset).count;
}
size_t ItemParameterTable::num_events() const {
@@ -1155,14 +1153,14 @@ size_t ItemParameterTable::num_events() const {
}
}
template <bool IsBigEndian>
template <bool BE>
std::pair<const ItemParameterTable::EventItem*, size_t> ItemParameterTable::get_event_items_t(
uint32_t base_offset, uint8_t event_number) const {
const auto& co = this->r.pget<ArrayRefT<IsBigEndian>>(base_offset);
const auto& co = this->r.pget<ArrayRefT<BE>>(base_offset);
if (event_number >= co.count) {
throw out_of_range("invalid event number");
}
const auto& event_co = this->r.pget<ArrayRefT<IsBigEndian>>(co.offset + sizeof(ArrayRefT<IsBigEndian>) * event_number);
const auto& event_co = this->r.pget<ArrayRefT<BE>>(co.offset + sizeof(ArrayRefT<BE>) * event_number);
const auto* defs = &this->r.pget<EventItem>(event_co.offset, event_co.count * sizeof(EventItem));
return make_pair(defs, event_co.count);
}
+101 -122
View File
@@ -11,6 +11,7 @@
#include "ItemData.hh"
#include "Text.hh"
#include "Types.hh"
class ItemParameterTable {
public:
@@ -18,11 +19,10 @@ public:
// functions instead of manually branching on various offset table pointers
// being null or not in each public function. Rewrite this and make it better.
template <bool IsBigEndian>
template <bool BE>
struct ArrayRefT {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
/* 00 */ U32T count;
/* 04 */ U32T offset;
/* 00 */ U32T<BE> count;
/* 04 */ U32T<BE> offset;
/* 08 */
} __packed__;
@@ -31,25 +31,22 @@ public:
check_struct_size(ArrayRef, 8);
check_struct_size(ArrayRefBE, 8);
template <bool IsBigEndian>
template <bool BE>
struct ItemBaseV2T {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
// id specifies several things; notably, it doubles as the index of the
// item's name in the text archive (e.g. TextEnglish) collection 0.
/* 00 */ U32T id = 0xFFFFFFFF;
/* 00 */ U32T<BE> id = 0xFFFFFFFF;
/* 04 */
} __packed__;
template <bool IsBigEndian>
struct ItemBaseV3T : ItemBaseV2T<IsBigEndian> {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
/* 04 */ U16T type = 0;
/* 06 */ U16T skin = 0;
template <bool BE>
struct ItemBaseV3T : ItemBaseV2T<BE> {
/* 04 */ U16T<BE> type = 0;
/* 06 */ U16T<BE> skin = 0;
/* 08 */
} __packed__;
template <bool IsBigEndian>
struct ItemBaseV4T : ItemBaseV3T<IsBigEndian> {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
/* 08 */ U32T team_points = 0;
template <bool BE>
struct ItemBaseV4T : ItemBaseV3T<BE> {
/* 08 */ U32T<BE> team_points = 0;
/* 0C */
} __packed__;
@@ -118,17 +115,16 @@ public:
WeaponV4 to_v4() const;
} __packed_ws__(WeaponGCNTE, 0x24);
template <bool IsBigEndian>
template <bool BE>
struct WeaponV3T {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
/* 00 */ ItemBaseV3T<IsBigEndian> base;
/* 08 */ U16T class_flags = 0;
/* 0A */ U16T atp_min = 0;
/* 0C */ U16T atp_max = 0;
/* 0E */ U16T atp_required = 0;
/* 10 */ U16T mst_required = 0;
/* 12 */ U16T ata_required = 0;
/* 14 */ U16T mst = 0;
/* 00 */ ItemBaseV3T<BE> base;
/* 08 */ U16T<BE> class_flags = 0;
/* 0A */ U16T<BE> atp_min = 0;
/* 0C */ U16T<BE> atp_max = 0;
/* 0E */ U16T<BE> atp_required = 0;
/* 10 */ U16T<BE> mst_required = 0;
/* 12 */ U16T<BE> ata_required = 0;
/* 14 */ U16T<BE> mst = 0;
/* 16 */ uint8_t max_grind = 0;
/* 17 */ uint8_t photon = 0;
/* 18 */ uint8_t special = 0;
@@ -187,16 +183,15 @@ public:
/* 2C */
} __packed_ws__(WeaponV4, 0x2C);
template <typename BaseT, bool IsBigEndian>
template <typename BaseT, bool BE>
struct ArmorOrShieldT {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
/* V1/V2 offsets */
/* 00 */ BaseT base;
/* 04 */ U16T dfp = 0;
/* 06 */ U16T evp = 0;
/* 04 */ U16T<BE> dfp = 0;
/* 06 */ U16T<BE> evp = 0;
/* 08 */ uint8_t block_particle = 0;
/* 09 */ uint8_t block_effect = 0;
/* 0A */ U16T class_flags = 0x00FF;
/* 0A */ U16T<BE> class_flags = 0x00FF;
/* 0C */ uint8_t required_level = 0;
/* 0D */ uint8_t efr = 0;
/* 0E */ uint8_t eth = 0;
@@ -208,12 +203,11 @@ public:
/* 14 */
} __packed__;
template <typename BaseT, bool IsBigEndian>
struct ArmorOrShieldFinalT : ArmorOrShieldT<BaseT, IsBigEndian> {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
template <typename BaseT, bool BE>
struct ArmorOrShieldFinalT : ArmorOrShieldT<BaseT, BE> {
/* 14 */ uint8_t stat_boost = 0;
/* 15 */ uint8_t tech_boost = 0;
/* 16 */ U16T unknown_a2 = 0;
/* 16 */ U16T<BE> unknown_a2 = 0;
/* 18 */
} __packed__;
using ArmorOrShieldV4 = ArmorOrShieldFinalT<ItemBaseV4T<false>, false>;
@@ -225,8 +219,8 @@ public:
struct ArmorOrShieldV1V2 : ArmorOrShieldFinalT<ItemBaseV2T<false>, false> {
ArmorOrShieldV4 to_v4() const;
} __packed_ws__(ArmorOrShieldV1V2, 0x18);
template <bool IsBigEndian>
struct ArmorOrShieldV3T : ArmorOrShieldFinalT<ItemBaseV3T<IsBigEndian>, IsBigEndian> {
template <bool BE>
struct ArmorOrShieldV3T : ArmorOrShieldFinalT<ItemBaseV3T<BE>, BE> {
ArmorOrShieldV4 to_v4() const;
} __packed__;
using ArmorOrShieldV3 = ArmorOrShieldV3T<false>;
@@ -234,21 +228,18 @@ public:
check_struct_size(ArmorOrShieldV3, 0x1C);
check_struct_size(ArmorOrShieldV3BE, 0x1C);
template <typename BaseT, bool IsBigEndian>
template <typename BaseT, bool BE>
struct UnitT {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
using S16T = typename std::conditional<IsBigEndian, be_int16_t, le_int16_t>::type;
/* V1/V2 offsets */
/* 00 */ BaseT base;
/* 04 */ U16T stat = 0;
/* 06 */ U16T stat_amount = 0;
/* 04 */ U16T<BE> stat = 0;
/* 06 */ U16T<BE> stat_amount = 0;
/* 08 */
} __packed__;
template <typename BaseT, bool IsBigEndian>
struct UnitFinalT : UnitT<BaseT, IsBigEndian> {
using S16T = typename std::conditional<IsBigEndian, be_int16_t, le_int16_t>::type;
/* 08 */ S16T modifier_amount = 0;
template <typename BaseT, bool BE>
struct UnitFinalT : UnitT<BaseT, BE> {
/* 08 */ S16T<BE> modifier_amount = 0;
/* 0A */ parray<uint8_t, 2> unused;
/* 0C */
} __packed__;
@@ -260,8 +251,8 @@ public:
struct UnitV1V2 : UnitFinalT<ItemBaseV2T<false>, false> {
UnitV4 to_v4() const;
} __packed_ws__(UnitV1V2, 0x0C);
template <bool IsBigEndian>
struct UnitV3T : UnitFinalT<ItemBaseV3T<IsBigEndian>, IsBigEndian> {
template <bool BE>
struct UnitV3T : UnitFinalT<ItemBaseV3T<BE>, BE> {
UnitV4 to_v4() const;
} __packed__;
using UnitV3 = UnitV3T<false>;
@@ -269,12 +260,11 @@ public:
check_struct_size(UnitV3, 0x10);
check_struct_size(UnitV3BE, 0x10);
template <typename BaseT, bool IsBigEndian>
template <typename BaseT, bool BE>
struct MagT {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
/* V1/V2 offsets */
/* 00 */ BaseT base;
/* 04 */ U16T feed_table = 0;
/* 04 */ U16T<BE> feed_table = 0;
/* 06 */ uint8_t photon_blast = 0;
/* 07 */ uint8_t activation = 0;
/* 08 */ uint8_t on_pb_full = 0;
@@ -317,10 +307,9 @@ public:
MagV4 to_v4() const;
} __packed_ws__(MagV2, 0x14);
template <bool IsBigEndian>
struct MagV3T : MagT<ItemBaseV3T<IsBigEndian>, IsBigEndian> {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
/* 10 */ U16T class_flags = 0x00FF;
template <bool BE>
struct MagV3T : MagT<ItemBaseV3T<BE>, BE> {
/* 10 */ U16T<BE> class_flags = 0x00FF;
/* 12 */ parray<uint8_t, 2> unused;
/* 14 */
@@ -331,16 +320,13 @@ public:
check_struct_size(MagV3, 0x18);
check_struct_size(MagV3BE, 0x18);
template <typename BaseT, bool IsBigEndian>
template <typename BaseT, bool BE>
struct ToolT {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
using S32T = typename std::conditional<IsBigEndian, be_int32_t, le_int32_t>::type;
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
/* V1/V2 offsets */
/* 00 */ BaseT base;
/* 04 */ U16T amount = 0;
/* 06 */ U16T tech = 0;
/* 08 */ S32T cost = 0;
/* 04 */ U16T<BE> amount = 0;
/* 06 */ U16T<BE> tech = 0;
/* 08 */ S32T<BE> cost = 0;
// Bits in item_flags:
// 00000001 - ever usable by player ("Use" appears in inventory menu)
// 00000002 - unknown
@@ -350,7 +336,7 @@ public:
// 00000020 - usable in boss arenas
// 00000040 - usable in Challenge mode
// 00000080 - is rare (renders as red box; V3+)
/* 0C */ U32T item_flags = 0;
/* 0C */ U32T<BE> item_flags = 0;
/* 10 */
} __packed__;
@@ -359,8 +345,8 @@ public:
struct ToolV1V2 : ToolT<ItemBaseV2T<false>, false> {
ToolV4 to_v4() const;
} __packed_ws__(ToolV1V2, 0x10);
template <bool IsBigEndian>
struct ToolV3T : ToolT<ItemBaseV3T<IsBigEndian>, IsBigEndian> {
template <bool BE>
struct ToolV3T : ToolT<ItemBaseV3T<BE>, BE> {
ToolV4 to_v4() const;
} __packed__;
using ToolV3 = ToolV3T<false>;
@@ -380,34 +366,31 @@ public:
using MagFeedResultsList = parray<MagFeedResult, 11>;
template <bool IsBigEndian>
template <bool BE>
struct MagFeedResultsListOffsetsT {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
parray<U32T, 8> offsets; // Offsets of MagFeedResultsList objects
parray<U32T<BE>, 8> offsets; // Offsets of MagFeedResultsList objects
} __packed__;
using MagFeedResultsListOffsets = MagFeedResultsListOffsetsT<false>;
using MagFeedResultsListOffsetsBE = MagFeedResultsListOffsetsT<true>;
check_struct_size(MagFeedResultsListOffsets, 0x20);
check_struct_size(MagFeedResultsListOffsetsBE, 0x20);
template <bool IsBigEndian>
template <bool BE>
struct SpecialT {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
U16T type = 0xFFFF;
U16T amount = 0;
U16T<BE> type = 0xFFFF;
U16T<BE> amount = 0;
} __packed__;
using Special = SpecialT<false>;
using SpecialBE = SpecialT<true>;
check_struct_size(Special, 4);
check_struct_size(SpecialBE, 4);
template <bool IsBigEndian>
template <bool BE>
struct StatBoostT {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
uint8_t stat1 = 0;
uint8_t stat2 = 0;
U16T amount1 = 0;
U16T amount2 = 0;
U16T<BE> amount1 = 0;
U16T<BE> amount2 = 0;
} __packed__;
using StatBoost = StatBoostT<false>;
using StatBoostBE = StatBoostT<true>;
@@ -428,16 +411,14 @@ public:
parray<uint8_t, 3> unused;
} __packed_ws__(ItemCombination, 0x10);
template <bool IsBigEndian>
template <bool BE>
struct TechniqueBoostT {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
using FloatT = typename std::conditional<IsBigEndian, be_float, le_float>::type;
U32T tech1 = 0;
FloatT boost1 = 0.0f;
U32T tech2 = 0;
FloatT boost2 = 0.0f;
U32T tech3 = 0;
FloatT boost3 = 0.0f;
U32T<BE> tech1 = 0;
F32T<BE> boost1 = 0.0f;
U32T<BE> tech2 = 0;
F32T<BE> boost2 = 0.0f;
U32T<BE> tech3 = 0;
F32T<BE> boost3 = 0.0f;
} __packed__;
using TechniqueBoost = TechniqueBoostT<false>;
using TechniqueBoostBE = TechniqueBoostT<true>;
@@ -454,13 +435,12 @@ public:
uint8_t unused = 0;
} __packed_ws__(UnsealableItem, 4);
template <bool IsBigEndian>
template <bool BE>
struct NonWeaponSaleDivisorsT {
using FloatT = typename std::conditional<IsBigEndian, be_float, le_float>::type;
FloatT armor_divisor = 0.0f;
FloatT shield_divisor = 0.0f;
FloatT unit_divisor = 0.0f;
FloatT mag_divisor = 0.0f;
F32T<BE> armor_divisor = 0.0f;
F32T<BE> shield_divisor = 0.0f;
F32T<BE> unit_divisor = 0.0f;
F32T<BE> mag_divisor = 0.0f;
} __packed__;
using NonWeaponSaleDivisors = NonWeaponSaleDivisorsT<false>;
using NonWeaponSaleDivisorsBE = NonWeaponSaleDivisorsT<true>;
@@ -587,33 +567,32 @@ private:
/* 4C / 6B1C */ be_uint32_t tech_boost_table; // -> [TechniqueBoost] (always 0x2C of them? from counts struct?)
} __packed_ws__(TableOffsetsGCNTE, 0x50);
template <bool IsBigEndian>
template <bool BE>
struct TableOffsetsV3V4T {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
/* ## / GC / BB */
/* 00 / F078 / 14884 */ U32T weapon_table; // -> [{count, offset -> [WeaponV3/WeaponV4]}](0xED)
/* 04 / EF90 / 1478C */ U32T armor_table; // -> [{count, offset -> [ArmorOrShieldV3/ArmorOrShieldV4]}](2; armors and shields)
/* 08 / EFA0 / 1479C */ U32T unit_table; // -> {count, offset -> [UnitV3/UnitV4]} (last if out of range)
/* 0C / EFB0 / 147AC */ U32T tool_table; // -> [{count, offset -> [ToolV3/ToolV4]}](0x1A) (last if out of range)
/* 10 / EFA8 / 147A4 */ U32T mag_table; // -> {count, offset -> [MagV3/MagV4]}
/* 14 / B88C / 0F4B8 */ U32T v1_replacement_table; // -> [uint8_t](0xED)
/* 18 / A7FC / 0DE7C */ U32T photon_color_table; // -> [0x24-byte structs](0x20)
/* 1C / AACC / 0E194 */ U32T weapon_range_table; // -> ???
/* 20 / B938 / 0F5A8 */ U32T weapon_sale_divisor_table; // -> [float](0xED)
/* 24 / BBCC / 0F83C */ U32T sale_divisor_table; // -> NonWeaponSaleDivisors
/* 28 / F608 / 1502C */ U32T mag_feed_table; // -> MagFeedResultsTable
/* 2C / BE9C / 0FB0C */ U32T star_value_table; // -> [uint8_t](0x330) (indexed by .id from weapon, armor, etc.)
/* 30 / C100 / 0FE3C */ U32T special_data_table; // -> [Special]
/* 34 / C1A4 / 0FEE0 */ U32T weapon_effect_table; // -> [16-byte structs]
/* 38 / DE50 / 1275C */ U32T stat_boost_table; // -> [StatBoost]
/* 3C / D6E4 / 11C80 */ U32T shield_effect_table; // -> [8-byte structs]
/* 40 / DF88 / 12894 */ U32T max_tech_level_table; // -> MaxTechniqueLevels
/* 44 / F5D0 / 14FF4 */ U32T combination_table; // -> {count, offset -> [ItemCombination]}
/* 48 / DE48 / 12754 */ U32T unknown_a1;
/* 4C / EB8C / 14278 */ U32T tech_boost_table; // -> [TechniqueBoost] (always 0x2C of them? from counts struct?)
/* 50 / F5F0 / 15014 */ U32T unwrap_table; // -> {count, offset -> [{count, offset -> [EventItem]}]}
/* 54 / F5F8 / 1501C */ U32T unsealable_table; // -> {count, offset -> [UnsealableItem]}
/* 58 / F600 / 15024 */ U32T ranged_special_table; // -> {count, offset -> [4-byte structs]}
/* 00 / F078 / 14884 */ U32T<BE> weapon_table; // -> [{count, offset -> [WeaponV3/WeaponV4]}](0xED)
/* 04 / EF90 / 1478C */ U32T<BE> armor_table; // -> [{count, offset -> [ArmorOrShieldV3/ArmorOrShieldV4]}](2; armors and shields)
/* 08 / EFA0 / 1479C */ U32T<BE> unit_table; // -> {count, offset -> [UnitV3/UnitV4]} (last if out of range)
/* 0C / EFB0 / 147AC */ U32T<BE> tool_table; // -> [{count, offset -> [ToolV3/ToolV4]}](0x1A) (last if out of range)
/* 10 / EFA8 / 147A4 */ U32T<BE> mag_table; // -> {count, offset -> [MagV3/MagV4]}
/* 14 / B88C / 0F4B8 */ U32T<BE> v1_replacement_table; // -> [uint8_t](0xED)
/* 18 / A7FC / 0DE7C */ U32T<BE> photon_color_table; // -> [0x24-byte structs](0x20)
/* 1C / AACC / 0E194 */ U32T<BE> weapon_range_table; // -> ???
/* 20 / B938 / 0F5A8 */ U32T<BE> weapon_sale_divisor_table; // -> [float](0xED)
/* 24 / BBCC / 0F83C */ U32T<BE> sale_divisor_table; // -> NonWeaponSaleDivisors
/* 28 / F608 / 1502C */ U32T<BE> mag_feed_table; // -> MagFeedResultsTable
/* 2C / BE9C / 0FB0C */ U32T<BE> star_value_table; // -> [uint8_t](0x330) (indexed by .id from weapon, armor, etc.)
/* 30 / C100 / 0FE3C */ U32T<BE> special_data_table; // -> [Special]
/* 34 / C1A4 / 0FEE0 */ U32T<BE> weapon_effect_table; // -> [16-byte structs]
/* 38 / DE50 / 1275C */ U32T<BE> stat_boost_table; // -> [StatBoost]
/* 3C / D6E4 / 11C80 */ U32T<BE> shield_effect_table; // -> [8-byte structs]
/* 40 / DF88 / 12894 */ U32T<BE> max_tech_level_table; // -> MaxTechniqueLevels
/* 44 / F5D0 / 14FF4 */ U32T<BE> combination_table; // -> {count, offset -> [ItemCombination]}
/* 48 / DE48 / 12754 */ U32T<BE> unknown_a1;
/* 4C / EB8C / 14278 */ U32T<BE> tech_boost_table; // -> [TechniqueBoost] (always 0x2C of them? from counts struct?)
/* 50 / F5F0 / 15014 */ U32T<BE> unwrap_table; // -> {count, offset -> [{count, offset -> [EventItem]}]}
/* 54 / F5F8 / 1501C */ U32T<BE> unsealable_table; // -> {count, offset -> [UnsealableItem]}
/* 58 / F600 / 15024 */ U32T<BE> ranged_special_table; // -> {count, offset -> [4-byte structs]}
} __packed__;
using TableOffsetsV3V4 = TableOffsetsV3V4T<false>;
using TableOffsetsV3V4BE = TableOffsetsV3V4T<true>;
@@ -622,7 +601,7 @@ private:
Version version;
std::shared_ptr<const std::string> data;
StringReader r;
phosg::StringReader r;
const TableOffsetsDCProtos* offsets_dc_protos;
const TableOffsetsV1V2* offsets_v1_v2;
const TableOffsetsGCNTE* offsets_gc_nte;
@@ -644,13 +623,13 @@ private:
// equipped_item may contain wildcards, and the matching order matters.
mutable std::map<uint32_t, std::vector<ItemCombination>> item_combination_index;
template <typename ToolDefT, bool IsBigEndian>
template <typename ToolDefT, bool BE>
std::pair<uint8_t, uint8_t> find_tool_by_id_t(uint32_t tool_table_offset, uint32_t id) const;
template <bool IsBigEndian, typename OffsetsT>
template <bool BE, typename OffsetsT>
float get_sale_divisor_t(const OffsetsT* offsets, uint8_t data1_0, uint8_t data1_1) const;
template <bool IsBigEndian>
template <bool BE>
size_t num_events_t(uint32_t base_offset) const;
template <bool IsBigEndian>
template <bool BE>
std::pair<const ItemParameterTable::EventItem*, size_t> get_event_items_t(uint32_t base_offset, uint8_t event_number) const;
};
@@ -677,6 +656,6 @@ public:
private:
std::shared_ptr<const std::string> data;
StringReader r;
phosg::StringReader r;
const TableOffsets* offsets;
};
+9 -14
View File
@@ -12,7 +12,8 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<PSOLFGE
// On PC (and presumably DC), the client sends a 6x29 after this to delete the
// used item. On GC and later versions, this does not happen, so we should
// delete the item here.
bool is_v3_or_later = is_v3(c->version()) || is_v4(c->version());
bool is_v4 = ::is_v4(c->version());
bool is_v3_or_later = is_v3(c->version()) || is_v4;
bool should_delete_item = is_v3_or_later;
auto player = c->character();
@@ -36,20 +37,14 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<PSOLFGE
}
auto& weapon = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::WEAPON)];
// Don't enforce the weapon's grind limit on V1 and V2. This is necessary
// because the V2 client replaces its inventory items on the fly with items
// compatible with V1 when sending the 61 and 98 commands. There appears to
// be no way to disable this behavior, so there's no way for the server to
// get an accurate picture of what's actually in the player's inventory, so
// there's no way to know if we would be enforcing the correct grind limit.
if (is_v3_or_later) {
auto item_parameter_table = s->item_parameter_table(c->version());
auto weapon_def = item_parameter_table->get_weapon(weapon.data.data1[1], weapon.data.data1[2]);
if (weapon.data.data1[3] >= weapon_def.max_grind) {
throw runtime_error("weapon already at maximum grind");
}
// Only enforce grind limits on BB, since the server doesn't have direct
// control over players' inventories on other versions
auto item_parameter_table = s->item_parameter_table(c->version());
auto weapon_def = item_parameter_table->get_weapon(weapon.data.data1[1], weapon.data.data1[2]);
if (is_v4 && (weapon.data.data1[3] >= weapon_def.max_grind)) {
throw runtime_error("weapon already at maximum grind");
}
weapon.data.data1[3] += (item.data.data1[2] + 1);
weapon.data.data1[3] = min<uint8_t>(weapon.data.data1[3] + item.data.data1[2] + 1, weapon_def.max_grind);
} else if ((primary_identifier & 0xFFFF0000) == 0x030B0000) { // Material
auto p = c->character();
+9 -9
View File
@@ -54,13 +54,13 @@ LevelTableV2::LevelTableV2(const string& data, bool compressed) {
le_uint32_t unknown_a12; // -> u32[3] -> (0x14-byte struct)[0x0F]
} __packed_ws__(Offsets, 0x40);
StringReader r;
phosg::StringReader r;
string decompressed_data;
if (compressed) {
decompressed_data = prs_decompress(data);
r = StringReader(decompressed_data);
r = phosg::StringReader(decompressed_data);
} else {
r = StringReader(data);
r = phosg::StringReader(data);
}
const auto& offsets = r.pget<Offsets>(r.pget_u32l(r.size() - 0x10));
@@ -94,7 +94,7 @@ const LevelStatsDelta& LevelTableV2::stats_delta_for_level(uint8_t char_class, u
}
LevelTableV3BE::LevelTableV3BE(const string& data, bool encrypted) {
StringReader r;
phosg::StringReader r;
string decompressed_data;
if (encrypted) {
auto decrypted = decrypt_pr2_data<true>(data);
@@ -102,9 +102,9 @@ LevelTableV3BE::LevelTableV3BE(const string& data, bool encrypted) {
if (decompressed_data.size() != decrypted.decompressed_size) {
throw runtime_error("decompressed data size does not match expected size");
}
r = StringReader(decompressed_data);
r = phosg::StringReader(decompressed_data);
} else {
r = StringReader(data);
r = phosg::StringReader(data);
}
// The GC format is very simple (but everything is big-endian):
@@ -160,13 +160,13 @@ LevelTableV4::LevelTableV4(const string& data, bool compressed) {
le_uint32_t level_deltas; // -> u32[12] -> LevelStatsDelta[200]
} __packed_ws__(Offsets, 8);
StringReader r;
phosg::StringReader r;
string decompressed_data;
if (compressed) {
decompressed_data = prs_decompress(data);
r = StringReader(decompressed_data);
r = phosg::StringReader(decompressed_data);
} else {
r = StringReader(data);
r = phosg::StringReader(data);
}
const auto& offsets = r.pget<Offsets>(r.pget_u32l(r.size() - 0x10));
+22 -30
View File
@@ -11,21 +11,19 @@
class LevelTable;
template <bool IsBigEndian>
template <bool BE>
struct CharacterStatsT {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
/* 00 */ U16T atp = 0;
/* 02 */ U16T mst = 0;
/* 04 */ U16T evp = 0;
/* 06 */ U16T hp = 0;
/* 08 */ U16T dfp = 0;
/* 0A */ U16T ata = 0;
/* 0C */ U16T lck = 0;
/* 00 */ U16T<BE> atp = 0;
/* 02 */ U16T<BE> mst = 0;
/* 04 */ U16T<BE> evp = 0;
/* 06 */ U16T<BE> hp = 0;
/* 08 */ U16T<BE> dfp = 0;
/* 0A */ U16T<BE> ata = 0;
/* 0C */ U16T<BE> lck = 0;
/* 0E */
operator CharacterStatsT<!IsBigEndian>() const {
CharacterStatsT<!IsBigEndian> ret;
operator CharacterStatsT<!BE>() const {
CharacterStatsT<!BE> ret;
ret.atp = this->atp.load();
ret.mst = this->mst.load();
ret.evp = this->evp.load();
@@ -41,23 +39,19 @@ using CharacterStatsBE = CharacterStatsT<true>;
check_struct_size(CharacterStats, 0x0E);
check_struct_size(CharacterStatsBE, 0x0E);
template <bool IsBigEndian>
template <bool BE>
struct PlayerStatsT {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
using F32T = typename std::conditional<IsBigEndian, be_float, le_float>::type;
/* 00 */ CharacterStatsT<IsBigEndian> char_stats;
/* 0E */ U16T esp = 0;
/* 10 */ F32T height = 0.0;
/* 14 */ F32T unknown_a3 = 0.0;
/* 18 */ U32T level = 0;
/* 1C */ U32T experience = 0;
/* 20 */ U32T meseta = 0;
/* 00 */ CharacterStatsT<BE> char_stats;
/* 0E */ U16T<BE> esp = 0;
/* 10 */ F32T<BE> height = 0.0;
/* 14 */ F32T<BE> unknown_a3 = 0.0;
/* 18 */ U32T<BE> level = 0;
/* 1C */ U32T<BE> experience = 0;
/* 20 */ U32T<BE> meseta = 0;
/* 24 */
operator PlayerStatsT<!IsBigEndian>() const {
PlayerStatsT<!IsBigEndian> ret;
operator PlayerStatsT<!BE>() const {
PlayerStatsT<!BE> ret;
ret.char_stats = this->char_stats;
ret.esp = this->esp.load();
ret.height = this->height.load();
@@ -73,10 +67,8 @@ using PlayerStatsBE = PlayerStatsT<true>;
check_struct_size(PlayerStats, 0x24);
check_struct_size(PlayerStatsBE, 0x24);
template <bool IsBigEndian>
template <bool BE>
struct LevelStatsDeltaT {
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
/* 00 */ uint8_t atp;
/* 01 */ uint8_t mst;
/* 02 */ uint8_t evp;
@@ -85,7 +77,7 @@ struct LevelStatsDeltaT {
/* 05 */ uint8_t ata;
/* 06 */ uint8_t lck;
/* 07 */ uint8_t tp;
/* 08 */ U32T experience;
/* 08 */ U32T<BE> experience;
/* 0C */
void apply(CharacterStats& ps) const {
+11 -10
View File
@@ -16,7 +16,7 @@ bool Lobby::FloorItem::visible_to_client(uint8_t client_id) const {
}
Lobby::FloorItemManager::FloorItemManager(uint32_t lobby_id, uint8_t floor)
: log(string_printf("[Lobby:%08" PRIX32 ":FloorItems:%02hhX] ", lobby_id, floor), lobby_log.min_level),
: log(phosg::string_printf("[Lobby:%08" PRIX32 ":FloorItems:%02hhX] ", lobby_id, floor), lobby_log.min_level),
next_drop_number(0) {}
bool Lobby::FloorItemManager::exists(uint32_t item_id) const {
@@ -137,7 +137,7 @@ uint32_t Lobby::FloorItemManager::reassign_all_item_ids(uint32_t next_item_id) {
Lobby::Lobby(shared_ptr<ServerState> s, uint32_t id, bool is_game)
: server_state(s),
log(string_printf("[%s:%" PRIX32 "] ", is_game ? "Game" : "Lobby", id), lobby_log.min_level),
log(phosg::string_printf("[%s:%" PRIX32 "] ", is_game ? "Game" : "Lobby", id), lobby_log.min_level),
lobby_id(id),
min_level(0),
max_level(0xFFFFFFFF),
@@ -151,7 +151,7 @@ Lobby::Lobby(shared_ptr<ServerState> s, uint32_t id, bool is_game)
base_exp_multiplier(1),
exp_share_multiplier(0.5),
challenge_exp_multiplier(1.0f),
random_seed(random_object<uint32_t>()),
random_seed(phosg::random_object<uint32_t>()),
drop_mode(DropMode::CLIENT),
event(0),
block(0),
@@ -325,7 +325,7 @@ shared_ptr<Map> Lobby::load_maps(
uint32_t random_seed,
shared_ptr<PSOLFGEncryption> opt_rand_crypt,
const parray<le_uint32_t, 0x20>& variations,
const PrefixedLogger* log) {
const phosg::PrefixedLogger* log) {
auto enemy_filenames = sdt->map_filenames_for_variations(variations, episode, mode, SetDataTable::FilenameType::ENEMIES);
auto object_filenames = sdt->map_filenames_for_variations(variations, episode, mode, SetDataTable::FilenameType::OBJECTS);
auto event_filenames = sdt->map_filenames_for_variations(variations, episode, mode, SetDataTable::FilenameType::EVENTS);
@@ -360,7 +360,7 @@ shared_ptr<Map> Lobby::load_maps(
shared_ptr<const Map::RareEnemyRates> rare_rates,
uint32_t rare_seed,
shared_ptr<PSOLFGEncryption> opt_rand_crypt,
const PrefixedLogger* log) {
const phosg::PrefixedLogger* log) {
auto map = make_shared<Map>(version, lobby_id, rare_seed, opt_rand_crypt);
// Don't load free-roam maps in Challenge mode, since players can't go to
@@ -682,7 +682,7 @@ void Lobby::add_client(shared_ptr<Client> c, ssize_t required_client_id) {
void Lobby::remove_client(shared_ptr<Client> c) {
if (this->clients.at(c->lobby_client_id) != c) {
auto other_c = this->clients[c->lobby_client_id].get();
throw logic_error(string_printf(
throw logic_error(phosg::string_printf(
"client\'s lobby client id (%hhu) does not match client list (%u)",
c->lobby_client_id,
static_cast<uint8_t>(other_c ? other_c->lobby_client_id : 0xFF)));
@@ -743,7 +743,7 @@ void Lobby::remove_client(shared_ptr<Client> c) {
(this->idle_timeout_usecs > 0)) {
// If the lobby is persistent but has an idle timeout, make it expire after
// the specified time
auto tv = usecs_to_timeval(this->idle_timeout_usecs);
auto tv = phosg::usecs_to_timeval(this->idle_timeout_usecs);
event_add(this->idle_timeout_event.get(), &tv);
this->log.info("Idle timeout scheduled");
}
@@ -918,8 +918,9 @@ void Lobby::assign_inventory_and_bank_item_ids(shared_ptr<Client> c, bool consum
if (c->log.info("Assigned inventory item IDs%s", consume_ids ? "" : " but did not mark IDs as used")) {
c->print_inventory(stderr);
auto& bank = c->current_bank();
if (p->bank.num_items) {
p->bank.assign_ids(0x99000000 + (c->lobby_client_id << 20));
bank.assign_ids(0x99000000 + (c->lobby_client_id << 20));
c->log.info("Assigned bank item IDs");
c->print_bank(stderr);
} else {
@@ -1031,7 +1032,7 @@ bool Lobby::compare_shared(const shared_ptr<const Lobby>& a, const shared_ptr<co
}
template <>
Lobby::DropMode enum_for_name<Lobby::DropMode>(const char* name) {
Lobby::DropMode phosg::enum_for_name<Lobby::DropMode>(const char* name) {
if (!strcmp(name, "DISABLED")) {
return Lobby::DropMode::DISABLED;
} else if (!strcmp(name, "CLIENT")) {
@@ -1048,7 +1049,7 @@ Lobby::DropMode enum_for_name<Lobby::DropMode>(const char* name) {
}
template <>
const char* name_for_enum<Lobby::DropMode>(Lobby::DropMode value) {
const char* phosg::name_for_enum<Lobby::DropMode>(Lobby::DropMode value) {
switch (value) {
case Lobby::DropMode::DISABLED:
return "DISABLED";
+7 -8
View File
@@ -40,7 +40,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
bool visible_to_client(uint8_t client_id) const;
};
struct FloorItemManager {
PrefixedLogger log;
phosg::PrefixedLogger log;
uint64_t next_drop_number;
// It's important that this is a map and not an unordered_map. See the
// comment in send_game_item_state for more details.
@@ -91,9 +91,8 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
SERVER_DUPLICATE = 4,
};
std::weak_ptr<ServerState>
server_state;
PrefixedLogger log;
std::weak_ptr<ServerState> server_state;
phosg::PrefixedLogger log;
uint32_t lobby_id;
@@ -232,7 +231,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
uint32_t random_seed,
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt,
const parray<le_uint32_t, 0x20>& variations,
const PrefixedLogger* log = nullptr);
const phosg::PrefixedLogger* log = nullptr);
static std::shared_ptr<Map> load_maps(
const std::vector<std::string>& enemy_filenames,
const std::vector<std::string>& object_filenames,
@@ -247,7 +246,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
std::shared_ptr<const Map::RareEnemyRates> rare_rates,
uint32_t random_seed,
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt,
const PrefixedLogger* log = nullptr);
const phosg::PrefixedLogger* log = nullptr);
void load_maps();
void create_ep3_server();
@@ -317,6 +316,6 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
};
template <>
Lobby::DropMode enum_for_name<Lobby::DropMode>(const char* name);
Lobby::DropMode phosg::enum_for_name<Lobby::DropMode>(const char* name);
template <>
const char* name_for_enum<Lobby::DropMode>(Lobby::DropMode value);
const char* phosg::name_for_enum<Lobby::DropMode>(Lobby::DropMode value);
+19 -19
View File
@@ -4,32 +4,32 @@
using namespace std;
PrefixedLogger ax_messages_log("[$ax message] ", LogLevel::USE_DEFAULT);
PrefixedLogger channel_exceptions_log("[Channel] ", LogLevel::USE_DEFAULT);
PrefixedLogger client_log("", LogLevel::USE_DEFAULT);
PrefixedLogger command_data_log("[Commands] ", LogLevel::USE_DEFAULT);
PrefixedLogger config_log("[Config] ", LogLevel::USE_DEFAULT);
PrefixedLogger dns_server_log("[DNSServer] ", LogLevel::USE_DEFAULT);
PrefixedLogger function_compiler_log("[FunctionCompiler] ", LogLevel::USE_DEFAULT);
PrefixedLogger ip_stack_simulator_log("[IPStackSimulator] ", LogLevel::USE_DEFAULT);
PrefixedLogger lobby_log("", LogLevel::USE_DEFAULT);
PrefixedLogger patch_index_log("[PatchFileIndex] ", LogLevel::USE_DEFAULT);
PrefixedLogger player_data_log("", LogLevel::USE_DEFAULT);
PrefixedLogger proxy_server_log("[ProxyServer] ", LogLevel::USE_DEFAULT);
PrefixedLogger replay_log("[ReplaySession] ", LogLevel::USE_DEFAULT);
PrefixedLogger server_log("[Server] ", LogLevel::USE_DEFAULT);
PrefixedLogger static_game_data_log("[StaticGameData] ", LogLevel::USE_DEFAULT);
phosg::PrefixedLogger ax_messages_log("[$ax message] ", phosg::LogLevel::USE_DEFAULT);
phosg::PrefixedLogger channel_exceptions_log("[Channel] ", phosg::LogLevel::USE_DEFAULT);
phosg::PrefixedLogger client_log("", phosg::LogLevel::USE_DEFAULT);
phosg::PrefixedLogger command_data_log("[Commands] ", phosg::LogLevel::USE_DEFAULT);
phosg::PrefixedLogger config_log("[Config] ", phosg::LogLevel::USE_DEFAULT);
phosg::PrefixedLogger dns_server_log("[DNSServer] ", phosg::LogLevel::USE_DEFAULT);
phosg::PrefixedLogger function_compiler_log("[FunctionCompiler] ", phosg::LogLevel::USE_DEFAULT);
phosg::PrefixedLogger ip_stack_simulator_log("[IPStackSimulator] ", phosg::LogLevel::USE_DEFAULT);
phosg::PrefixedLogger lobby_log("", phosg::LogLevel::USE_DEFAULT);
phosg::PrefixedLogger patch_index_log("[PatchFileIndex] ", phosg::LogLevel::USE_DEFAULT);
phosg::PrefixedLogger player_data_log("", phosg::LogLevel::USE_DEFAULT);
phosg::PrefixedLogger proxy_server_log("[ProxyServer] ", phosg::LogLevel::USE_DEFAULT);
phosg::PrefixedLogger replay_log("[ReplaySession] ", phosg::LogLevel::USE_DEFAULT);
phosg::PrefixedLogger server_log("[Server] ", phosg::LogLevel::USE_DEFAULT);
phosg::PrefixedLogger static_game_data_log("[StaticGameData] ", phosg::LogLevel::USE_DEFAULT);
static void set_log_level_from_json(
PrefixedLogger& log, const JSON& d, const char* json_key) {
phosg::PrefixedLogger& log, const phosg::JSON& d, const char* json_key) {
try {
string name = toupper(d.at(json_key).as_string());
log.min_level = enum_for_name<LogLevel>(name.c_str());
string name = phosg::toupper(d.at(json_key).as_string());
log.min_level = phosg::enum_for_name<phosg::LogLevel>(name.c_str());
} catch (const out_of_range&) {
}
}
void set_log_levels_from_json(const JSON& json) {
void set_log_levels_from_json(const phosg::JSON& json) {
set_log_level_from_json(ax_messages_log, json, "AXMessages");
set_log_level_from_json(channel_exceptions_log, json, "ChannelExceptions");
set_log_level_from_json(client_log, json, "Clients");
+16 -16
View File
@@ -3,20 +3,20 @@
#include <phosg/JSON.hh>
#include <phosg/Strings.hh>
extern PrefixedLogger ax_messages_log;
extern PrefixedLogger channel_exceptions_log;
extern PrefixedLogger client_log;
extern PrefixedLogger command_data_log;
extern PrefixedLogger config_log;
extern PrefixedLogger dns_server_log;
extern PrefixedLogger function_compiler_log;
extern PrefixedLogger ip_stack_simulator_log;
extern PrefixedLogger lobby_log;
extern PrefixedLogger patch_index_log;
extern PrefixedLogger player_data_log;
extern PrefixedLogger proxy_server_log;
extern PrefixedLogger replay_log;
extern PrefixedLogger server_log;
extern PrefixedLogger static_game_data_log;
extern phosg::PrefixedLogger ax_messages_log;
extern phosg::PrefixedLogger channel_exceptions_log;
extern phosg::PrefixedLogger client_log;
extern phosg::PrefixedLogger command_data_log;
extern phosg::PrefixedLogger config_log;
extern phosg::PrefixedLogger dns_server_log;
extern phosg::PrefixedLogger function_compiler_log;
extern phosg::PrefixedLogger ip_stack_simulator_log;
extern phosg::PrefixedLogger lobby_log;
extern phosg::PrefixedLogger patch_index_log;
extern phosg::PrefixedLogger player_data_log;
extern phosg::PrefixedLogger proxy_server_log;
extern phosg::PrefixedLogger replay_log;
extern phosg::PrefixedLogger server_log;
extern phosg::PrefixedLogger static_game_data_log;
void set_log_levels_from_json(const JSON& json);
void set_log_levels_from_json(const phosg::JSON& json);
+292 -278
View File
File diff suppressed because it is too large Load Diff
+115 -86
View File
@@ -583,7 +583,7 @@ Map::RareEnemyRates::RareEnemyRates(uint32_t enemy_rate, uint32_t boss_rate)
dorphon_eclair(enemy_rate),
kondrieu(boss_rate) {}
Map::RareEnemyRates::RareEnemyRates(const JSON& json)
Map::RareEnemyRates::RareEnemyRates(const phosg::JSON& json)
: hildeblue(json.get_int("Hildeblue")),
rappy(json.get_int("Rappy")),
nar_lily(json.get_int("NarLily")),
@@ -593,8 +593,8 @@ Map::RareEnemyRates::RareEnemyRates(const JSON& json)
dorphon_eclair(json.get_int("DorphonEclair")),
kondrieu(json.get_int("Kondrieu")) {}
JSON Map::RareEnemyRates::json() const {
return JSON::dict({
phosg::JSON Map::RareEnemyRates::json() const {
return phosg::JSON::dict({
{"Hildeblue", this->hildeblue},
{"Rappy", this->rappy},
{"NarLily", this->nar_lily},
@@ -608,7 +608,7 @@ JSON Map::RareEnemyRates::json() const {
string Map::ObjectEntry::str() const {
string name_str = Map::name_for_object_type(this->base_type);
return string_printf("[ObjectEntry type=%04hX \"%s\" flags=%04hX index=%04hX a2=%04hX entity_id=%04hX group=%04hX section=%04hX a3=%04hX x=%g y=%g z=%g x_angle=%08" PRIX32 " y_angle=%08" PRIX32 " z_angle=%08" PRIX32 " params=[%g %g %g %08" PRIX32 " %08" PRIX32 " %08" PRIX32 "] unused=%08" PRIX32 "]",
return phosg::string_printf("[ObjectEntry type=%04hX \"%s\" flags=%04hX index=%04hX a2=%04hX entity_id=%04hX group=%04hX section=%04hX a3=%04hX x=%g y=%g z=%g x_angle=%08" PRIX32 " y_angle=%08" PRIX32 " z_angle=%08" PRIX32 " params=[%g %g %g %08" PRIX32 " %08" PRIX32 " %08" PRIX32 "] unused=%08" PRIX32 "]",
this->base_type.load(),
name_str.c_str(),
this->flags.load(),
@@ -634,7 +634,7 @@ string Map::ObjectEntry::str() const {
}
string Map::EnemyEntry::str() const {
return string_printf("[EnemyEntry type=%04hX flags=%04hX index=%04hX num_children=%04hX floor=%04hX entity_id=%04hX section=%04hX wave_number=%04hX wave_number2=%04hX a1=%04hX x=%g y=%g z=%g x_angle=%08" PRIX32 " y_angle=%08" PRIX32 " z_angle=%08" PRIX32 " params=[%g %g %g %g %g %04hX %04hX] unused=%08" PRIX32 "]",
return phosg::string_printf("[EnemyEntry type=%04hX flags=%04hX index=%04hX num_children=%04hX floor=%04hX entity_id=%04hX section=%04hX wave_number=%04hX wave_number2=%04hX a1=%04hX x=%g y=%g z=%g x_angle=%08" PRIX32 " y_angle=%08" PRIX32 " z_angle=%08" PRIX32 " params=[%g %g %g %g %g %04hX %04hX] unused=%08" PRIX32 "]",
this->base_type.load(),
this->flags.load(),
this->index.load(),
@@ -668,7 +668,8 @@ Map::Enemy::Enemy(
uint8_t floor,
uint16_t section,
uint16_t wave_number,
EnemyType type)
EnemyType type,
uint16_t alias_entity_id)
: source_index(source_index),
set_index(set_index),
enemy_id(enemy_id),
@@ -678,13 +679,14 @@ Map::Enemy::Enemy(
wave_number(wave_number),
type(type),
floor(floor),
server_flags(0) {}
server_flags(0),
alias_entity_id(alias_entity_id) {}
string Map::Enemy::str() const {
return string_printf("[Map::Enemy E-%hX source %zX %s%s floor=%02hhX section=%04hX wave_number=%04hX server_flags=%02hhX]",
return phosg::string_printf("[Map::Enemy E-%hX source %zX %s%s floor=%02hhX section=%04hX wave_number=%04hX server_flags=%02hhX]",
this->enemy_id,
this->source_index,
name_for_enum(this->type),
phosg::name_for_enum(this->type),
enemy_type_is_rare(this->type) ? " RARE" : "",
this->floor,
this->section,
@@ -693,7 +695,7 @@ string Map::Enemy::str() const {
}
string Map::Event::str() const {
return string_printf("[Map::Event W-%02hhX-%" PRIX32 " flags=%04hX floor=%02hhX action_stream_offset=%" PRIX32 "]",
return phosg::string_printf("[Map::Event W-%02hhX-%" PRIX32 " flags=%04hX floor=%02hhX action_stream_offset=%" PRIX32 "]",
this->floor,
this->event_id,
this->flags,
@@ -702,7 +704,7 @@ string Map::Event::str() const {
}
string Map::Object::str() const {
return string_printf("[Map::Object source %zX %04hX(%s) @%04hX p1=%g p456=[%08" PRIX32 " %08" PRIX32 " %08" PRIX32 "] floor=%02hhX item_drop_checked=%s]",
return phosg::string_printf("[Map::Object source %zX %04hX(%s) @%04hX p1=%g p456=[%08" PRIX32 " %08" PRIX32 " %08" PRIX32 "] floor=%02hhX item_drop_checked=%s]",
this->source_index,
this->base_type,
Map::name_for_object_type(this->base_type),
@@ -716,7 +718,7 @@ string Map::Object::str() const {
}
Map::Map(Version version, uint32_t lobby_id, uint32_t rare_seed, std::shared_ptr<PSOLFGEncryption> opt_rand_crypt)
: log(string_printf("[Lobby:%08" PRIX32 ":map] ", lobby_id), lobby_log.min_level),
: log(phosg::string_printf("[Lobby:%08" PRIX32 ":map] ", lobby_id), lobby_log.min_level),
version(version),
rare_seed(rare_seed),
opt_rand_crypt(opt_rand_crypt) {}
@@ -800,9 +802,9 @@ void Map::add_enemy(
size_t set_index = this->enemy_set_flags.size();
this->enemy_set_flags.emplace_back(0);
auto add = [&](EnemyType type) -> void {
auto add = [&](EnemyType type, uint16_t alias_enemy_id = 0xFFFF) -> void {
uint16_t enemy_id = this->enemies.size();
this->enemies.emplace_back(enemy_id, source_index, set_index, floor, e.section, e.wave_number, type);
this->enemies.emplace_back(enemy_id, source_index, set_index, floor, e.section, e.wave_number, type, alias_enemy_id);
uint64_t k = section_index_key(floor, e.section, e.wave_number);
this->floor_section_and_wave_number_to_enemy_index.emplace(k, enemy_id);
};
@@ -1075,27 +1077,34 @@ void Map::add_enemy(
case 0x00C5: // Unnamed subclass of TObjEnemyCustom
add(EnemyType::VOL_OPT_2);
break;
case 0x00C8: // TBoss4DarkFalz
case 0x00C8: { // TBoss4DarkFalz
if ((e.num_children != 0) && (e.num_children != 0x200)) {
this->log.warning("DARK_FALZ has an unusual num_children (0x%hX)", e.num_children.load());
}
default_num_children = -1; // Skip adding children (because we do it here)
uint16_t base_enemy_id = this->enemies.size();
if (difficulty) {
add(EnemyType::DARK_FALZ_3);
} else {
add(EnemyType::DARK_FALZ_2);
}
default_num_children = -1; // Skip adding children (because we do it here)
for (size_t x = 0; x < 0x1FD; x++) {
add(difficulty == 3 ? EnemyType::DARVANT_ULTIMATE : EnemyType::DARVANT);
}
add(EnemyType::DARK_FALZ_3);
add(EnemyType::DARK_FALZ_2);
add(EnemyType::DARK_FALZ_1);
add(EnemyType::DARK_FALZ_3, base_enemy_id);
add(EnemyType::DARK_FALZ_2, base_enemy_id);
add(EnemyType::DARK_FALZ_1, base_enemy_id);
break;
case 0x00CA: // TBoss6PlotFalz
}
case 0x00CA: { // TBoss6PlotFalz
uint16_t base_enemy_id = this->enemies.size();
add(EnemyType::OLGA_FLOW_2);
default_num_children = 0x200;
default_num_children = -1; // Skip adding children (because we do it here)
for (size_t x = 0; x < 0x200; x++) {
add(EnemyType::OLGA_FLOW_2, base_enemy_id);
}
break;
}
case 0x00CB: // TBoss7DeRolLeC
add(EnemyType::BARBA_RAY);
child_type = EnemyType::PIG_RAY;
@@ -1254,7 +1263,7 @@ void Map::add_enemies_from_map_data(
throw runtime_error("data size is not a multiple of entry size");
}
StringReader r(data, size);
phosg::StringReader r(data, size);
for (size_t y = 0; y < entry_count; y++) {
this->add_enemy(episode, difficulty, event, floor, y, r.get<EnemyEntry>(), rare_rates);
}
@@ -1284,12 +1293,12 @@ uint32_t Map::DATParserRandomState::next_location_index() {
}
void Map::DATParserRandomState::generate_shuffled_location_table(
const Map::RandomEnemyLocationsHeader& header, StringReader r, uint16_t section) {
const Map::RandomEnemyLocationsHeader& header, phosg::StringReader r, uint16_t section) {
if (header.num_sections == 0) {
throw runtime_error("no locations defined");
}
StringReader sections_r = r.sub(header.section_table_offset, header.num_sections * sizeof(Map::RandomEnemyLocationSection));
phosg::StringReader sections_r = r.sub(header.section_table_offset, header.num_sections * sizeof(Map::RandomEnemyLocationSection));
size_t bs_min = 0;
size_t bs_max = header.num_sections - 1;
@@ -1330,9 +1339,9 @@ void Map::add_random_enemies_from_map_data(
uint8_t difficulty,
uint8_t event,
uint8_t floor,
StringReader wave_events_segment_r,
StringReader locations_segment_r,
StringReader definitions_segment_r,
phosg::StringReader wave_events_segment_r,
phosg::StringReader locations_segment_r,
phosg::StringReader definitions_segment_r,
std::shared_ptr<DATParserRandomState> random_state,
std::shared_ptr<const RareEnemyRates> rare_rates) {
@@ -1362,7 +1371,7 @@ void Map::add_random_enemies_from_map_data(
definitions_header.weight_entry_count * sizeof(RandomEnemyWeight));
for (size_t wave_entry_index = 0; wave_entry_index < wave_events_header.entry_count; wave_entry_index++) {
auto entry_log = this->log.sub(string_printf("(Entry %zu/%" PRIu32 ") ", wave_entry_index, wave_events_header.entry_count.load()));
auto entry_log = this->log.sub(phosg::string_printf("(Entry %zu/%" PRIu32 ") ", wave_entry_index, wave_events_header.entry_count.load()));
const auto& entry = wave_events_segment_r.get<Event2Entry>();
size_t remaining_waves = random_state->rand_int_biased(1, entry.max_waves);
@@ -1372,7 +1381,7 @@ void Map::add_random_enemies_from_map_data(
uint32_t wave_number = entry.wave_number;
while (remaining_waves) {
remaining_waves--;
auto wave_log = entry_log.sub(string_printf("(Wave %zu) ", remaining_waves));
auto wave_log = entry_log.sub(phosg::string_printf("(Wave %zu) ", remaining_waves));
size_t remaining_enemies = random_state->rand_int_biased(entry.min_enemies, entry.max_enemies);
// Trace: at 0080E208 EDI is enemy count
@@ -1382,7 +1391,7 @@ void Map::add_random_enemies_from_map_data(
while (remaining_enemies) {
remaining_enemies--;
auto enemy_log = wave_log.sub(string_printf("(Enemy %zu) ", remaining_enemies));
auto enemy_log = wave_log.sub(phosg::string_printf("(Enemy %zu) ", remaining_enemies));
// TODO: Factor this sum out of the loops
weights_r.go(0);
@@ -1506,7 +1515,7 @@ vector<const Map::Event*> Map::get_events(uint8_t floor, uint32_t event_id) cons
}
void Map::add_events_from_map_data(uint8_t floor, const void* data, size_t size) {
StringReader r(data, size);
phosg::StringReader r(data, size);
const auto& header = r.get<EventsSectionHeader>();
if (header.format != 0) {
throw runtime_error("events section format is not zero");
@@ -1525,7 +1534,7 @@ void Map::add_events_from_map_data(uint8_t floor, const void* data, size_t size)
vector<Map::DATSectionsForFloor> Map::collect_quest_map_data_sections(const void* data, size_t size) {
vector<DATSectionsForFloor> ret;
StringReader r(data, size);
phosg::StringReader r(data, size);
while (!r.eof()) {
size_t header_offset = r.where();
const auto& header = r.get<SectionHeader>();
@@ -1534,7 +1543,7 @@ vector<Map::DATSectionsForFloor> Map::collect_quest_map_data_sections(const void
break;
}
if (header.section_size < sizeof(header)) {
throw runtime_error(string_printf("quest layout has invalid section header at offset 0x%zX", r.where() - sizeof(header)));
throw runtime_error(phosg::string_printf("quest layout has invalid section header at offset 0x%zX", r.where() - sizeof(header)));
}
if (header.floor > 0x100) {
@@ -1593,7 +1602,7 @@ void Map::add_entities_from_quest_data(
std::shared_ptr<const RareEnemyRates> rare_rates) {
auto all_floor_sections = this->collect_quest_map_data_sections(data, size);
StringReader r(data, size);
phosg::StringReader r(data, size);
shared_ptr<DATParserRandomState> random_state;
for (size_t floor = 0; floor < all_floor_sections.size(); floor++) {
const auto& floor_sections = all_floor_sections[floor];
@@ -1653,12 +1662,34 @@ void Map::add_entities_from_quest_data(
}
}
const Map::Enemy& Map::find_enemy(uint16_t enemy_id) const {
return const_cast<Map*>(this)->find_enemy(enemy_id);
}
Map::Enemy& Map::find_enemy(uint16_t enemy_id) {
if (this->enemies.empty()) {
throw out_of_range("no enemies defined");
}
if (enemy_id >= this->enemies.size()) {
throw out_of_range("enemy ID out of range");
}
auto& enemy = this->enemies[enemy_id];
if (enemy.alias_entity_id != 0xFFFF) {
if (enemy.alias_entity_id >= this->enemies.size()) {
throw out_of_range("aliased enemy ID out of range");
}
return this->enemies[enemy.alias_entity_id];
} else {
return enemy;
}
}
const Map::Enemy& Map::find_enemy(uint8_t floor, EnemyType type) const {
return const_cast<Map*>(this)->find_enemy(floor, type);
}
Map::Enemy& Map::find_enemy(uint8_t floor, EnemyType type) {
if (enemies.empty()) {
if (this->enemies.empty()) {
throw out_of_range("no enemies defined");
}
// TODO: Linear search is bad here. Do something better, like binary search
@@ -1713,7 +1744,7 @@ std::vector<Map::Event*> Map::get_events(uint8_t floor) {
template <typename EntryT>
static string disassemble_vector_file_t(const void* data, size_t size, size_t* entry_number, char type_ch) {
deque<string> ret;
StringReader r(data, size);
phosg::StringReader r(data, size);
size_t local_entry_number = 0;
if (!entry_number) {
@@ -1722,14 +1753,14 @@ static string disassemble_vector_file_t(const void* data, size_t size, size_t* e
while (r.remaining() >= sizeof(EntryT)) {
string o_str = r.get<EntryT>().str();
ret.emplace_back(string_printf("/* %c-%zX */ %s", type_ch, (*entry_number)++, o_str.c_str()));
ret.emplace_back(phosg::string_printf("/* %c-%zX */ %s", type_ch, (*entry_number)++, o_str.c_str()));
}
if (r.remaining()) {
ret.emplace_back("// Warning: section size is not a multiple of entry size");
size_t size = r.remaining();
ret.emplace_back(format_data(r.getv(size), size));
ret.emplace_back(phosg::format_data(r.getv(size), size));
}
return join(ret, "\n");
return phosg::join(ret, "\n");
}
string Map::disassemble_objects_data(const void* data, size_t size, size_t* object_number) {
@@ -1742,18 +1773,18 @@ string Map::disassemble_enemies_data(const void* data, size_t size, size_t* enem
string Map::disassemble_wave_events_data(const void* data, size_t size, uint8_t floor) {
deque<string> ret;
StringReader r(data, size);
phosg::StringReader r(data, size);
const auto& evt_header = r.get<EventsSectionHeader>();
if (evt_header.format == 0x65767432) { // 'evt2'
ret.emplace_back(".evt2_format"); // TODO
size_t size = r.remaining();
ret.emplace_back(format_data(r.getv(size), size));
ret.emplace_back(phosg::format_data(r.getv(size), size));
} else {
auto action_stream_r = r.sub(evt_header.action_stream_offset);
for (size_t z = 0; z < evt_header.entry_count; z++) {
const auto& entry = r.get<Event1Entry>();
ret.emplace_back(string_printf("/* W-%02hhX-%" PRIX32 " */ [Event1Entry flags=%04hX type=%04hX section=%04hX wave_number=%04hX delay=%" PRIu32 "]",
ret.emplace_back(phosg::string_printf("/* W-%02hhX-%" PRIX32 " */ [Event1Entry flags=%04hX type=%04hX section=%04hX wave_number=%04hX delay=%" PRIu32 "]",
floor,
entry.event_id.load(),
entry.flags.load(),
@@ -1767,103 +1798,103 @@ string Map::disassemble_wave_events_data(const void* data, size_t size, uint8_t
uint8_t opcode = ev_actions_r.get_u8();
switch (opcode) {
case 0x00:
ret.emplace_back(string_printf(" 00 nop"));
ret.emplace_back(phosg::string_printf(" 00 nop"));
break;
case 0x01:
ret.emplace_back(string_printf(" 01 stop"));
ret.emplace_back(phosg::string_printf(" 01 stop"));
should_continue = false;
break;
case 0x08: {
uint16_t section = ev_actions_r.get_u16l();
uint16_t group = ev_actions_r.get_u16l();
ret.emplace_back(string_printf(" 08 %04hX %04hX construct_objects section=%04hX group=%04hX",
ret.emplace_back(phosg::string_printf(" 08 %04hX %04hX construct_objects section=%04hX group=%04hX",
section, group, section, group));
break;
}
case 0x09: {
uint16_t section = ev_actions_r.get_u16l();
uint16_t wave_number = ev_actions_r.get_u16l();
ret.emplace_back(string_printf(" 09 %04hX %04hX construct_enemies section=%04hX wave_number=%04hX",
ret.emplace_back(phosg::string_printf(" 09 %04hX %04hX construct_enemies section=%04hX wave_number=%04hX",
section, wave_number, section, wave_number));
break;
}
case 0x0A: {
uint16_t id = ev_actions_r.get_u16l();
ret.emplace_back(string_printf(" 0A %04hX enable_switch_flag id=%04hX", id, id));
ret.emplace_back(phosg::string_printf(" 0A %04hX enable_switch_flag id=%04hX", id, id));
break;
}
case 0x0B: {
uint16_t id = ev_actions_r.get_u16l();
ret.emplace_back(string_printf(" 0B %04hX disable_switch_flag id=%04hX", id, id));
ret.emplace_back(phosg::string_printf(" 0B %04hX disable_switch_flag id=%04hX", id, id));
break;
}
case 0x0C: {
uint32_t event_id = ev_actions_r.get_u32l();
ret.emplace_back(string_printf(" 0C %08" PRIX32 " trigger_event event_id=%08" PRIX32, event_id, event_id));
ret.emplace_back(phosg::string_printf(" 0C %08" PRIX32 " trigger_event event_id=%08" PRIX32, event_id, event_id));
break;
}
case 0x0D: {
uint16_t section = ev_actions_r.get_u16l();
uint16_t wave_number = ev_actions_r.get_u16l();
ret.emplace_back(string_printf(" 0D %04hX %04hX construct_enemies_stop section=%04hX wave_number=%04hX",
ret.emplace_back(phosg::string_printf(" 0D %04hX %04hX construct_enemies_stop section=%04hX wave_number=%04hX",
section, wave_number, section, wave_number));
break;
}
default:
ret.emplace_back(string_printf(" %02hhX .invalid", opcode));
ret.emplace_back(phosg::string_printf(" %02hhX .invalid", opcode));
}
}
}
}
return join(ret, "\n");
return phosg::join(ret, "\n");
}
string Map::disassemble_quest_data(const void* data, size_t size) {
auto all_floor_sections = Map::collect_quest_map_data_sections(data, size);
deque<string> ret;
StringReader r(data, size);
phosg::StringReader r(data, size);
size_t object_number = 0;
size_t enemy_number = 0;
for (size_t floor = 0; floor < all_floor_sections.size(); floor++) {
const auto& floor_sections = all_floor_sections[floor];
if (floor_sections.objects != 0xFFFFFFFF) {
ret.emplace_back(string_printf(".objects %zu", floor));
ret.emplace_back(phosg::string_printf(".objects %zu", floor));
const auto& header = r.pget<SectionHeader>(floor_sections.objects);
size_t offset = floor_sections.objects + sizeof(SectionHeader);
ret.emplace_back(Map::disassemble_objects_data(r.pgetv(offset, header.data_size), header.data_size, &object_number));
}
if (floor_sections.enemies != 0xFFFFFFFF) {
ret.emplace_back(string_printf(".enemies %zu", floor));
ret.emplace_back(phosg::string_printf(".enemies %zu", floor));
const auto& header = r.pget<SectionHeader>(floor_sections.enemies);
size_t offset = floor_sections.enemies + sizeof(SectionHeader);
ret.emplace_back(Map::disassemble_enemies_data(r.pgetv(offset, header.data_size), header.data_size, &enemy_number));
}
if (floor_sections.wave_events != 0xFFFFFFFF) {
ret.emplace_back(string_printf(".wave_events %zu", floor));
ret.emplace_back(phosg::string_printf(".wave_events %zu", floor));
const auto& header = r.pget<SectionHeader>(floor_sections.wave_events);
size_t offset = floor_sections.wave_events + sizeof(SectionHeader);
ret.emplace_back(Map::disassemble_wave_events_data(r.pgetv(offset, header.data_size), header.data_size, floor));
}
if (floor_sections.random_enemy_locations != 0xFFFFFFFF) {
ret.emplace_back(string_printf(".random_enemy_locations %zu", floor));
ret.emplace_back(phosg::string_printf(".random_enemy_locations %zu", floor));
const auto& header = r.pget<SectionHeader>(floor_sections.random_enemy_locations);
size_t offset = floor_sections.random_enemy_locations + sizeof(SectionHeader);
auto sub_r = r.sub(offset, header.data_size);
ret.emplace_back(format_data(sub_r.getv(sub_r.remaining()), header.data_size, offset));
ret.emplace_back(phosg::format_data(sub_r.getv(sub_r.remaining()), header.data_size, offset));
}
if (floor_sections.random_enemy_definitions != 0xFFFFFFFF) {
ret.emplace_back(string_printf(".random_enemy_definitions %zu", floor));
ret.emplace_back(phosg::string_printf(".random_enemy_definitions %zu", floor));
const auto& header = r.pget<SectionHeader>(floor_sections.random_enemy_definitions);
size_t offset = floor_sections.random_enemy_definitions + sizeof(SectionHeader);
auto sub_r = r.sub(offset, header.data_size);
ret.emplace_back(format_data(sub_r.getv(sub_r.remaining()), header.data_size, offset));
ret.emplace_back(phosg::format_data(sub_r.getv(sub_r.remaining()), header.data_size, offset));
}
}
return join(ret, "\n") + "\n";
return phosg::join(ret, "\n") + "\n";
}
SetDataTableBase::SetDataTableBase(Version version) : version(version) {}
@@ -1925,21 +1956,19 @@ SetDataTable::SetDataTable(Version version, const string& data) : SetDataTableBa
}
}
template <bool IsBigEndian>
template <bool BE>
void SetDataTable::load_table_t(const string& data) {
using U32T = typename conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
StringReader r(data);
phosg::StringReader r(data);
struct Footer {
U32T table3_offset;
U32T table3_count; // In le_uint16_ts (so *2 for size in bytes)
U32T unknown_a3; // == 1
U32T unknown_a4; // == 0
U32T root_table_offset_offset;
U32T unknown_a6; // == 0
U32T unknown_a7; // == 0
U32T unknown_a8; // == 0
U32T<BE> table3_offset;
U32T<BE> table3_count; // In le_uint16_ts (so *2 for size in bytes)
U32T<BE> unknown_a3; // == 1
U32T<BE> unknown_a4; // == 0
U32T<BE> root_table_offset_offset;
U32T<BE> unknown_a6; // == 0
U32T<BE> unknown_a7; // == 0
U32T<BE> unknown_a8; // == 0
} __packed_ws__(Footer, 0x20);
if (r.size() < sizeof(Footer)) {
@@ -1947,23 +1976,23 @@ void SetDataTable::load_table_t(const string& data) {
}
auto& footer = r.pget<Footer>(r.size() - sizeof(Footer));
uint32_t root_table_offset = r.pget<U32T>(footer.root_table_offset_offset);
uint32_t root_table_offset = r.pget<U32T<BE>>(footer.root_table_offset_offset);
auto root_r = r.sub(root_table_offset, footer.root_table_offset_offset - root_table_offset);
while (!root_r.eof()) {
auto& var1_v = this->entries.emplace_back();
uint32_t var1_table_offset = root_r.template get<U32T>();
uint32_t var1_table_count = root_r.template get<U32T>();
uint32_t var1_table_offset = root_r.template get<U32T<BE>>();
uint32_t var1_table_count = root_r.template get<U32T<BE>>();
auto var1_r = r.sub(var1_table_offset, var1_table_count * 0x08);
while (!var1_r.eof()) {
auto& var2_v = var1_v.emplace_back();
uint32_t var2_table_offset = var1_r.get<U32T>();
uint32_t var2_table_count = var1_r.get<U32T>();
uint32_t var2_table_offset = var1_r.get<U32T<BE>>();
uint32_t var2_table_count = var1_r.get<U32T<BE>>();
auto var2_r = r.sub(var2_table_offset, var2_table_count * 0x0C);
while (!var2_r.eof()) {
auto& entry = var2_v.emplace_back();
entry.object_list_basename = r.pget_cstr(var2_r.get<U32T>());
entry.enemy_and_event_list_basename = r.pget_cstr(var2_r.get<U32T>());
entry.area_setup_filename = r.pget_cstr(var2_r.get<U32T>());
entry.object_list_basename = r.pget_cstr(var2_r.get<U32T<BE>>());
entry.enemy_and_event_list_basename = r.pget_cstr(var2_r.get<U32T<BE>>());
entry.area_setup_filename = r.pget_cstr(var2_r.get<U32T<BE>>());
}
}
}
@@ -2066,19 +2095,19 @@ string SetDataTable::map_filename_for_variation(
string SetDataTable::str() const {
vector<string> lines;
lines.emplace_back(string_printf("FL/V1/V2 => ----------------------OBJECT -----------------ENEMY+EVENT -----------------------SETUP\n"));
lines.emplace_back(phosg::string_printf("FL/V1/V2 => ----------------------OBJECT -----------------ENEMY+EVENT -----------------------SETUP\n"));
for (size_t a = 0; a < this->entries.size(); a++) {
const auto& v1_v = this->entries[a];
for (size_t v1 = 0; v1 < v1_v.size(); v1++) {
const auto& v2_v = v1_v[v1];
for (size_t v2 = 0; v2 < v2_v.size(); v2++) {
const auto& e = v2_v[v2];
lines.emplace_back(string_printf("%02zX/%02zX/%02zX => %28s %28s %28s\n",
lines.emplace_back(phosg::string_printf("%02zX/%02zX/%02zX => %28s %28s %28s\n",
a, v1, v2, e.object_list_basename.c_str(), e.enemy_and_event_list_basename.c_str(), e.area_setup_filename.c_str()));
}
}
}
return join(lines, "");
return phosg::join(lines, "");
}
struct AreaMapFileInfo {
@@ -2515,10 +2544,10 @@ vector<string> map_filenames_for_variation_deprecated(
string filename = "map_";
filename += a.name_token;
if (!a.variation1_values.empty()) {
filename += string_printf("_%02" PRIX32, a.variation1_values.at(var1));
filename += phosg::string_printf("_%02" PRIX32, a.variation1_values.at(var1));
}
if (!a.variation2_values.empty()) {
filename += string_printf("_%02" PRIX32, a.variation2_values.at(var2));
filename += phosg::string_printf("_%02" PRIX32, a.variation2_values.at(var2));
}
vector<string> ret;
+13 -9
View File
@@ -200,9 +200,9 @@ struct Map {
uint32_t kondrieu; // {SAINT_MILLION, SHAMBERTIN} -> KONDRIEU
RareEnemyRates(uint32_t enemy_rate, uint32_t boss_rate);
explicit RareEnemyRates(const JSON& json);
explicit RareEnemyRates(const phosg::JSON& json);
JSON json() const;
phosg::JSON json() const;
};
static const std::shared_ptr<const RareEnemyRates> NO_RARE_ENEMIES;
@@ -249,6 +249,7 @@ struct Map {
EnemyType type;
uint8_t floor;
uint8_t server_flags;
uint16_t alias_entity_id;
Enemy(
uint16_t enemy_id,
@@ -257,7 +258,8 @@ struct Map {
uint8_t floor,
uint16_t section,
uint16_t wave_number,
EnemyType type);
EnemyType type,
uint16_t alias_entity_id);
std::string str() const;
@@ -295,7 +297,7 @@ struct Map {
DATParserRandomState(uint32_t rare_seed);
size_t rand_int_biased(size_t min_v, size_t max_v);
uint32_t next_location_index();
void generate_shuffled_location_table(const Map::RandomEnemyLocationsHeader& header, StringReader r, uint16_t section);
void generate_shuffled_location_table(const Map::RandomEnemyLocationsHeader& header, phosg::StringReader r, uint16_t section);
};
Map(Version version, uint32_t lobby_id, uint32_t rare_seed, std::shared_ptr<PSOLFGEncryption> opt_rand_crypt);
@@ -327,9 +329,9 @@ struct Map {
uint8_t difficulty,
uint8_t event,
uint8_t floor,
StringReader wave_events_r,
StringReader random_enemy_locations_r,
StringReader random_enemy_definitions_r,
phosg::StringReader wave_events_r,
phosg::StringReader random_enemy_locations_r,
phosg::StringReader random_enemy_definitions_r,
std::shared_ptr<DATParserRandomState> random_state,
std::shared_ptr<const RareEnemyRates> rare_rates = DEFAULT_RARE_ENEMIES);
@@ -361,6 +363,8 @@ struct Map {
size_t size,
std::shared_ptr<const RareEnemyRates> rare_rates = Map::DEFAULT_RARE_ENEMIES);
const Enemy& find_enemy(uint16_t enemy_id) const;
Enemy& find_enemy(uint16_t enemy_id);
const Enemy& find_enemy(uint8_t floor, EnemyType type) const;
Enemy& find_enemy(uint8_t floor, EnemyType type);
std::vector<Object*> get_objects(uint8_t floor, uint16_t section, uint16_t wave_number);
@@ -373,7 +377,7 @@ struct Map {
static std::string disassemble_wave_events_data(const void* data, size_t size, uint8_t floor = 0xFF);
static std::string disassemble_quest_data(const void* data, size_t size);
PrefixedLogger log;
phosg::PrefixedLogger log;
Version version;
uint32_t rare_seed;
std::shared_ptr<PSOLFGEncryption> opt_rand_crypt;
@@ -438,7 +442,7 @@ public:
std::string str() const;
private:
template <bool IsBigEndian>
template <bool BE>
void load_table_t(const std::string& data);
// Indexes are [floor][variation1][variation2]
+8 -7
View File
@@ -80,13 +80,14 @@ constexpr uint32_t SWITCH_ASSIST = 0xAA0707AA;
constexpr uint32_t BLOCK_EVENTS = 0xAA0808AA;
constexpr uint32_t BLOCK_PATCHES = 0xAA0909AA;
constexpr uint32_t SAVE_FILES = 0xAA0A0AAA;
constexpr uint32_t RED_NAME = 0xAA0B0BAA;
constexpr uint32_t BLANK_NAME = 0xAA0C0CAA;
constexpr uint32_t SUPPRESS_LOGIN = 0xAA0D0DAA;
constexpr uint32_t SKIP_CARD = 0xAA0E0EAA;
constexpr uint32_t EP3_INFINITE_MESETA = 0xAA0F0FAA;
constexpr uint32_t EP3_INFINITE_TIME = 0xAA1010AA;
constexpr uint32_t EP3_UNMASK_WHISPERS = 0xAA1111AA;
constexpr uint32_t VIRTUAL_CLIENT = 0xAA0B0BAA;
constexpr uint32_t RED_NAME = 0xAA0C0CAA;
constexpr uint32_t BLANK_NAME = 0xAA0D0DAA;
constexpr uint32_t SUPPRESS_LOGIN = 0xAA0E0EAA;
constexpr uint32_t SKIP_CARD = 0xAA0F0FAA;
constexpr uint32_t EP3_INFINITE_MESETA = 0xAA1010AA;
constexpr uint32_t EP3_INFINITE_TIME = 0xAA1111AA;
constexpr uint32_t EP3_UNMASK_WHISPERS = 0xAA1212AA;
} // namespace ProxyOptionsMenuItemID
namespace TeamRewardMenuItemID {
+6 -6
View File
@@ -19,8 +19,8 @@ using namespace std;
uint32_t resolve_address(const char* address) {
struct addrinfo* res0;
if (getaddrinfo(address, nullptr, nullptr, &res0)) {
auto e = string_for_error(errno);
throw runtime_error(string_printf(
auto e = phosg::string_for_error(errno);
throw runtime_error(phosg::string_printf(
"can\'t resolve hostname %s: %s", address, e.c_str()));
}
@@ -32,7 +32,7 @@ uint32_t resolve_address(const char* address) {
}
}
if (!res4) {
throw runtime_error(string_printf(
throw runtime_error(phosg::string_printf(
"can\'t resolve hostname %s: no usable data", address));
}
@@ -43,8 +43,8 @@ uint32_t resolve_address(const char* address) {
map<string, uint32_t> get_local_addresses() {
struct ifaddrs* ifa_raw;
if (getifaddrs(&ifa_raw)) {
auto s = string_for_error(errno);
throw runtime_error(string_printf("failed to get interface addresses: %s", s.c_str()));
auto s = phosg::string_for_error(errno);
throw runtime_error(phosg::string_printf("failed to get interface addresses: %s", s.c_str()));
}
unique_ptr<struct ifaddrs, void (*)(struct ifaddrs*)> ifa(ifa_raw, freeifaddrs);
@@ -80,7 +80,7 @@ bool is_local_address(const sockaddr_storage& daddr) {
}
string string_for_address(uint32_t address) {
return string_printf("%hhu.%hhu.%hhu.%hhu",
return phosg::string_printf("%hhu.%hhu.%hhu.%hhu",
static_cast<uint8_t>(address >> 24), static_cast<uint8_t>(address >> 16),
static_cast<uint8_t>(address >> 8), static_cast<uint8_t>(address));
}

Some files were not shown because too many files have changed in this diff Show More