aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.gitlab-ci.yml196
-rw-r--r--.travis.yml11
-rw-r--r--Attorney_Online.pro7
-rw-r--r--README_BUILD.md34
-rw-r--r--include/aoapplication.h1
-rw-r--r--include/aoevidencebutton.h2
-rw-r--r--include/courtroom.h13
-rw-r--r--include/networkmanager.h7
-rw-r--r--scripts/.gitignore6
-rwxr-xr-xscripts/configure_macos.sh42
-rwxr-xr-xscripts/configure_ubuntu.sh11
-rwxr-xr-xscripts/macos_build.sh33
-rw-r--r--scripts/macos_post_build.sh24
-rw-r--r--scripts/package.json9
-rwxr-xr-xscripts/release_macos.sh22
-rwxr-xr-xscripts/update_manifest.js151
-rwxr-xr-xscripts/update_program_manifest.js39
-rwxr-xr-xscripts/wasabi.sh73
-rwxr-xr-xscripts/wasabi_program.sh35
-rw-r--r--scripts/windows/Dockerfile17
-rw-r--r--scripts/windows/Dockerfile-mxe44
-rw-r--r--scripts/windows/how-to-push.md19
-rw-r--r--src/aoapplication.cpp14
-rw-r--r--src/aoevidencebutton.cpp2
-rw-r--r--src/charselect.cpp28
-rw-r--r--src/courtroom.cpp112
-rw-r--r--src/discord_rich_presence.cpp4
-rw-r--r--src/evidence.cpp10
-rw-r--r--src/hardware_functions.cpp7
-rw-r--r--src/networkmanager.cpp16
-rw-r--r--src/packet_distribution.cpp10
32 files changed, 905 insertions, 95 deletions
diff --git a/.gitignore b/.gitignore
index 02a6b7f8..5119800f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
*.so
*.pro.autosave
base_override.h
+.DS_Store
base-full/
base/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 00000000..cf676152
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,196 @@
+stages:
+ - build
+ - deploy
+ - publish
+
+cache:
+ key: ${CI_COMMIT_REF_SLUG}
+ paths:
+ - lib/
+
+before_script:
+ - echo Current working directory is $(pwd)
+
+build linux x86_64:
+ image: ubuntu
+ stage: build
+ tags:
+ - docker
+ - linux
+ script:
+ # Install dependencies
+ - apt-get update
+ - >
+ apt-get install --no-install-recommends -y qt5-default qtmultimedia5-dev
+ clang make git sudo curl ca-certificates pkg-config upx unzip
+
+ # Print versions
+ - qmake --version
+ - clang --version
+
+ # Extract BASS
+ - mkdir bass
+ - cd bass
+ - curl http://www.un4seen.com/files/bass24-linux.zip -o bass.zip
+ - unzip bass.zip
+ - cp x64/libbass.so ../lib
+ - curl http://www.un4seen.com/files/bassopus24-linux.zip -o bassopus.zip
+ - unzip bassopus.zip
+ - cp x64/libbassopus.so ../lib
+ - cd ..
+
+ # Extract Discord RPC
+ - mkdir discord-rpc
+ - cd discord-rpc
+ - curl -L https://github.com/discordapp/discord-rpc/releases/download/v3.4.0/discord-rpc-linux.zip -o discord_rpc_linux.zip
+ - unzip discord_rpc_linux.zip
+ - cp discord-rpc/linux-dynamic/lib/libdiscord-rpc.so ../lib
+ - cd ..
+
+ # Extract QtApng
+ - mkdir qtapng
+ - cd qtapng
+ - curl -L https://github.com/Skycoder42/QtApng/releases/download/1.1.0-5/build_gcc_64_5.12.0.tar.xz -o apng.tar.xz
+ - tar -xvf apng.tar.xz
+ - cp gcc_64/plugins/imageformats/libqapng.so ../lib
+ - cd ..
+
+ # Build
+ - qmake -spec linux-clang
+ - make -j4
+
+ # Post-processing
+ - upx --lzma -9 --force bin/Attorney_Online
+ artifacts:
+ paths:
+ - bin/
+
+build windows i686:
+ image: ${CI_REGISTRY_IMAGE}/builder-windows-i686
+ stage: build
+ tags:
+ - docker
+ - linux
+ script:
+ # Install dependencies
+ - apt-get update
+ - apt-get install --no-install-recommends -y make curl ca-certificates upx unzip
+
+ # Extract BASS
+ - mkdir bass
+ - cd bass
+ - curl http://www.un4seen.com/files/bass24.zip -o bass.zip
+ - unzip bass.zip
+ - cp bass.dll ../lib
+ - curl http://www.un4seen.com/files/bassopus24.zip -o bassopus.zip
+ - unzip bassopus.zip
+ - cp bassopus.dll ../lib
+ - cd ..
+
+ # Build
+ - /opt/mxe/usr/${TARGET_SPEC}/qt5/bin/qmake
+ - make -j4
+
+ # Post-processing
+ - upx --lzma -9 --force bin/Attorney_Online.exe
+ artifacts:
+ paths:
+ - bin/
+
+# Base folder
+.deploy_base: &deploy_base |
+ mkdir base
+ mkdir base/themes
+ cp -a ../base/themes/default base/themes/
+ cp -a ../base/config.ini base/config.sample.ini
+ cp -a ../base/serverlist.txt base/serverlist.sample.txt
+
+# Miscellaneous files
+.deploy_misc: &deploy_misc |
+ cp -a ../README.md README.md.txt
+ cp -a ../LICENSE.MIT LICENSE.txt
+
+deploy linux x86_64:
+ stage: deploy
+ dependencies:
+ - build linux x86_64
+ tags:
+ - docker
+ - linux
+ script:
+ - mkdir artifact
+ - cd artifact
+ - *deploy_base
+ - *deploy_misc
+
+ # Platform-specific
+ - cp -a ../lib/*.so .
+ - cp -a ../bin/Attorney_Online .
+ - echo "#!/bin/sh" >> ./run.sh
+ - echo "LD_LIBRARY_PATH=.:\$LD_LIBRARY_PATH ./Attorney_Online" >> ./run.sh
+ - chmod +x ./run.sh
+
+ # Zipping
+ # zip -r -9 -l Attorney_Online_$(git describe --tags)_linux_x86_64.zip .
+ - mkdir ../zip
+ - tar cavf ../zip/Attorney_Online_$(git describe --tags)_linux_x64.tar.xz *
+ - sha1sum ../zip/*
+ artifacts:
+ paths:
+ - zip/
+
+deploy windows i686:
+ image: ubuntu
+ stage: deploy
+ dependencies:
+ - build windows i686
+ tags:
+ - docker
+ - linux
+ script:
+ - apt-get update
+ - apt-get install --no-install-recommends -y zip git
+
+ - mkdir artifact
+ - cd artifact
+ - *deploy_base
+ - *deploy_misc
+
+ # Platform-specific
+ - cp -a ../lib/*.dll .
+ - cp -a ../bin/Attorney_Online.exe .
+
+ # Zipping
+ # -r: recursive; -9: max compression; -l: convert to CR LF
+ - mkdir ../zip
+ - zip -r -9 -l ../zip/Attorney_Online_$(git describe --tags)_windows_x86.zip .
+ - sha1sum ../zip/*
+ artifacts:
+ paths:
+ - zip/
+
+publish linux x86_64:
+ image: ubuntu
+ stage: publish
+ dependencies:
+ - deploy linux x86_64
+ when: manual
+ script:
+ - cd zip
+ - ../scripts/wasabi.sh
+ variables:
+ MANIFEST: program_linux_x86_64.json
+ ARTIFACT_SUFFIX: _linux_x64.tar.xz
+
+publish windows i686:
+ image: ubuntu
+ stage: publish
+ dependencies:
+ - deploy windows i686
+ when: manual
+ script:
+ - cd zip
+ - ../scripts/wasabi.sh
+ variables:
+ MANIFEST: program_winnt_i386.json
+ ARTIFACT_SUFFIX: _windows_x86.zip
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..4243b3b9
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+language: cpp
+os: osx
+
+addons:
+ homebrew:
+ packages:
+ - qt5
+
+script:
+ - ./scripts/macos_build.sh
+ - ./scripts/macos_post_build.sh
diff --git a/Attorney_Online.pro b/Attorney_Online.pro
index 351f2b38..f4909173 100644
--- a/Attorney_Online.pro
+++ b/Attorney_Online.pro
@@ -16,13 +16,13 @@ HEADERS += $$files($$PWD/include/*.h)
LIBS += -L$$PWD/lib
-#DEFINES += DISCORD
+DEFINES += DISCORD
contains(DEFINES, DISCORD) {
LIBS += -ldiscord-rpc
}
-#DEFINES += BASSAUDIO
+DEFINES += BASSAUDIO
contains(DEFINES, BASSAUDIO) {
LIBS += -lbass
@@ -34,6 +34,9 @@ contains(DEFINES, QTAUDIO) {
QT += multimedia
}
+macx:LIBS += -framework CoreFoundation -framework Foundation -framework CoreServices
+
+
CONFIG += c++14
RESOURCES += resources.qrc
diff --git a/README_BUILD.md b/README_BUILD.md
index 685699c9..f7441b88 100644
--- a/README_BUILD.md
+++ b/README_BUILD.md
@@ -8,22 +8,40 @@ This program has five main dependencies
* Discord Rich Presence (https://github.com/discordapp/discord-rpc/releases)
* Qt Apng Plugin (https://github.com/Skycoder42/QtApng/releases)
+### Help
+
+If you're having issues with any of this, ask in the offical Discord: https://discord.gg/wWvQ3pw
+Alternatively, you can ask OmniTroid#4004 on Discord.
+
### How to build dynamically (the easy way)
+#### General preparation
+
What you want to do is first download the latest version of Qt from the first link. (get the prebuilt dynamic version)
-If you're on Ubuntu, go to the scripts/ folder and run configure_ubuntu.sh. This should fetch all the required dependencies automatically.
-If not, go to each one of the links above and find the right dynamic library for your platform:
-* Windows: .dll
-* Linux: .so
-* Mac: .dylib
+After going through the OS-specific steps below, compiling in Qt creator should work.
+
+#### Windows
+
+If you're on Windows, you need to go find all the dependencies (see above) and put them in the lib/ folder.
+
+#### MacOS
+
+If you're on MacOS, you can simply go to terminal and run ./scripts/configure_macos.sh
+This will automatically fetch all the required dependencies. Additionally, if you need to create a standalone release, just run ./scripts/release_macos.sh
+This will make the .app bundle in bin/ able to execute as a standalone.
+
+#### Ubuntu
+
+If you're on Ubuntu, just go to terminal and run ./scripts/configure_ubuntu.sh
+This should fetch all the required dependencies automatically.
-And put them in BOTH lib/ and the repository root (lib/ is required for linking and root is required for runtime)
+#### Other Linux
-Launch Qt creator, open the .pro file and try running it. Ask in the Discord if you're having issues: https://discord.gg/wWvQ3pw
+With some tweaks to the ubuntu script, it shouldn't be a big hassle to compile it on a modern linux. Look in the script and see what you may have to modify.
### How to build statically (the hard way)
-You're gonna have a bad time.
+You're gonna have a bad time.
Building statically means you can distribute the final program without needing to pack alongside a lot of dynamic libraries.
This is a tricky process and is not recommended unless you know what you're doing.
diff --git a/include/aoapplication.h b/include/aoapplication.h
index 6c8039cc..78982f14 100644
--- a/include/aoapplication.h
+++ b/include/aoapplication.h
@@ -22,6 +22,7 @@
#include <QTextStream>
#include <QStringList>
#include <QColor>
+#include <QScreen>
class NetworkManager;
class Lobby;
diff --git a/include/aoevidencebutton.h b/include/aoevidencebutton.h
index 27fb84b3..80b747cd 100644
--- a/include/aoevidencebutton.h
+++ b/include/aoevidencebutton.h
@@ -35,8 +35,10 @@ protected:
void enterEvent(QEvent *e);
void leaveEvent(QEvent *e);
void mouseDoubleClickEvent(QMouseEvent *e);
+ /*
void dragLeaveEvent(QMouseEvent *e);
void dragEnterEvent(QMouseEvent *e);
+ */
signals:
void evidence_clicked(int p_id);
diff --git a/include/courtroom.h b/include/courtroom.h
index ec9f9ef0..99a090b0 100644
--- a/include/courtroom.h
+++ b/include/courtroom.h
@@ -35,7 +35,6 @@
#include <QSlider>
#include <QVector>
#include <QCloseEvent>
-#include <QSignalMapper>
#include <QMap>
#include <QTextBrowser>
#include <QSpinBox>
@@ -48,6 +47,7 @@
#include <QFont>
#include <QInputDialog>
#include <QFileDialog>
+#include <QTextBoundaryFinder>
#include <stack>
@@ -272,8 +272,6 @@ private:
QVector<QString> arup_cms;
QVector<QString> arup_locks;
- QSignalMapper *char_button_mapper;
-
QVector<chatlogpiece> ic_chatlog_history;
// These map music row items and area row items to their actual IDs.
@@ -537,6 +535,7 @@ private:
void construct_char_select();
void set_char_select();
void set_char_select_page();
+ void char_clicked(int n_char);
void put_button_in_place(int starting, int chars_on_this_page);
void filter_character_list();
@@ -651,14 +650,12 @@ private slots:
void on_char_select_left_clicked();
void on_char_select_right_clicked();
- void on_char_search_changed(const QString& newtext);
- void on_char_taken_clicked(int newstate);
- void on_char_passworded_clicked(int newstate);
+ void on_char_search_changed();
+ void on_char_taken_clicked();
+ void on_char_passworded_clicked();
void on_spectator_clicked();
- void char_clicked(int n_char);
-
void on_switch_area_music_clicked();
void on_casing_clicked();
diff --git a/include/networkmanager.h b/include/networkmanager.h
index e28abfda..f26e658c 100644
--- a/include/networkmanager.h
+++ b/include/networkmanager.h
@@ -44,13 +44,14 @@ public:
QString ms_nosrv_hostname = "master.aceattorneyonline.com";
#endif
- const int ms_port = 27016;
+ const quint16 ms_port = 27016;
const int timeout_milliseconds = 2000;
- const int ms_reconnect_delay_ms = 7000;
+ // in seconds
+ const int ms_reconnect_delay = 7;
// kind of arbitrary max buffer size
- const size_t buffer_max_size = 16384;
+ #define BUFFER_MAX_SIZE 16384
bool ms_partial_packet = false;
QString ms_temp_packet = "";
diff --git a/scripts/.gitignore b/scripts/.gitignore
new file mode 100644
index 00000000..2f8a2b91
--- /dev/null
+++ b/scripts/.gitignore
@@ -0,0 +1,6 @@
+node_modules/
+
+# Cursed file
+package-lock.json
+
+s3_keys.sh
diff --git a/scripts/configure_macos.sh b/scripts/configure_macos.sh
new file mode 100755
index 00000000..af08966f
--- /dev/null
+++ b/scripts/configure_macos.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# This script fetches all build dependencies for MacOS
+# Tested on MacOS 10.14 (Mojave), Qt 5.13 and XCode 10.2
+
+# Exit on errors and unset variables
+set -eu
+
+ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/"
+
+cd ${ROOT_DIR}
+
+LIB_TARGET="../../lib"
+BASS_LINK="http://uk.un4seen.com/files/bass24-osx.zip"
+BASSOPUS_LINK="http://www.un4seen.com/files/bassopus24-osx.zip"
+DISCORD_RPC_LINK="https://github.com/discordapp/discord-rpc/releases/download/v3.4.0/discord-rpc-osx.zip"
+APNG_LINK="https://github.com/Skycoder42/QtApng/releases/download/1.1.2-2/qtapng_clang_64_5.13.0.tar.xz"
+
+# Easier if we don't need to worry about an existing tmp folder tbh smh
+# v Add a slash here for free tmp folder cleanup in true javascript community style
+rm -rf tmp
+mkdir tmp
+cd tmp
+
+curl -Ls ${BASS_LINK} -o bass.zip
+unzip -qq bass.zip
+cp libbass.dylib ${LIB_TARGET}
+
+curl -Ls ${BASSOPUS_LINK} -o bassopus.zip
+unzip -qq bassopus.zip
+cp libbassopus.dylib ${LIB_TARGET}
+
+curl -Ls ${DISCORD_RPC_LINK} -o discord_rpc.zip
+unzip -qq discord_rpc.zip
+cp discord-rpc/osx-dynamic/lib/libdiscord-rpc.dylib ${LIB_TARGET}
+
+curl -Ls ${APNG_LINK} -o apng.tar.xz
+tar -xf apng.tar.xz
+cp clang_64/plugins/imageformats/libqapng.dylib ../../lib
+
+cd ..
+rm -rf tmp
diff --git a/scripts/configure_ubuntu.sh b/scripts/configure_ubuntu.sh
index 3817997c..159dc4d1 100755
--- a/scripts/configure_ubuntu.sh
+++ b/scripts/configure_ubuntu.sh
@@ -1,6 +1,13 @@
-#!/bin/bash
+#!/bin/sh
-#assumes a somewhat recent 64-bit ubuntu
+# Assumes a somewhat recent 64-bit ubuntu
+
+# Exit on errors and unset variables
+set -eu
+
+ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/"
+
+cd ${ROOT_DIR}
#need some openGL stuff
sudo apt install libgl1-mesa-dev
diff --git a/scripts/macos_build.sh b/scripts/macos_build.sh
new file mode 100755
index 00000000..22e2d457
--- /dev/null
+++ b/scripts/macos_build.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+set -Eexo pipefail
+
+export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/local/opt/openssl/lib/pkgconfig
+export PATH=$PATH:/usr/local/opt/qt5/bin:/usr/local/bin
+
+mkdir bass
+cd bass
+curl http://www.un4seen.com/files/bass24-osx.zip -o bass.zip
+unzip bass.zip
+cp libbass.dylib ../lib
+
+curl http://www.un4seen.com/files/bassopus24-osx.zip -o bassopus.zip
+unzip bassopus.zip
+cp libbassopus.dylib ../lib
+cd ..
+
+mkdir discord-rpc
+cd discord-rpc
+curl -L https://github.com/discordapp/discord-rpc/releases/download/v3.4.0/discord-rpc-osx.zip -o discord_rpc_osx.zip
+unzip discord_rpc_osx.zip
+cp discord-rpc/osx-static/lib/libdiscord-rpc.a ../lib
+cd ..
+
+mkdir qtapng
+cd qtapng
+curl -L https://github.com/Skycoder42/QtApng/releases/download/1.1.0-5/build_clang_64_5.12.0.tar.xz -o apng.tar.xz
+tar -xvf apng.tar.xz
+cp clang_64/plugins/imageformats/libqapng.dylib ../lib
+cd ..
+
+qmake && make -j2
diff --git a/scripts/macos_post_build.sh b/scripts/macos_post_build.sh
index d69da0b4..50acb408 100644
--- a/scripts/macos_post_build.sh
+++ b/scripts/macos_post_build.sh
@@ -1,16 +1,22 @@
-#!/bin/bash
+#!/bin/sh
-DST_FOLDER="./bin/Attorney_Online.app/Contents/Frameworks"
+# This script prepares the compiled bundle for shipping as a standalone release
+# Assumes the Qt bin folder is in PATH
+# Should be used on a "Release" build from QT creator
+# Note that this DOES NOT add the base/ folder
-cd ..
+# Exit on errors and unset variables
+set -eu
-mkdir $DST_FOLDER
+ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/"
-cp ./lib/libbass.dylib $DST_FOLDER
-cp ./lib/libbassopus.dylib $DST_FOLDER
+cd ${ROOT_DIR}
-install_name_tool -id @executable_path/../Frameworks/libbass.dylib $DST_FOLDER/libbass.dylib
+# This thing basically does all the work
+macdeployqt ../bin/Attorney_Online.app
-install_name_tool -id @executable_path/../Frameworks/libbassopus.dylib $DST_FOLDER/libbassopus.dylib
+# Need to add the dependencies
+cp ../lib/* ../bin/Attorney_Online.app/Contents/Frameworks
-install_name_tool -change @loader_path/libbass.dylib @executable_path/../Frameworks/libbass.dylib ./bin/Attorney_Online.app/Contents/MacOS/Attorney_Online
+# libbass has a funny path for some reason, just use rpath
+install_name_tool -change @loader_path/libbass.dylib @rpath/libbass.dylib ../bin/Attorney_Online.app/Contents/MacOS/Attorney_Online
diff --git a/scripts/package.json b/scripts/package.json
new file mode 100644
index 00000000..126a3920
--- /dev/null
+++ b/scripts/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "ao-ci-scripts",
+ "version": "1.0.0",
+ "main": "update_manifest.js",
+ "dependencies": {
+ "argparse": "^1.0.10"
+ },
+ "license": "ISC"
+}
diff --git a/scripts/release_macos.sh b/scripts/release_macos.sh
new file mode 100755
index 00000000..50acb408
--- /dev/null
+++ b/scripts/release_macos.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# This script prepares the compiled bundle for shipping as a standalone release
+# Assumes the Qt bin folder is in PATH
+# Should be used on a "Release" build from QT creator
+# Note that this DOES NOT add the base/ folder
+
+# Exit on errors and unset variables
+set -eu
+
+ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/"
+
+cd ${ROOT_DIR}
+
+# This thing basically does all the work
+macdeployqt ../bin/Attorney_Online.app
+
+# Need to add the dependencies
+cp ../lib/* ../bin/Attorney_Online.app/Contents/Frameworks
+
+# libbass has a funny path for some reason, just use rpath
+install_name_tool -change @loader_path/libbass.dylib @rpath/libbass.dylib ../bin/Attorney_Online.app/Contents/MacOS/Attorney_Online
diff --git a/scripts/update_manifest.js b/scripts/update_manifest.js
new file mode 100755
index 00000000..1a06a2d7
--- /dev/null
+++ b/scripts/update_manifest.js
@@ -0,0 +1,151 @@
+#!/usr/bin/env node
+
+const fs = require("fs");
+const crypto = require("crypto");
+const path = require("path");
+const ArgumentParser = require("argparse").ArgumentParser;
+
+function isFile(file) {
+ if (!fs.existsSync(file)) {
+ console.error(`File '${file}' not found. Try again.`);
+ throw Error();
+ }
+ return file;
+}
+
+const argParser = new ArgumentParser({
+ addHelp: true,
+ description: "Adds a new latest version to the manifest file based on the " +
+ "provided zip file, including an incremental update."
+});
+argParser.addArgument("manifestFile", {
+ metavar: "<manifest file>", type: isFile
+});
+argParser.addArgument("version", {
+ metavar: "<version>"
+});
+argParser.addArgument([ "-f", "--full" ], {
+ metavar: "<full zip file>", type: isFile, nargs: 1,
+ dest: "fullZipFileArgs"
+});
+argParser.addArgument([ "-i", "--incremental" ], {
+ type: isFile, nargs: 2, dest: "incrementalArgs",
+ metavar: ["<incremental zip file>", "<file containing list of changed files>"]
+});
+argParser.addArgument([ "-e", "--executable" ], {
+ metavar: "[executable file]", nargs: 1,
+ dest: "executableArgs"
+});
+
+const {
+ manifestFile,
+ version,
+ fullZipFileArgs,
+ incrementalArgs,
+ executableArgs
+} = argParser.parseArgs();
+
+const [incrementalZipFile, changesFile] = incrementalArgs || [];
+const [fullZipFile] = fullZipFileArgs || [];
+const [executable] = executableArgs || [];
+
+// Do one final check
+if (!incrementalZipFile && !fullZipFile) {
+ console.error("No download archive specified! Abort.");
+ process.exit(1);
+}
+
+// Do a quick litmus test to prevent deleting everything incorrectly
+if (changesFile && !fs.existsSync("base")) {
+ console.error("The working directory must be set to an " +
+ "asset folder in order for deleted directories " +
+ "to be calculated correctly. Abort.");
+ process.exit(1);
+}
+
+const manifest = JSON.parse(fs.readFileSync(manifestFile));
+
+const dirsDeleted = new Set();
+const specialActions = changesFile ?
+ fs.readFileSync(changesFile)
+ .toString()
+ .trim()
+ .split("\n")
+ .map(line => line.split("\t"))
+ .map(([mode, target, source]) => {
+ switch (mode[0]) {
+ case "D": // Deleted
+ // Check if the folder exists relative to the working
+ // directory, and if not, add it to the dirsDeleted list.
+ // Keep going up the tree to see how many directories were
+ // deleted.
+ let dir = path.dirname(target);
+ while (!dirsDeleted.has(dir) && !fs.existsSync(dir)) {
+ dirsDeleted.add(dir);
+ dir = path.dirname(dir);
+ }
+
+ return { action: "delete", target };
+ case "R": // Renamed
+ // NOTE: Make sure that the launcher's implementation of
+ // the move action also creates directories when needed.
+ return { action: "move", source, target};
+ default:
+ return null;
+ }
+ })
+ // Remove ignored file mode changes
+ .filter(action => action !== null)
+ // Create actions based on directories to be deleted.
+ // Always have deeper directories first, to guarantee that deleting
+ // higher-level directories will succeed.
+ .concat(Array.from(dirsDeleted.values())
+ .sort((a, b) => b.split("/").length - a.split("/").length)
+ .map(dir => {
+ return { action: "deleteDir", target: dir };
+ }))
+ : [];
+
+const urlBase = "https://s3.wasabisys.com/ao-downloads/";
+
+const versionEntry = {
+ version,
+ executable,
+ prev: manifest.versions[0] ? manifest.versions[0].version : undefined,
+ full: fullZipFile ? [
+ {
+ action: "dl",
+ url: urlBase + encodeURIComponent(path.basename(fullZipFile)),
+ hash: crypto.createHash("sha1")
+ .update(fs.readFileSync(fullZipFile))
+ .digest("hex")
+ }
+ ] : undefined,
+ update: incrementalArgs ? [
+ ...specialActions,
+ {
+ action: "dl",
+ url: urlBase + encodeURIComponent(path.basename(incrementalZipFile)),
+ hash: crypto.createHash("sha1")
+ .update(fs.readFileSync(incrementalZipFile))
+ .digest("hex")
+ }
+ ] : undefined
+};
+
+console.log("Generated version entry:", versionEntry);
+
+const existingVersions = manifest.versions.filter(v => v.version == version);
+if (existingVersions.length > 0) {
+ console.warn(`Warning: version ${version} already exists. Adding new values.`);
+
+ // Don't overwrite prev - it will cause headaches
+ delete versionEntry.prev;
+
+ Object.assign(existingVersions[0], versionEntry);
+ console.log("Merged version entry:", existingVersions[0]);
+} else {
+ manifest.versions = [versionEntry, ...manifest.versions];
+}
+
+fs.writeFileSync(manifestFile, JSON.stringify(manifest, null, 4));
diff --git a/scripts/update_program_manifest.js b/scripts/update_program_manifest.js
new file mode 100755
index 00000000..9efc814f
--- /dev/null
+++ b/scripts/update_program_manifest.js
@@ -0,0 +1,39 @@
+#!/usr/bin/env node
+
+const fs = require("fs");
+const crypto = require("crypto");
+
+const [ _nodeExe, _jsPath, manifestFile, version, zipFile ] = process.argv;
+
+if (!manifestFile || !version || !zipFile) {
+ console.log(`Usage: update_program_manifest <manifest file> <version> <zip file>`);
+ console.log(`Adds a new latest version to the manifest file based on the ` +
+ `provided zip file.`);
+ process.exit(1);
+}
+
+if (!fs.existsSync(manifestFile)) {
+ console.error(`Manifest file '${manifestFile}' not found. Try again.`);
+ process.exit(2);
+}
+
+if (!fs.existsSync(zipFile)) {
+ console.error(`Zip file '${zipFile}' not found. Try again.`);
+ process.exit(2);
+}
+
+const manifest = JSON.parse(fs.readFileSync(manifestFile));
+
+manifest.versions = [{
+ version,
+ executable: "Attorney_Online.exe",
+ full: [
+ {
+ action: "dl",
+ url: "https://s3.wasabisys.com/ao-downloads/" + encodeURIComponent(zipFile),
+ hash: crypto.createHash("sha1").update(fs.readFileSync(zipFile)).digest("hex")
+ }
+ ]
+}, ...manifest.versions];
+
+fs.writeFileSync(manifestFile, JSON.stringify(manifest, null, 4)); \ No newline at end of file
diff --git a/scripts/wasabi.sh b/scripts/wasabi.sh
new file mode 100755
index 00000000..95fb3ee9
--- /dev/null
+++ b/scripts/wasabi.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+# Updates the specified program manifest to a new archive and version
+# and uploads the new archive and manifest to S3/Wasabi.
+#
+# Requires:
+# MANIFEST: name of the manifest file
+# S3_ACCESS_KEY, S3_SECRET_KEY: S3 credentials
+# S3_MANIFESTS, S3_ARCHIVES: S3 paths to manifests and downloads
+# ARCHIVE_FULL: name of the full archive (if desired)
+# ARCHIVE_INCR: name of the incremental archive (if desired)
+# VERSION: name of the new version
+# EXECUTABLE: name of the executable (if program manifest)
+
+
+# -E: inherit ERR trap by shell functions
+# -e: stop script on ERR trap
+# -u: stop script on unbound variables
+# -x: print command before running it
+# -o pipefail: fail if any command in a pipeline fails
+set -Eeuxo pipefail
+
+aws configure set aws_access_key_id ${S3_ACCESS_KEY}
+aws configure set aws_secret_access_key ${S3_SECRET_KEY}
+aws configure set default.region us-east-1
+
+export S3_COPY="aws s3 cp --endpoint-url=https://s3.wasabisys.com"
+
+export ARCHIVE_FULL_ARG=""
+export ARCHIVE_INCR_ARG=""
+export EXECUTABLE_ARG=""
+
+export LAST_TAGGED_VERSION=$(git rev-list --tags --skip=1 --max-count=1)
+echo "Previous tagged version: ${LAST_TAGGED_VERSION}"
+echo "Current tagged version: ${VERSION}"
+
+if [[ -n $ARCHIVE_INCR && -n $LAST_TAGGED_VERSION ]]; then
+ echo "Incremental archive: ${ARCHIVE_INCR}"
+
+ # Get all files
+ export CHANGES_FILE="changes.txt"
+ git diff --name-status ${LAST_TAGGED_VERSION}..HEAD > ${CHANGES_FILE}
+
+ # Get added/modified files
+ git diff --name-only --diff-filter=dr ${LAST_TAGGED_VERSION}..HEAD | \
+ zip ${ARCHIVE_INCR} -@
+
+ export ARCHIVE_INCR_ARG="-i ${ARCHIVE_INCR} ${CHANGES_FILE}"
+elif [[ -n $ARCHIVE_INCR && -z $LAST_TAGGED_VERSION ]]; then
+ echo "Incremental archive was requested, but there is no previous version"
+fi
+
+if [[ -n $ARCHIVE_FULL ]]; then
+ echo "Full archive: ${ARCHIVE_INCR}"
+ export ARCHIVE_FULL_ARG="-f ${ARCHIVE_FULL}"
+fi
+
+if [[ -v EXECUTABLE ]]; then
+ export EXECUTABLE_ARG="-e ${EXECUTABLE}"
+fi
+
+${S3_COPY} ${S3_MANIFESTS}/${MANIFEST} .
+node $(dirname $0)/update_manifest.js ${MANIFEST} ${VERSION} \
+ ${ARCHIVE_FULL_ARG} ${ARCHIVE_INCR_ARG} ${EXECUTABLE_ARG}
+
+if [[ -n $ARCHIVE_INCR_ARG ]]; then
+ ${S3_COPY} ${ARCHIVE_INCR} ${S3_ARCHIVES}
+fi
+
+if [[ -n $ARCHIVE_FULL_ARG ]]; then
+ ${S3_COPY} ${ARCHIVE_FULL} ${S3_ARCHIVES}
+fi
+
+${S3_COPY} ${MANIFEST} ${S3_MANIFESTS}
diff --git a/scripts/wasabi_program.sh b/scripts/wasabi_program.sh
new file mode 100755
index 00000000..41e2e352
--- /dev/null
+++ b/scripts/wasabi_program.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+# Updates the specified program manifest to a new archive and version
+# and uploads the new archive and manifest to S3/Wasabi.
+#
+# Requires:
+# MANIFEST: name of the manifest file
+# ARTIFACT_SUFFIX: suffix of the archive to be uploaded (including extension)
+# S3_ACCESS_KEY and S3_SECRET_KEY
+
+
+# -E: inherit ERR trap by shell functions
+# -e: stop script on ERR trap
+# -u: stop script on unbound variables
+# -x: print command before running it
+# -o pipefail: fail if any command in a pipeline fails
+set -Eeuxo pipefail
+
+aws configure set aws_access_key_id ${S3_ACCESS_KEY}
+aws configure set aws_secret_access_key ${S3_SECRET_KEY}
+aws configure set default.region us-east-1
+
+export S3_COPY="aws s3 cp --endpoint-url=https://s3.wasabisys.com"
+export S3_MANIFESTS="s3://ao-manifests"
+export S3_ARCHIVES="s3://ao-downloads"
+
+export VERSION=$(git describe --tags)
+export ARCHIVE="Attorney_Online_${VERSION}_${ARTIFACT_SUFFIX}"
+
+${S3_COPY} ${S3_MANIFESTS}/${MANIFEST} .
+node $(dirname $0)/update_manifest.js ${MANIFEST} ${VERSION} \
+ -f ${ARCHIVE} -e Attorney_Online.exe
+${S3_COPY} ${ARCHIVE} ${S3_ARCHIVES}
+${S3_COPY} ${MANIFEST} ${S3_MANIFESTS}
+
+rm -f ${MANIFEST}
diff --git a/scripts/windows/Dockerfile b/scripts/windows/Dockerfile
new file mode 100644
index 00000000..90d6c27e
--- /dev/null
+++ b/scripts/windows/Dockerfile
@@ -0,0 +1,17 @@
+FROM oldmud0/mxe-qt:5.12.1-win32-static-posix
+#FROM fffaraz/qt:windows
+
+ENV TARGET_SPEC i686-w64-mingw32.static.posix
+
+# Build Discord RPC statically
+RUN git clone https://github.com/discordapp/discord-rpc
+WORKDIR discord-rpc/build
+RUN /opt/mxe/usr/bin/${TARGET_SPEC}-cmake .. -DCMAKE_INSTALL_PREFIX=/opt/mxe/usr/${TARGET_SPEC}
+RUN /opt/mxe/usr/bin/${TARGET_SPEC}-cmake --build . --config Release --target install
+WORKDIR ../..
+
+# Build QtApng statically
+RUN git clone https://github.com/Skycoder42/QtApng
+WORKDIR QtApng
+RUN /opt/mxe/usr/${TARGET_SPEC}/qt5/bin/qmake && make qmake_all && make && make install
+WORKDIR ..
diff --git a/scripts/windows/Dockerfile-mxe b/scripts/windows/Dockerfile-mxe
new file mode 100644
index 00000000..e6caec57
--- /dev/null
+++ b/scripts/windows/Dockerfile-mxe
@@ -0,0 +1,44 @@
+FROM ubuntu:18.04
+
+RUN apt-get update
+RUN apt-get install -y \
+ autoconf \
+ automake \
+ autopoint \
+ bash \
+ bison \
+ bzip2 \
+ flex \
+ g++ \
+ g++-multilib \
+ gettext \
+ git \
+ gperf \
+ intltool \
+ libc6-dev-i386 \
+ libgdk-pixbuf2.0-dev \
+ libltdl-dev \
+ libssl-dev \
+ libtool-bin \
+ libxml-parser-perl \
+ lzip \
+ make \
+ openssl \
+ p7zip-full \
+ patch \
+ perl \
+ pkg-config \
+ python \
+ ruby \
+ sed \
+ unzip \
+ wget \
+ xz-utils
+
+RUN git clone https://github.com/mxe/mxe.git
+RUN mv mxe /opt/mxe
+WORKDIR /opt/mxe
+RUN make -j4 MXE_TARGETS="i686-w64-mingw32.static.posix" qtbase qtmultimedia
+ENV PATH=/opt/mxe/usr/bin:$PATH
+
+WORKDIR /
diff --git a/scripts/windows/how-to-push.md b/scripts/windows/how-to-push.md
new file mode 100644
index 00000000..8c1c18d6
--- /dev/null
+++ b/scripts/windows/how-to-push.md
@@ -0,0 +1,19 @@
+When you want to build a new version of Qt:
+```docker
+docker build -t mxe-windows-static . -f Dockerfile-mxe
+docker tag mxe-windows-static oldmud0/mxe-qt:5.12.1-win32-static-posix
+docker push oldmud0/mxe-qt:5.12.1-win32-static-posix
+```
+
+Remember to log into Docker Hub before attempting to push.
+
+When you want to build a new version of any dependency required for building AO:
+```docker
+docker build -t mxe-windows-static-ao . -f Dockerfile
+docker tag mxe-windows-static-ao registry.gitlab.com/attorneyonline/ao2-client/builder-windows-i686
+docker push registry.gitlab.com/attorneyonline/ao2-client/builder-windows-i686
+```
+
+Remember to create an access token in GitLab before attempting to push.
+
+GitLab CI depends on `builder-windows-i686` image to be present in the repository's registry in order for the Windows build to succeed.
diff --git a/src/aoapplication.cpp b/src/aoapplication.cpp
index d7c3e662..4ef1edd3 100644
--- a/src/aoapplication.cpp
+++ b/src/aoapplication.cpp
@@ -37,9 +37,9 @@ void AOApplication::construct_lobby()
w_lobby = new Lobby(this);
lobby_constructed = true;
- QRect screenGeometry = QApplication::desktop()->screenGeometry();
- int x = (screenGeometry.width()-w_lobby->width()) / 2;
- int y = (screenGeometry.height()-w_lobby->height()) / 2;
+ QRect geometry = QGuiApplication::primaryScreen()->geometry();
+ int x = (geometry.width()-w_lobby->width()) / 2;
+ int y = (geometry.height()-w_lobby->height()) / 2;
w_lobby->move(x, y);
if (is_discord_enabled())
@@ -72,9 +72,9 @@ void AOApplication::construct_courtroom()
w_courtroom = new Courtroom(this);
courtroom_constructed = true;
- QRect screenGeometry = QApplication::desktop()->screenGeometry();
- int x = (screenGeometry.width()-w_courtroom->width()) / 2;
- int y = (screenGeometry.height()-w_courtroom->height()) / 2;
+ QRect geometry = QGuiApplication::primaryScreen()->geometry();
+ int x = (geometry.width()-w_courtroom->width()) / 2;
+ int y = (geometry.height()-w_courtroom->height()) / 2;
w_courtroom->move(x, y);
}
@@ -161,7 +161,7 @@ void AOApplication::ms_connect_finished(bool connected, bool will_retry)
{
if (lobby_constructed)
w_lobby->append_error("Error connecting to master server. Will try again in "
- + QString::number(net_manager->ms_reconnect_delay_ms / 1000.f) + " seconds.");
+ + QString::number(net_manager->ms_reconnect_delay) + " seconds.");
}
else
{
diff --git a/src/aoevidencebutton.cpp b/src/aoevidencebutton.cpp
index 924aeb88..15b598fc 100644
--- a/src/aoevidencebutton.cpp
+++ b/src/aoevidencebutton.cpp
@@ -86,6 +86,7 @@ void AOEvidenceButton::mouseDoubleClickEvent(QMouseEvent *e)
evidence_double_clicked(m_id);
}
+/*
void AOEvidenceButton::dragLeaveEvent(QMouseEvent *e)
{
//QWidget::dragLeaveEvent(e);
@@ -99,6 +100,7 @@ void AOEvidenceButton::dragEnterEvent(QMouseEvent *e)
qDebug() << "drag enter event";
}
+*/
void AOEvidenceButton::enterEvent(QEvent * e)
{
diff --git a/src/charselect.cpp b/src/charselect.cpp
index 8e1b9122..4987cf57 100644
--- a/src/charselect.cpp
+++ b/src/charselect.cpp
@@ -44,7 +44,6 @@ void Courtroom::construct_char_select()
set_size_and_pos(ui_char_buttons, "char_buttons");
- connect (char_button_mapper, SIGNAL(mapped(int)), this, SLOT(char_clicked(int)));
connect(ui_back_to_lobby, SIGNAL(clicked()), this, SLOT(on_back_to_lobby_clicked()));
connect(ui_char_select_left, SIGNAL(clicked()), this, SLOT(on_char_select_left_clicked()));
@@ -52,9 +51,9 @@ void Courtroom::construct_char_select()
connect(ui_spectator, SIGNAL(clicked()), this, SLOT(on_spectator_clicked()));
- connect(ui_char_search, SIGNAL(textEdited(const QString&)), this, SLOT(on_char_search_changed(const QString&)));
- connect(ui_char_passworded, SIGNAL(stateChanged(int)), this, SLOT(on_char_passworded_clicked(int)));
- connect(ui_char_taken, SIGNAL(stateChanged(int)), this, SLOT(on_char_taken_clicked(int)));
+ connect(ui_char_search, SIGNAL(textEdited(const QString&)), this, SLOT(on_char_search_changed()));
+ connect(ui_char_passworded, SIGNAL(stateChanged(int)), this, SLOT(on_char_passworded_clicked()));
+ connect(ui_char_taken, SIGNAL(stateChanged(int)), this, SLOT(on_char_taken_clicked()));
}
void Courtroom::set_char_select()
@@ -197,14 +196,15 @@ void Courtroom::character_loading_finished()
// Later on, we'll be revealing buttons as we need them.
for (int n = 0; n < char_list.size(); n++)
{
- AOCharButton* character = new AOCharButton(ui_char_buttons, ao_app, 0, 0, char_list.at(n).taken);
- character->reset();
- character->hide();
- character->set_image(char_list.at(n).name);
- ui_char_button_list.append(character);
+ AOCharButton* char_button = new AOCharButton(ui_char_buttons, ao_app, 0, 0, char_list.at(n).taken);
+ char_button->reset();
+ char_button->hide();
+ char_button->set_image(char_list.at(n).name);
+ ui_char_button_list.append(char_button);
- connect(character, SIGNAL(clicked()), char_button_mapper, SLOT(map()));
- char_button_mapper->setMapping(character, ui_char_button_list.size() - 1);
+ connect(char_button, &AOCharButton::clicked, [this, n](){
+ this->char_clicked(n);
+ });
// This part here serves as a way of showing to the player that the game is still running, it is
// just loading the pictures of the characters.
@@ -252,17 +252,17 @@ void Courtroom::filter_character_list()
set_char_select_page();
}
-void Courtroom::on_char_search_changed(const QString& newtext)
+void Courtroom::on_char_search_changed()
{
filter_character_list();
}
-void Courtroom::on_char_passworded_clicked(int newstate)
+void Courtroom::on_char_passworded_clicked()
{
filter_character_list();
}
-void Courtroom::on_char_taken_clicked(int newstate)
+void Courtroom::on_char_taken_clicked()
{
filter_character_list();
}
diff --git a/src/courtroom.cpp b/src/courtroom.cpp
index 6a5379aa..dd6aedcb 100644
--- a/src/courtroom.cpp
+++ b/src/courtroom.cpp
@@ -6,7 +6,7 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow()
#ifdef BASSAUDIO
// Change the default audio output device to be the one the user has given
// in his config.ini file for now.
- int a = 0;
+ unsigned int a = 0;
BASS_DEVICEINFO info;
if (ao_app->get_audio_output_device() == "default")
@@ -21,7 +21,7 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow()
if (ao_app->get_audio_output_device() == info.name)
{
BASS_SetDevice(a);
- BASS_Init(a, 48000, BASS_DEVICE_LATENCY, nullptr, nullptr);
+ BASS_Init(static_cast<int>(a), 48000, BASS_DEVICE_LATENCY, nullptr, nullptr);
load_bass_opus_plugin();
qDebug() << info.name << "was set as the default audio output device.";
break;
@@ -50,8 +50,6 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow()
testimony_hide_timer = new QTimer(this);
testimony_hide_timer->setSingleShot(true);
- char_button_mapper = new QSignalMapper(this);
-
music_player = new AOMusicPlayer(this, ao_app);
music_player->set_volume(0);
sfx_player = new AOSfxPlayer(this, ao_app);
@@ -2025,6 +2023,7 @@ void Courtroom::chat_tick()
//do not perform heavy operations here
QString f_message = m_chatmessage[MESSAGE];
+ f_message.remove(0, tick_pos);
// Due to our new text speed system, we always need to stop the timer now.
chat_tick_timer->stop();
@@ -2039,7 +2038,7 @@ void Courtroom::chat_tick()
f_message.remove(0,2);
}
- if (tick_pos >= f_message.size())
+ if (f_message.size() == 0)
{
text_state = 2;
if (anim_state != 4)
@@ -2051,9 +2050,21 @@ void Courtroom::chat_tick()
else
{
- QString f_character = f_message.at(tick_pos);
+ QTextBoundaryFinder tbf(QTextBoundaryFinder::Grapheme, f_message);
+ QString f_character;
+ int f_char_length;
+
+ tbf.toNextBoundary();
+
+ if (tbf.position() == -1)
+ f_character = f_message;
+ else
+ f_character = f_message.left(tbf.position());
+
+ f_char_length = f_character.length();
f_character = f_character.toHtmlEscaped();
+
if (f_character == " ")
ui_vp_message->insertPlainText(" ");
@@ -2146,7 +2157,7 @@ void Courtroom::chat_tick()
else
{
next_character_is_not_special = true;
- tick_pos--;
+ tick_pos -= f_char_length;
}
}
@@ -2167,7 +2178,7 @@ void Courtroom::chat_tick()
else
{
next_character_is_not_special = true;
- tick_pos--;
+ tick_pos -= f_char_length;
}
}
@@ -2211,11 +2222,7 @@ void Courtroom::chat_tick()
case INLINE_GREY:
ui_vp_message->insertHtml("<font color=\""+ get_text_color("_inline_grey").name() +"\">" + f_character + "</font>");
break;
- default:
- ui_vp_message->insertHtml(f_character);
- break;
}
-
}
else
{
@@ -2266,7 +2273,7 @@ void Courtroom::chat_tick()
if(blank_blip)
qDebug() << "blank_blip found true";
- if (f_message.at(tick_pos) != ' ' || blank_blip)
+ if (f_character != ' ' || blank_blip)
{
if (blip_pos % blip_rate == 0 && !formatting_char)
@@ -2278,7 +2285,7 @@ void Courtroom::chat_tick()
++blip_pos;
}
- ++tick_pos;
+ tick_pos += f_char_length;
// Restart the timer, but according to the newly set speeds, if there were any.
// Keep the speed at bay.
@@ -2305,6 +2312,7 @@ void Courtroom::chat_tick()
}
}
+
void Courtroom::show_testimony()
{
if (!testimony_in_progress || m_chatmessage[SIDE] != "wit")
@@ -2697,6 +2705,7 @@ void Courtroom::on_ooc_return_pressed()
}
else
{
+ other_charid = -1;
append_server_chatmessage("CLIENT", "You are no longer paired with anyone.", "1");
}
}
@@ -2853,6 +2862,60 @@ void Courtroom::on_ooc_return_pressed()
ui_ooc_chat_message->clear();
return;
}
+ else if(ooc_message.startsWith("/save_case"))
+ {
+ QStringList command = ooc_message.split(" ", QString::SkipEmptyParts);
+
+ QDir casefolder("base/cases");
+ if (!casefolder.exists())
+ {
+ QDir::current().mkdir("base/" + casefolder.dirName());
+ append_server_chatmessage("CLIENT", "You don't have a `base/cases/` folder! It was just made for you, but seeing as it WAS just made for you, it's likely that you somehow deleted it.", "1");
+ ui_ooc_chat_message->clear();
+ return;
+ }
+ QStringList caseslist = casefolder.entryList();
+ caseslist.removeOne(".");
+ caseslist.removeOne("..");
+ caseslist.replaceInStrings(".ini","");
+
+ if (command.size() < 3)
+ {
+ append_server_chatmessage("CLIENT", "You need to give a filename to save (extension not needed) and the courtroom status!", "1");
+ ui_ooc_chat_message->clear();
+ return;
+ }
+
+
+ if (command.size() > 3)
+ {
+ append_server_chatmessage("CLIENT", "Too many arguments to save a case! You only need a filename without extension and the courtroom status!", "1");
+ ui_ooc_chat_message->clear();
+ return;
+ }
+ QSettings casefile("base/cases/" + command[1] + ".ini", QSettings::IniFormat);
+ casefile.setValue("author",ui_ooc_chat_name->text());
+ casefile.setValue("cmdoc","");
+ casefile.setValue("doc", "");
+ casefile.setValue("status",command[2]);
+ casefile.sync();
+ for(int i = local_evidence_list.size() - 1; i >= 0; i--)
+ {
+ QString clean_evidence_dsc = local_evidence_list[i].description.replace(QRegularExpression("<owner = ...>..."), "");
+ clean_evidence_dsc = clean_evidence_dsc.replace(clean_evidence_dsc.lastIndexOf(">"), 1, "");
+ casefile.beginGroup(QString::number(i));
+ casefile.sync();
+ casefile.setValue("name",local_evidence_list[i].name);
+ casefile.setValue("description",local_evidence_list[i].description);
+ casefile.setValue("image",local_evidence_list[i].image);
+ casefile.endGroup();
+ }
+ casefile.sync();
+ append_server_chatmessage("CLIENT", "Succesfully saved, edit doc and cmdoc link on the ini!", "1");
+ ui_ooc_chat_message->clear();
+ return;
+
+ }
QStringList packet_contents;
packet_contents.append(ui_ooc_chat_name->text());
@@ -2982,6 +3045,7 @@ void Courtroom::on_pair_list_clicked(QModelIndex p_index)
QListWidgetItem *f_item = ui_pair_list->item(p_index.row());
QString f_char = f_item->text();
QString real_char;
+ int f_cid = -1;
if (f_char.endsWith(" [x]"))
{
@@ -2989,17 +3053,19 @@ void Courtroom::on_pair_list_clicked(QModelIndex p_index)
f_item->setText(real_char);
}
else
- real_char = f_char;
-
- int f_cid = -1;
-
- for (int n_char = 0 ; n_char < char_list.size() ; n_char++)
{
+ real_char = f_char;
+ for (int n_char = 0 ; n_char < char_list.size() ; n_char++)
+ {
if (char_list.at(n_char).name == real_char)
f_cid = n_char;
+ }
}
- if (f_cid < 0 || f_cid >= char_list.size())
+
+
+
+ if (f_cid < -2 || f_cid >= char_list.size())
{
qDebug() << "W: " << real_char << " not present in char_list";
return;
@@ -3018,8 +3084,10 @@ void Courtroom::on_pair_list_clicked(QModelIndex p_index)
for (int i = 0; i < ui_pair_list->count(); i++) {
ui_pair_list->item(i)->setText(sorted_pair_list.at(i));
}
-
- f_item->setText(real_char + " [x]");
+ if(other_charid != -1)
+ {
+ f_item->setText(real_char + " [x]");
+ }
}
void Courtroom::on_music_list_double_clicked(QModelIndex p_model)
diff --git a/src/discord_rich_presence.cpp b/src/discord_rich_presence.cpp
index a310a684..95a824a1 100644
--- a/src/discord_rich_presence.cpp
+++ b/src/discord_rich_presence.cpp
@@ -12,10 +12,10 @@ Discord::Discord()
qInfo() << "Discord RPC ready";
};
handlers.disconnected = [](int errorCode, const char* message) {
- qInfo() << "Discord RPC disconnected! " << message;
+ qInfo() << "Discord RPC disconnected! " << message << errorCode;
};
handlers.errored = [](int errorCode, const char* message) {
- qWarning() << "Discord RPC errored out! " << message;
+ qWarning() << "Discord RPC errored out! " << message << errorCode;
};
qInfo() << "Initializing Discord RPC";
Discord_Initialize(APPLICATION_ID, &handlers, 1, nullptr);
diff --git a/src/evidence.cpp b/src/evidence.cpp
index 4e796642..ebef7490 100644
--- a/src/evidence.cpp
+++ b/src/evidence.cpp
@@ -188,11 +188,12 @@ void Courtroom::on_evidence_image_name_edited()
void Courtroom::on_evidence_image_button_clicked()
{
+ QDir dir(ao_app->get_base_path() + "evidence");
QFileDialog dialog(this);
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilter(tr("Images (*.png)"));
dialog.setViewMode(QFileDialog::List);
- dialog.setDirectory(ao_app->get_base_path() + "evidence");
+ dialog.setDirectory(dir);
QStringList filenames;
@@ -203,13 +204,8 @@ void Courtroom::on_evidence_image_button_clicked()
return;
QString filename = filenames.at(0);
-
- QStringList split_filename = filename.split("/");
-
- filename = split_filename.at(split_filename.size() - 1);
-
+ filename = dir.relativeFilePath(filename);
ui_evidence_image_name->setText(filename);
-
on_evidence_image_name_edited();
}
diff --git a/src/hardware_functions.cpp b/src/hardware_functions.cpp
index ebba6ab7..5d6b6ffa 100644
--- a/src/hardware_functions.cpp
+++ b/src/hardware_functions.cpp
@@ -5,12 +5,12 @@
#if (defined (_WIN32) || defined (_WIN64))
#include <windows.h>
-DWORD dwVolSerial;
-BOOL bIsRetrieved;
+static DWORD dwVolSerial;
+static BOOL bIsRetrieved;
QString get_hdid()
{
- bIsRetrieved = GetVolumeInformation(TEXT("C:\\"), NULL, NULL, &dwVolSerial, NULL, NULL, NULL, NULL);
+ bIsRetrieved = GetVolumeInformation(TEXT("C:\\"), nullptr, 0, &dwVolSerial, nullptr, nullptr, nullptr, 0);
if (bIsRetrieved)
return QString::number(dwVolSerial, 16);
@@ -18,7 +18,6 @@ QString get_hdid()
//a totally random string
//what could possibly go wrong
return "gxsps32sa9fnwic92mfbs0";
-
}
#elif (defined (LINUX) || defined (__linux__))
diff --git a/src/networkmanager.cpp b/src/networkmanager.cpp
index 288a9007..5b16db40 100644
--- a/src/networkmanager.cpp
+++ b/src/networkmanager.cpp
@@ -78,9 +78,9 @@ void NetworkManager::ship_server_packet(QString p_packet)
void NetworkManager::handle_ms_packet()
{
- char buffer[buffer_max_size];
- std::memset(buffer, 0, buffer_max_size);
- ms_socket->read(buffer, buffer_max_size);
+ char buffer[BUFFER_MAX_SIZE];
+ std::memset(buffer, 0, BUFFER_MAX_SIZE);
+ ms_socket->read(buffer, BUFFER_MAX_SIZE);
QString in_data = buffer;
@@ -137,7 +137,9 @@ void NetworkManager::on_srv_lookup()
for (const QDnsServiceRecord &record : srv_records)
{
+#ifdef DEBUG_NETWORK
qDebug() << "Connecting to " << record.target() << ":" << record.port();
+#endif
ms_socket->connectToHost(record.target(), record.port());
QTime timer;
timer.start();
@@ -206,7 +208,7 @@ void NetworkManager::on_ms_socket_error(QAbstractSocket::SocketError error)
emit ms_connect_finished(false, true);
- ms_reconnect_timer->start(ms_reconnect_delay_ms);
+ ms_reconnect_timer->start(ms_reconnect_delay * 1000);
}
void NetworkManager::retry_ms_connect()
@@ -217,9 +219,9 @@ void NetworkManager::retry_ms_connect()
void NetworkManager::handle_server_packet()
{
- char buffer[buffer_max_size];
- std::memset(buffer, 0, buffer_max_size);
- server_socket->read(buffer, buffer_max_size);
+ char buffer[BUFFER_MAX_SIZE];
+ std::memset(buffer, 0, BUFFER_MAX_SIZE);
+ server_socket->read(buffer, BUFFER_MAX_SIZE);
QString in_data = buffer;
diff --git a/src/packet_distribution.cpp b/src/packet_distribution.cpp
index 68d2fb9b..942c275d 100644
--- a/src/packet_distribution.cpp
+++ b/src/packet_distribution.cpp
@@ -14,8 +14,10 @@ void AOApplication::ms_packet_received(AOPacket *p_packet)
QString header = p_packet->get_header();
QStringList f_contents = p_packet->get_contents();
+#ifdef DEBUG_NETWORK
if (header != "CHECK")
qDebug() << "R(ms):" << p_packet->to_string();
+#endif
if (header == "ALL")
{
@@ -127,8 +129,10 @@ void AOApplication::server_packet_received(AOPacket *p_packet)
QStringList f_contents = p_packet->get_contents();
QString f_packet = p_packet->to_string();
+#ifdef DEBUG_NETWORK
if (header != "checkconnection")
qDebug() << "R:" << f_packet;
+#endif
if (header == "decryptor")
{
@@ -677,7 +681,9 @@ void AOApplication::send_ms_packet(AOPacket *p_packet)
net_manager->ship_ms_packet(f_packet);
+#ifdef DEBUG_NETWORK
qDebug() << "S(ms):" << f_packet;
+#endif
delete p_packet;
}
@@ -691,14 +697,18 @@ void AOApplication::send_server_packet(AOPacket *p_packet, bool encoded)
if (encryption_needed)
{
+#ifdef DEBUG_NETWORK
qDebug() << "S(e):" << f_packet;
+#endif
p_packet->encrypt_header(s_decryptor);
f_packet = p_packet->to_string();
}
else
{
+#ifdef DEBUG_NETWORK
qDebug() << "S:" << f_packet;
+#endif
}
net_manager->ship_server_packet(f_packet);