Skip to content

Commit

Permalink
Macready Golf
Browse files Browse the repository at this point in the history
  • Loading branch information
Waterdish committed Aug 25, 2024
1 parent bf9e3ed commit e9dd4e4
Show file tree
Hide file tree
Showing 65 changed files with 942 additions and 321 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ android {
}
minSdkVersion 18
targetSdkVersion 31
versionCode 5
versionName "1.2.1"
versionCode 6
versionName "1.2.2"
externalNativeBuild {
//ndkBuild {
// arguments "APP_PLATFORM=android-23"
Expand Down
4 changes: 2 additions & 2 deletions app/jni/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20 ")

project(Game VERSION 8.0.5 LANGUAGES C CXX)
set(PROJECT_BUILD_NAME "MacReady Foxtrot" CACHE STRING "")
project(Game VERSION 8.0.6 LANGUAGES C CXX)
set(PROJECT_BUILD_NAME "MacReady Golf" CACHE STRING "")
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")

add_subdirectory(src)
Expand Down
3 changes: 1 addition & 2 deletions app/jni/src/CMake/Packaging-2.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ if (CPACK_GENERATOR MATCHES "Bundle")
set(CPACK_BUNDLE_NAME "soh")
set(CPACK_BUNDLE_PLIST "macosx/Info.plist")
set(CPACK_BUNDLE_ICON "macosx/soh.icns")
set(CPACK_BUNDLE_STARTUP_COMMAND "../soh/macosx/soh-macos.sh")
set(CPACK_BUNDLE_STARTUP_COMMAND "macosx/soh-macos.sh")
set(CPACK_BUNDLE_APPLE_CERT_APP "-")
endif()

16 changes: 13 additions & 3 deletions app/jni/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")

set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")

project(Ship VERSION 8.0.5 LANGUAGES C CXX)
set(PROJECT_BUILD_NAME "MacReady Foxtrot" CACHE STRING "")
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")
project(Ship VERSION 8.0.6 LANGUAGES C CXX)
set(PROJECT_BUILD_NAME "MacReady Golf" CACHE STRING "" FORCE)
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "" FORCE)

set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)
add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/MP>)
Expand Down Expand Up @@ -117,6 +117,16 @@ install(FILES "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/ObjectList_OoTMqDbg.txt" DEST
install(FILES "${CMAKE_SOURCE_DIR}/OTRExporter/CFG/SymbolMap_OoTMqDbg.txt" DESTINATION ./assets/extractor/symbols COMPONENT ship)
endif()

if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Android")
file(COPY "${CMAKE_SOURCE_DIR}/src/soh/assets/extractor/" DESTINATION "${CMAKE_SOURCE_DIR}/../src/main/assets/assets/extractor")
file(COPY "${CMAKE_SOURCE_DIR}/src/soh/assets/xml/" DESTINATION "${CMAKE_SOURCE_DIR}/../src/main/assets/assets/extractor/xmls")
file(COPY "${CMAKE_SOURCE_DIR}/src/OTRExporter/CFG/filelists/" DESTINATION "${CMAKE_SOURCE_DIR}/../src/main/assets/assets/extractor/filelists")
file(COPY "${CMAKE_SOURCE_DIR}/src/OTRExporter/CFG/ActorList_OoTMqDbg.txt" DESTINATION "${CMAKE_SOURCE_DIR}/../src/main/assets/assets/extractor/symbols")
file(COPY "${CMAKE_SOURCE_DIR}/src/OTRExporter/CFG/ObjectList_OoTMqDbg.txt" DESTINATION "${CMAKE_SOURCE_DIR}/../src/main/assets/assets/extractor/symbols")
file(COPY "${CMAKE_SOURCE_DIR}/src/OTRExporter/CFG/SymbolMap_OoTMqDbg.txt" DESTINATION "${CMAKE_SOURCE_DIR}/../src/main/assets/assets/extractor/symbols")
endif()


find_package(Python3 COMPONENTS Interpreter)

# Target to generate OTRs
Expand Down
20 changes: 11 additions & 9 deletions app/jni/src/OTRExporter/OTRExporter/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,18 @@ static void ExporterProgramEnd()

for (const auto& item : lst)
{
std::vector<std::string> splitPath = StringHelper::Split(item, ".");
size_t filenameSepAt = item.find_last_of("/\\");
const std::string filename = item.substr(filenameSepAt + 1);

if (splitPath.size() >= 3)
if (std::count(filename.begin(), filename.end(), '.') >= 2)
{
const std::string extension = splitPath.at(splitPath.size() - 1);
const std::string format = splitPath.at(splitPath.size() - 2);
splitPath.pop_back();
splitPath.pop_back();
std::string afterPath = std::accumulate(splitPath.begin(), splitPath.end(), std::string(""));
size_t extensionSepAt = filename.find_last_of(".");
size_t formatSepAt = filename.find_last_of(".", extensionSepAt - 1);

const std::string extension = filename.substr(extensionSepAt + 1);
const std::string format = filename.substr(formatSepAt + 1, extensionSepAt - formatSepAt - 1);
std::string afterPath = item.substr(0, filenameSepAt + formatSepAt + 1);

if (extension == "png" && (format == "rgba32" || format == "rgb5a1" || format == "i4" || format == "i8" || format == "ia4" || format == "ia8" || format == "ia16" || format == "ci4" || format == "ci8"))
{
ZTexture tex(nullptr);
Expand All @@ -208,8 +211,7 @@ static void ExporterProgramEnd()

if (item.find("accessibility") != std::string::npos)
{
std::string extension = splitPath.at(splitPath.size() - 1);
splitPath.pop_back();
std::string extension = filename.substr(filename.find_last_of(".") + 1);
if (extension == "json")
{
const auto &fileData = DiskFile::ReadAllBytes(item);
Expand Down
9 changes: 9 additions & 0 deletions app/jni/src/docs/BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ cd Shipwright
# If you need to regenerate the asset headers to check them into source
& 'C:\Program Files\CMake\bin\cmake.exe' --build .\build\x64 --target ExtractAssetHeaders
# If you need a newer soh.otr only
& 'C:\Program Files\CMake\bin\cmake.exe' --build .\build\x64 --target GenerateSohOtr
```

### Developing SoH
Expand Down Expand Up @@ -110,6 +113,9 @@ cmake --build build-cmake --target clean

# If you need to regenerate the asset headers to check them into source
cmake --build build-cmake --target ExtractAssetHeaders

# If you need a newer soh.otr only
cmake --build build-cmake --target GenerateSohOtr
```

### Generating a distributable
Expand Down Expand Up @@ -157,6 +163,9 @@ cmake --build build-cmake --target clean

# If you need to regenerate the asset headers to check them into source
cmake --build build-cmake --target ExtractAssetHeaders

# If you need a newer soh.otr only
cmake --build build-cmake --target GenerateSohOtr
```

### Generating a distributable
Expand Down
31 changes: 31 additions & 0 deletions app/jni/src/docs/GAME_CONTROLLER_DB.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# SDL GameControllerDB

The Ship of Harkinian utilizes a text file with SDL controller mappings for extended controller hardware support.
This file is pulled from https://github.com/gabomdq/SDL_GameControllerDB during the build process as [a part of CMakeLists.txt](https://github.com/HarbourMasters/Shipwright/blob/bb643661f62865dfc757c185d0daaebb32f2d53d/soh/CMakeLists.txt#L760).

## Released versions

| Release | sha | diff |
| - | - | - |
| Zhora Alfa 4.0.0 | [967daa8](https://github.com/gabomdq/SDL_GameControllerDB/tree/967daa8f89c48b01ed0f9c6a86ac849930442fc6) | |
| Zhora Bravo 4.0.1 | [ccac7cd](https://github.com/gabomdq/SDL_GameControllerDB/tree/ccac7cd97f445955d4437e21c5f82123d9b4349b) | [+1](https://github.com/gabomdq/SDL_GameControllerDB/compare/967daa8...ccac7cd) |
| Zhora Charlie 4.0.2 | [ff26eb0](https://github.com/gabomdq/SDL_GameControllerDB/tree/ff26eb04d0fe18356985d968119429d6012e7d75) | [+8/-3](https://github.com/gabomdq/SDL_GameControllerDB/compare/ccac7cd...ff26eb0) |
| Zhora Delta 4.0.3 | [ad02da5](https://github.com/gabomdq/SDL_GameControllerDB/tree/ad02da5a95ca8005f2c1facc11a5a52f8522f0ee) | [+4/-5](https://github.com/gabomdq/SDL_GameControllerDB/compare/ff26eb0...ad02da5) |
| Zohra Echo 4.0.4 | [c203690](https://github.com/gabomdq/SDL_GameControllerDB/tree/c203690b1e13980699802918d362cd9dadf89bd0) | [+8/-4](https://github.com/gabomdq/SDL_GameControllerDB/compare/ad02da5...c203690) |
| Zhora Foxtrot 4.0.5 | [9db8101](https://github.com/gabomdq/SDL_GameControllerDB/tree/9db8101a5780d1b0721bf6de385e6ffe0d07dfc7) | [+6](https://github.com/gabomdq/SDL_GameControllerDB/compare/c203690...9db8101) |
| Flynn Alfa 5.0.0 | [163cc5d](https://github.com/gabomdq/SDL_GameControllerDB/tree/163cc5d45e9fc2f1bb2b95ea7eee4bbc9a57955c) | [+29/-8](https://github.com/gabomdq/SDL_GameControllerDB/compare/9db8101...163cc5d) |
| Flynn Bravo 5.0.1 | [7efce7d](https://github.com/gabomdq/SDL_GameControllerDB/tree/7efce7d3f309ec1fa409b1af09153f9eb77fbedf) | [-1](https://github.com/gabomdq/SDL_GameControllerDB/compare/163cc5d...7efce7d) |
| Flynn Charlie 5.0.2 | [e607703](https://github.com/gabomdq/SDL_GameControllerDB/tree/e607703392145343e8aca42be052121c0b7bd1c9) | [+40/-17](https://github.com/gabomdq/SDL_GameControllerDB/compare/7efce7d...e607703) |
| Bradley Alfa 5.1.0 | [2ba9676](https://github.com/gabomdq/SDL_GameControllerDB/tree/2ba96761af795c15e916cc97790b51e09dc0cd54) | [+1/-1](https://github.com/gabomdq/SDL_GameControllerDB/compare/e607703...2ba9676) |
| Bradley Charlie 5.1.2 | [4f5d1d4](https://github.com/gabomdq/SDL_GameControllerDB/tree/4f5d1d497985b75f4a83a5de46f596dc4d7f002e) | [+5/-1](https://github.com/gabomdq/SDL_GameControllerDB/compare/2ba9676...4f5d1d4) |
| Bradley Delta 5.1.3 | [9b73049](https://github.com/gabomdq/SDL_GameControllerDB/tree/9b73049ee62a2cc862d6ad94c2c777f2e8363a48) | [+4/-1](https://github.com/gabomdq/SDL_GameControllerDB/compare/4f5d1d4...9b73049) |
| Bradley Echo 5.1.4 | [6d3801f](https://github.com/gabomdq/SDL_GameControllerDB/tree/6d3801fcfe74b1989de96403b7b560eba72a175c) | [+56/-21](https://github.com/gabomdq/SDL_GameControllerDB/compare/9b73049...6d3801f) |
| Gibbs Alfa 6.0.0 | [0562b00](https://github.com/gabomdq/SDL_GameControllerDB/tree/0562b00eaf5c0308c49d329b79263d2dae1c3a85) | [+8/-2](https://github.com/gabomdq/SDL_GameControllerDB/compare/6d3801f...0562b00) |
| Khan Alfa 6.1.0 | [436c7e3](https://github.com/gabomdq/SDL_GameControllerDB/tree/436c7e3d54a57189ea0ab44d05f36b7cc7ea496c) | [+31/-16](https://github.com/gabomdq/SDL_GameControllerDB/compare/0562b00...436c7e3) |
| Khan Bravo 6.1.1 | [01cca2e](https://github.com/gabomdq/SDL_GameControllerDB/tree/01cca2e77f9bf9f1432be04f876f287eb78297fe) | [+23/-6](https://github.com/gabomdq/SDL_GameControllerDB/compare/436c7e3...01cca2e) |
| Khan Charlie 6.1.2 | [6852946](https://github.com/gabomdq/SDL_GameControllerDB/tree/6852946487534c69b7d228fd4eb8c87cf6966475) | [+25/-15](https://github.com/gabomdq/SDL_GameControllerDB/compare/01cca2e...6852946) |
| Spock Alfa 7.0.0 | [38bda81](https://github.com/gabomdq/SDL_GameControllerDB/tree/38bda816dc786f18493876f7bc30bc12dfd2636a) | [+15/-1](https://github.com/gabomdq/SDL_GameControllerDB/compare/6852946...38bda81) |
| Spock Bravo 7.0.1 | [228d980](https://github.com/gabomdq/SDL_GameControllerDB/tree/228d980d3d791e9df3b096472f6b97459f8709fe) | [+7/-3](https://github.com/gabomdq/SDL_GameControllerDB/compare/38bda81...228d980) |
| Spock Charlie 7.0.2 | [c5b4df0](https://github.com/gabomdq/SDL_GameControllerDB/tree/c5b4df0e1061175cb11e3ebbf8045178339864a5) | [+3](https://github.com/gabomdq/SDL_GameControllerDB/compare/228d980...c5b4df0) |
| Sulu Alfa 7.1.0 | [a2cf171](https://github.com/gabomdq/SDL_GameControllerDB/tree/a2cf1711b4ebc646a3814705d2fb6aac5707bcae) | [+4/-1](https://github.com/gabomdq/SDL_GameControllerDB/compare/c5b4df0...a2cf171) |
| Sulu Bravo 7.1.1 | [cc9f777](https://github.com/gabomdq/SDL_GameControllerDB/tree/cc9f777721f0cb30058d9eef52a295130b734a4a) | [+29/-9](https://github.com/gabomdq/SDL_GameControllerDB/compare/a2cf171...cc9f777) |
2 changes: 1 addition & 1 deletion app/jni/src/libultraship
203 changes: 203 additions & 0 deletions app/jni/src/scripts/linux/appimage/soh.sh.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#!/bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"/../..

export PATH="$HERE"/bin:"$HERE"/usr/bin:"$PATH"
export LD_LIBRARY_PATH="$HERE"/usr/lib:"$LD_LIBRARY_PATH"
export ZENITY=$(command -v zenity)

if [ -z ${SHIP_HOME+x} ]; then
export SHIP_HOME=$PWD
fi

if [ -z ${SHIP_BIN_DIR+x} ]; then
export SHIP_BIN_DIR="$HERE/usr/bin"
fi

if [[ ! -e "$SHIP_HOME"/mods ]]; then
mkdir -p "$SHIP_HOME"/mods
touch "$SHIP_HOME"/mods/custom_otr_files_go_here.txt
fi

while [[ (! -e "$SHIP_HOME"/oot.otr) || (! -e "$SHIP_HOME"/oot-mq.otr) ]]; do
for romfile in "$SHIP_HOME"/*.*64
do
if [[ -e $romfile ]]; then
export ASSETDIR="$(mktemp -d /tmp/assets-XXXXX)"
ln -s "$HERE"/usr/bin/{assets,soh.elf,ZAPD} "$ASSETDIR"
export OLDPWD="$PWD"
mkdir -p "$ASSETDIR"/tmp
ln -s "$romfile" "$ASSETDIR"/tmp/rom.z64
cd "$ASSETDIR"
ROMHASH=$(sha1sum -b "$ASSETDIR"/tmp/rom.z64 | awk '{ print $1 }')

# Remap v64 and n64 hashes to their z64 hash equivalent
# ZAPD will handle converting the data into z64 format
case "$ROMHASH" in
a9059b56e761c9034fbe02fe4c24985aaa835dac) # v64
ROMHASH=cee6bc3c2a634b41728f2af8da54d9bf8cc14099
;;
24708102dc504d3f375a37f4ae4e149c167dc515) # n64
ROMHASH=cee6bc3c2a634b41728f2af8da54d9bf8cc14099
;;
580dd0bd1b6d2c51cc20a764eece84dba558964c) # v64
ROMHASH=0227d7c0074f2d0ac935631990da8ec5914597b4
;;
d6342c59007e57c1194661ec6880b2f078403f4e) # n64
ROMHASH=0227d7c0074f2d0ac935631990da8ec5914597b4
;;
d0bdc2eb320668b4ba6893b9aefe4040a73123ff) # v64
ROMHASH=328a1f1beba30ce5e178f031662019eb32c5f3b5
;;
4946ab250f6ac9b32d76b21f309ebb8ebc8103d2) # n64
ROMHASH=328a1f1beba30ce5e178f031662019eb32c5f3b5
;;
663c34f1b2c05a09e5beffe4d0dcd440f7d49dc7) # v64
ROMHASH=cfbb98d392e4a9d39da8285d10cbef3974c2f012
;;
24c73d378b0620a380ce5ef9f2b186c6c157a68b) # n64
ROMHASH=cfbb98d392e4a9d39da8285d10cbef3974c2f012
;;
8ebf2e29313f44f2d49e5b4191971d09919e8e48) # v64
ROMHASH=f46239439f59a2a594ef83cf68ef65043b1bffe2
;;
4264bf7b875737b8fae77d52322a5099d051fc11) # n64
ROMHASH=f46239439f59a2a594ef83cf68ef65043b1bffe2
;;
973bc6fe56010a8d646166a1182a81b4f13b8cf9) # v64
ROMHASH=50bebedad9e0f10746a52b07239e47fa6c284d03
;;
d327752c46edc70ff3668b9514083dbbee08927c) # v64
ROMHASH=50bebedad9e0f10746a52b07239e47fa6c284d03
;;
ecdeb1747560834e079c22243febea7f6f26ba3b) # v64
ROMHASH=079b855b943d6ad8bd1eb026c0ed169ecbdac7da
;;
f19f8662ec7abee29484a272a6fda53e39efe0f1) # n64
ROMHASH=079b855b943d6ad8bd1eb026c0ed169ecbdac7da
;;
ab519ce04a33818ce2c39b3c514a751d807a494a) # v64
ROMHASH=cfecfdc58d650e71a200c81f033de4e6d617a9f6
;;
c19a34f7646305e1755249fca2071e178bd7cd00) # n64
ROMHASH=cfecfdc58d650e71a200c81f033de4e6d617a9f6
;;
25e8ae79ea0839ca5c984473f7460d8040c36f9c) # v64
ROMHASH=517bd9714c73cb96c21e7c2ef640d7b55186102f
;;
166c02770d67fcc3954c443eb400a6a3573d3fc0) # n64
ROMHASH=517bd9714c73cb96c21e7c2ef640d7b55186102f
;;
esac

case "$ROMHASH" in
cee6bc3c2a634b41728f2af8da54d9bf8cc14099)
if [[ ! -e "$SHIP_HOME"/oot.otr ]]; then
ROM=GC_NMQ_D
OTRNAME="oot.otr"
fi
;;
0227d7c0074f2d0ac935631990da8ec5914597b4)
if [[ ! -e "$SHIP_HOME"/oot.otr ]]; then
ROM=GC_NMQ_PAL_F
OTRNAME="oot.otr"
else
continue
fi
;;
328a1f1beba30ce5e178f031662019eb32c5f3b5)
if [[ ! -e "$SHIP_HOME"/oot.otr ]]; then
ROM=N64_PAL_10
OTRNAME="oot.otr"
else
continue
fi
;;
cfbb98d392e4a9d39da8285d10cbef3974c2f012)
if [[ ! -e "$SHIP_HOME"/oot.otr ]]; then
ROM=N64_PAL_11
OTRNAME="oot.otr"
else
continue
fi
;;
f46239439f59a2a594ef83cf68ef65043b1bffe2)
if [[ ! -e "$SHIP_HOME"/oot-mq.otr ]]; then
ROM=GC_MQ_PAL_F
OTRNAME="oot-mq.otr"
else
continue
fi
;;
50bebedad9e0f10746a52b07239e47fa6c284d03)
if [[ ! -e "$SHIP_HOME"/oot-mq.otr ]]; then
ROM=GC_MQ_D
OTRNAME="oot-mq.otr"
else
continue
fi
;;
079b855b943d6ad8bd1eb026c0ed169ecbdac7da)
if [[ ! -e "$SHIP_HOME"/oot-mq.otr ]]; then
ROM=GC_MQ_D
OTRNAME="oot-mq.otr"
else
continue
fi
;;
cfecfdc58d650e71a200c81f033de4e6d617a9f6)
if [[ ! -e "$SHIP_HOME"/oot-mq.otr ]]; then
ROM=GC_MQ_D
OTRNAME="oot-mq.otr"
else
continue
fi
;;
517bd9714c73cb96c21e7c2ef640d7b55186102f)
if [[ ! -e "$SHIP_HOME"/oot-mq.otr ]]; then
ROM=GC_MQ_D
OTRNAME="oot-mq.otr"
else
continue
fi
;;
*)
echo -e "\n$romfile - $ROMHASH rom hash does not match\n"
continue;;
esac
if [[ ! -e "$SHIP_HOME"/"$OTRNAME" ]]; then
if [ -n "$ZENITY" ]; then
(echo "# 25%"; echo "25"; sleep 2; echo "# 50%"; echo "50"; sleep 3; echo "# 75%"; echo "75"; sleep 2; echo "# 100%"; echo "100"; sleep 3) |
zenity --progress --title="OTR Generating..." --timeout=10 --percentage=0 --icon-name=soh --window-icon=soh.png --height=80 --width=400 &
else
echo "Processing..."
fi
assets/extractor/ZAPD.out ed -eh -i assets/extractor/xmls/"${ROM}" -b tmp/rom.z64 -fl assets/extractor/filelists -o placeholder -osf placeholder -gsf 1 -rconf assets/extractor/Config_"${ROM}".xml -se OTR --otrfile "${OTRNAME}" --portVer "@CMAKE_PROJECT_VERSION@" > /dev/null 2>&1
cp "$ASSETDIR"/"$OTRNAME" "$SHIP_HOME"
fi
else
if [[ (! -e "$SHIP_HOME"/oot.otr) && (! -e "$SHIP_HOME"/oot-mq.otr) ]]; then
if [ -n "$ZENITY" ]; then
zenity --error --timeout=5 --text="Place ROM in $SHIP_HOME" --title="Missing ROM file" --width=500 --width=200
else
echo -e "\nPlace ROM in this folder\n"
fi
exit
fi
fi
done
if [[ (! -e "$SHIP_HOME"/oot.otr) && (! -e "$SHIP_HOME"/oot-mq.otr) ]]; then
if [ -n "$ZENITY" ]; then
zenity --error --timeout=10 --text="No valid ROMs were provided, No OTR was generated." --title="Incorrect ROM file" --width=500 --width=200
else
echo "No valid roms provided, no OTR was generated."
fi
rm -r "$ASSETDIR"
exit
else
(cd "$SHIP_BIN_DIR"; ./soh.elf)
exit
fi
rm -r "$ASSETDIR"
done
(cd "$SHIP_BIN_DIR"; ./soh.elf)
exit
Loading

0 comments on commit e9dd4e4

Please sign in to comment.