Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 49992be60a | |||
| 7414b6ce8e | |||
| 591f3c7b36 | |||
| de2df5f6cf | |||
| 4a40dfd361 | |||
| b760bf5066 | |||
| 8e85167cb6 | |||
| af27ea080f | |||
| 65de5d0060 | |||
| a9b816c548 | |||
| 075c576116 | |||
| f9986f5ac5 | |||
| a9a28aa71b | |||
| c6bbd5daa3 | |||
| c89c3c27ad | |||
| 3205afbcdb | |||
| 61003b509a | |||
| ce3f25be7b | |||
| a8fd1bdada | |||
| 4426476a15 | |||
| 7d775a38d1 | |||
| a7d3720050 | |||
| 596ea40bc0 | |||
| f8f194e19b | |||
| 170111422b | |||
| 81969fc91b | |||
| f0366a3550 | |||
| d676e9bb38 | |||
| 188aac48eb | |||
| 24be0d8195 | |||
| fbc5cd5967 | |||
| d11329b2c9 | |||
| 3a74dbf04e | |||
| 299e187380 | |||
| 0f29b1801d | |||
| f8162d442a | |||
| cd09bfa7e8 | |||
| 1bfbf09891 | |||
| 5523388ad4 | |||
| a3cc0bd13f | |||
| 70ada6669d | |||
| 4d76229527 | |||
| 5ea3d0ad4b | |||
| 90efde7aa9 | |||
| 55f1869125 | |||
| b4efd90fdc | |||
| 87dd554592 | |||
| 58974ae1be | |||
| 21c8bab91c | |||
| c58b37be23 | |||
| d3d98c44b8 | |||
| dc2e73d198 | |||
| 774f9649da | |||
| 093287af75 | |||
| 0126189cbd | |||
| c250a2dbc4 | |||
| 2ff9df19c8 | |||
| 528593651b | |||
| 9f073d07cd | |||
| 4bd6ef12a9 | |||
| 52644695a3 | |||
| 45e619718c | |||
| 43fd979763 | |||
| 082bc49a4d | |||
| 4adcaa7bee | |||
| 630ae0beb4 | |||
| 246dfd9fe0 | |||
| 6f056cb1bd | |||
| 9322c023da | |||
| fd4719f8ec | |||
| 3a22a5c489 | |||
| 862b3d27da | |||
| 998664d2fb | |||
| 0bf2d950ac | |||
| 3ae5e875a1 | |||
| a88795d8b9 | |||
| 9ca1b79409 | |||
| ce8277b96a | |||
| 25731eb71f | |||
| e55963b82b | |||
| b9d9b38351 | |||
| 782babf3ae | |||
| 9869fa03c2 | |||
| 0ae02b0191 | |||
| c0ea976fdc | |||
| c4bf9e7d5b | |||
| 2e5d95d612 | |||
| 75b2827da9 |
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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"]
|
||||
@@ -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,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.)
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+41
-11
@@ -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
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Executable → Regular
Binary file not shown.
Executable → Regular
+8
-10
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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, ×tamp, 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
+256
-200
File diff suppressed because it is too large
Load Diff
+67
-32
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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, ðer, sizeof(ether));
|
||||
evbuffer_add(out_buf, data, size);
|
||||
if (this->pcap_text_log_file) {
|
||||
StringWriter w;
|
||||
phosg::StringWriter w;
|
||||
w.write(ðer, 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+115
-86
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user