diff --git a/.ci/app.rc b/.ci/app.rc new file mode 100644 index 0000000..8a785d2 --- /dev/null +++ b/.ci/app.rc @@ -0,0 +1,33 @@ +IDI_ICON1 ICON DISCARDABLE "5sync.ico" +#define RT_MANIFEST 24 +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "gta5view.exe.manifest" +#include +VS_VERSION_INFO VERSIONINFO +FILEVERSION MAJOR_VER, MINOR_VER, PATCH_VER, INT_BUILD_VER +PRODUCTVERSION MAJOR_VER, MINOR_VER, PATCH_VER, INT_BUILD_VER +FILEFLAGSMASK 0x3fL +FILEFLAGS 0 +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0809, 1200 + END + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Syping" + VALUE "FileDescription", "gta5view" + VALUE "FileVersion", "MAJOR_VER.MINOR_VER.PATCH_VERSTR_BUILD_VER" + VALUE "InternalName", "gta5view" + VALUE "LegalCopyright", "Copyright 2016-2023 Syping" + VALUE "OriginalFilename", "gta5view.exe" + VALUE "ProductName", "gta5view" + VALUE "ProductVersion", "MAJOR_VER.MINOR_VER.PATCH_VERSTR_BUILD_VER" + END + END +END diff --git a/.ci/ci.sh b/.ci/ci.sh new file mode 100755 index 0000000..8cc49f6 --- /dev/null +++ b/.ci/ci.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash + +if [ $(git name-rev --tags --name-only $(git rev-parse HEAD)) == "undefined" ]; then + export APPLICATION_VERSION=$(lua -e 'for line in io.lines("config.h") do local m = string.match(line, "#define GTA5SYNC_APPVER \"(.+)\"$"); if m then print(m); os.exit(0) end end') +else + export APPLICATION_VERSION=$(git name-rev --tags --name-only $(git rev-parse HEAD)) +fi +export PACKAGE_VERSION=$(grep -oE '^[^\-]*' <<< $APPLICATION_VERSION) +export PACKAGE_BUILD=$(grep -oP '\-\K.+' <<< $APPLICATION_VERSION) +export EXECUTABLE_VERSION=${PACKAGE_VERSION}${PACKAGE_BUILD}${EXECUTABLE_TAG} + +export APPLICATION_MAJOR_VERSION=$(cut -d. -f1 <<< $APPLICATION_VERSION) +export APPLICATION_MINOR_VERSION=$(cut -d. -f2 <<< $APPLICATION_VERSION) +export APPLICATION_PATCH_VERSION=$(cut -d. -f3 <<< $APPLICATION_VERSION) + +if [ "${PACKAGE_BUILD}" == "" ]; then + export PACKAGE_BUILD=1 +else + export APPLICATION_BUILD_INT_VERSION=$(grep -oE '[1-9]*$' <<< $PACKAGE_BUILD) + export APPLICATION_BUILD_STR_VERSION=-${PACKAGE_BUILD} +fi + +cat ".ci/app.rc" | sed \ + -e "s/MAJOR_VER/$APPLICATION_MAJOR_VERSION/g" \ + -e "s/MINOR_VER/$APPLICATION_MINOR_VERSION/g" \ + -e "s/PATCH_VER/$APPLICATION_PATCH_VERSION/g" \ + -e "s/INT_BUILD_VER/0/g" \ + -e "s/STR_BUILD_VER/$APPLICATION_BUILD_STR_VERSION/g" \ + -e "s/STR_BUILD_VER/$APPLICATION_BUILD_STR_VERSION/g" \ + > "res/app.rc" + +if [ "${BUILD_TYPE}" == "ALPHA" ]; then + export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Alpha" + export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Alpha\\\\\\\"" +elif [ "${BUILD_TYPE}" == "Alpha" ]; then + export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Alpha" + export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Alpha\\\\\\\"" +elif [ "${BUILD_TYPE}" == "BETA" ]; then + export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Beta" + export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Beta\\\\\\\"" +elif [ "${BUILD_TYPE}" == "Beta" ]; then + export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Beta" + export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Beta\\\\\\\"" +elif [ "${BUILD_TYPE}" == "DEV" ]; then + export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Developer" + export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Developer\\\\\\\"" +elif [ "${BUILD_TYPE}" == "Development" ]; then + export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Developer" + export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Developer\\\\\\\"" +elif [ "${BUILD_TYPE}" == "DAILY" ]; then + export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Daily Build" + export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Daily Build\\\\\\\"" +elif [ "${BUILD_TYPE}" == "Daily" ]; then + export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Daily Build" + export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Daily Build\\\\\\\"" +elif [ "${BUILD_TYPE}" == "RC" ]; then + export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Release Candidate" + export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Release Candidate\\\\\\\"" +elif [ "${BUILD_TYPE}" == "Release Candidate" ]; then + export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Release Candidate" + export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Release Candidate\\\\\\\"" +elif [ "${BUILD_TYPE}" == "REL" ]; then + export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Release" + export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Release\\\\\\\"" +elif [ "${BUILD_TYPE}" == "Release" ]; then + export CMAKE_BUILD_TYPE="-DGTA5VIEW_BUILDTYPE=Release" + export QMAKE_BUILD_TYPE="DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Release\\\\\\\"" +fi + +export PROJECT_DIR=$(pwd) + +.ci/${BUILD_SCRIPT} diff --git a/.ci/debian_build.sh b/.ci/debian_build.sh new file mode 100755 index 0000000..57aa9c9 --- /dev/null +++ b/.ci/debian_build.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# Creating folders +cd ${PROJECT_DIR} && \ +echo "gta5view build version is ${APPLICATION_VERSION}" && \ +mkdir -p build && \ +mkdir -p assets && \ +chmod -x res/gta5sync_*.qm res/*.desktop res/*gta5view*.png && \ +cd build && \ +mkdir -p qt5 && \ +cd qt5 && \ +echo "Grand Theft Auto V Snapmatic and Savegame viewer/editor" > ./description-pak && \ +cd .. && \ + +# Set compiler +export CC=clang && \ +export CXX=clang++ && \ + +# Prepare checkinstall step +mkdir -p /usr/share/gta5view && \ + +# Starting build +cd qt5 && \ +cmake \ + "-DCMAKE_INSTALL_PREFIX=/usr" \ + "${CMAKE_BUILD_TYPE}" \ + "-DFORCE_QT_VERSION=5" \ + "-DGTA5VIEW_BUILDCODE=${PACKAGE_CODE}" \ + "-DGTA5VIEW_APPVER=${APPLICATION_VERSION}" \ + "-DGTA5VIEW_COMMIT=${APPLICATION_COMMIT}" \ + "-DWITH_DONATE=ON" \ + "-DWITH_TELEMETRY=ON" \ + "-DDONATE_ADDRESSES=$(cat ${PROJECT_DIR}/.ci/donate.txt)" \ + "-DTELEMETRY_WEBURL=https://dev.syping.de/gta5view-userstats/" \ + "-DQCONF_BUILD=ON" \ + ../../ && \ +make -j 4 && \ +checkinstall -D --default --nodoc --install=no --pkgname=gta5view --pkgversion=${PACKAGE_VERSION} --pkgrelease=${PACKAGE_BUILD} --pkggroup=utility --maintainer="Syping \" --requires=libqt5core5a,libqt5gui5,libqt5network5,libqt5svg5,libqt5widgets5,qttranslations5-l10n --conflicts=gta5view-qt4,gta5view-qt5 --replaces=gta5view-qt4,gta5view-qt5 --pakdir=${PROJECT_DIR}/assets diff --git a/.ci/debian_ci.sh b/.ci/debian_ci.sh new file mode 100755 index 0000000..bc14a86 --- /dev/null +++ b/.ci/debian_ci.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +# Install packages +.ci/debian_install.sh && \ + +# Build gta5view +.ci/debian_build.sh && \ +cd ${PROJECT_DIR} diff --git a/.ci/debian_docker.sh b/.ci/debian_docker.sh new file mode 100755 index 0000000..80f6c42 --- /dev/null +++ b/.ci/debian_docker.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +if [ "${DOCKER_USER}" != "" ]; then + DOCKER_IMAGE=${DOCKER_USER}/debian:${DEBIAN_VERSION} +else + DOCKER_IMAGE=debian:${DEBIAN_VERSION} +fi +PROJECT_DIR_DOCKER=/gta5view + +cd ${PROJECT_DIR} && \ +docker pull ${DOCKER_IMAGE} && \ +docker run --rm \ + -v "${PROJECT_DIR}:${PROJECT_DIR_DOCKER}" \ + ${DOCKER_IMAGE} \ + /bin/bash -c "export PROJECT_DIR=${PROJECT_DIR_DOCKER} && export QT_SELECT=${QT_SELECT} && export APPLICATION_VERSION=${APPLICATION_VERSION} && export APPLICATION_COMMIT=${APPLICATION_COMMIT} && export BUILD_TYPE=${BUILD_TYPE} && export APT_INSTALL=${APT_INSTALL} && export QMAKE_FLAGS_QT4=${QMAKE_FLAGS_QT4} && export QMAKE_FLAGS_QT5=${QMAKE_FLAGS_QT5} && export CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} && export QMAKE_BUILD_TYPE=${QMAKE_BUILD_TYPE} && export PACKAGE_VERSION=${PACKAGE_VERSION} && export PACKAGE_BUILD=${PACKAGE_BUILD} && export PACKAGE_CODE=${PACKAGE_CODE} && export EXECUTABLE_VERSION=${EXECUTABLE_VERSION} && export EXECUTABLE_ARCH=${EXECUTABLE_ARCH} && cd ${PROJECT_DIR_DOCKER} && .ci/debian_install.sh && .ci/debian_build.sh" diff --git a/.ci/debian_install.sh b/.ci/debian_install.sh new file mode 100755 index 0000000..12356e9 --- /dev/null +++ b/.ci/debian_install.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +# Source OS Release +source /etc/os-release + +# When Debian add backports +if [ "${ID}" == "debian" ]; then + echo "deb http://deb.debian.org/debian ${VERSION_CODENAME}-backports main" >> /etc/apt/sources.list +fi + +# Install packages +apt-get update -qq && \ +apt-get install -qq ${APT_INSTALL} checkinstall cmake dpkg-dev fakeroot g++ gcc qtbase5-dev qt5-qmake qttranslations5-l10n libqt5svg5-dev diff --git a/.ci/donate.txt b/.ci/donate.txt new file mode 100644 index 0000000..407130d --- /dev/null +++ b/.ci/donate.txt @@ -0,0 +1 @@ +btc:187NSQSPzdMpQDGhxZAuw4AhZ7LgoAPV7D,eth:0x19d71DfCa86104d37a13D3c5d419936421CDC569,ltc:LKr6yvBoMMGmcxViS8Kc1A2sDjVSWTXn4m,xmr:43TB3ZMP5nk1pu5EQXRGPzdTKvmFEBGgccX3tNhRknLLiUYQ7z7dNedVHEA6WrWdByZv1isvFmjSGhCF7ddx3eRxFdm5Fzz \ No newline at end of file diff --git a/.ci/dropbox_uploader.sh b/.ci/dropbox_uploader.sh new file mode 100755 index 0000000..ca8ee36 --- /dev/null +++ b/.ci/dropbox_uploader.sh @@ -0,0 +1,1763 @@ +#!/usr/bin/env bash +# +# Dropbox Uploader +# +# Copyright (C) 2010-2017 Andrea Fabrizi +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +#Default configuration file +CONFIG_FILE=~/.dropbox_uploader + +#Default chunk size in Mb for the upload process +#It is recommended to increase this value only if you have enough free space on your /tmp partition +#Lower values may increase the number of http requests +CHUNK_SIZE=50 + +#Curl location +#If not set, curl will be searched into the $PATH +#CURL_BIN="/usr/bin/curl" + +#Default values +TMP_DIR="/tmp" +DEBUG=0 +QUIET=0 +SHOW_PROGRESSBAR=0 +SKIP_EXISTING_FILES=0 +ERROR_STATUS=0 +EXCLUDE=() + +#Don't edit these... +API_LONGPOLL_FOLDER="https://notify.dropboxapi.com/2/files/list_folder/longpoll" +API_CHUNKED_UPLOAD_START_URL="https://content.dropboxapi.com/2/files/upload_session/start" +API_CHUNKED_UPLOAD_FINISH_URL="https://content.dropboxapi.com/2/files/upload_session/finish" +API_CHUNKED_UPLOAD_APPEND_URL="https://content.dropboxapi.com/2/files/upload_session/append_v2" +API_UPLOAD_URL="https://content.dropboxapi.com/2/files/upload" +API_DOWNLOAD_URL="https://content.dropboxapi.com/2/files/download" +API_DELETE_URL="https://api.dropboxapi.com/2/files/delete" +API_MOVE_URL="https://api.dropboxapi.com/2/files/move" +API_COPY_URL="https://api.dropboxapi.com/2/files/copy" +API_METADATA_URL="https://api.dropboxapi.com/2/files/get_metadata" +API_LIST_FOLDER_URL="https://api.dropboxapi.com/2/files/list_folder" +API_LIST_FOLDER_CONTINUE_URL="https://api.dropboxapi.com/2/files/list_folder/continue" +API_ACCOUNT_INFO_URL="https://api.dropboxapi.com/2/users/get_current_account" +API_ACCOUNT_SPACE_URL="https://api.dropboxapi.com/2/users/get_space_usage" +API_MKDIR_URL="https://api.dropboxapi.com/2/files/create_folder" +API_SHARE_URL="https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings" +API_SHARE_LIST="https://api.dropboxapi.com/2/sharing/list_shared_links" +API_SAVEURL_URL="https://api.dropboxapi.com/2/files/save_url" +API_SAVEURL_JOBSTATUS_URL="https://api.dropboxapi.com/2/files/save_url/check_job_status" +API_SEARCH_URL="https://api.dropboxapi.com/2/files/search" +APP_CREATE_URL="https://www.dropbox.com/developers/apps" +RESPONSE_FILE="$TMP_DIR/du_resp_$RANDOM" +CHUNK_FILE="$TMP_DIR/du_chunk_$RANDOM" +TEMP_FILE="$TMP_DIR/du_tmp_$RANDOM" +BIN_DEPS="sed basename date grep stat dd mkdir" +VERSION="1.0" + +umask 077 + +#Check the shell +if [ -z "$BASH_VERSION" ]; then + echo -e "Error: this script requires the BASH shell!" + exit 1 +fi + +shopt -s nullglob #Bash allows filename patterns which match no files to expand to a null string, rather than themselves +shopt -s dotglob #Bash includes filenames beginning with a "." in the results of filename expansion + +#Check temp folder +if [[ ! -d "$TMP_DIR" ]]; then + echo -e "Error: the temporary folder $TMP_DIR doesn't exists!" + echo -e "Please edit this script and set the TMP_DIR variable to a valid temporary folder to use." + exit 1 +fi + +#Look for optional config file parameter +while getopts ":qpskdhf:x:" opt; do + case $opt in + + f) + CONFIG_FILE=$OPTARG + ;; + + d) + DEBUG=1 + ;; + + q) + QUIET=1 + ;; + + p) + SHOW_PROGRESSBAR=1 + ;; + + k) + CURL_ACCEPT_CERTIFICATES="-k" + ;; + + s) + SKIP_EXISTING_FILES=1 + ;; + + h) + HUMAN_READABLE_SIZE=1 + ;; + + x) + EXCLUDE+=( $OPTARG ) + ;; + + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + + :) + echo "Option -$OPTARG requires an argument." >&2 + exit 1 + ;; + + esac +done + +if [[ $DEBUG != 0 ]]; then + echo $VERSION + uname -a 2> /dev/null + cat /etc/issue 2> /dev/null + set -x + RESPONSE_FILE="$TMP_DIR/du_resp_debug" +fi + +if [[ $CURL_BIN == "" ]]; then + BIN_DEPS="$BIN_DEPS curl" + CURL_BIN="curl" +fi + +#Dependencies check +which $BIN_DEPS > /dev/null +if [[ $? != 0 ]]; then + for i in $BIN_DEPS; do + which $i > /dev/null || + NOT_FOUND="$i $NOT_FOUND" + done + echo -e "Error: Required program could not be found: $NOT_FOUND" + exit 1 +fi + +#Check if readlink is installed and supports the -m option +#It's not necessary, so no problem if it's not installed +which readlink > /dev/null +if [[ $? == 0 && $(readlink -m "//test" 2> /dev/null) == "/test" ]]; then + HAVE_READLINK=1 +else + HAVE_READLINK=0 +fi + +#Forcing to use the builtin printf, if it's present, because it's better +#otherwise the external printf program will be used +#Note that the external printf command can cause character encoding issues! +builtin printf "" 2> /dev/null +if [[ $? == 0 ]]; then + PRINTF="builtin printf" + PRINTF_OPT="-v o" +else + PRINTF=$(which printf) + if [[ $? != 0 ]]; then + echo -e "Error: Required program could not be found: printf" + fi + PRINTF_OPT="" +fi + +#Print the message based on $QUIET variable +function print +{ + if [[ $QUIET == 0 ]]; then + echo -ne "$1"; + fi +} + +#Returns unix timestamp +function utime +{ + date '+%s' +} + +#Remove temporary files +function remove_temp_files +{ + if [[ $DEBUG == 0 ]]; then + rm -fr "$RESPONSE_FILE" + rm -fr "$CHUNK_FILE" + rm -fr "$TEMP_FILE" + fi +} + +#Converts bytes to human readable format +function convert_bytes +{ + if [[ $HUMAN_READABLE_SIZE == 1 && "$1" != "" ]]; then + if (($1 > 1073741824));then + echo $(($1/1073741824)).$(($1%1073741824/100000000))"G"; + elif (($1 > 1048576));then + echo $(($1/1048576)).$(($1%1048576/100000))"M"; + elif (($1 > 1024));then + echo $(($1/1024)).$(($1%1024/100))"K"; + else + echo $1; + fi + else + echo $1; + fi +} + +#Returns the file size in bytes +function file_size +{ + #Generic GNU + SIZE=$(stat --format="%s" "$1" 2> /dev/null) + if [ $? -eq 0 ]; then + echo $SIZE + return + fi + + #Some embedded linux devices + SIZE=$(stat -c "%s" "$1" 2> /dev/null) + if [ $? -eq 0 ]; then + echo $SIZE + return + fi + + #BSD, OSX and other OSs + SIZE=$(stat -f "%z" "$1" 2> /dev/null) + if [ $? -eq 0 ]; then + echo $SIZE + return + fi + + echo "0" +} + + +#Usage +function usage +{ + echo -e "Dropbox Uploader v$VERSION" + echo -e "Andrea Fabrizi - andrea.fabrizi@gmail.com\n" + echo -e "Usage: $0 [PARAMETERS] COMMAND..." + echo -e "\nCommands:" + + echo -e "\t upload " + echo -e "\t download [LOCAL_FILE/DIR]" + echo -e "\t delete " + echo -e "\t move " + echo -e "\t copy " + echo -e "\t mkdir " + echo -e "\t list [REMOTE_DIR]" + echo -e "\t monitor [REMOTE_DIR] [TIMEOUT]" + echo -e "\t share " + echo -e "\t saveurl " + echo -e "\t search " + echo -e "\t info" + echo -e "\t space" + echo -e "\t unlink" + + echo -e "\nOptional parameters:" + echo -e "\t-f Load the configuration file from a specific file" + echo -e "\t-s Skip already existing files when download/upload. Default: Overwrite" + echo -e "\t-d Enable DEBUG mode" + echo -e "\t-q Quiet mode. Don't show messages" + echo -e "\t-h Show file sizes in human readable format" + echo -e "\t-p Show cURL progress meter" + echo -e "\t-k Doesn't check for SSL certificates (insecure)" + echo -e "\t-x Ignores/excludes directories or files from syncing. -x filename -x directoryname. example: -x .git" + + echo -en "\nFor more info and examples, please see the README file.\n\n" + remove_temp_files + exit 1 +} + +#Check the curl exit code +function check_http_response +{ + CODE=$? + + #Checking curl exit code + case $CODE in + + #OK + 0) + + ;; + + #Proxy error + 5) + print "\nError: Couldn't resolve proxy. The given proxy host could not be resolved.\n" + + remove_temp_files + exit 1 + ;; + + #Missing CA certificates + 60|58|77) + print "\nError: cURL is not able to performs peer SSL certificate verification.\n" + print "Please, install the default ca-certificates bundle.\n" + print "To do this in a Debian/Ubuntu based system, try:\n" + print " sudo apt-get install ca-certificates\n\n" + print "If the problem persists, try to use the -k option (insecure).\n" + + remove_temp_files + exit 1 + ;; + + 6) + print "\nError: Couldn't resolve host.\n" + + remove_temp_files + exit 1 + ;; + + 7) + print "\nError: Couldn't connect to host.\n" + + remove_temp_files + exit 1 + ;; + + esac + + #Checking response file for generic errors + if grep -q "HTTP/1.1 400" "$RESPONSE_FILE"; then + ERROR_MSG=$(sed -n -e 's/{"error": "\([^"]*\)"}/\1/p' "$RESPONSE_FILE") + + case $ERROR_MSG in + *access?attempt?failed?because?this?app?is?not?configured?to?have*) + echo -e "\nError: The Permission type/Access level configured doesn't match the DropBox App settings!\nPlease run \"$0 unlink\" and try again." + exit 1 + ;; + esac + + fi + +} + +#Urlencode +function urlencode +{ + #The printf is necessary to correctly decode unicode sequences + local string=$($PRINTF "${1}") + local strlen=${#string} + local encoded="" + + for (( pos=0 ; pos /dev/null + check_http_response + + local TYPE=$(sed -n 's/{".tag": *"*\([^"]*\)"*.*/\1/p' "$RESPONSE_FILE") + + case $TYPE in + + file) + echo "FILE" + ;; + + folder) + echo "DIR" + ;; + + deleted) + echo "ERR" + ;; + + *) + echo "ERR" + ;; + + esac +} + +#Generic upload wrapper around db_upload_file and db_upload_dir functions +#$1 = Local source file/dir +#$2 = Remote destination file/dir +function db_upload +{ + local SRC=$(normalize_path "$1") + local DST=$(normalize_path "$2") + + for j in "${EXCLUDE[@]}" + do : + if [[ $(echo "$SRC" | grep "$j" | wc -l) -gt 0 ]]; then + print "Skipping excluded file/dir: "$j + return + fi + done + + #Checking if the file/dir exists + if [[ ! -e $SRC && ! -d $SRC ]]; then + print " > No such file or directory: $SRC\n" + ERROR_STATUS=1 + return + fi + + #Checking if the file/dir has read permissions + if [[ ! -r $SRC ]]; then + print " > Error reading file $SRC: permission denied\n" + ERROR_STATUS=1 + return + fi + + TYPE=$(db_stat "$DST") + + #If DST it's a file, do nothing, it's the default behaviour + if [[ $TYPE == "FILE" ]]; then + DST="$DST" + + #if DST doesn't exists and doesn't ends with a /, it will be the destination file name + elif [[ $TYPE == "ERR" && "${DST: -1}" != "/" ]]; then + DST="$DST" + + #if DST doesn't exists and ends with a /, it will be the destination folder + elif [[ $TYPE == "ERR" && "${DST: -1}" == "/" ]]; then + local filename=$(basename "$SRC") + DST="$DST/$filename" + + #If DST it's a directory, it will be the destination folder + elif [[ $TYPE == "DIR" ]]; then + local filename=$(basename "$SRC") + DST="$DST/$filename" + fi + + #It's a directory + if [[ -d $SRC ]]; then + db_upload_dir "$SRC" "$DST" + + #It's a file + elif [[ -e $SRC ]]; then + db_upload_file "$SRC" "$DST" + + #Unsupported object... + else + print " > Skipping not regular file \"$SRC\"\n" + fi +} + +#Generic upload wrapper around db_chunked_upload_file and db_simple_upload_file +#The final upload function will be choosen based on the file size +#$1 = Local source file +#$2 = Remote destination file +function db_upload_file +{ + local FILE_SRC=$(normalize_path "$1") + local FILE_DST=$(normalize_path "$2") + + shopt -s nocasematch + + #Checking not allowed file names + basefile_dst=$(basename "$FILE_DST") + if [[ $basefile_dst == "thumbs.db" || \ + $basefile_dst == "desktop.ini" || \ + $basefile_dst == ".ds_store" || \ + $basefile_dst == "icon\r" || \ + $basefile_dst == ".dropbox" || \ + $basefile_dst == ".dropbox.attr" \ + ]]; then + print " > Skipping not allowed file name \"$FILE_DST\"\n" + return + fi + + shopt -u nocasematch + + #Checking file size + FILE_SIZE=$(file_size "$FILE_SRC") + + #Checking if the file already exists + TYPE=$(db_stat "$FILE_DST") + if [[ $TYPE != "ERR" && $SKIP_EXISTING_FILES == 1 ]]; then + print " > Skipping already existing file \"$FILE_DST\"\n" + return + fi + + # Checking if the file has the correct check sum + if [[ $TYPE != "ERR" ]]; then + sha_src=$(db_sha_local "$FILE_SRC") + sha_dst=$(db_sha "$FILE_DST") + if [[ $sha_src == $sha_dst && $sha_src != "ERR" ]]; then + print "> Skipping file \"$FILE_SRC\", file exists with the same hash\n" + return + fi + fi + + if [[ $FILE_SIZE -gt 157286000 ]]; then + #If the file is greater than 150Mb, the chunked_upload API will be used + db_chunked_upload_file "$FILE_SRC" "$FILE_DST" + else + db_simple_upload_file "$FILE_SRC" "$FILE_DST" + fi + +} + +#Simple file upload +#$1 = Local source file +#$2 = Remote destination file +function db_simple_upload_file +{ + local FILE_SRC=$(normalize_path "$1") + local FILE_DST=$(normalize_path "$2") + + if [[ $SHOW_PROGRESSBAR == 1 && $QUIET == 0 ]]; then + CURL_PARAMETERS="--progress-bar" + LINE_CR="\n" + else + CURL_PARAMETERS="-L -s" + LINE_CR="" + fi + + print " > Uploading \"$FILE_SRC\" to \"$FILE_DST\"... $LINE_CR" + $CURL_BIN $CURL_ACCEPT_CERTIFICATES $CURL_PARAMETERS -X POST -i --globoff -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Dropbox-API-Arg: {\"path\": \"$FILE_DST\",\"mode\": \"overwrite\",\"autorename\": true,\"mute\": false}" --header "Content-Type: application/octet-stream" --data-binary @"$FILE_SRC" "$API_UPLOAD_URL" + check_http_response + + #Check + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + print "DONE\n" + else + print "FAILED\n" + print "An error occurred requesting /upload\n" + ERROR_STATUS=1 + fi +} + +#Chunked file upload +#$1 = Local source file +#$2 = Remote destination file +function db_chunked_upload_file +{ + local FILE_SRC=$(normalize_path "$1") + local FILE_DST=$(normalize_path "$2") + + + if [[ $SHOW_PROGRESSBAR == 1 && $QUIET == 0 ]]; then + VERBOSE=1 + CURL_PARAMETERS="--progress-bar" + else + VERBOSE=0 + CURL_PARAMETERS="-L -s" + fi + + + + local FILE_SIZE=$(file_size "$FILE_SRC") + local OFFSET=0 + local UPLOAD_ID="" + local UPLOAD_ERROR=0 + local CHUNK_PARAMS="" + + ## Ceil division + let NUMBEROFCHUNK=($FILE_SIZE/1024/1024+$CHUNK_SIZE-1)/$CHUNK_SIZE + + if [[ $VERBOSE == 1 ]]; then + print " > Uploading \"$FILE_SRC\" to \"$FILE_DST\" by $NUMBEROFCHUNK chunks ...\n" + else + print " > Uploading \"$FILE_SRC\" to \"$FILE_DST\" by $NUMBEROFCHUNK chunks " + fi + + #Starting a new upload session + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Dropbox-API-Arg: {\"close\": false}" --header "Content-Type: application/octet-stream" --data-binary @/dev/null "$API_CHUNKED_UPLOAD_START_URL" 2> /dev/null + check_http_response + + SESSION_ID=$(sed -n 's/{"session_id": *"*\([^"]*\)"*.*/\1/p' "$RESPONSE_FILE") + + chunkNumber=1 + #Uploading chunks... + while ([[ $OFFSET != "$FILE_SIZE" ]]); do + + let OFFSET_MB=$OFFSET/1024/1024 + + #Create the chunk + dd if="$FILE_SRC" of="$CHUNK_FILE" bs=1048576 skip=$OFFSET_MB count=$CHUNK_SIZE 2> /dev/null + local CHUNK_REAL_SIZE=$(file_size "$CHUNK_FILE") + + if [[ $VERBOSE == 1 ]]; then + print " >> Uploading chunk $chunkNumber of $NUMBEROFCHUNK\n" + fi + + #Uploading the chunk... + echo > "$RESPONSE_FILE" + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST $CURL_PARAMETERS --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Dropbox-API-Arg: {\"cursor\": {\"session_id\": \"$SESSION_ID\",\"offset\": $OFFSET},\"close\": false}" --header "Content-Type: application/octet-stream" --data-binary @"$CHUNK_FILE" "$API_CHUNKED_UPLOAD_APPEND_URL" + #check_http_response not needed, because we have to retry the request in case of error + + #Check + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + let OFFSET=$OFFSET+$CHUNK_REAL_SIZE + UPLOAD_ERROR=0 + if [[ $VERBOSE != 1 ]]; then + print "." + fi + ((chunkNumber=chunkNumber+1)) + else + if [[ $VERBOSE != 1 ]]; then + print "*" + fi + let UPLOAD_ERROR=$UPLOAD_ERROR+1 + + #On error, the upload is retried for max 3 times + if [[ $UPLOAD_ERROR -gt 2 ]]; then + print " FAILED\n" + print "An error occurred requesting /chunked_upload\n" + ERROR_STATUS=1 + return + fi + fi + + done + + UPLOAD_ERROR=0 + + #Commit the upload + while (true); do + + echo > "$RESPONSE_FILE" + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Dropbox-API-Arg: {\"cursor\": {\"session_id\": \"$SESSION_ID\",\"offset\": $OFFSET},\"commit\": {\"path\": \"$FILE_DST\",\"mode\": \"overwrite\",\"autorename\": true,\"mute\": false}}" --header "Content-Type: application/octet-stream" --data-binary @/dev/null "$API_CHUNKED_UPLOAD_FINISH_URL" 2> /dev/null + #check_http_response not needed, because we have to retry the request in case of error + + #Check + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + UPLOAD_ERROR=0 + break + else + print "*" + let UPLOAD_ERROR=$UPLOAD_ERROR+1 + + #On error, the commit is retried for max 3 times + if [[ $UPLOAD_ERROR -gt 2 ]]; then + print " FAILED\n" + print "An error occurred requesting /commit_chunked_upload\n" + ERROR_STATUS=1 + return + fi + fi + + done + + print " DONE\n" +} + +#Directory upload +#$1 = Local source dir +#$2 = Remote destination dir +function db_upload_dir +{ + local DIR_SRC=$(normalize_path "$1") + local DIR_DST=$(normalize_path "$2") + + #Creatig remote directory + db_mkdir "$DIR_DST" + + for file in "$DIR_SRC/"*; do + db_upload "$file" "$DIR_DST" + done +} + +#Generic download wrapper +#$1 = Remote source file/dir +#$2 = Local destination file/dir +function db_download +{ + local SRC=$(normalize_path "$1") + local DST=$(normalize_path "$2") + + TYPE=$(db_stat "$SRC") + + #It's a directory + if [[ $TYPE == "DIR" ]]; then + + #If the DST folder is not specified, I assume that is the current directory + if [[ $DST == "" ]]; then + DST="." + fi + + #Checking if the destination directory exists + if [[ ! -d $DST ]]; then + local basedir="" + else + local basedir=$(basename "$SRC") + fi + + local DEST_DIR=$(normalize_path "$DST/$basedir") + print " > Downloading folder \"$SRC\" to \"$DEST_DIR\"... \n" + + if [[ ! -d "$DEST_DIR" ]]; then + print " > Creating local directory \"$DEST_DIR\"... " + mkdir -p "$DEST_DIR" + + #Check + if [[ $? == 0 ]]; then + print "DONE\n" + else + print "FAILED\n" + ERROR_STATUS=1 + return + fi + fi + + if [[ $SRC == "/" ]]; then + SRC_REQ="" + else + SRC_REQ="$SRC" + fi + + OUT_FILE=$(db_list_outfile "$SRC_REQ") + if [ $? -ne 0 ]; then + # When db_list_outfile fail, the error message is OUT_FILE + print "$OUT_FILE\n" + ERROR_STATUS=1 + return + fi + + #For each entry... + while read -r line; do + + local FILE=${line%:*} + local META=${line##*:} + local TYPE=${META%;*} + local SIZE=${META#*;} + + #Removing unneeded / + FILE=${FILE##*/} + + if [[ $TYPE == "file" ]]; then + db_download_file "$SRC/$FILE" "$DEST_DIR/$FILE" + elif [[ $TYPE == "folder" ]]; then + db_download "$SRC/$FILE" "$DEST_DIR" + fi + + done < $OUT_FILE + + rm -fr $OUT_FILE + + #It's a file + elif [[ $TYPE == "FILE" ]]; then + + #Checking DST + if [[ $DST == "" ]]; then + DST=$(basename "$SRC") + fi + + #If the destination is a directory, the file will be download into + if [[ -d $DST ]]; then + DST="$DST/$SRC" + fi + + db_download_file "$SRC" "$DST" + + #Doesn't exists + else + print " > No such file or directory: $SRC\n" + ERROR_STATUS=1 + return + fi +} + +#Simple file download +#$1 = Remote source file +#$2 = Local destination file +function db_download_file +{ + local FILE_SRC=$(normalize_path "$1") + local FILE_DST=$(normalize_path "$2") + + if [[ $SHOW_PROGRESSBAR == 1 && $QUIET == 0 ]]; then + CURL_PARAMETERS="-L --progress-bar" + LINE_CR="\n" + else + CURL_PARAMETERS="-L -s" + LINE_CR="" + fi + + #Checking if the file already exists + if [[ -e $FILE_DST && $SKIP_EXISTING_FILES == 1 ]]; then + print " > Skipping already existing file \"$FILE_DST\"\n" + return + fi + + # Checking if the file has the correct check sum + if [[ $TYPE != "ERR" ]]; then + sha_src=$(db_sha "$FILE_SRC") + sha_dst=$(db_sha_local "$FILE_DST") + if [[ $sha_src == $sha_dst && $sha_src != "ERR" ]]; then + print "> Skipping file \"$FILE_SRC\", file exists with the same hash\n" + return + fi + fi + + #Creating the empty file, that for two reasons: + #1) In this way I can check if the destination file is writable or not + #2) Curl doesn't automatically creates files with 0 bytes size + dd if=/dev/zero of="$FILE_DST" count=0 2> /dev/null + if [[ $? != 0 ]]; then + print " > Error writing file $FILE_DST: permission denied\n" + ERROR_STATUS=1 + return + fi + + print " > Downloading \"$FILE_SRC\" to \"$FILE_DST\"... $LINE_CR" + $CURL_BIN $CURL_ACCEPT_CERTIFICATES $CURL_PARAMETERS -X POST --globoff -D "$RESPONSE_FILE" -o "$FILE_DST" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Dropbox-API-Arg: {\"path\": \"$FILE_SRC\"}" "$API_DOWNLOAD_URL" + check_http_response + + #Check + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + print "DONE\n" + else + print "FAILED\n" + rm -fr "$FILE_DST" + ERROR_STATUS=1 + return + fi +} + +#Saveurl +#$1 = URL +#$2 = Remote file destination +function db_saveurl +{ + local URL="$1" + local FILE_DST=$(normalize_path "$2") + local FILE_NAME=$(basename "$URL") + + print " > Downloading \"$URL\" to \"$FILE_DST\"..." + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Content-Type: application/json" --data "{\"path\": \"$FILE_DST/$FILE_NAME\", \"url\": \"$URL\"}" "$API_SAVEURL_URL" 2> /dev/null + check_http_response + + JOB_ID=$(sed -n 's/.*"async_job_id": *"*\([^"]*\)"*.*/\1/p' "$RESPONSE_FILE") + if [[ $JOB_ID == "" ]]; then + print " > Error getting the job id\n" + return + fi + + #Checking the status + while (true); do + + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Content-Type: application/json" --data "{\"async_job_id\": \"$JOB_ID\"}" "$API_SAVEURL_JOBSTATUS_URL" 2> /dev/null + check_http_response + + STATUS=$(sed -n 's/{".tag": *"*\([^"]*\)"*.*/\1/p' "$RESPONSE_FILE") + case $STATUS in + + in_progress) + print "+" + ;; + + complete) + print " DONE\n" + break + ;; + + failed) + print " ERROR\n" + MESSAGE=$(sed -n 's/.*"error_summary": *"*\([^"]*\)"*.*/\1/p' "$RESPONSE_FILE") + print " > Error: $MESSAGE\n" + break + ;; + + esac + + sleep 2 + + done +} + +#Prints account info +function db_account_info +{ + print "Dropbox Uploader v$VERSION\n\n" + print " > Getting info... " + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" "$API_ACCOUNT_INFO_URL" 2> /dev/null + check_http_response + + #Check + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + + name=$(sed -n 's/.*"display_name": "\([^"]*\).*/\1/p' "$RESPONSE_FILE") + echo -e "\n\nName:\t\t$name" + + uid=$(sed -n 's/.*"account_id": "\([^"]*\).*/\1/p' "$RESPONSE_FILE") + echo -e "UID:\t\t$uid" + + email=$(sed -n 's/.*"email": "\([^"]*\).*/\1/p' "$RESPONSE_FILE") + echo -e "Email:\t\t$email" + + country=$(sed -n 's/.*"country": "\([^"]*\).*/\1/p' "$RESPONSE_FILE") + echo -e "Country:\t$country" + + echo "" + + else + print "FAILED\n" + ERROR_STATUS=1 + fi +} + +#Prints account space usage info +function db_account_space +{ + print "Dropbox Uploader v$VERSION\n\n" + print " > Getting space usage info... " + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" "$API_ACCOUNT_SPACE_URL" 2> /dev/null + check_http_response + + #Check + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + + quota=$(sed -n 's/.*"allocated": \([0-9]*\).*/\1/p' "$RESPONSE_FILE") + let quota_mb=$quota/1024/1024 + echo -e "\n\nQuota:\t$quota_mb Mb" + + used=$(sed -n 's/.*"used": \([0-9]*\).*/\1/p' "$RESPONSE_FILE") + let used_mb=$used/1024/1024 + echo -e "Used:\t$used_mb Mb" + + let free_mb=$((quota-used))/1024/1024 + echo -e "Free:\t$free_mb Mb" + + echo "" + + else + print "FAILED\n" + ERROR_STATUS=1 + fi +} + +#Account unlink +function db_unlink +{ + echo -ne "Are you sure you want unlink this script from your Dropbox account? [y/n]" + read -r answer + if [[ $answer == "y" ]]; then + rm -fr "$CONFIG_FILE" + echo -ne "DONE\n" + fi +} + +#Delete a remote file +#$1 = Remote file to delete +function db_delete +{ + local FILE_DST=$(normalize_path "$1") + + print " > Deleting \"$FILE_DST\"... " + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Content-Type: application/json" --data "{\"path\": \"$FILE_DST\"}" "$API_DELETE_URL" 2> /dev/null + check_http_response + + #Check + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + print "DONE\n" + else + print "FAILED\n" + ERROR_STATUS=1 + fi +} + +#Move/Rename a remote file +#$1 = Remote file to rename or move +#$2 = New file name or location +function db_move +{ + local FILE_SRC=$(normalize_path "$1") + local FILE_DST=$(normalize_path "$2") + + TYPE=$(db_stat "$FILE_DST") + + #If the destination it's a directory, the source will be moved into it + if [[ $TYPE == "DIR" ]]; then + local filename=$(basename "$FILE_SRC") + FILE_DST=$(normalize_path "$FILE_DST/$filename") + fi + + print " > Moving \"$FILE_SRC\" to \"$FILE_DST\" ... " + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Content-Type: application/json" --data "{\"from_path\": \"$FILE_SRC\", \"to_path\": \"$FILE_DST\"}" "$API_MOVE_URL" 2> /dev/null + check_http_response + + #Check + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + print "DONE\n" + else + print "FAILED\n" + ERROR_STATUS=1 + fi +} + +#Copy a remote file to a remote location +#$1 = Remote file to rename or move +#$2 = New file name or location +function db_copy +{ + local FILE_SRC=$(normalize_path "$1") + local FILE_DST=$(normalize_path "$2") + + TYPE=$(db_stat "$FILE_DST") + + #If the destination it's a directory, the source will be copied into it + if [[ $TYPE == "DIR" ]]; then + local filename=$(basename "$FILE_SRC") + FILE_DST=$(normalize_path "$FILE_DST/$filename") + fi + + print " > Copying \"$FILE_SRC\" to \"$FILE_DST\" ... " + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Content-Type: application/json" --data "{\"from_path\": \"$FILE_SRC\", \"to_path\": \"$FILE_DST\"}" "$API_COPY_URL" 2> /dev/null + check_http_response + + #Check + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + print "DONE\n" + else + print "FAILED\n" + ERROR_STATUS=1 + fi +} + +#Create a new directory +#$1 = Remote directory to create +function db_mkdir +{ + local DIR_DST=$(normalize_path "$1") + + print " > Creating Directory \"$DIR_DST\"... " + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Content-Type: application/json" --data "{\"path\": \"$DIR_DST\"}" "$API_MKDIR_URL" 2> /dev/null + check_http_response + + #Check + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + print "DONE\n" + elif grep -q "^HTTP/1.1 403 Forbidden" "$RESPONSE_FILE"; then + print "ALREADY EXISTS\n" + else + print "FAILED\n" + ERROR_STATUS=1 + fi +} + +#List a remote folder and returns the path to the file containing the output +#$1 = Remote directory +#$2 = Cursor (Optional) +function db_list_outfile +{ + + local DIR_DST="$1" + local HAS_MORE="false" + local CURSOR="" + + if [[ -n "$2" ]]; then + CURSOR="$2" + HAS_MORE="true" + fi + + OUT_FILE="$TMP_DIR/du_tmp_out_$RANDOM" + + while (true); do + + if [[ $HAS_MORE == "true" ]]; then + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Content-Type: application/json" --data "{\"cursor\": \"$CURSOR\"}" "$API_LIST_FOLDER_CONTINUE_URL" 2> /dev/null + else + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Content-Type: application/json" --data "{\"path\": \"$DIR_DST\",\"include_media_info\": false,\"include_deleted\": false,\"include_has_explicit_shared_members\": false}" "$API_LIST_FOLDER_URL" 2> /dev/null + fi + + check_http_response + + HAS_MORE=$(sed -n 's/.*"has_more": *\([a-z]*\).*/\1/p' "$RESPONSE_FILE") + CURSOR=$(sed -n 's/.*"cursor": *"\([^"]*\)".*/\1/p' "$RESPONSE_FILE") + + #Check + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + + #Extracting directory content [...] + #and replacing "}, {" with "}\n{" + #I don't like this piece of code... but seems to be the only way to do this with SED, writing a portable code... + local DIR_CONTENT=$(sed -n 's/.*: \[{\(.*\)/\1/p' "$RESPONSE_FILE" | sed 's/}, *{/}\ + {/g') + + #Converting escaped quotes to unicode format + echo "$DIR_CONTENT" | sed 's/\\"/\\u0022/' > "$TEMP_FILE" + + #Extracting files and subfolders + while read -r line; do + + local FILE=$(echo "$line" | sed -n 's/.*"path_display": *"\([^"]*\)".*/\1/p') + local TYPE=$(echo "$line" | sed -n 's/.*".tag": *"\([^"]*\).*/\1/p') + local SIZE=$(convert_bytes $(echo "$line" | sed -n 's/.*"size": *\([0-9]*\).*/\1/p')) + + echo -e "$FILE:$TYPE;$SIZE" >> "$OUT_FILE" + + done < "$TEMP_FILE" + + if [[ $HAS_MORE == "false" ]]; then + break + fi + + else + return + fi + + done + + echo $OUT_FILE +} + +#List remote directory +#$1 = Remote directory +function db_list +{ + local DIR_DST=$(normalize_path "$1") + + print " > Listing \"$DIR_DST\"... " + + if [[ "$DIR_DST" == "/" ]]; then + DIR_DST="" + fi + + OUT_FILE=$(db_list_outfile "$DIR_DST") + if [ -z "$OUT_FILE" ]; then + print "FAILED\n" + ERROR_STATUS=1 + return + else + print "DONE\n" + fi + + #Looking for the biggest file size + #to calculate the padding to use + local padding=0 + while read -r line; do + local FILE=${line%:*} + local META=${line##*:} + local SIZE=${META#*;} + + if [[ ${#SIZE} -gt $padding ]]; then + padding=${#SIZE} + fi + done < "$OUT_FILE" + + #For each entry, printing directories... + while read -r line; do + + local FILE=${line%:*} + local META=${line##*:} + local TYPE=${META%;*} + local SIZE=${META#*;} + + #Removing unneeded / + FILE=${FILE##*/} + + if [[ $TYPE == "folder" ]]; then + FILE=$(echo -e "$FILE") + $PRINTF " [D] %-${padding}s %s\n" "$SIZE" "$FILE" + fi + + done < "$OUT_FILE" + + #For each entry, printing files... + while read -r line; do + + local FILE=${line%:*} + local META=${line##*:} + local TYPE=${META%;*} + local SIZE=${META#*;} + + #Removing unneeded / + FILE=${FILE##*/} + + if [[ $TYPE == "file" ]]; then + FILE=$(echo -e "$FILE") + $PRINTF " [F] %-${padding}s %s\n" "$SIZE" "$FILE" + fi + + done < "$OUT_FILE" + + rm -fr "$OUT_FILE" +} + +#Longpoll remote directory only once +#$1 = Timeout +#$2 = Remote directory +function db_monitor_nonblock +{ + local TIMEOUT=$1 + local DIR_DST=$(normalize_path "$2") + + if [[ "$DIR_DST" == "/" ]]; then + DIR_DST="" + fi + + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Content-Type: application/json" --data "{\"path\": \"$DIR_DST\",\"include_media_info\": false,\"include_deleted\": false,\"include_has_explicit_shared_members\": false}" "$API_LIST_FOLDER_URL" 2> /dev/null + check_http_response + + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + + local CURSOR=$(sed -n 's/.*"cursor": *"\([^"]*\)".*/\1/p' "$RESPONSE_FILE") + + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Content-Type: application/json" --data "{\"cursor\": \"$CURSOR\",\"timeout\": ${TIMEOUT}}" "$API_LONGPOLL_FOLDER" 2> /dev/null + check_http_response + + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + local CHANGES=$(sed -n 's/.*"changes" *: *\([a-z]*\).*/\1/p' "$RESPONSE_FILE") + else + ERROR_MSG=$(grep "Error in call" "$RESPONSE_FILE") + print "FAILED to longpoll (http error): $ERROR_MSG\n" + ERROR_STATUS=1 + return 1 + fi + + if [[ -z "$CHANGES" ]]; then + print "FAILED to longpoll (unexpected response)\n" + ERROR_STATUS=1 + return 1 + fi + + if [ "$CHANGES" == "true" ]; then + + OUT_FILE=$(db_list_outfile "$DIR_DST" "$CURSOR") + + if [ -z "$OUT_FILE" ]; then + print "FAILED to list changes\n" + ERROR_STATUS=1 + return + fi + + #For each entry, printing directories... + while read -r line; do + + local FILE=${line%:*} + local META=${line##*:} + local TYPE=${META%;*} + local SIZE=${META#*;} + + #Removing unneeded / + FILE=${FILE##*/} + + if [[ $TYPE == "folder" ]]; then + FILE=$(echo -e "$FILE") + $PRINTF " [D] %s\n" "$FILE" + elif [[ $TYPE == "file" ]]; then + FILE=$(echo -e "$FILE") + $PRINTF " [F] %s %s\n" "$SIZE" "$FILE" + elif [[ $TYPE == "deleted" ]]; then + FILE=$(echo -e "$FILE") + $PRINTF " [-] %s\n" "$FILE" + fi + + done < "$OUT_FILE" + + rm -fr "$OUT_FILE" + fi + + else + ERROR_STATUS=1 + return 1 + fi + +} + +#Longpoll continuously remote directory +#$1 = Timeout +#$2 = Remote directory +function db_monitor +{ + local TIMEOUT=$1 + local DIR_DST=$(normalize_path "$2") + + while (true); do + db_monitor_nonblock "$TIMEOUT" "$2" + done +} + +#Share remote file +#$1 = Remote file +function db_share +{ + local FILE_DST=$(normalize_path "$1") + + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Content-Type: application/json" --data "{\"path\": \"$FILE_DST\",\"settings\": {\"requested_visibility\": \"public\"}}" "$API_SHARE_URL" 2> /dev/null + check_http_response + + #Check + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + print " > Share link: " + SHARE_LINK=$(sed -n 's/.*"url": "\([^"]*\).*/\1/p' "$RESPONSE_FILE") + echo "$SHARE_LINK" + else + get_Share "$FILE_DST" + fi +} + +#Query existing shared link +#$1 = Remote file +function get_Share +{ + local FILE_DST=$(normalize_path "$1") + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Content-Type: application/json" --data "{\"path\": \"$FILE_DST\",\"direct_only\": true}" "$API_SHARE_LIST" + check_http_response + + #Check + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + print " > Share link: " + SHARE_LINK=$(sed -n 's/.*"url": "\([^"]*\).*/\1/p' "$RESPONSE_FILE") + echo "$SHARE_LINK" + else + print "FAILED\n" + MESSAGE=$(sed -n 's/.*"error_summary": *"*\([^"]*\)"*.*/\1/p' "$RESPONSE_FILE") + print " > Error: $MESSAGE\n" + ERROR_STATUS=1 + fi +} + +#Search on Dropbox +#$1 = query +function db_search +{ + local QUERY="$1" + + print " > Searching for \"$QUERY\"... " + + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Content-Type: application/json" --data "{\"path\": \"\",\"query\": \"$QUERY\",\"start\": 0,\"max_results\": 1000,\"mode\": \"filename\"}" "$API_SEARCH_URL" 2> /dev/null + check_http_response + + #Check + if grep -q "^HTTP/1.1 200 OK" "$RESPONSE_FILE"; then + print "DONE\n" + else + print "FAILED\n" + ERROR_STATUS=1 + fi + + #Extracting directory content [...] + #and replacing "}, {" with "}\n{" + #I don't like this piece of code... but seems to be the only way to do this with SED, writing a portable code... + local DIR_CONTENT=$(sed 's/}, *{/}\ +{/g' "$RESPONSE_FILE") + + #Converting escaped quotes to unicode format + echo "$DIR_CONTENT" | sed 's/\\"/\\u0022/' > "$TEMP_FILE" + + #Extracting files and subfolders + rm -fr "$RESPONSE_FILE" + while read -r line; do + + local FILE=$(echo "$line" | sed -n 's/.*"path_display": *"\([^"]*\)".*/\1/p') + local TYPE=$(echo "$line" | sed -n 's/.*".tag": *"\([^"]*\).*/\1/p') + local SIZE=$(convert_bytes $(echo "$line" | sed -n 's/.*"size": *\([0-9]*\).*/\1/p')) + + echo -e "$FILE:$TYPE;$SIZE" >> "$RESPONSE_FILE" + + done < "$TEMP_FILE" + + #Looking for the biggest file size + #to calculate the padding to use + local padding=0 + while read -r line; do + local FILE=${line%:*} + local META=${line##*:} + local SIZE=${META#*;} + + if [[ ${#SIZE} -gt $padding ]]; then + padding=${#SIZE} + fi + done < "$RESPONSE_FILE" + + #For each entry, printing directories... + while read -r line; do + + local FILE=${line%:*} + local META=${line##*:} + local TYPE=${META%;*} + local SIZE=${META#*;} + + if [[ $TYPE == "folder" ]]; then + FILE=$(echo -e "$FILE") + $PRINTF " [D] %-${padding}s %s\n" "$SIZE" "$FILE" + fi + + done < "$RESPONSE_FILE" + + #For each entry, printing files... + while read -r line; do + + local FILE=${line%:*} + local META=${line##*:} + local TYPE=${META%;*} + local SIZE=${META#*;} + + if [[ $TYPE == "file" ]]; then + FILE=$(echo -e "$FILE") + $PRINTF " [F] %-${padding}s %s\n" "$SIZE" "$FILE" + fi + + done < "$RESPONSE_FILE" + +} + +#Query the sha256-dropbox-sum of a remote file +#see https://www.dropbox.com/developers/reference/content-hash for more information +#$1 = Remote file +function db_sha +{ + local FILE=$(normalize_path "$1") + + if [[ $FILE == "/" ]]; then + echo "ERR" + return + fi + + #Checking if it's a file or a directory and get the sha-sum + $CURL_BIN $CURL_ACCEPT_CERTIFICATES -X POST -L -s --show-error --globoff -i -o "$RESPONSE_FILE" --header "Authorization: Bearer $OAUTH_ACCESS_TOKEN" --header "Content-Type: application/json" --data "{\"path\": \"$FILE\"}" "$API_METADATA_URL" 2> /dev/null + check_http_response + + local TYPE=$(sed -n 's/{".tag": *"*\([^"]*\)"*.*/\1/p' "$RESPONSE_FILE") + if [[ $TYPE == "folder" ]]; then + echo "ERR" + return + fi + + local SHA256=$(sed -n 's/.*"content_hash": "\([^"]*\).*/\1/p' "$RESPONSE_FILE") + echo "$SHA256" +} + +#Query the sha256-dropbox-sum of a local file +#see https://www.dropbox.com/developers/reference/content-hash for more information +#$1 = Local file +function db_sha_local +{ + local FILE=$(normalize_path "$1") + local FILE_SIZE=$(file_size "$FILE") + local OFFSET=0 + local SKIP=0 + local SHA_CONCAT="" + + which shasum > /dev/null + if [[ $? != 0 ]]; then + echo "ERR" + return + fi + + while ([[ $OFFSET -lt "$FILE_SIZE" ]]); do + dd if="$FILE" of="$CHUNK_FILE" bs=4194304 skip=$SKIP count=1 2> /dev/null + local SHA=$(shasum -a 256 "$CHUNK_FILE" | awk '{print $1}') + SHA_CONCAT="${SHA_CONCAT}${SHA}" + + let OFFSET=$OFFSET+4194304 + let SKIP=$SKIP+1 + done + + shaHex=$(echo $SHA_CONCAT | sed 's/\([0-9A-F]\{2\}\)/\\x\1/gI') + echo -ne $shaHex | shasum -a 256 | awk '{print $1}' +} + +################ +#### SETUP #### +################ + +#CHECKING FOR AUTH FILE +if [[ -e $CONFIG_FILE ]]; then + + #Loading data... and change old format config if necesary. + source "$CONFIG_FILE" 2>/dev/null || { + sed -i'' 's/:/=/' "$CONFIG_FILE" && source "$CONFIG_FILE" 2>/dev/null + } + + #Checking if it's still a v1 API configuration file + if [[ $APPKEY != "" || $APPSECRET != "" ]]; then + echo -ne "The config file contains the old deprecated v1 oauth tokens.\n" + echo -ne "Please run again the script and follow the configuration wizard. The old configuration file has been backed up to $CONFIG_FILE.old\n" + mv "$CONFIG_FILE" "$CONFIG_FILE".old + exit 1 + fi + + #Checking loaded data + if [[ $OAUTH_ACCESS_TOKEN = "" ]]; then + echo -ne "Error loading data from $CONFIG_FILE...\n" + echo -ne "It is recommended to run $0 unlink\n" + remove_temp_files + exit 1 + fi + +#NEW SETUP... +else + + echo -ne "\n This is the first time you run this script, please follow the instructions:\n\n" + echo -ne " 1) Open the following URL in your Browser, and log in using your account: $APP_CREATE_URL\n" + echo -ne " 2) Click on \"Create App\", then select \"Dropbox API app\"\n" + echo -ne " 3) Now go on with the configuration, choosing the app permissions and access restrictions to your DropBox folder\n" + echo -ne " 4) Enter the \"App Name\" that you prefer (e.g. MyUploader$RANDOM$RANDOM$RANDOM)\n\n" + + echo -ne " Now, click on the \"Create App\" button.\n\n" + + echo -ne " When your new App is successfully created, please click on the Generate button\n" + echo -ne " under the 'Generated access token' section, then copy and paste the new access token here:\n\n" + + echo -ne " # Access token: " + read -r OAUTH_ACCESS_TOKEN + + echo -ne "\n > The access token is $OAUTH_ACCESS_TOKEN. Looks ok? [y/N]: " + read -r answer + if [[ $answer != "y" ]]; then + remove_temp_files + exit 1 + fi + + echo "OAUTH_ACCESS_TOKEN=$OAUTH_ACCESS_TOKEN" > "$CONFIG_FILE" + echo " The configuration has been saved." + + remove_temp_files + exit 0 +fi + +################ +#### START #### +################ + +COMMAND="${*:$OPTIND:1}" +ARG1="${*:$OPTIND+1:1}" +ARG2="${*:$OPTIND+2:1}" + +let argnum=$#-$OPTIND + +#CHECKING PARAMS VALUES +case $COMMAND in + + upload) + + if [[ $argnum -lt 2 ]]; then + usage + fi + + FILE_DST="${*:$#:1}" + + for (( i=OPTIND+1; i<$#; i++ )); do + FILE_SRC="${*:$i:1}" + db_upload "$FILE_SRC" "/$FILE_DST" + done + + ;; + + download) + + if [[ $argnum -lt 1 ]]; then + usage + fi + + FILE_SRC="$ARG1" + FILE_DST="$ARG2" + + db_download "/$FILE_SRC" "$FILE_DST" + + ;; + + saveurl) + + if [[ $argnum -lt 1 ]]; then + usage + fi + + URL=$ARG1 + FILE_DST="$ARG2" + + db_saveurl "$URL" "/$FILE_DST" + + ;; + + share) + + if [[ $argnum -lt 1 ]]; then + usage + fi + + FILE_DST="$ARG1" + + db_share "/$FILE_DST" + + ;; + + info) + + db_account_info + + ;; + + space) + + db_account_space + + ;; + + delete|remove) + + if [[ $argnum -lt 1 ]]; then + usage + fi + + FILE_DST="$ARG1" + + db_delete "/$FILE_DST" + + ;; + + move|rename) + + if [[ $argnum -lt 2 ]]; then + usage + fi + + FILE_SRC="$ARG1" + FILE_DST="$ARG2" + + db_move "/$FILE_SRC" "/$FILE_DST" + + ;; + + copy) + + if [[ $argnum -lt 2 ]]; then + usage + fi + + FILE_SRC="$ARG1" + FILE_DST="$ARG2" + + db_copy "/$FILE_SRC" "/$FILE_DST" + + ;; + + mkdir) + + if [[ $argnum -lt 1 ]]; then + usage + fi + + DIR_DST="$ARG1" + + db_mkdir "/$DIR_DST" + + ;; + + search) + + if [[ $argnum -lt 1 ]]; then + usage + fi + + QUERY=$ARG1 + + db_search "$QUERY" + + ;; + + list) + + DIR_DST="$ARG1" + + #Checking DIR_DST + if [[ $DIR_DST == "" ]]; then + DIR_DST="/" + fi + + db_list "/$DIR_DST" + + ;; + + monitor) + + DIR_DST="$ARG1" + TIMEOUT=$ARG2 + + #Checking DIR_DST + if [[ $DIR_DST == "" ]]; then + DIR_DST="/" + fi + + print " > Monitoring \"$DIR_DST\" for changes...\n" + + if [[ -n $TIMEOUT ]]; then + db_monitor_nonblock $TIMEOUT "/$DIR_DST" + else + db_monitor 60 "/$DIR_DST" + fi + + ;; + + unlink) + + db_unlink + + ;; + + *) + + if [[ $COMMAND != "" ]]; then + print "Error: Unknown command: $COMMAND\n\n" + ERROR_STATUS=1 + fi + usage + + ;; + +esac + +remove_temp_files + +if [[ $ERROR_STATUS -ne 0 ]]; then + echo "Some error occured. Please check the log." +fi + +exit $ERROR_STATUS diff --git a/.ci/gta5view.nsi b/.ci/gta5view.nsi new file mode 100644 index 0000000..1ddd882 --- /dev/null +++ b/.ci/gta5view.nsi @@ -0,0 +1,364 @@ +###################################################################### + +!define APP_NAME "gta5view" +!define APP_EXT ".g5e" +!define COMP_NAME "Syping" +!define WEB_SITE "https://gta5view.syping.de/" +!define VERSION "1.10.2.0" +!define COPYRIGHT "Copyright © 2016-2022 Syping" +!define DESCRIPTION "Open Source Snapmatic and Savegame viewer/editor for GTA V" +!define INSTALLER_NAME "gta5view_setup.exe" +!define MAIN_APP_EXE "gta5view.exe" +!define INSTALL_TYPE "SetShellVarContext all" +!define REG_ROOT "HKLM" +!define REG_APP_PATH "Software\Microsoft\Windows\CurrentVersion\App Paths\${MAIN_APP_EXE}" +!define UNINSTALL_PATH "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" +!define LICENSE_TXT "../LICENSE" + +###################################################################### + +VIProductVersion "${VERSION}" +VIAddVersionKey "ProductName" "${APP_NAME}" +VIAddVersionKey "ProductVersion" "${VERSION}" +VIAddVersionKey "CompanyName" "${COMP_NAME}" +VIAddVersionKey "LegalCopyright" "${COPYRIGHT}" +VIAddVersionKey "FileDescription" "${DESCRIPTION}" +VIAddVersionKey "FileVersion" "${VERSION}" + +###################################################################### + +!include "x64.nsh" +SetCompressor LZMA +Name "${APP_NAME}" +Caption "${APP_NAME}" +OutFile "${INSTALLER_NAME}" +#BrandingText "${APP_NAME}" +XPStyle on +Unicode true +InstallDirRegKey "${REG_ROOT}" "${REG_APP_PATH}" "" +InstallDir "$PROGRAMFILES64\Syping\gta5view" + +###################################################################### + +!include "MUI2.nsh" + +!define MUI_ABORTWARNING +!define MUI_UNABORTWARNING + +!define MUI_LANGDLL_REGISTRY_ROOT "${REG_ROOT}" +!define MUI_LANGDLL_REGISTRY_KEY "${UNINSTALL_PATH}" +!define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language" + +!insertmacro MUI_PAGE_WELCOME + +!ifdef LICENSE_TXT +!insertmacro MUI_PAGE_LICENSE "${LICENSE_TXT}" +!endif + +!insertmacro MUI_PAGE_DIRECTORY + +!ifdef REG_START_MENU +!define MUI_STARTMENUPAGE_NODISABLE +!define MUI_STARTMENUPAGE_DEFAULTFOLDER "gta5view" +!define MUI_STARTMENUPAGE_REGISTRY_ROOT "${REG_ROOT}" +!define MUI_STARTMENUPAGE_REGISTRY_KEY "${UNINSTALL_PATH}" +!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${REG_START_MENU}" +!insertmacro MUI_PAGE_STARTMENU Application $SM_Folder +!endif + +!insertmacro MUI_PAGE_INSTFILES + +!define MUI_FINISHPAGE_RUN "$INSTDIR\${MAIN_APP_EXE}" +!insertmacro MUI_PAGE_FINISH + +!insertmacro MUI_UNPAGE_CONFIRM + +!insertmacro MUI_UNPAGE_INSTFILES + +!insertmacro MUI_UNPAGE_FINISH + +!insertmacro MUI_LANGUAGE "English" +!insertmacro MUI_LANGUAGE "French" +!insertmacro MUI_LANGUAGE "German" +!insertmacro MUI_LANGUAGE "Korean" +!insertmacro MUI_LANGUAGE "Russian" +!insertmacro MUI_LANGUAGE "Ukrainian" +!insertmacro MUI_LANGUAGE "TradChinese" + +!insertmacro MUI_RESERVEFILE_LANGDLL + +###################################################################### + +Function .onInit +!insertmacro MUI_LANGDLL_DISPLAY +!ifdef WIN32 + MessageBox MB_OK|MB_ICONSTOP "Windows 32-Bit is not supported anymore!" + Quit +!endif +SetRegView 64 +FunctionEnd + +###################################################################### + +Section -MainProgram +${INSTALL_TYPE} +SetOverwrite ifnewer +SetOutPath "$INSTDIR" +File "../build/gta5view.exe" +File "/opt/llvm-mingw/x86_64-w64-mingw32/bin/libc++.dll" +File "/opt/llvm-mingw/x86_64-w64-mingw32/bin/libunwind.dll" +File "/usr/local/lib/x86_64-w64-mingw32/openssl/bin/libcrypto-1_1-x64.dll" +File "/usr/local/lib/x86_64-w64-mingw32/openssl/bin/libssl-1_1-x64.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/bin/Qt5Core.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/bin/Qt5Gui.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/bin/Qt5Network.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/bin/Qt5Svg.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/bin/Qt5Widgets.dll" +SetOutPath "$INSTDIR\lang" +File "../build/gta5sync_en_US.qm" +File "../build/gta5sync_de.qm" +File "../build/gta5sync_fr.qm" +File "../build/gta5sync_ko.qm" +File "../build/gta5sync_ru.qm" +File "../build/gta5sync_uk.qm" +File "../build/gta5sync_zh_TW.qm" +File "../build/qtbase_en_GB.qm" +File "../res/qt5/qtbase_de.qm" +File "../res/qt5/qtbase_fr.qm" +File "../res/qt5/qtbase_ko.qm" +File "../res/qt5/qtbase_ru.qm" +File "../res/qt5/qtbase_uk.qm" +File "../res/qt5/qtbase_zh_TW.qm" +SetOutPath "$INSTDIR\resources" +File "../res/add.svgz" +File "../res/avatararea.png" +File "../res/avatarareaimport.png" +File "../res/back.svgz" +File "../res/flag-de.png" +File "../res/flag-fr.png" +File "../res/flag-gb.png" +File "../res/flag-kr.png" +File "../res/flag-ru.png" +File "../res/flag-tw.png" +File "../res/flag-ua.png" +File "../res/flag-us.png" +File "../res/gta5view-16.png" +File "../res/gta5view-24.png" +File "../res/gta5view-32.png" +File "../res/gta5view-40.png" +File "../res/gta5view-48.png" +File "../res/gta5view-64.png" +File "../res/gta5view-96.png" +File "../res/gta5view-128.png" +File "../res/gta5view-256.png" +File "../res/mapcayoperico.jpg" +File "../res/mappreview.jpg" +File "../res/next.svgz" +File "../res/pointmaker-8.png" +File "../res/pointmaker-16.png" +File "../res/pointmaker-24.png" +File "../res/pointmaker-32.png" +File "../res/savegame.svgz" +File "../res/watermark_1b.png" +File "../res/watermark_2b.png" +File "../res/watermark_2r.png" +SetOutPath "$INSTDIR\imageformats" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qgif.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qicns.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qico.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qjpeg.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qsvg.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qtga.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qtiff.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qwbmp.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/imageformats/qwebp.dll" +SetOutPath "$INSTDIR\platforms" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/platforms/qwindows.dll" +SetOutPath "$INSTDIR\styles" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/styles/qcleanlooksstyle.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/styles/qplastiquestyle.dll" +File "/usr/local/lib/x86_64-w64-mingw32/qt5/plugins/styles/qwindowsvistastyle.dll" +SectionEnd + +###################################################################### + +Section -Icons_Reg +SetOutPath "$INSTDIR" +WriteUninstaller "$INSTDIR\uninstall.exe" + +!ifdef REG_START_MENU +!insertmacro MUI_STARTMENU_WRITE_BEGIN Application +CreateDirectory "$SMPROGRAMS\$SM_Folder" +CreateShortCut "$SMPROGRAMS\$SM_Folder\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}" +CreateShortCut "$SMPROGRAMS\$SM_Folder\Uninstall ${APP_NAME}.lnk" "$INSTDIR\uninstall.exe" + +!ifdef WEB_SITE +WriteIniStr "$INSTDIR\${APP_NAME} website.url" "InternetShortcut" "URL" "${WEB_SITE}" +CreateShortCut "$SMPROGRAMS\$SM_Folder\gta5view Website.lnk" "$INSTDIR\${APP_NAME} website.url" +!endif +!insertmacro MUI_STARTMENU_WRITE_END +!endif + +!ifndef REG_START_MENU +CreateDirectory "$SMPROGRAMS\gta5view" +CreateShortCut "$SMPROGRAMS\gta5view\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}" +CreateShortCut "$SMPROGRAMS\gta5view\Uninstall ${APP_NAME}.lnk" "$INSTDIR\uninstall.exe" + +!ifdef WEB_SITE +WriteIniStr "$INSTDIR\${APP_NAME} website.url" "InternetShortcut" "URL" "${WEB_SITE}" +CreateShortCut "$SMPROGRAMS\gta5view\gta5view Website.lnk" "$INSTDIR\${APP_NAME} website.url" +!endif +!endif + +WriteRegStr ${REG_ROOT} "${REG_APP_PATH}" "" "$INSTDIR\${MAIN_APP_EXE}" +WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayName" "${APP_NAME}" +WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "UninstallString" "$INSTDIR\uninstall.exe" +WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayIcon" "$INSTDIR\${MAIN_APP_EXE}" +WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayVersion" "${VERSION}" +WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "Publisher" "${COMP_NAME}" + +!ifdef WEB_SITE +WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "URLInfoAbout" "${WEB_SITE}" +!endif +SectionEnd + +###################################################################### + +Section -ShellAssoc +WriteRegStr ${REG_ROOT} "Software\Classes\${APP_NAME}\DefaultIcon" "" "$INSTDIR\${MAIN_APP_EXE},0" +WriteRegStr ${REG_ROOT} "Software\Classes\${APP_NAME}\shell\open\command" "" '"$INSTDIR\${MAIN_APP_EXE}" "%1"' +WriteRegStr ${REG_ROOT} "Software\Classes\${APP_EXT}" "" "${APP_NAME}" +WriteRegStr ${REG_ROOT} "Software\Classes\${APP_EXT}" "Content Type" "application/x-gta5view-export" +System::Call 'SHELL32::SHChangeNotify(i0x8000000,i0,p0,p0)' +SectionEnd + +###################################################################### + +Section -un.ShellAssoc +ClearErrors +ReadRegStr $0 ${REG_ROOT} "Software\Classes\${APP_EXT}" "" +DeleteRegKey ${REG_ROOT} "Software\Classes\${APP_NAME}" +${IfNot} ${Errors} +${AndIf} $0 == "${APP_NAME}" +DeleteRegValue ${REG_ROOT} "Software\Classes\${APP_EXT}" "" +DeleteRegKey /IfEmpty ${REG_ROOT} "Software\Classes\${APP_EXT}" +${EndIf} +System::Call 'SHELL32::SHChangeNotify(i0x8000000,i0,p0,p0)' +SectionEnd + +###################################################################### + +Section Uninstall +${INSTALL_TYPE} +Delete "$INSTDIR\gta5view.exe" +Delete "$INSTDIR\libc++.dll" +Delete "$INSTDIR\libunwind.dll" +Delete "$INSTDIR\libcrypto-1_1-x64.dll" +Delete "$INSTDIR\libssl-1_1-x64.dll" +Delete "$INSTDIR\Qt5Core.dll" +Delete "$INSTDIR\Qt5Gui.dll" +Delete "$INSTDIR\Qt5Network.dll" +Delete "$INSTDIR\Qt5Svg.dll" +Delete "$INSTDIR\Qt5Widgets.dll" +Delete "$INSTDIR\lang\gta5sync_en_US.qm" +Delete "$INSTDIR\lang\gta5sync_de.qm" +Delete "$INSTDIR\lang\gta5sync_fr.qm" +Delete "$INSTDIR\lang\gta5sync_ko.qm" +Delete "$INSTDIR\lang\gta5sync_ru.qm" +Delete "$INSTDIR\lang\gta5sync_uk.qm" +Delete "$INSTDIR\lang\gta5sync_zh_TW.qm" +Delete "$INSTDIR\lang\qtbase_en_GB.qm" +Delete "$INSTDIR\lang\qtbase_de.qm" +Delete "$INSTDIR\lang\qtbase_fr.qm" +Delete "$INSTDIR\lang\qtbase_ko.qm" +Delete "$INSTDIR\lang\qtbase_ru.qm" +Delete "$INSTDIR\lang\qtbase_uk.qm" +Delete "$INSTDIR\lang\qtbase_zh_TW.qm" +Delete "$INSTDIR\resources\add.svgz" +Delete "$INSTDIR\resources\avatararea.png" +Delete "$INSTDIR\resources\avatarareaimport.png" +Delete "$INSTDIR\resources\back.svgz" +Delete "$INSTDIR\resources\flag-de.png" +Delete "$INSTDIR\resources\flag-fr.png" +Delete "$INSTDIR\resources\flag-gb.png" +Delete "$INSTDIR\resources\flag-kr.png" +Delete "$INSTDIR\resources\flag-ru.png" +Delete "$INSTDIR\resources\flag-tw.png" +Delete "$INSTDIR\resources\flag-ua.png" +Delete "$INSTDIR\resources\flag-us.png" +Delete "$INSTDIR\resources\gta5view-16.png" +Delete "$INSTDIR\resources\gta5view-24.png" +Delete "$INSTDIR\resources\gta5view-32.png" +Delete "$INSTDIR\resources\gta5view-40.png" +Delete "$INSTDIR\resources\gta5view-48.png" +Delete "$INSTDIR\resources\gta5view-64.png" +Delete "$INSTDIR\resources\gta5view-96.png" +Delete "$INSTDIR\resources\gta5view-128.png" +Delete "$INSTDIR\resources\gta5view-256.png" +Delete "$INSTDIR\resources\mapcayoperico.jpg" +Delete "$INSTDIR\resources\mappreview.jpg" +Delete "$INSTDIR\resources\next.svgz" +Delete "$INSTDIR\resources\pointmaker-8.png" +Delete "$INSTDIR\resources\pointmaker-16.png" +Delete "$INSTDIR\resources\pointmaker-24.png" +Delete "$INSTDIR\resources\pointmaker-32.png" +Delete "$INSTDIR\resources\savegame.svgz" +Delete "$INSTDIR\resources\watermark_1b.png" +Delete "$INSTDIR\resources\watermark_2b.png" +Delete "$INSTDIR\resources\watermark_2r.png" +Delete "$INSTDIR\imageformats\qgif.dll" +Delete "$INSTDIR\imageformats\qicns.dll" +Delete "$INSTDIR\imageformats\qico.dll" +Delete "$INSTDIR\imageformats\qjpeg.dll" +Delete "$INSTDIR\imageformats\qsvg.dll" +Delete "$INSTDIR\imageformats\qtga.dll" +Delete "$INSTDIR\imageformats\qtiff.dll" +Delete "$INSTDIR\imageformats\qwbmp.dll" +Delete "$INSTDIR\imageformats\qwebp.dll" +Delete "$INSTDIR\platforms\qwindows.dll" +Delete "$INSTDIR\styles\qcleanlooksstyle.dll" +Delete "$INSTDIR\styles\qplastiquestyle.dll" +Delete "$INSTDIR\styles\qwindowsvistastyle.dll" +RmDir "$INSTDIR\lang" +RmDir "$INSTDIR\imageformats" +RmDir "$INSTDIR\platforms" +RmDir "$INSTDIR\styles" + +Delete "$INSTDIR\uninstall.exe" +!ifdef WEB_SITE +Delete "$INSTDIR\${APP_NAME} website.url" +!endif + +RmDir "$INSTDIR" + +!ifdef REG_START_MENU +!insertmacro MUI_STARTMENU_GETFOLDER "Application" $SM_Folder +Delete "$SMPROGRAMS\$SM_Folder\${APP_NAME}.lnk" +Delete "$SMPROGRAMS\$SM_Folder\Uninstall ${APP_NAME}.lnk" +!ifdef WEB_SITE +Delete "$SMPROGRAMS\$SM_Folder\gta5view Website.lnk" +!endif +RmDir "$SMPROGRAMS\$SM_Folder" +!endif + +!ifndef REG_START_MENU +Delete "$SMPROGRAMS\gta5view\${APP_NAME}.lnk" +Delete "$SMPROGRAMS\gta5view\Uninstall ${APP_NAME}.lnk" +!ifdef WEB_SITE +Delete "$SMPROGRAMS\gta5view\gta5view Website.lnk" +!endif +RmDir "$SMPROGRAMS\gta5view" +!endif + +DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}" +DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}" +SectionEnd + +###################################################################### + +Function un.onInit +!insertmacro MUI_UNGETLANGUAGE +SetRegView 64 +FunctionEnd + +###################################################################### diff --git a/.ci/osx_build.sh b/.ci/osx_build.sh new file mode 100755 index 0000000..c8545c4 --- /dev/null +++ b/.ci/osx_build.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# Creating folders +cd ${PROJECT_DIR} && \ +echo "gta5view build version is ${APPLICATION_VERSION}" && \ +echo "gta5view image name is gta5view-osx_${APPLICATION_VERSION}.dmg" && \ +mkdir -p build && \ +mkdir -p assets && \ +cd build && \ + +/usr/local/bin/cmake \ + "-DCMAKE_PREFIX_PATH=/usr/local/opt/qt" \ + "${CMAKE_BUILD_TYPE}" \ + "-DGTA5VIEW_BUILDCODE=${PACKAGE_CODE}" \ + "-DGTA5VIEW_APPVER=${APPLICATION_VERSION}" \ + "-DGTA5VIEW_COMMIT=${APPLICATION_COMMIT}" \ + "-DWITH_DONATE=ON" \ + "-DWITH_TELEMETRY=ON" \ + "-DDONATE_ADDRESSES=$(cat ${PROJECT_DIR}/.ci/donate.txt)" \ + "-DTELEMETRY_WEBURL=https://dev.syping.de/gta5view-userstats/" \ + ../ && \ +make -j 4 && \ +/usr/local/opt/qt/bin/macdeployqt gta5view.app -dmg && \ +cp -Rf gta5view.dmg ../assets/gta5view-osx_${APPLICATION_VERSION}.dmg diff --git a/.ci/osx_ci.sh b/.ci/osx_ci.sh new file mode 100755 index 0000000..59a722e --- /dev/null +++ b/.ci/osx_ci.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +# Install packages +.ci/osx_install.sh && \ + +# Build gta5view +.ci/osx_build.sh && \ +cd ${PROJECT_DIR} diff --git a/.ci/osx_install.sh b/.ci/osx_install.sh new file mode 100755 index 0000000..9c5c5f3 --- /dev/null +++ b/.ci/osx_install.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +# Install packages +brew upgrade cmake qt diff --git a/.ci/windows_build.sh b/.ci/windows_build.sh new file mode 100755 index 0000000..4b85c38 --- /dev/null +++ b/.ci/windows_build.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# Prepare environment variable +export GTA5VIEW_EXECUTABLE=gta5view-${EXECUTABLE_VERSION}${EXECUTABLE_ARCH}.exe && \ + +# Creating folders +cd ${PROJECT_DIR} && \ +echo "gta5view build version is ${APPLICATION_VERSION}" && \ +echo "gta5view executable is ${GTA5VIEW_EXECUTABLE}" && \ +mkdir -p build && \ +mkdir -p assets && \ + +# Starting build +cd build && \ +mingw64-qt-cmake \ + "${CMAKE_BUILD_TYPE}" \ + "-DGTA5VIEW_BUILDCODE=${PACKAGE_CODE}" \ + "-DGTA5VIEW_APPVER=${APPLICATION_VERSION}" \ + "-DGTA5VIEW_COMMIT=${APPLICATION_COMMIT}" \ + "-DWITH_DONATE=ON" \ + "-DWITH_TELEMETRY=ON" \ + "-DDONATE_ADDRESSES=$(cat ${PROJECT_DIR}/.ci/donate.txt)" \ + "-DTELEMETRY_WEBURL=https://dev.syping.de/gta5view-userstats/" \ + .. && \ +make -j 4 && \ +x86_64-w64-mingw32-strip -s gta5view.exe && \ +cp -Rf *.exe ${PROJECT_DIR}/assets/${GTA5VIEW_EXECUTABLE} && \ +cd ${PROJECT_DIR}/assets diff --git a/.ci/windows_docker.sh b/.ci/windows_docker.sh new file mode 100755 index 0000000..54862f1 --- /dev/null +++ b/.ci/windows_docker.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +DOCKER_IMAGE=sypingauto/gta5view-build:1.10-static +PROJECT_DIR_DOCKER=/gta5view + +cd ${PROJECT_DIR} && \ +docker pull ${DOCKER_IMAGE} && \ +docker run --rm \ + -v "${PROJECT_DIR}:${PROJECT_DIR_DOCKER}" \ + ${DOCKER_IMAGE} \ + /bin/bash -c "export PROJECT_DIR=${PROJECT_DIR_DOCKER} && export QT_SELECT=${QT_SELECT} && export APPLICATION_VERSION=${APPLICATION_VERSION} && export APPLICATION_COMMIT=${APPLICATION_COMMIT} && export BUILD_TYPE=${BUILD_TYPE} && export QMAKE_FLAGS_QT4=${QMAKE_FLAGS_QT4} && export QMAKE_FLAGS_QT5=${QMAKE_FLAGS_QT5} && export CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} && export QMAKE_BUILD_TYPE=${QMAKE_BUILD_TYPE} && export PACKAGE_VERSION=${PACKAGE_VERSION} && export PACKAGE_BUILD=${PACKAGE_BUILD} && export PACKAGE_CODE=${PACKAGE_CODE} && export EXECUTABLE_VERSION=${EXECUTABLE_VERSION} && export EXECUTABLE_ARCH=${EXECUTABLE_ARCH} && cd ${PROJECT_DIR_DOCKER} && .ci/windows_build.sh" && \ + +# Prepare environment variable +export GTA5VIEW_EXECUTABLE=gta5view-${EXECUTABLE_VERSION}${EXECUTABLE_ARCH}.exe && \ + +# Upload Assets to Dropbox +if [ "${PACKAGE_CODE}" == "gta5-mods" ]; then + ${PROJECT_DIR}/.ci/dropbox_uploader.sh mkdir gta5-mods/${PACKAGE_VERSION} + ${PROJECT_DIR}/.ci/dropbox_uploader.sh upload ${PROJECT_DIR}/assets/${GTA5VIEW_EXECUTABLE} gta5-mods/${PACKAGE_VERSION}/${GTA5VIEW_EXECUTABLE} && \ + rm -rf ${GTA5VIEW_EXECUTABLE} +elif [ "${PACKAGE_CODE}" == "gtainside" ]; then + ${PROJECT_DIR}/.ci/dropbox_uploader.sh mkdir gtainside/${PACKAGE_VERSION} + ${PROJECT_DIR}/.ci/dropbox_uploader.sh upload ${PROJECT_DIR}/assets/${GTA5VIEW_EXECUTABLE} gtainside/${PACKAGE_VERSION}/${GTA5VIEW_EXECUTABLE} && \ + rm -rf ${GTA5VIEW_EXECUTABLE} +fi diff --git a/.ci/wininstall_build.sh b/.ci/wininstall_build.sh new file mode 100755 index 0000000..8ecbe4e --- /dev/null +++ b/.ci/wininstall_build.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Creating folders +cd ${PROJECT_DIR} && \ +echo "gta5view build version is ${APPLICATION_VERSION}" && \ +mkdir -p build && \ +mkdir -p assets && \ + +# Starting build +cd build && \ +mingw64-qt-cmake \ + "${CMAKE_BUILD_TYPE}" \ + "-DGTA5VIEW_BUILDCODE=${PACKAGE_CODE}" \ + "-DGTA5VIEW_APPVER=${APPLICATION_VERSION}" \ + "-DGTA5VIEW_COMMIT=${APPLICATION_COMMIT}" \ + "-DWITH_DONATE=ON" \ + "-DWITH_TELEMETRY=ON" \ + "-DDONATE_ADDRESSES=$(cat ${PROJECT_DIR}/.ci/donate.txt)" \ + "-DTELEMETRY_WEBURL=https://dev.syping.de/gta5view-userstats/" \ + "-DQCONF_BUILD=ON" \ + "-DGTA5VIEW_INLANG=RUNDIR:SEPARATOR:lang" \ + "-DGTA5VIEW_LANG=RUNDIR:SEPARATOR:lang" \ + "-DGTA5VIEW_PLUG=RUNDIR:SEPARATOR:plugins" \ + .. && \ +make -j 4 && \ +x86_64-w64-mingw32-strip -s gta5view.exe && \ +cd ${PROJECT_DIR}/assets && \ +makensis "-XTarget amd64-unicode" -NOCD ${PROJECT_DIR}/.ci/gta5view.nsi && \ +mv -f gta5view_setup.exe gta5view-${EXECUTABLE_VERSION}_setup.exe diff --git a/.ci/wininstall_docker.sh b/.ci/wininstall_docker.sh new file mode 100755 index 0000000..7a51180 --- /dev/null +++ b/.ci/wininstall_docker.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +DOCKER_IMAGE=sypingauto/gta5view-build:1.10-shared +PROJECT_DIR_DOCKER=/gta5view + +cd ${PROJECT_DIR} && \ +docker pull ${DOCKER_IMAGE} && \ +docker run --rm \ + -v "${PROJECT_DIR}:${PROJECT_DIR_DOCKER}" \ + ${DOCKER_IMAGE} \ + /bin/bash -c "export PROJECT_DIR=${PROJECT_DIR_DOCKER} && export QT_SELECT=${QT_SELECT} && export APPLICATION_VERSION=${APPLICATION_VERSION} && export APPLICATION_COMMIT=${APPLICATION_COMMIT} && export BUILD_TYPE=${BUILD_TYPE} && export QMAKE_FLAGS_QT4=${QMAKE_FLAGS_QT4} && export QMAKE_FLAGS_QT5=${QMAKE_FLAGS_QT5} && export CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} && export QMAKE_BUILD_TYPE=${QMAKE_BUILD_TYPE} && export PACKAGE_VERSION=${PACKAGE_VERSION} && export PACKAGE_BUILD=${PACKAGE_BUILD} && export PACKAGE_CODE=${PACKAGE_CODE} && export EXECUTABLE_VERSION=${EXECUTABLE_VERSION} && export EXECUTABLE_ARCH=${EXECUTABLE_ARCH} && cd ${PROJECT_DIR_DOCKER} && .ci/wininstall_build.sh" diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..ba4a1c5 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,36 @@ +kind: pipeline +type: docker + +environment: + BUILD_TYPE: "REL" + +steps: +- name: Windows Installer + image: sypingauto/gta5view-build:1.10-shared + environment: + BUILD_SCRIPT: "wininstall_build.sh" + QT_SELECT: "qt5-x86_64-w64-mingw32" + TCA_PASS: + from_secret: tca_pass + commands: + - .drone/drone.sh + volumes: + - name: gta5view + path: /srv/gta5view +- name: Windows Portable + image: sypingauto/gta5view-build:1.10-static + environment: + BUILD_SCRIPT: "windows_build.sh" + QT_SELECT: "qt5-x86_64-w64-mingw32" + TCA_PASS: + from_secret: tca_pass + commands: + - .drone/drone.sh + volumes: + - name: gta5view + path: /srv/gta5view + +volumes: +- name: gta5view + host: + path: /srv/gta5view diff --git a/.drone/TelemetryClassAuthenticator.cpp.enc b/.drone/TelemetryClassAuthenticator.cpp.enc new file mode 100644 index 0000000..e905d2f Binary files /dev/null and b/.drone/TelemetryClassAuthenticator.cpp.enc differ diff --git a/.drone/drone.sh b/.drone/drone.sh new file mode 100755 index 0000000..b489e12 --- /dev/null +++ b/.drone/drone.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Cleanup previous Drone build +if [ -d "assets" ]; then + rm -rf assets +fi +if [ -d "build" ]; then + rm -rf build +fi + +# Decrypt Telemetry Authenticator +rm -rf tmext/TelemetryClassAuthenticator.cpp && \ +openssl aes-256-cbc -k ${TCA_PASS} -in .drone/TelemetryClassAuthenticator.cpp.enc -out tmext/TelemetryClassAuthenticator.cpp -d -pbkdf2 + +# Check if build is not tagged +if [ "${DRONE_TAG}" == "" ]; then + export EXECUTABLE_TAG=-$(git rev-parse --short HEAD) +else + export EXECUTABLE_TAG= +fi + +# Check if package code is not set +if [ "${PACKAGE_CODE}" == "" ]; then + export PACKAGE_CODE=Drone +fi + +# Init Application Commit Hash +export APPLICATION_COMMIT=$(git rev-parse --short HEAD) + +# Start CI script and copying assets into gta5view directory +.ci/ci.sh && \ +mkdir -p /srv/gta5view/${APPLICATION_COMMIT} && \ +cp -Rf assets/* /srv/gta5view/${APPLICATION_COMMIT}/ diff --git a/.flatpak/de.syping.gta5view.yaml b/.flatpak/de.syping.gta5view.yaml new file mode 100644 index 0000000..c9a9775 --- /dev/null +++ b/.flatpak/de.syping.gta5view.yaml @@ -0,0 +1,22 @@ +app-id: de.syping.gta5view +runtime: org.kde.Platform +runtime-version: '5.15-21.08' +sdk: org.kde.Sdk +command: gta5view +finish-args: + - --share=network + - --share=ipc + - --socket=fallback-x11 + - --socket=wayland + - --device=dri +modules: + - name: gta5view + buildsystem: cmake-ninja + config-opts: + - -DFLATPAK_BUILD=ON + - -DQCONF_BUILD=ON + - -DGTA5VIEW_BUILDCODE=Flatpak + - -DGTA5VIEW_BUILDTYPE=Release + sources: + - type: dir + path: ../ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..cd1cbfb --- /dev/null +++ b/.gitattributes @@ -0,0 +1,28 @@ +# Auto detect text files and perform LF normalization +* text=auto eol=lf + +# Development files +*.cpp text eol=lf +*.h text eol=lf +*.ui text eol=lf +*.qrc text eol=lf + +# Development resources +*.ini text eol=lf + +# Linux development files +*.desktop text eol=lf + +# Windows development files +*.rc text eol=crlf +*.nsi text eol=crlf +*.exe.manifest text eol=crlf + +# Binary files +*.png binary +*.jpg binary +*.qm binary +*.ico binary +*.icns binary +*.xcf binary +*.g5e binary diff --git a/.gitignore b/.gitignore index 023ea2a..fcd678b 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,7 @@ # Qt project user file *.pro.user + +# Gettext translation files +*.po +*.pot diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..72ea2e6 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,31 @@ +stages: + - build + +variables: + BUILD_TYPE: "REL" + +Windows Installer: + stage: build + image: sypingauto/gta5view-build:1.10-shared + variables: + BUILD_SCRIPT: "wininstall_build.sh" + QT_SELECT: "qt5-x86_64-w64-mingw32" + script: + - .gitlab/gitlab.sh + artifacts: + name: "gta5view-$CI_COMMIT_REF_NAME-${CI_COMMIT_SHA:0:8}_setup" + paths: + - "gta5view-*.exe" + +Windows Portable: + stage: build + image: sypingauto/gta5view-build:1.10-static + variables: + BUILD_SCRIPT: "windows_build.sh" + QT_SELECT: "qt5-x86_64-w64-mingw32" + script: + - .gitlab/gitlab.sh + artifacts: + name: "gta5view-$CI_COMMIT_REF_NAME-${CI_COMMIT_SHA:0:8}_portable" + paths: + - "gta5view-*.exe" diff --git a/.gitlab/TelemetryClassAuthenticator.cpp.enc b/.gitlab/TelemetryClassAuthenticator.cpp.enc new file mode 100644 index 0000000..3c7445b Binary files /dev/null and b/.gitlab/TelemetryClassAuthenticator.cpp.enc differ diff --git a/.gitlab/gitlab.sh b/.gitlab/gitlab.sh new file mode 100755 index 0000000..87d2078 --- /dev/null +++ b/.gitlab/gitlab.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Decrypt Telemetry Authenticator +rm -rf tmext/TelemetryClassAuthenticator.cpp && \ +openssl aes-256-cbc -k ${tca_pass} -in .gitlab/TelemetryClassAuthenticator.cpp.enc -out tmext/TelemetryClassAuthenticator.cpp -d + +# Check if build is not tagged +if [ "${CI_COMMIT_TAG}" == "" ]; then + export EXECUTABLE_TAG=-$(git rev-parse --short HEAD) +else + export EXECUTABLE_TAG= +fi + +# Check if package code is not set +if [ "${PACKAGE_CODE}" == "" ]; then + export PACKAGE_CODE=GitLab +fi + +# Init Application Commit Hash +export APPLICATION_COMMIT=$(git rev-parse --short HEAD) + +# Start CI script and copying assets into base directory +.ci/ci.sh && \ +cp -Rf assets/* ./ diff --git a/.travis.yml b/.travis.yml index 5ea857e..f6c3397 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,38 +1,50 @@ -dist: trusty +dist: bionic sudo: required language: cpp +services: + - docker + env: -- PACKAGE_VERSION="1.4.0" + global: + - BUILD_TYPE=REL + +matrix: + include: + - env: + - BUILD_SCRIPT=debian_docker.sh + - RELEASE_LABEL="Debian 64-Bit Package" + - DEBIAN_VERSION=buster + - DOCKER_USER=amd64 + - APT_INSTALL=clang + - env: + - BUILD_SCRIPT=windows_docker.sh + - QT_SELECT=qt5-x86_64-w64-mingw32 + - RELEASE_LABEL="Windows 64-Bit Portable" + - env: + - BUILD_SCRIPT=wininstall_docker.sh + - QT_SELECT=qt5-x86_64-w64-mingw32 + - RELEASE_LABEL="Windows 64-Bit Installer" + - os: osx + osx_image: xcode14.2 + env: + - BUILD_SCRIPT=osx_ci.sh + - RELEASE_LABEL="Mac OS X 64-Bit Disk Image" before_install: - - test -n $CC && unset CC - - test -n $CXX && unset CXX - -install: - - sudo apt-get update -qq - - sudo apt-get install -qq checkinstall dpkg-dev g++ gcc qtbase5-dev qt5-qmake - -before_script: - - export INSTALL_ROOT=/usr - - if [ `git name-rev --tags --name-only $(git rev-parse HEAD)` == "undefined" ]; then export APPLICATION_VERSION="$PACKAGE_VERSION.$TRAVIS_BUILD_NUMBER"; else export APPLICATION_VERSION=`git name-rev --tags --name-only $(git rev-parse HEAD)`; fi - - echo "gta5view build version is $APPLICATION_VERSION" - - mkdir build - - mkdir package - - cd build - - echo "Grand Theft Auto V Snapmatic and Savegame viewer" > ./description-pak + - ".travis/source.sh" script: - - qmake -qt=5 "DEFINES+=GTA5SYNC_BUILDTYPE=\\\\\\\"Release\\\\\\\"" "DEFINES+=GTA5SYNC_DAILYB=\\\\\\\"$APPLICATION_VERSION\\\\\\\"" ../gta5view.pro - - make -j 4 - - sudo checkinstall -D --default --nodoc --pkgname=gta5view --pkgversion=$APPLICATION_VERSION --pkgrelease=travis1 --pkggroup=utility --maintainer="Syping on Travis \" --requires=libqt5core5a,libqt5gui5,libqt5network5,libqt5widgets5 --pakdir=../package + - ".travis/travis.sh" deploy: provider: releases api_key: - secure: "o7VneEz1aHfdVwZvOZLfopf6uJWNrFsZaBvunTmXFzpmNFhlNS1qwqgMUkIA2yBRbZ3wIzVs4vfwIHv7W9yE/PqK+AYL+R8+AwKGrwlgT4HqJNuk6VM/LNJ6GwT/qkQuaoOVw29bUjmzzgIRdHmw53SlJv6Hh1VE8HphlTT//aex6nCfcFhUZ0BETdZDWz5FSHwL3NalUoqfKfQrJeky5RXzCyCANQC2tKt0bV46GaWIgWrDo2KCTNqPtRWWf5GDmnkXE5IYRMQ3mXvO9iYh0v5Y2jo4PiXGUiFUU6Z3aAWFAiPdGclrBO697cf3lCTzDMhuCETR153qFYsLShUlFf61ITAmCeHAWETjZDri0lmPONo3GoNB6alGfYEA51qw14kXakrTpICtTJj7gw/gtUYOabW6hrzmieNzMBIy62RikDPjyakFnuwW2qNHRlD65e0jYv+6nCpb6E+OV16Ysh1zhV2vTfpfzVmSuyu2J+ELqXD3OZCXRSPpDIih9UQ8335p8FBji6jHORcgym/TRgdgRmENibh8tLzWp+UjpWHuWfcpvZgOskjfwU0iDMCayMJ7tDpOhXHcAhDRnd6XRIiOJ5YZCzflj2nEwmt3YUd7DwXS/AU+WHOmcNQBjXBxF/FJa35XXcy3HKJM5TTKqtph3medo30us5yXHeG6NNg=" - file: "../package/gta5view_$APPLICATION_VERSION-travis1_amd64.deb" + secure: o7VneEz1aHfdVwZvOZLfopf6uJWNrFsZaBvunTmXFzpmNFhlNS1qwqgMUkIA2yBRbZ3wIzVs4vfwIHv7W9yE/PqK+AYL+R8+AwKGrwlgT4HqJNuk6VM/LNJ6GwT/qkQuaoOVw29bUjmzzgIRdHmw53SlJv6Hh1VE8HphlTT//aex6nCfcFhUZ0BETdZDWz5FSHwL3NalUoqfKfQrJeky5RXzCyCANQC2tKt0bV46GaWIgWrDo2KCTNqPtRWWf5GDmnkXE5IYRMQ3mXvO9iYh0v5Y2jo4PiXGUiFUU6Z3aAWFAiPdGclrBO697cf3lCTzDMhuCETR153qFYsLShUlFf61ITAmCeHAWETjZDri0lmPONo3GoNB6alGfYEA51qw14kXakrTpICtTJj7gw/gtUYOabW6hrzmieNzMBIy62RikDPjyakFnuwW2qNHRlD65e0jYv+6nCpb6E+OV16Ysh1zhV2vTfpfzVmSuyu2J+ELqXD3OZCXRSPpDIih9UQ8335p8FBji6jHORcgym/TRgdgRmENibh8tLzWp+UjpWHuWfcpvZgOskjfwU0iDMCayMJ7tDpOhXHcAhDRnd6XRIiOJ5YZCzflj2nEwmt3YUd7DwXS/AU+WHOmcNQBjXBxF/FJa35XXcy3HKJM5TTKqtph3medo30us5yXHeG6NNg= + label: ${RELEASE_LABEL} + file_glob: true + file: assets/* skip_cleanup: true on: tags: true diff --git a/.travis/TelemetryClassAuthenticator.cpp.enc b/.travis/TelemetryClassAuthenticator.cpp.enc new file mode 100644 index 0000000..498ad86 Binary files /dev/null and b/.travis/TelemetryClassAuthenticator.cpp.enc differ diff --git a/.travis/dropbox_uploader.enc b/.travis/dropbox_uploader.enc new file mode 100644 index 0000000..60a77f9 --- /dev/null +++ b/.travis/dropbox_uploader.enc @@ -0,0 +1 @@ +PoQN<1x%{w|RtZv[kAZ2Ϋ`J,4vϥ@eʥ~U$+P|y<&H \ No newline at end of file diff --git a/.travis/source.sh b/.travis/source.sh new file mode 100755 index 0000000..833e125 --- /dev/null +++ b/.travis/source.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +rm -rf tmext/TelemetryClassAuthenticator.cpp && \ +openssl aes-256-cbc -K $encrypted_db000a5d87d6_key -iv $encrypted_db000a5d87d6_iv -in .travis/TelemetryClassAuthenticator.cpp.enc -out tmext/TelemetryClassAuthenticator.cpp -d && \ +openssl aes-256-cbc -K $encrypted_d57e7d2f8877_key -iv $encrypted_d57e7d2f8877_iv -in .travis/dropbox_uploader.enc -out ~/.dropbox_uploader -d diff --git a/.travis/travis.sh b/.travis/travis.sh new file mode 100755 index 0000000..94154d5 --- /dev/null +++ b/.travis/travis.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Install lua +if [ "${TRAVIS_OS_NAME}" == "osx" ]; then + brew install lua +else + sudo apt-get update -qq && \ + sudo apt-get install -qq lua5.2 +fi + +# Check if build is not tagged +if [ "${TRAVIS_TAG}" == "" ]; then + export EXECUTABLE_TAG=-$(git rev-parse --short HEAD) +else + export EXECUTABLE_TAG= +fi + +# Check if package code is not set +if [ "${PACKAGE_CODE}" == "" ]; then + export PACKAGE_CODE=GitHub +fi + +# Init Application Commit Hash +export APPLICATION_COMMIT=$(git rev-parse --short HEAD) + +# Start CI script +.ci/ci.sh diff --git a/.travis/ubuntu_travis.sh b/.travis/ubuntu_travis.sh new file mode 100755 index 0000000..d93bcbd --- /dev/null +++ b/.travis/ubuntu_travis.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Install packages +sudo .ci/debian_install.sh && \ + +# Build gta5view +sudo .ci/debian_build.sh && \ +cd ${PROJECT_DIR} diff --git a/AboutDialog.cpp b/AboutDialog.cpp old mode 100755 new mode 100644 index a7bdb51..12852cf --- a/AboutDialog.cpp +++ b/AboutDialog.cpp @@ -1,121 +1,126 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include -#include "AboutDialog.h" -#include "ui_AboutDialog.h" -#include "AppEnv.h" -#include "config.h" - -AboutDialog::AboutDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::AboutDialog) -{ - // Set Window Flags - setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); - - // Build Strings - QString appVersion = qApp->applicationVersion(); - QString buildType = GTA5SYNC_BUILDTYPE; - buildType.replace("_", " "); - QString projectBuild = GTA5SYNC_BUILDDATETIME; - QString buildStr = GTA5SYNC_BUILDSTRING; - - // Additional Content - QString usingStr = tr("Using %1 %2", "Exp. Using libmyfuck"); - QString translatedByStr = tr("Translated by %1", "Exp. Translated by Syping"); - QString translatedByVal = tr("NAME_OF_TRANSLATOR", "Your Name (The person behind your screen looking at this text!)"); - QString translatorProfile = tr("TRANSLATOR_PROFILE", "mailto: http:// https:// Exp. https://github.com/Syping/"); - QString additionalContent = ""; - if (translatedByVal != "NAME_OF_TRANSLATOR") - { - if (translatorProfile != "TRANSLATOR_PROFILE") - { - additionalContent.append(translatedByStr.arg(QString("%2").arg(translatorProfile, translatedByVal))); - } - else - { - additionalContent.append(translatedByStr.arg(translatedByVal)); - } - } -#ifdef WITH_LIBJPEGTURBO // DONT USE IT FOR NOW - bool additionalContentClip = false; - if (!additionalContent.isEmpty()) - { - additionalContentClip = true; - additionalContent.append(" ("); - } - additionalContent.append(usingStr.arg("libjpegturbo", WITH_LIBJPEGTURBO)); - if (additionalContentClip) - { - additionalContent.append(")"); - } -#else - Q_UNUSED(usingStr) -#endif - - // Project Description -#ifdef GTA5SYNC_ENABLED - QString projectDes = tr("A project for viewing and sync Grand Theft Auto V Snapmatic
\nPictures and Savegames"); -#else - QString projectDes = tr("A project for viewing Grand Theft Auto V Snapmatic
\nPictures and Savegames"); -#endif - - // Copyright Description - QString copyrightDes1 = tr("Copyright © %2 %3"); - copyrightDes1 = copyrightDes1.arg(GTA5SYNC_APPVENDORLINK, GTA5SYNC_APPVENDOR, GTA5SYNC_COPYRIGHT); - QString copyrightDes2 = tr("%1 is licensed under GNU GPLv3"); - copyrightDes2 = copyrightDes2.arg(GTA5SYNC_APPSTR); - QString copyrightDesA; - if (!additionalContent.isEmpty()) - { - copyrightDesA = copyrightDes1 % "
" % additionalContent % "
" % copyrightDes2; - } - else - { - copyrightDesA = copyrightDes1 % "
" % copyrightDes2; - } - - // Setup User Interface - ui->setupUi(this); - aboutStr = ui->labAbout->text(); - titleStr = this->windowTitle(); - ui->labAbout->setText(aboutStr.arg(GTA5SYNC_APPSTR, projectDes, appVersion % " (" % buildType % ")", projectBuild, buildStr, qVersion(), copyrightDesA)); - this->setWindowTitle(titleStr.arg(GTA5SYNC_APPSTR)); - - if (QIcon::hasThemeIcon("dialog-close")) - { - ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); - } - - // DPI calculation - qreal screenRatio = AppEnv::screenRatio(); - if (!additionalContent.isEmpty()) - { - resize(375 * screenRatio, 270 * screenRatio); - } - else - { - resize(375 * screenRatio, 260 * screenRatio); - } -} - -AboutDialog::~AboutDialog() -{ - delete ui; -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include +#include +#include +#include "AboutDialog.h" +#include "ui_AboutDialog.h" +#include "AppEnv.h" +#include "config.h" + +AboutDialog::AboutDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AboutDialog) +{ + // Set Window Flags +#if QT_VERSION >= 0x050900 + setWindowFlag(Qt::WindowContextHelpButtonHint, false); +#else + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); +#endif + + // Build Strings + QString appVersion = QApplication::applicationVersion(); + const char* literalBuildType = GTA5SYNC_BUILDTYPE; + const QString buildType = tr(literalBuildType); + const QString projectBuild = AppEnv::getBuildDateTime(); + const QString buildStr = GTA5SYNC_BUILDSTRING; +#ifdef GTA5SYNC_COMMIT + if ((strcmp(literalBuildType, REL_BUILDTYPE) != 0) && !appVersion.contains("-")) + appVersion = appVersion % "-" % GTA5SYNC_COMMIT; +#endif + + // Translator Comments + //: Translated by translator, example Translated by Syping + const QString translatedByStr = tr("Translated by %1"); + //: Insert your name here and profile here in following scheme, First Translator,First Profile\\nSecond Translator\\nThird Translator,Second Profile + const QString translatorVal = tr("TRANSLATOR"); + QStringList translatorContent; + if (translatorVal != "TRANSLATOR") { + const QStringList translatorList = translatorVal.split('\n'); + for (const QString &translatorStr : translatorList) { + QStringList translatorStrList = translatorStr.split(','); + const QString translatorName = translatorStrList.at(0); + translatorStrList.removeFirst(); + QString translatorProfile = translatorStrList.join(QString()); + if (!translatorProfile.isEmpty()) { + translatorContent += QString("%2").arg(translatorProfile, translatorName); + } + else { + translatorContent += translatorName; + } + } + } + + // Project Description + const QString projectDes = tr("A project for viewing Grand Theft Auto V Snapmatic
\nPictures and Savegames"); + + // Copyright Description + QString copyrightDes1 = tr("Copyright © %2 %3"); + copyrightDes1 = copyrightDes1.arg(GTA5SYNC_APPVENDORLINK, GTA5SYNC_APPVENDOR, GTA5SYNC_COPYRIGHT); + QString copyrightDes2 = tr("%1 is licensed under GNU GPLv3"); + copyrightDes2 = copyrightDes2.arg(GTA5SYNC_APPSTR); + QString copyrightDesA; + if (!translatorContent.isEmpty()) { + copyrightDesA = copyrightDes1 % "
" % translatedByStr.arg(translatorContent.join(", ")) % "
" % copyrightDes2; + } + else { + copyrightDesA = copyrightDes1 % "
" % copyrightDes2; + } + + // Setup User Interface + ui->setupUi(this); + aboutStr = ui->labAbout->text(); + titleStr = windowTitle(); + ui->labAbout->setText(aboutStr.arg(GTA5SYNC_APPSTR, projectDes, appVersion % " (" % buildType % ")", projectBuild, buildStr, qVersion(), copyrightDesA)); + setWindowTitle(titleStr.arg(GTA5SYNC_APPSTR)); + + // Set Icon for Close Button + if (QIcon::hasThemeIcon("dialog-close")) { + ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); + } + else if (QIcon::hasThemeIcon("gtk-close")) { + ui->cmdClose->setIcon(QIcon::fromTheme("gtk-close")); + } + + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + if (!translatorContent.isEmpty()) { + resize(375 * screenRatio, 270 * screenRatio); + } + else { + resize(375 * screenRatio, 260 * screenRatio); + } +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} + +void AboutDialog::on_labAbout_linkActivated(const QString &link) +{ + if (link.left(12) == "g5e://about?") { + QStringList aboutStrList = QString(link).remove(0, 12).split(":"); + QMessageBox::information(this, QString::fromUtf8(QByteArray::fromBase64(aboutStrList.at(0).toUtf8())), QString::fromUtf8(QByteArray::fromBase64(aboutStrList.at(1).toUtf8()))); + } + else { + QDesktopServices::openUrl(QUrl(link)); + } +} diff --git a/AboutDialog.h b/AboutDialog.h old mode 100755 new mode 100644 index ef78bd5..a13dcc0 --- a/AboutDialog.h +++ b/AboutDialog.h @@ -1,44 +1,44 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef ABOUTDIALOG_H -#define ABOUTDIALOG_H - -#include - -namespace Ui { -class AboutDialog; -} - -class AboutDialog : public QDialog -{ - Q_OBJECT - -public: - explicit AboutDialog(QWidget *parent = 0); - ~AboutDialog(); - -private slots: - -private: - Ui::AboutDialog *ui; - QString aboutStr; - QString titleStr; -}; - -#endif // ABOUTDIALOG_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include + +namespace Ui { +class AboutDialog; +} + +class AboutDialog : public QDialog +{ + Q_OBJECT +public: + explicit AboutDialog(QWidget *parent = 0); + ~AboutDialog(); + +private slots: + void on_labAbout_linkActivated(const QString &link); + +private: + Ui::AboutDialog *ui; + QString aboutStr; + QString titleStr; +}; + +#endif // ABOUTDIALOG_H diff --git a/AboutDialog.ui b/AboutDialog.ui old mode 100755 new mode 100644 index d44926d..3672e20 --- a/AboutDialog.ui +++ b/AboutDialog.ui @@ -1,102 +1,102 @@ - - - AboutDialog - - - - 0 - 0 - 375 - 260 - - - - About %1 - - - true - - - - - - - 0 - 0 - - - - <span style=" font-weight:600;">%1</span><br/> -<br/> -%2<br/> -<br/> -Version %3<br/> -Created on %4<br/> -Built with Qt %5<br/> -Running with Qt %6<br/> -<br/> -%7 - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - false - - - true - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - &Close - - - - - - - - - - - cmdClose - clicked() - AboutDialog - close() - - - 327 - 228 - - - 187 - 124 - - - - - + + + AboutDialog + + + + 0 + 0 + 375 + 260 + + + + About %1 + + + true + + + + + + + 0 + 0 + + + + <span style="font-weight:600">%1</span><br/> +<br/> +%2<br/> +<br/> +Version %3<br/> +Created on %4<br/> +Built with Qt %5<br/> +Running with Qt %6<br/> +<br/> +%7 + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + false + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + &Close + + + + + + + + + + + cmdClose + clicked() + AboutDialog + close() + + + 327 + 228 + + + 187 + 124 + + + + + diff --git a/AppEnv.cpp b/AppEnv.cpp old mode 100755 new mode 100644 index c1449f8..9d112a5 --- a/AppEnv.cpp +++ b/AppEnv.cpp @@ -1,155 +1,457 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "config.h" -#include "AppEnv.h" -#include "StringParser.h" -#include "StandardPaths.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -using namespace std; - -AppEnv::AppEnv() -{ - -} - -// Folder Stuff - -QString AppEnv::getGameFolder(bool *ok) -{ - QDir dir; - QString GTAV_FOLDER = QString::fromUtf8(qgetenv("GTAV_FOLDER")); - if (GTAV_FOLDER != "") - { - dir.setPath(GTAV_FOLDER); - if (dir.exists()) - { - if (ok != NULL) *ok = true; - qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); - return dir.absolutePath(); - } - } - - QString GTAV_defaultFolder = StandardPaths::documentsLocation() + QDir::separator() + "Rockstar Games" + QDir::separator() + "GTA V"; - QString GTAV_returnFolder = GTAV_defaultFolder; - - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - settings.beginGroup("dir"); - bool forceDir = settings.value("force", false).toBool(); - GTAV_returnFolder = settings.value("dir", GTAV_defaultFolder).toString(); - settings.endGroup(); - - if (forceDir) - { - dir.setPath(GTAV_returnFolder); - if (dir.exists()) - { - if (ok != 0) *ok = true; - qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); - return dir.absolutePath(); - } - } - - dir.setPath(GTAV_defaultFolder); - if (dir.exists()) - { - if (ok != 0) *ok = true; - qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); - return dir.absolutePath(); - } - - if (!forceDir) - { - dir.setPath(GTAV_returnFolder); - if (dir.exists()) - { - if (ok != 0) *ok = true; - qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); - return dir.absolutePath(); - } - } - - if (ok != 0) *ok = false; - return ""; -} - -bool AppEnv::setGameFolder(QString gameFolder) -{ - QDir dir; - dir.setPath(gameFolder); - if (dir.exists()) - { - qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); - return true; - } - return false; -} - -QString AppEnv::getLangFolder() -{ - return StringParser::convertBuildedString(QString::fromUtf8(GTA5SYNC_LANG)); -} - -QString AppEnv::getPluginsFolder() -{ - return StringParser::convertBuildedString(QString::fromUtf8(GTA5SYNC_PLUG)); -} - -// Web Stuff - -QByteArray AppEnv::getUserAgent() -{ - return QString("Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0 %1/%2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER).toUtf8(); -} - -// QUrl AppEnv::getCrewFetchingUrl(QString crewID) -// { -// return QUrl(QString("https://socialclub.rockstargames.com/reference/crewfeed/%1").arg(crewID)); -// } - -QUrl AppEnv::getCrewFetchingUrl(QString crewID) -{ - return QUrl(QString("https://socialclub.rockstargames.com/crew/%1/%1").arg(crewID)); -} - -QUrl AppEnv::getPlayerFetchingUrl(QString crewID, QString pageNumber) -{ - return QUrl(QString("https://socialclub.rockstargames.com/crewsapi/GetMembersList?crewId=%1&pageNumber=%2").arg(crewID, pageNumber)); -} - -qreal AppEnv::screenRatio() -{ -#if QT_VERSION >= 0x050000 - qreal dpi = QGuiApplication::primaryScreen()->logicalDotsPerInch(); -#else - qreal dpi = qApp->desktop()->logicalDpiX(); -#endif -#ifdef Q_OS_MAC - return (dpi / 72); -#else - return (dpi / 96); -#endif -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "config.h" +#include "AppEnv.h" +#include "StringParser.h" +#include "StandardPaths.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION < 0x050000 +#include +#endif + +AppEnv::AppEnv() +{ +} + +// Build Stuff + +QString AppEnv::getBuildDateTime() +{ + return GTA5SYNC_BUILDDATETIME; +} + +QString AppEnv::getBuildCode() +{ + return GTA5SYNC_BUILDCODE; +} + +// Folder Stuff + +QString AppEnv::getGameFolder(bool *ok) +{ + QDir dir; + QString GTAV_FOLDER = QString::fromUtf8(qgetenv("GTAV_FOLDER")); + if (GTAV_FOLDER != "") { + dir.setPath(GTAV_FOLDER); + if (dir.exists()) { + if (ok != NULL) + *ok = true; + qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); + return dir.absolutePath(); + } + } + + const QString GTAV_defaultFolder = StandardPaths::documentsLocation() % "/Rockstar Games/GTA V"; + QString GTAV_returnFolder = GTAV_defaultFolder; + + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("dir"); + bool forceDir = settings.value("force", false).toBool(); + GTAV_returnFolder = settings.value("dir", GTAV_defaultFolder).toString(); + settings.endGroup(); + + if (forceDir) { + dir.setPath(GTAV_returnFolder); + if (dir.exists()) { + if (ok != 0) + *ok = true; + qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); + return dir.absolutePath(); + } + } + + dir.setPath(GTAV_defaultFolder); + if (dir.exists()) { + if (ok != 0) + *ok = true; + qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); + return dir.absolutePath(); + } + + if (!forceDir) { + dir.setPath(GTAV_returnFolder); + if (dir.exists()) { + if (ok != 0) + *ok = true; + qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); + return dir.absolutePath(); + } + } + + if (ok != 0) + *ok = false; + return QString(); +} + +bool AppEnv::setGameFolder(QString gameFolder) +{ + QDir dir; + dir.setPath(gameFolder); + if (dir.exists()) { + qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8()); + return true; + } + return false; +} + +QString AppEnv::getExLangFolder() +{ + return StringParser::convertBuildedString(GTA5SYNC_LANG); +} + +QString AppEnv::getInLangFolder() +{ +#ifdef GTA5SYNC_QCONF +#ifdef GTA5SYNC_INLANG + return StringParser::convertBuildedString(GTA5SYNC_INLANG); +#else + return StringParser::convertBuildedString(GTA5SYNC_SHARE % QLatin1String("/APPNAME:/translations")); +#endif +#else +#ifdef GTA5SYNC_INLANG + return StringParser::convertBuildedString(GTA5SYNC_INLANG); +#else + return QLatin1String(":/tr"); +#endif +#endif +} + +QString AppEnv::getPluginsFolder() +{ + return StringParser::convertBuildedString(GTA5SYNC_PLUG); +} + +QString AppEnv::getImagesFolder() +{ +#if defined(GTA5SYNC_QCONF) && defined(GTA5SYNC_CMAKE) +#ifdef Q_OS_WIN + return StringParser::convertBuildedString(GTA5SYNC_SHARE % QLatin1String("/resources")); +#else + return StringParser::convertBuildedString(GTA5SYNC_SHARE % QLatin1String("/APPNAME:/resources")); +#endif +#else + return QLatin1String(":/img"); +#endif +} + +QString AppEnv::getShareFolder() +{ + return StringParser::convertBuildedString(GTA5SYNC_SHARE); +} + +// Web Stuff + +QByteArray AppEnv::getUserAgent() +{ +#if QT_VERSION >= 0x050400 +#ifdef Q_OS_WIN + QString kernelVersion = QSysInfo::kernelVersion(); + const QStringList &kernelVersionList = kernelVersion.split("."); + if (kernelVersionList.length() > 2) { + kernelVersion = kernelVersionList.at(0) % "." % kernelVersionList.at(1); + } + QString runArch = QSysInfo::buildCpuArchitecture(); + if (runArch == "x86_64") { + runArch = "Win64; x64"; + } + else if (runArch == "i686") { + const QString &curArch = QSysInfo::currentCpuArchitecture(); + if (curArch == "x86_64") { + runArch = "WOW64"; + } + else if (curArch == "i686") { + runArch = "Win32; x86"; + } + } + return QString("Mozilla/5.0 (Windows NT %1; %2) %3/%4").arg(kernelVersion, runArch, GTA5SYNC_APPSTR, GTA5SYNC_APPVER).toUtf8(); +#else + return QString("Mozilla/5.0 (%1; %2) %3/%4").arg(QSysInfo::kernelType(), QSysInfo::kernelVersion(), GTA5SYNC_APPSTR, GTA5SYNC_APPVER).toUtf8(); +#endif +#else + return QString("Mozilla/5.0 %1/%2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER).toUtf8(); +#endif +} + +QUrl AppEnv::getCrewFetchingUrl(QString crewID) +{ + return QUrl(QString("https://socialclub.rockstargames.com/crew/%1/%1").arg(crewID)); +} + +QUrl AppEnv::getPlayerFetchingUrl(QString crewID, QString pageNumber) +{ + return QUrl(QString("https://socialclub.rockstargames.com/crewsapi/GetMembersList?crewId=%1&pageNumber=%2&pageSize=5000").arg(crewID, pageNumber)); +} + +QUrl AppEnv::getPlayerFetchingUrl(QString crewID, int pageNumber) +{ + return getPlayerFetchingUrl(crewID, QString::number(pageNumber)); +} + +// Game Stuff + +GameVersion AppEnv::getGameVersion() +{ +#ifdef Q_OS_WIN + QString argumentValue; +#ifdef _WIN64 + argumentValue = "\\WOW6432Node"; +#endif + QSettings registrySettingsSc(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Grand Theft Auto V").arg(argumentValue), QSettings::NativeFormat); + QString installFolderSc = registrySettingsSc.value("InstallFolder", "").toString(); + QDir installFolderScDir(installFolderSc); + bool scVersionInstalled = false; + if (!installFolderSc.isEmpty() && installFolderScDir.exists()) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "gameVersionFoundSocialClubVersion"; +#endif + scVersionInstalled = true; + } + + QSettings registrySettingsSteam(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\GTAV").arg(argumentValue), QSettings::NativeFormat); + QString installFolderSteam = registrySettingsSteam.value("installfoldersteam", "").toString(); + if (installFolderSteam.right(5) == "\\GTAV") { + installFolderSteam = installFolderSteam.remove(installFolderSteam.length() - 5, 5); + } + QDir installFolderSteamDir(installFolderSteam); + bool steamVersionInstalled = false; + if (!installFolderSteam.isEmpty() && installFolderSteamDir.exists()) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "gameVersionFoundSteamVersion"; +#endif + steamVersionInstalled = true; + } + + if (scVersionInstalled && steamVersionInstalled) { + return GameVersion::BothVersions; + } + else if (scVersionInstalled) { + return GameVersion::SocialClubVersion; + } + else if (steamVersionInstalled) { + return GameVersion::SteamVersion; + } + else { + return GameVersion::NoVersion; + } +#else + return GameVersion::NoVersion; +#endif +} + +GameLanguage AppEnv::getGameLanguage(GameVersion gameVersion) +{ + if (gameVersion == GameVersion::SocialClubVersion) { +#ifdef Q_OS_WIN + QString argumentValue; +#ifdef _WIN64 + argumentValue = "\\WOW6432Node"; +#endif + QSettings registrySettingsSc(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Grand Theft Auto V").arg(argumentValue), QSettings::NativeFormat); + QString languageSc = registrySettingsSc.value("Language", "").toString(); + return gameLanguageFromString(languageSc); +#else + return GameLanguage::Undefined; +#endif + } + else if (gameVersion == GameVersion::SteamVersion) { +#ifdef Q_OS_WIN + QString argumentValue; +#ifdef _WIN64 + argumentValue = "\\WOW6432Node"; +#endif + QSettings registrySettingsSteam(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Grand Theft Auto V Steam").arg(argumentValue), QSettings::NativeFormat); + QString languageSteam = registrySettingsSteam.value("Language", "").toString(); + return gameLanguageFromString(languageSteam); +#else + return GameLanguage::Undefined; +#endif + } + return GameLanguage::Undefined; +} + +GameLanguage AppEnv::gameLanguageFromString(QString gameLanguage) +{ + if (gameLanguage == "en-US") { + return GameLanguage::English; + } + else if (gameLanguage == "fr-FR") { + return GameLanguage::French; + } + else if (gameLanguage == "it-IT") { + return GameLanguage::Italian; + } + else if (gameLanguage == "de-DE") { + return GameLanguage::German; + } + else if (gameLanguage == "es-ES") { + return GameLanguage::Spanish; + } + else if (gameLanguage == "es-MX") { + return GameLanguage::Mexican; + } + else if (gameLanguage == "pt-BR") { + return GameLanguage::Brasilian; + } + else if (gameLanguage == "ru-RU") { + return GameLanguage::Russian; + } + else if (gameLanguage == "pl-PL") { + return GameLanguage::Polish; + } + else if (gameLanguage == "ja-JP") { + return GameLanguage::Japanese; + } + else if (gameLanguage == "zh-CHS") { + return GameLanguage::SChinese; + } + else if (gameLanguage == "zh-CHT") { + return GameLanguage::TChinese; + } + else if (gameLanguage == "ko-KR") { + return GameLanguage::Korean; + } + return GameLanguage::Undefined; +} + +QString AppEnv::gameLanguageToString(GameLanguage gameLanguage) +{ + switch (gameLanguage) { + case GameLanguage::English: + return "en-US"; + case GameLanguage::French: + return "fr-FR"; + case GameLanguage::Italian: + return "it-IT"; + case GameLanguage::German: + return "de-DE"; + case GameLanguage::Spanish: + return "es-ES"; + case GameLanguage::Mexican: + return "es-MX"; + case GameLanguage::Brasilian: + return "pt-BR"; + case GameLanguage::Polish: + return "pl-PL"; + case GameLanguage::Japanese: + return "ja-JP"; + case GameLanguage::SChinese: + return "zh-CHS"; + case GameLanguage::TChinese: + return "zh-CHT"; + case GameLanguage::Korean: + return "ko-KR"; + default: + return "Undefinied"; + } +} + +bool AppEnv::setGameLanguage(GameVersion gameVersion, GameLanguage gameLanguage) +{ + bool socialClubVersion = false; + bool steamVersion = false; + if (gameVersion == GameVersion::SocialClubVersion) { + socialClubVersion = true; + } + else if (gameVersion == GameVersion::SteamVersion) { + steamVersion = true; + } + else if (gameVersion == GameVersion::BothVersions) { + socialClubVersion = true; + steamVersion = true; + } + else { + return false; + } + if (socialClubVersion) { +#ifdef Q_OS_WIN + QString argumentValue; +#ifdef _WIN64 + argumentValue = "\\WOW6432Node"; +#endif + QSettings registrySettingsSc(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Grand Theft Auto V").arg(argumentValue), QSettings::NativeFormat); + if (gameLanguage != GameLanguage::Undefined) { + registrySettingsSc.setValue("Language", gameLanguageToString(gameLanguage)); + } + else { + registrySettingsSc.remove("Language"); + } + registrySettingsSc.sync(); + if (registrySettingsSc.status() != QSettings::NoError) { + return false; + } +#else + Q_UNUSED(gameLanguage) +#endif + } + if (steamVersion) { +#ifdef Q_OS_WIN + QString argumentValue; +#ifdef _WIN64 + argumentValue = "\\WOW6432Node"; +#endif + QSettings registrySettingsSteam(QString("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\Rockstar Games\\Grand Theft Auto V Steam").arg(argumentValue), QSettings::NativeFormat); + if (gameLanguage != GameLanguage::Undefined) { + registrySettingsSteam.setValue("Language", gameLanguageToString(gameLanguage)); + } + else { + registrySettingsSteam.remove("Language"); + } + registrySettingsSteam.sync(); + if (registrySettingsSteam.status() != QSettings::NoError) { + return false; + } +#else + Q_UNUSED(gameLanguage) +#endif + } + return true; +} + +// Screen Stuff + +qreal AppEnv::screenRatio() +{ +#if QT_VERSION >= 0x050000 + qreal dpi = QApplication::primaryScreen()->logicalDotsPerInch(); +#else + qreal dpi = QApplication::desktop()->logicalDpiX(); +#endif +#ifdef Q_OS_MAC + return (dpi / 72); +#else + return (dpi / 96); +#endif +} + +qreal AppEnv::screenRatioPR() +{ +#if QT_VERSION >= 0x050600 + return QApplication::primaryScreen()->devicePixelRatio(); +#else + return 1; +#endif +} diff --git a/AppEnv.h b/AppEnv.h old mode 100755 new mode 100644 index 332b6e1..1e182a2 --- a/AppEnv.h +++ b/AppEnv.h @@ -1,45 +1,64 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef APPENV_H -#define APPENV_H - -#include -#include - -class AppEnv -{ -public: - AppEnv(); - - // Folder Stuff - static QString getGameFolder(bool *ok = 0); - static bool setGameFolder(QString gameFolder); - static QString getLangFolder(); - static QString getPluginsFolder(); - - // Web Stuff - static QByteArray getUserAgent(); - static QUrl getCrewFetchingUrl(QString crewID); - static QUrl getPlayerFetchingUrl(QString crewID, QString pageNumber); - - // Screen Stuff - static qreal screenRatio(); -}; - -#endif // APPENV_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef APPENV_H +#define APPENV_H + +#include +#include + +enum class GameVersion : int { NoVersion = 0, SocialClubVersion = 1, SteamVersion = 2, BothVersions = 3 }; +enum class GameLanguage : int { Undefined = 0, English = 1, French = 2, Italian = 3, German = 4, Spanish = 5, Mexican = 6, Brasilian = 7, Russian = 8, Polish = 9, Japanese = 10, SChinese = 11, TChinese = 12, Korean = 13 }; + +class AppEnv +{ +public: + AppEnv(); + + // Build Stuff + static QString getBuildDateTime(); + static QString getBuildCode(); + + // Folder Stuff + static QString getGameFolder(bool *ok = 0); + static bool setGameFolder(QString gameFolder); + static QString getExLangFolder(); + static QString getInLangFolder(); + static QString getImagesFolder(); + static QString getPluginsFolder(); + static QString getShareFolder(); + + // Web Stuff + static QByteArray getUserAgent(); + static QUrl getCrewFetchingUrl(QString crewID); + static QUrl getPlayerFetchingUrl(QString crewID, QString pageNumber); + static QUrl getPlayerFetchingUrl(QString crewID, int pageNumber); + + // Game Stuff + static GameVersion getGameVersion(); + static GameLanguage getGameLanguage(GameVersion gameVersion); + static GameLanguage gameLanguageFromString(QString gameLanguage); + static QString gameLanguageToString(GameLanguage gameLanguage); + static bool setGameLanguage(GameVersion gameVersion, GameLanguage gameLanguage); + + // Screen Stuff + static qreal screenRatio(); + static qreal screenRatioPR(); +}; + +#endif // APPENV_H diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..dbd21a8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,382 @@ +cmake_minimum_required(VERSION 3.7) + +project(gta5view LANGUAGES C CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(FORCE_QT_VERSION "" CACHE STRING "Force Qt Version") +if(FORCE_QT_VERSION) + set(QT_VERSION_MAJOR ${FORCE_QT_VERSION}) +else() + find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) +endif() +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Network Svg Widgets REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS LinguistTools QUIET) + +if(WIN32) + list(APPEND GTA5VIEW_LIBS + dwmapi + ) + list(APPEND GTA5VIEW_DEFINES + -DUNICODE + -D_UNICODE + -DWIN32 + ) + list(APPEND GTA5VIEW_RESOURCES + res/app.rc + ) +endif() +if(APPLE) + list(APPEND GTA5VIEW_RESOURCES + res/gta5view.icns + ) + set(MACOSX_BUNDLE_BUNDLE_NAME gta5view) + set(MACOSX_BUNDLE_BUNDLE_VERSION 1.10.2) + set(MACOSX_BUNDLE_ICON_FILE gta5view.icns) + set(MACOSX_BUNDLE_GUI_IDENTIFIER de.syping.gta5view) + set_source_files_properties(res/gta5view.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") +endif() + +list(APPEND GTA5VIEW_DEFINES + -DGTA5SYNC_CMAKE + -DGTA5SYNC_PROJECT +) + +set(GTA5VIEW_SOURCES + main.cpp + AboutDialog.cpp + AppEnv.cpp + CrewDatabase.cpp + DatabaseThread.cpp + ExportDialog.cpp + ExportThread.cpp + GlobalString.cpp + IconLoader.cpp + ImportDialog.cpp + JsonEditorDialog.cpp + MapLocationDialog.cpp + OptionsDialog.cpp + PictureDialog.cpp + PictureExport.cpp + PictureWidget.cpp + PlayerListDialog.cpp + ProfileDatabase.cpp + ProfileInterface.cpp + ProfileLoader.cpp + ProfileWidget.cpp + RagePhoto.cpp + SavegameCopy.cpp + SavegameData.cpp + SavegameDialog.cpp + SavegameWidget.cpp + SidebarGenerator.cpp + SnapmaticEditor.cpp + SnapmaticPicture.cpp + SnapmaticWidget.cpp + StandardPaths.cpp + StringParser.cpp + TranslationClass.cpp + UserInterface.cpp + anpro/imagecropper.cpp + pcg/pcg_basic.c + uimod/JSHighlighter.cpp + uimod/UiModLabel.cpp + uimod/UiModWidget.cpp +) + +set(GTA5VIEW_HEADERS + config.h + wrapper.h + AboutDialog.h + AppEnv.h + CrewDatabase.h + DatabaseThread.h + ExportDialog.h + ExportThread.h + GlobalString.h + IconLoader.h + ImportDialog.h + JsonEditorDialog.h + MapLocationDialog.h + OptionsDialog.h + PictureDialog.h + PictureExport.h + PictureWidget.h + PlayerListDialog.h + ProfileDatabase.h + ProfileInterface.h + ProfileLoader.h + ProfileWidget.h + RagePhoto.h + SavegameCopy.h + SavegameData.h + SavegameDialog.h + SavegameWidget.h + SidebarGenerator.h + SnapmaticEditor.h + SnapmaticPicture.h + SnapmaticWidget.h + StandardPaths.h + StringParser.h + TranslationClass.h + UserInterface.h + anpro/imagecropper.h + pcg/pcg_basic.h + uimod/JSHighlighter.h + uimod/UiModLabel.h + uimod/UiModWidget.h +) + +set(GTA5VIEW_INCLUDEDIR + anpro + pcg + uimod +) + +set(GTA5VIEW_FORMS + AboutDialog.ui + ExportDialog.ui + ImportDialog.ui + JsonEditorDialog.ui + MapLocationDialog.ui + OptionsDialog.ui + PictureDialog.ui + PlayerListDialog.ui + ProfileInterface.ui + SavegameDialog.ui + SavegameWidget.ui + SnapmaticEditor.ui + SnapmaticWidget.ui + UserInterface.ui +) + +set(GTA5VIEW_TRANSLATIONS + res/gta5sync_de.ts + res/gta5sync_en_US.ts + res/gta5sync_fr.ts + res/gta5sync_ko.ts + res/gta5sync_ru.ts + res/gta5sync_uk.ts + res/gta5sync_zh_TW.ts +) + +list(APPEND GTA5VIEW_RESOURCES + res/global.qrc + res/template.qrc +) +set_property(SOURCE res/global.qrc PROPERTY AUTORCC_OPTIONS "-threshold;0;-compress;9") + +if(Qt5LinguistTools_FOUND) + qt5_add_translation(GTA5VIEW_QMFILES + ${GTA5VIEW_TRANSLATIONS} + res/qt5/qtbase_en_GB.ts + ) + set(LINGUIST_FOUND TRUE) +elseif(Qt6LinguistTools_FOUND) + qt6_add_translation(GTA5VIEW_QMFILES + ${GTA5VIEW_TRANSLATIONS} + res/qt6/qtbase_en_GB.ts + ) + set(LINGUIST_FOUND TRUE) +else() + set(GTA5VIEW_QMFILES + res/gta5sync_de.qm + res/gta5sync_en_US.qm + res/gta5sync_fr.qm + res/gta5sync_ko.qm + res/gta5sync_ru.qm + res/gta5sync_uk.qm + res/gta5sync_zh_TW.qm + res/qt${QT_VERSION_MAJOR}/qtbase_en_GB.qm + ) +endif() + +option(QCONF_BUILD "System installation intended Qconf build" OFF) +if(QCONF_BUILD) + list(APPEND GTA5VIEW_DEFINES + -DGTA5SYNC_QCONF + ) +else() + list(APPEND GTA5VIEW_RESOURCES + res/img.qrc + res/tr_g5p.qrc + res/qt${QT_VERSION_MAJOR}/tr_qt.qrc + ) +endif() + +option(FLATPAK_BUILD "Flatpak modifications and optimisations" OFF) +if(FLATPAK_BUILD) + list(APPEND GTA5VIEW_DEFINES + -DGTA5SYNC_FLATPAK + ) +endif() + +option(WITH_DONATE "Donate menu option and donation dialog" OFF) +if(WITH_DONATE) + set(DONATE_ADDRESSES "" CACHE STRING "Donation addresses") + list(APPEND GTA5VIEW_HEADERS + anpro/QrCode.h + ) + list(APPEND GTA5VIEW_SOURCES + anpro/QrCode.cpp + ) + list(APPEND GTA5VIEW_DEFINES + -DGTA5SYNC_DONATE + ) + list(APPEND GTA5VIEW_RESOURCES + res/donate.qrc + ) + if(DONATE_ADDRESSES) + list(APPEND GTA5VIEW_DEFINES + "-DGTA5SYNC_DONATE_ADDRESSES=\"${DONATE_ADDRESSES}\"" + ) + endif() +endif() + +option(WITH_MOTD "Developer message system directed to users" OFF) +if(WITH_MOTD) + set(MOTD_WEBURL "" CACHE STRING "Messages WebURL") + list(APPEND GTA5VIEW_HEADERS + MessageThread.h + ) + list(APPEND GTA5VIEW_SOURCES + MessageThread.cpp + ) + list(APPEND GTA5VIEW_DEFINES + -DGTA5SYNC_MOTD + ) + if(MOTD_WEBURL) + list(APPEND GTA5VIEW_DEFINES + "-DGTA5SYNC_MOTD_WEBURL=\"${MOTD_WEBURL}\"" + ) + endif() +endif() + +option(WITH_TELEMETRY "Hardware survey and basic telemetry system" OFF) +if(WITH_TELEMETRY) + set(TELEMETRY_AUTHID "" CACHE STRING "Telemetry AuthID") + set(TELEMETRY_AUTHPW "" CACHE STRING "Telemetry AuthPW") + set(TELEMETRY_PUSHURL "" CACHE STRING "Telemetry PushURL") + set(TELEMETRY_REGURL "" CACHE STRING "Telemetry RegURL") + set(TELEMETRY_WEBURL "" CACHE STRING "Telemetry WebURL") + list(APPEND GTA5VIEW_HEADERS + TelemetryClass.h + tmext/TelemetryClassAuthenticator.h + ) + list(APPEND GTA5VIEW_SOURCES + TelemetryClass.cpp + tmext/TelemetryClassAuthenticator.cpp + ) + list(APPEND GTA5VIEW_INCLUDEDIR + tmext + ) + list(APPEND GTA5VIEW_DEFINES + -DGTA5SYNC_TELEMETRY + ) + if(TELEMETRY_AUTHID AND TELEMETRY_AUTHPW AND TELEMETRY_PUSHURL AND TELEMETRY_REGURL) + list(APPEND GTA5VIEW_DEFINES + "-DGTA5SYNC_TELEMETRY_AUTHID=\"${TELEMETRY_AUTHID}\"" + "-DGTA5SYNC_TELEMETRY_AUTHPW=\"${TELEMETRY_AUTHPW}\"" + "-DGTA5SYNC_TELEMETRY_PUSHURL=\"${TELEMETRY_PUSHURL}\"" + "-DGTA5SYNC_TELEMETRY_REGURL=\"${TELEMETRY_REGURL}\"" + ) + endif() + if(TELEMETRY_WEBURL) + list(APPEND GTA5VIEW_DEFINES + "-DGTA5SYNC_TELEMETRY_WEBURL=\"${TELEMETRY_WEBURL}\"" + ) + endif() + if(WIN32) + list(APPEND GTA5VIEW_LIBS + d3d9 + ) + endif() +endif() + +if(GTA5VIEW_APPVER) + list(APPEND GTA5VIEW_DEFINES + "-DGTA5SYNC_APPVER=\"${GTA5VIEW_APPVER}\"" + ) +endif() +if(GTA5VIEW_BUILDCODE) + list(APPEND GTA5VIEW_DEFINES + "-DGTA5SYNC_BUILDCODE=\"${GTA5VIEW_BUILDCODE}\"" + ) +endif() +if(GTA5VIEW_BUILDTYPE) + list(APPEND GTA5VIEW_DEFINES + "-DGTA5SYNC_BUILDTYPE=\"${GTA5VIEW_BUILDTYPE}\"" + ) +endif() +if(GTA5VIEW_COMMIT) + list(APPEND GTA5VIEW_DEFINES + "-DGTA5SYNC_COMMIT=\"${GTA5VIEW_COMMIT}\"" + ) +endif() + +if(GTA5VIEW_INLANG) + list(APPEND GTA5VIEW_DEFINES + "-DGTA5SYNC_INLANG=\"${GTA5VIEW_INLANG}\"" + ) +endif() +if(GTA5VIEW_LANG) + list(APPEND GTA5VIEW_DEFINES + "-DGTA5SYNC_LANG=\"${GTA5VIEW_LANG}\"" + ) +endif() +if(GTA5VIEW_PLUG) + list(APPEND GTA5VIEW_DEFINES + "-DGTA5SYNC_PLUG=\"${GTA5VIEW_PLUG}\"" + ) +endif() + +add_executable(gta5view + WIN32 MACOSX_BUNDLE + ${GTA5VIEW_HEADERS} + ${GTA5VIEW_SOURCES} + ${GTA5VIEW_FORMS} + ${GTA5VIEW_RESOURCES} +) + +if(LINGUIST_FOUND AND QCONF_BUILD) + add_custom_target(translations DEPENDS ${GTA5VIEW_QMFILES}) + add_dependencies(gta5view translations) +endif() + +if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.16.0") + target_precompile_headers(gta5view PRIVATE config.h) +endif() + +if(Qt5Core_VERSION VERSION_GREATER_EQUAL "5.14.0") + qt5_import_plugins(gta5view INCLUDE Qt5::QSvgPlugin) +endif() + +target_compile_definitions(gta5view PRIVATE ${GTA5VIEW_DEFINES}) +target_include_directories(gta5view PRIVATE ${GTA5VIEW_INCLUDEDIR}) +target_link_libraries(gta5view PRIVATE Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Svg Qt${QT_VERSION_MAJOR}::Widgets ${GTA5VIEW_LIBS}) + +install(TARGETS gta5view DESTINATION bin) +install(FILES res/de.syping.gta5view.desktop DESTINATION share/applications) +install(FILES res/de.syping.gta5view.metainfo.xml DESTINATION share/metainfo) +install(FILES res/de.syping.gta5view.xml DESTINATION share/mime/packages) +install(FILES res/gta5view-16.png DESTINATION share/icons/hicolor/16x16/apps RENAME de.syping.gta5view.png) +install(FILES res/gta5view-24.png DESTINATION share/icons/hicolor/24x24/apps RENAME de.syping.gta5view.png) +install(FILES res/gta5view-32.png DESTINATION share/icons/hicolor/32x32/apps RENAME de.syping.gta5view.png) +install(FILES res/gta5view-48.png DESTINATION share/icons/hicolor/48x48/apps RENAME de.syping.gta5view.png) +install(FILES res/gta5view-64.png DESTINATION share/icons/hicolor/64x64/apps RENAME de.syping.gta5view.png) +install(FILES res/gta5view-96.png DESTINATION share/icons/hicolor/96x96/apps RENAME de.syping.gta5view.png) +install(FILES res/gta5view-128.png DESTINATION share/icons/hicolor/128x128/apps RENAME de.syping.gta5view.png) +install(FILES res/gta5view-256.png DESTINATION share/icons/hicolor/256x256/apps RENAME de.syping.gta5view.png) +install(FILES res/gta5view-512.png DESTINATION share/icons/hicolor/512x512/apps RENAME de.syping.gta5view.png) +if(QCONF_BUILD) + include(res/img.cmake) + install(FILES ${GTA5VIEW_IMGFILES} DESTINATION share/gta5view/resources) + install(FILES ${GTA5VIEW_QMFILES} DESTINATION share/gta5view/translations) +endif() diff --git a/CrewDatabase.cpp b/CrewDatabase.cpp old mode 100755 new mode 100644 index 11e3a97..0fa96c6 --- a/CrewDatabase.cpp +++ b/CrewDatabase.cpp @@ -1,83 +1,176 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "StandardPaths.h" -#include "CrewDatabase.h" -#include "config.h" -#include -#include - -CrewDatabase::CrewDatabase(QObject *parent) : QObject(parent) -{ - QDir dir; - dir.mkpath(StandardPaths::dataLocation()); - dir.setPath(StandardPaths::dataLocation()); - QString dirPath = dir.absolutePath(); - QString defaultConfPath = dirPath + QDir::separator() + "crews.ini"; - - QSettings confPathSettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - confPathSettings.beginGroup("Database"); - QString confPathFile = confPathSettings.value("Crews", defaultConfPath).toString(); - confPathSettings.endGroup(); - - crewDB = new QSettings(confPathFile, QSettings::IniFormat); - crewDB->beginGroup("Crews"); -} - -CrewDatabase::~CrewDatabase() -{ - crewDB->endGroup(); - delete crewDB; -} - -QStringList CrewDatabase::getCrews() -{ - QStringList compatibleCrewList = crewDB->childKeys(); - crewDB->endGroup(); - crewDB->beginGroup("CrewList"); - QStringList crewIDs = crewDB->value("IDs", QStringList()).toStringList(); - crewIDs.append(compatibleCrewList); - crewIDs.removeDuplicates(); - crewDB->endGroup(); - crewDB->beginGroup("Crews"); - return crewIDs; -} - -QString CrewDatabase::getCrewName(int crewID) -{ - QString crewStr = crewDB->value(QString::number(crewID), crewID).toString(); - if (crewID == 0) crewStr = tr("No Crew", ""); - return crewStr; -} - -void CrewDatabase::setCrewName(int crewID, QString crewName) -{ - crewDB->setValue(QString::number(crewID), crewName); -} - -void CrewDatabase::addCrew(int crewID) -{ - QStringList crews = getCrews(); - crews.append(QString::number(crewID)); - crews.removeDuplicates(); - crewDB->endGroup(); - crewDB->beginGroup("CrewList"); - crewDB->setValue("IDs", crews); - crewDB->endGroup(); - crewDB->beginGroup("Crews"); -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "StandardPaths.h" +#include "CrewDatabase.h" +#include "config.h" +#include +#include +#include +#include +#include + +CrewDatabase::CrewDatabase(QObject *parent) : QObject(parent) +{ + QDir dir; + dir.mkpath(StandardPaths::dataLocation()); + dir.setPath(StandardPaths::dataLocation()); + QString dirPath = dir.absolutePath(); + QString defaultConfPath = dirPath % "/crews.ini"; + + QSettings confPathSettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + confPathSettings.beginGroup("Database"); + QString confPathFile = confPathSettings.value("Crews", defaultConfPath).toString(); + confPathSettings.endGroup(); + + crewDB = new QSettings(confPathFile, QSettings::IniFormat); + crewDB->beginGroup("Crews"); + + addProcess = false; +} + +CrewDatabase::~CrewDatabase() +{ + crewDB->endGroup(); + delete crewDB; +} + +QStringList CrewDatabase::getCrews() +{ + QMutexLocker locker(&mutex); +#ifdef GTA5SYNC_DEBUG + qDebug() << "getCrews"; +#endif + return getCrews_p(); +} + +QStringList CrewDatabase::getCrews_p() +{ +#ifdef GTA5SYNC_DEBUG + qDebug() << "getCrews_p"; +#endif + QStringList compatibleCrewList = getCompatibleCrews_p(); + crewDB->endGroup(); + crewDB->beginGroup("CrewList"); + QStringList crewIDs = crewDB->value("IDs", QStringList()).toStringList(); + crewIDs += compatibleCrewList; + crewIDs.removeDuplicates(); + crewDB->endGroup(); + crewDB->beginGroup("Crews"); + return crewIDs; +} + +QStringList CrewDatabase::getCompatibleCrews() +{ + QMutexLocker locker(&mutex); +#ifdef GTA5SYNC_DEBUG + qDebug() << "getCompatibleCrews"; +#endif + return getCompatibleCrews_p(); +} + +QStringList CrewDatabase::getCompatibleCrews_p() +{ +#ifdef GTA5SYNC_DEBUG + qDebug() << "getCompatibleCrews_p"; +#endif + return crewDB->childKeys(); +} + +QString CrewDatabase::getCrewName(QString crewID) +{ + QMutexLocker locker(&mutex); +#ifdef GTA5SYNC_DEBUG + qDebug() << "getCrewName" << crewID; +#endif + QString crewStr = crewDB->value(crewID, crewID).toString(); + if (crewID == "0") crewStr = tr("No Crew", ""); + return crewStr; +} + +QString CrewDatabase::getCrewName(int crewID) +{ + QMutexLocker locker(&mutex); +#ifdef GTA5SYNC_DEBUG + qDebug() << "getCrewName" << crewID; +#endif + QString crewStr = crewDB->value(QString::number(crewID), crewID).toString(); + if (crewID == 0) crewStr = tr("No Crew", ""); + return crewStr; +} + +void CrewDatabase::setCrewName(int crewID, QString crewName) +{ + QMutexLocker locker(&mutex); +#ifdef GTA5SYNC_DEBUG + qDebug() << "setCrewName" << crewID << crewName; +#endif + crewDB->setValue(QString::number(crewID), crewName); +} + +void CrewDatabase::addCrew(int crewID) +{ + QMutexLocker locker(&mutex); +#ifdef GTA5SYNC_DEBUG + qDebug() << "addCrew" << crewID; +#endif + QStringList crews = getCrews_p(); + crews += QString::number(crewID); + crews.removeDuplicates(); + crewDB->endGroup(); + crewDB->beginGroup("CrewList"); + crewDB->setValue("IDs", crews); + crewDB->endGroup(); + crewDB->beginGroup("Crews"); +} + +bool CrewDatabase::isCompatibleCrew(QString crewNID) +{ + QMutexLocker locker(&mutex); +#ifdef GTA5SYNC_DEBUG + qDebug() << "isCompatibleCrew" << crewNID; +#endif + return crewDB->contains(crewNID); +} + +bool CrewDatabase::isCompatibleCrew(int crewID) +{ + QMutexLocker locker(&mutex); +#ifdef GTA5SYNC_DEBUG + qDebug() << "isCompatibleCrew" << crewID; +#endif + return crewDB->contains(QString::number(crewID)); +} + +void CrewDatabase::setAddingCrews(bool addingCrews) +{ + QMutexLocker locker(&mutex); +#ifdef GTA5SYNC_DEBUG + qDebug() << "setAddingCrews" << addingCrews; +#endif + addProcess = addingCrews; +} + +bool CrewDatabase::isAddingCrews() +{ + QMutexLocker locker(&mutex); +#ifdef GTA5SYNC_DEBUG + qDebug() << "isAddingCrews"; +#endif + return addProcess; +} diff --git a/CrewDatabase.h b/CrewDatabase.h old mode 100755 new mode 100644 index 471b236..692ea79 --- a/CrewDatabase.h +++ b/CrewDatabase.h @@ -1,43 +1,54 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef CREWDATABASE_H -#define CREWDATABASE_H - -#include -#include -#include - -class CrewDatabase : public QObject -{ - Q_OBJECT -public: - explicit CrewDatabase(QObject *parent = 0); - void setCrewName(int crewID, QString crewName); - QString getCrewName(int crewID); - QStringList getCrews(); - ~CrewDatabase(); - -private: - QSettings *crewDB; - -public slots: - void addCrew(int crewID); -}; - -#endif // CREWDATABASE_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef CREWDATABASE_H +#define CREWDATABASE_H + +#include +#include +#include +#include + +class CrewDatabase : public QObject +{ + Q_OBJECT +public: + explicit CrewDatabase(QObject *parent = 0); + QString getCrewName(QString crewID); + QString getCrewName(int crewID); + QStringList getCompatibleCrews(); + QStringList getCrews(); + void setAddingCrews(bool addingCrews); + bool isCompatibleCrew(QString crewNID); + bool isCompatibleCrew(int crewID); + bool isAddingCrews(); + ~CrewDatabase(); + +private: + mutable QMutex mutex; + bool addProcess; + QSettings *crewDB; + QStringList getCrews_p(); + QStringList getCompatibleCrews_p(); + +public slots: + void setCrewName(int crewID, QString crewName); + void addCrew(int crewID); +}; + +#endif // CREWDATABASE_H diff --git a/DatabaseThread.cpp b/DatabaseThread.cpp old mode 100755 new mode 100644 index 0b090d0..9173eac --- a/DatabaseThread.cpp +++ b/DatabaseThread.cpp @@ -1,296 +1,223 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "DatabaseThread.h" -#include "CrewDatabase.h" -#include "AppEnv.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -DatabaseThread::DatabaseThread(CrewDatabase *crewDB, QObject *parent) : QThread(parent), crewDB(crewDB) -{ - crewMaxPages = 83; - threadRunning = true; -} - -void DatabaseThread::run() -{ - QEventLoop threadLoop; - QStringList crewList; - - // Register thread loop end signal - QObject::connect(this, SIGNAL(threadEndCommited()), &threadLoop, SLOT(quit())); - - // Quick time scan - if (crewList.length() <= 3) - { - scanCrewReference(crewList, 2500); - scanCrewMembersList(crewList, 3, 2500); - emit playerNameUpdated(); - } - else if (crewList.length() <= 5) - { - scanCrewReference(crewList, 2500); - scanCrewMembersList(crewList, 2, 2500); - emit playerNameUpdated(); - } - - QEventLoop *waitingLoop = new QEventLoop(); - QTimer::singleShot(10000, waitingLoop, SLOT(quit())); - QObject::connect(this, SIGNAL(threadEndCommited()), waitingLoop, SLOT(quit())); - waitingLoop->exec(); - delete waitingLoop; - - while (threadRunning) - { - crewList = crewDB->getCrews(); - - // Long time scan - scanCrewReference(crewList, 10000); - scanCrewMembersList(crewList, crewMaxPages, 10000); - emit playerNameUpdated(); - - if (threadRunning) - { - QTimer::singleShot(300000, &threadLoop, SLOT(quit())); - threadLoop.exec(); - } - } -} - -// void DatabaseThread::scanCrewReference(QStringList crewList, int requestDelay) -// { -// foreach (const QString &crewID, crewList) -// { -// if (threadRunning && crewID != "0") -// { -// QNetworkAccessManager *netManager = new QNetworkAccessManager(); - -// QNetworkRequest netRequest(AppEnv::getCrewFetchingUrl(crewID)); -// netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent()); -// netRequest.setRawHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); -// netRequest.setRawHeader("Accept-Language", "en-US;q=0.5,en;q=0.3"); -// netRequest.setRawHeader("Connection", "keep-alive"); - -// QNetworkReply *netReply = netManager->get(netRequest); - -// QEventLoop *downloadLoop = new QEventLoop(); -// QObject::connect(netReply, SIGNAL(finished()), downloadLoop, SLOT(quit())); -// QObject::connect(this, SIGNAL(threadEndCommited()), downloadLoop, SLOT(quit())); -// QTimer::singleShot(30000, downloadLoop, SLOT(quit())); -// downloadLoop->exec(); -// delete downloadLoop; - -// if (netReply->isFinished()) -// { -// QByteArray crewJson = netReply->readAll(); -// QJsonDocument crewDocument = QJsonDocument::fromJson(crewJson); -// QJsonObject crewObject = crewDocument.object(); -// QVariantMap crewMap = crewObject.toVariantMap(); -// QString crewName; -// bool isFound = false; - -// if (crewMap.contains("activities")) -// { -// QList activitiesList = crewMap["activities"].toList(); -// foreach (const QVariant &activitiesVariant, activitiesList) -// { -// QMap activityRootMap = activitiesVariant.toMap(); -// foreach(const QVariant &activityRootVariant, activityRootMap) -// { -// QMap activityMap = activityRootVariant.toMap(); -// foreach(const QVariant &activityVariant, activityMap) -// { -// QMap activityFinalMap = activityVariant.toMap(); -// if (activityFinalMap.contains("id") && activityFinalMap["id"] == crewID) -// { -// if (activityFinalMap.contains("name") && isFound == false) -// { -// isFound = true; -// crewName = activityFinalMap["name"].toString(); -// } -// } -// } -// } -// } -// } -// if (!crewName.isNull()) -// { -// crewDB->setCrewName(crewID.toInt(), crewName); -// } -// } - -// QEventLoop *waitingLoop = new QEventLoop(); -// QTimer::singleShot(requestDelay, waitingLoop, SLOT(quit())); -// QObject::connect(this, SIGNAL(threadEndCommited()), waitingLoop, SLOT(quit())); -// waitingLoop->exec(); -// delete waitingLoop; - -// delete netReply; -// delete netManager; -// } -// } -// } - -void DatabaseThread::scanCrewReference(QStringList crewList, int requestDelay) -{ - foreach (const QString &crewID, crewList) - { - if (threadRunning && crewID != "0") - { - QNetworkAccessManager *netManager = new QNetworkAccessManager(); - - QNetworkRequest netRequest(AppEnv::getCrewFetchingUrl(crewID)); -#if QT_VERSION >= 0x050600 - netRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); -#endif - netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent()); - netRequest.setRawHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); - netRequest.setRawHeader("Accept-Language", "en-US;q=0.5,en;q=0.3"); - netRequest.setRawHeader("Connection", "keep-alive"); - - QNetworkReply *netReply = netManager->get(netRequest); - - QEventLoop *downloadLoop = new QEventLoop(); - QObject::connect(netReply, SIGNAL(finished()), downloadLoop, SLOT(quit())); - QObject::connect(this, SIGNAL(threadEndCommited()), downloadLoop, SLOT(quit())); - QTimer::singleShot(30000, downloadLoop, SLOT(quit())); - downloadLoop->exec(); - delete downloadLoop; - - if (netReply->isFinished()) - { - QString crewName; - QByteArray crewHtml = netReply->readAll(); - QStringList crewHtmlSplit1 = QString::fromUtf8(crewHtml).split("Rockstar Games Social Club - Crew : "); - if (crewHtmlSplit1.length() >= 2) - { - QStringList crewHtmlSplit2 = QString(crewHtmlSplit1.at(1)).split(""); - if (crewHtmlSplit2.length() >= 1) - { - crewName = crewHtmlSplit2.at(0); - } - } - if (!crewName.isEmpty()) - { - crewDB->setCrewName(crewID.toInt(), crewName); - } - } - - QEventLoop *waitingLoop = new QEventLoop(); - QTimer::singleShot(requestDelay, waitingLoop, SLOT(quit())); - QObject::connect(this, SIGNAL(threadEndCommited()), waitingLoop, SLOT(quit())); - waitingLoop->exec(); - delete waitingLoop; - - delete netReply; - delete netManager; - } - } -} - -void DatabaseThread::scanCrewMembersList(QStringList crewList, int maxPages, int requestDelay) -{ - foreach (const QString &crewID, crewList) - { - if (threadRunning && crewID != "0") - { - int currentPage = 0; - int foundPlayers = 0; - int totalPlayers = 1000; - - while(foundPlayers < totalPlayers && currentPage < maxPages) - { - QNetworkAccessManager *netManager = new QNetworkAccessManager(); - - QNetworkRequest netRequest(AppEnv::getPlayerFetchingUrl(crewID, QString::number(currentPage))); -#if QT_VERSION >= 0x050600 - netRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); -#endif - netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent()); - netRequest.setRawHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); - netRequest.setRawHeader("Accept-Language", "en-US;q=0.5,en;q=0.3"); - netRequest.setRawHeader("Connection", "keep-alive"); - - QNetworkReply *netReply = netManager->get(netRequest); - - QEventLoop *downloadLoop = new QEventLoop(); - QObject::connect(netReply, SIGNAL(finished()), downloadLoop, SLOT(quit())); - QObject::connect(this, SIGNAL(threadEndCommited()), downloadLoop, SLOT(quit())); - QTimer::singleShot(30000, downloadLoop, SLOT(quit())); - downloadLoop->exec(); - delete downloadLoop; - - if (netReply->isFinished()) - { - QByteArray crewJson = netReply->readAll(); - QJsonDocument crewDocument = QJsonDocument::fromJson(crewJson); - QJsonObject crewObject = crewDocument.object(); - QVariantMap crewMap = crewObject.toVariantMap(); - - if (crewMap.contains("Total")) { totalPlayers = crewMap["Total"].toInt(); } - - if (crewMap.contains("Members")) - { - QList memberList = crewMap["Members"].toList(); - foreach (const QVariant &memberVariant, memberList) - { - QMap memberMap = memberVariant.toMap(); - foundPlayers++; - if (memberMap.contains("RockstarId") && memberMap.contains("Name")) - { - int RockstarId = memberMap["RockstarId"].toInt(); - QString memberName = memberMap["Name"].toString(); - if (memberName != "" && RockstarId != 0) - { - emit playerNameFound(RockstarId, memberName); - } - } - } - } - - QEventLoop *waitingLoop = new QEventLoop(); - QTimer::singleShot(requestDelay, waitingLoop, SLOT(quit())); - QObject::connect(this, SIGNAL(threadEndCommited()), waitingLoop, SLOT(quit())); - waitingLoop->exec(); - delete waitingLoop; - - currentPage++; - } - - delete netReply; - delete netManager; - } - } - } -} - -void DatabaseThread::doEndThread() -{ - threadRunning = false; - emit threadEndCommited(); -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "DatabaseThread.h" +#include "CrewDatabase.h" +#include "AppEnv.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define crewMaxPages 83 +#define maxLoadFails 3 + +DatabaseThread::DatabaseThread(CrewDatabase *crewDB, QObject *parent) : QThread(parent), crewDB(crewDB) +{ + continueLastCrew = true; + threadRunning = true; +} + +void DatabaseThread::run() +{ + QEventLoop threadLoop; + + QObject::connect(this, SIGNAL(threadTerminated()), &threadLoop, SLOT(quit())); + + while (threadRunning) { + QTimer::singleShot(300000, &threadLoop, SLOT(quit())); + threadLoop.exec(); + } +} + +void DatabaseThread::scanCrewReference(const QStringList &crewList, const int &requestDelay) +{ + for (const QString &crewID : crewList) { + if (threadRunning && crewID != QLatin1String("0")) { + QNetworkAccessManager *netManager = new QNetworkAccessManager(); + QNetworkRequest netRequest(AppEnv::getCrewFetchingUrl(crewID)); +#if QT_VERSION >= 0x050600 +#if QT_VERSION < 0x060000 + netRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); +#endif +#endif + netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent()); + netRequest.setRawHeader("Accept", "text/html"); + netRequest.setRawHeader("Accept-Charset", "utf-8"); + netRequest.setRawHeader("Accept-Language", "en-US,en;q=0.9"); + netRequest.setRawHeader("Connection", "keep-alive"); + + QNetworkReply *netReply = netManager->get(netRequest); + + QEventLoop *downloadLoop = new QEventLoop(); + QObject::connect(netReply, SIGNAL(finished()), downloadLoop, SLOT(quit())); + if (!continueLastCrew) + QObject::connect(this, SIGNAL(threadTerminated()), downloadLoop, SLOT(quit())); + QTimer::singleShot(30000, downloadLoop, SLOT(quit())); + downloadLoop->exec(); + downloadLoop->disconnect(); + delete downloadLoop; + + if (netReply->isFinished()) { + QString crewName; + QByteArray crewHtml = netReply->readAll(); + QStringList crewHtmlSplit1 = QString::fromUtf8(crewHtml).split("Rockstar Games Social Club - Crew : "); + if (crewHtmlSplit1.length() >= 2) { + QStringList crewHtmlSplit2 = QString(crewHtmlSplit1.at(1)).split(""); + if (crewHtmlSplit2.length() >= 1) { + crewName = crewHtmlSplit2.at(0); + } + } + if (!crewName.isEmpty()) { + emit crewNameFound(crewID.toInt(), crewName); + } + } + else { + netReply->abort(); + } + + if (threadRunning) { + QEventLoop *waitingLoop = new QEventLoop(); + QTimer::singleShot(requestDelay, waitingLoop, SLOT(quit())); + if (!continueLastCrew) + QObject::connect(this, SIGNAL(threadTerminated()), waitingLoop, SLOT(quit())); + waitingLoop->exec(); + waitingLoop->disconnect(); + delete waitingLoop; + } + + delete netReply; + delete netManager; + } + } +} + +void DatabaseThread::scanCrewMembersList(const QStringList &crewList, const int &maxPages, const int &requestDelay) +{ + for (const QString &crewID : crewList) { + if (threadRunning && crewID != QLatin1String("0")) { + int currentFail = 0; + int currentPage = 0; + int foundPlayers = 0; + int totalPlayers = 1000; + + while(foundPlayers < totalPlayers && currentPage < maxPages && (continueLastCrew ? true : threadRunning)) { + QNetworkAccessManager *netManager = new QNetworkAccessManager(); + QNetworkRequest netRequest(AppEnv::getPlayerFetchingUrl(crewID, currentPage)); +#if QT_VERSION >= 0x050600 +#if QT_VERSION < 0x060000 + netRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); +#endif +#endif + netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent()); + netRequest.setRawHeader("Accept", "application/json"); + netRequest.setRawHeader("Accept-Charset", "utf-8"); + netRequest.setRawHeader("Accept-Language", "en-US,en;q=0.9"); + netRequest.setRawHeader("Connection", "keep-alive"); + + QNetworkReply *netReply = netManager->get(netRequest); + + QEventLoop *downloadLoop = new QEventLoop(); + QObject::connect(netReply, SIGNAL(finished()), downloadLoop, SLOT(quit())); + if (!continueLastCrew) + QObject::connect(this, SIGNAL(threadTerminated()), downloadLoop, SLOT(quit())); + QTimer::singleShot(30000, downloadLoop, SLOT(quit())); + downloadLoop->exec(); + downloadLoop->disconnect(); + delete downloadLoop; + + if (netReply->isFinished()) { + QByteArray crewJson = netReply->readAll(); + QJsonDocument crewDocument = QJsonDocument::fromJson(crewJson); + QJsonObject crewObject = crewDocument.object(); + QVariantMap crewMap = crewObject.toVariantMap(); + + if (crewMap.contains("Total")) { totalPlayers = crewMap["Total"].toInt(); } + + if (crewMap.contains("Members")) { + const QList memberList = crewMap["Members"].toList(); + for (const QVariant &memberVariant : memberList) { + QMap memberMap = memberVariant.toMap(); + if (memberMap.contains("RockstarId") && memberMap.contains("Name")) { + int RockstarId = memberMap["RockstarId"].toInt(); + QString memberName = memberMap["Name"].toString(); + if (!memberName.isEmpty() && RockstarId != 0) { + foundPlayers++; + emit playerNameFound(RockstarId, memberName); + } + } + } + } + currentPage++; + } + else { + currentFail++; + if (currentFail == maxLoadFails) { + currentFail = 0; + currentPage++; + } + } + + delete netReply; + delete netManager; + + if (foundPlayers < totalPlayers && currentPage < maxPages && (continueLastCrew ? true : threadRunning)) { + QEventLoop *waitingLoop = new QEventLoop(); + QTimer::singleShot(requestDelay, waitingLoop, SLOT(quit())); + if (!continueLastCrew) { QObject::connect(this, SIGNAL(threadTerminated()), waitingLoop, SLOT(quit())); } + waitingLoop->exec(); + waitingLoop->disconnect(); + delete waitingLoop; + } + } + } + } +} + +void DatabaseThread::deleteCompatibleCrews(QStringList *crewList) +{ + for (const QString &crewNID : *crewList) { + if (crewDB->isCompatibleCrew(crewNID)) { + crewList->removeAll(crewNID); + } + } +} + +QStringList DatabaseThread::deleteCompatibleCrews(const QStringList &crewList) +{ + QStringList crewListR = crewList; + for (const QString &crewNID : crewListR) { + if (crewDB->isCompatibleCrew(crewNID)) { + crewListR.removeAll(crewNID); + } + } + return crewListR; +} + +void DatabaseThread::terminateThread() +{ + threadRunning = false; + emit threadTerminated(); +} diff --git a/DatabaseThread.h b/DatabaseThread.h old mode 100755 new mode 100644 index 0009291..37c6f76 --- a/DatabaseThread.h +++ b/DatabaseThread.h @@ -1,52 +1,56 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef DATABASETHREAD_H -#define DATABASETHREAD_H - -#include "CrewDatabase.h" -#include -#include - -class DatabaseThread : public QThread -{ - Q_OBJECT -public: - explicit DatabaseThread(CrewDatabase *crewDB, QObject *parent = 0); - -public slots: - void doEndThread(); - -private: - CrewDatabase *crewDB; - void scanCrewMembersList(QStringList crewList, int maxPages, int requestDelay); - void scanCrewReference(QStringList crewList, int requestDelay); - bool threadRunning; - int crewMaxPages; - int plyrPerReq; - -protected: - void run(); - -signals: - void playerNameFound(int playerID, QString playerName); - void playerNameUpdated(); - void threadEndCommited(); -}; - -#endif // DATABASETHREAD_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef DATABASETHREAD_H +#define DATABASETHREAD_H + +#include "CrewDatabase.h" +#include +#include + +class DatabaseThread : public QThread +{ + Q_OBJECT +public: + explicit DatabaseThread(CrewDatabase *crewDB, QObject *parent = 0); + +public slots: + void terminateThread(); + +private: + CrewDatabase *crewDB; + void scanCrewMembersList(const QStringList &crewList, const int &maxPages, const int &requestDelay); + void scanCrewReference(const QStringList &crewList, const int &requestDelay); + void deleteCompatibleCrews(QStringList *crewList); + QStringList deleteCompatibleCrews(const QStringList &crewList); + bool continueLastCrew; + bool threadRunning; + int plyrPerReq; + +protected: + void run(); + +signals: + void crewNameFound(int crewID, QString crewName); + void crewNameUpdated(); + void playerNameFound(int playerID, QString playerName); + void playerNameUpdated(); + void threadTerminated(); +}; + +#endif // DATABASETHREAD_H diff --git a/ExportDialog.cpp b/ExportDialog.cpp old mode 100755 new mode 100644 index 6210ea3..43d8d97 --- a/ExportDialog.cpp +++ b/ExportDialog.cpp @@ -1,48 +1,48 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "ExportDialog.h" -#include "ui_ExportDialog.h" - -ExportDialog::ExportDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::ExportDialog) -{ - ui->setupUi(this); - success = false; -} - -ExportDialog::~ExportDialog() -{ - delete ui; -} - -bool ExportDialog::isSucceeded() -{ - return success; -} - -void ExportDialog::on_cmdSnapmaticClose_clicked() -{ - this->close(); -} - -void ExportDialog::setupPictureExport() -{ - ui->swExport->setCurrentWidget(ui->pageSnapmatic); -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "ExportDialog.h" +#include "ui_ExportDialog.h" + +ExportDialog::ExportDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ExportDialog) +{ + ui->setupUi(this); + success = false; +} + +ExportDialog::~ExportDialog() +{ + delete ui; +} + +bool ExportDialog::isSucceeded() +{ + return success; +} + +void ExportDialog::on_cmdSnapmaticClose_clicked() +{ + this->close(); +} + +void ExportDialog::setupPictureExport() +{ + ui->swExport->setCurrentWidget(ui->pageSnapmatic); +} diff --git a/ExportDialog.h b/ExportDialog.h old mode 100755 new mode 100644 index cf5a5cd..5da4b91 --- a/ExportDialog.h +++ b/ExportDialog.h @@ -1,46 +1,46 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef EXPORTDIALOG_H -#define EXPORTDIALOG_H - -#include - -namespace Ui { -class ExportDialog; -} - -class ExportDialog : public QDialog -{ - Q_OBJECT - -public: - explicit ExportDialog(QWidget *parent = 0); - void setupPictureExport(); - bool isSucceeded(); - ~ExportDialog(); - -private slots: - void on_cmdSnapmaticClose_clicked(); - -private: - Ui::ExportDialog *ui; - bool success; -}; - -#endif // EXPORTDIALOG_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef EXPORTDIALOG_H +#define EXPORTDIALOG_H + +#include + +namespace Ui { +class ExportDialog; +} + +class ExportDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ExportDialog(QWidget *parent = 0); + void setupPictureExport(); + bool isSucceeded(); + ~ExportDialog(); + +private slots: + void on_cmdSnapmaticClose_clicked(); + +private: + Ui::ExportDialog *ui; + bool success; +}; + +#endif // EXPORTDIALOG_H diff --git a/ExportDialog.ui b/ExportDialog.ui old mode 100755 new mode 100644 index 6e8c254..d00b208 --- a/ExportDialog.ui +++ b/ExportDialog.ui @@ -1,226 +1,226 @@ - - - ExportDialog - - - - 0 - 0 - 400 - 300 - - - - Dialog - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - - - - Export Format - - - - - - &JPEG/PNG format - - - - - - - GTA &Snapmatic format - - - - - - - - - - Export Size - - - - - - Default &Size - - - - - - - &Desktop Size - - - - - - - &Custom Size - - - - - - - - - false - - - Custom Size: - - - - - - - false - - - 1 - - - 3840 - - - 960 - - - - - - - x - - - - - - - false - - - 1 - - - 2160 - - - 536 - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - &Export - - - - - - - - 0 - 0 - - - - &Close - - - - - - - - - - - - - - - + + + ExportDialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + Export Format + + + + + + &JPEG/PNG format + + + + + + + GTA &Snapmatic format + + + + + + + + + + Export Size + + + + + + Default &Size + + + + + + + &Desktop Size + + + + + + + &Custom Size + + + + + + + + + false + + + Custom Size: + + + + + + + false + + + 1 + + + 3840 + + + 960 + + + + + + + x + + + + + + + false + + + 1 + + + 2160 + + + 536 + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + &Export + + + + + + + + 0 + 0 + + + + &Close + + + + + + + + + + + + + + + diff --git a/ExportThread.cpp b/ExportThread.cpp old mode 100755 new mode 100644 index 5c39bc0..63aef4f --- a/ExportThread.cpp +++ b/ExportThread.cpp @@ -1,184 +1,199 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "SnapmaticPicture.h" -#include "ProfileInterface.h" -#include "PictureExport.h" -#include "ProfileWidget.h" -#include "ExportThread.h" -#include "SavegameData.h" -#include "config.h" -#include -#include -#include -#include -#include - -ExportThread::ExportThread(QMap profileMap, QString exportDirectory, bool pictureCopyEnabled, bool pictureExportEnabled, int exportCount, QObject *parent) : QThread(parent), - profileMap(profileMap), exportDirectory(exportDirectory), pictureCopyEnabled(pictureCopyEnabled), pictureExportEnabled(pictureExportEnabled), exportCount(exportCount) -{ - -} - -void ExportThread::run() -{ - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - - // Picture Settings - // Quality Settings - settings.beginGroup("Pictures"); - int defaultQuality = 100; - QSize defExportSize = QSize(960, 536); - int customQuality = settings.value("CustomQuality", defaultQuality).toInt(); - if (customQuality < 1 || customQuality > 100) - { - customQuality = 100; - } - bool useCustomQuality = settings.value("CustomQualityEnabled", false).toBool(); - - // Size Settings - QSize cusExportSize = settings.value("CustomSize", defExportSize).toSize(); - if (cusExportSize.width() > 3840) - { - cusExportSize.setWidth(3840); - } - else if (cusExportSize.height() > 2160) - { - cusExportSize.setHeight(2160); - } - if (cusExportSize.width() < 1) - { - cusExportSize.setWidth(1); - } - else if (cusExportSize.height() < 1) - { - cusExportSize.setHeight(1); - } - QString sizeMode = settings.value("ExportSizeMode", "Default").toString(); - Qt::AspectRatioMode aspectRatio = (Qt::AspectRatioMode)settings.value("AspectRatio", Qt::KeepAspectRatio).toInt(); - settings.endGroup(); - // End Picture Settings - - int intExportProgress = 0; - foreach(ProfileWidget *widget, profileMap.keys()) - { - if (widget->isSelected()) - { - if (widget->getWidgetType() == "SnapmaticWidget") - { - SnapmaticWidget *picWidget = (SnapmaticWidget*)widget; - SnapmaticPicture *picture = picWidget->getPicture(); - - if (pictureExportEnabled) - { - QString exportFileName = PictureExport::getPictureFileName(picture); - if (exportFileName.right(4) != ".jpg" && exportFileName.right(4) != ".png") - { - exportFileName.append(".jpg"); - } - - intExportProgress++; - emit exportStringUpdate(ProfileInterface::tr("Export file %1 of %2 files").arg(QString::number(intExportProgress), QString::number(exportCount))); - emit exportProgressUpdate(intExportProgress); - - // Scale Picture - QImage exportPicture = picture->getImage(); - if (sizeMode == "Desktop") - { - QRect desktopResolution = QApplication::desktop()->screenGeometry(); - exportPicture = exportPicture.scaled(desktopResolution.width(), desktopResolution.height(), aspectRatio, Qt::SmoothTransformation); - } - else if (sizeMode == "Custom") - { - exportPicture = exportPicture.scaled(cusExportSize, aspectRatio, Qt::SmoothTransformation); - } - - bool isSaved; - if (useCustomQuality) - { - isSaved = exportPicture.save(exportDirectory + "/" + exportFileName, "JPEG", customQuality); - } - else - { - isSaved = exportPicture.save(exportDirectory + "/" + exportFileName, "JPEG", 100); - } - - if (!isSaved) - { - failedExportPictures.append(exportFileName); - } - } - if (pictureCopyEnabled) - { - QString exportFileName = PictureExport::getPictureFileName(picture); - if (exportFileName.right(4) != ".g5e") - { - exportFileName.append(".g5e"); - } - - intExportProgress++; - emit exportStringUpdate(ProfileInterface::tr("Export file %1 of %2 files").arg(QString::number(intExportProgress), QString::number(exportCount))); - emit exportProgressUpdate(intExportProgress); - - QString exportFilePath = exportDirectory + "/" + exportFileName; - if (QFile::exists(exportFilePath)) {QFile::remove(exportFilePath);} - if (!picture->exportPicture(exportDirectory + "/" + exportFileName, "G5E")) - { - failedCopyPictures.append(exportFileName); - } - } - } - else if (widget->getWidgetType() == "SavegameWidget") - { - SavegameWidget *sgdWidget = (SavegameWidget*)widget; - SavegameData *savegame = sgdWidget->getSavegame(); - - QString originalFileName = savegame->getSavegameFileName(); - QFileInfo originalFileInfo(originalFileName); - QString exportFileName = originalFileInfo.fileName(); - - intExportProgress++; - emit exportStringUpdate(ProfileInterface::tr("Export file %1 of %2 files").arg(QString::number(intExportProgress), QString::number(exportCount))); - emit exportProgressUpdate(intExportProgress); - - QString exportFilePath = exportDirectory + "/" + exportFileName; - if (QFile::exists(exportFilePath)) {QFile::remove(exportFilePath);} - if (!QFile::copy(originalFileName, exportFilePath)) - { - failedSavegames.append(exportFileName); - } - } - } - } - emit exportFinished(); -} - -QStringList ExportThread::getFailedCopyPictures() -{ - return failedCopyPictures; -} - -QStringList ExportThread::getFailedExportPictures() -{ - return failedExportPictures; -} - -QStringList ExportThread::getFailedSavegames() -{ - return failedSavegames; -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2020 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "SnapmaticPicture.h" +#include "ProfileInterface.h" +#include "PictureExport.h" +#include "ProfileWidget.h" +#include "ExportThread.h" +#include "SavegameData.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include + +#if QT_VERSION >= 0x050000 +#include +#else +#include +#endif + +ExportThread::ExportThread(QMap profileMap, QString exportDirectory, bool pictureCopyEnabled, bool pictureExportEnabled, int exportCount, QObject *parent) : QThread(parent), + profileMap(profileMap), exportDirectory(exportDirectory), pictureCopyEnabled(pictureCopyEnabled), pictureExportEnabled(pictureExportEnabled), exportCount(exportCount) +{ + +} + +void ExportThread::run() +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + + // Picture Settings + // Quality Settings + settings.beginGroup("Pictures"); + int defaultQuality = 100; + QSize defExportSize = SnapmaticPicture::getSnapmaticResolution(); + int customQuality = settings.value("CustomQuality", defaultQuality).toInt(); + if (customQuality < 1 || customQuality > 100) + { + customQuality = 100; + } + bool useCustomQuality = settings.value("CustomQualityEnabled", false).toBool(); + + // Size Settings + QSize cusExportSize = settings.value("CustomSize", defExportSize).toSize(); + if (cusExportSize.width() > 3840) + { + cusExportSize.setWidth(3840); + } + else if (cusExportSize.height() > 2160) + { + cusExportSize.setHeight(2160); + } + if (cusExportSize.width() < 1) + { + cusExportSize.setWidth(1); + } + else if (cusExportSize.height() < 1) + { + cusExportSize.setHeight(1); + } + QString sizeMode = settings.value("ExportSizeMode", "Default").toString(); + Qt::AspectRatioMode aspectRatio = (Qt::AspectRatioMode)settings.value("AspectRatio", Qt::KeepAspectRatio).toInt(); + settings.endGroup(); + // End Picture Settings + + int intExportProgress = 0; + for (ProfileWidget *widget : profileMap.keys()) + { + if (widget->isSelected()) + { + if (widget->getWidgetType() == "SnapmaticWidget") + { + SnapmaticWidget *picWidget = qobject_cast(widget); + SnapmaticPicture *picture = picWidget->getPicture(); + + if (pictureExportEnabled) + { + QString exportFileName = PictureExport::getPictureFileName(picture); + if (exportFileName.right(4) != ".jpg" && exportFileName.right(4) != ".png") + { + exportFileName += ".jpg"; + } + + intExportProgress++; + emit exportStringUpdate(ProfileInterface::tr("Export file %1 of %2 files").arg(QString::number(intExportProgress), QString::number(exportCount))); + emit exportProgressUpdate(intExportProgress); + + // Scale Picture + QImage exportPicture = picture->getImage(); + if (sizeMode == "Desktop") + { +#if QT_VERSION >= 0x050000 + qreal screenRatioPR = AppEnv::screenRatioPR(); + QRect desktopResolution = QApplication::primaryScreen()->geometry(); + int desktopSizeWidth = qRound((double)desktopResolution.width() * screenRatioPR); + int desktopSizeHeight = qRound((double)desktopResolution.height() * screenRatioPR); +#else + QRect desktopResolution = QApplication::desktop()->screenGeometry(); + int desktopSizeWidth = desktopResolution.width(); + int desktopSizeHeight = desktopResolution.height(); +#endif + exportPicture = exportPicture.scaled(desktopSizeWidth, desktopSizeHeight, aspectRatio, Qt::SmoothTransformation); + } + else if (sizeMode == "Custom") + { + exportPicture = exportPicture.scaled(cusExportSize, aspectRatio, Qt::SmoothTransformation); + } + + bool isSaved; + if (useCustomQuality) + { + isSaved = exportPicture.save(exportDirectory % "/" % exportFileName, "JPEG", customQuality); + } + else + { + isSaved = exportPicture.save(exportDirectory % "/" % exportFileName, "JPEG", 100); + } + + if (!isSaved) + { + failedExportPictures += exportFileName; + } + } + if (pictureCopyEnabled) + { + QString exportFileName = PictureExport::getPictureFileName(picture); + if (exportFileName.right(4) != ".g5e") + { + exportFileName += ".g5e"; + } + + intExportProgress++; + emit exportStringUpdate(ProfileInterface::tr("Export file %1 of %2 files").arg(QString::number(intExportProgress), QString::number(exportCount))); + emit exportProgressUpdate(intExportProgress); + + QString exportFilePath = exportDirectory % "/" % exportFileName; + if (QFile::exists(exportFilePath)) {QFile::remove(exportFilePath);} + if (!picture->exportPicture(exportDirectory % "/" % exportFileName, SnapmaticFormat::G5E_Format)) + { + failedCopyPictures += exportFileName; + } + } + } + else if (widget->getWidgetType() == "SavegameWidget") + { + SavegameWidget *sgdWidget = qobject_cast(widget); + SavegameData *savegame = sgdWidget->getSavegame(); + + QString originalFileName = savegame->getSavegameFileName(); + QFileInfo originalFileInfo(originalFileName); + QString exportFileName = originalFileInfo.fileName(); + + intExportProgress++; + emit exportStringUpdate(ProfileInterface::tr("Export file %1 of %2 files").arg(QString::number(intExportProgress), QString::number(exportCount))); + emit exportProgressUpdate(intExportProgress); + + QString exportFilePath = exportDirectory % "/" % exportFileName; + if (QFile::exists(exportFilePath)) {QFile::remove(exportFilePath);} + if (!QFile::copy(originalFileName, exportFilePath)) + { + failedSavegames += exportFileName; + } + } + } + } + emit exportFinished(); +} + +QStringList ExportThread::getFailedCopyPictures() +{ + return failedCopyPictures; +} + +QStringList ExportThread::getFailedExportPictures() +{ + return failedExportPictures; +} + +QStringList ExportThread::getFailedSavegames() +{ + return failedSavegames; +} diff --git a/ExportThread.h b/ExportThread.h old mode 100755 new mode 100644 index ecd0cd7..99ad28b --- a/ExportThread.h +++ b/ExportThread.h @@ -1,56 +1,56 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef EXPORTTHREAD_H -#define EXPORTTHREAD_H - -#include "SnapmaticWidget.h" -#include "SavegameWidget.h" -#include "ProfileWidget.h" -#include -#include - -class ExportThread : public QThread -{ - Q_OBJECT -public: - explicit ExportThread(QMap profileMap, QString exportDirectory, bool pictureCopyEnabled, bool pictureExportEnabled, int exportCount, QObject *parent = 0); - QStringList getFailedSavegames(); - QStringList getFailedCopyPictures(); - QStringList getFailedExportPictures(); - -protected: - void run(); - -private: - QMap profileMap; - QString exportDirectory; - bool pictureCopyEnabled; - bool pictureExportEnabled; - int exportCount; - QStringList failedSavegames; - QStringList failedCopyPictures; - QStringList failedExportPictures; - -signals: - void exportStringUpdate(QString currentFileName); - void exportProgressUpdate(int currentProgressValue); - void exportFinished(); -}; - -#endif // EXPORTTHREAD_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef EXPORTTHREAD_H +#define EXPORTTHREAD_H + +#include "SnapmaticWidget.h" +#include "SavegameWidget.h" +#include "ProfileWidget.h" +#include +#include + +class ExportThread : public QThread +{ + Q_OBJECT +public: + explicit ExportThread(QMap profileMap, QString exportDirectory, bool pictureCopyEnabled, bool pictureExportEnabled, int exportCount, QObject *parent = 0); + QStringList getFailedSavegames(); + QStringList getFailedCopyPictures(); + QStringList getFailedExportPictures(); + +protected: + void run(); + +private: + QMap profileMap; + QString exportDirectory; + bool pictureCopyEnabled; + bool pictureExportEnabled; + int exportCount; + QStringList failedSavegames; + QStringList failedCopyPictures; + QStringList failedExportPictures; + +signals: + void exportStringUpdate(QString currentFileName); + void exportProgressUpdate(int currentProgressValue); + void exportFinished(); +}; + +#endif // EXPORTTHREAD_H diff --git a/GlobalString.cpp b/GlobalString.cpp old mode 100755 new mode 100644 index 00289c3..0a04a59 --- a/GlobalString.cpp +++ b/GlobalString.cpp @@ -1,89 +1,84 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include -#include -#include -#include -#include "GlobalString.h" -#include "config.h" - -GlobalString::GlobalString() -{ - -} - -QMap GlobalString::getGlobalMap() -{ - QMap globalMap; - QSettings globalFile(getLanguageFile(), QSettings::IniFormat); - globalFile.setIniCodec("UTF-8"); - globalFile.beginGroup("Global"); - QStringList globalStrList = globalFile.childKeys(); - foreach(const QString &globalStr, globalStrList) - { - globalMap[globalStr] = globalFile.value(globalStr, globalStr).toString(); - } - globalFile.endGroup(); - return globalMap; -} - -QString GlobalString::getString(QString valueStr, bool *ok) -{ - QString globalString = valueStr; - QSettings globalFile(getLanguageFile(), QSettings::IniFormat); - globalFile.setIniCodec("UTF-8"); - globalFile.beginGroup("Global"); - QStringList globalStrList = globalFile.childKeys(); - if (globalStrList.contains(valueStr)) - { - if (ok != 0) *ok = true; - globalString = globalFile.value(valueStr, valueStr).toString(); - } - globalFile.endGroup(); - return globalString; -} - -QString GlobalString::getLanguageFile() -{ - QString language = getLanguage(); - QString languageFile = ":/global/global." + language + ".ini"; - if (!QFileInfo(languageFile).exists()) - { - languageFile = ":/global/global.en.ini"; - } - return languageFile; -} - -QString GlobalString::getLanguage() -{ - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - settings.beginGroup("Interface"); - QString language = settings.value("Language","System").toString(); - settings.endGroup(); - if (language == "System" || language.trimmed() == "") - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - language = langList.at(0); - } - } - return language; -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "TranslationClass.h" +#include "GlobalString.h" +#include "config.h" +#include +#include +#include +#include +#include +#include + +GlobalString::GlobalString() +{ +} + +QMap GlobalString::getGlobalMap() +{ + QMap globalMap; + QSettings globalFile(getLanguageFile(), QSettings::IniFormat); +#if QT_VERSION < 0x060000 + globalFile.setIniCodec("UTF-8"); +#endif + globalFile.beginGroup("Global"); + for (const QString &globalStr : globalFile.childKeys()) { + globalMap[globalStr] = globalFile.value(globalStr, globalStr).toString(); + } + globalFile.endGroup(); + return globalMap; +} + +QString GlobalString::getString(QString valueStr, bool *ok) +{ + QString globalString = valueStr; + QSettings globalFile(getLanguageFile(), QSettings::IniFormat); +#if QT_VERSION < 0x060000 + globalFile.setIniCodec("UTF-8"); +#endif + globalFile.beginGroup("Global"); + QStringList globalStrList = globalFile.childKeys(); + if (globalStrList.contains(valueStr)) { + if (ok != nullptr) + *ok = true; + globalString = globalFile.value(valueStr, valueStr).toString(); + } + globalFile.endGroup(); + return globalString; +} + +QString GlobalString::getLanguageFile() +{ + QString language = getLanguage(); + QString languageFile = ":/global/global." % language % ".ini"; +#if QT_VERSION >= 0x050200 + if (!QFileInfo::exists(languageFile)) + languageFile = ":/global/global.en.ini"; +#else + if (!QFileInfo(languageFile).exists()) + languageFile = ":/global/global.en.ini"; +#endif + + return languageFile; +} + +QString GlobalString::getLanguage() +{ + return Translator->getCurrentAreaLanguage(); +} diff --git a/GlobalString.h b/GlobalString.h old mode 100755 new mode 100644 index c9b5bde..711afa9 --- a/GlobalString.h +++ b/GlobalString.h @@ -1,35 +1,35 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef GLOBALSTRING_H -#define GLOBALSTRING_H - -#include -#include - -class GlobalString -{ -public: - GlobalString(); - static QString getString(QString valueStr, bool *ok = 0); - static QString getLanguageFile(); - static QString getLanguage(); - static QMap getGlobalMap(); -}; - -#endif // GLOBALSTRING_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef GLOBALSTRING_H +#define GLOBALSTRING_H + +#include +#include + +class GlobalString +{ +public: + GlobalString(); + static QString getString(QString valueStr, bool *ok = nullptr); + static QString getLanguageFile(); + static QString getLanguage(); + static QMap getGlobalMap(); +}; + +#endif // GLOBALSTRING_H diff --git a/IconLoader.cpp b/IconLoader.cpp old mode 100755 new mode 100644 index 13ef339..71ccee1 --- a/IconLoader.cpp +++ b/IconLoader.cpp @@ -1,40 +1,61 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "IconLoader.h" -#include - -IconLoader::IconLoader() -{ - -} - -QIcon IconLoader::loadingAppIcon() -{ - QIcon appIcon; - appIcon.addFile(":/img/5sync-16.png", QSize(16, 16)); - appIcon.addFile(":/img/5sync-24.png", QSize(24, 24)); - appIcon.addFile(":/img/5sync-32.png", QSize(32, 32)); - appIcon.addFile(":/img/5sync-40.png", QSize(40, 40)); - appIcon.addFile(":/img/5sync-48.png", QSize(48, 48)); - appIcon.addFile(":/img/5sync-64.png", QSize(64, 64)); - appIcon.addFile(":/img/5sync-96.png", QSize(96, 96)); - appIcon.addFile(":/img/5sync-128.png", QSize(128, 128)); - appIcon.addFile(":/img/5sync-256.png", QSize(256, 256)); - return appIcon; -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "IconLoader.h" +#include "AppEnv.h" +#include +#include + +IconLoader::IconLoader() +{ +} + +QIcon IconLoader::loadingAppIcon() +{ + QIcon appIcon; +#if defined(GTA5SYNC_QCONF) && defined(GTA5SYNC_CMAKE) +#ifdef Q_OS_WIN + const QString pattern = AppEnv::getImagesFolder() % QLatin1String("/gta5view-%1.png"); +#else + const QString pattern = AppEnv::getShareFolder() % QLatin1String("/icons/hicolor/%1x%1/apps/de.syping.gta5view.png"); +#endif +#else + const QString pattern = AppEnv::getImagesFolder() % QLatin1String("/gta5view-%1.png"); +#endif + appIcon.addFile(pattern.arg("16"), QSize(16, 16)); + appIcon.addFile(pattern.arg("24"), QSize(24, 24)); + appIcon.addFile(pattern.arg("32"), QSize(32, 32)); + appIcon.addFile(pattern.arg("40"), QSize(40, 40)); + appIcon.addFile(pattern.arg("48"), QSize(48, 48)); + appIcon.addFile(pattern.arg("64"), QSize(64, 64)); + appIcon.addFile(pattern.arg("96"), QSize(96, 96)); + appIcon.addFile(pattern.arg("128"), QSize(128, 128)); + appIcon.addFile(pattern.arg("256"), QSize(256, 256)); + return appIcon; +} + +QIcon IconLoader::loadingPointmakerIcon() +{ + QIcon pointmakerIcon; + const QString pattern = AppEnv::getImagesFolder() % QLatin1String("/pointmaker-%1.png"); + pointmakerIcon.addFile(pattern.arg("8"), QSize(8, 8)); + pointmakerIcon.addFile(pattern.arg("16"), QSize(16, 16)); + pointmakerIcon.addFile(pattern.arg("24"), QSize(24, 24)); + pointmakerIcon.addFile(pattern.arg("32"), QSize(32, 32)); + return pointmakerIcon; +} diff --git a/IconLoader.h b/IconLoader.h old mode 100755 new mode 100644 index c7d5107..8456688 --- a/IconLoader.h +++ b/IconLoader.h @@ -1,31 +1,32 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef ICONLOADER_H -#define ICONLOADER_H - -#include - -class IconLoader -{ -public: - IconLoader(); - static QIcon loadingAppIcon(); -}; - -#endif // ICONLOADER_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef ICONLOADER_H +#define ICONLOADER_H + +#include + +class IconLoader +{ +public: + IconLoader(); + static QIcon loadingAppIcon(); + static QIcon loadingPointmakerIcon(); +}; + +#endif // ICONLOADER_H diff --git a/ImportDialog.cpp b/ImportDialog.cpp index 19de296..594cbf8 100644 --- a/ImportDialog.cpp +++ b/ImportDialog.cpp @@ -1,190 +1,974 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "ImportDialog.h" -#include "ui_ImportDialog.h" -#include "AppEnv.h" -#include -#include -#include -#include - -// IMAGES VALUES -#define snapmaticResolutionW 960 -#define snapmaticResolutionH 536 -#define snapmaticAvatarResolution 470 -#define snapmaticAvatarPlacementW 145 -#define snapmaticAvatarPlacementH 66 - -ImportDialog::ImportDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::ImportDialog) -{ - ui->setupUi(this); - doImport = false; - avatarAreaImage = QImage(":/img/avatarareaimport.png"); - - if (QIcon::hasThemeIcon("dialog-ok")) - { - ui->cmdOK->setIcon(QIcon::fromTheme("dialog-ok")); - } - if (QIcon::hasThemeIcon("dialog-cancel")) - { - ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel")); - } - - ui->rbKeep->setChecked(true); - - qreal screenRatio = AppEnv::screenRatio(); - snapmaticResolutionLW = 430 * screenRatio; - snapmaticResolutionLH = 240 * screenRatio; - setMinimumSize(430 * screenRatio, 380 * screenRatio); - setMaximumSize(430 * screenRatio, 380 * screenRatio); - setFixedSize(430 * screenRatio, 380 * screenRatio); - ui->vlButtom->setSpacing(6 * screenRatio); - ui->vlButtom->setContentsMargins(9 * screenRatio, 6 * screenRatio, 9 * screenRatio, 9 * screenRatio); -} - -ImportDialog::~ImportDialog() -{ - delete ui; -} - -void ImportDialog::processImage() -{ - QImage snapmaticImage = workImage; - QPixmap snapmaticPixmap(snapmaticResolutionW, snapmaticResolutionH); - snapmaticPixmap.fill(Qt::black); - QPainter snapmaticPainter(&snapmaticPixmap); - if (ui->cbAvatar->isChecked()) - { - // Avatar mode - int diffWidth = 0; - int diffHeight = 0; - if (ui->rbKeep->isChecked()) - { - snapmaticImage = snapmaticImage.scaled(snapmaticAvatarResolution, snapmaticAvatarResolution, Qt::KeepAspectRatio, Qt::SmoothTransformation); - if (snapmaticImage.width() > snapmaticImage.height()) - { - diffHeight = snapmaticAvatarResolution - snapmaticImage.height(); - diffHeight = diffHeight / 2; - } - else if (snapmaticImage.width() < snapmaticImage.height()) - { - diffWidth = snapmaticAvatarResolution - snapmaticImage.width(); - diffWidth = diffWidth / 2; - } - } - else - { - snapmaticImage = snapmaticImage.scaled(snapmaticAvatarResolution, snapmaticAvatarResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - } - snapmaticPainter.drawImage(snapmaticAvatarPlacementW + diffWidth, snapmaticAvatarPlacementH + diffHeight, snapmaticImage); - imageTitle = "Custom Avatar"; - } - else - { - // Picture mode - int diffWidth = 0; - int diffHeight = 0; - if (ui->rbKeep->isChecked()) - { - snapmaticImage = snapmaticImage.scaled(snapmaticResolutionW, snapmaticResolutionH, Qt::KeepAspectRatio, Qt::SmoothTransformation); - if (snapmaticImage.width() != snapmaticResolutionW) - { - diffWidth = snapmaticResolutionW - snapmaticImage.width(); - diffWidth = diffWidth / 2; - } - else if (snapmaticImage.height() != snapmaticResolutionH) - { - diffHeight = snapmaticResolutionH - snapmaticImage.height(); - diffHeight = diffHeight / 2; - } - } - else - { - snapmaticImage = snapmaticImage.scaled(snapmaticResolutionW, snapmaticResolutionH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - } - snapmaticPainter.drawImage(0 + diffWidth, 0 + diffHeight, snapmaticImage); - imageTitle = "Custom Picture"; - } - snapmaticPainter.end(); - newImage = snapmaticPixmap.toImage(); - ui->labPicture->setPixmap(snapmaticPixmap.scaled(snapmaticResolutionLW, snapmaticResolutionLH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); -} - -QImage ImportDialog::image() -{ - return newImage; -} - -void ImportDialog::setImage(const QImage &image_) -{ - workImage = image_; - if (workImage.width() == workImage.height()) - { - ui->cbAvatar->setChecked(true); - } - processImage(); -} - -bool ImportDialog::isDoImport() -{ - return doImport; -} - -QString ImportDialog::getImageTitle() -{ - return imageTitle; -} - -void ImportDialog::on_rbIgnore_clicked() -{ - processImage(); -} - -void ImportDialog::on_rbKeep_clicked() -{ - processImage(); -} - -void ImportDialog::on_cbAvatar_clicked() -{ - processImage(); -} - -void ImportDialog::on_cmdCancel_clicked() -{ - close(); -} - -void ImportDialog::on_cmdOK_clicked() -{ - doImport = true; - close(); -} - -void ImportDialog::on_labPicture_labelPainted() -{ - if (ui->cbAvatar->isChecked()) - { - QPainter labelPainter(ui->labPicture); - labelPainter.drawImage(0, 0, avatarAreaImage.scaled(snapmaticResolutionLW, snapmaticResolutionLH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - labelPainter.end(); - } -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2017-2022 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "ui_ImportDialog.h" +#include "SnapmaticPicture.h" +#include "SidebarGenerator.h" +#include "StandardPaths.h" +#include "ImportDialog.h" +#include "imagecropper.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// IMAGES VALUES +#define snapmaticAvatarResolution 470 +#define snapmaticAvatarPlacementW 145 +#define snapmaticAvatarPlacementH 66 + +ImportDialog::ImportDialog(QString profileName, QWidget *parent) : + QDialog(parent), profileName(profileName), + ui(new Ui::ImportDialog) +{ + // Set Window Flags +#if QT_VERSION >= 0x050900 + setWindowFlag(Qt::WindowContextHelpButtonHint, false); +#else + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); +#endif + + ui->setupUi(this); + ui->cmdOK->setDefault(true); + ui->cmdOK->setFocus(); + importAgreed = false; + settingsLocked = false; + watermarkAvatar = true; + watermarkPicture = false; + insideAvatarZone = false; + avatarAreaImage = QImage(AppEnv::getImagesFolder() % "/avatarareaimport.png"); + selectedColour = QColor::fromRgb(0, 0, 0, 255); + + // Set Icon for OK Button + if (QIcon::hasThemeIcon("dialog-ok")) { + ui->cmdOK->setIcon(QIcon::fromTheme("dialog-ok")); + } + else if (QIcon::hasThemeIcon("gtk-ok")) { + ui->cmdOK->setIcon(QIcon::fromTheme("gtk-ok")); + } + + // Set Icon for Cancel Button + if (QIcon::hasThemeIcon("dialog-cancel")) { + ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel")); + } + else if (QIcon::hasThemeIcon("gtk-cancel")) { + ui->cmdCancel->setIcon(QIcon::fromTheme("gtk-cancel")); + } + + ui->cbIgnore->setChecked(false); + ui->labColour->setText(tr("Background Colour: %1").arg(selectedColour.name())); + ui->labBackgroundImage->setText(tr("Background Image:")); + ui->cmdBackgroundWipe->setVisible(false); + + // Snapmatic Resolution + snapmaticResolution = SnapmaticPicture::getSnapmaticResolution(); + ui->cbResolution->addItem("GTA V", snapmaticResolution); + ui->cbResolution->addItem("FiveM", QSize(1920, 1072)); + ui->cbResolution->addItem("1280x720", QSize(1280, 720)); + ui->cbResolution->addItem("1920x1080", QSize(1920, 1080)); + ui->cbResolution->addItem("2560x1440", QSize(2560, 1440)); + + // Set Import Settings + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("Import"); + QString currentProfile = settings.value("Profile", "Default").toString(); + settings.endGroup(); + processSettings(currentProfile); + + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + snapmaticResolutionLW = 516 * screenRatio; // 430 + snapmaticResolutionLH = 288 * screenRatio; // 240 + ui->labPicture->setMinimumSize(snapmaticResolutionLW, snapmaticResolutionLH); + + ui->vlButtom->setSpacing(6 * screenRatio); +#ifndef Q_OS_MAC + ui->vlButtom->setContentsMargins(9 * screenRatio, 6 * screenRatio, 9 * screenRatio, 9 * screenRatio); +#else +#if QT_VERSION >= 0x060000 + if (QApplication::style()->objectName() == "macos") { +#else + if (QApplication::style()->objectName() == "macintosh") { +#endif + ui->vlButtom->setContentsMargins(9 * screenRatio, 9 * screenRatio, 9 * screenRatio, 9 * screenRatio); + } + else { + ui->vlButtom->setContentsMargins(9 * screenRatio, 6 * screenRatio, 9 * screenRatio, 9 * screenRatio); + } +#endif + + // Options menu + optionsMenu.addAction(tr("&Import new Picture..."), this, SLOT(importNewPicture())); + optionsMenu.addAction(tr("&Crop Picture..."), this, SLOT(cropPicture())); + optionsMenu.addSeparator(); + optionsMenu.addAction(tr("&Load Settings..."), this, SLOT(loadImportSettings())); + optionsMenu.addAction(tr("&Save Settings..."), this, SLOT(saveImportSettings())); + ui->cmdOptions->setMenu(&optionsMenu); + + const QSize windowSize = sizeHint(); + setMinimumSize(windowSize); + setMaximumSize(windowSize); +} + +ImportDialog::~ImportDialog() +{ + delete ui; +} + +void ImportDialog::processImage() +{ + if (workImage.isNull()) + return; + + QImage snapmaticImage = workImage; + QPixmap snapmaticPixmap(snapmaticResolution); + snapmaticPixmap.fill(selectedColour); + QPainter snapmaticPainter(&snapmaticPixmap); + qreal screenRatioPR = AppEnv::screenRatioPR(); + if (!backImage.isNull()) { + if (!ui->cbStretch->isChecked()) { + int diffWidth = 0; + int diffHeight = 0; + if (backImage.width() != snapmaticResolution.width()) { + diffWidth = snapmaticResolution.width() - backImage.width(); + diffWidth = diffWidth / 2; + } + else if (backImage.height() != snapmaticResolution.height()) { + diffHeight = snapmaticResolution.height() - backImage.height(); + diffHeight = diffHeight / 2; + } + snapmaticPainter.drawImage(0 + diffWidth, 0 + diffHeight, backImage); + } + else { + snapmaticPainter.drawImage(0, 0, QImage(backImage).scaled(snapmaticResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + } + if (ui->cbAvatar->isChecked() && ui->cbForceAvatarColour->isChecked()) { + snapmaticPainter.fillRect(snapmaticAvatarPlacementW, snapmaticAvatarPlacementH, snapmaticAvatarResolution, snapmaticAvatarResolution, selectedColour); + } + } + if (insideAvatarZone) { + // Avatar mode + int diffWidth = 0; + int diffHeight = 0; + if (ui->cbIgnore->isChecked()) { + snapmaticImage = snapmaticImage.scaled(snapmaticAvatarResolution, snapmaticAvatarResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + else if (ui->cbBorderless->isChecked()) { + snapmaticImage = snapmaticImage.scaled(snapmaticAvatarResolution, snapmaticAvatarResolution, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + if (snapmaticImage.width() > snapmaticAvatarResolution) { + int diffWidth = snapmaticImage.width() - snapmaticAvatarResolution; + diffWidth = diffWidth / 2; + QImage croppedImage(snapmaticAvatarResolution, snapmaticAvatarResolution, QImage::Format_ARGB32); + croppedImage.fill(Qt::transparent); + QPainter croppedPainter(&croppedImage); + croppedPainter.drawImage(0 - diffWidth, 0, snapmaticImage); + croppedPainter.end(); + snapmaticImage = croppedImage; + } + else if (snapmaticImage.height() > snapmaticAvatarResolution) { + int diffHeight = snapmaticImage.height() - snapmaticAvatarResolution; + diffHeight = diffHeight / 2; + QImage croppedImage(snapmaticAvatarResolution, snapmaticAvatarResolution, QImage::Format_ARGB32); + croppedImage.fill(Qt::transparent); + QPainter croppedPainter(&croppedImage); + croppedPainter.drawImage(0, 0 - diffHeight, snapmaticImage); + croppedPainter.end(); + snapmaticImage = croppedImage; + } + } + else { + snapmaticImage = snapmaticImage.scaled(snapmaticAvatarResolution, snapmaticAvatarResolution, Qt::KeepAspectRatio, Qt::SmoothTransformation); + if (snapmaticImage.width() > snapmaticImage.height()) { + diffHeight = snapmaticAvatarResolution - snapmaticImage.height(); + diffHeight = diffHeight / 2; + } + else if (snapmaticImage.width() < snapmaticImage.height()) { + diffWidth = snapmaticAvatarResolution - snapmaticImage.width(); + diffWidth = diffWidth / 2; + } + } + snapmaticPainter.drawImage(snapmaticAvatarPlacementW + diffWidth, snapmaticAvatarPlacementH + diffHeight, snapmaticImage); + if (ui->cbWatermark->isChecked()) + processWatermark(&snapmaticPainter); + imageTitle = tr("Custom Avatar", "Custom Avatar Description in SC, don't use Special Character!"); + } + else { + // Picture mode + int diffWidth = 0; + int diffHeight = 0; + if (ui->cbIgnore->isChecked()) { + snapmaticImage = snapmaticImage.scaled(snapmaticResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + else if (ui->cbBorderless->isChecked()) { + snapmaticImage = snapmaticImage.scaled(snapmaticResolution, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + if (snapmaticImage.width() > snapmaticResolution.width()) { + int diffWidth = snapmaticImage.width() - snapmaticResolution.width(); + diffWidth = diffWidth / 2; + QImage croppedImage(snapmaticResolution, QImage::Format_ARGB32); + croppedImage.fill(Qt::transparent); + QPainter croppedPainter(&croppedImage); + croppedPainter.drawImage(0 - diffWidth, 0, snapmaticImage); + croppedPainter.end(); + snapmaticImage = croppedImage; + } + else if (snapmaticImage.height() > snapmaticResolution.height()) { + int diffHeight = snapmaticImage.height() - snapmaticResolution.height(); + diffHeight = diffHeight / 2; + QImage croppedImage(snapmaticResolution, QImage::Format_ARGB32); + croppedImage.fill(Qt::transparent); + QPainter croppedPainter(&croppedImage); + croppedPainter.drawImage(0, 0 - diffHeight, snapmaticImage); + croppedPainter.end(); + snapmaticImage = croppedImage; + } + } + else { + snapmaticImage = snapmaticImage.scaled(snapmaticResolution, Qt::KeepAspectRatio, Qt::SmoothTransformation); + if (snapmaticImage.width() != snapmaticResolution.width()) { + diffWidth = snapmaticResolution.width() - snapmaticImage.width(); + diffWidth = diffWidth / 2; + } + else if (snapmaticImage.height() != snapmaticResolution.height()) { + diffHeight = snapmaticResolution.height() - snapmaticImage.height(); + diffHeight = diffHeight / 2; + } + } + snapmaticPainter.drawImage(0 + diffWidth, 0 + diffHeight, snapmaticImage); + if (ui->cbWatermark->isChecked()) + processWatermark(&snapmaticPainter); + imageTitle = tr("Custom Picture", "Custom Picture Description in SC, don't use Special Character!"); + } + snapmaticPainter.end(); + newImage = snapmaticPixmap.toImage(); +#if QT_VERSION >= 0x050600 + snapmaticPixmap.setDevicePixelRatio(screenRatioPR); +#endif + ui->labPicture->setPixmap(snapmaticPixmap.scaled(snapmaticResolutionLW * screenRatioPR, snapmaticResolutionLH * screenRatioPR, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); +} + +void ImportDialog::reworkImage() +{ + workImage = QImage(); + if (origImage.width() == origImage.height()) { + if (ui->cbResolution->currentIndex() == 0) { + insideAvatarZone = true; + ui->cbAvatar->setChecked(true); + } + else { + insideAvatarZone = false; + ui->cbAvatar->setChecked(false); + } + if (origImage.height() > snapmaticResolution.height()) { + workImage = origImage.scaled(snapmaticResolution.height(), snapmaticResolution.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + else { + workImage = origImage; + } + } + else if (origImage.width() > snapmaticResolution.width() && origImage.width() > origImage.height()) { + insideAvatarZone = false; + ui->cbAvatar->setChecked(false); + workImage = origImage.scaledToWidth(snapmaticResolution.width(), Qt::SmoothTransformation); + } + else if (origImage.height() > snapmaticResolution.height() && origImage.height() > origImage.width()) { + insideAvatarZone = false; + ui->cbAvatar->setChecked(false); + workImage = origImage.scaledToHeight(snapmaticResolution.height(), Qt::SmoothTransformation); + } + else { + insideAvatarZone = false; + ui->cbAvatar->setChecked(false); + workImage = origImage; + } + processImage(); +} + +void ImportDialog::processWatermark(QPainter *snapmaticPainter) +{ + bool blackWatermark = false; + bool redWatermark = false; + if (selectedColour.red() > 127) { + if (selectedColour.green() > 127 || selectedColour.blue() > 127) { + redWatermark = true; + } + } + else { + redWatermark = true; + } + if (selectedColour.lightness() > 127) { + blackWatermark = true; + } + // draw watermark + if (redWatermark) { + const QImage viewWatermark = QImage(AppEnv::getImagesFolder() % "/watermark_2r.png"); + snapmaticPainter->drawImage(snapmaticResolution.width() - viewWatermark.width(), 0, viewWatermark); + } + else + { + QImage viewWatermark = QImage(AppEnv::getImagesFolder() % "/watermark_2b.png"); + if (!blackWatermark) { + viewWatermark.invertPixels(QImage::InvertRgb); + } + snapmaticPainter->drawImage(snapmaticResolution.width() - viewWatermark.width(), 0, viewWatermark); + } + QImage textWatermark = QImage(AppEnv::getImagesFolder() % "/watermark_1b.png"); + if (!blackWatermark) { + textWatermark.invertPixels(QImage::InvertRgb); + } + snapmaticPainter->drawImage(snapmaticResolution.width() - textWatermark.width(), 0, textWatermark); +} + +void ImportDialog::processSettings(QString settingsProfile, bool setDefault) +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("Import"); + if (setDefault) { + settings.setValue("Profile", settingsProfile); + } + if (settingsProfile == "Default") { + watermarkAvatar = true; + watermarkPicture = false; + selectedColour = QColor::fromRgb(0, 0, 0, 255); + backImage = QImage(); + ui->cbBorderless->setChecked(false); + ui->cbStretch->setChecked(false); + ui->cbForceAvatarColour->setChecked(false); + ui->cbUnlimited->setChecked(false); + ui->cbImportAsIs->setChecked(false); + ui->cbResolution->setCurrentIndex(0); + } + else { + settings.beginGroup(settingsProfile); + watermarkAvatar = settings.value("WatermarkAvatar", true).toBool(); + watermarkPicture = settings.value("WatermarkPicture", false).toBool(); + backImage = qvariant_cast(settings.value("BackgroundImage", QImage())); + selectedColour = qvariant_cast(settings.value("SelectedColour", QColor::fromRgb(0, 0, 0, 255))); + ui->cbBorderless->setChecked(settings.value("BorderlessImage", false).toBool()); + ui->cbStretch->setChecked(settings.value("BackgroundStretch", false).toBool()); + ui->cbForceAvatarColour->setChecked(settings.value("ForceAvatarColour", false).toBool()); + ui->cbUnlimited->setChecked(settings.value("UnlimitedBuffer", false).toBool()); + ui->cbImportAsIs->setChecked(settings.value("ImportAsIs", false).toBool()); + const QVariant data = settings.value("Resolution", SnapmaticPicture::getSnapmaticResolution()); +#if QT_VERSION >= 0x060000 + if (data.typeId() == QMetaType::QSize) +#else + if (data.type() == QVariant::Size) +#endif + { + int index = ui->cbResolution->findData(data); + if (index != -1) { + ui->cbResolution->setCurrentIndex(index); + } + } + settings.endGroup(); + } + if (!workImage.isNull()) { + if (ui->cbAvatar->isChecked()) { + ui->cbWatermark->setChecked(watermarkAvatar); + } + else { + ui->cbWatermark->setChecked(watermarkPicture); + } + } + ui->labColour->setText(tr("Background Colour: %1").arg(selectedColour.name())); + if (!backImage.isNull()) { + ui->labBackgroundImage->setText(tr("Background Image: %1").arg(tr("Storage", "Background Image: Storage"))); + ui->cmdBackgroundWipe->setVisible(true); + } + else { + ui->labBackgroundImage->setText(tr("Background Image:")); + ui->cmdBackgroundWipe->setVisible(false); + } + settings.endGroup(); +} + +void ImportDialog::saveSettings(QString settingsProfile) +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("Import"); + settings.beginGroup(settingsProfile); + settings.setValue("WatermarkAvatar", watermarkAvatar); + settings.setValue("WatermarkPicture", watermarkPicture); + settings.setValue("BackgroundImage", backImage); + settings.setValue("SelectedColour", selectedColour); + settings.setValue("BorderlessImage", ui->cbBorderless->isChecked()); + settings.setValue("BackgroundStretch", ui->cbStretch->isChecked()); + settings.setValue("ForceAvatarColour", ui->cbForceAvatarColour->isChecked()); +#if QT_VERSION >= 0x050000 + const QVariant data = ui->cbResolution->currentData(); +#else + const QVariant data = ui->cbResolution->itemData(ui->cbResolution->currentIndex()); +#endif +#if QT_VERSION >= 0x060000 + if (data.typeId() == QMetaType::QSize) +#else + if (data.type() == QVariant::Size) +#endif + { + settings.setValue("Resolution", data); + } + else { + settings.setValue("Resolution", SnapmaticPicture::getSnapmaticResolution()); + } + settings.setValue("UnlimitedBuffer", ui->cbUnlimited->isChecked()); + settings.setValue("ImportAsIs", ui->cbImportAsIs->isChecked()); + settings.endGroup(); + settings.setValue("Profile", settingsProfile); + settings.endGroup(); +} + +void ImportDialog::cropPicture() +{ + qreal screenRatio = AppEnv::screenRatio(); + + QDialog cropDialog(this); +#if QT_VERSION >= 0x050000 + cropDialog.setObjectName(QStringLiteral("CropDialog")); +#else + cropDialog.setObjectName(QString::fromUtf8("CropDialog")); +#endif + cropDialog.setWindowTitle(tr("Crop Picture...")); + cropDialog.setWindowFlags(cropDialog.windowFlags()^Qt::WindowContextHelpButtonHint); + cropDialog.setModal(true); + + QVBoxLayout cropLayout; +#if QT_VERSION >= 0x050000 + cropLayout.setObjectName(QStringLiteral("CropLayout")); +#else + cropLayout.setObjectName(QString::fromUtf8("CropLayout")); +#endif + cropLayout.setContentsMargins(0, 0, 0, 0); + cropLayout.setSpacing(0); + cropDialog.setLayout(&cropLayout); + + ImageCropper imageCropper(&cropDialog); +#if QT_VERSION >= 0x050000 + imageCropper.setObjectName(QStringLiteral("ImageCropper")); +#else + imageCropper.setObjectName(QString::fromUtf8("ImageCropper")); +#endif + imageCropper.setBackgroundColor(Qt::black); + imageCropper.setCroppingRectBorderColor(QColor(255, 255, 255, 127)); + imageCropper.setImage(QPixmap::fromImage(origImage, Qt::AutoColor)); + imageCropper.setProportion(QSize(1, 1)); + imageCropper.setFixedSize(workImage.size()); + cropLayout.addWidget(&imageCropper); + + QHBoxLayout buttonLayout; +#if QT_VERSION >= 0x050000 + cropLayout.setObjectName(QStringLiteral("ButtonLayout")); +#else + cropLayout.setObjectName(QString::fromUtf8("ButtonLayout")); +#endif + cropLayout.addLayout(&buttonLayout); + + QPushButton cropButton(&cropDialog); +#if QT_VERSION >= 0x050000 + cropButton.setObjectName(QStringLiteral("CropButton")); +#else + cropButton.setObjectName(QString::fromUtf8("CropButton")); +#endif + cropButton.setMinimumSize(0, 40 * screenRatio); + cropButton.setText(tr("&Crop")); + cropButton.setToolTip(tr("Crop Picture")); + QObject::connect(&cropButton, SIGNAL(clicked(bool)), &cropDialog, SLOT(accept())); + + buttonLayout.addWidget(&cropButton); + + cropDialog.show(); + cropDialog.setFixedSize(cropDialog.sizeHint()); + if (cropDialog.exec() == QDialog::Accepted) { + QImage *croppedImage = new QImage(imageCropper.cropImage().toImage()); + setImage(croppedImage); + } +} + +void ImportDialog::importNewPicture() +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("FileDialogs"); + bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool(); + settings.beginGroup("ImportCopy"); + +fileDialogPreOpen: //Work? + QFileDialog fileDialog(this); + fileDialog.setFileMode(QFileDialog::ExistingFile); + fileDialog.setViewMode(QFileDialog::Detail); + fileDialog.setAcceptMode(QFileDialog::AcceptOpen); + fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog); + fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); + fileDialog.setWindowTitle(QApplication::translate("ProfileInterface", "Import...")); + fileDialog.setLabelText(QFileDialog::Accept, QApplication::translate("ProfileInterface", "Import")); + + // Getting readable Image formats + QString imageFormatsStr = " "; + for (const QByteArray &imageFormat : QImageReader::supportedImageFormats()) { + imageFormatsStr += QString("*.") % QString::fromUtf8(imageFormat).toLower() % " "; + } + + QStringList filters; + filters << QApplication::translate("ProfileInterface", "All image files (%1)").arg(imageFormatsStr.trimmed()); + filters << QApplication::translate("ProfileInterface", "All files (**)"); + fileDialog.setNameFilters(filters); + + QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); + + fileDialog.setSidebarUrls(sidebarUrls); + fileDialog.setDirectory(settings.value(profileName % "+Directory", StandardPaths::documentsLocation()).toString()); + fileDialog.restoreGeometry(settings.value(profileName % "+Geometry", "").toByteArray()); + + if (fileDialog.exec()) { + QStringList selectedFiles = fileDialog.selectedFiles(); + if (selectedFiles.length() == 1) { + QString selectedFile = selectedFiles.at(0); + QString selectedFileName = QFileInfo(selectedFile).fileName(); + + QFile snapmaticFile(selectedFile); + if (!snapmaticFile.open(QFile::ReadOnly)) { + QMessageBox::warning(this, QApplication::translate("ProfileInterface", "Import"), QApplication::translate("ProfileInterface", "Can't import %1 because file can't be open").arg("\""+selectedFileName+"\"")); + goto fileDialogPreOpen; + } + QImage *importImage = new QImage(); + QImageReader snapmaticImageReader; + snapmaticImageReader.setDecideFormatFromContent(true); + snapmaticImageReader.setDevice(&snapmaticFile); + if (!snapmaticImageReader.read(importImage)) { + QMessageBox::warning(this, QApplication::translate("ProfileInterface", "Import"), QApplication::translate("ProfileInterface", "Can't import %1 because file can't be parsed properly").arg("\""+selectedFileName+"\"")); + delete importImage; + goto fileDialogPreOpen; + } + setImage(importImage); + } + } + + settings.setValue(profileName % "+Geometry", fileDialog.saveGeometry()); + settings.setValue(profileName % "+Directory", fileDialog.directory().absolutePath()); + settings.endGroup(); + settings.endGroup(); +} + +void ImportDialog::loadImportSettings() +{ + if (settingsLocked) { + QMessageBox::information(this, tr("Load Settings..."), tr("Please import a new picture first")); + return; + } + bool ok; + QStringList profileList; + profileList << tr("Default", "Default as Default Profile") + << tr("Profile %1", "Profile %1 as Profile 1").arg("1") + << tr("Profile %1", "Profile %1 as Profile 1").arg("2") + << tr("Profile %1", "Profile %1 as Profile 1").arg("3") + << tr("Profile %1", "Profile %1 as Profile 1").arg("4") + << tr("Profile %1", "Profile %1 as Profile 1").arg("5"); + QString sProfile = QInputDialog::getItem(this, tr("Load Settings..."), tr("Please select your settings profile"), profileList, 0, false, &ok, windowFlags()); + if (ok) { + QString pProfile; + if (sProfile == tr("Default", "Default as Default Profile")) { + pProfile = "Default"; + } + else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("1")) { + pProfile = "Profile 1"; + } + else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("2")) { + pProfile = "Profile 2"; + } + else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("3")) { + pProfile = "Profile 3"; + } + else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("4")) + { + pProfile = "Profile 4"; + } + else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("5")) { + pProfile = "Profile 5"; + } + processSettings(pProfile, true); + processImage(); + } +} + +void ImportDialog::saveImportSettings() +{ + if (settingsLocked) { + QMessageBox::information(this, tr("Save Settings..."), tr("Please import a new picture first")); + return; + } + bool ok; + QStringList profileList; + profileList << tr("Profile %1", "Profile %1 as Profile 1").arg("1") + << tr("Profile %1", "Profile %1 as Profile 1").arg("2") + << tr("Profile %1", "Profile %1 as Profile 1").arg("3") + << tr("Profile %1", "Profile %1 as Profile 1").arg("4") + << tr("Profile %1", "Profile %1 as Profile 1").arg("5"); + QString sProfile = QInputDialog::getItem(this, tr("Save Settings..."), tr("Please select your settings profile"), profileList, 0, false, &ok, windowFlags()); + if (ok) { + QString pProfile; + if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("1")) { + pProfile = "Profile 1"; + } + else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("2")) { + pProfile = "Profile 2"; + } + else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("3")) { + pProfile = "Profile 3"; + } + else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("4")) { + pProfile = "Profile 4"; + } + else if (sProfile == tr("Profile %1", "Profile %1 as Profile 1").arg("5")) { + pProfile = "Profile 5"; + } + saveSettings(pProfile); + } +} + +QImage ImportDialog::image() +{ + if (ui->cbImportAsIs->isChecked()) { + return origImage; + } + else { + return newImage; + } +} + +void ImportDialog::setImage(QImage *image_) +{ + origImage = *image_; + workImage = QImage(); + if (image_->width() == image_->height()) { + if (ui->cbResolution->currentIndex() == 0) { + insideAvatarZone = true; + ui->cbAvatar->setChecked(true); + } + else { + insideAvatarZone = false; + ui->cbAvatar->setChecked(false); + } + if (image_->height() > snapmaticResolution.height()) { + workImage = image_->scaled(snapmaticResolution.height(), snapmaticResolution.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + delete image_; + } + else { + workImage = *image_; + delete image_; + } + } + else if (image_->width() > snapmaticResolution.width() && image_->width() > image_->height()) { + insideAvatarZone = false; + ui->cbAvatar->setChecked(false); + workImage = image_->scaledToWidth(snapmaticResolution.width(), Qt::SmoothTransformation); + delete image_; + } + else if (image_->height() > snapmaticResolution.height() && image_->height() > image_->width()) { + insideAvatarZone = false; + ui->cbAvatar->setChecked(false); + workImage = image_->scaledToHeight(snapmaticResolution.height(), Qt::SmoothTransformation); + delete image_; + } + else { + insideAvatarZone = false; + ui->cbAvatar->setChecked(false); + workImage = *image_; + delete image_; + } + processImage(); + lockSettings(false); +} + +void ImportDialog::lockSettings(bool lock) +{ + ui->gbAdvanced->setDisabled(lock); + if (ui->cbImportAsIs->isChecked()) { + ui->gbBackground->setDisabled(true); + ui->gbSettings->setDisabled(true); + } + else { + ui->gbBackground->setDisabled(lock); + ui->gbSettings->setDisabled(lock); + } + ui->cmdOK->setDisabled(lock); + settingsLocked = lock; +} + +void ImportDialog::enableOverwriteMode() +{ + setWindowTitle(QApplication::translate("ImageEditorDialog", "Overwrite Image...")); + ui->cmdOK->setText(QApplication::translate("ImageEditorDialog", "&Overwrite")); + ui->cmdOK->setToolTip(QApplication::translate("ImageEditorDialog", "Apply changes")); + ui->cmdCancel->setText(QApplication::translate("ImageEditorDialog", "&Close")); + ui->cmdCancel->setToolTip(QApplication::translate("ImageEditorDialog", "Discard changes")); + ui->cmdCancel->setDefault(true); + ui->cmdCancel->setFocus(); + lockSettings(true); +} + +bool ImportDialog::isImportAgreed() +{ + return importAgreed; +} + +bool ImportDialog::isUnlimitedBuffer() +{ + return ui->cbUnlimited->isChecked(); +} + +bool ImportDialog::areSettingsLocked() +{ + return settingsLocked; +} + +QString ImportDialog::getImageTitle() +{ + if (ui->cbImportAsIs->isChecked()) { + return tr("Custom Picture", "Custom Picture Description in SC, don't use Special Character!"); + } + else { + return imageTitle; + } +} + +void ImportDialog::on_cbIgnore_toggled(bool checked) +{ + ui->cbBorderless->setDisabled(checked); + processImage(); +} + +void ImportDialog::on_cbAvatar_toggled(bool checked) +{ + if (ui->cbResolution->currentIndex() != 0) + return; + + if (!workImage.isNull() && workImage.width() == workImage.height() && !checked) { + if (QMessageBox::No == QMessageBox::warning(this, tr("Snapmatic Avatar Zone"), tr("Are you sure to use a square image outside of the Avatar Zone?\nWhen you want to use it as Avatar the image will be detached!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) { + ui->cbAvatar->setChecked(true); + insideAvatarZone = true; + return; + } + } + insideAvatarZone = ui->cbAvatar->isChecked(); + watermarkBlock = true; + if (insideAvatarZone) { + ui->cbWatermark->setChecked(watermarkAvatar); + } + else { + ui->cbWatermark->setChecked(watermarkPicture); + } + watermarkBlock = false; + processImage(); +} + +void ImportDialog::on_cmdCancel_clicked() +{ + close(); +} + +void ImportDialog::on_cmdOK_clicked() +{ + importAgreed = true; + close(); +} + +void ImportDialog::on_labPicture_labelPainted() +{ + if (insideAvatarZone) { + QImage avatarAreaFinalImage(avatarAreaImage); + if (selectedColour.lightness() > 127) { + avatarAreaFinalImage.setColor(1, qRgb(0, 0, 0)); + } + QPainter labelPainter(ui->labPicture); + labelPainter.drawImage(0, 0, avatarAreaFinalImage.scaled(snapmaticResolutionLW, snapmaticResolutionLH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + labelPainter.end(); + } +} + +void ImportDialog::on_cmdColourChange_clicked() +{ + QColor newSelectedColour = QColorDialog::getColor(selectedColour, this, tr("Select Colour...")); + if (newSelectedColour.isValid()) { + selectedColour = newSelectedColour; + ui->labColour->setText(tr("Background Colour: %1").arg(selectedColour.name())); + processImage(); + } +} + +void ImportDialog::on_cmdBackgroundChange_clicked() +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("FileDialogs"); + bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool(); + settings.beginGroup("ImportBackground"); + +fileDialogPreOpen: + QFileDialog fileDialog(this); + fileDialog.setFileMode(QFileDialog::ExistingFiles); + fileDialog.setViewMode(QFileDialog::Detail); + fileDialog.setAcceptMode(QFileDialog::AcceptOpen); + fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog); + fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); + fileDialog.setWindowTitle(QApplication::translate("ProfileInterface", "Import...")); + fileDialog.setLabelText(QFileDialog::Accept, QApplication::translate("ProfileInterface", "Import")); + + // Getting readable Image formats + QString imageFormatsStr = " "; + for (const QByteArray &imageFormat : QImageReader::supportedImageFormats()) { + imageFormatsStr += QString("*.") % QString::fromUtf8(imageFormat).toLower() % " "; + } + + QStringList filters; + filters << QApplication::translate("ProfileInterface", "All image files (%1)").arg(imageFormatsStr.trimmed()); + filters << QApplication::translate("ProfileInterface", "All files (**)"); + fileDialog.setNameFilters(filters); + + QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); + + fileDialog.setSidebarUrls(sidebarUrls); + fileDialog.setDirectory(settings.value("Directory", StandardPaths::documentsLocation()).toString()); + fileDialog.restoreGeometry(settings.value("Geometry", "").toByteArray()); + + if (fileDialog.exec()) { + QStringList selectedFiles = fileDialog.selectedFiles(); + if (selectedFiles.length() == 1) { + QString selectedFile = selectedFiles.at(0); + QString selectedFileName = QFileInfo(selectedFile).fileName(); + + QFile snapmaticFile(selectedFile); + if (!snapmaticFile.open(QFile::ReadOnly)) { + QMessageBox::warning(this, QApplication::translate("ProfileInterface", "Import"), QApplication::translate("ProfileInterface", "Can't import %1 because file can't be open").arg("\""+selectedFileName+"\"")); + goto fileDialogPreOpen; + } + QImage importImage; + QImageReader snapmaticImageReader; + snapmaticImageReader.setDecideFormatFromContent(true); + snapmaticImageReader.setDevice(&snapmaticFile); + if (!snapmaticImageReader.read(&importImage)) { + QMessageBox::warning(this, QApplication::translate("ProfileInterface", "Import"), QApplication::translate("ProfileInterface", "Can't import %1 because file can't be parsed properly").arg("\""+selectedFileName+"\"")); + goto fileDialogPreOpen; + } + backImage = importImage.scaled(snapmaticResolution, Qt::KeepAspectRatio, Qt::SmoothTransformation); + backgroundPath = selectedFile; + ui->labBackgroundImage->setText(tr("Background Image: %1").arg(tr("File", "Background Image: File"))); + ui->cmdBackgroundWipe->setVisible(true); + processImage(); + } + } + + settings.setValue("Geometry", fileDialog.saveGeometry()); + settings.setValue("Directory", fileDialog.directory().absolutePath()); + settings.endGroup(); + settings.endGroup(); +} + +void ImportDialog::on_cmdBackgroundWipe_clicked() +{ + backImage = QImage(); + ui->labBackgroundImage->setText(tr("Background Image:")); + ui->cmdBackgroundWipe->setVisible(false); + processImage(); +} + +void ImportDialog::on_cbStretch_toggled(bool checked) +{ + Q_UNUSED(checked) + processImage(); +} + +void ImportDialog::on_cbForceAvatarColour_toggled(bool checked) +{ + Q_UNUSED(checked) + processImage(); +} + +void ImportDialog::on_cbWatermark_toggled(bool checked) +{ + if (!watermarkBlock) { + if (insideAvatarZone) { + watermarkAvatar = checked; + } + else { + watermarkPicture = checked; + } + processImage(); + } +} + +void ImportDialog::on_cbBorderless_toggled(bool checked) +{ + ui->cbIgnore->setDisabled(checked); + processImage(); +} + +void ImportDialog::on_cbImportAsIs_toggled(bool checked) +{ + ui->cbResolution->setDisabled(checked); + ui->labResolution->setDisabled(checked); + ui->gbBackground->setDisabled(checked); + ui->gbSettings->setDisabled(checked); +} + +void ImportDialog::on_cbResolution_currentIndexChanged(int index) +{ + Q_UNUSED(index) +#if QT_VERSION >= 0x050000 + const QVariant data = ui->cbResolution->currentData(); +#else + const QVariant data = ui->cbResolution->itemData(ui->cbResolution->currentIndex()); +#endif +#if QT_VERSION >= 0x060000 + if (data.typeId() == QMetaType::QSize) +#else + if (data.type() == QVariant::Size) +#endif + { + const QSize dataSize = data.toSize(); + if (dataSize == SnapmaticPicture::getSnapmaticResolution()) { + ui->cbAvatar->setEnabled(true); + snapmaticResolution = dataSize; + reworkImage(); + } + else { + if (!workImage.isNull() && workImage.width() == workImage.height() && ui->cbAvatar->isChecked()) { + if (QMessageBox::No == QMessageBox::warning(this, tr("Snapmatic Avatar Zone"), tr("Are you sure to use a square image outside of the Avatar Zone?\nWhen you want to use it as Avatar the image will be detached!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) { + ui->cbResolution->setCurrentIndex(0); + ui->cbAvatar->setChecked(true); + insideAvatarZone = true; + return; + } + } + ui->cbAvatar->setChecked(false); + ui->cbAvatar->setDisabled(true); + insideAvatarZone = false; + ui->cbWatermark->setChecked(watermarkPicture); + snapmaticResolution = dataSize; + reworkImage(); + } + } +} diff --git a/ImportDialog.h b/ImportDialog.h index 1da27fd..1cd93ac 100644 --- a/ImportDialog.h +++ b/ImportDialog.h @@ -1,60 +1,93 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef IMPORTDIALOG_H -#define IMPORTDIALOG_H - -#include - -namespace Ui { -class ImportDialog; -} - -class ImportDialog : public QDialog -{ - Q_OBJECT - -public: - explicit ImportDialog(QWidget *parent = 0); - ~ImportDialog(); - QImage image(); - QString getImageTitle(); - void setImage(const QImage &image); - bool isDoImport(); - -private slots: - void processImage(); - void on_rbIgnore_clicked(); - void on_rbKeep_clicked(); - void on_cbAvatar_clicked(); - void on_cmdCancel_clicked(); - void on_cmdOK_clicked(); - void on_labPicture_labelPainted(); - -private: - Ui::ImportDialog *ui; - QImage avatarAreaImage; - QString imageTitle; - QImage workImage; - QImage newImage; - bool doImport; - int snapmaticResolutionLW; - int snapmaticResolutionLH; -}; - -#endif // IMPORTDIALOG_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2017-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef IMPORTDIALOG_H +#define IMPORTDIALOG_H + +#include +#include + +namespace Ui { +class ImportDialog; +} + +class ImportDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ImportDialog(QString profileName, QWidget *parent = 0); + ~ImportDialog(); + QImage image(); + QString getImageTitle(); + void setImage(QImage *image); + void lockSettings(bool lock); + void enableOverwriteMode(); + bool isImportAgreed(); + bool isUnlimitedBuffer(); + bool areSettingsLocked(); + +private slots: + void processImage(); + void reworkImage(); + void cropPicture(); + void importNewPicture(); + void loadImportSettings(); + void saveImportSettings(); + void on_cbIgnore_toggled(bool checked); + void on_cbAvatar_toggled(bool checked); + void on_cmdCancel_clicked(); + void on_cmdOK_clicked(); + void on_labPicture_labelPainted(); + void on_cmdColourChange_clicked(); + void on_cmdBackgroundChange_clicked(); + void on_cmdBackgroundWipe_clicked(); + void on_cbStretch_toggled(bool checked); + void on_cbForceAvatarColour_toggled(bool checked); + void on_cbWatermark_toggled(bool checked); + void on_cbBorderless_toggled(bool checked); + void on_cbImportAsIs_toggled(bool checked); + void on_cbResolution_currentIndexChanged(int index); + +private: + QString profileName; + Ui::ImportDialog *ui; + QImage avatarAreaImage; + QString backgroundPath; + QString imageTitle; + QImage backImage; + QImage workImage; + QImage origImage; + QImage newImage; + QColor selectedColour; + QMenu optionsMenu; + QSize snapmaticResolution; + bool insideAvatarZone; + bool watermarkPicture; + bool watermarkAvatar; + bool watermarkBlock; + bool settingsLocked; + bool importAgreed; + int snapmaticResolutionLW; + int snapmaticResolutionLH; + void processWatermark(QPainter *snapmaticPainter); + void processSettings(QString settingsProfile, bool setDefault = false); + void saveSettings(QString settingsProfile); +}; + +#endif // IMPORTDIALOG_H diff --git a/ImportDialog.ui b/ImportDialog.ui index dc42083..5780456 100644 --- a/ImportDialog.ui +++ b/ImportDialog.ui @@ -1,187 +1,420 @@ - - - ImportDialog - - - - 0 - 0 - 430 - 380 - - - - - 430 - 380 - - - - - 430 - 380 - - - - Import... - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 430 - 240 - - - - - - - - - - - - 0 - 0 - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - 9 - - - 6 - - - 9 - - - 9 - - - - - Settings - - - - - - &Keep Aspect Ratio - - - - - - - &Ignore Aspect Ratio - - - - - - - &Avatar - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - &OK - - - - - - - - 0 - 0 - - - - &Cancel - - - - - - - - - - - - - UiModLabel - QLabel -
UiModLabel.h
-
-
- - -
+ + + ImportDialog + + + + 0 + 0 + 516 + 673 + + + + + 516 + 512 + + + + Import... + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 516 + 288 + + + + + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 9 + + + 6 + + + 9 + + + 9 + + + + + Picture + + + + + + + + Ignore Aspect Ratio + + + + + + + Avatar + + + + + + + Watermark + + + + + + + Crop to Aspect Ratio + + + + + + + + + + + + Background + + + + + + + + + + Background Colour: <span style="color: %1">%1</span> + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Select background colour + + + ... + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + true + + + Ignore Aspect Ratio + + + + + + + Force Colour in Avatar Zone + + + + + + + + + Background Image: + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Select background image + + + ... + + + + + + + Remove background image + + + X + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + Advanced + + + + + + + + Avoid compression and expand buffer instead, improves picture quality, but may break Snapmatic + + + Unlimited Buffer + + + + + + + Import as-is, don't change the picture at all, guaranteed to break Snapmatic unless you know what you doing + + + Import as-is + + + + + + + + + Resolution: + + + + + + + + 0 + 0 + + + + Snapmatic resolution + + + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + Import options + + + &Options + + + false + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Import picture + + + &OK + + + + + + + + 0 + 0 + + + + Discard picture + + + &Cancel + + + + + + + + + + + + + UiModLabel + QLabel +
uimod/UiModLabel.h
+ + mouseMoved() + mouseReleased() + mousePressed() + mouseDoubleClicked() + +
+
+ + +
diff --git a/JsonEditorDialog.cpp b/JsonEditorDialog.cpp new file mode 100644 index 0000000..ae13966 --- /dev/null +++ b/JsonEditorDialog.cpp @@ -0,0 +1,226 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2017-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "JsonEditorDialog.h" +#include "ui_JsonEditorDialog.h" +#include "SnapmaticEditor.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include + +#if QT_VERSION >= 0x050200 +#include +#endif + +#ifdef GTA5SYNC_TELEMETRY +#include "TelemetryClass.h" +#endif + +JsonEditorDialog::JsonEditorDialog(SnapmaticPicture *picture, QWidget *parent) : + QDialog(parent), smpic(picture), + ui(new Ui::JsonEditorDialog) +{ + // Set Window Flags +#if QT_VERSION >= 0x050900 + setWindowFlag(Qt::WindowContextHelpButtonHint, false); + setWindowFlag(Qt::WindowMinMaxButtonsHint, true); +#else + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowMinMaxButtonsHint); +#endif + + ui->setupUi(this); + ui->cmdClose->setDefault(true); + ui->cmdClose->setFocus(); + + // Set Icon for Close Button + if (QIcon::hasThemeIcon("dialog-close")) { + ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); + } + else if (QIcon::hasThemeIcon("gtk-close")) { + ui->cmdClose->setIcon(QIcon::fromTheme("gtk-close")); + } + + // Set Icon for Save Button + if (QIcon::hasThemeIcon("document-save")) { + ui->cmdSave->setIcon(QIcon::fromTheme("document-save")); + } + else if (QIcon::hasThemeIcon("gtk-save")) { + ui->cmdSave->setIcon(QIcon::fromTheme("gtk-save")); + } + + jsonCode = picture->getJsonStr(); + +#if QT_VERSION >= 0x050200 + ui->txtJSON->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); +#else + QFont jsonFont = ui->txtJSON->font(); + jsonFont.setStyleHint(QFont::Monospace); + jsonFont.setFixedPitch(true); + ui->txtJSON->setFont(jsonFont); +#endif + QFontMetrics fontMetrics(ui->txtJSON->font()); +#if QT_VERSION >= 0x050B00 + ui->txtJSON->setTabStopDistance(fontMetrics.horizontalAdvance(" ")); +#else + ui->txtJSON->setTabStopWidth(fontMetrics.width(" ")); +#endif + + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonCode.toUtf8()); + ui->txtJSON->setStyleSheet("QPlainTextEdit{background-color: rgb(46, 47, 48); color: rgb(238, 231, 172);}"); + ui->txtJSON->setPlainText(QString::fromUtf8(jsonDocument.toJson(QJsonDocument::Indented)).trimmed()); + jsonHl = new JSHighlighter(ui->txtJSON->document()); + + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); +#ifndef Q_OS_MAC + ui->hlButtons->setSpacing(6 * screenRatio); + ui->hlButtons->setContentsMargins(9 * screenRatio, 0, 9 * screenRatio, 0); + ui->vlInterface->setContentsMargins(0, 0, 0, 9 * screenRatio); +#else + ui->hlButtons->setSpacing(6 * screenRatio); + ui->hlButtons->setContentsMargins(9 * screenRatio, 0, 9 * screenRatio, 0); + ui->vlInterface->setContentsMargins(0, 0, 0, 9 * screenRatio); +#endif + if (screenRatio > 1) { + ui->lineJSON->setMinimumHeight(qRound(1 * screenRatio)); + ui->lineJSON->setMaximumHeight(qRound(1 * screenRatio)); + ui->lineJSON->setLineWidth(qRound(1 * screenRatio)); + } + resize(450 * screenRatio, 550 * screenRatio); +} + +JsonEditorDialog::~JsonEditorDialog() +{ + delete jsonHl; + delete ui; +} + +void JsonEditorDialog::closeEvent(QCloseEvent *ev) +{ + QString jsonPatched = QString(ui->txtJSON->toPlainText()).replace("\t", " "); + QJsonDocument jsonNew = QJsonDocument::fromJson(jsonPatched.toUtf8()); + QJsonDocument jsonOriginal = QJsonDocument::fromJson(jsonCode.toUtf8()); + QString originalCode = QString::fromUtf8(jsonOriginal.toJson(QJsonDocument::Compact)); + QString newCode = QString::fromUtf8(jsonNew.toJson(QJsonDocument::Compact)); + if (newCode != originalCode) { + QMessageBox::StandardButton button = QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("

Unsaved changes detected

You want to save the JSON content before you quit?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Cancel); + if (button == QMessageBox::Yes) { + if (saveJsonContent()) { + ev->accept(); + } + else { + ev->ignore(); + } + return; + } + else if (button == QMessageBox::No) { + ev->accept(); + return; + } + else { + ev->ignore(); + return; + } + } +} + +bool JsonEditorDialog::saveJsonContent() +{ + QString jsonPatched = QString(ui->txtJSON->toPlainText()).replace("\t", " "); + QJsonDocument jsonNew = QJsonDocument::fromJson(jsonPatched.toUtf8()); + if (!jsonNew.isEmpty()) { + QJsonDocument jsonOriginal = QJsonDocument::fromJson(jsonCode.toUtf8()); + QString originalCode = QString::fromUtf8(jsonOriginal.toJson(QJsonDocument::Compact)); + QString newCode = QString::fromUtf8(jsonNew.toJson(QJsonDocument::Compact)); + if (newCode != originalCode) { + QString currentFilePath = smpic->getPictureFilePath(); + QString originalFilePath = smpic->getOriginalPictureFilePath(); + QString backupFileName = originalFilePath % ".bak"; + if (!QFile::exists(backupFileName)) { + QFile::copy(currentFilePath, backupFileName); + } + smpic->setJsonStr(newCode, true); + if (!smpic->isJsonOk()) { + QString lastStep = smpic->getLastStep(false); + QString readableError; + if (lastStep.contains("JSONINCOMPLETE") && lastStep.contains("JSONERROR")) { + readableError = SnapmaticPicture::tr("JSON is incomplete and malformed"); + } + else if (lastStep.contains("JSONINCOMPLETE")) { + readableError = SnapmaticPicture::tr("JSON is incomplete"); + } + else if (lastStep.contains("JSONERROR")) { + readableError = SnapmaticPicture::tr("JSON is malformed"); + } + else { + readableError = tr("JSON Error"); + } + QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("Patching of Snapmatic Properties failed because of %1").arg(readableError)); + smpic->setJsonStr(originalCode, true); + return false; + } + if (!smpic->exportPicture(currentFilePath)) { + QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("Patching of Snapmatic Properties failed because of I/O Error")); + smpic->setJsonStr(originalCode, true); + return false; + } + jsonCode = newCode; + smpic->updateStrings(); + smpic->emitUpdate(); +#ifdef GTA5SYNC_TELEMETRY + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool(); + telemetrySettings.endGroup(); + if (pushUsageData && Telemetry->canPush()) { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "JSONEdited"; + jsonObject["EditedSize"] = QString::number(smpic->getContentMaxLength()); +#if QT_VERSION >= 0x060000 + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } +#endif + return true; + } + return true; + } + else { + QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("Patching of Snapmatic Properties failed because of JSON Error")); + return false; + } +} + +void JsonEditorDialog::on_cmdClose_clicked() +{ + close(); +} + +void JsonEditorDialog::on_cmdSave_clicked() +{ + if (saveJsonContent()) + close(); +} diff --git a/JsonEditorDialog.h b/JsonEditorDialog.h new file mode 100644 index 0000000..4e618bb --- /dev/null +++ b/JsonEditorDialog.h @@ -0,0 +1,56 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef JSONEDITORDIALOG_H +#define JSONEDITORDIALOG_H + +#include "SnapmaticPicture.h" +#include "JSHighlighter.h" +#include + +namespace Ui { +class JsonEditorDialog; +} + +class JsonEditorDialog : public QDialog +{ + Q_OBJECT + +public: + explicit JsonEditorDialog(SnapmaticPicture *picture, QWidget *parent = 0); + bool saveJsonContent(); + ~JsonEditorDialog(); + +protected: + void closeEvent(QCloseEvent *ev); + +private slots: + void on_cmdClose_clicked(); + void on_cmdSave_clicked(); + +signals: + void codeUpdated(QString jsonCode); + +private: + QString jsonCode; + JSHighlighter *jsonHl; + SnapmaticPicture *smpic; + Ui::JsonEditorDialog *ui; +}; + +#endif // JSONEDITORDIALOG_H diff --git a/JsonEditorDialog.ui b/JsonEditorDialog.ui new file mode 100644 index 0000000..a52f087 --- /dev/null +++ b/JsonEditorDialog.ui @@ -0,0 +1,145 @@ + + + JsonEditorDialog + + + + 0 + 0 + 550 + 450 + + + + Snapmatic JSON Editor + + + + 0 + + + 0 + + + 0 + + + 9 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + + + + + 0 + 1 + + + + + 16777215 + 1 + + + + QFrame[frameShape="4"] +{ + color: black; +} + + + QFrame::Plain + + + 1 + + + Qt::Horizontal + + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Apply changes + + + &Save + + + + + + + + 0 + 0 + + + + Discard changes + + + &Close + + + + + + + + + + diff --git a/LICENSE b/LICENSE index 9cecc1d..f288702 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} + + Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -645,14 +645,14 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - {project} Copyright (C) {year} {fullname} + Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/LICENSE.GPL b/LICENSE.GPL old mode 100755 new mode 100644 index e963df8..f288702 --- a/LICENSE.GPL +++ b/LICENSE.GPL @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -620,3 +620,55 @@ copy of the Program in return for a fee. END OF TERMS AND CONDITIONS + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/LICENSE.LGPL b/LICENSE.LGPL old mode 100755 new mode 100644 index add53b4..0a04128 --- a/LICENSE.LGPL +++ b/LICENSE.LGPL @@ -1,459 +1,165 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/MapLocationDialog.cpp b/MapLocationDialog.cpp new file mode 100644 index 0000000..b233a28 --- /dev/null +++ b/MapLocationDialog.cpp @@ -0,0 +1,346 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2017-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "MapLocationDialog.h" +#include "ui_MapLocationDialog.h" +#include "IconLoader.h" +#include "AppEnv.h" +#include +#include +#include + +MapLocationDialog::MapLocationDialog(double x, double y, QWidget *parent) : + QDialog(parent), xpos_old(x), ypos_old(y), + ui(new Ui::MapLocationDialog) +{ + // Set Window Flags +#if QT_VERSION >= 0x050900 + setWindowFlag(Qt::WindowContextHelpButtonHint, false); +#else + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); +#endif + + ui->setupUi(this); + ui->cmdDone->setVisible(false); + ui->cmdApply->setVisible(false); + ui->cmdRevert->setVisible(false); + ui->cmdDone->setCursor(Qt::ArrowCursor); + ui->cmdClose->setCursor(Qt::ArrowCursor); + + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + int widgetMargin = qRound(3 * screenRatio); + ui->hlMapDialog->setContentsMargins(widgetMargin, widgetMargin, widgetMargin, widgetMargin); + ui->vlMapDialog->setSpacing(widgetMargin); + setMinimumSize(500 * screenRatio, 600 * screenRatio); + setMaximumSize(500 * screenRatio, 600 * screenRatio); + + zoomPercent = 100; + changeMode = false; + propUpdate = false; +} + +MapLocationDialog::~MapLocationDialog() +{ + delete ui; +} + +void MapLocationDialog::drawPointOnMap(double xpos_d, double ypos_d) +{ + ui->labPos->setText(tr("X: %1\nY: %2", "X and Y position").arg(QString::number(xpos_d), QString::number(ypos_d))); + xpos_new = xpos_d; + ypos_new = ypos_d; + repaint(); +} + +void MapLocationDialog::setCayoPerico(bool isCayoPerico) +{ + qreal screenRatio = AppEnv::screenRatio(); + p_isCayoPerico = isCayoPerico; + if (isCayoPerico) { + setMinimumSize(500 * screenRatio, 500 * screenRatio); + setMaximumSize(500 * screenRatio, 500 * screenRatio); + ui->hlMapDialog->removeItem(ui->vlMapDialog); + ui->hlMapDialog->insertLayout(0, ui->vlMapDialog); + ui->hlMapDialog->removeItem(ui->vlPosLayout); + ui->hlMapDialog->addLayout(ui->vlPosLayout); + ui->labPos->setAlignment(Qt::AlignRight); + mapImage = QImage(AppEnv::getImagesFolder() % "/mapcayoperico.jpg"); + } + else { + mapImage = QImage(AppEnv::getImagesFolder() % "/mappreview.jpg"); + } + drawPointOnMap(xpos_old, ypos_old); +} + +void MapLocationDialog::updatePosFromEvent(double x, double y) +{ + QSize mapPixelSize = size(); + double x_per = x / mapPixelSize.width(); // get X % + double y_per = y / mapPixelSize.height(); // get Y % + double x_pos, y_pos; + if (p_isCayoPerico) { + x_pos = x_per * 2340; // 2340 is 100% for X (Cayo Perico) + y_pos = y_per * -2340; // -2340 is 100% for Y (Cayo Perico) + x_pos = x_pos + 3560; // +3560 gets corrected for X (Cayo Perico) + y_pos = y_pos - 3980; // -3980 gets corrected for Y (Cayo Perico) + } + else { + x_pos = x_per * 10000; // 10000 is 100% for X (Los Santos) + y_pos = y_per * -12000; // -12000 is 100% for Y (Los Santos) + x_pos = x_pos - 4000; // -4000 gets corrected for X (Los Santos) + y_pos = y_pos + 8000; // +8000 gets corrected for Y (Los Santos) + } + drawPointOnMap(x_pos, y_pos); +} + +void MapLocationDialog::paintEvent(QPaintEvent *ev) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + + // Screen Ratio + qreal screenRatio = AppEnv::screenRatio(); + qreal screenRatioPR = AppEnv::screenRatioPR(); + + // Paint Map + const double zoomLevel = static_cast(zoomPercent) / 100; + const QSize mapImageSize = mapImage.size(); + const QPointF mapImageMid(static_cast(mapImageSize.width()) / 2, static_cast(mapImageSize.height()) / 2); + const QSizeF srcImageSize(static_cast(mapImageSize.width()) / zoomLevel , static_cast(mapImageSize.height()) / zoomLevel); + const QPointF mapImageTopLeft(mapImageMid.x() - (srcImageSize.width() / 2), mapImageMid.y() - (srcImageSize.height() / 2)); + const QPointF mapImageBottomRight(mapImageMid.x() + (srcImageSize.width() / 2), mapImageMid.y() + (srcImageSize.height() / 2)); + painter.drawImage(QRect(QPoint(0, 0), size()), mapImage, QRectF(mapImageTopLeft, mapImageBottomRight)); + + // Paint Marker + QSize mapPixelSize = size(); + int pointMarkerSize = 8 * screenRatio; + int pointMarkerHalfSize = pointMarkerSize / 2; + double xpos_mp, ypos_mp; + if (p_isCayoPerico) { + double xpos_per = xpos_new - 3560; // correct X in reserve + double ypos_per = ypos_new + 3980; // correct y in reserve + xpos_per = xpos_per / 2340; // divide 100% for X + ypos_per = ypos_per / -2340; // divide 100% for Y + xpos_mp = xpos_per * mapPixelSize.width(); // locate window width pos + ypos_mp = ypos_per * mapPixelSize.height(); // locate window height pos + } + else { + double xpos_per = xpos_new + 4000; // correct X in reserve + double ypos_per = ypos_new - 8000; // correct y in reserve + xpos_per = xpos_per / 10000; // divide 100% for X + ypos_per = ypos_per / -12000; // divide 100% for Y + xpos_mp = xpos_per * mapPixelSize.width(); // locate window width pos + ypos_mp = ypos_per * mapPixelSize.height(); // locate window height pos + } + QPointF pointMarkerPos(xpos_mp, ypos_mp); + if (screenRatioPR != 1) { + pointMarkerPos.setX(pointMarkerPos.x() - pointMarkerHalfSize + screenRatioPR); + pointMarkerPos.setY(pointMarkerPos.y() - pointMarkerHalfSize + screenRatioPR); + } + else { + pointMarkerPos.setX(pointMarkerPos.x() - pointMarkerHalfSize); + pointMarkerPos.setY(pointMarkerPos.y() - pointMarkerHalfSize); + } + QPixmap mapMarkerPixmap = IconLoader::loadingPointmakerIcon().pixmap(QSize(pointMarkerSize, pointMarkerSize)); + painter.drawPixmap(pointMarkerPos, mapMarkerPixmap); + + QDialog::paintEvent(ev); +} + +void MapLocationDialog::mouseMoveEvent(QMouseEvent *ev) +{ + if (changeMode && ev->buttons() & Qt::LeftButton) { +#if QT_VERSION >= 0x060000 + const QPointF localPos = ev->position(); +#elif QT_VERSION >= 0x050000 + const QPointF localPos = ev->localPos(); +#else + const QPointF localPos = ev->posF(); +#endif +#ifdef Q_OS_WIN + qreal screenRatioPR = AppEnv::screenRatioPR(); + if (screenRatioPR != 1) { + updatePosFromEvent(localPos.x() - screenRatioPR, localPos.y() - screenRatioPR); + } + else { + updatePosFromEvent(localPos.x(), localPos.y()); + } +#else + updatePosFromEvent(localPos.x(), localPos.y()); +#endif + } + else if (dragStart && ev->buttons() & Qt::LeftButton) { +#if QT_VERSION >= 0x060000 + const QPointF dragNewPosition = ev->position(); +#elif QT_VERSION >= 0x050000 + const QPointF dragNewPosition = ev->localPos(); +#else + const QPointF dragNewPosition = ev->posF(); +#endif + mapDiffPosition = dragNewPosition - dragPosition + mapDiffPosition; + dragPosition = dragNewPosition; + } +} + +void MapLocationDialog::mousePressEvent(QMouseEvent *ev) +{ + if (!changeMode && ev->button() == Qt::LeftButton) { +#if QT_VERSION >= 0x060000 + dragPosition = ev->position(); +#elif QT_VERSION >= 0x050000 + dragPosition = ev->localPos(); +#else + dragPosition = ev->posF(); +#endif + dragStart = true; + } +} + +void MapLocationDialog::mouseReleaseEvent(QMouseEvent *ev) +{ + if (changeMode && ev->button() == Qt::LeftButton) { +#if QT_VERSION >= 0x060000 + const QPointF localPos = ev->position(); +#elif QT_VERSION >= 0x050000 + const QPointF localPos = ev->localPos(); +#else + const QPointF localPos = ev->posF(); +#endif +#ifdef Q_OS_WIN + qreal screenRatioPR = AppEnv::screenRatioPR(); + if (screenRatioPR != 1) { + updatePosFromEvent(localPos.x() - screenRatioPR, localPos.y() - screenRatioPR); + } + else { + updatePosFromEvent(localPos.x(), localPos.y()); + } +#else + updatePosFromEvent(localPos.x(), localPos.y()); +#endif + } + else if (dragStart && ev->button() == Qt::LeftButton) { + dragStart = false; + } +} + +void MapLocationDialog::wheelEvent(QWheelEvent *ev) +{ +#ifdef GTA5SYNC_EXPERIMENTAL +#if QT_VERSION >= 0x050000 + const QPoint numPixels = ev->pixelDelta(); + const QPoint numDegrees = ev->angleDelta(); +#else + QPoint numDegrees; + if (ev->orientation() == Qt::Horizontal) { + numDegrees.setX(ev->delta()); + } + else { + numDegrees.setY(ev->delta()); + } +#endif +#if QT_VERSION >= 0x050000 + if (!numPixels.isNull()) { + if (numPixels.y() < 0 && zoomPercent != 100) { + zoomPercent = zoomPercent - 10; + repaint(); + } + else if (numPixels.y() > 0 && zoomPercent != 400) { + zoomPercent = zoomPercent + 10; + repaint(); + } + return; + } +#endif + if (!numDegrees.isNull()) { + if (numDegrees.y() < 0 && zoomPercent != 100) { + zoomPercent = zoomPercent - 10; + repaint(); + } + else if (numDegrees.y() > 0 && zoomPercent != 400) { + zoomPercent = zoomPercent + 10; + repaint(); + } + } +#else + Q_UNUSED(ev) +#endif +} + +void MapLocationDialog::on_cmdChange_clicked() +{ + qreal screenRatio = AppEnv::screenRatio(); + int pointMakerSize = 8 * screenRatio; + QPixmap pointMakerPixmap = IconLoader::loadingPointmakerIcon().pixmap(QSize(pointMakerSize, pointMakerSize)); + QCursor pointMakerCursor(pointMakerPixmap); + ui->cmdDone->setVisible(true); + ui->cmdApply->setVisible(false); + ui->cmdChange->setVisible(false); + ui->cmdRevert->setVisible(false); + + setCursor(pointMakerCursor); + changeMode = true; +} + +void MapLocationDialog::on_cmdDone_clicked() +{ + ui->cmdDone->setVisible(false); + ui->cmdChange->setVisible(true); + if (xpos_new != xpos_old || ypos_new != ypos_old) { + ui->cmdApply->setVisible(true); + ui->cmdRevert->setVisible(true); + } + setCursor(Qt::ArrowCursor); + changeMode = false; +} + +void MapLocationDialog::on_cmdApply_clicked() +{ + propUpdate = true; + xpos_old = xpos_new; + ypos_old = ypos_new; + ui->cmdApply->setVisible(false); + ui->cmdRevert->setVisible(false); +} + +void MapLocationDialog::on_cmdRevert_clicked() +{ + drawPointOnMap(xpos_old, ypos_old); + ui->cmdApply->setVisible(false); + ui->cmdRevert->setVisible(false); +} + +bool MapLocationDialog::propUpdated() +{ + return propUpdate; +} + +double MapLocationDialog::getXpos() +{ + return xpos_old; +} + +double MapLocationDialog::getYpos() +{ + return ypos_old; +} + +void MapLocationDialog::on_cmdClose_clicked() +{ + close(); +} diff --git a/MapLocationDialog.h b/MapLocationDialog.h new file mode 100644 index 0000000..c67e02f --- /dev/null +++ b/MapLocationDialog.h @@ -0,0 +1,73 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2017-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef MAPLOCATIONDIALOG_H +#define MAPLOCATIONDIALOG_H + +#include +#include + +namespace Ui { +class MapLocationDialog; +} + +class MapLocationDialog : public QDialog +{ + Q_OBJECT + +public: + explicit MapLocationDialog(double x, double y, QWidget *parent = 0); + void drawPointOnMap(double x, double y); + void setCayoPerico(bool isCayoPerico); + bool propUpdated(); + double getXpos(); + double getYpos(); + ~MapLocationDialog(); + +protected: + void paintEvent(QPaintEvent *ev); + void mouseMoveEvent(QMouseEvent *ev); + void mousePressEvent(QMouseEvent *ev); + void mouseReleaseEvent(QMouseEvent *ev); + void wheelEvent(QWheelEvent *ev); + +private slots: + void on_cmdApply_clicked(); + void on_cmdDone_clicked(); + void on_cmdClose_clicked(); + void on_cmdChange_clicked(); + void on_cmdRevert_clicked(); + void updatePosFromEvent(double x, double y); + +private: + int zoomPercent; + double xpos_old; + double ypos_old; + double xpos_new; + double ypos_new; + bool dragStart; + bool propUpdate; + bool changeMode; + bool p_isCayoPerico; + QImage mapImage; + QPointF dragPosition; + QPointF mapDiffPosition; + Ui::MapLocationDialog *ui; +}; + +#endif // MAPLOCATIONDIALOG_H diff --git a/MapLocationDialog.ui b/MapLocationDialog.ui new file mode 100644 index 0000000..06da9ed --- /dev/null +++ b/MapLocationDialog.ui @@ -0,0 +1,238 @@ + + + MapLocationDialog + + + + 0 + 0 + 500 + 600 + + + + + 500 + 600 + + + + + 500 + 600 + + + + Snapmatic Map Viewer + + + QDialog#MapLocationDialog { + background-color: transparent; +} + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QLabel{ +color: rgb(255, 255, 255); +} + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::NoFocus + + + Close viewer + + + &Close + + + false + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Qt::NoFocus + + + Apply new position + + + &Apply + + + false + + + + + + + Qt::NoFocus + + + Revert old position + + + &Revert + + + false + + + + + + + Qt::NoFocus + + + Select new position + + + &Select + + + false + + + + + + + Qt::NoFocus + + + Quit select position + + + &Done + + + false + + + + + + + + + + + + diff --git a/MessageThread.cpp b/MessageThread.cpp new file mode 100644 index 0000000..2781ac6 --- /dev/null +++ b/MessageThread.cpp @@ -0,0 +1,111 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2020 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "TranslationClass.h" +#include "MessageThread.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +MessageThread::MessageThread(uint cacheId, QObject *parent) : QThread(parent), cacheId(cacheId) +{ + threadRunning = true; +} + +void MessageThread::run() +{ + QEventLoop threadLoop; + + QObject::connect(this, SIGNAL(threadTerminated()), &threadLoop, SLOT(quit())); + + while (threadRunning) { + { +#ifdef GTA5SYNC_MOTD_WEBURL + QUrl motdWebUrl = QUrl(GTA5SYNC_MOTD_WEBURL); +#else + QUrl motdWebUrl = QUrl("https://motd.syping.de/gta5view-dev/"); +#endif + QUrlQuery urlQuery(motdWebUrl); + urlQuery.addQueryItem("code", GTA5SYNC_BUILDCODE); + urlQuery.addQueryItem("cacheid", QString::number(cacheId)); + urlQuery.addQueryItem("lang", Translator->getCurrentLanguage()); + urlQuery.addQueryItem("version", GTA5SYNC_APPVER); + motdWebUrl.setQuery(urlQuery); + + QNetworkAccessManager *netManager = new QNetworkAccessManager(); + QNetworkRequest netRequest(motdWebUrl); + netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent()); + QNetworkReply *netReply = netManager->get(netRequest); + + QEventLoop downloadLoop; + QObject::connect(netManager, SIGNAL(finished(QNetworkReply*)), &downloadLoop, SLOT(quit())); + QObject::connect(this, SIGNAL(threadTerminated()), &threadLoop, SLOT(quit())); + QTimer::singleShot(60000, &downloadLoop, SLOT(quit())); + downloadLoop.exec(); + + if (netReply->isFinished()) { + QByteArray jsonContent = netReply->readAll(); + QString headerData = QString::fromUtf8(netReply->rawHeader("gta5view")); + if (!headerData.isEmpty()) { + QMap headerMap; + const QStringList headerVarList = headerData.split(';'); + for (QString headerVar : headerVarList) { + QStringList varValueList = headerVar.split('='); + if (varValueList.length() >= 2) { + const QString variable = varValueList.at(0).trimmed(); + varValueList.removeFirst(); + const QString value = varValueList.join('='); + headerMap.insert(variable, value); + } + } + if (headerMap.value("update", "false") == "true") { + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonContent); + emit messagesArrived(jsonDocument.object()); + } + if (headerMap.contains("cache")) { + bool uintOk; + uint cacheVal = headerMap.value("cache").toUInt(&uintOk); + if (uintOk) { + cacheId = cacheVal; + emit updateCacheId(cacheId); + } + } + } + } + + delete netReply; + delete netManager; + } + + QTimer::singleShot(300000, &threadLoop, SLOT(quit())); + threadLoop.exec(); + } +} + +void MessageThread::terminateThread() +{ + threadRunning = false; + emit threadTerminated(); +} diff --git a/MessageThread.h b/MessageThread.h new file mode 100644 index 0000000..fa2d25b --- /dev/null +++ b/MessageThread.h @@ -0,0 +1,48 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2020 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef MESSAGETHREAD_H +#define MESSAGETHREAD_H + +#include +#include +#include + +class MessageThread : public QThread +{ + Q_OBJECT +public: + explicit MessageThread(uint cacheId, QObject *parent = 0); + +public slots: + void terminateThread(); + +private: + bool threadRunning; + uint cacheId; + +protected: + void run(); + +signals: + void messagesArrived(const QJsonObject &messageObject); + void updateCacheId(uint cacheId); + void threadTerminated(); +}; + +#endif // MESSAGETHREAD_H diff --git a/OptionsDialog.cpp b/OptionsDialog.cpp old mode 100755 new mode 100644 index f42c3c3..d096383 --- a/OptionsDialog.cpp +++ b/OptionsDialog.cpp @@ -1,457 +1,745 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "OptionsDialog.h" -#include "ui_OptionsDialog.h" -#include "StandardPaths.h" -#include "UserInterface.h" -#include "AppEnv.h" -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -OptionsDialog::OptionsDialog(ProfileDatabase *profileDB, QWidget *parent) : - QDialog(parent), profileDB(profileDB), - ui(new Ui::OptionsDialog) -{ - // Set Window Flags - setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); - - // Setup User Interface - ui->setupUi(this); - ui->tabWidget->setCurrentIndex(0); - ui->labPicCustomRes->setVisible(false); - - QRect desktopResolution = QApplication::desktop()->screenGeometry(parent); - int desktopSizeWidth = desktopResolution.width(); - int desktopSizeHeight = desktopResolution.height(); - aspectRatio = Qt::KeepAspectRatio; - defExportSize = QSize(960, 536); - cusExportSize = defExportSize; - defaultQuality = 100; - customQuality = 100; - contentMode = 0; - settings = new QSettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - - percentString = ui->labPicQuality->text(); - ui->labPicQuality->setText(percentString.arg(QString::number(defaultQuality))); - ui->rbPicDesktopRes->setText(ui->rbPicDesktopRes->text().arg(QString::number(desktopSizeWidth), QString::number(desktopSizeHeight))); - ui->rbPicDefaultRes->setText(ui->rbPicDefaultRes->text().arg(QString::number(defExportSize.width()), QString::number(defExportSize.height()))); - - if (QIcon::hasThemeIcon("dialog-ok")) - { - ui->cmdOK->setIcon(QIcon::fromTheme("dialog-ok")); - } - if (QIcon::hasThemeIcon("dialog-cancel")) - { - ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel")); - } - - // DPI calculation - qreal screenRatio = AppEnv::screenRatio(); - resize(435 * screenRatio, 405 * screenRatio); - - setupTreeWidget(); - setupLanguageBox(); - setupRadioButtons(); - setupDefaultProfile(); - setupPictureSettings(); - setupCustomGTAFolder(); - setupSnapmaticPictureViewer(); - -#ifdef GTA5SYNC_DISABLED - ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabSync)); -#endif - - this->setWindowTitle(windowTitle().arg(GTA5SYNC_APPSTR)); -} - -OptionsDialog::~OptionsDialog() -{ - delete settings; - foreach(QTreeWidgetItem *playerItem, playerItems) - { - delete playerItem; - } - delete ui; -} - -void OptionsDialog::setupTreeWidget() -{ - foreach(const QString &playerIDStr, profileDB->getPlayers()) - { - bool ok; - int playerID = playerIDStr.toInt(&ok); - if (ok) - { - QString playerName = profileDB->getPlayerName(playerID); - - QStringList playerTreeViewList; - playerTreeViewList << playerIDStr; - playerTreeViewList << playerName; - - QTreeWidgetItem *playerItem = new QTreeWidgetItem(playerTreeViewList); - ui->twPlayers->addTopLevelItem(playerItem); - playerItems.append(playerItem); - } - } - ui->twPlayers->sortItems(1, Qt::AscendingOrder); -} - -void OptionsDialog::setupLanguageBox() -{ - settings->beginGroup("Interface"); - currentLanguage = settings->value("Language","System").toString(); - settings->endGroup(); - - QStringList langList = QLocale::system().name().split("_"); - if (langList.length() > 0) - { - QString cbSysStr = tr("%1 (%2 if available)", "System like PC System = %1, System Language like Deutsch = %2").arg(tr("System", - "System like PC System"), QLocale::languageToString(QLocale(langList.at(0)).language())); - ui->cbLanguage->addItem(cbSysStr, "System"); - } - - QString cbEngStr = "English (English) [en]"; - ui->cbLanguage->addItem(QIcon::fromTheme("flag-us"), cbEngStr, "en"); - if (currentLanguage == "en") - { -#if QT_VERSION >= 0x050000 - ui->cbLanguage->setCurrentText(cbEngStr); -#else - int indexOfEnglish = ui->cbLanguage->findText(cbEngStr); - ui->cbLanguage->setCurrentIndex(indexOfEnglish); -#endif - } - - QDir langDir; - langDir.setNameFilters(QStringList("gta5sync_*.qm")); - langDir.setPath(AppEnv::getLangFolder()); - QStringList langFiles; - langFiles << langDir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::NoSort); - langDir.setPath(":/tr"); - langFiles << langDir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::NoSort); - langFiles.removeDuplicates(); - - foreach(const QString &langFile, langFiles) - { - QString lang = langFile; - lang.remove("gta5sync_"); - lang.remove(".qm"); - - QLocale langLocale(lang); - QString languageNameInternational = QLocale::languageToString(langLocale.language()); - QString languageNameNative = langLocale.nativeLanguageName(); - - QString cbLangStr = languageNameNative + " (" + languageNameInternational + ") [" + lang + "]"; - QString langIconStr = "flag-" + lang; - - ui->cbLanguage->addItem(QIcon::fromTheme(langIconStr), cbLangStr, lang); - if (currentLanguage == lang) - { -#if QT_VERSION >= 0x050000 - ui->cbLanguage->setCurrentText(cbLangStr); -#else - int indexOfLang = ui->cbLanguage->findText(cbLangStr); - ui->cbLanguage->setCurrentIndex(indexOfLang); -#endif - } - } -} - -void OptionsDialog::setupRadioButtons() -{ - bool contentModeOk; - settings->beginGroup("Profile"); - contentMode = settings->value("ContentMode", 0).toInt(&contentModeOk); - settings->endGroup(); - - if (contentModeOk) - { - if (contentMode == 0) - { - ui->rbOpenWithSC->setChecked(true); - } - else if (contentMode == 1) - { - ui->rbOpenWithDC->setChecked(true); - } - else if (contentMode == 2) - { - ui->rbSelectWithSC->setChecked(true); - } - } -} - -void OptionsDialog::on_cmdOK_clicked() -{ - applySettings(); - close(); -} - -void OptionsDialog::applySettings() -{ - settings->beginGroup("Interface"); -#if QT_VERSION >= 0x050000 - settings->setValue("Language", ui->cbLanguage->currentData()); -#else - settings->setValue("Language", ui->cbLanguage->itemData(ui->cbLanguage->currentIndex())); -#endif -#ifdef GTA5SYNC_WIN -#if QT_VERSION >= 0x050200 - settings->setValue("NavigationBar", ui->cbSnapmaticNavigationBar->isChecked()); -#endif -#endif - settings->endGroup(); - - settings->beginGroup("Profile"); - int newContentMode = 0; - if (ui->rbOpenWithSC->isChecked()) - { - newContentMode = 0; - } - else if (ui->rbOpenWithDC->isChecked()) - { - newContentMode = 1; - } - else if (ui->rbSelectWithSC->isChecked()) - { - newContentMode = 2; - } - settings->setValue("ContentMode", newContentMode); -#if QT_VERSION >= 0x050000 - settings->setValue("Default", ui->cbProfiles->currentData()); -#else - settings->setValue("Default", ui->cbProfiles->itemData(ui->cbProfiles->currentIndex())); -#endif - settings->endGroup(); - - settings->beginGroup("Pictures"); - if (ui->cbPicCustomQuality->isChecked()) - { - settings->setValue("CustomQuality", ui->hsPicQuality->value()); - } - settings->setValue("CustomQualityEnabled", ui->cbPicCustomQuality->isChecked()); - QString sizeMode = "Default"; - if (ui->rbPicDesktopRes->isChecked()) - { - sizeMode = "Desktop"; - } - else if (ui->rbPicCustomRes->isChecked()) - { - sizeMode = "Custom"; - settings->setValue("CustomSize", QSize(ui->sbPicExportWidth->value(), ui->sbPicExportHeight->value())); - } - settings->setValue("ExportSizeMode", sizeMode); - settings->setValue("AspectRatio", aspectRatio); - settings->endGroup(); - - bool forceCustomFolder = ui->cbForceCustomFolder->isChecked(); - settings->beginGroup("dir"); - settings->setValue("dir", ui->txtFolder->text()); - settings->setValue("force", forceCustomFolder); - settings->endGroup(); - -#if QT_VERSION >= 0x050000 - emit settingsApplied(newContentMode, ui->cbLanguage->currentData().toString()); -#else - emit settingsApplied(newContentMode, ui->cbLanguage->itemData(ui->cbLanguage->currentIndex()).toString()); -#endif - -#if QT_VERSION >= 0x050000 - bool languageChanged = ui->cbLanguage->currentData().toString() != currentLanguage; -#else - bool languageChanged = ui->cbLanguage->itemData(ui->cbLanguage->currentIndex()).toString() != currentLanguage; -#endif - - if ((forceCustomFolder && ui->txtFolder->text() != currentCFolder) || (forceCustomFolder != currentFFolder && forceCustomFolder)) - { - QMessageBox::information(this, tr("%1", "%1").arg(GTA5SYNC_APPSTR), tr("The new Custom Folder will initialize after you restart %1.").arg(GTA5SYNC_APPSTR)); - } - if (languageChanged) - { - QMessageBox::information(this, tr("%1", "%1").arg(GTA5SYNC_APPSTR), tr("The language change will take effect after you restart %1.").arg(GTA5SYNC_APPSTR)); - } -} - -void OptionsDialog::setupDefaultProfile() -{ - settings->beginGroup("Profile"); - defaultProfile = settings->value("Default", "").toString(); - settings->endGroup(); - - QString cbNoneStr = tr("No Profile", "No Profile, as default"); - ui->cbProfiles->addItem(cbNoneStr, ""); -} - -void OptionsDialog::commitProfiles(QStringList profiles) -{ - foreach(const QString &profile, profiles) - { - ui->cbProfiles->addItem(tr("Profile: %1").arg(profile), profile); - if (defaultProfile == profile) - { -#if QT_VERSION >= 0x050000 - ui->cbProfiles->setCurrentText(tr("Profile: %1").arg(profile)); -#else - int indexOfProfile = ui->cbProfiles->findText(tr("Profile: %1").arg(profile)); - ui->cbProfiles->setCurrentIndex(indexOfProfile); -#endif - } - } -} - -void OptionsDialog::on_rbPicCustomRes_toggled(bool checked) -{ - ui->labPicCustomRes->setEnabled(checked); - ui->sbPicExportWidth->setEnabled(checked); - ui->sbPicExportHeight->setEnabled(checked); - ui->labPicXDescription->setEnabled(checked); -} - -void OptionsDialog::on_cbPicCustomQuality_toggled(bool checked) -{ - ui->hsPicQuality->setEnabled(checked); - ui->labPicQuality->setEnabled(checked); - ui->labPicQualityDescription->setEnabled(checked); -} - -void OptionsDialog::on_hsPicQuality_valueChanged(int value) -{ - customQuality = value; - ui->labPicQuality->setText(percentString.arg(QString::number(value))); -} - -void OptionsDialog::setupPictureSettings() -{ - settings->beginGroup("Pictures"); - - // Quality Settings - customQuality = settings->value("CustomQuality", defaultQuality).toInt(); - if (customQuality < 1 || customQuality > 100) - { - customQuality = 100; - } - ui->hsPicQuality->setValue(customQuality); - ui->cbPicCustomQuality->setChecked(settings->value("CustomQualityEnabled", false).toBool()); - - // Size Settings - cusExportSize = settings->value("CustomSize", defExportSize).toSize(); - if (cusExportSize.width() > 3840) - { - cusExportSize.setWidth(3840); - } - else if (cusExportSize.height() > 2160) - { - cusExportSize.setHeight(2160); - } - if (cusExportSize.width() < 1) - { - cusExportSize.setWidth(1); - } - else if (cusExportSize.height() < 1) - { - cusExportSize.setHeight(1); - } - ui->sbPicExportWidth->setValue(cusExportSize.width()); - ui->sbPicExportHeight->setValue(cusExportSize.height()); - - QString sizeMode = settings->value("ExportSizeMode", "Default").toString(); - if (sizeMode == "Desktop") - { - ui->rbPicDesktopRes->setChecked(true); - } - else if (sizeMode == "Custom") - { - ui->rbPicCustomRes->setChecked(true); - } - else - { - ui->rbPicDefaultRes->setChecked(true); - } - - aspectRatio = (Qt::AspectRatioMode)settings->value("AspectRatio", Qt::KeepAspectRatio).toInt(); - if (aspectRatio == Qt::IgnoreAspectRatio) - { - ui->cbIgnoreAspectRatio->setChecked(true); - } - - settings->endGroup(); -} - -void OptionsDialog::on_cbIgnoreAspectRatio_toggled(bool checked) -{ - if (checked) - { - aspectRatio = Qt::IgnoreAspectRatio; - } - else - { - aspectRatio = Qt::KeepAspectRatio; - } -} - -void OptionsDialog::setupCustomGTAFolder() -{ - bool ok; - QString defaultGameFolder = AppEnv::getGameFolder(&ok); - settings->beginGroup("dir"); - currentCFolder = settings->value("dir", "").toString(); - currentFFolder = settings->value("force", false).toBool(); - if (currentCFolder == "" && ok) - { - currentCFolder = defaultGameFolder; - } - ui->txtFolder->setText(currentCFolder); - ui->cbForceCustomFolder->setChecked(currentFFolder); - settings->endGroup(); -} - -void OptionsDialog::setupSnapmaticPictureViewer() -{ -#ifdef GTA5SYNC_WIN -#if QT_VERSION >= 0x050200 - settings->beginGroup("Interface"); - ui->cbSnapmaticNavigationBar->setChecked(settings->value("NavigationBar", false).toBool()); - settings->endGroup(); -#else - ui->cbSnapmaticNavigationBar->setVisible(false); - ui->gbSnapmaticPictureViewer->setVisible(false); -#endif -#else - ui->cbSnapmaticNavigationBar->setVisible(false); - ui->gbSnapmaticPictureViewer->setVisible(false); -#endif -} - -void OptionsDialog::on_cmdExploreFolder_clicked() -{ - QString GTAV_Folder = QFileDialog::getExistingDirectory(this, UserInterface::tr("Select GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly); - if (QFileInfo(GTAV_Folder).exists()) - { - ui->txtFolder->setText(GTAV_Folder); - } -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "OptionsDialog.h" +#include "ui_OptionsDialog.h" +#include "TranslationClass.h" +#include "StandardPaths.h" +#include "UserInterface.h" +#include "wrapper.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x050000 +#include +#else +#include +#endif + +#ifdef GTA5SYNC_TELEMETRY +#include "TelemetryClass.h" +#endif + +OptionsDialog::OptionsDialog(ProfileDatabase *profileDB, QWidget *parent) : + QDialog(parent), profileDB(profileDB), + ui(new Ui::OptionsDialog) +{ + // Set Window Flags +#if QT_VERSION >= 0x050900 + setWindowFlag(Qt::WindowContextHelpButtonHint, false); +#else + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); +#endif + + // Setup User Interface + ui->setupUi(this); + ui->tabWidget->setCurrentIndex(0); + ui->labPicCustomRes->setVisible(false); + ui->cmdCancel->setDefault(true); + ui->cmdCancel->setFocus(); + +#if QT_VERSION >= 0x050000 + qreal screenRatioPR = AppEnv::screenRatioPR(); + QRect desktopResolution = QApplication::primaryScreen()->geometry(); + int desktopSizeWidth = qRound((double)desktopResolution.width() * screenRatioPR); + int desktopSizeHeight = qRound((double)desktopResolution.height() * screenRatioPR); +#else + QRect desktopResolution = QApplication::desktop()->screenGeometry(this); + int desktopSizeWidth = desktopResolution.width(); + int desktopSizeHeight = desktopResolution.height(); +#endif + aspectRatio = Qt::KeepAspectRatio; + defExportSize = SnapmaticPicture::getSnapmaticResolution(); + cusExportSize = defExportSize; + defaultQuality = 100; + customQuality = 100; + contentMode = 0; + settings = new QSettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + + percentString = ui->labPicQuality->text(); + ui->labPicQuality->setText(percentString.arg(QString::number(defaultQuality))); + ui->rbPicDesktopRes->setText(ui->rbPicDesktopRes->text().arg(QString::number(desktopSizeWidth), QString::number(desktopSizeHeight))); + ui->rbPicDefaultRes->setText(ui->rbPicDefaultRes->text().arg(QString::number(defExportSize.width()), QString::number(defExportSize.height()))); + + // Set Icon for OK Button + if (QIcon::hasThemeIcon("dialog-ok")) { + ui->cmdOK->setIcon(QIcon::fromTheme("dialog-ok")); + } + else if (QIcon::hasThemeIcon("gtk-ok")) { + ui->cmdOK->setIcon(QIcon::fromTheme("gtk-ok")); + } + + // Set Icon for Cancel Button + if (QIcon::hasThemeIcon("dialog-cancel")) { + ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel")); + } + else if (QIcon::hasThemeIcon("gtk-cancel")) { + ui->cmdCancel->setIcon(QIcon::fromTheme("gtk-cancel")); + } + + // Set Icon for Copy Button + if (QIcon::hasThemeIcon("edit-copy")) { + ui->cmdCopyStatsID->setIcon(QIcon::fromTheme("edit-copy")); + } + + setupTreeWidget(); + setupLanguageBox(); + setupRadioButtons(); + setupDefaultProfile(); + setupPictureSettings(); + setupCustomGTAFolder(); + setupInterfaceSettings(); + setupStatisticsSettings(); + setupSnapmaticPictureViewer(); + setupWindowsGameSettings(); + +#ifndef Q_QS_ANDROID + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + resize(435 * screenRatio, 405 * screenRatio); +#endif + + ui->rbModern->setText(ui->rbModern->text().arg(GTA5SYNC_APPSTR)); + ui->rbClassic->setText(ui->rbClassic->text().arg(GTA5SYNC_APPSTR)); + setWindowTitle(windowTitle().arg(GTA5SYNC_APPSTR)); +} + +OptionsDialog::~OptionsDialog() +{ + delete settings; + qDeleteAll(playerItems.begin(), playerItems.end()); + playerItems.clear(); + delete ui; +} + +void OptionsDialog::setupTreeWidget() +{ + const QStringList players = profileDB->getPlayers(); + if (players.length() != 0) { + for (auto it = players.constBegin(); it != players.constEnd(); it++) { + bool ok; + int playerID = it->toInt(&ok); + if (ok) { + const QString playerName = profileDB->getPlayerName(playerID); + + QStringList playerTreeViewList; + playerTreeViewList += *it; + playerTreeViewList += playerName; + + QTreeWidgetItem *playerItem = new QTreeWidgetItem(playerTreeViewList); + ui->twPlayers->addTopLevelItem(playerItem); + playerItems += playerItem; + } + } + ui->twPlayers->sortItems(1, Qt::AscendingOrder); + } + else { + ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabPlayers)); + } +} + +void OptionsDialog::setupLanguageBox() +{ + settings->beginGroup("Interface"); + currentLanguage = settings->value("Language", "System").toString(); + currentAreaLanguage = settings->value("AreaLanguage", "Auto").toString(); + settings->endGroup(); + + const QString cbSysStr = tr("%1 (Language priority)", "First language a person can talk with a different person/application. \"Native\" or \"Not Native\".").arg(tr("System", + "System in context of System default")); +#ifdef Q_OS_WIN + QString cbAutoStr; + if (AppEnv::getGameLanguage(AppEnv::getGameVersion()) != GameLanguage::Undefined) { + cbAutoStr = tr("%1 (Game language)", "Next closest language compared to the Game settings").arg(tr("Auto", "Automatic language choice.")); + } + else { + cbAutoStr = tr("%1 (Closest to Interface)", "Next closest language compared to the Interface").arg(tr("Auto", "Automatic language choice.")); + } +#else + const QString cbAutoStr = tr("%1 (Closest to Interface)", "Next closest language compared to the Interface").arg(tr("Auto", "Automatic language choice.")); +#endif + ui->cbLanguage->addItem(cbSysStr, "System"); + ui->cbAreaLanguage->addItem(cbAutoStr, "Auto"); + + QStringList availableLanguages; + availableLanguages << QString("en_GB"); +#ifndef GTA5SYNC_QCONF + availableLanguages << TranslationClass::listTranslations(AppEnv::getExLangFolder()); +#endif + availableLanguages << TranslationClass::listTranslations(AppEnv::getInLangFolder()); + availableLanguages.removeDuplicates(); + availableLanguages.sort(); + + for (const QString &lang : qAsConst(availableLanguages)) { + QLocale langLocale(lang); + const QString cbLangStr = langLocale.nativeLanguageName() % " (" % langLocale.nativeCountryName() % ") [" % lang % "]"; + const QString langIconPath = AppEnv::getImagesFolder() % "/flag-" % TranslationClass::getCountryCode(langLocale) % ".png"; + + if (QFile::exists(langIconPath)) { + ui->cbLanguage->addItem(QIcon(langIconPath), cbLangStr, lang); + } + else { + ui->cbLanguage->addItem(cbLangStr, lang); + } + if (currentLanguage == lang) { +#if QT_VERSION >= 0x050000 + ui->cbLanguage->setCurrentText(cbLangStr); +#else + int indexOfLang = ui->cbLanguage->findText(cbLangStr); + ui->cbLanguage->setCurrentIndex(indexOfLang); +#endif + } + } + + QString aCurrentLanguage = QString("en_GB"); + if (Translator->isLanguageLoaded()) + aCurrentLanguage = Translator->getCurrentLanguage(); + QLocale currentLocale = QLocale(aCurrentLanguage); + ui->labCurrentLanguage->setText(tr("Current: %1").arg(currentLocale.nativeLanguageName() % " (" % currentLocale.nativeCountryName() % ") [" % aCurrentLanguage % "]")); + + availableLanguages.clear(); + availableLanguages << TranslationClass::listAreaTranslations(); + availableLanguages.removeDuplicates(); + availableLanguages.sort(); + + for (const QString &lang : qAsConst(availableLanguages)) { + // correcting Language Location if possible + QString aLang = lang; + if (QFile::exists(":/global/global." % lang % ".loc")) { + QFile locFile(":/global/global." % lang % ".loc"); + if (locFile.open(QFile::ReadOnly)) { + aLang = QString::fromUtf8(locFile.readLine()).trimmed(); + locFile.close(); + } + } + + QLocale langLocale(aLang); + const QString cbLangStr = langLocale.nativeLanguageName() % " (" % langLocale.nativeCountryName() % ") [" % aLang % "]"; + ui->cbAreaLanguage->addItem(cbLangStr, lang); + if (currentAreaLanguage == lang) { +#if QT_VERSION >= 0x050000 + ui->cbAreaLanguage->setCurrentText(cbLangStr); +#else + int indexOfLang = ui->cbAreaLanguage->findText(cbLangStr); + ui->cbAreaLanguage->setCurrentIndex(indexOfLang); +#endif + } + } + + QString aCurrentAreaLanguage = Translator->getCurrentAreaLanguage(); + if (QFile::exists(":/global/global." % aCurrentAreaLanguage % ".loc")) { + qDebug() << "locFile found"; + QFile locFile(":/global/global." % aCurrentAreaLanguage % ".loc"); + if (locFile.open(QFile::ReadOnly)) { + aCurrentAreaLanguage = QString::fromUtf8(locFile.readLine()).trimmed(); + locFile.close(); + } + } + currentLocale = QLocale(aCurrentAreaLanguage); + ui->labCurrentAreaLanguage->setText(tr("Current: %1").arg(currentLocale.nativeLanguageName() % " (" % currentLocale.nativeCountryName() % ") [" % aCurrentAreaLanguage % "]")); +} + +void OptionsDialog::setupRadioButtons() +{ + bool contentModeOk; + settings->beginGroup("Profile"); + contentMode = settings->value("ContentMode", 0).toInt(&contentModeOk); + settings->endGroup(); + + if (contentModeOk) { + switch (contentMode) { + case 0: + case 20: + ui->rbModern->setChecked(true); + ui->cbDoubleclick->setChecked(false); + break; + case 1: + case 2: + case 21: + ui->rbModern->setChecked(true); + ui->cbDoubleclick->setChecked(true); + break; + case 10: + ui->rbClassic->setChecked(true); + ui->cbDoubleclick->setChecked(false); + break; + case 11: + ui->rbClassic->setChecked(true); + ui->cbDoubleclick->setChecked(true); + break; + } + } +} + +void OptionsDialog::setupInterfaceSettings() +{ + settings->beginGroup("Startup"); + const QString currentStyle = QApplication::style()->objectName(); + const QString appStyle = settings->value("AppStyle", currentStyle).toString(); + bool customStyle = settings->value("CustomStyle", false).toBool(); + const QStringList availableStyles = QStyleFactory::keys(); + ui->cbStyleList->addItems(availableStyles); + if (availableStyles.contains(appStyle, Qt::CaseInsensitive)) { + // use 'for' for select to be sure it's case insensitive + int currentIndex = 0; + for (const QString ¤tStyleFF : availableStyles) { + if (currentStyleFF.toLower() == appStyle.toLower()) { + ui->cbStyleList->setCurrentIndex(currentIndex); + } + currentIndex++; + } + } + else { + if (availableStyles.contains(currentStyle, Qt::CaseInsensitive)) { + int currentIndex = 0; + for (const QString ¤tStyleFF : availableStyles) { + if (currentStyleFF.toLower() == currentStyle.toLower()) { + ui->cbStyleList->setCurrentIndex(currentIndex); + } + currentIndex++; + } + } + } + ui->cbDefaultStyle->setChecked(!customStyle); + ui->cbStyleList->setEnabled(customStyle); + const QFont currentFont = QApplication::font(); + const QFont appFont = qvariant_cast(settings->value("AppFont", currentFont)); + bool customFont = settings->value("CustomFont", false).toBool(); + ui->cbDefaultFont->setChecked(!customFont); + ui->cbFont->setEnabled(customFont); + ui->cbFont->setCurrentFont(appFont); + settings->endGroup(); +} + +void OptionsDialog::on_cmdOK_clicked() +{ + applySettings(); + close(); +} + +void OptionsDialog::applySettings() +{ + settings->beginGroup("Interface"); +#if QT_VERSION >= 0x050000 + settings->setValue("Language", ui->cbLanguage->currentData()); + settings->setValue("AreaLanguage", ui->cbAreaLanguage->currentData()); +#else + settings->setValue("Language", ui->cbLanguage->itemData(ui->cbLanguage->currentIndex())); + settings->setValue("AreaLanguage", ui->cbAreaLanguage->itemData(ui->cbAreaLanguage->currentIndex())); +#endif +#ifdef Q_OS_WIN +#if QT_VERSION >= 0x050200 + settings->setValue("NavigationBar", ui->cbSnapmaticNavigationBar->isChecked()); +#endif +#else + settings->setValue("NavigationBar", ui->cbSnapmaticNavigationBar->isChecked()); +#endif + settings->endGroup(); + + settings->beginGroup("Profile"); + int newContentMode = 20; + if (ui->rbModern->isChecked()) { + newContentMode = 20; + } + else if (ui->rbClassic->isChecked()) { + newContentMode = 10; + } + if (ui->cbDoubleclick->isChecked()) { + newContentMode++; + } + settings->setValue("ContentMode", newContentMode); +#if QT_VERSION >= 0x050000 + settings->setValue("Default", ui->cbProfiles->currentData()); +#else + settings->setValue("Default", ui->cbProfiles->itemData(ui->cbProfiles->currentIndex())); +#endif + settings->endGroup(); + + settings->beginGroup("Pictures"); + if (ui->cbPicCustomQuality->isChecked()) { + settings->setValue("CustomQuality", ui->hsPicQuality->value()); + } + settings->setValue("CustomQualityEnabled", ui->cbPicCustomQuality->isChecked()); + QString sizeMode = "Default"; + if (ui->rbPicDesktopRes->isChecked()) { + sizeMode = "Desktop"; + } + else if (ui->rbPicCustomRes->isChecked()) { + sizeMode = "Custom"; + settings->setValue("CustomSize", QSize(ui->sbPicExportWidth->value(), ui->sbPicExportHeight->value())); + } + settings->setValue("ExportSizeMode", sizeMode); + settings->setValue("AspectRatio", aspectRatio); + settings->endGroup(); + + bool forceCustomFolder = ui->cbForceCustomFolder->isChecked(); + settings->beginGroup("dir"); + settings->setValue("dir", ui->txtFolder->text()); + settings->setValue("force", forceCustomFolder); + settings->endGroup(); + + bool defaultStyle = ui->cbDefaultStyle->isChecked(); + settings->beginGroup("Startup"); + if (!defaultStyle) { + QString newStyle = ui->cbStyleList->currentText(); + settings->setValue("CustomStyle", true); + settings->setValue("AppStyle", newStyle); + QApplication::setStyle(QStyleFactory::create(newStyle)); + } + else { + settings->setValue("CustomStyle", false); + } + bool defaultFont = ui->cbDefaultFont->isChecked(); + if (!defaultFont) { + QFont newFont = ui->cbFont->currentFont(); + settings->setValue("CustomFont", true); + settings->setValue("AppFont", newFont); + QApplication::setFont(newFont); + } + else { + settings->setValue("CustomFont", false); + } + settings->endGroup(); + +#ifdef GTA5SYNC_TELEMETRY + settings->beginGroup("Telemetry"); + settings->setValue("PushAppConf", ui->cbAppConfigStats->isChecked()); + settings->setValue("PushUsageData", ui->cbUsageData->isChecked()); + if (!Telemetry->isStateForced()) { settings->setValue("IsEnabled", ui->cbParticipateStats->isChecked()); } + settings->endGroup(); + Telemetry->refresh(); + Telemetry->work(); + if (ui->cbUsageData->isChecked() && Telemetry->canPush()) { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "SettingsUpdated"; +#if QT_VERSION >= 0x060000 + jsonObject["UpdateTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["UpdateTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } +#endif + +#if QT_VERSION >= 0x050000 + bool languageChanged = ui->cbLanguage->currentData().toString() != currentLanguage; + bool languageAreaChanged = ui->cbAreaLanguage->currentData().toString() != currentAreaLanguage; +#else + bool languageChanged = ui->cbLanguage->itemData(ui->cbLanguage->currentIndex()).toString() != currentLanguage; + bool languageAreaChanged = ui->cbAreaLanguage->itemData(ui->cbLanguage->currentIndex()).toString() != currentAreaLanguage; +#endif + if (languageChanged) { + Translator->unloadTranslation(qApp); + Translator->initUserLanguage(); + Translator->loadTranslation(qApp); + } + else if (languageAreaChanged) { + Translator->initUserLanguage(); + } + + settings->sync(); + emit settingsApplied(newContentMode, languageChanged); + + if ((forceCustomFolder && ui->txtFolder->text() != currentCFolder) || (forceCustomFolder != currentFFolder && forceCustomFolder)) { + QMessageBox::information(this, tr("%1", "%1").arg(GTA5SYNC_APPSTR), tr("The new Custom Folder will initialise after you restart %1.").arg(GTA5SYNC_APPSTR)); + } +} + +void OptionsDialog::setupDefaultProfile() +{ + settings->beginGroup("Profile"); + defaultProfile = settings->value("Default", "").toString(); + settings->endGroup(); + + QString cbNoneStr = tr("No Profile", "No Profile, as default"); + ui->cbProfiles->addItem(cbNoneStr, ""); +} + +void OptionsDialog::commitProfiles(const QStringList &profiles) +{ + for (const QString &profile : profiles) { + ui->cbProfiles->addItem(tr("Profile: %1").arg(profile), profile); + if (defaultProfile == profile) { +#if QT_VERSION >= 0x050000 + ui->cbProfiles->setCurrentText(tr("Profile: %1").arg(profile)); +#else + int indexOfProfile = ui->cbProfiles->findText(tr("Profile: %1").arg(profile)); + ui->cbProfiles->setCurrentIndex(indexOfProfile); +#endif + } + } +} + +void OptionsDialog::on_rbPicCustomRes_toggled(bool checked) +{ + ui->labPicCustomRes->setEnabled(checked); + ui->sbPicExportWidth->setEnabled(checked); + ui->sbPicExportHeight->setEnabled(checked); + ui->labPicXDescription->setEnabled(checked); +} + +void OptionsDialog::on_cbPicCustomQuality_toggled(bool checked) +{ + ui->hsPicQuality->setEnabled(checked); + ui->labPicQuality->setEnabled(checked); + ui->labPicQualityDescription->setEnabled(checked); +} + +void OptionsDialog::on_hsPicQuality_valueChanged(int value) +{ + customQuality = value; + ui->labPicQuality->setText(percentString.arg(QString::number(value))); +} + +void OptionsDialog::setupPictureSettings() +{ + settings->beginGroup("Pictures"); + + // Quality Settings + customQuality = settings->value("CustomQuality", defaultQuality).toInt(); + if (customQuality < 1 || customQuality > 100) { + customQuality = 100; + } + ui->hsPicQuality->setValue(customQuality); + ui->cbPicCustomQuality->setChecked(settings->value("CustomQualityEnabled", false).toBool()); + + // Size Settings + cusExportSize = settings->value("CustomSize", defExportSize).toSize(); + if (cusExportSize.width() > 3840) { + cusExportSize.setWidth(3840); + } + else if (cusExportSize.height() > 2160) { + cusExportSize.setHeight(2160); + } + if (cusExportSize.width() < 1) { + cusExportSize.setWidth(1); + } + else if (cusExportSize.height() < 1) { + cusExportSize.setHeight(1); + } + ui->sbPicExportWidth->setValue(cusExportSize.width()); + ui->sbPicExportHeight->setValue(cusExportSize.height()); + + QString sizeMode = settings->value("ExportSizeMode", "Default").toString(); + if (sizeMode == "Desktop") { + ui->rbPicDesktopRes->setChecked(true); + } + else if (sizeMode == "Custom") { + ui->rbPicCustomRes->setChecked(true); + } + else { + ui->rbPicDefaultRes->setChecked(true); + } + + aspectRatio = (Qt::AspectRatioMode)settings->value("AspectRatio", Qt::KeepAspectRatio).toInt(); + if (aspectRatio == Qt::IgnoreAspectRatio) { + ui->cbIgnoreAspectRatio->setChecked(true); + } + + settings->endGroup(); +} + +void OptionsDialog::setupStatisticsSettings() +{ +#ifdef GTA5SYNC_TELEMETRY + ui->cbParticipateStats->setText(tr("Participate in %1 User Statistics").arg(GTA5SYNC_APPSTR)); + ui->labUserStats->setText(QString("%1").arg(tr("View %1 User Statistics Online").arg(GTA5SYNC_APPSTR), TelemetryClass::getWebURL().toString())); + + settings->beginGroup("Telemetry"); + ui->cbParticipateStats->setChecked(Telemetry->isEnabled()); + ui->cbAppConfigStats->setChecked(settings->value("PushAppConf", false).toBool()); + ui->cbUsageData->setChecked(settings->value("PushUsageData", false).toBool()); + settings->endGroup(); + + if (Telemetry->isStateForced()) { + ui->cbParticipateStats->setEnabled(false); + } + + if (Telemetry->isRegistered()) { + ui->labParticipationID->setText(tr("Participation ID: %1").arg(Telemetry->getRegisteredID())); + } + else { + ui->labParticipationID->setText(tr("Participation ID: %1").arg(tr("Not registered"))); + ui->cmdCopyStatsID->setVisible(false); + } +#else + ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabStats)); +#endif +} + +void OptionsDialog::setupWindowsGameSettings() +{ +#ifdef GTA5SYNC_GAME + GameVersion gameVersion = AppEnv::getGameVersion(); +#ifdef Q_OS_WIN + if (gameVersion != GameVersion::NoVersion) { + if (gameVersion == GameVersion::SocialClubVersion) { + ui->gbSteam->setDisabled(true); + ui->labSocialClubFound->setText(tr("Found: %1").arg(QString("%1").arg(tr("Yes")))); + ui->labSteamFound->setText(tr("Found: %1").arg(QString("%1").arg(tr("No")))); + if (AppEnv::getGameLanguage(GameVersion::SocialClubVersion) != GameLanguage::Undefined) { + ui->labSocialClubLanguage->setText(tr("Language: %1").arg(QLocale(AppEnv::gameLanguageToString(AppEnv::getGameLanguage(GameVersion::SocialClubVersion))).nativeLanguageName())); + } + else { + ui->labSocialClubLanguage->setText(tr("Language: %1").arg(tr("OS defined"))); + } + ui->labSteamLanguage->setVisible(false); + } + else if (gameVersion == GameVersion::SteamVersion) { + ui->gbSocialClub->setDisabled(true); + ui->labSocialClubFound->setText(tr("Found: %1").arg(QString("%1").arg(tr("No")))); + ui->labSteamFound->setText(tr("Found: %1").arg(QString("%1").arg(tr("Yes")))); + ui->labSocialClubLanguage->setVisible(false); + if (AppEnv::getGameLanguage(GameVersion::SteamVersion) != GameLanguage::Undefined) { + ui->labSteamLanguage->setText(tr("Language: %1").arg(QLocale(AppEnv::gameLanguageToString(AppEnv::getGameLanguage(GameVersion::SteamVersion))).nativeLanguageName())); + } + else { + ui->labSteamLanguage->setText(tr("Language: %1").arg(tr("Steam defined"))); + } + } + else { + ui->labSocialClubFound->setText(tr("Found: %1").arg(QString("%1").arg(tr("Yes")))); + ui->labSteamFound->setText(tr("Found: %1").arg(QString("%1").arg(tr("Yes")))); + if (AppEnv::getGameLanguage(GameVersion::SocialClubVersion) != GameLanguage::Undefined) { + ui->labSocialClubLanguage->setText(tr("Language: %1").arg(QLocale(AppEnv::gameLanguageToString(AppEnv::getGameLanguage(GameVersion::SocialClubVersion))).nativeLanguageName())); + } + else { + ui->labSocialClubLanguage->setText(tr("Language: %1").arg(tr("OS defined"))); + } + if (AppEnv::getGameLanguage(GameVersion::SteamVersion) != GameLanguage::Undefined) { + ui->labSteamLanguage->setText(tr("Language: %1").arg(QLocale(AppEnv::gameLanguageToString(AppEnv::getGameLanguage(GameVersion::SteamVersion))).nativeLanguageName())); + } + else { + ui->labSteamLanguage->setText(tr("Language: %1").arg(tr("Steam defined"))); + } + } + } + else { + ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabGame)); + } +#else + ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabGame)); +#endif +#else + ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabGame)); +#endif +} + +void OptionsDialog::on_cbIgnoreAspectRatio_toggled(bool checked) +{ + if (checked) { + aspectRatio = Qt::IgnoreAspectRatio; + } + else { + aspectRatio = Qt::KeepAspectRatio; + } +} + +void OptionsDialog::setupCustomGTAFolder() +{ + bool ok; + QString defaultGameFolder = AppEnv::getGameFolder(&ok); + settings->beginGroup("dir"); + currentCFolder = settings->value("dir", "").toString(); + currentFFolder = settings->value("force", false).toBool(); + if (currentCFolder == "" && ok) { + currentCFolder = defaultGameFolder; + } + ui->txtFolder->setText(currentCFolder); + ui->cbForceCustomFolder->setChecked(currentFFolder); + settings->endGroup(); +} + +void OptionsDialog::setupSnapmaticPictureViewer() +{ +#ifdef Q_OS_WIN +#if QT_VERSION >= 0x050200 + settings->beginGroup("Interface"); + ui->cbSnapmaticNavigationBar->setChecked(settings->value("NavigationBar", true).toBool()); + settings->endGroup(); +#else + ui->cbSnapmaticNavigationBar->setVisible(false); + ui->gbSnapmaticPictureViewer->setVisible(false); +#endif +#else + settings->beginGroup("Interface"); + ui->cbSnapmaticNavigationBar->setChecked(settings->value("NavigationBar", true).toBool()); + settings->endGroup(); +#endif +} + +void OptionsDialog::on_cmdExploreFolder_clicked() +{ + const QString GTAV_Folder = QFileDialog::getExistingDirectory(this, UserInterface::tr("Select GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly); + if (QDir(GTAV_Folder).exists()) { + ui->txtFolder->setText(GTAV_Folder); + } +} + +void OptionsDialog::on_cbDefaultStyle_toggled(bool checked) +{ + ui->cbStyleList->setDisabled(checked); + ui->labStyle->setDisabled(checked); +} + +void OptionsDialog::on_cbDefaultFont_toggled(bool checked) +{ + ui->cbFont->setDisabled(checked); + ui->cmdFont->setDisabled(checked); + ui->labFont->setDisabled(checked); +} + +void OptionsDialog::on_cmdCopyStatsID_clicked() +{ +#ifdef GTA5SYNC_TELEMETRY + QApplication::clipboard()->setText(Telemetry->getRegisteredID()); +#endif +} + +void OptionsDialog::on_cbFont_currentFontChanged(const QFont &font) +{ + ui->cbFont->setFont(font); +} + +void OptionsDialog::on_cmdFont_clicked() +{ + bool ok; + const QFont font = QFontDialog::getFont(&ok, ui->cbFont->currentFont(), this); + if (ok) { + ui->cbFont->setCurrentFont(font); + ui->cbFont->setFont(font); + } +} diff --git a/OptionsDialog.h b/OptionsDialog.h old mode 100755 new mode 100644 index 83aa3e3..f53854e --- a/OptionsDialog.h +++ b/OptionsDialog.h @@ -1,79 +1,89 @@ -/****************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef OPTIONSDIALOG_H -#define OPTIONSDIALOG_H - -#include -#include -#include -#include -#include -#include "ProfileDatabase.h" - -namespace Ui { -class OptionsDialog; -} - -class OptionsDialog : public QDialog -{ - Q_OBJECT - -public: - explicit OptionsDialog(ProfileDatabase *profileDB, QWidget *parent = 0); - void commitProfiles(QStringList profiles); - ~OptionsDialog(); - -private slots: - void on_cmdOK_clicked(); - void on_rbPicCustomRes_toggled(bool checked); - void on_cbPicCustomQuality_toggled(bool checked); - void on_hsPicQuality_valueChanged(int value); - void on_cbIgnoreAspectRatio_toggled(bool checked); - void on_cmdExploreFolder_clicked(); - -signals: - void settingsApplied(int contentMode, QString language); - -private: - ProfileDatabase *profileDB; - Ui::OptionsDialog *ui; - QList playerItems; - Qt::AspectRatioMode aspectRatio; - QString currentLanguage; - QString currentCFolder; - QString defaultProfile; - QString percentString; - QSettings *settings; - bool currentFFolder; - int contentMode; - int customQuality; - int defaultQuality; - QSize defExportSize; - QSize cusExportSize; - void setupTreeWidget(); - void setupLanguageBox(); - void setupRadioButtons(); - void setupDefaultProfile(); - void setupPictureSettings(); - void setupCustomGTAFolder(); - void setupSnapmaticPictureViewer(); - void applySettings(); -}; - -#endif // OPTIONSDIALOG_H +/****************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef OPTIONSDIALOG_H +#define OPTIONSDIALOG_H + +#include +#include +#include +#include +#include +#include "ProfileDatabase.h" + +namespace Ui { +class OptionsDialog; +} + +class OptionsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit OptionsDialog(ProfileDatabase *profileDB, QWidget *parent = 0); + void commitProfiles(const QStringList &profiles); + ~OptionsDialog(); + +private slots: + void on_cmdOK_clicked(); + void on_rbPicCustomRes_toggled(bool checked); + void on_cbPicCustomQuality_toggled(bool checked); + void on_hsPicQuality_valueChanged(int value); + void on_cbIgnoreAspectRatio_toggled(bool checked); + void on_cmdExploreFolder_clicked(); + void on_cbDefaultStyle_toggled(bool checked); + void on_cbDefaultFont_toggled(bool checked); + void on_cmdCopyStatsID_clicked(); + void on_cbFont_currentFontChanged(const QFont &font); + void on_cmdFont_clicked(); + +signals: + void settingsApplied(int contentMode, bool languageChanged); + +private: + ProfileDatabase *profileDB; + Ui::OptionsDialog *ui; + QList playerItems; + Qt::AspectRatioMode aspectRatio; + QString currentAreaLanguage; + QString currentLanguage; + QString currentCFolder; + QString defaultProfile; + QString percentString; + QSettings *settings; + bool withoutPlayers; + bool currentFFolder; + int contentMode; + int customQuality; + int defaultQuality; + QSize defExportSize; + QSize cusExportSize; + void setupTreeWidget(); + void setupLanguageBox(); + void setupRadioButtons(); + void setupDefaultProfile(); + void setupPictureSettings(); + void setupCustomGTAFolder(); + void setupInterfaceSettings(); + void setupStatisticsSettings(); + void setupSnapmaticPictureViewer(); + void setupWindowsGameSettings(); + void applySettings(); +}; + +#endif // OPTIONSDIALOG_H diff --git a/OptionsDialog.ui b/OptionsDialog.ui old mode 100755 new mode 100644 index 995ae79..6c5e80b --- a/OptionsDialog.ui +++ b/OptionsDialog.ui @@ -1,503 +1,832 @@ - - - OptionsDialog - - - - 0 - 0 - 435 - 405 - - - - %1 - Settings - - - true - - - - - - 0 - - - - Profiles - - - - - - Content Open/Select Mode - - - - - - Open with Singleclick - - - true - - - - - - - Open with Doubleclick - - - - - - - Select with Singleclick - - - - - - - - - - Default Profile - - - - - - - - - - - - Custom GTA V Folder - - - - - - Force using Custom Folder - - - - - - - - - - - - ... - - - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - Pictures - - - - - - Export Size - - - - - - Default: %1x%2 - - - true - - - - - - - Screen Resolution: %1x%2 - - - - - - - - - Custom Size: - - - - - - - false - - - Custom Size: - - - - - - - false - - - 1 - - - 3840 - - - 960 - - - - - - - false - - - x - - - - - - - false - - - 1 - - - 2160 - - - 536 - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - - - - Ignore Aspect Ratio - - - - - - - - - - - - Export Quality - - - - - - Enable Custom Quality - - - - - - - - - false - - - Quality: - - - - - - - false - - - 1 - - - 100 - - - 100 - - - Qt::Horizontal - - - - - - - false - - - %1% - - - true - - - - - - - - - - - - Picture Viewer - - - - - - Enable Navigation Bar - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - Players - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::ScrollPerPixel - - - true - - - true - - - - ID - - - - - Name - - - - - - - - - Language - - - - - - Language - - - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - Sync - - - - - - Sync is not implemented at current time - - - Qt::AlignCenter - - - true - - - - - - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - &OK - - - - - - - - 0 - 0 - - - - &Cancel - - - - - - - - - - - cmdCancel - clicked() - OptionsDialog - close() - - - 352 - 328 - - - 199 - 174 - - - - - + + + OptionsDialog + + + + 0 + 0 + 435 + 524 + + + + %1 - Settings + + + true + + + + + + 0 + + + + Profiles + + + + + + Content Open/Select Mode + + + + + + %1 1.9+ + + + true + + + + + + + %1 1.0-1.8 + + + + + + + Open with Doubleclick + + + + + + + + + + Default Profile + + + + + + + + + + + + Custom GTA V Folder + + + + + + Force using Custom Folder + + + + + + + + + + + + ... + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + Pictures + + + + + + Export Size + + + + + + Default: %1x%2 + + + true + + + + + + + Screen Resolution: %1x%2 + + + + + + + + + Custom Size: + + + + + + + false + + + Custom Size: + + + + + + + false + + + 1 + + + 3840 + + + 960 + + + + + + + false + + + x + + + + + + + false + + + 1 + + + 2160 + + + 536 + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + + + Ignore Aspect Ratio + + + + + + + + + + + + Export Quality + + + + + + Enable Custom Quality + + + + + + + + + false + + + Quality: + + + + + + + false + + + 1 + + + 100 + + + 100 + + + Qt::Horizontal + + + + + + + false + + + %1% + + + true + + + + + + + + + + + + Picture Viewer + + + + + + Enable Navigation Bar + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + Players + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ScrollPerPixel + + + true + + + true + + + + ID + + + + + Name + + + + + + + + + Game + + + + + + Social Club Version + + + + + + Found: %1 + + + + + + + Language: %1 + + + + + + + + + + Steam Version + + + + + + Found: %1 + + + + + + + Language: %1 + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + Feedback + + + + + + Participation + + + + + + Participate in %1 User Statistics + + + + + + + <a href="%2">%1</a> + + + true + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + + + + + + + Categories + + + + + + false + + + Hardware, Application and OS Specification + + + true + + + + + + + false + + + System Language Configuration + + + true + + + + + + + Application Configuration + + + + + + + Personal Usage Data + + + + + + + + + + Other + + + + + + + + + 0 + 0 + + + + Participation ID: %1 + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + &Copy + + + false + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + Interface + + + + + + Language for Interface + + + + + + + + + Current: %1 + + + true + + + + + + + + + + Language for Areas + + + + + + + + + Current: %1 + + + true + + + + + + + + + + Style + + + + + + Use Default Style (Restart) + + + true + + + + + + + + + false + + + Style: + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + + + Font + + + + + + Use Default Font (Restart) + + + true + + + + + + + + + false + + + Font: + + + + + + + false + + + + 0 + 0 + + + + + + + + false + + + ... + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Apply changes + + + &OK + + + + + + + + 0 + 0 + + + + Discard changes + + + &Cancel + + + + + + + + + + + cmdCancel + clicked() + OptionsDialog + close() + + + 352 + 328 + + + 199 + 174 + + + + + diff --git a/PictureDialog.cpp b/PictureDialog.cpp old mode 100755 new mode 100644 index edba768..99850f8 --- a/PictureDialog.cpp +++ b/PictureDialog.cpp @@ -1,689 +1,928 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "PictureDialog.h" -#include "PictureWidget.h" -#include "ProfileDatabase.h" -#include "ui_PictureDialog.h" -#include "SidebarGenerator.h" -#include "StandardPaths.h" -#include "PictureExport.h" -#include "StringParser.h" -#include "GlobalString.h" -#include "UiModLabel.h" -#include "AppEnv.h" - -#ifdef GTA5SYNC_WIN -#if QT_VERSION >= 0x050200 -#include -#include -#endif -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -PictureDialog::PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent) : - QDialog(parent), profileDB(profileDB), crewDB(crewDB), - ui(new Ui::PictureDialog) -{ - primaryWindow = false; - setupPictureDialog(true); -} - -PictureDialog::PictureDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::PictureDialog) -{ - primaryWindow = false; - setupPictureDialog(false); -} - -PictureDialog::PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent) : - QDialog(parent), primaryWindow(primaryWindow), profileDB(profileDB), crewDB(crewDB), - ui(new Ui::PictureDialog) -{ - setupPictureDialog(true); -} - -PictureDialog::PictureDialog(bool primaryWindow, QWidget *parent) : - QDialog(parent), primaryWindow(primaryWindow), - ui(new Ui::PictureDialog) -{ - setupPictureDialog(false); -} - -void PictureDialog::setupPictureDialog(bool withDatabase_) -{ - // Set Window Flags - setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); - - // Setup User Interface - ui->setupUi(this); - windowTitleStr = this->windowTitle(); - jsonDrawString = ui->labJSON->text(); - ui->cmdExport->setEnabled(0); - plyrsList = QStringList(); - fullscreenWidget = 0; - rqFullscreen = 0; - previewMode = 0; - naviEnabled = 0; - indexed = 0; - picArea = ""; - picTitl = ""; - picPath = ""; - created = ""; - crewID = ""; - locX = ""; - locY = ""; - locZ = ""; - smpic = 0; - - // With datebase - withDatabase = withDatabase_; - - // Avatar area - qreal screenRatio = AppEnv::screenRatio(); - if (screenRatio != 1) - { - avatarAreaPicture = QImage(":/img/avatararea.png").scaledToHeight(536 * screenRatio, Qt::FastTransformation); - } - else - { - avatarAreaPicture = QImage(":/img/avatararea.png"); - } - avatarLocX = 145; - avatarLocY = 66; - avatarSize = 470; - - // Overlay area - renderOverlayPicture(); - overlayEnabled = 1; - - // Export menu - exportMenu = new QMenu(this); - jpegExportAction = exportMenu->addAction(tr("Export as &JPG picture..."), this, SLOT(exportSnapmaticPicture())); - pgtaExportAction = exportMenu->addAction(tr("Export as >A Snapmatic..."), this, SLOT(copySnapmaticPicture())); - ui->cmdExport->setMenu(exportMenu); - - // Global map - globalMap = GlobalString::getGlobalMap(); - - // Event connects - connect(ui->labJSON, SIGNAL(resized(QSize)), this, SLOT(adaptNewDialogSize(QSize))); - - // Dialog buttons - if (QIcon::hasThemeIcon("dialog-close")) - { - ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); - } - - installEventFilter(this); - installEventFilter(ui->labPicture); - ui->labPicture->setFixedSize(960 * screenRatio, 536 * screenRatio); - ui->labPicture->setFocusPolicy(Qt::StrongFocus); - - // Pre-adapt window for DPI - setFixedWidth(960 * screenRatio); - setFixedHeight(536 * screenRatio); -} - -PictureDialog::~PictureDialog() -{ - delete jpegExportAction; - delete pgtaExportAction; - delete exportMenu; - delete ui; -} - -void PictureDialog::closeEvent(QCloseEvent *ev) -{ - Q_UNUSED(ev) - if (primaryWindow && withDatabase) - { - emit endDatabaseThread(); - } -} - -void PictureDialog::addPreviousNextButtons() -{ - // Windows Vista additions -#ifdef GTA5SYNC_WIN -#if QT_VERSION >= 0x050200 - QPalette palette; - QToolBar *uiToolbar = new QToolBar("Picture Toolbar", this); - layout()->setMenuBar(uiToolbar); - uiToolbar->addAction(QIcon(":/img/back.png"), "", this, SLOT(previousPictureRequestedSlot())); - uiToolbar->addAction(QIcon(":/img/next.png"), "", this, SLOT(nextPictureRequestedSlot())); - ui->jsonFrame->setStyleSheet(QString("QFrame { background: %1; }").arg(palette.window().color().name())); - naviEnabled = true; -#endif -#endif -} - -void PictureDialog::adaptNewDialogSize(QSize newLabelSize) -{ - Q_UNUSED(newLabelSize) - int newDialogHeight = ui->labPicture->pixmap()->height(); - newDialogHeight = newDialogHeight + ui->jsonFrame->height(); - if (naviEnabled) newDialogHeight = newDialogHeight + layout()->menuBar()->height(); - setMinimumSize(width(), newDialogHeight); - setMaximumSize(width(), newDialogHeight); - setFixedHeight(newDialogHeight); - ui->labPicture->updateGeometry(); - ui->jsonFrame->updateGeometry(); - updateGeometry(); -} - -void PictureDialog::stylizeDialog() -{ -#ifdef GTA5SYNC_WIN -#if QT_VERSION >= 0x050200 - if (QtWin::isCompositionEnabled()) - { - QtWin::extendFrameIntoClientArea(this, 0, this->layout()->menuBar()->height(), 0, 0); - setAttribute(Qt::WA_TranslucentBackground, true); - setAttribute(Qt::WA_NoSystemBackground, false); - setStyleSheet("PictureDialog { background: transparent; }"); - } - else - { - QtWin::resetExtendedFrame(this); - setAttribute(Qt::WA_TranslucentBackground, false); - setStyleSheet(QString("PictureDialog { background: %1; }").arg(QtWin::realColorizationColor().name())); - } -#endif -#endif -} - -bool PictureDialog::event(QEvent *event) -{ -#ifdef GTA5SYNC_WIN -#if QT_VERSION >= 0x050200 - if (naviEnabled) - { - if (event->type() == QWinEvent::CompositionChange || event->type() == QWinEvent::ColorizationChange) - { - stylizeDialog(); - } - } -#endif -#endif - return QDialog::event(event); -} - -void PictureDialog::nextPictureRequestedSlot() -{ - emit nextPictureRequested(); -} - -void PictureDialog::previousPictureRequestedSlot() -{ - emit previousPictureRequested(); -} - -bool PictureDialog::eventFilter(QObject *obj, QEvent *ev) -{ - bool returnValue = false; - if (obj == this || obj == ui->labPicture) - { - if (ev->type() == QEvent::KeyPress) - { - QKeyEvent *keyEvent = (QKeyEvent*)ev; - switch (keyEvent->key()){ - case Qt::Key_Left: - emit previousPictureRequested(); - returnValue = true; - break; - case Qt::Key_Right: - emit nextPictureRequested(); - returnValue = true; - break; - case Qt::Key_E: case Qt::Key_S: case Qt::Key_Save: - ui->cmdExport->click(); - returnValue = true; - break; - case Qt::Key_1: - if (previewMode) - { - previewMode = false; - renderPicture(); - } - else - { - previewMode = true; - renderPicture(); - } - break; - case Qt::Key_2: - if (overlayEnabled) - { - overlayEnabled = false; - if (!previewMode) renderPicture(); - } - else - { - overlayEnabled = true; - if (!previewMode) renderPicture(); - } - break; -#if QT_VERSION >= 0x050300 - case Qt::Key_Exit: - ui->cmdClose->click(); - returnValue = true; - break; -#endif - case Qt::Key_Enter: case Qt::Key_Return: - on_labPicture_mouseDoubleClicked(Qt::LeftButton); - returnValue = true; - break; - case Qt::Key_Escape: - ui->cmdClose->click(); - returnValue = true; - break; - } - } - } - return returnValue; -} - -void PictureDialog::triggerFullscreenDoubeClick() -{ - on_labPicture_mouseDoubleClicked(Qt::LeftButton); -} - -void PictureDialog::exportCustomContextMenuRequestedPrivate(const QPoint &pos, bool fullscreen) -{ - rqFullscreen = fullscreen; - exportMenu->popup(pos); -} - -void PictureDialog::exportCustomContextMenuRequested(const QPoint &pos) -{ - exportCustomContextMenuRequestedPrivate(pos, true); -} - -void PictureDialog::mousePressEvent(QMouseEvent *ev) -{ - QDialog::mousePressEvent(ev); -} - -void PictureDialog::dialogNextPictureRequested() -{ - emit nextPictureRequested(); -} - -void PictureDialog::dialogPreviousPictureRequested() -{ - emit previousPictureRequested(); -} - -void PictureDialog::renderOverlayPicture() -{ - // Generating Overlay Preview - qreal screenRatio = AppEnv::screenRatio(); - QRect preferedRect = QRect(0, 0, 200 * screenRatio, 160 * screenRatio); - QString overlayText = tr("Key 1 - Avatar Preview Mode\nKey 2 - Toggle Overlay\nArrow Keys - Navigate"); - QImage overlayImage(1, 1, QImage::Format_ARGB32_Premultiplied); - overlayImage.fill(Qt::transparent); - - QPainter overlayPainter(&overlayImage); - QFont overlayPainterFont; - overlayPainterFont.setPixelSize(12 * screenRatio); - overlayPainter.setFont(overlayPainterFont); - QRect overlaySpace = overlayPainter.boundingRect(preferedRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip | Qt::TextWordWrap, overlayText); - overlayPainter.end(); - - int hOverlay = Qt::AlignTop; - if (overlaySpace.height() < 74 * screenRatio) - { - hOverlay = Qt::AlignVCenter; - preferedRect.setHeight(71 * screenRatio); - overlaySpace.setHeight(80 * screenRatio); - } - else - { - overlaySpace.setHeight(overlaySpace.height() + 6 * screenRatio); - } - - overlayImage = overlayImage.scaled(overlaySpace.size()); - overlayPainter.begin(&overlayImage); - overlayPainter.setPen(QColor::fromRgb(255, 255, 255, 255)); - overlayPainter.setFont(overlayPainterFont); - overlayPainter.drawText(preferedRect, Qt::AlignLeft | hOverlay | Qt::TextDontClip | Qt::TextWordWrap, overlayText); - overlayPainter.end(); - - if (overlaySpace.width() < 194 * screenRatio) - { - overlaySpace.setWidth(200 * screenRatio); - } - else - { - overlaySpace.setWidth(overlaySpace.width() + 6 * screenRatio); - } - - QImage overlayBorderImage(overlaySpace.width(), overlaySpace.height(), QImage::Format_ARGB6666_Premultiplied); - overlayBorderImage.fill(QColor(15, 15, 15, 162)); - - overlayTempImage = QImage(overlaySpace.width(), overlaySpace.height(), QImage::Format_ARGB6666_Premultiplied); - overlayTempImage.fill(Qt::transparent); - QPainter overlayTempPainter(&overlayTempImage); - overlayTempPainter.drawImage(0, 0, overlayBorderImage); - overlayTempPainter.drawImage(3 * screenRatio, 3 * screenRatio, overlayImage); - overlayTempPainter.end(); -} - -void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, bool _indexed, int _index) -{ - snapmaticPicture = QImage(); - indexed = _indexed; - index = _index; - picPath = picture->getPictureFilePath(); - smpic = picture; - if (!readOk) - { - QMessageBox::warning(this, tr("Snapmatic Picture Viewer"), tr("Failed at %1").arg(picture->getLastStep())); - return; - } - if (picture->isPicOk()) - { - snapmaticPicture = picture->getImage(); - renderPicture(); - ui->cmdExport->setEnabled(true); - } - if (picture->isJsonOk()) - { - locX = QString::number(picture->getSnapmaticProperties().location.x); - locY = QString::number(picture->getSnapmaticProperties().location.y); - locZ = QString::number(picture->getSnapmaticProperties().location.z); - if (withDatabase) - { - crewID = crewDB->getCrewName(picture->getSnapmaticProperties().crewID); - } - else - { - crewID = QString::number(picture->getSnapmaticProperties().crewID); - } - created = picture->getSnapmaticProperties().createdDateTime.toString(Qt::DefaultLocaleShortDate); - plyrsList = picture->getSnapmaticProperties().playersList; - picTitl = StringParser::escapeString(picture->getPictureTitle()); - picArea = picture->getSnapmaticProperties().location.area; - if (globalMap.contains(picArea)) - { - picAreaStr = globalMap[picArea]; - } - else - { - picAreaStr = picArea; - } - - QString plyrsStr; - if (plyrsList.length() >= 1) - { - foreach (const QString &player, plyrsList) - { - QString playerName; - if (withDatabase) - { - playerName = profileDB->getPlayerName(player.toInt()); - } - else - { - playerName = player; - } - plyrsStr.append(", "); - plyrsStr.append(playerName); - plyrsStr.append(""); - } - plyrsStr.remove(0,2); - } - else - { - plyrsStr = tr("No player"); - } - - if (crewID == "") { crewID = tr("No crew"); } - - this->setWindowTitle(windowTitleStr.arg(picture->getPictureStr())); - ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, plyrsStr, crewID, picTitl, picAreaStr, created)); - } - else - { - ui->labJSON->setText(jsonDrawString.arg("0.0", "0.0", "0.0", tr("No player"), tr("No crew"), tr("Unknown Location"))); - QMessageBox::warning(this,tr("Snapmatic Picture Viewer"),tr("Failed at %1").arg(picture->getLastStep())); - } - emit newPictureCommited(snapmaticPicture); -} - -void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, int index) -{ - setSnapmaticPicture(picture, readOk, true, index); -} - -void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk) -{ - setSnapmaticPicture(picture, readOk, false, 0); -} - -void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, int index) -{ - setSnapmaticPicture(picture, true, index); -} - -void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture) -{ - setSnapmaticPicture(picture, true); -} - -void PictureDialog::renderPicture() -{ - qreal screenRatio = AppEnv::screenRatio(); - if (!previewMode) - { - if (overlayEnabled) - { - QPixmap shownImagePixmap(960 * screenRatio, 536 * screenRatio); - shownImagePixmap.fill(Qt::transparent); - QPainter shownImagePainter(&shownImagePixmap); - if (screenRatio == 1) - { - shownImagePainter.drawImage(0, 0, snapmaticPicture); - shownImagePainter.drawImage(3 * screenRatio, 3 * screenRatio, overlayTempImage); - } - else - { - shownImagePainter.drawImage(0, 0, snapmaticPicture.scaledToHeight(536 * screenRatio, Qt::SmoothTransformation)); - shownImagePainter.drawImage(3 * screenRatio, 3 * screenRatio, overlayTempImage); - } - shownImagePainter.end(); - ui->labPicture->setPixmap(shownImagePixmap); - } - else - { - if (screenRatio != 1) - { - QPixmap shownImagePixmap(960 * screenRatio, 536 * screenRatio); - shownImagePixmap.fill(Qt::transparent); - QPainter shownImagePainter(&shownImagePixmap); - shownImagePainter.drawImage(0, 0, snapmaticPicture.scaledToHeight(536 * screenRatio, Qt::SmoothTransformation)); - shownImagePainter.end(); - ui->labPicture->setPixmap(shownImagePixmap); - } - else - { - ui->labPicture->setPixmap(QPixmap::fromImage(snapmaticPicture)); - } - } - } - else - { - // Generating Avatar Preview - QPixmap avatarPixmap(960 * screenRatio, 536 * screenRatio); - QPainter snapPainter(&avatarPixmap); - QFont snapPainterFont; - snapPainterFont.setPixelSize(12 * screenRatio); - if (screenRatio == 1) - { - snapPainter.drawImage(0, 0, snapmaticPicture); - } - else - { - snapPainter.drawImage(0, 0, snapmaticPicture.scaledToHeight(536 * screenRatio, Qt::SmoothTransformation)); - } - snapPainter.drawImage(0, 0, avatarAreaPicture); - snapPainter.setPen(QColor::fromRgb(255, 255, 255, 255)); - snapPainter.setFont(snapPainterFont); - snapPainter.drawText(QRect(3 * screenRatio, 3 * screenRatio, 140 * screenRatio, 60 * screenRatio), Qt::AlignLeft | Qt::TextWordWrap, tr("Avatar Preview Mode\nPress 1 for Default View")); - snapPainter.end(); - ui->labPicture->setPixmap(avatarPixmap); - } -} - -void PictureDialog::playerNameUpdated() -{ - if (plyrsList.count() >= 1) - { - QString plyrsStr; - foreach (const QString &player, plyrsList) - { - QString playerName; - if (withDatabase) - { - playerName = profileDB->getPlayerName(player.toInt()); - } - else - { - playerName = player; - } - plyrsStr.append(", "); - plyrsStr.append(playerName); - plyrsStr.append(""); - } - plyrsStr.remove(0,2); - ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, plyrsStr, crewID, picTitl, picAreaStr, created)); - } -} - -void PictureDialog::exportSnapmaticPicture() -{ - if (rqFullscreen && fullscreenWidget) - { - PictureExport::exportAsPicture(fullscreenWidget, smpic); - } - else - { - PictureExport::exportAsPicture(this, smpic); - } -} - -void PictureDialog::copySnapmaticPicture() -{ - if (rqFullscreen && fullscreenWidget) - { - PictureExport::exportAsSnapmatic(fullscreenWidget, smpic); - } - else - { - PictureExport::exportAsSnapmatic(this, smpic); - } -} - -void PictureDialog::on_labPicture_mouseDoubleClicked(Qt::MouseButton button) -{ - if (button == Qt::LeftButton) - { - QRect desktopRect = QApplication::desktop()->screenGeometry(this); - PictureWidget *pictureWidget = new PictureWidget(this); // Work! - pictureWidget->setObjectName("PictureWidget"); -#if QT_VERSION >= 0x050600 - pictureWidget->setWindowFlags(pictureWidget->windowFlags()^Qt::FramelessWindowHint^Qt::WindowStaysOnTopHint^Qt::MaximizeUsingFullscreenGeometryHint); -#else - pictureWidget->setWindowFlags(pictureWidget->windowFlags()^Qt::FramelessWindowHint^Qt::WindowStaysOnTopHint); -#endif - pictureWidget->setWindowTitle(this->windowTitle()); - pictureWidget->setStyleSheet("QLabel#pictureLabel{background-color: black;}"); - pictureWidget->setImage(snapmaticPicture, desktopRect); - pictureWidget->setModal(true); - - fullscreenWidget = pictureWidget; - QObject::connect(this, SIGNAL(newPictureCommited(QImage)), pictureWidget, SLOT(setImage(QImage))); - QObject::connect(pictureWidget, SIGNAL(nextPictureRequested()), this, SLOT(dialogNextPictureRequested())); - QObject::connect(pictureWidget, SIGNAL(previousPictureRequested()), this, SLOT(dialogPreviousPictureRequested())); - - pictureWidget->move(desktopRect.x(), desktopRect.y()); - pictureWidget->resize(desktopRect.width(), desktopRect.height()); - pictureWidget->showFullScreen(); - pictureWidget->setFocus(); - pictureWidget->raise(); - pictureWidget->exec(); - - fullscreenWidget = 0; // Work! - delete pictureWidget; // Work! - } -} - -void PictureDialog::on_labPicture_customContextMenuRequested(const QPoint &pos) -{ - exportCustomContextMenuRequestedPrivate(ui->labPicture->mapToGlobal(pos), false); -} - -bool PictureDialog::isIndexed() -{ - return indexed; -} - -int PictureDialog::getIndex() -{ - return index; -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "PictureDialog.h" +#include "PictureWidget.h" +#include "ProfileDatabase.h" +#include "ui_PictureDialog.h" +#include "SidebarGenerator.h" +#include "MapLocationDialog.h" +#include "JsonEditorDialog.h" +#include "SnapmaticEditor.h" +#include "StandardPaths.h" +#include "PictureExport.h" +#include "ImportDialog.h" +#include "StringParser.h" +#include "GlobalString.h" +#include "UiModLabel.h" +#include "AppEnv.h" +#include "config.h" + +#if QT_VERSION < 0x060000 +#include +#endif + +#ifdef Q_OS_WIN +#if QT_VERSION >= 0x050000 +#include "dwmapi.h" +#endif +#endif + +#ifdef Q_OS_MAC +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GTA5SYNC_TELEMETRY +#include "TelemetryClass.h" +#endif + +// Macros for better Overview + RAM +#define locX QString::number(picture->getSnapmaticProperties().location.x) +#define locY QString::number(picture->getSnapmaticProperties().location.y) +#define locZ QString::number(picture->getSnapmaticProperties().location.z) +#define crewID QString::number(picture->getSnapmaticProperties().crewID) +#define picArea picture->getSnapmaticProperties().location.area +#define picPath picture->getPictureFilePath() +#define picTitl StringParser::escapeString(picture->getPictureTitle()) +#define plyrsList picture->getSnapmaticProperties().playersList +#if QT_VERSION >= 0x060000 +#define created QLocale().toString(picture->getSnapmaticProperties().createdDateTime, QLocale::ShortFormat) +#else +#define created picture->getSnapmaticProperties().createdDateTime.toString(Qt::DefaultLocaleShortDate) +#endif + +PictureDialog::PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent) : + QDialog(parent), profileDB(profileDB), crewDB(crewDB), + ui(new Ui::PictureDialog) +{ + primaryWindow = false; + setupPictureDialog(); +} + +PictureDialog::PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QString profileName, QWidget *parent) : + QDialog(parent), profileDB(profileDB), crewDB(crewDB), profileName(profileName), + ui(new Ui::PictureDialog) +{ + primaryWindow = false; + setupPictureDialog(); +} + +PictureDialog::PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent) : + QDialog(parent), primaryWindow(primaryWindow), profileDB(profileDB), crewDB(crewDB), + ui(new Ui::PictureDialog) +{ + setupPictureDialog(); +} + +PictureDialog::PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, CrewDatabase *crewDB, QString profileName, QWidget *parent) : + QDialog(parent), primaryWindow(primaryWindow), profileDB(profileDB), crewDB(crewDB), profileName(profileName), + ui(new Ui::PictureDialog) +{ + setupPictureDialog(); +} + +void PictureDialog::setupPictureDialog() +{ + // Set Window Flags +#if QT_VERSION >= 0x050900 + setWindowFlag(Qt::WindowContextHelpButtonHint, false); + setWindowFlag(Qt::CustomizeWindowHint, true); +#else + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint^Qt::CustomizeWindowHint); +#endif +#ifdef Q_OS_LINUX + // for stupid Window Manager like GNOME +#if QT_VERSION >= 0x050900 + setWindowFlag(Qt::Dialog, false); + setWindowFlag(Qt::Window, true); +#else + setWindowFlags(windowFlags()^Qt::Dialog^Qt::Window); +#endif +#endif + + // Setup User Interface + ui->setupUi(this); + windowTitleStr = this->windowTitle(); + jsonDrawString = ui->labJSON->text(); + ui->cmdManage->setEnabled(false); + fullscreenWidget = nullptr; + rqFullscreen = false; + previewMode = false; + naviEnabled = false; + indexed = false; + smpic = nullptr; + crewStr = ""; + + // Get Snapmatic Resolution + const QSize snapmaticResolution = SnapmaticPicture::getSnapmaticResolution(); + + // Avatar area + qreal screenRatio = AppEnv::screenRatio(); + qreal screenRatioPR = AppEnv::screenRatioPR(); + if (screenRatio != 1 || screenRatioPR != 1) { + avatarAreaPicture = QImage(AppEnv::getImagesFolder() % "/avatararea.png").scaledToHeight(snapmaticResolution.height() * screenRatio * screenRatioPR, Qt::FastTransformation); + } + else { + avatarAreaPicture = QImage(AppEnv::getImagesFolder() % "/avatararea.png"); + } + avatarLocX = 145; + avatarLocY = 66; + avatarSize = 470; + + // DPI calculation (picture) + ui->labPicture->setFixedSize(snapmaticResolution.width() * screenRatio, snapmaticResolution.height() * screenRatio); + ui->labPicture->setFocusPolicy(Qt::StrongFocus); + ui->labPicture->setScaledContents(true); + + // Overlay area + renderOverlayPicture(); + overlayEnabled = true; + + // Manage menu + manageMenu = new QMenu(this); + manageMenu->addAction(tr("Export as &Picture..."), this, SLOT(exportSnapmaticPicture())); + manageMenu->addAction(tr("Export as &Snapmatic..."), this, SLOT(copySnapmaticPicture())); + manageMenu->addSeparator(); + manageMenu->addAction(tr("&Edit Properties..."), this, SLOT(editSnapmaticProperties())); + manageMenu->addAction(tr("&Overwrite Image..."), this, SLOT(editSnapmaticImage())); + manageMenu->addSeparator(); + QAction *openViewerAction = manageMenu->addAction(tr("Open &Map Viewer..."), this, SLOT(openPreviewMap())); + openViewerAction->setShortcut(Qt::Key_M); + manageMenu->addAction(tr("Open &JSON Editor..."), this, SLOT(editSnapmaticRawJson())); + ui->cmdManage->setMenu(manageMenu); + + // Global map + globalMap = GlobalString::getGlobalMap(); + + // Set Icon for Close Button + if (QIcon::hasThemeIcon("dialog-close")) { + ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); + } + else if (QIcon::hasThemeIcon("gtk-close")) { + ui->cmdClose->setIcon(QIcon::fromTheme("gtk-close")); + } + + installEventFilter(this); + + // DPI calculation + ui->hlButtons->setSpacing(6 * screenRatio); + ui->vlButtons->setSpacing(6 * screenRatio); + ui->vlButtons->setContentsMargins(0, 0, 5 * screenRatio, 5 * screenRatio); + ui->jsonLayout->setContentsMargins(4 * screenRatio, 10 * screenRatio, 4 * screenRatio, 4 * screenRatio); + + // Pre-adapt window for DPI + const QSize windowSize(snapmaticResolution.width() * screenRatio, snapmaticResolution.height() * screenRatio); + setMinimumSize(windowSize); + setMaximumSize(windowSize); +} + +PictureDialog::~PictureDialog() +{ + delete ui; +} + +void PictureDialog::closeEvent(QCloseEvent *ev) +{ + Q_UNUSED(ev) + if (primaryWindow) + emit endDatabaseThread(); +} + +void PictureDialog::addPreviousNextButtons() +{ + QToolBar *uiToolbar = new QToolBar("Picture Toolbar", this); +#if QT_VERSION < 0x050600 + qreal screenRatio = AppEnv::screenRatio(); + if (screenRatio != 1) { + QSize iconSize = uiToolbar->iconSize(); + uiToolbar->setIconSize(QSize(iconSize.width() * screenRatio, iconSize.height() * screenRatio)); + } +#endif + uiToolbar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + uiToolbar->setObjectName("UiToolbar"); + uiToolbar->addAction(QIcon(AppEnv::getImagesFolder() % "/back.svgz"), "", this, SLOT(previousPictureRequestedSlot())); + uiToolbar->addAction(QIcon(AppEnv::getImagesFolder() % "/next.svgz"), "", this, SLOT(nextPictureRequestedSlot())); +#ifdef Q_OS_MAC +#if QT_VERSION >= 0x050000 + uiToolbar->setStyle(QStyleFactory::create("Fusion")); +#endif +#endif + layout()->setMenuBar(uiToolbar); + naviEnabled = true; +} + +void PictureDialog::adaptDialogSize() +{ + int newDialogHeight = (SnapmaticPicture::getSnapmaticResolution().height() * AppEnv::screenRatio()) + ui->jsonFrame->heightForWidth(width()); + if (naviEnabled) + newDialogHeight = newDialogHeight + layout()->menuBar()->height(); + const QSize windowSize(width(), newDialogHeight); + setMinimumSize(windowSize); + setMaximumSize(windowSize); +} + +void PictureDialog::styliseDialog() +{ +#ifdef Q_OS_WIN + BOOL isEnabled; + DwmIsCompositionEnabled(&isEnabled); + if (isEnabled == TRUE) { + MARGINS margins = {0, 0, qRound(layout()->menuBar()->height() * AppEnv::screenRatioPR()), 0}; + HRESULT hr = S_OK; + hr = DwmExtendFrameIntoClientArea(reinterpret_cast(winId()), &margins); + if (SUCCEEDED(hr)) { + setStyleSheet("PictureDialog{background:transparent}"); + } + } + else { + MARGINS margins = {0, 0, 0, 0}; + DwmExtendFrameIntoClientArea(reinterpret_cast(winId()), &margins); + bool colorOk = false; + QSettings dwmRegistry("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\DWM", QSettings::NativeFormat); + QRgb color = dwmRegistry.value("ColorizationColor").toUInt(&colorOk); + if (colorOk) { + setStyleSheet(QString("PictureDialog{background:%1}").arg(QColor::fromRgba(color).name())); + } + else { + HRESULT hr = S_OK; + BOOL isOpaqueBlend; + DWORD colorization; + hr = DwmGetColorizationColor(&colorization, &isOpaqueBlend); + if (SUCCEEDED(hr) && isOpaqueBlend == FALSE) { + color = colorization; + setStyleSheet(QString("PictureDialog{background:%1}").arg(QColor::fromRgba(color).name())); + } + else { + setStyleSheet("PictureDialog{background:palette(window)}"); + } + } + } + ui->jsonFrame->setStyleSheet("QFrame{background:palette(window)}"); +#endif +} + +#ifdef Q_OS_WIN +#if QT_VERSION >= 0x050000 +#if QT_VERSION >= 0x060000 +bool PictureDialog::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) +#else +bool PictureDialog::nativeEvent(const QByteArray &eventType, void *message, long *result) +#endif +{ + MSG *msg = reinterpret_cast(message); + if (msg->message == 0x031e || msg->message == 0x0320) { + styliseDialog(); + } + return QWidget::nativeEvent(eventType, message, result); +} +#endif +#endif + +void PictureDialog::nextPictureRequestedSlot() +{ + emit nextPictureRequested(); +} + +void PictureDialog::previousPictureRequestedSlot() +{ + emit previousPictureRequested(); +} + +bool PictureDialog::eventFilter(QObject *obj, QEvent *ev) +{ + bool returnValue = false; + if (obj == this || obj == ui->labPicture) { + if (ev->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = dynamic_cast(ev); + switch (keyEvent->key()) { + case Qt::Key_Left: + emit previousPictureRequested(); + returnValue = true; + break; + case Qt::Key_Right: + emit nextPictureRequested(); + returnValue = true; + break; + case Qt::Key_1: + if (previewMode) { + previewMode = false; + renderPicture(); + } + else { + previewMode = true; + renderPicture(); + } + break; + case Qt::Key_2: + if (overlayEnabled) { + overlayEnabled = false; + if (!previewMode) renderPicture(); + } + else { + overlayEnabled = true; + if (!previewMode) renderPicture(); + } + break; + case Qt::Key_M: + openPreviewMap(); + returnValue = true; + break; +#if QT_VERSION >= 0x050300 + case Qt::Key_Exit: + ui->cmdClose->click(); + returnValue = true; + break; +#endif + case Qt::Key_Enter: case Qt::Key_Return: + on_labPicture_mouseDoubleClicked(Qt::LeftButton); + returnValue = true; + break; + case Qt::Key_Escape: + ui->cmdClose->click(); + returnValue = true; + break; + } + } +#ifdef Q_OS_WIN +#if QT_VERSION >= 0x050000 + if (obj != ui->labPicture && naviEnabled) { + if (ev->type() == QEvent::MouseButtonPress) { + QMouseEvent *mouseEvent = dynamic_cast(ev); + if (mouseEvent->pos().y() <= layout()->menuBar()->height()) { + if (mouseEvent->button() == Qt::LeftButton) { + dragPosition = mouseEvent->pos(); + dragStart = true; + } + } + } + if (ev->type() == QEvent::MouseButtonRelease) { + QMouseEvent *mouseEvent = dynamic_cast(ev); + if (mouseEvent->pos().y() <= layout()->menuBar()->height()) { + if (mouseEvent->button() == Qt::LeftButton) { + dragStart = false; + } + } + } + if (dragStart && ev->type() == QEvent::MouseMove) { + QMouseEvent *mouseEvent = dynamic_cast(ev); + if (mouseEvent->pos().y() <= layout()->menuBar()->height()) { + if (mouseEvent->buttons() & Qt::LeftButton) { + const QPoint diff = mouseEvent->pos() - dragPosition; + move(QPoint(pos() + diff)); + updateGeometry(); + } + } + } + } +#endif +#endif + } + return returnValue; +} + +void PictureDialog::triggerFullscreenDoubeClick() +{ + on_labPicture_mouseDoubleClicked(Qt::LeftButton); +} + +void PictureDialog::exportCustomContextMenuRequestedPrivate(const QPoint &pos, bool fullscreen) +{ + rqFullscreen = fullscreen; + manageMenu->popup(pos); +} + +void PictureDialog::exportCustomContextMenuRequested(const QPoint &pos) +{ + exportCustomContextMenuRequestedPrivate(pos, true); +} + +void PictureDialog::mousePressEvent(QMouseEvent *ev) +{ + QDialog::mousePressEvent(ev); +} + +void PictureDialog::dialogNextPictureRequested() +{ + emit nextPictureRequested(); +} + +void PictureDialog::dialogPreviousPictureRequested() +{ + emit previousPictureRequested(); +} + +void PictureDialog::renderOverlayPicture() +{ + // Generating Overlay Preview + qreal screenRatio = AppEnv::screenRatio(); + qreal screenRatioPR = AppEnv::screenRatioPR(); + QRect preferedRect = QRect(0, 0, 200 * screenRatio * screenRatioPR, 160 * screenRatio * screenRatioPR); + QString overlayText = tr("Key 1 - Avatar Preview Mode\nKey 2 - Toggle Overlay\nArrow Keys - Navigate"); + + QFont overlayPainterFont; + overlayPainterFont.setPixelSize(12 * screenRatio * screenRatioPR); + QFontMetrics fontMetrics(overlayPainterFont); + QRect overlaySpace = fontMetrics.boundingRect(preferedRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip | Qt::TextWordWrap, overlayText); + + int hOverlay = Qt::AlignTop; + if (overlaySpace.height() < 74 * screenRatio * screenRatioPR) { + hOverlay = Qt::AlignVCenter; + preferedRect.setHeight(71 * screenRatio * screenRatioPR); + overlaySpace.setHeight(80 * screenRatio * screenRatioPR); + } + else { + overlaySpace.setHeight(overlaySpace.height() + 6 * screenRatio * screenRatioPR); + } + + QImage overlayImage(overlaySpace.size(), QImage::Format_ARGB32_Premultiplied); + overlayImage.fill(Qt::transparent); + + QPainter overlayPainter(&overlayImage); + overlayPainter.setPen(QColor::fromRgb(255, 255, 255, 255)); + overlayPainter.setFont(overlayPainterFont); + overlayPainter.drawText(preferedRect, Qt::AlignLeft | hOverlay | Qt::TextDontClip | Qt::TextWordWrap, overlayText); + overlayPainter.end(); + + if (overlaySpace.width() < 194 * screenRatio * screenRatioPR) { + overlaySpace.setWidth(200 * screenRatio * screenRatioPR); + } + else { + overlaySpace.setWidth(overlaySpace.width() + 6 * screenRatio * screenRatioPR); + } + + QImage overlayBorderImage(overlaySpace.width(), overlaySpace.height(), QImage::Format_ARGB6666_Premultiplied); + overlayBorderImage.fill(QColor(15, 15, 15, 162)); + + overlayTempImage = QImage(overlaySpace.width(), overlaySpace.height(), QImage::Format_ARGB6666_Premultiplied); + overlayTempImage.fill(Qt::transparent); + QPainter overlayTempPainter(&overlayTempImage); + overlayTempPainter.drawImage(0, 0, overlayBorderImage); + overlayTempPainter.drawImage(3 * screenRatio * screenRatioPR, 3 * screenRatio * screenRatioPR, overlayImage); + overlayTempPainter.end(); +} + +void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, bool _indexed, int _index) +{ + if (smpic != nullptr) { + QObject::disconnect(smpic, SIGNAL(updated()), this, SLOT(updated())); + QObject::disconnect(smpic, SIGNAL(customSignal(QString)), this, SLOT(customSignal(QString))); + } + snapmaticPicture = QImage(); + indexed = _indexed; + index = _index; + smpic = picture; + if (!readOk) { + QMessageBox::warning(this, tr("Snapmatic Picture Viewer"), tr("Failed at %1").arg(picture->getLastStep())); + return; + } + if (picture->isPicOk()) { + snapmaticPicture = picture->getImage(); + renderPicture(); + ui->cmdManage->setEnabled(true); + } + if (picture->isJsonOk()) { + crewStr = crewDB->getCrewName(crewID); + if (globalMap.contains(picArea)) { + picAreaStr = globalMap.value(picArea); + } + else { + picAreaStr = picArea; + } + setWindowTitle(windowTitleStr.arg(picTitl)); + ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created)); + QTimer::singleShot(0, this, SLOT(adaptDialogSize())); + } + else { + ui->labJSON->setText(jsonDrawString.arg("0", "0", "0", tr("No Players"), tr("No Crew"), tr("Unknown Location"))); + QTimer::singleShot(0, this, SLOT(adaptDialogSize())); + } + QObject::connect(smpic, SIGNAL(updated()), this, SLOT(updated())); + QObject::connect(smpic, SIGNAL(customSignal(QString)), this, SLOT(customSignal(QString))); + emit newPictureCommited(snapmaticPicture); +} + +void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, int index) +{ + setSnapmaticPicture(picture, readOk, true, index); +} + +void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk) +{ + setSnapmaticPicture(picture, readOk, false, 0); +} + +void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, int index) +{ + setSnapmaticPicture(picture, true, index); +} + +void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture) +{ + setSnapmaticPicture(picture, true); +} + +void PictureDialog::renderPicture() +{ + const qreal screenRatio = AppEnv::screenRatio(); + const qreal screenRatioPR = AppEnv::screenRatioPR(); + const QSize snapmaticResolution(SnapmaticPicture::getSnapmaticResolution()); + const QSize renderResolution(snapmaticResolution.width() * screenRatio * screenRatioPR, snapmaticResolution.height() * screenRatio * screenRatioPR); + QPixmap shownImagePixmap(renderResolution); + shownImagePixmap.fill(Qt::black); + QPainter shownImagePainter(&shownImagePixmap); + const QImage renderImage = snapmaticPicture.scaled(renderResolution, Qt::KeepAspectRatio, Qt::SmoothTransformation); + if (renderImage.width() < renderResolution.width()) { + shownImagePainter.drawImage((renderResolution.width() - renderImage.width()) / 2, 0, renderImage, Qt::AutoColor); + } + else if (renderImage.height() < renderResolution.height()) { + shownImagePainter.drawImage(0, (renderResolution.height() - renderImage.height()) / 2, renderImage, Qt::AutoColor); + } + else { + shownImagePainter.drawImage(0, 0, renderImage, Qt::AutoColor); + } + if (previewMode) { + QFont shownImagePainterFont; + shownImagePainterFont.setPixelSize(12 * screenRatio * screenRatioPR); + shownImagePainter.drawImage(0, 0, avatarAreaPicture); + shownImagePainter.setPen(QColor::fromRgb(255, 255, 255, 255)); + shownImagePainter.setFont(shownImagePainterFont); + shownImagePainter.drawText(QRect(3 * screenRatio * screenRatioPR, 3 * screenRatio * screenRatioPR, 140 * screenRatio * screenRatioPR, snapmaticResolution.height() * screenRatio * screenRatioPR), Qt::AlignLeft | Qt::TextWordWrap, tr("Avatar Preview Mode\nPress 1 for Default View")); + } + else if (overlayEnabled) { + shownImagePainter.drawImage(3 * screenRatio * screenRatioPR, 3 * screenRatio * screenRatioPR, overlayTempImage, Qt::AutoColor); + } + shownImagePainter.end(); +#if QT_VERSION >= 0x050600 + shownImagePixmap.setDevicePixelRatio(screenRatioPR); +#endif + ui->labPicture->setPixmap(shownImagePixmap); +} + +void PictureDialog::crewNameUpdated() +{ + SnapmaticPicture *picture = smpic; // used by macro + QString crewIDStr = crewID; + if (crewIDStr == crewStr) { + crewStr = crewDB->getCrewName(crewIDStr); + ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created)); + QTimer::singleShot(0, this, SLOT(adaptDialogSize())); + } +} + +void PictureDialog::playerNameUpdated() +{ + SnapmaticPicture *picture = smpic; // used by macro + if (plyrsList.count() >= 1) { + ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created)); + QTimer::singleShot(0, this, SLOT(adaptDialogSize())); + } +} + +QString PictureDialog::generateCrewString() +{ + SnapmaticPicture *picture = smpic; // used by macro + const QString crewIDStr = crewID; // save operation time + if (crewIDStr != "0" && !crewIDStr.isEmpty()) { + if (crewIDStr != crewStr) { + return QString("" % crewStr % ""); + } + else { + return QString(crewIDStr); + } + } + return tr("No Crew"); +} + +QString PictureDialog::generatePlayersString() +{ + SnapmaticPicture *picture = smpic; // used by macro + const QStringList playersList = plyrsList; // save operation time + QString plyrsStr; + if (playersList.length() >= 1) { + for (const QString &player : playersList) { + const QString playerName = profileDB->getPlayerName(player); + if (player != playerName) { + plyrsStr += ", " % playerName % ""; + } + else { + plyrsStr += ", " % player; + } + } + plyrsStr.remove(0, 2); + } + else { + plyrsStr = tr("No Players"); + } + return plyrsStr; +} + +void PictureDialog::exportSnapmaticPicture() +{ + if (rqFullscreen && fullscreenWidget != nullptr) { + PictureExport::exportAsPicture(fullscreenWidget, smpic); + } + else { + PictureExport::exportAsPicture(this, smpic); + } +} + +void PictureDialog::copySnapmaticPicture() +{ + if (rqFullscreen && fullscreenWidget != nullptr) { + PictureExport::exportAsSnapmatic(fullscreenWidget, smpic); + } + else { + PictureExport::exportAsSnapmatic(this, smpic); + } +} + +void PictureDialog::on_labPicture_mouseDoubleClicked(Qt::MouseButton button) +{ + if (button == Qt::LeftButton) { +#if QT_VERSION >= 0x060000 + QRect desktopRect = QApplication::screenAt(pos())->geometry(); +#else + QRect desktopRect = QApplication::desktop()->screenGeometry(this); +#endif + PictureWidget *pictureWidget = new PictureWidget(this); // Work! + pictureWidget->setObjectName("PictureWidget"); +#if QT_VERSION >= 0x050900 + pictureWidget->setWindowFlag(Qt::FramelessWindowHint, true); + pictureWidget->setWindowFlag(Qt::MaximizeUsingFullscreenGeometryHint, true); +#elif QT_VERSION >= 0x050600 + pictureWidget->setWindowFlags(pictureWidget->windowFlags()^Qt::FramelessWindowHint^Qt::MaximizeUsingFullscreenGeometryHint); +#else + pictureWidget->setWindowFlags(pictureWidget->windowFlags()^Qt::FramelessWindowHint); +#endif + pictureWidget->setWindowTitle(windowTitle()); + pictureWidget->setStyleSheet("QLabel#pictureLabel{background-color:black;}"); + pictureWidget->setImage(smpic->getImage(), desktopRect); + pictureWidget->setModal(true); + + fullscreenWidget = pictureWidget; + QObject::connect(this, SIGNAL(newPictureCommited(QImage)), pictureWidget, SLOT(setImage(QImage))); + QObject::connect(pictureWidget, SIGNAL(nextPictureRequested()), this, SLOT(dialogNextPictureRequested())); + QObject::connect(pictureWidget, SIGNAL(previousPictureRequested()), this, SLOT(dialogPreviousPictureRequested())); + + pictureWidget->move(desktopRect.x(), desktopRect.y()); + pictureWidget->resize(desktopRect.width(), desktopRect.height()); + pictureWidget->showFullScreen(); + pictureWidget->setFocus(); + pictureWidget->raise(); + pictureWidget->exec(); + + fullscreenWidget = nullptr; // Work! + delete pictureWidget; // Work! + } +} + +void PictureDialog::on_labPicture_customContextMenuRequested(const QPoint &pos) +{ + exportCustomContextMenuRequestedPrivate(ui->labPicture->mapToGlobal(pos), false); +} + +bool PictureDialog::isIndexed() +{ + return indexed; +} + +int PictureDialog::getIndex() +{ + return index; +} + +void PictureDialog::openPreviewMap() +{ + SnapmaticPicture *picture = smpic; + SnapmaticProperties currentProperties = picture->getSnapmaticProperties(); + MapLocationDialog *mapLocDialog; + if (rqFullscreen && fullscreenWidget != nullptr) { + mapLocDialog = new MapLocationDialog(currentProperties.location.x, currentProperties.location.y, fullscreenWidget); + } + else { + mapLocDialog = new MapLocationDialog(currentProperties.location.x, currentProperties.location.y, this); + } + mapLocDialog->setCayoPerico(currentProperties.location.isCayoPerico); + mapLocDialog->setWindowIcon(windowIcon()); + mapLocDialog->setModal(true); +#ifndef Q_OS_ANDROID + mapLocDialog->show(); +#else + mapLocDialog->showMaximized(); +#endif + mapLocDialog->exec(); + if (mapLocDialog->propUpdated()) { + // Update Snapmatic Properties + currentProperties.location.x = mapLocDialog->getXpos(); + currentProperties.location.y = mapLocDialog->getYpos(); + currentProperties.location.z = 0; + + // Update Snapmatic Picture + QString currentFilePath = picture->getPictureFilePath(); + QString originalFilePath = picture->getOriginalPictureFilePath(); + QString backupFileName = originalFilePath % ".bak"; + if (!QFile::exists(backupFileName)) { + QFile::copy(currentFilePath, backupFileName); + } + SnapmaticProperties fallbackProperties = picture->getSnapmaticProperties(); + picture->setSnapmaticProperties(currentProperties); + if (!picture->exportPicture(currentFilePath)) { + QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("Patching of Snapmatic Properties failed because of I/O Error")); + picture->setSnapmaticProperties(fallbackProperties); + } + else { + updated(); +#ifdef GTA5SYNC_TELEMETRY + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool(); + telemetrySettings.endGroup(); + if (pushUsageData && Telemetry->canPush()) { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "LocationEdited"; + jsonObject["ExtraFlags"] = "Viewer"; + jsonObject["EditedSize"] = QString::number(picture->getContentMaxLength()); +#if QT_VERSION >= 0x060000 + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } +#endif + } + } + delete mapLocDialog; +} + +void PictureDialog::editSnapmaticProperties() +{ + SnapmaticPicture *picture = smpic; + SnapmaticEditor *snapmaticEditor; + if (rqFullscreen && fullscreenWidget != nullptr) { + snapmaticEditor = new SnapmaticEditor(crewDB, profileDB, fullscreenWidget); + } + else { + snapmaticEditor = new SnapmaticEditor(crewDB, profileDB, this); + } + snapmaticEditor->setWindowIcon(windowIcon()); + snapmaticEditor->setSnapmaticPicture(picture); + snapmaticEditor->setModal(true); +#ifndef Q_OS_ANDROID + snapmaticEditor->show(); +#else + snapmaticEditor->showMaximized(); +#endif + snapmaticEditor->exec(); + delete snapmaticEditor; +} + +void PictureDialog::editSnapmaticImage() +{ + QImage *currentImage = new QImage(smpic->getImage()); + ImportDialog *importDialog; + if (rqFullscreen && fullscreenWidget != nullptr) { + importDialog = new ImportDialog(profileName, fullscreenWidget); + } + else { + importDialog = new ImportDialog(profileName, this); + } + importDialog->setWindowIcon(windowIcon()); + importDialog->setImage(currentImage); + importDialog->enableOverwriteMode(); + importDialog->setModal(true); + importDialog->exec(); + if (importDialog->isImportAgreed()) { + const QByteArray previousPicture = smpic->getPictureStream(); + bool success = smpic->setImage(importDialog->image(), importDialog->isUnlimitedBuffer()); + if (success) { + QString currentFilePath = smpic->getPictureFilePath(); + QString originalFilePath = smpic->getOriginalPictureFilePath(); + QString backupFileName = originalFilePath % ".bak"; + if (!QFile::exists(backupFileName)) { + QFile::copy(currentFilePath, backupFileName); + } + if (!smpic->exportPicture(currentFilePath)) { + smpic->setPictureStream(previousPicture); + QMessageBox::warning(this, QApplication::translate("ImageEditorDialog", "Snapmatic Image Editor"), QApplication::translate("ImageEditorDialog", "Patching of Snapmatic Image failed because of I/O Error")); + return; + } + smpic->emitCustomSignal("PictureUpdated"); +#ifdef GTA5SYNC_TELEMETRY + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool(); + telemetrySettings.endGroup(); + if (pushUsageData && Telemetry->canPush()) { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "ImageEdited"; + jsonObject["ExtraFlags"] = "Viewer"; + jsonObject["EditedSize"] = QString::number(smpic->getContentMaxLength()); +#if QT_VERSION >= 0x060000 + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } +#endif + } + else { + QMessageBox::warning(this, QApplication::translate("ImageEditorDialog", "Snapmatic Image Editor"), QApplication::translate("ImageEditorDialog", "Patching of Snapmatic Image failed because of Image Error")); + return; + } + } + delete importDialog; +} + +void PictureDialog::editSnapmaticRawJson() +{ + SnapmaticPicture *picture = smpic; + JsonEditorDialog *jsonEditor; + if (rqFullscreen && fullscreenWidget != nullptr) { + jsonEditor = new JsonEditorDialog(picture, fullscreenWidget); + } + else { + jsonEditor = new JsonEditorDialog(picture, this); + } + jsonEditor->setWindowIcon(windowIcon()); + jsonEditor->setModal(true); +#ifndef Q_OS_ANDROID + jsonEditor->show(); +#else + jsonEditor->showMaximized(); +#endif + jsonEditor->exec(); + delete jsonEditor; +} + +void PictureDialog::updated() +{ + SnapmaticPicture *picture = smpic; // used by macro + crewStr = crewDB->getCrewName(crewID); + if (globalMap.contains(picArea)) { + picAreaStr = globalMap[picArea]; + } + else { + picAreaStr = picArea; + } + setWindowTitle(windowTitleStr.arg(picTitl)); + ui->labJSON->setText(jsonDrawString.arg(locX, locY, locZ, generatePlayersString(), generateCrewString(), picTitl, picAreaStr, created)); + QTimer::singleShot(0, this, SLOT(adaptDialogSize())); +} + +void PictureDialog::customSignal(QString signal) +{ + SnapmaticPicture *picture = smpic; // used by macro + if (signal == "PictureUpdated") { + snapmaticPicture = picture->getImage(); + renderPicture(); + } +} diff --git a/PictureDialog.h b/PictureDialog.h old mode 100755 new mode 100644 index 7cd6958..a35ebb4 --- a/PictureDialog.h +++ b/PictureDialog.h @@ -1,123 +1,136 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef PICTUREDIALOG_H -#define PICTUREDIALOG_H - -#include "SnapmaticPicture.h" -#include "ProfileDatabase.h" -#include "CrewDatabase.h" -#include -#include -#include -#include - -namespace Ui { -class PictureDialog; -} - -class PictureDialog : public QDialog -{ - Q_OBJECT -public: - explicit PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent = 0); - explicit PictureDialog(QWidget *parent = 0); - explicit PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent = 0); - explicit PictureDialog(bool primaryWindow, QWidget *parent = 0); - void setupPictureDialog(bool withDatabase); - void setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, bool indexed, int index); - void setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, int index); - void setSnapmaticPicture(SnapmaticPicture *picture, bool readOk); - void setSnapmaticPicture(SnapmaticPicture *picture, int index); - void setSnapmaticPicture(SnapmaticPicture *picture); - void addPreviousNextButtons(); - void stylizeDialog(); - bool isIndexed(); - int getIndex(); - ~PictureDialog(); - -public slots: - void playerNameUpdated(); - void dialogNextPictureRequested(); - void dialogPreviousPictureRequested(); - void adaptNewDialogSize(QSize newLabelSize); - void exportCustomContextMenuRequested(const QPoint &pos); - -private slots: - void copySnapmaticPicture(); - void exportSnapmaticPicture(); - void triggerFullscreenDoubeClick(); - void on_labPicture_mouseDoubleClicked(Qt::MouseButton button); - void on_labPicture_customContextMenuRequested(const QPoint &pos); - void exportCustomContextMenuRequestedPrivate(const QPoint &pos, bool fullscreen); - void nextPictureRequestedSlot(); - void previousPictureRequestedSlot(); - void renderOverlayPicture(); - void renderPicture(); - -signals: - void nextPictureRequested(); - void previousPictureRequested(); - void newPictureCommited(QImage picture); - void endDatabaseThread(); - -protected: - void closeEvent(QCloseEvent *ev); - bool eventFilter(QObject *obj, QEvent *ev); - void mousePressEvent(QMouseEvent *ev); - bool event(QEvent *event); - -private: - bool primaryWindow; - ProfileDatabase *profileDB; - CrewDatabase *crewDB; - Ui::PictureDialog *ui; - QMap globalMap; - SnapmaticPicture *smpic; - QWidget *fullscreenWidget; - QAction *jpegExportAction; - QAction *pgtaExportAction; - QImage avatarAreaPicture; - QImage snapmaticPicture; - QImage overlayTempImage; - QString jsonDrawString; - QString windowTitleStr; - QStringList plyrsList; - QString picAreaStr; - QString picArea; - QString picTitl; - QString picPath; - QString created; - QString crewID; - QString locX; - QString locY; - QString locZ; - bool overlayEnabled; - bool withDatabase; - bool rqFullscreen; - bool naviEnabled; - bool previewMode; - bool indexed; - int index; - int avatarLocX; - int avatarLocY; - int avatarSize; - QMenu *exportMenu; -}; - -#endif // PICTUREDIALOG_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef PICTUREDIALOG_H +#define PICTUREDIALOG_H + +#include "SnapmaticPicture.h" +#include "ProfileDatabase.h" +#include "CrewDatabase.h" +#include +#include +#include +#include +#include +#include + +namespace Ui { +class PictureDialog; +} + +class PictureDialog : public QDialog +{ + Q_OBJECT +public: + explicit PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent = 0); + explicit PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QString profileName, QWidget *parent = 0); + explicit PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent = 0); + explicit PictureDialog(bool primaryWindow, ProfileDatabase *profileDB, CrewDatabase *crewDB, QString profileName, QWidget *parent = 0); + void setupPictureDialog(); + void setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, bool indexed, int index); + void setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, int index); + void setSnapmaticPicture(SnapmaticPicture *picture, bool readOk); + void setSnapmaticPicture(SnapmaticPicture *picture, int index); + void setSnapmaticPicture(SnapmaticPicture *picture); + void addPreviousNextButtons(); + void styliseDialog(); + bool isIndexed(); + int getIndex(); + ~PictureDialog(); + +public slots: + void adaptDialogSize(); + void crewNameUpdated(); + void playerNameUpdated(); + void dialogNextPictureRequested(); + void dialogPreviousPictureRequested(); + void exportCustomContextMenuRequested(const QPoint &pos); + +private slots: + void copySnapmaticPicture(); + void exportSnapmaticPicture(); + void triggerFullscreenDoubeClick(); + void on_labPicture_mouseDoubleClicked(Qt::MouseButton button); + void on_labPicture_customContextMenuRequested(const QPoint &pos); + void exportCustomContextMenuRequestedPrivate(const QPoint &pos, bool fullscreen); + void nextPictureRequestedSlot(); + void previousPictureRequestedSlot(); + void editSnapmaticProperties(); + void editSnapmaticRawJson(); + void editSnapmaticImage(); + void renderOverlayPicture(); + void renderPicture(); + void openPreviewMap(); + void updated(); + void customSignal(QString signal); + +signals: + void nextPictureRequested(); + void previousPictureRequested(); + void newPictureCommited(QImage picture); + void endDatabaseThread(); + +protected: + void closeEvent(QCloseEvent *ev); + bool eventFilter(QObject *obj, QEvent *ev); + void mousePressEvent(QMouseEvent *ev); +#ifdef Q_OS_WIN +#if QT_VERSION >= 0x060000 + bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result); +#elif QT_VERSION >= 0x050000 + bool nativeEvent(const QByteArray &eventType, void *message, long *result); +#endif +#endif + +private: + QString generateCrewString(); + QString generatePlayersString(); + bool primaryWindow; + ProfileDatabase *profileDB; + CrewDatabase *crewDB; + QString profileName; + Ui::PictureDialog *ui; + QMap globalMap; + SnapmaticPicture *smpic; + QWidget *fullscreenWidget; + QImage avatarAreaPicture; + QImage snapmaticPicture; + QImage overlayTempImage; + QString jsonDrawString; + QString windowTitleStr; + QString picAreaStr; + QString crewStr; + bool overlayEnabled; + bool rqFullscreen; + bool naviEnabled; + bool previewMode; + bool indexed; + int index; + int avatarLocX; + int avatarLocY; + int avatarSize; + QMenu *manageMenu; +#ifdef Q_OS_WIN +#if QT_VERSION >= 0x050000 + QPoint dragPosition; + bool dragStart; +#endif +#endif +}; + +#endif // PICTUREDIALOG_H diff --git a/PictureDialog.ui b/PictureDialog.ui old mode 100755 new mode 100644 index 44eb828..f324d84 --- a/PictureDialog.ui +++ b/PictureDialog.ui @@ -1,254 +1,236 @@ - - - PictureDialog - - - - 0 - 0 - 960 - 602 - - - - %1 - Snapmatic Picture Viewer - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 1 - - - - Qt::CustomContextMenu - - - - - - :/img/960x536.png - - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 4 - - - 10 - - - 4 - - - 4 - - - - - - 0 - 0 - - - - <span style=" font-weight:600;">Title: </span>%6<br/> -<span style=" font-weight:600;">Location: </span>%7 (%1, %2, %3)<br/> -<span style=" font-weight:600;">Players: </span>%4 (Crew %5)<br/> -<span style=" font-weight:600;">Created: </span>%8 - - - true - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - 6 - - - 5 - - - 5 - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 0 - 0 - - - - - - - - 6 - - - - - - 0 - 0 - - - - Qt::NoFocus - - - Export picture - - - &Export - - - false - - - - - - - - 0 - 0 - - - - Qt::NoFocus - - - Close - - - &Close - - - false - - - - - - - - - - - - - - - - - UiModLabel - QLabel -
uimod/UiModLabel.h
- - mouseMoved() - mouseReleased() - mousePressed() - mouseDoubleClicked() - -
-
- - - - - - cmdClose - clicked() - PictureDialog - close() - - - 912 - 514 - - - 479 - 267 - - - - -
+ + + PictureDialog + + + + 0 + 0 + 960 + 618 + + + + Snapmatic Picture Viewer - %1 + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 1 + + + + Qt::CustomContextMenu + + + + + + Qt::AlignCenter + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 4 + + + 10 + + + 4 + + + 4 + + + + + + 0 + 0 + + + + <span style="font-weight:600">Title: </span>%6<br/> +<span style="font-weight:600">Location: </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">Players: </span>%4 (Crew %5)<br/> +<span style="font-weight:600">Created: </span>%8 + + + true + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + 6 + + + 5 + + + 5 + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 0 + 0 + + + + + + + + 6 + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Manage picture + + + &Manage + + + false + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + Close viewer + + + &Close + + + false + + + + + + + + + + + + + + + + + UiModLabel + QLabel +
uimod/UiModLabel.h
+ + mouseMoved() + mouseReleased() + mousePressed() + mouseDoubleClicked() + +
+
+ + + + cmdClose + clicked() + PictureDialog + close() + + + 912 + 514 + + + 479 + 267 + + + + +
diff --git a/PictureExport.cpp b/PictureExport.cpp old mode 100755 new mode 100644 index ad20e9a..8101ddd --- a/PictureExport.cpp +++ b/PictureExport.cpp @@ -1,310 +1,308 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "config.h" -#include "PictureExport.h" -#include "PictureDialog.h" -#include "StandardPaths.h" -#include "SidebarGenerator.h" -#include -#include -#include -#include -#include -#include - -PictureExport::PictureExport() -{ - -} - -void PictureExport::exportAsPicture(QWidget *parent, SnapmaticPicture *picture) -{ - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - - // Picture Settings - // Quality Settings - settings.beginGroup("Pictures"); - int defaultQuality = 100; - QSize defExportSize = QSize(960, 536); - int customQuality = settings.value("CustomQuality", defaultQuality).toInt(); - if (customQuality < 1 || customQuality > 100) - { - customQuality = 100; - } - bool useCustomQuality = settings.value("CustomQualityEnabled", false).toBool(); - - // Size Settings - QSize cusExportSize = settings.value("CustomSize", defExportSize).toSize(); - if (cusExportSize.width() > 3840) - { - cusExportSize.setWidth(3840); - } - else if (cusExportSize.height() > 2160) - { - cusExportSize.setHeight(2160); - } - if (cusExportSize.width() < 1) - { - cusExportSize.setWidth(1); - } - else if (cusExportSize.height() < 1) - { - cusExportSize.setHeight(1); - } - QString sizeMode = settings.value("ExportSizeMode", "Default").toString(); - Qt::AspectRatioMode aspectRatio = (Qt::AspectRatioMode)settings.value("AspectRatio", Qt::KeepAspectRatio).toInt(); - QString defaultExportFormat = settings.value("DefaultExportFormat", ".jpg").toString(); - settings.endGroup(); - // End Picture Settings - - settings.beginGroup("FileDialogs"); - settings.beginGroup("ExportAsPicture"); - -fileDialogPreSave: //Work? - QFileDialog fileDialog(parent); - fileDialog.setFileMode(QFileDialog::AnyFile); - fileDialog.setViewMode(QFileDialog::Detail); - fileDialog.setAcceptMode(QFileDialog::AcceptSave); - fileDialog.setOption(QFileDialog::DontUseNativeDialog, false); - fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true); - fileDialog.setDefaultSuffix("suffix"); - fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); - fileDialog.setWindowTitle(PictureDialog::tr("Export as JPG picture...")); - fileDialog.setLabelText(QFileDialog::Accept, PictureDialog::tr("Export")); - - QStringList filters; - filters << PictureDialog::tr("JPEG picture (*.jpg)"); - filters << PictureDialog::tr("Portable Network Graphics (*.png)"); - fileDialog.setNameFilters(filters); - - QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); - - fileDialog.setSidebarUrls(sidebarUrls); - fileDialog.setDirectory(settings.value("Directory", StandardPaths::picturesLocation()).toString()); - fileDialog.restoreGeometry(settings.value(parent->objectName() + "+Geomtery", "").toByteArray()); - - QString newPictureFileName = getPictureFileName(picture) + defaultExportFormat; - fileDialog.selectFile(newPictureFileName); - - if (fileDialog.exec()) - { - QStringList selectedFiles = fileDialog.selectedFiles(); - if (selectedFiles.length() == 1) - { - QString saveFileFormat; - QString selectedFile = selectedFiles.at(0); - - if (selectedFile.right(4) == ".jpg") - { - saveFileFormat = "JPEG"; - } - else if (selectedFile.right(4) == ".jpeg") - { - saveFileFormat = "JPEG"; - } - else if (selectedFile.right(4) == ".png") - { - saveFileFormat = "PNG"; - } - else if (selectedFile.right(7) == ".suffix") - { - if (fileDialog.selectedNameFilter() == "JPEG picture (*.jpg)") - { - selectedFile.replace(".suffix", ".jpg"); - } - else if (fileDialog.selectedNameFilter() == "Portable Network Graphics (*.png)") - { - selectedFile.replace(".suffix", ".png"); - } - else - { - selectedFile.replace(".suffix", ".jpg"); - } - } - - if (QFile::exists(selectedFile)) - { - if (QMessageBox::Yes == QMessageBox::warning(parent, PictureDialog::tr("Export as JPG picture"), PictureDialog::tr("Overwrite %1 with current Snapmatic picture?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)) - { - if (!QFile::remove(selectedFile)) - { - QMessageBox::warning(parent, PictureDialog::tr("Export as JPG picture"), PictureDialog::tr("Failed to overwrite %1 with current Snapmatic picture").arg("\""+selectedFile+"\"")); - goto fileDialogPreSave; //Work? - } - } - else - { - goto fileDialogPreSave; //Work? - } - } - - // Scale Picture - QImage exportPicture = picture->getImage(); - if (sizeMode == "Desktop") - { - QRect desktopResolution = QApplication::desktop()->screenGeometry(); - exportPicture = exportPicture.scaled(desktopResolution.width(), desktopResolution.height(), aspectRatio, Qt::SmoothTransformation); - } - else if (sizeMode == "Custom") - { - exportPicture = exportPicture.scaled(cusExportSize, aspectRatio, Qt::SmoothTransformation); - } - - bool isSaved; - if (useCustomQuality) - { - isSaved = exportPicture.save(selectedFile, saveFileFormat.toStdString().c_str(), customQuality); - } - else - { - isSaved = exportPicture.save(selectedFile, saveFileFormat.toStdString().c_str(), 100); - } - - if (!isSaved) - { - QMessageBox::warning(parent, PictureDialog::tr("Export as JPG picture"), PictureDialog::tr("Failed to export current Snapmatic picture")); - goto fileDialogPreSave; //Work? - } - } - else - { - QMessageBox::warning(parent, PictureDialog::tr("Export as JPG picture"), PictureDialog::tr("No valid file is selected")); - goto fileDialogPreSave; //Work? - } - } - - settings.setValue(parent->objectName() + "+Geometry", fileDialog.saveGeometry()); - settings.setValue("Directory", fileDialog.directory().absolutePath()); - settings.endGroup(); - settings.endGroup(); -} - -void PictureExport::exportAsSnapmatic(QWidget *parent, SnapmaticPicture *picture) -{ - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - settings.beginGroup("FileDialogs"); - settings.beginGroup("ExportAsSnapmatic"); - - QString adjustedPicPath = picture->getPictureFileName(); - if (adjustedPicPath.right(7) == ".hidden") // for the hidden file system - { - adjustedPicPath.remove(adjustedPicPath.length() - 7, 7); - } - -fileDialogPreSave: //Work? - QFileInfo sgdFileInfo(adjustedPicPath); - QFileDialog fileDialog(parent); - fileDialog.setFileMode(QFileDialog::AnyFile); - fileDialog.setViewMode(QFileDialog::Detail); - fileDialog.setAcceptMode(QFileDialog::AcceptSave); - fileDialog.setOption(QFileDialog::DontUseNativeDialog, false); - fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true); - fileDialog.setDefaultSuffix(".rem"); - fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); - fileDialog.setWindowTitle(PictureDialog::tr("Export as GTA Snapmatic...")); - fileDialog.setLabelText(QFileDialog::Accept, PictureDialog::tr("Export")); - - QStringList filters; - filters << PictureDialog::tr("GTA V Export (*.g5e)"); - filters << PictureDialog::tr("GTA V Raw Export (*.auto)"); - filters << PictureDialog::tr("Snapmatic pictures (PGTA*)"); - fileDialog.setNameFilters(filters); - - QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); - - fileDialog.setSidebarUrls(sidebarUrls); - fileDialog.setDirectory(settings.value("Directory", StandardPaths::documentsLocation()).toString()); - fileDialog.selectFile(QString(picture->getExportPictureFileName() + ".g5e")); - fileDialog.restoreGeometry(settings.value(parent->objectName() + "+Geomtery", "").toByteArray()); - - - if (fileDialog.exec()) - { - QStringList selectedFiles = fileDialog.selectedFiles(); - if (selectedFiles.length() == 1) - { - QString selectedFile = selectedFiles.at(0); - - if (QFile::exists(selectedFile)) - { - if (QMessageBox::Yes == QMessageBox::warning(parent, PictureDialog::tr("Export as GTA Snapmatic"), PictureDialog::tr("Overwrite %1 with current Snapmatic picture?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)) - { - if (!QFile::remove(selectedFile)) - { - QMessageBox::warning(parent, PictureDialog::tr("Export as GTA Snapmatic"), PictureDialog::tr("Failed to overwrite %1 with current Snapmatic picture").arg("\""+selectedFile+"\"")); - goto fileDialogPreSave; //Work? - } - } - else - { - goto fileDialogPreSave; //Work? - } - } - - if (selectedFile.right(4) == ".g5e") - { - bool isExported = picture->exportPicture(selectedFile, "G5E"); - if (!isExported) - { - QMessageBox::warning(parent, PictureDialog::tr("Export as GTA Snapmatic"), PictureDialog::tr("Failed to export current Snapmatic picture")); - goto fileDialogPreSave; //Work? - } - } - else - { - bool isAutoExt = false; - if (selectedFile.right(5) == ".auto") - { - isAutoExt = true; - QString dirPath = QFileInfo(selectedFile).dir().path(); - QString stockFileName = sgdFileInfo.fileName(); - selectedFile = dirPath + "/" + stockFileName; - } - else if (selectedFile.right(4) == ".rem") - { - selectedFile.remove(".rem"); - } - bool isCopied = picture->exportPicture(selectedFile, "PGTA"); - if (!isCopied) - { - QMessageBox::warning(parent, PictureDialog::tr("Export as GTA Snapmatic"), PictureDialog::tr("Failed to export current Snapmatic picture")); - goto fileDialogPreSave; //Work? - } - else - { - if (isAutoExt) QMessageBox::information(parent, PictureDialog::tr("Export as GTA Snapmatic"), PictureDialog::tr("Exported Snapmatic to \"%1\" because of using the .auto extension.").arg(selectedFile)); - } - } - } - else - { - QMessageBox::warning(parent, PictureDialog::tr("Export as GTA Snapmatic"), PictureDialog::tr("No valid file is selected")); - goto fileDialogPreSave; //Work? - } - } - - settings.setValue(parent->objectName() + "+Geometry", fileDialog.saveGeometry()); - settings.setValue("Directory", fileDialog.directory().absolutePath()); - settings.endGroup(); -} - -QString PictureExport::getPictureFileName(SnapmaticPicture *picture) -{ - return picture->getExportPictureFileName(); -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2020 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "config.h" +#include "AppEnv.h" +#include "PictureExport.h" +#include "PictureDialog.h" +#include "StandardPaths.h" +#include "SidebarGenerator.h" +#include +#include +#include +#include +#include +#include + +#if QT_VERSION < 0x050000 +#include +#endif + +#if QT_VERSION >= 0x050000 +#include +#include +#endif + +PictureExport::PictureExport() +{ +} + +void PictureExport::exportAsPicture(QWidget *parent, SnapmaticPicture *picture) +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + + // Picture Settings + // Quality Settings + settings.beginGroup("Pictures"); + int defaultQuality = 100; + QSize defExportSize = SnapmaticPicture::getSnapmaticResolution(); + int customQuality = settings.value("CustomQuality", defaultQuality).toInt(); + if (customQuality < 1 || customQuality > 100) { + customQuality = 100; + } + bool useCustomQuality = settings.value("CustomQualityEnabled", false).toBool(); + + // Size Settings + QSize cusExportSize = settings.value("CustomSize", defExportSize).toSize(); + if (cusExportSize.width() > 3840) { + cusExportSize.setWidth(3840); + } + else if (cusExportSize.height() > 2160) { + cusExportSize.setHeight(2160); + } + if (cusExportSize.width() < 1) { + cusExportSize.setWidth(1); + } + else if (cusExportSize.height() < 1) { + cusExportSize.setHeight(1); + } + QString sizeMode = settings.value("ExportSizeMode", "Default").toString(); + Qt::AspectRatioMode aspectRatio = (Qt::AspectRatioMode)settings.value("AspectRatio", Qt::KeepAspectRatio).toInt(); + QString defaultExportFormat = settings.value("DefaultExportFormat", ".jpg").toString(); + settings.endGroup(); + // End Picture Settings + + settings.beginGroup("FileDialogs"); + bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool(); + settings.beginGroup("ExportAsPicture"); + +fileDialogPreSave: //Work? + QFileDialog fileDialog(parent); + fileDialog.setFileMode(QFileDialog::AnyFile); + fileDialog.setViewMode(QFileDialog::Detail); + fileDialog.setAcceptMode(QFileDialog::AcceptSave); + fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog); + fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true); + fileDialog.setDefaultSuffix("suffix"); + fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); + fileDialog.setWindowTitle(PictureDialog::tr("Export as Picture...")); + fileDialog.setLabelText(QFileDialog::Accept, PictureDialog::tr("Export")); + + QStringList filters; + filters << PictureDialog::tr("JPEG Graphics (*.jpg *.jpeg)"); + filters << PictureDialog::tr("Portable Network Graphics (*.png)"); + fileDialog.setNameFilters(filters); + + QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); + + fileDialog.setSidebarUrls(sidebarUrls); + fileDialog.setDirectory(settings.value("Directory", StandardPaths::picturesLocation()).toString()); + fileDialog.restoreGeometry(settings.value(parent->objectName() % "+Geometry", "").toByteArray()); + + QString newPictureFileName = getPictureFileName(picture) % defaultExportFormat; + fileDialog.selectFile(newPictureFileName); + + if (fileDialog.exec()) { + QStringList selectedFiles = fileDialog.selectedFiles(); + if (selectedFiles.length() == 1) { + QString saveFileFormat; + QString selectedFile = selectedFiles.at(0); + + if (selectedFile.right(4) == ".jpg") { + saveFileFormat = "JPEG"; + } + else if (selectedFile.right(4) == ".jpeg") { + saveFileFormat = "JPEG"; + } + else if (selectedFile.right(4) == ".png") { + saveFileFormat = "PNG"; + } + else if (selectedFile.right(7) == ".suffix") { + if (fileDialog.selectedNameFilter() == "JPEG picture (*.jpg)") { + selectedFile.replace(".suffix", ".jpg"); + } + else if (fileDialog.selectedNameFilter() == "Portable Network Graphics (*.png)") { + selectedFile.replace(".suffix", ".png"); + } + else { + selectedFile.replace(".suffix", ".jpg"); + } + } + + if (QFile::exists(selectedFile)) { + if (QMessageBox::No == QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("Overwrite %1 with current Snapmatic picture?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)) { + goto fileDialogPreSave; //Work? + } + } + + // Scale Picture + QImage exportPicture = picture->getImage(); + if (sizeMode == "Desktop") { +#if QT_VERSION >= 0x050000 + qreal screenRatioPR = AppEnv::screenRatioPR(); + QRect desktopResolution = QApplication::primaryScreen()->geometry(); + int desktopSizeWidth = qRound((double)desktopResolution.width() * screenRatioPR); + int desktopSizeHeight = qRound((double)desktopResolution.height() * screenRatioPR); +#else + QRect desktopResolution = QApplication::desktop()->screenGeometry(); + int desktopSizeWidth = desktopResolution.width(); + int desktopSizeHeight = desktopResolution.height(); +#endif + exportPicture = exportPicture.scaled(desktopSizeWidth, desktopSizeHeight, aspectRatio, Qt::SmoothTransformation); + } + else if (sizeMode == "Custom") { + exportPicture = exportPicture.scaled(cusExportSize, aspectRatio, Qt::SmoothTransformation); + } + + int errorId = 0; + bool isSaved = false; +#if QT_VERSION >= 0x050000 + QSaveFile *picFile = new QSaveFile(selectedFile); +#else + QFile *picFile = new QFile(selectedFile); +#endif + if (picFile->open(QIODevice::WriteOnly)) { + isSaved = exportPicture.save(picFile, saveFileFormat.toStdString().c_str(), useCustomQuality ? customQuality : defaultQuality); +#if QT_VERSION >= 0x050000 + if (isSaved) { + isSaved = picFile->commit(); + } + else { + errorId = 1; + } +#else + picFile->close(); +#endif + } + else { + errorId = 2; + } + delete picFile; + + if (!isSaved) { + switch (errorId) { + case 0: + QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("Failed to export the picture because the system occurred a write failure")); + break; + case 1: + QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("Failed to export the picture because the format detection failures")); + break; + case 2: + QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("Failed to export the picture because the file can't be written")); + break; + default: + QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("Failed to export the picture because of an unknown reason")); + } + goto fileDialogPreSave; //Work? + } + } + else { + QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("No valid file is selected")); + goto fileDialogPreSave; //Work? + } + } + + settings.setValue(parent->objectName() % "+Geometry", fileDialog.saveGeometry()); + settings.setValue("Directory", fileDialog.directory().absolutePath()); + settings.endGroup(); + settings.endGroup(); +} + +void PictureExport::exportAsSnapmatic(QWidget *parent, SnapmaticPicture *picture) +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("FileDialogs"); + bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool(); + settings.beginGroup("ExportAsSnapmatic"); + + QString adjustedPicPath = picture->getOriginalPictureFileName(); + +fileDialogPreSave: //Work? + QFileInfo sgdFileInfo(adjustedPicPath); + QFileDialog fileDialog(parent); + fileDialog.setFileMode(QFileDialog::AnyFile); + fileDialog.setViewMode(QFileDialog::Detail); + fileDialog.setAcceptMode(QFileDialog::AcceptSave); + fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog); + fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true); + fileDialog.setDefaultSuffix(".rem"); + fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); + fileDialog.setWindowTitle(PictureDialog::tr("Export as Snapmatic...")); + fileDialog.setLabelText(QFileDialog::Accept, PictureDialog::tr("Export")); + + QStringList filters; + filters << PictureDialog::tr("GTA V Export (*.g5e)"); +#ifndef GTA5SYNC_FLATPAK + filters << PictureDialog::tr("GTA V Raw Export (*.auto)"); +#endif + filters << PictureDialog::tr("Snapmatic pictures (PGTA*)"); + fileDialog.setNameFilters(filters); + + QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); + + fileDialog.setSidebarUrls(sidebarUrls); + fileDialog.setDirectory(settings.value("Directory", StandardPaths::documentsLocation()).toString()); + fileDialog.restoreGeometry(settings.value(parent->objectName() % "+Geometry", "").toByteArray()); + fileDialog.selectFile(QString(picture->getExportPictureFileName() % ".g5e")); + + if (fileDialog.exec()) { + QStringList selectedFiles = fileDialog.selectedFiles(); + if (selectedFiles.length() == 1) { + QString selectedFile = selectedFiles.at(0); + bool isAutoExt = false; +#ifndef GTA5SYNC_FLATPAK + if (selectedFile.right(5) == ".auto") { + isAutoExt = true; + QString dirPath = QFileInfo(selectedFile).dir().path(); + QString stockFileName = sgdFileInfo.fileName(); + selectedFile = dirPath % "/" % stockFileName; + } +#endif + if (selectedFile.right(4) == ".rem") { + selectedFile.remove(selectedFile.length() - 4, 4); + } + + if (QFile::exists(selectedFile)) { + if (QMessageBox::No == QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Overwrite %1 with current Snapmatic picture?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)) { + goto fileDialogPreSave; //Work? + } + } + + if (selectedFile.right(4) == ".g5e") { + bool isExported = picture->exportPicture(selectedFile, SnapmaticFormat::G5E_Format); + if (!isExported) { + QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Failed to export current Snapmatic picture")); + goto fileDialogPreSave; //Work? + } + } + else { + bool isCopied = picture->exportPicture(selectedFile, SnapmaticFormat::PGTA_Format); + if (!isCopied) { + QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Failed to export current Snapmatic picture")); + goto fileDialogPreSave; //Work? + } + else { + if (isAutoExt) QMessageBox::information(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Exported Snapmatic to \"%1\" because of using the .auto extension.").arg(selectedFile)); + } + } + } + else { + QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("No valid file is selected")); + goto fileDialogPreSave; //Work? + } + } + + settings.setValue(parent->objectName() % "+Geometry", fileDialog.saveGeometry()); + settings.setValue("Directory", fileDialog.directory().absolutePath()); + settings.endGroup(); +} + +QString PictureExport::getPictureFileName(SnapmaticPicture *picture) +{ + return picture->getExportPictureFileName(); +} diff --git a/PictureExport.h b/PictureExport.h old mode 100755 new mode 100644 index f7aeee9..623f093 --- a/PictureExport.h +++ b/PictureExport.h @@ -1,35 +1,35 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef PICTUREEXPORT_H -#define PICTUREEXPORT_H - -#include "SnapmaticPicture.h" -#include -#include - -class PictureExport -{ -public: - PictureExport(); - static void exportAsPicture(QWidget *parent, SnapmaticPicture *picture); - static void exportAsSnapmatic(QWidget *parent, SnapmaticPicture *picture); - static QString getPictureFileName(SnapmaticPicture *picture); -}; - -#endif // PICTUREEXPORT_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef PICTUREEXPORT_H +#define PICTUREEXPORT_H + +#include "SnapmaticPicture.h" +#include +#include + +class PictureExport +{ +public: + PictureExport(); + static void exportAsPicture(QWidget *parent, SnapmaticPicture *picture); + static void exportAsSnapmatic(QWidget *parent, SnapmaticPicture *picture); + static QString getPictureFileName(SnapmaticPicture *picture); +}; + +#endif // PICTUREEXPORT_H diff --git a/PictureWidget.cpp b/PictureWidget.cpp index 95d51a1..49bdb8b 100644 --- a/PictureWidget.cpp +++ b/PictureWidget.cpp @@ -1,6 +1,6 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2020 Syping * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,13 +19,12 @@ #include "PictureDialog.h" #include "PictureWidget.h" #include "UiModLabel.h" -#include +#include "AppEnv.h" #include #include #include #include #include -#include PictureWidget::PictureWidget(QWidget *parent) : QDialog(parent) { @@ -44,7 +43,6 @@ PictureWidget::PictureWidget(QWidget *parent) : QDialog(parent) QObject::connect(pictureLabel, SIGNAL(mouseDoubleClicked(Qt::MouseButton)), this, SLOT(pictureDoubleClicked(Qt::MouseButton))); QObject::connect(pictureLabel, SIGNAL(customContextMenuRequested(QPoint)), parent, SLOT(exportCustomContextMenuRequested(QPoint))); - QObject::connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(updateWindowSize(int))); setLayout(widgetLayout); } @@ -58,12 +56,10 @@ PictureWidget::~PictureWidget() bool PictureWidget::eventFilter(QObject *obj, QEvent *ev) { - if (obj == this) - { - if (ev->type() == QEvent::KeyPress) - { + if (obj == this) { + if (ev->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = (QKeyEvent*)ev; - switch (keyEvent->key()){ + switch (keyEvent->key()) { case Qt::Key_Left: emit previousPictureRequested(); break; @@ -78,32 +74,29 @@ bool PictureWidget::eventFilter(QObject *obj, QEvent *ev) void PictureWidget::pictureDoubleClicked(Qt::MouseButton button) { - if (button == Qt::LeftButton) - { + if (button == Qt::LeftButton) { close(); } } void PictureWidget::setImage(QImage image_, QRect rec) { + const qreal screenRatioPR = AppEnv::screenRatioPR(); image = image_; - pictureLabel->setPixmap(QPixmap::fromImage(image.scaled(rec.width(), rec.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation))); + QPixmap pixmap = QPixmap::fromImage(image.scaled(rec.width() * screenRatioPR, rec.height() * screenRatioPR, Qt::KeepAspectRatio, Qt::SmoothTransformation)); +#if QT_VERSION >= 0x050600 + pixmap.setDevicePixelRatio(AppEnv::screenRatioPR()); +#endif + pictureLabel->setPixmap(pixmap); } void PictureWidget::setImage(QImage image_) { + const qreal screenRatioPR = AppEnv::screenRatioPR(); image = image_; - pictureLabel->setPixmap(QPixmap::fromImage(image.scaled(geometry().width(), geometry().height(), Qt::KeepAspectRatio, Qt::SmoothTransformation))); -} - -void PictureWidget::updateWindowSize(int screenID) -{ - if (screenID == QApplication::desktop()->screenNumber(this)) - { - QRect desktopRect = QApplication::desktop()->screenGeometry(this); - this->move(desktopRect.x(), desktopRect.y()); - this->resize(desktopRect.width(), desktopRect.height()); - this->showFullScreen(); - pictureLabel->setPixmap(QPixmap::fromImage(image.scaled(desktopRect.width(), desktopRect.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation))); - } + QPixmap pixmap = QPixmap::fromImage(image.scaled(geometry().width() * screenRatioPR, geometry().height() * screenRatioPR, Qt::KeepAspectRatio, Qt::SmoothTransformation)); +#if QT_VERSION >= 0x050600 + pixmap.setDevicePixelRatio(screenRatioPR); +#endif + pictureLabel->setPixmap(pixmap); } diff --git a/PictureWidget.h b/PictureWidget.h index 2ebd21c..d2e64f3 100644 --- a/PictureWidget.h +++ b/PictureWidget.h @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016-2017 Syping * * This program is free software: you can redistribute it and/or modify @@ -46,7 +46,6 @@ private: private slots: void pictureDoubleClicked(Qt::MouseButton button); - void updateWindowSize(int screenID); signals: void nextPictureRequested(); diff --git a/PlayerListDialog.cpp b/PlayerListDialog.cpp new file mode 100644 index 0000000..7c1b9a3 --- /dev/null +++ b/PlayerListDialog.cpp @@ -0,0 +1,225 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "PlayerListDialog.h" +#include "ui_PlayerListDialog.h" +#include "wrapper.h" +#include "AppEnv.h" +#include +#include +#include +#include +#include +#include +#include + +PlayerListDialog::PlayerListDialog(QStringList players, ProfileDatabase *profileDB, QWidget *parent) : + QDialog(parent), players(players), profileDB(profileDB), + ui(new Ui::PlayerListDialog) +{ + // Set Window Flags +#if QT_VERSION >= 0x050900 + setWindowFlag(Qt::WindowContextHelpButtonHint, false); +#else + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); +#endif + + listUpdated = false; + ui->setupUi(this); + ui->cmdCancel->setDefault(true); + ui->cmdCancel->setFocus(); + + // Set Icon for Apply Button + if (QIcon::hasThemeIcon("dialog-ok-apply")) { + ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok-apply")); + } + else if (QIcon::hasThemeIcon("dialog-apply")) { + ui->cmdApply->setIcon(QIcon::fromTheme("dialog-apply")); + } + else if (QIcon::hasThemeIcon("gtk-apply")) { + ui->cmdApply->setIcon(QIcon::fromTheme("gtk-apply")); + } + else if (QIcon::hasThemeIcon("dialog-ok")) { + ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok")); + } + else if (QIcon::hasThemeIcon("gtk-ok")) { + ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok")); + } + + // Set Icon for Cancel Button + if (QIcon::hasThemeIcon("dialog-cancel")) { + ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel")); + } + else if (QIcon::hasThemeIcon("gtk-cancel")) { + ui->cmdCancel->setIcon(QIcon::fromTheme("gtk-cancel")); + } + + // Set Icon for Manage Buttons + if (QIcon::hasThemeIcon("go-previous") && QIcon::hasThemeIcon("go-next") && QIcon::hasThemeIcon("list-add")) { +#if QT_VERSION < 0x050600 + qreal screenRatio = AppEnv::screenRatio(); + if (screenRatio != 1) { + QSize iconSize = ui->cmdMakeAv->iconSize(); + iconSize = QSize(iconSize.width() * screenRatio, iconSize.height() * screenRatio); + ui->cmdMakeAv->setIconSize(iconSize); + ui->cmdMakeSe->setIconSize(iconSize); + ui->cmdMakeAd->setIconSize(iconSize); + } +#endif + ui->cmdMakeAv->setIcon(QIcon::fromTheme("go-previous")); + ui->cmdMakeSe->setIcon(QIcon::fromTheme("go-next")); + ui->cmdMakeAd->setIcon(QIcon::fromTheme("list-add")); + } + else { +#if QT_VERSION < 0x050600 + qreal screenRatio = AppEnv::screenRatio(); + if (screenRatio != 1) { + QSize iconSize = ui->cmdMakeAv->iconSize(); + iconSize = QSize(iconSize.width() * screenRatio, iconSize.height() * screenRatio); + ui->cmdMakeAv->setIconSize(iconSize); + ui->cmdMakeSe->setIconSize(iconSize); + ui->cmdMakeAd->setIconSize(iconSize); + } +#endif + ui->cmdMakeAv->setIcon(QIcon(AppEnv::getImagesFolder() % "/back.svgz")); + ui->cmdMakeSe->setIcon(QIcon(AppEnv::getImagesFolder() % "/next.svgz")); + ui->cmdMakeAd->setIcon(QIcon(AppEnv::getImagesFolder() % "/add.svgz")); + } + buildInterface(); + + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + resize(500 * screenRatio, 350 * screenRatio); +} + +PlayerListDialog::~PlayerListDialog() +{ + for (QObject *object : ui->listAvPlayers->children()) { + delete object; + } + for (QObject *object : ui->listSePlayers->children()) { + delete object; + } + delete ui; +} + +void PlayerListDialog::on_cmdCancel_clicked() +{ + close(); +} + +void PlayerListDialog::buildInterface() +{ + const QStringList dbPlayers = profileDB->getPlayers(); + for (const QString &sePlayer : qAsConst(players)) { + QListWidgetItem *playerItem = new QListWidgetItem(profileDB->getPlayerName(sePlayer)); + playerItem->setData(Qt::UserRole, sePlayer); + ui->listSePlayers->addItem(playerItem); + } + for (const QString &dbPlayer : dbPlayers) { + if (!players.contains(dbPlayer)) { + QListWidgetItem *playerItem = new QListWidgetItem(profileDB->getPlayerName(dbPlayer)); + playerItem->setData(Qt::UserRole, dbPlayer); + ui->listAvPlayers->addItem(playerItem); + } + } + ui->listAvPlayers->sortItems(Qt::AscendingOrder); +} + +void PlayerListDialog::on_cmdMakeAv_clicked() +{ + for (QListWidgetItem *item : ui->listSePlayers->selectedItems()) { + QString playerName = item->text(); + int playerID = item->data(Qt::UserRole).toInt(); + delete item; + QListWidgetItem *playerItem = new QListWidgetItem(playerName); + playerItem->setData(Qt::UserRole, playerID); + ui->listAvPlayers->addItem(playerItem); + ui->listAvPlayers->sortItems(Qt::AscendingOrder); + } +} + +void PlayerListDialog::on_cmdMakeSe_clicked() +{ + int maxPlayers = 30; + if (maxPlayers < ui->listSePlayers->count() + ui->listAvPlayers->selectedItems().count()) { + QMessageBox::warning(this, tr("Add Players..."), tr("Failed to add more Players because the limit of Players are %1!").arg(QString::number(maxPlayers))); + return; + } + for (QListWidgetItem *item : ui->listAvPlayers->selectedItems()) { + QString playerName = item->text(); + int playerID = item->data(Qt::UserRole).toInt(); + delete item; + QListWidgetItem *playerItem = new QListWidgetItem(playerName); + playerItem->setData(Qt::UserRole, playerID); + ui->listSePlayers->addItem(playerItem); + } +} + +void PlayerListDialog::on_cmdMakeAd_clicked() +{ + bool playerOk; + int playerID = QInputDialog::getInt(this, tr("Add Player..."), tr("Enter Social Club Player ID"), 1, 1, 214783647, 1, &playerOk, windowFlags()); + if (playerOk) { + for (int i = 0; i < ui->listAvPlayers->count(); ++i) { + QListWidgetItem *item = ui->listAvPlayers->item(i); + QString itemPlayerName = item->text(); + int itemPlayerID = item->data(Qt::UserRole).toInt(); + if (itemPlayerID == playerID) { + delete item; + QListWidgetItem *playerItem = new QListWidgetItem(itemPlayerName); + playerItem->setData(Qt::UserRole, playerID); + ui->listSePlayers->addItem(playerItem); + return; + } + } + for (int i = 0; i < ui->listSePlayers->count(); ++i) { + QListWidgetItem *item = ui->listSePlayers->item(i); + int itemPlayerID = item->data(Qt::UserRole).toInt(); + if (itemPlayerID == playerID) + { + QMessageBox::warning(this, tr("Add Player..."), tr("Failed to add Player %1 because Player %1 is already added!").arg(QString::number(playerID))); + return; + } + } + QListWidgetItem *playerItem = new QListWidgetItem(QString::number(playerID)); + playerItem->setData(Qt::UserRole, playerID); + ui->listSePlayers->addItem(playerItem); + } +} + +void PlayerListDialog::on_cmdApply_clicked() +{ + players.clear(); + for (int i = 0; i < ui->listSePlayers->count(); ++i) { + players += ui->listSePlayers->item(i)->data(Qt::UserRole).toString(); + } + emit playerListUpdated(players); + listUpdated = true; + close(); +} + +QStringList PlayerListDialog::getPlayerList() const +{ + return players; +} + +bool PlayerListDialog::isListUpdated() +{ + return listUpdated; +} diff --git a/PlayerListDialog.h b/PlayerListDialog.h new file mode 100644 index 0000000..c341a22 --- /dev/null +++ b/PlayerListDialog.h @@ -0,0 +1,57 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef PLAYERLISTDIALOG_H +#define PLAYERLISTDIALOG_H + +#include "ProfileDatabase.h" +#include + +namespace Ui { +class PlayerListDialog; +} + +class PlayerListDialog : public QDialog +{ + Q_OBJECT + +public: + explicit PlayerListDialog(QStringList players, ProfileDatabase *profileDB, QWidget *parent = 0); + QStringList getPlayerList() const; + bool isListUpdated(); + ~PlayerListDialog(); + +private slots: + void on_cmdCancel_clicked(); + void on_cmdMakeAv_clicked(); + void on_cmdMakeSe_clicked(); + void on_cmdMakeAd_clicked(); + void on_cmdApply_clicked(); + +private: + QStringList players; + ProfileDatabase *profileDB; + Ui::PlayerListDialog *ui; + bool listUpdated; + void buildInterface(); + +signals: + void playerListUpdated(QStringList playerList); +}; + +#endif // PLAYERLISTDIALOG_H diff --git a/PlayerListDialog.ui b/PlayerListDialog.ui new file mode 100644 index 0000000..3bdda4c --- /dev/null +++ b/PlayerListDialog.ui @@ -0,0 +1,173 @@ + + + PlayerListDialog + + + + 0 + 0 + 500 + 350 + + + + Edit Players... + + + + + + + + + + Available Players: + + + + + + + QAbstractItemView::ExtendedSelection + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Qt::NoFocus + + + false + + + + + + + Qt::NoFocus + + + + + + false + + + + + + + Qt::NoFocus + + + + + + false + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + + + Selected Players: + + + + + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + &Apply + + + + + + + + 0 + 0 + + + + &Cancel + + + + + + + + + + diff --git a/ProfileDatabase.cpp b/ProfileDatabase.cpp old mode 100755 new mode 100644 index b7bc1c7..09ff581 --- a/ProfileDatabase.cpp +++ b/ProfileDatabase.cpp @@ -1,61 +1,85 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "ProfileDatabase.h" -#include "StandardPaths.h" -#include "config.h" -#include -#include - -ProfileDatabase::ProfileDatabase(QObject *parent) : QObject(parent) -{ - QDir dir; - dir.mkpath(StandardPaths::dataLocation()); - dir.setPath(StandardPaths::dataLocation()); - QString dirPath = dir.absolutePath(); - QString defaultConfPath = dirPath + QDir::separator() + "players.ini"; - - QSettings confPathSettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - confPathSettings.beginGroup("Database"); - QString confPathFile = confPathSettings.value("Players", defaultConfPath).toString(); - confPathSettings.endGroup(); - - profileDB = new QSettings(confPathFile, QSettings::IniFormat); - profileDB->beginGroup("Players"); -} - -ProfileDatabase::~ProfileDatabase() -{ - profileDB->endGroup(); - delete profileDB; -} - -QStringList ProfileDatabase::getPlayers() -{ - return profileDB->childKeys(); -} - -QString ProfileDatabase::getPlayerName(int playerID) -{ - return profileDB->value(QString::number(playerID), playerID).toString(); -} - -void ProfileDatabase::setPlayerName(int playerID, QString playerName) -{ - profileDB->setValue(QString::number(playerID), playerName); -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "ProfileDatabase.h" +#include "StandardPaths.h" +#include "config.h" +#include +#include +#include +#include +#include + +ProfileDatabase::ProfileDatabase(QObject *parent) : QObject(parent) +{ + QDir dir; + dir.mkpath(StandardPaths::dataLocation()); + dir.setPath(StandardPaths::dataLocation()); + QString dirPath = dir.absolutePath(); + QString defaultConfPath = dirPath % "/players.ini"; + + QSettings confPathSettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + confPathSettings.beginGroup("Database"); + QString confPathFile = confPathSettings.value("Players", defaultConfPath).toString(); + confPathSettings.endGroup(); + + profileDB = new QSettings(confPathFile, QSettings::IniFormat); + profileDB->beginGroup("Players"); +} + +ProfileDatabase::~ProfileDatabase() +{ + profileDB->endGroup(); + delete profileDB; +} + +QStringList ProfileDatabase::getPlayers() +{ + QMutexLocker locker(&mutex); +#ifdef GTA5SYNC_DEBUG + qDebug() << "getPlayers"; +#endif + return profileDB->childKeys(); +} + +QString ProfileDatabase::getPlayerName(QString playerID) +{ + QMutexLocker locker(&mutex); +#ifdef GTA5SYNC_DEBUG + qDebug() << "getPlayerName" << playerID; +#endif + return profileDB->value(playerID, playerID).toString(); +} + +QString ProfileDatabase::getPlayerName(int playerID) +{ + QMutexLocker locker(&mutex); +#ifdef GTA5SYNC_DEBUG + qDebug() << "getPlayerName" << playerID; +#endif + return profileDB->value(QString::number(playerID), playerID).toString(); +} + +void ProfileDatabase::setPlayerName(int playerID, QString playerName) +{ + QMutexLocker locker(&mutex); +#ifdef GTA5SYNC_DEBUG + qDebug() << "setPlayerName" << playerID << playerName; +#endif + profileDB->setValue(QString::number(playerID), playerName); +} diff --git a/ProfileDatabase.h b/ProfileDatabase.h old mode 100755 new mode 100644 index b92fb4f..99bfc80 --- a/ProfileDatabase.h +++ b/ProfileDatabase.h @@ -1,43 +1,46 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef PROFILEDATABASE_H -#define PROFILEDATABASE_H - -#include -#include -#include - -class ProfileDatabase : public QObject -{ - Q_OBJECT -public: - explicit ProfileDatabase(QObject *parent = 0); - QString getPlayerName(int playerID); - QStringList getPlayers(); - ~ProfileDatabase(); - -private: - QSettings *profileDB; - -public slots: - void setPlayerName(int playerID, QString playerName); - -}; - -#endif // PROFILEDATABASE_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef PROFILEDATABASE_H +#define PROFILEDATABASE_H + +#include +#include +#include +#include + +class ProfileDatabase : public QObject +{ + Q_OBJECT +public: + explicit ProfileDatabase(QObject *parent = 0); + QString getPlayerName(QString playerID); + QString getPlayerName(int playerID); + QStringList getPlayers(); + ~ProfileDatabase(); + +private: + mutable QMutex mutex; + QSettings *profileDB; + +public slots: + void setPlayerName(int playerID, QString playerName); + +}; + +#endif // PROFILEDATABASE_H diff --git a/ProfileInterface.cpp b/ProfileInterface.cpp old mode 100755 new mode 100644 index 6050840..8eca40a --- a/ProfileInterface.cpp +++ b/ProfileInterface.cpp @@ -1,6 +1,6 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2022 Syping * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,7 @@ #include "ProfileInterface.h" #include "ui_ProfileInterface.h" +#include "PlayerListDialog.h" #include "SidebarGenerator.h" #include "SnapmaticWidget.h" #include "DatabaseThread.h" @@ -28,29 +29,52 @@ #include "ProfileLoader.h" #include "ExportThread.h" #include "ImportDialog.h" +#include "UiModLabel.h" +#include "pcg_basic.h" +#include "wrapper.h" #include "AppEnv.h" #include "config.h" +#include #include +#include +#include +#include +#include #include #include #include #include #include +#include #include +#include #include #include +#include #include -#include #include -#include #include #include #include #include +#include #include #include #include +#include +#include +#include + +#ifdef GTA5SYNC_TELEMETRY +#include "TelemetryClass.h" +#include +#include +#endif + +#define importTimeFormat "HHmmss" +#define findRetryLimit 500 + ProfileInterface::ProfileInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent) : QWidget(parent), profileDB(profileDB), crewDB(crewDB), threadDB(threadDB), ui(new Ui::ProfileInterface) @@ -62,19 +86,38 @@ ProfileInterface::ProfileInterface(ProfileDatabase *profileDB, CrewDatabase *cre enabledPicStr = tr("Enabled pictures: %1 of %2"); selectedWidgts = 0; profileFolder = ""; - profileLoader = 0; - saSpacerItem = 0; + contextMenuOpened = false; + isProfileLoaded = false; + previousWidget = nullptr; + profileLoader = nullptr; + saSpacerItem = nullptr; - QPalette palette; - QColor baseColor = palette.base().color(); - ui->labVersion->setText(ui->labVersion->text().arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER)); - ui->saProfile->setStyleSheet(QString("QWidget#saProfileContent{background-color: rgb(%1, %2, %3)}").arg(QString::number(baseColor.red()),QString::number(baseColor.green()),QString::number(baseColor.blue()))); - ui->saProfileContent->setFilesMode(true); + updatePalette(); + QString appVersion = QApplication::applicationVersion(); + const char* literalBuildType = GTA5SYNC_BUILDTYPE; +#ifdef GTA5SYNC_COMMIT + if ((strcmp(literalBuildType, REL_BUILDTYPE) != 0) && !appVersion.contains("-")) + appVersion = appVersion % "-" % GTA5SYNC_COMMIT; +#endif + ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, appVersion)); + ui->saProfileContent->setFilesDropEnabled(true); + ui->saProfileContent->setImageDropEnabled(true); - if (QIcon::hasThemeIcon("dialog-close")) - { + // Set Icon for Close Button + if (QIcon::hasThemeIcon("dialog-close")) { ui->cmdCloseProfile->setIcon(QIcon::fromTheme("dialog-close")); } + else if (QIcon::hasThemeIcon("gtk-close")) { + ui->cmdCloseProfile->setIcon(QIcon::fromTheme("gtk-close")); + } + + // Set Icon for Import Button + if (QIcon::hasThemeIcon("document-import")) { + ui->cmdImport->setIcon(QIcon::fromTheme("document-import")); + } + else if (QIcon::hasThemeIcon("document-open")) { + ui->cmdImport->setIcon(QIcon::fromTheme("document-open")); + } // DPI calculation qreal screenRatio = AppEnv::screenRatio(); @@ -82,30 +125,55 @@ ProfileInterface::ProfileInterface(ProfileDatabase *profileDB, CrewDatabase *cre ui->hlButtons->setSpacing(6 * screenRatio); ui->hlButtons->setContentsMargins(9 * screenRatio, 9 * screenRatio, 9 * screenRatio, 9 * screenRatio); #else - ui->hlButtons->setSpacing(6 * screenRatio); - ui->hlButtons->setContentsMargins(9 * screenRatio, 15 * screenRatio, 15 * screenRatio, 17 * screenRatio); +#if QT_VERSION >= 0x060000 + if (QApplication::style()->objectName() == "macos") { +#else + if (QApplication::style()->objectName() == "macintosh") { #endif + ui->hlButtons->setSpacing(6 * screenRatio); + ui->hlButtons->setContentsMargins(9 * screenRatio, 15 * screenRatio, 15 * screenRatio, 17 * screenRatio); + } + else { + ui->hlButtons->setSpacing(6 * screenRatio); + ui->hlButtons->setContentsMargins(9 * screenRatio, 9 * screenRatio, 9 * screenRatio, 9 * screenRatio); + } +#endif + + // Seed RNG + pcg32_srandom_r(&rng, time(NULL), (intptr_t)&rng); + +#if QT_VERSION >= 0x050000 + // Register Metatypes + qRegisterMetaType>(); +#endif + + setMouseTracking(true); + installEventFilter(this); } ProfileInterface::~ProfileInterface() { - foreach(ProfileWidget *widget, widgets.keys()) - { - widgets.remove(widget); - delete widget; + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr) { + widget->removeEventFilter(this); + widget->disconnect(); + delete widget; + } } - foreach(SavegameData *savegame, savegames) - { - savegames.removeAll(savegame); + widgets.clear(); + + for (SavegameData *savegame : qAsConst(savegames)) { delete savegame; } - foreach(SnapmaticPicture *picture, pictures) - { - pictures.removeAll(picture); + savegames.clear(); + + for (SnapmaticPicture *picture : qAsConst(pictures)) { delete picture; } - delete profileLoader; + pictures.clear(); + delete profileLoader; delete ui; } @@ -117,10 +185,15 @@ void ProfileInterface::setProfileFolder(QString folder, QString profile) void ProfileInterface::setupProfileInterface() { + fixedPictures.clear(); ui->labProfileLoading->setText(tr("Loading...")); profileLoader = new ProfileLoader(profileFolder, crewDB); +#if QT_VERSION >= 0x050000 + QObject::connect(profileLoader, SIGNAL(directoryScanned(QVector,QVector)), this, SLOT(directoryScanned(QVector,QVector))); +#endif QObject::connect(profileLoader, SIGNAL(savegameLoaded(SavegameData*, QString)), this, SLOT(savegameLoaded_event(SavegameData*, QString))); QObject::connect(profileLoader, SIGNAL(pictureLoaded(SnapmaticPicture*)), this, SLOT(pictureLoaded_event(SnapmaticPicture*))); + QObject::connect(profileLoader, SIGNAL(pictureFixed(SnapmaticPicture*)), this, SLOT(pictureFixed_event(SnapmaticPicture*))); QObject::connect(profileLoader, SIGNAL(loadingProgress(int,int)), this, SLOT(loadingProgress(int,int))); QObject::connect(profileLoader, SIGNAL(finished()), this, SLOT(profileLoaded_p())); profileLoader->start(); @@ -136,16 +209,20 @@ void ProfileInterface::savegameLoaded(SavegameData *savegame, QString savegamePa SavegameWidget *sgdWidget = new SavegameWidget(this); sgdWidget->setSavegameData(savegame, savegamePath); sgdWidget->setContentMode(contentMode); - widgets[sgdWidget] = "SGD" + QFileInfo(savegamePath).fileName(); - savegames.append(savegame); - if (selectedWidgts != 0 || contentMode == 2) { sgdWidget->setSelectionMode(true); } + sgdWidget->setMouseTracking(true); + sgdWidget->installEventFilter(this); + widgets[sgdWidget] = "SGD" % QFileInfo(savegamePath).fileName(); + savegames += savegame; + if (selectedWidgts != 0 || contentMode == 2) + sgdWidget->setSelectionMode(true); QObject::connect(sgdWidget, SIGNAL(savegameDeleted()), this, SLOT(savegameDeleted_event())); QObject::connect(sgdWidget, SIGNAL(widgetSelected()), this, SLOT(profileWidgetSelected())); QObject::connect(sgdWidget, SIGNAL(widgetDeselected()), this, SLOT(profileWidgetDeselected())); QObject::connect(sgdWidget, SIGNAL(allWidgetsSelected()), this, SLOT(selectAllWidgets())); QObject::connect(sgdWidget, SIGNAL(allWidgetsDeselected()), this, SLOT(deselectAllWidgets())); QObject::connect(sgdWidget, SIGNAL(contextMenuTriggered(QContextMenuEvent*)), this, SLOT(contextMenuTriggeredSGD(QContextMenuEvent*))); - if (inserted) { insertSavegameIPI(sgdWidget); } + if (inserted) + insertSavegameIPI(sgdWidget); } void ProfileInterface::pictureLoaded_event(SnapmaticPicture *picture) @@ -153,14 +230,23 @@ void ProfileInterface::pictureLoaded_event(SnapmaticPicture *picture) pictureLoaded(picture, false); } +void ProfileInterface::pictureFixed_event(SnapmaticPicture *picture) +{ + QString fixedPicture = picture->getPictureStr() % " (" % picture->getPictureTitl() % ")"; + fixedPictures << fixedPicture; +} + void ProfileInterface::pictureLoaded(SnapmaticPicture *picture, bool inserted) { - SnapmaticWidget *picWidget = new SnapmaticWidget(profileDB, crewDB, threadDB, this); + SnapmaticWidget *picWidget = new SnapmaticWidget(profileDB, crewDB, threadDB, profileName, this); picWidget->setSnapmaticPicture(picture); picWidget->setContentMode(contentMode); - widgets[picWidget] = "PIC" + picture->getPictureSortStr(); - pictures.append(picture); - if (selectedWidgts != 0 || contentMode == 2) { picWidget->setSelectionMode(true); } + picWidget->setMouseTracking(true); + picWidget->installEventFilter(this); + widgets[picWidget] = "PIC" % picture->getPictureSortStr(); + pictures += picture; + if (selectedWidgts != 0 || contentMode == 2) + picWidget->setSelectionMode(true); QObject::connect(picWidget, SIGNAL(pictureDeleted()), this, SLOT(pictureDeleted_event())); QObject::connect(picWidget, SIGNAL(widgetSelected()), this, SLOT(profileWidgetSelected())); QObject::connect(picWidget, SIGNAL(widgetDeselected()), this, SLOT(profileWidgetDeselected())); @@ -169,7 +255,8 @@ void ProfileInterface::pictureLoaded(SnapmaticPicture *picture, bool inserted) QObject::connect(picWidget, SIGNAL(nextPictureRequested(QWidget*)), this, SLOT(dialogNextPictureRequested(QWidget*))); QObject::connect(picWidget, SIGNAL(previousPictureRequested(QWidget*)), this, SLOT(dialogPreviousPictureRequested(QWidget*))); QObject::connect(picWidget, SIGNAL(contextMenuTriggered(QContextMenuEvent*)), this, SLOT(contextMenuTriggeredPIC(QContextMenuEvent*))); - if (inserted) { insertSnapmaticIPI(picWidget); } + if (inserted) + insertSnapmaticIPI(picWidget); } void ProfileInterface::loadingProgress(int value, int maximum) @@ -179,110 +266,168 @@ void ProfileInterface::loadingProgress(int value, int maximum) ui->labProfileLoading->setText(loadingStr.arg(QString::number(value), QString::number(maximum))); } +#if QT_VERSION >= 0x050000 +void ProfileInterface::directoryChanged(const QString &path) +{ + Q_UNUSED(path) + + QDir dir(profileFolder); + QVector t_savegameFiles; + QVector t_snapmaticPics; + QVector n_savegameFiles; + QVector n_snapmaticPics; + + const QStringList files = dir.entryList(QDir::Files); + for (const QString &fileName : files) { + if (fileName.startsWith("SGTA5") && !fileName.endsWith(".bak")) { + t_savegameFiles << fileName; + if (!savegameFiles.contains(fileName)) { + n_savegameFiles << fileName; + } + } + if (fileName.startsWith("PGTA5") && !fileName.endsWith(".bak")) { + t_snapmaticPics << fileName; + if (fileName.endsWith(".hidden")) { + const QString originalFileName = fileName.left(fileName.length() - 7); + if (!snapmaticPics.contains(fileName) && !snapmaticPics.contains(originalFileName)) { + n_snapmaticPics << fileName; + } + } + else { + if (!snapmaticPics.contains(fileName) && !snapmaticPics.contains(fileName % ".hidden")) { + n_snapmaticPics << fileName; + } + } + } + } + savegameFiles = t_savegameFiles; + snapmaticPics = t_snapmaticPics; + + if (!n_savegameFiles.isEmpty() || !n_snapmaticPics.isEmpty()) { + QTimer::singleShot(1000, this, [=](){ + for (const QString &fileName : qAsConst(n_savegameFiles)) { + const QString filePath = profileFolder % "/" % fileName; + SavegameData *savegame = new SavegameData(filePath); + if (savegame->readingSavegame()) + savegameLoaded(savegame, filePath, true); + else + delete savegame; + } + for (const QString &fileName : qAsConst(n_snapmaticPics)) { + const QString filePath = profileFolder % "/" % fileName; + SnapmaticPicture *picture = new SnapmaticPicture(filePath); + if (picture->readingPicture(true)) + pictureLoaded(picture, true); + else + delete picture; + } + }); + } +} + +void ProfileInterface::directoryScanned(QVector savegameFiles_s, QVector snapmaticPics_s) +{ + savegameFiles = savegameFiles_s; + snapmaticPics = snapmaticPics_s; + fileSystemWatcher.addPath(profileFolder); + QObject::connect(&fileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &ProfileInterface::directoryChanged); +} +#endif + void ProfileInterface::insertSnapmaticIPI(QWidget *widget) { - ProfileWidget *proWidget = (ProfileWidget*)widget; - if (widgets.contains(proWidget)) - { - QString widgetKey = widgets[proWidget]; + ProfileWidget *proWidget = qobject_cast(widget); + const QString widgetKey = widgets.value(proWidget, QString()); + if (!widgetKey.isNull()) { QStringList widgetsKeyList = widgets.values(); QStringList pictureKeyList = widgetsKeyList.filter("PIC", Qt::CaseSensitive); #if QT_VERSION >= 0x050600 - qSort(pictureKeyList.rbegin(), pictureKeyList.rend()); + std::sort(pictureKeyList.rbegin(), pictureKeyList.rend()); #else qSort(pictureKeyList.begin(), pictureKeyList.end(), qGreater()); #endif - int picIndex = pictureKeyList.indexOf(QRegExp(widgetKey)); + int picIndex = pictureKeyList.indexOf(widgetKey); ui->vlSnapmatic->insertWidget(picIndex, proWidget); - qApp->processEvents(); + QApplication::processEvents(); ui->saProfile->ensureWidgetVisible(proWidget, 0, 0); } } void ProfileInterface::insertSavegameIPI(QWidget *widget) { - ProfileWidget *proWidget = (ProfileWidget*)widget; - if (widgets.contains(proWidget)) - { - QString widgetKey = widgets[proWidget]; + ProfileWidget *proWidget = qobject_cast(widget); + const QString widgetKey = widgets.value(proWidget, QString()); + if (!widgetKey.isNull()) { QStringList widgetsKeyList = widgets.values(); QStringList savegameKeyList = widgetsKeyList.filter("SGD", Qt::CaseSensitive); +#if QT_VERSION >= 0x050600 + std::sort(savegameKeyList.begin(), savegameKeyList.end()); +#else qSort(savegameKeyList.begin(), savegameKeyList.end()); - int sgdIndex = savegameKeyList.indexOf(QRegExp(widgetKey)); +#endif + int sgdIndex = savegameKeyList.indexOf(widgetKey); ui->vlSavegame->insertWidget(sgdIndex, proWidget); - qApp->processEvents(); + QApplication::processEvents(); ui->saProfile->ensureWidgetVisible(proWidget, 0, 0); } } void ProfileInterface::dialogNextPictureRequested(QWidget *dialog) { - PictureDialog *picDialog = (PictureDialog*)dialog; - ProfileWidget *proWidget = (ProfileWidget*)sender(); - if (widgets.contains(proWidget)) - { - QString widgetKey = widgets[proWidget]; + PictureDialog *picDialog = qobject_cast(dialog); + ProfileWidget *proWidget = qobject_cast(sender()); + const QString widgetKey = widgets.value(proWidget, QString()); + if (!widgetKey.isNull()) { QStringList widgetsKeyList = widgets.values(); QStringList pictureKeyList = widgetsKeyList.filter("PIC", Qt::CaseSensitive); #if QT_VERSION >= 0x050600 - qSort(pictureKeyList.rbegin(), pictureKeyList.rend()); + std::sort(pictureKeyList.rbegin(), pictureKeyList.rend()); #else qSort(pictureKeyList.begin(), pictureKeyList.end(), qGreater()); #endif int picIndex; - if (picDialog->isIndexed()) - { + if (picDialog->isIndexed()) { picIndex = picDialog->getIndex(); } - else - { - picIndex = pictureKeyList.indexOf(QRegExp(widgetKey)); + else { + picIndex = pictureKeyList.indexOf(widgetKey); } picIndex++; - if (pictureKeyList.length() > picIndex) - { - QString newWidgetKey = pictureKeyList.at(picIndex); - SnapmaticWidget *picWidget = (SnapmaticWidget*)widgets.key(newWidgetKey); - //picDialog->setMaximumHeight(QWIDGETSIZE_MAX); + if (pictureKeyList.length() > picIndex) { + const QString newWidgetKey = pictureKeyList.at(picIndex); + SnapmaticWidget *picWidget = static_cast(widgets.key(newWidgetKey)); picDialog->setSnapmaticPicture(picWidget->getPicture(), picIndex); - //picDialog->setMaximumHeight(picDialog->height()); } } } void ProfileInterface::dialogPreviousPictureRequested(QWidget *dialog) { - PictureDialog *picDialog = (PictureDialog*)dialog; - ProfileWidget *proWidget = (ProfileWidget*)sender(); - if (widgets.contains(proWidget)) - { - QString widgetKey = widgets[proWidget]; + PictureDialog *picDialog = qobject_cast(dialog); + ProfileWidget *proWidget = qobject_cast(sender()); + const QString widgetKey = widgets.value(proWidget, QString()); + if (!widgetKey.isNull()) { QStringList widgetsKeyList = widgets.values(); QStringList pictureKeyList = widgetsKeyList.filter("PIC", Qt::CaseSensitive); #if QT_VERSION >= 0x050600 - qSort(pictureKeyList.rbegin(), pictureKeyList.rend()); + std::sort(pictureKeyList.rbegin(), pictureKeyList.rend()); #else qSort(pictureKeyList.begin(), pictureKeyList.end(), qGreater()); #endif int picIndex; - if (picDialog->isIndexed()) - { + if (picDialog->isIndexed()) { picIndex = picDialog->getIndex(); } - else - { - picIndex = pictureKeyList.indexOf(QRegExp(widgetKey)); + else { + picIndex = pictureKeyList.indexOf(widgetKey); } - if (picIndex > 0) - { + if (picIndex > 0) { picIndex--; - QString newWidgetKey = pictureKeyList.at(picIndex ); - SnapmaticWidget *picWidget = (SnapmaticWidget*)widgets.key(newWidgetKey); - //picDialog->setMaximumHeight(QWIDGETSIZE_MAX); + const QString newWidgetKey = pictureKeyList.at(picIndex); + SnapmaticWidget *picWidget = static_cast(widgets.key(newWidgetKey)); picDialog->setSnapmaticPicture(picWidget->getPicture(), picIndex); - //picDialog->setMaximumHeight(picDialog->height()); } } } @@ -293,17 +438,19 @@ void ProfileInterface::sortingProfileInterface() ui->vlSnapmatic->setEnabled(false); QStringList widgetsKeyList = widgets.values(); - qSort(widgetsKeyList.begin(), widgetsKeyList.end()); - foreach(QString widgetKey, widgetsKeyList) - { +#if QT_VERSION >= 0x050600 + std::sort(widgetsKeyList.begin(), widgetsKeyList.end()); +#else + qSort(widgetsKeyList.begin(), widgetsKeyList.end()); +#endif + + for (const QString &widgetKey : qAsConst(widgetsKeyList)) { ProfileWidget *widget = widgets.key(widgetKey); - if (widget->getWidgetType() == "SnapmaticWidget") - { + if (widget->getWidgetType() == "SnapmaticWidget") { ui->vlSnapmatic->insertWidget(0, widget); } - else if (widget->getWidgetType() == "SavegameWidget") - { + else if (widget->getWidgetType() == "SavegameWidget") { ui->vlSavegame->addWidget(widget); } } @@ -311,7 +458,7 @@ void ProfileInterface::sortingProfileInterface() ui->vlSavegame->setEnabled(true); ui->vlSnapmatic->setEnabled(true); - qApp->processEvents(); + QApplication::processEvents(); } void ProfileInterface::profileLoaded_p() @@ -322,29 +469,41 @@ void ProfileInterface::profileLoaded_p() ui->swProfile->setCurrentWidget(ui->pageProfile); ui->cmdCloseProfile->setEnabled(true); ui->cmdImport->setEnabled(true); + isProfileLoaded = true; emit profileLoaded(); + + if (!fixedPictures.isEmpty()) { + int fixedInt = 0; + QString fixedStr; + for (const QString &fixedPicture : qAsConst(fixedPictures)) { + if (fixedInt != 0) { fixedStr += "
"; } + fixedStr += fixedPicture; + fixedInt++; + } + QMessageBox::information(this, tr("Snapmatic Loader"), tr("

Following Snapmatic Pictures got repaired

%1").arg(fixedStr)); + } } void ProfileInterface::savegameDeleted_event() { - savegameDeleted((SavegameWidget*)sender(), true); + savegameDeleted(qobject_cast(sender()), true); } void ProfileInterface::savegameDeleted(SavegameWidget *sgdWidget, bool isRemoteEmited) { SavegameData *savegame = sgdWidget->getSavegame(); - if (sgdWidget->isSelected()) { sgdWidget->setSelected(false); } + if (sgdWidget->isSelected()) + sgdWidget->setSelected(false); widgets.remove(sgdWidget); + sgdWidget->disconnect(); + sgdWidget->removeEventFilter(this); + if (sgdWidget == previousWidget) { + previousWidget = nullptr; + } + // Deleting when the widget did send a event cause a crash - if (isRemoteEmited) - { - sgdWidget->deleteLater(); - } - else - { - delete sgdWidget; - } + isRemoteEmited ? sgdWidget->deleteLater() : delete sgdWidget; savegames.removeAll(savegame); delete savegame; @@ -352,24 +511,24 @@ void ProfileInterface::savegameDeleted(SavegameWidget *sgdWidget, bool isRemoteE void ProfileInterface::pictureDeleted_event() { - pictureDeleted((SnapmaticWidget*)sender(), true); + pictureDeleted(qobject_cast(sender()), true); } void ProfileInterface::pictureDeleted(SnapmaticWidget *picWidget, bool isRemoteEmited) { SnapmaticPicture *picture = picWidget->getPicture(); - if (picWidget->isSelected()) { picWidget->setSelected(false); } + if (picWidget->isSelected()) + picWidget->setSelected(false); widgets.remove(picWidget); + picWidget->disconnect(); + picWidget->removeEventFilter(this); + if (picWidget == previousWidget) { + previousWidget = nullptr; + } + // Deleting when the widget did send a event cause a crash - if (isRemoteEmited) - { - picWidget->deleteLater(); - } - else - { - delete picWidget; - } + isRemoteEmited ? picWidget->deleteLater() : delete picWidget; pictures.removeAll(picture); delete picture; @@ -384,6 +543,7 @@ void ProfileInterface::on_cmdImport_clicked() { QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); settings.beginGroup("FileDialogs"); + bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool(); settings.beginGroup("ImportCopy"); fileDialogPreOpen: //Work? @@ -391,64 +551,70 @@ fileDialogPreOpen: //Work? fileDialog.setFileMode(QFileDialog::ExistingFiles); fileDialog.setViewMode(QFileDialog::Detail); fileDialog.setAcceptMode(QFileDialog::AcceptOpen); - fileDialog.setOption(QFileDialog::DontUseNativeDialog, false); + fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog); fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); fileDialog.setWindowTitle(tr("Import...")); - fileDialog.setLabelText(QFileDialog::Accept, tr("Import")); + fileDialog.setLabelText(QFileDialog::Accept, tr("Import...")); + + // Getting readable Image formats + QString imageFormatsStr = " "; + for (const QByteArray &imageFormat : QImageReader::supportedImageFormats()) { + imageFormatsStr += QString("*.") % QString::fromUtf8(imageFormat).toLower() % " "; + } + QString importableFormatsStr = QString("*.g5e SGTA* PGTA*"); + if (!imageFormatsStr.trimmed().isEmpty()) { + importableFormatsStr = QString("*.g5e%1SGTA* PGTA*").arg(imageFormatsStr); + } QStringList filters; - filters << tr("Importable files (*.g5e *.jpg *.png SGTA* PGTA*)"); + filters << tr("Importable files (%1)").arg(importableFormatsStr); filters << tr("GTA V Export (*.g5e)"); filters << tr("Savegames files (SGTA*)"); filters << tr("Snapmatic pictures (PGTA*)"); - filters << tr("All image files (*.jpg *.png)"); + filters << tr("All image files (%1)").arg(imageFormatsStr.trimmed()); filters << tr("All files (**)"); fileDialog.setNameFilters(filters); QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); fileDialog.setSidebarUrls(sidebarUrls); - fileDialog.setDirectory(settings.value(profileName + "+Directory", StandardPaths::documentsLocation()).toString()); - fileDialog.restoreGeometry(settings.value(profileName + "+Geometry", "").toByteArray()); + fileDialog.setDirectory(settings.value(profileName % "+Directory", StandardPaths::documentsLocation()).toString()); + fileDialog.restoreGeometry(settings.value(profileName % "+Geometry", "").toByteArray()); - if (fileDialog.exec()) - { + if (fileDialog.exec()) { QStringList selectedFiles = fileDialog.selectedFiles(); - if (selectedFiles.length() == 1) - { + if (selectedFiles.length() == 1) { QString selectedFile = selectedFiles.at(0); - if (!importFile(selectedFile, true, 0)) goto fileDialogPreOpen; //Work? + QDateTime importDateTime = QDateTime::currentDateTime(); + if (!importFile(selectedFile, importDateTime, true)) goto fileDialogPreOpen; //Work? } - else if (selectedFiles.length() > 1) - { + else if (selectedFiles.length() > 1) { importFilesProgress(selectedFiles); } - else - { - QMessageBox::warning(this, tr("Import"), tr("No valid file is selected")); + else { + QMessageBox::warning(this, tr("Import..."), tr("No valid file is selected")); goto fileDialogPreOpen; //Work? } } - settings.setValue(profileName + "+Geometry", fileDialog.saveGeometry()); - settings.setValue(profileName + "+Directory", fileDialog.directory().absolutePath()); + settings.setValue(profileName % "+Geometry", fileDialog.saveGeometry()); + settings.setValue(profileName % "+Directory", fileDialog.directory().absolutePath()); settings.endGroup(); settings.endGroup(); } -void ProfileInterface::importFilesProgress(QStringList selectedFiles) +bool ProfileInterface::importFilesProgress(QStringList selectedFiles) { int maximumId = selectedFiles.length(); - int overallId = 1; - int currentId = 0; + int overallId = 0; QString errorStr; - QStringList failedFiles; + QStringList failed; // Progress dialog QProgressDialog pbDialog(this); pbDialog.setWindowFlags(pbDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); pbDialog.setWindowTitle(tr("Import...")); - pbDialog.setLabelText(tr("Import file %1 of %2 files").arg(QString::number(overallId), QString::number(maximumId))); + pbDialog.setLabelText(tr("Import file %1 of %2 files").arg(QString::number(1), QString::number(maximumId))); pbDialog.setRange(1, maximumId); pbDialog.setValue(1); pbDialog.setModal(true); @@ -456,277 +622,618 @@ void ProfileInterface::importFilesProgress(QStringList selectedFiles) pbBtn.at(0)->setDisabled(true); QList pbBar = pbDialog.findChildren(); pbBar.at(0)->setTextVisible(false); + pbDialog.setAutoClose(false); pbDialog.show(); - QTime t; - t.start(); - foreach(const QString &selectedFile, selectedFiles) - { + // THREADING HERE PLEASE + QDateTime importDateTime = QDateTime::currentDateTime(); + for (const QString &selectedFile : selectedFiles) { + overallId++; pbDialog.setValue(overallId); pbDialog.setLabelText(tr("Import file %1 of %2 files").arg(QString::number(overallId), QString::number(maximumId))); - if (currentId == 10) - { - // Break until two seconds are over (this prevent import failures) - int elapsedTime = t.elapsed(); - if (elapsedTime > 2000) - { - } - else if (elapsedTime < 0) - { - QEventLoop loop; - QTimer::singleShot(2000, &loop, SLOT(quit())); - loop.exec(); - } - else - { - QEventLoop loop; - QTimer::singleShot(2000 - elapsedTime, &loop, SLOT(quit())); - loop.exec(); - } - currentId = 0; - t.restart(); + importDateTime = QDateTime::currentDateTime(); + if (!importFile(selectedFile, importDateTime, false)) { + failed << QFileInfo(selectedFile).fileName(); } - if (!importFile(selectedFile, false, currentId)) - { - failedFiles << QFileInfo(selectedFile).fileName(); - } - overallId++; - currentId++; } + pbDialog.close(); - foreach (const QString &curErrorStr, failedFiles) - { - errorStr.append(", " + curErrorStr); + for (const QString &curErrorStr : qAsConst(failed)) { + errorStr += ", " % curErrorStr; } - if (errorStr != "") - { + if (errorStr != "") { errorStr.remove(0, 2); - QMessageBox::warning(this, tr("Import"), tr("Import failed with...\n\n%1").arg(errorStr)); + QMessageBox::warning(this, tr("Import..."), tr("Import failed with...\n\n%1").arg(errorStr)); + return false; } + return true; } -bool ProfileInterface::importFile(QString selectedFile, bool notMultiple, int currentId) +bool ProfileInterface::importFile(QString selectedFile, QDateTime importDateTime, bool notMultiple) { QString selectedFileName = QFileInfo(selectedFile).fileName(); - if (QFile::exists(selectedFile)) - { - if (selectedFileName.left(4) == "PGTA" || selectedFileName.right(4) == ".g5e") - { + if (QFile::exists(selectedFile)) { + if ((selectedFileName.left(4) == "PGTA" && !selectedFileName.contains('.')) || selectedFileName.right(4) == ".g5e") { SnapmaticPicture *picture = new SnapmaticPicture(selectedFile); - if (picture->readingPicture(true, true, true)) - { + if (picture->readingPicture(true)) { bool success = importSnapmaticPicture(picture, notMultiple); - if (!success) delete picture; + if (!success) + delete picture; +#ifdef GTA5SYNC_TELEMETRY + if (success && notMultiple) { + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool(); + telemetrySettings.endGroup(); + if (pushUsageData && Telemetry->canPush()) { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "ImportSuccess"; + jsonObject["ImportSize"] = QString::number(picture->getContentMaxLength()); +#if QT_VERSION >= 0x060000 + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonObject["ImportType"] = "Snapmatic"; + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } + } +#endif return success; } - else - { - if (notMultiple) QMessageBox::warning(this, tr("Import"), tr("Failed to read Snapmatic picture")); + else { + if (notMultiple) + QMessageBox::warning(this, tr("Import..."), tr("Failed to read Snapmatic picture")); delete picture; return false; } } - else if (selectedFileName.left(4) == "SGTA") - { + else if (selectedFileName.left(4) == "SGTA") { SavegameData *savegame = new SavegameData(selectedFile); - if (savegame->readingSavegame()) - { + if (savegame->readingSavegame()) { bool success = importSavegameData(savegame, selectedFile, notMultiple); - if (!success) delete savegame; + if (!success) + delete savegame; +#ifdef GTA5SYNC_TELEMETRY + if (success && notMultiple) { + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool(); + telemetrySettings.endGroup(); + if (pushUsageData && Telemetry->canPush()) { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "ImportSuccess"; +#if QT_VERSION >= 0x060000 + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonObject["ImportType"] = "Savegame"; + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } + } +#endif return success; } - else - { - if (notMultiple) QMessageBox::warning(this, tr("Import"), tr("Failed to read Savegame file")); + else { + if (notMultiple) QMessageBox::warning(this, tr("Import..."), tr("Failed to read Savegame file")); delete savegame; return false; } } - else if(selectedFileName.right(4) == ".jpg" || selectedFileName.right(4) == ".png") - { + else if (isSupportedImageFile(selectedFileName)) { SnapmaticPicture *picture = new SnapmaticPicture(":/template/template.g5e"); - if (picture->readingPicture(true, false, true, false)) - { - if (!notMultiple) - { + if (picture->readingPicture(false)) { + if (!notMultiple) { + QFile snapmaticFile(selectedFile); + if (!snapmaticFile.open(QFile::ReadOnly)) { + delete picture; + return false; + } QImage snapmaticImage; + QImageReader snapmaticImageReader; + snapmaticImageReader.setDecideFormatFromContent(true); + snapmaticImageReader.setDevice(&snapmaticFile); + if (!snapmaticImageReader.read(&snapmaticImage)) { + delete picture; + return false; + } QString customImageTitle; QPixmap snapmaticPixmap(960, 536); snapmaticPixmap.fill(Qt::black); QPainter snapmaticPainter(&snapmaticPixmap); - if (!snapmaticImage.load(selectedFile)) - { - delete picture; - return false; - } - if (snapmaticImage.height() == snapmaticImage.width()) - { + if (snapmaticImage.height() == snapmaticImage.width()) { // Avatar mode int diffWidth = 0; int diffHeight = 0; snapmaticImage = snapmaticImage.scaled(470, 470, Qt::KeepAspectRatio, Qt::SmoothTransformation); - if (snapmaticImage.width() > snapmaticImage.height()) - { + if (snapmaticImage.width() > snapmaticImage.height()) { diffHeight = 470 - snapmaticImage.height(); diffHeight = diffHeight / 2; } - else if (snapmaticImage.width() < snapmaticImage.height()) - { + else if (snapmaticImage.width() < snapmaticImage.height()) { diffWidth = 470 - snapmaticImage.width(); diffWidth = diffWidth / 2; } snapmaticPainter.drawImage(145 + diffWidth, 66 + diffHeight, snapmaticImage); - customImageTitle = "Custom Avatar"; + customImageTitle = ImportDialog::tr("Custom Avatar", "Custom Avatar Description in SC, don't use Special Character!"); } - else - { + else { // Picture mode int diffWidth = 0; int diffHeight = 0; snapmaticImage = snapmaticImage.scaled(960, 536, Qt::KeepAspectRatio, Qt::SmoothTransformation); - if (snapmaticImage.width() != 960) - { + if (snapmaticImage.width() != 960) { diffWidth = 960 - snapmaticImage.width(); diffWidth = diffWidth / 2; } - else if (snapmaticImage.height() != 536) - { + else if (snapmaticImage.height() != 536) { diffHeight = 536 - snapmaticImage.height(); diffHeight = diffHeight / 2; } snapmaticPainter.drawImage(0 + diffWidth, 0 + diffHeight, snapmaticImage); - customImageTitle = "Custom Picture"; + customImageTitle = ImportDialog::tr("Custom Picture", "Custom Picture Description in SC, don't use Special Character!"); } snapmaticPainter.end(); - if (!picture->setImage(snapmaticPixmap.toImage())) - { + if (!picture->setImage(snapmaticPixmap.toImage())) { delete picture; return false; } SnapmaticProperties spJson = picture->getSnapmaticProperties(); - spJson.uid = QString(QTime::currentTime().toString("HHmmss") + - QString::number(currentId) + - QString::number(QDate::currentDate().dayOfYear())).toInt(); - spJson.createdDateTime = QDateTime::currentDateTime(); + spJson.uid = getRandomUid(); + bool fExists = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid)); + bool fExistsBackup = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid) % ".bak"); + bool fExistsHidden = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid) % ".hidden"); + int cEnough = 0; + while ((fExists || fExistsBackup || fExistsHidden) && cEnough < findRetryLimit) { + spJson.uid = getRandomUid(); + fExists = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid)); + fExistsBackup = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid) % ".bak"); + fExistsHidden = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid) % ".hidden"); + cEnough++; + } + spJson.createdDateTime = importDateTime; +#if QT_VERSION >= 0x060000 + quint64 timestamp = spJson.createdDateTime.toSecsSinceEpoch(); + if (timestamp > UINT32_MAX) { + timestamp = UINT32_MAX; + } + spJson.createdTimestamp = (quint32)timestamp; +#else spJson.createdTimestamp = spJson.createdDateTime.toTime_t(); +#endif picture->setSnapmaticProperties(spJson); - picture->setPicFileName(QString("PGTA5%1").arg(QString::number(spJson.uid))); + const QString picFileName = QString("PGTA5%1").arg(QString::number(spJson.uid)); + picture->setPicFileName(picFileName); picture->setPictureTitle(customImageTitle); picture->updateStrings(); bool success = importSnapmaticPicture(picture, notMultiple); - if (!success) delete picture; + if (!success) + delete picture; return success; } - else - { + else { bool success = false; - QImage snapmaticImage; - if (!snapmaticImage.load(selectedFile)) - { + QFile snapmaticFile(selectedFile); + if (!snapmaticFile.open(QFile::ReadOnly)) { + QMessageBox::warning(this, tr("Import..."), tr("Can't import %1 because file can't be open").arg("\""+selectedFileName+"\"")); delete picture; return false; } - ImportDialog *importDialog = new ImportDialog(this); - importDialog->setWindowFlags(importDialog->windowFlags()^Qt::WindowContextHelpButtonHint); + QImage *snapmaticImage = new QImage(); + QImageReader snapmaticImageReader; + snapmaticImageReader.setDecideFormatFromContent(true); + snapmaticImageReader.setDevice(&snapmaticFile); + if (!snapmaticImageReader.read(snapmaticImage)) { + QMessageBox::warning(this, tr("Import..."), tr("Can't import %1 because file can't be parsed properly").arg("\""+selectedFileName+"\"")); + delete snapmaticImage; + delete picture; + return false; + } + ImportDialog *importDialog = new ImportDialog(profileName, this); importDialog->setImage(snapmaticImage); importDialog->setModal(true); importDialog->show(); importDialog->exec(); - if (importDialog->isDoImport()) - { - if (picture->setImage(importDialog->image())) - { + if (importDialog->isImportAgreed()) { + if (picture->setImage(importDialog->image(), importDialog->isUnlimitedBuffer())) { SnapmaticProperties spJson = picture->getSnapmaticProperties(); - spJson.uid = QString(QTime::currentTime().toString("HHmmss") + - QString::number(currentId) + - QString::number(QDate::currentDate().dayOfYear())).toInt(); - spJson.createdDateTime = QDateTime::currentDateTime(); + spJson.uid = getRandomUid(); + bool fExists = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid)); + bool fExistsBackup = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid) % ".bak"); + bool fExistsHidden = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid) % ".hidden"); + int cEnough = 0; + while ((fExists || fExistsBackup || fExistsHidden) && cEnough < findRetryLimit) { + spJson.uid = getRandomUid(); + fExists = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid)); + fExistsBackup = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid) % ".bak"); + fExistsHidden = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid) % ".hidden"); + cEnough++; + } + spJson.createdDateTime = importDateTime; +#if QT_VERSION >= 0x060000 + quint64 timestamp = spJson.createdDateTime.toSecsSinceEpoch(); + if (timestamp > UINT32_MAX) { + timestamp = UINT32_MAX; + } + spJson.createdTimestamp = (quint32)timestamp; +#else spJson.createdTimestamp = spJson.createdDateTime.toTime_t(); +#endif picture->setSnapmaticProperties(spJson); - picture->setPicFileName(QString("PGTA5%1").arg(QString::number(spJson.uid))); + const QString picFileName = QString("PGTA5%1").arg(QString::number(spJson.uid)); + picture->setPicFileName(picFileName); picture->setPictureTitle(importDialog->getImageTitle()); picture->updateStrings(); success = importSnapmaticPicture(picture, notMultiple); +#ifdef GTA5SYNC_TELEMETRY + if (success) { + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool(); + telemetrySettings.endGroup(); + if (pushUsageData && Telemetry->canPush()) { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "ImportSuccess"; + jsonObject["ExtraFlag"] = "Dialog"; + jsonObject["ImportSize"] = QString::number(picture->getContentMaxLength()); +#if QT_VERSION >= 0x060000 + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonObject["ImportType"] = "Image"; + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } + } +#endif } } - else - { + else { delete picture; success = true; } delete importDialog; - if (!success) delete picture; + if (!success) + delete picture; return success; } } - else - { + else { delete picture; return false; } } - else - { + else { SnapmaticPicture *picture = new SnapmaticPicture(selectedFile); SavegameData *savegame = new SavegameData(selectedFile); - if (picture->readingPicture()) - { + if (picture->readingPicture()) { bool success = importSnapmaticPicture(picture, notMultiple); delete savegame; - if (!success) delete picture; + if (!success) + delete picture; +#ifdef GTA5SYNC_TELEMETRY + if (success && notMultiple) { + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool(); + telemetrySettings.endGroup(); + if (pushUsageData && Telemetry->canPush()) { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "ImportSuccess"; + jsonObject["ImportSize"] = QString::number(picture->getContentMaxLength()); +#if QT_VERSION >= 0x060000 + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonObject["ImportType"] = "Snapmatic"; + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } + } +#endif return success; } - else if (savegame->readingSavegame()) - { + else if (savegame->readingSavegame()) { bool success = importSavegameData(savegame, selectedFile, notMultiple); delete picture; - if (!success) delete savegame; + if (!success) + delete savegame; +#ifdef GTA5SYNC_TELEMETRY + if (success && notMultiple) { + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool(); + telemetrySettings.endGroup(); + if (pushUsageData && Telemetry->canPush()) { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "ImportSuccess"; +#if QT_VERSION >= 0x060000 + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["ImportTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonObject["ImportType"] = "Savegame"; + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } + } +#endif return success; } - else - { - delete savegame; + else { +#ifdef GTA5SYNC_DEBUG + qDebug() << "ImportError SnapmaticPicture" << picture->getLastStep(); + qDebug() << "ImportError SavegameData" << savegame->getLastStep(); +#endif delete picture; - if (notMultiple) QMessageBox::warning(this, tr("Import"), tr("Can't import %1 because of not valid file format").arg("\""+selectedFileName+"\"")); + delete savegame; + if (notMultiple) QMessageBox::warning(this, tr("Import..."), tr("Can't import %1 because file format can't be detected").arg("\""+selectedFileName+"\"")); return false; } } } - if (notMultiple) QMessageBox::warning(this, tr("Import"), tr("No valid file is selected")); + if (notMultiple) + QMessageBox::warning(this, tr("Import..."), tr("No valid file is selected")); return false; } +bool ProfileInterface::importUrls(const QMimeData *mimeData) +{ + QStringList pathList; + + for (const QUrl ¤tUrl : mimeData->urls()) { + if (currentUrl.isLocalFile()) + pathList += currentUrl.toLocalFile(); + } + + if (pathList.length() == 1) { + QString selectedFile = pathList.at(0); + return importFile(selectedFile, QDateTime::currentDateTime(), true); + } + else if (pathList.length() > 1) { + return importFilesProgress(pathList); + } + return false; +} + +bool ProfileInterface::importRemote(QUrl remoteUrl) +{ + bool retValue = false; + QDialog urlPasteDialog(this); +#if QT_VERSION >= 0x050000 + urlPasteDialog.setObjectName(QStringLiteral("UrlPasteDialog")); +#else + urlPasteDialog.setObjectName(QString::fromUtf8("UrlPasteDialog")); +#endif + urlPasteDialog.setWindowFlags(urlPasteDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); + urlPasteDialog.setWindowTitle(tr("Import...")); + urlPasteDialog.setModal(true); + QVBoxLayout urlPasteLayout(&urlPasteDialog); +#if QT_VERSION >= 0x050000 + urlPasteLayout.setObjectName(QStringLiteral("UrlPasteLayout")); +#else + urlPasteLayout.setObjectName(QString::fromUtf8("UrlPasteLayout")); +#endif + urlPasteDialog.setLayout(&urlPasteLayout); + UiModLabel urlPasteLabel(&urlPasteDialog); +#if QT_VERSION >= 0x050000 + urlPasteLabel.setObjectName(QStringLiteral("UrlPasteLabel")); +#else + urlPasteLabel.setObjectName(QString::fromUtf8("UrlPasteLabel")); +#endif + + urlPasteLabel.setText(tr("Prepare Content for Import...")); + urlPasteLayout.addWidget(&urlPasteLabel); + urlPasteDialog.setFixedSize(urlPasteDialog.sizeHint()); + urlPasteDialog.show(); + + QNetworkAccessManager *netManager = new QNetworkAccessManager(); + QNetworkRequest netRequest(remoteUrl); + netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent()); + netRequest.setRawHeader("Accept", "text/html"); + netRequest.setRawHeader("Accept-Charset", "utf-8"); + netRequest.setRawHeader("Accept-Language", "en-US,en;q=0.9"); + netRequest.setRawHeader("Connection", "keep-alive"); + QNetworkReply *netReply = netManager->get(netRequest); + QEventLoop *downloadLoop = new QEventLoop(); + QObject::connect(netReply, SIGNAL(finished()), downloadLoop, SLOT(quit())); + QTimer::singleShot(30000, downloadLoop, SLOT(quit())); + downloadLoop->exec(); + downloadLoop->disconnect(); + delete downloadLoop; + + urlPasteDialog.close(); + + if (netReply->isFinished()) { + QImage *snapmaticImage = new QImage(); + QImageReader snapmaticImageReader; + snapmaticImageReader.setDecideFormatFromContent(true); + snapmaticImageReader.setDevice(netReply); + if (snapmaticImageReader.read(snapmaticImage)) { + retValue = importImage(snapmaticImage, QDateTime::currentDateTime()); + } + else { + delete snapmaticImage; + } + } + else { + netReply->abort(); + } + delete netReply; + delete netManager; + return retValue; +} + +bool ProfileInterface::importImage(QImage *snapmaticImage, QDateTime importDateTime) +{ + SnapmaticPicture *picture = new SnapmaticPicture(":/template/template.g5e"); + if (picture->readingPicture(false)) { + bool success = false; + ImportDialog *importDialog = new ImportDialog(profileName, this); + importDialog->setImage(snapmaticImage); + importDialog->setModal(true); + importDialog->show(); + importDialog->exec(); + if (importDialog->isImportAgreed()) { + if (picture->setImage(importDialog->image(), importDialog->isUnlimitedBuffer())) { + SnapmaticProperties spJson = picture->getSnapmaticProperties(); + spJson.uid = getRandomUid(); + bool fExists = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid)); + bool fExistsBackup = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid) % ".bak"); + bool fExistsHidden = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid) % ".hidden"); + int cEnough = 0; + while ((fExists || fExistsBackup || fExistsHidden) && cEnough < findRetryLimit) { + spJson.uid = getRandomUid(); + fExists = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid)); + fExistsBackup = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid) % ".bak"); + fExistsHidden = QFile::exists(profileFolder % "/PGTA5" % QString::number(spJson.uid) % ".hidden"); + cEnough++; + } + spJson.createdDateTime = importDateTime; +#if QT_VERSION >= 0x060000 + quint64 timestamp = spJson.createdDateTime.toSecsSinceEpoch(); + if (timestamp > UINT32_MAX) { + timestamp = UINT32_MAX; + } + spJson.createdTimestamp = (quint32)timestamp; +#else + spJson.createdTimestamp = spJson.createdDateTime.toTime_t(); +#endif + picture->setSnapmaticProperties(spJson); + const QString picFileName = QString("PGTA5%1").arg(QString::number(spJson.uid)); + picture->setPicFileName(picFileName); + picture->setPictureTitle(importDialog->getImageTitle()); + picture->updateStrings(); + success = importSnapmaticPicture(picture, true); + } + } + else { + delete picture; + success = true; + } + delete importDialog; + if (!success) + delete picture; + return success; + } + else { + delete picture; + return false; + } +} + bool ProfileInterface::importSnapmaticPicture(SnapmaticPicture *picture, bool warn) { QString picFileName = picture->getPictureFileName(); - QString adjustedFileName = picFileName; - if (adjustedFileName.right(7) == ".hidden") // for the hidden file system - { - adjustedFileName.remove(adjustedFileName.length() - 7, 7); - } - if (adjustedFileName.right(4) == ".bak") // for the backup file system - { - adjustedFileName.remove(adjustedFileName.length() - 4, 4); - } - if (picFileName.left(4) != "PGTA") - { - if (warn) QMessageBox::warning(this, tr("Import"), tr("Failed to import the Snapmatic picture, file not begin with PGTA or end with .g5e")); + QString adjustedFileName = picture->getOriginalPictureFileName(); + if (!picFileName.startsWith("PGTA5")) { + if (warn) + QMessageBox::warning(this, tr("Import..."), tr("Failed to import the Snapmatic picture, file not begin with PGTA or end with .g5e")); return false; } - else if (QFile::exists(profileFolder + QDir::separator() + adjustedFileName) || QFile::exists(profileFolder + QDir::separator() + adjustedFileName + ".hidden")) - { - if (warn) QMessageBox::warning(this, tr("Import"), tr("Failed to import the Snapmatic picture, the picture is already in the game")); - return false; + else if (QFile::exists(profileFolder % "/" % adjustedFileName) || QFile::exists(profileFolder % "/" % adjustedFileName % ".hidden")) { + SnapmaticProperties snapmaticProperties = picture->getSnapmaticProperties(); + if (warn) { + int uchoice = QMessageBox::question(this, tr("Import..."), tr("A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp?").arg(QString::number(snapmaticProperties.uid)), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (uchoice == QMessageBox::Yes) { + // Update Snapmatic uid + snapmaticProperties.uid = getRandomUid(); + snapmaticProperties.createdDateTime = QDateTime::currentDateTime(); +#if QT_VERSION >= 0x060000 + quint64 timestamp = snapmaticProperties.createdDateTime.toSecsSinceEpoch(); + if (timestamp > UINT32_MAX) { + timestamp = UINT32_MAX; + } + snapmaticProperties.createdTimestamp = (quint32)timestamp; +#else + snapmaticProperties.createdTimestamp = snapmaticProperties.createdDateTime.toTime_t(); +#endif + bool fExists = QFile::exists(profileFolder % "/PGTA5" % QString::number(snapmaticProperties.uid)); + bool fExistsBackup = QFile::exists(profileFolder % "/PGTA5" % QString::number(snapmaticProperties.uid) % ".bak"); + bool fExistsHidden = QFile::exists(profileFolder % "/PGTA5" % QString::number(snapmaticProperties.uid) % ".hidden"); + int cEnough = 0; + while ((fExists || fExistsBackup || fExistsHidden) && cEnough < findRetryLimit) { + snapmaticProperties.uid = getRandomUid(); + fExists = QFile::exists(profileFolder % "/PGTA5" % QString::number(snapmaticProperties.uid)); + fExistsBackup = QFile::exists(profileFolder % "/PGTA5" % QString::number(snapmaticProperties.uid) % ".bak"); + fExistsHidden = QFile::exists(profileFolder % "/PGTA5" % QString::number(snapmaticProperties.uid) % ".hidden"); + cEnough++; + } + if (fExists || fExistsBackup || fExistsHidden) { + // That should never happen + return false; + } + if (!picture->setSnapmaticProperties(snapmaticProperties)) { + // That should never happen + return false; + } + picture->updateStrings(); + picFileName = picture->getPictureFileName(); + adjustedFileName = picture->getOriginalPictureFileName(); + } + else { + return false; + } + } + else { + // Update Snapmatic uid + snapmaticProperties.uid = getRandomUid(); + snapmaticProperties.createdDateTime = QDateTime::currentDateTime(); +#if QT_VERSION >= 0x060000 + quint64 timestamp = snapmaticProperties.createdDateTime.toSecsSinceEpoch(); + if (timestamp > UINT32_MAX) { + timestamp = UINT32_MAX; + } + snapmaticProperties.createdTimestamp = (quint32)timestamp; +#else + snapmaticProperties.createdTimestamp = snapmaticProperties.createdDateTime.toTime_t(); +#endif + bool fExists = QFile::exists(profileFolder % "/PGTA5" % QString::number(snapmaticProperties.uid)); + bool fExistsBackup = QFile::exists(profileFolder % "/PGTA5" % QString::number(snapmaticProperties.uid) % ".bak"); + bool fExistsHidden = QFile::exists(profileFolder % "/PGTA5" % QString::number(snapmaticProperties.uid) % ".hidden"); + int cEnough = 0; + while ((fExists || fExistsBackup || fExistsHidden) && cEnough < findRetryLimit) { + snapmaticProperties.uid = getRandomUid(); + fExists = QFile::exists(profileFolder % "/PGTA5" % QString::number(snapmaticProperties.uid)); + fExistsBackup = QFile::exists(profileFolder % "/PGTA5" % QString::number(snapmaticProperties.uid) % ".bak"); + fExistsHidden = QFile::exists(profileFolder % "/PGTA5" % QString::number(snapmaticProperties.uid) % ".hidden"); + cEnough++; + } + if (fExists || fExistsBackup || fExistsHidden) { + // That should never happen + return false; + } + if (!picture->setSnapmaticProperties(snapmaticProperties)) { + // That should never happen + return false; + } + picture->updateStrings(); + picFileName = picture->getPictureFileName(); + adjustedFileName = picture->getOriginalPictureFileName(); + } } - else if (picture->exportPicture(profileFolder + QDir::separator() + adjustedFileName, "PGTA")) - { - picture->setPicFilePath(profileFolder + QDir::separator() + adjustedFileName); + if (picture->exportPicture(profileFolder % "/" % adjustedFileName, SnapmaticFormat::PGTA_Format)) { + picture->setSnapmaticFormat(SnapmaticFormat::PGTA_Format); + picture->setPicFilePath(profileFolder % "/" % adjustedFileName); +#if QT_VERSION >= 0x050000 + snapmaticPics << picture->getPictureFileName(); +#endif pictureLoaded(picture, true); return true; } - else - { - if (warn) QMessageBox::warning(this, tr("Import"), tr("Failed to import the Snapmatic picture, can't copy the file into profile")); + else { + if (warn) + QMessageBox::warning(this, tr("Import..."), tr("Failed to import the Snapmatic picture, can't copy the file into profile")); return false; } } @@ -737,50 +1244,49 @@ bool ProfileInterface::importSavegameData(SavegameData *savegame, QString sgdPat bool foundFree = 0; int currentSgd = 0; - while (currentSgd < 15 && !foundFree) - { + while (currentSgd < 15 && !foundFree) { QString sgdNumber = QString::number(currentSgd); - if (sgdNumber.length() == 1) - { + if (sgdNumber.length() == 1) { sgdNumber.insert(0, "0"); } - sgdFileName = "SGTA500" + sgdNumber; + sgdFileName = "SGTA500" % sgdNumber; - if (!QFile::exists(profileFolder + QDir::separator() + sgdFileName)) - { + if (!QFile::exists(profileFolder % "/" % sgdFileName)) { foundFree = true; } currentSgd++; } - if (foundFree) - { - if (QFile::copy(sgdPath, profileFolder + QDir::separator() + sgdFileName)) - { - savegame->setSavegameFileName(profileFolder + QDir::separator() + sgdFileName); - savegameLoaded(savegame, profileFolder + QDir::separator() + sgdFileName, true); + if (foundFree) { + const QString newSgdPath = profileFolder % "/" % sgdFileName; + if (QFile::copy(sgdPath, newSgdPath)) { + savegame->setSavegameFileName(newSgdPath); +#if QT_VERSION >= 0x050000 + savegameFiles << newSgdPath; +#endif + savegameLoaded(savegame, newSgdPath, true); return true; } - else - { - if (warn) QMessageBox::warning(this, tr("Import"), tr("Failed to import the Savegame, can't copy the file into profile")); + else { + if (warn) + QMessageBox::warning(this, tr("Import..."), tr("Failed to import the Savegame, can't copy the file into profile")); return false; } } - else - { - if (warn) QMessageBox::warning(this, tr("Import"), tr("Failed to import the Savegame, no Savegame slot is left")); + else { + if (warn) + QMessageBox::warning(this, tr("Import..."), tr("Failed to import the Savegame, no Savegame slot is left")); return false; } } void ProfileInterface::profileWidgetSelected() { - if (selectedWidgts == 0) - { - foreach(ProfileWidget *widget, widgets.keys()) - { - widget->setSelectionMode(true); + if (selectedWidgts == 0) { + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr) + widget->setSelectionMode(true); } } selectedWidgts++; @@ -788,13 +1294,11 @@ void ProfileInterface::profileWidgetSelected() void ProfileInterface::profileWidgetDeselected() { - if (selectedWidgts == 1) - { + if (selectedWidgts == 1) { int scrollBarValue = ui->saProfile->verticalScrollBar()->value(); - foreach(ProfileWidget *widget, widgets.keys()) - { - if (contentMode != 2) - { + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr && contentMode != 2) { widget->setSelectionMode(false); } } @@ -805,24 +1309,25 @@ void ProfileInterface::profileWidgetDeselected() void ProfileInterface::selectAllWidgets() { - foreach(ProfileWidget *widget, widgets.keys()) - { - widget->setSelected(true); + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr) + widget->setSelected(true); } } void ProfileInterface::deselectAllWidgets() { - foreach(ProfileWidget *widget, widgets.keys()) - { - widget->setSelected(false); + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr) + widget->setSelected(false); } } void ProfileInterface::exportSelected() { - if (selectedWidgts != 0) - { + if (selectedWidgts != 0) { int exportCount = 0; int exportPictures = 0; int exportSavegames = 0; @@ -831,28 +1336,26 @@ void ProfileInterface::exportSelected() QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); settings.beginGroup("FileDialogs"); + //bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool(); settings.beginGroup("ExportDirectory"); - QString exportDirectory = QFileDialog::getExistingDirectory(this, tr("Export selected"), settings.value(profileName, profileFolder).toString()); - if (exportDirectory != "") - { + QString exportDirectory = QFileDialog::getExistingDirectory(this, tr("Export selected..."), settings.value(profileName, profileFolder).toString()); + if (exportDirectory != "") { settings.setValue(profileName, exportDirectory); - foreach (ProfileWidget *widget, widgets.keys()) - { - if (widget->isSelected()) - { - if (widget->getWidgetType() == "SnapmaticWidget") - { - exportPictures++; - } - else if (widget->getWidgetType() == "SavegameWidget") - { - exportSavegames++; + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr) { + if (widget->isSelected()) { + if (widget->getWidgetType() == "SnapmaticWidget") { + exportPictures++; + } + else if (widget->getWidgetType() == "SavegameWidget") { + exportSavegames++; + } } } } - if (exportPictures != 0) - { + if (exportPictures != 0) { QInputDialog inputDialog; QStringList inputDialogItems; inputDialogItems << tr("JPG pictures and GTA Snapmatic"); @@ -861,60 +1364,54 @@ void ProfileInterface::exportSelected() QString ExportPreSpan; QString ExportPostSpan; -#ifdef GTA5SYNC_WIN - ExportPreSpan = ""; +#ifdef Q_OS_WIN + ExportPreSpan = ""; ExportPostSpan = ""; #else - ExportPreSpan = ""; + ExportPreSpan = ""; ExportPostSpan = ""; #endif bool itemSelected = false; - QString selectedItem = inputDialog.getItem(this, tr("Export selected"), tr("%1Export Snapmatic pictures%2

JPG pictures make it possible to open the picture with a Image Viewer
GTA Snapmatic make it possible to import the picture into the game

Export as:").arg(ExportPreSpan, ExportPostSpan), inputDialogItems, 0, false, &itemSelected, inputDialog.windowFlags()^Qt::WindowContextHelpButtonHint); - if (itemSelected) - { - if (selectedItem == tr("JPG pictures and GTA Snapmatic")) - { + QString selectedItem = inputDialog.getItem(this, tr("Export selected..."), tr("%1Export Snapmatic pictures%2

JPG pictures make it possible to open the picture with a Image Viewer
GTA Snapmatic make it possible to import the picture into the game

Export as:").arg(ExportPreSpan, ExportPostSpan), inputDialogItems, 0, false, &itemSelected, inputDialog.windowFlags()^Qt::WindowContextHelpButtonHint); + if (itemSelected) { + if (selectedItem == tr("JPG pictures and GTA Snapmatic")) { pictureExportEnabled = true; pictureCopyEnabled = true; } - else if (selectedItem == tr("JPG pictures only")) - { + else if (selectedItem == tr("JPG pictures only")) { pictureExportEnabled = true; } - else if (selectedItem == tr("GTA Snapmatic only")) - { + else if (selectedItem == tr("GTA Snapmatic only")) { pictureCopyEnabled = true; } - else - { + else { pictureExportEnabled = true; pictureCopyEnabled = true; } } - else - { - pictureExportEnabled = true; - pictureCopyEnabled = true; + else { + // Don't export anymore when any Cancel button got clicked + settings.endGroup(); + settings.endGroup(); + return; } } // Counting the exports together exportCount = exportCount + exportSavegames; - if (pictureExportEnabled && pictureCopyEnabled) - { + if (pictureExportEnabled && pictureCopyEnabled) { int exportPictures2 = exportPictures * 2; exportCount = exportCount + exportPictures2; } - else - { + else { exportCount = exportCount + exportPictures; } QProgressDialog pbDialog(this); pbDialog.setWindowFlags(pbDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); pbDialog.setWindowTitle(tr("Export selected...")); - pbDialog.setLabelText(tr("Initializing export...")); + pbDialog.setLabelText(tr("Initialising export...")); pbDialog.setRange(0, exportCount); QList pbBtn = pbDialog.findChildren(); @@ -929,6 +1426,7 @@ void ProfileInterface::exportSelected() QObject::connect(exportThread, SIGNAL(exportFinished()), &pbDialog, SLOT(close())); exportThread->start(); + pbDialog.setAutoClose(false); pbDialog.exec(); QStringList getFailedSavegames = exportThread->getFailedSavegames(); QStringList getFailedCopyPictures = exportThread->getFailedCopyPictures(); @@ -940,22 +1438,18 @@ void ProfileInterface::exportSelected() errorList << getFailedCopyPictures; errorList << getFailedSavegames; - foreach (const QString &curErrorStr, errorList) - { - errorStr.append(", " + curErrorStr); + for (const QString &curErrorStr : qAsConst(errorList)) { + errorStr += ", " % curErrorStr; } - if (errorStr != "") - { + if (errorStr != "") { errorStr.remove(0, 2); - QMessageBox::warning(this, tr("Export selected"), tr("Export failed with...\n\n%1").arg(errorStr)); + QMessageBox::warning(this, tr("Export selected..."), tr("Export failed with...\n\n%1").arg(errorStr)); } - if (exportThread->isFinished()) - { + if (exportThread->isFinished()) { delete exportThread; } - else - { + else { QEventLoop threadFinishLoop; QObject::connect(exportThread, SIGNAL(finished()), &threadFinishLoop, SLOT(quit())); threadFinishLoop.exec(); @@ -965,52 +1459,74 @@ void ProfileInterface::exportSelected() settings.endGroup(); settings.endGroup(); } - else - { - QMessageBox::information(this, tr("Export selected"), tr("No Snapmatic pictures or Savegames files are selected")); + else { + QMessageBox::information(this, tr("Export selected..."), tr("No Snapmatic pictures or Savegames files are selected")); + } +} + +void ProfileInterface::deleteSelectedL(bool isRemoteEmited) +{ + if (selectedWidgts != 0) { + if (QMessageBox::Yes == QMessageBox::warning(this, tr("Remove selected"), tr("You really want remove the selected Snapmatic picutres and Savegame files?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) { + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr) { + if (widget->isSelected()) { + if (widget->getWidgetType() == "SnapmaticWidget") { + SnapmaticWidget *picWidget = qobject_cast(widget); + if (picWidget->getPicture()->deletePictureFile()) { + pictureDeleted(picWidget, isRemoteEmited); + } + } + else if (widget->getWidgetType() == "SavegameWidget") { + SavegameWidget *sgdWidget = qobject_cast(widget); + SavegameData *savegame = sgdWidget->getSavegame(); + QString fileName = savegame->getSavegameFileName(); + if (!QFile::exists(fileName) || QFile::remove(fileName)) { + savegameDeleted(sgdWidget, isRemoteEmited); + } + } + } + } + } + if (selectedWidgts != 0) { + QMessageBox::warning(this, tr("Remove selected"), tr("Failed to remove all selected Snapmatic pictures and/or Savegame files")); + } + } + } + else { + QMessageBox::information(this, tr("Remove selected"), tr("No Snapmatic pictures or Savegames files are selected")); } } void ProfileInterface::deleteSelected() { - if (selectedWidgts != 0) - { - if (QMessageBox::Yes == QMessageBox::warning(this, tr("Remove selected"), tr("You really want remove the selected Snapmatic picutres and Savegame files?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) - { - foreach (ProfileWidget *widget, widgets.keys()) - { - if (widget->isSelected()) - { - if (widget->getWidgetType() == "SnapmaticWidget") - { - SnapmaticWidget *picWidget = (SnapmaticWidget*)widget; - if (picWidget->getPicture()->deletePicFile()) - { - pictureDeleted(picWidget); - } - } - else if (widget->getWidgetType() == "SavegameWidget") - { - SavegameWidget *sgdWidget = (SavegameWidget*)widget; - SavegameData *savegame = sgdWidget->getSavegame(); - QString fileName = savegame->getSavegameFileName(); - if (!QFile::exists(fileName) || QFile::remove(fileName)) - { - savegameDeleted(sgdWidget); - } - } - } - } - if (selectedWidgts != 0) - { - QMessageBox::warning(this, tr("Remove selected"), tr("Failed at remove the complete selected Snapmatic pictures and/or Savegame files")); - } - } - } - else - { - QMessageBox::information(this, tr("Remove selected"), tr("No Snapmatic pictures or Savegames files are selected")); - } + deleteSelectedL(false); +} + +void ProfileInterface::deleteSelectedR() +{ + deleteSelectedL(true); +} + +void ProfileInterface::massToolQualify() +{ + massTool(MassTool::Qualify); +} + +void ProfileInterface::massToolPlayers() +{ + massTool(MassTool::Players); +} + +void ProfileInterface::massToolCrew() +{ + massTool(MassTool::Crew); +} + +void ProfileInterface::massToolTitle() +{ + massTool(MassTool::Title); } void ProfileInterface::importFiles() @@ -1018,68 +1534,111 @@ void ProfileInterface::importFiles() on_cmdImport_clicked(); } -void ProfileInterface::settingsApplied(int _contentMode, QString language) +void ProfileInterface::settingsApplied(int _contentMode, bool languageChanged) { - Q_UNUSED(language) + if (languageChanged) + retranslateUi(); contentMode = _contentMode; - - if (contentMode == 2) - { - foreach(ProfileWidget *widget, widgets.keys()) - { - widget->setSelectionMode(true); - widget->setContentMode(contentMode); - } - } - else - { - foreach(ProfileWidget *widget, widgets.keys()) - { - if (selectedWidgts == 0) - { - widget->setSelectionMode(false); + if (contentMode == 2) { + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr) { + widget->setSelectionMode(true); + widget->setContentMode(contentMode); + if (languageChanged) + widget->retranslate(); } - widget->setContentMode(contentMode); } } + else { + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr) { + if (selectedWidgts == 0) { + widget->setSelectionMode(false); + } + widget->setContentMode(contentMode); + if (languageChanged) + widget->retranslate(); + } + } + } +#ifdef Q_OS_MAC + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); +#if QT_VERSION >= 0x060000 + if (QApplication::style()->objectName() == "macos") { +#else + if (QApplication::style()->objectName() == "macintosh") { +#endif + ui->hlButtons->setSpacing(6 * screenRatio); + ui->hlButtons->setContentsMargins(9 * screenRatio, 15 * screenRatio, 15 * screenRatio, 17 * screenRatio); + } + else { + ui->hlButtons->setSpacing(6 * screenRatio); + ui->hlButtons->setContentsMargins(9 * screenRatio, 9 * screenRatio, 9 * screenRatio, 9 * screenRatio); + } +#endif } void ProfileInterface::enableSelected() { - int fails = 0; - foreach (ProfileWidget *widget, widgets.keys()) - { - if (widget->isSelected()) - { - if (widget->getWidgetType() == "SnapmaticWidget") - { - SnapmaticWidget *snapmaticWidget = (SnapmaticWidget*)widget; - if (!snapmaticWidget->makePictureVisible()) - { - fails++; + QList snapmaticWidgets; + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr) { + if (widget->isSelected()) { + if (widget->getWidgetType() == "SnapmaticWidget") { + SnapmaticWidget *snapmaticWidget = qobject_cast(widget); + snapmaticWidgets += snapmaticWidget; } } } } + if (snapmaticWidgets.isEmpty()) { + QMessageBox::information(this, QApplication::translate("UserInterface", "Show In-game"), QApplication::translate("ProfileInterface", "No Snapmatic pictures are selected")); + return; + } + QStringList fails; + for (SnapmaticWidget *widget : qAsConst(snapmaticWidgets)) { + SnapmaticPicture *picture = widget->getPicture(); + if (!widget->makePictureVisible()) { + fails << QString("%1 [%2]").arg(picture->getPictureTitle(), picture->getPictureString()); + } + } + if (!fails.isEmpty()) { + QMessageBox::warning(this, QApplication::translate("UserInterface", "Show In-game"), QApplication::translate("ProfileInterface", "%1 failed with...\n\n%2", "Action failed with...").arg(QApplication::translate("UserInterface", "Show In-game"), fails.join(", "))); + } } void ProfileInterface::disableSelected() { - int fails = 0; - foreach (ProfileWidget *widget, widgets.keys()) - { - if (widget->isSelected()) - { - if (widget->getWidgetType() == "SnapmaticWidget") - { - SnapmaticWidget *snapmaticWidget = (SnapmaticWidget*)widget; - if (!snapmaticWidget->makePictureHidden()) - { - fails++; + QList snapmaticWidgets; + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr) { + if (widget->isSelected()) { + if (widget->getWidgetType() == "SnapmaticWidget") { + SnapmaticWidget *snapmaticWidget = qobject_cast(widget); + snapmaticWidgets += snapmaticWidget; } } } } + if (snapmaticWidgets.isEmpty()) { + QMessageBox::information(this, QApplication::translate("UserInterface", "Hide In-game"), QApplication::translate("ProfileInterface", "No Snapmatic pictures are selected")); + return; + } + QStringList fails; + for (SnapmaticWidget *widget : qAsConst(snapmaticWidgets)) { + SnapmaticPicture *picture = widget->getPicture(); + if (!widget->makePictureHidden()) { + fails << QString("%1 [%2]").arg(picture->getPictureTitle(), picture->getPictureString()); + } + } + if (!fails.isEmpty()) { + QMessageBox::warning(this, QApplication::translate("UserInterface", "Hide In-game"), QApplication::translate("ProfileInterface", "%1 failed with...\n\n%2", "Action failed with...").arg(QApplication::translate("UserInterface", "Hide In-game"), fails.join(", "))); + } } int ProfileInterface::selectedWidgets() @@ -1089,81 +1648,751 @@ int ProfileInterface::selectedWidgets() void ProfileInterface::contextMenuTriggeredPIC(QContextMenuEvent *ev) { - SnapmaticWidget *picWidget = (SnapmaticWidget*)sender(); + SnapmaticWidget *picWidget = qobject_cast(sender()); + if (picWidget != previousWidget) { + if (previousWidget != nullptr) { + previousWidget->setStyleSheet(QLatin1String("")); + } + picWidget->setStyleSheet(QString("QFrame#SnapmaticFrame{background-color:palette(highlight)}QLabel#labPicStr{color:palette(highlighted-text)}")); + previousWidget = picWidget; + } QMenu contextMenu(picWidget); - QMenu editMenu(SnapmaticWidget::tr("Edi&t"), picWidget); - if (picWidget->isHidden()) - { - editMenu.addAction(SnapmaticWidget::tr("Show &In-game"), picWidget, SLOT(makePictureVisibleSlot())); + const int selectedCount = selectedWidgets(); + if (contentMode < 20 || selectedCount == 0) { + QMenu editMenu(SnapmaticWidget::tr("Edi&t"), picWidget); + if (picWidget->isHidden()) { + editMenu.addAction(SnapmaticWidget::tr("Show &In-game"), picWidget, SLOT(makePictureVisibleSlot())); + } + else { + editMenu.addAction(SnapmaticWidget::tr("Hide &In-game"), picWidget, SLOT(makePictureHiddenSlot())); + } + editMenu.addAction(PictureDialog::tr("&Edit Properties..."), picWidget, SLOT(editSnapmaticProperties())); + editMenu.addAction(PictureDialog::tr("&Overwrite Image..."), picWidget, SLOT(editSnapmaticImage())); + editMenu.addSeparator(); + editMenu.addAction(PictureDialog::tr("Open &Map Viewer..."), picWidget, SLOT(openMapViewer())); + editMenu.addAction(PictureDialog::tr("Open &JSON Editor..."), picWidget, SLOT(editSnapmaticRawJson())); + QMenu exportMenu(SnapmaticWidget::tr("&Export"), this); + exportMenu.addAction(PictureDialog::tr("Export as &Picture..."), picWidget, SLOT(on_cmdExport_clicked())); + exportMenu.addAction(PictureDialog::tr("Export as &Snapmatic..."), picWidget, SLOT(on_cmdCopy_clicked())); + contextMenu.addAction(SnapmaticWidget::tr("&View"), picWidget, SLOT(on_cmdView_clicked())); + contextMenu.addMenu(&editMenu); + contextMenu.addMenu(&exportMenu); + contextMenu.addAction(SnapmaticWidget::tr("&Remove"), picWidget, SLOT(on_cmdDelete_clicked())); + contextMenu.addSeparator(); + if (!picWidget->isSelected()) + contextMenu.addAction(SnapmaticWidget::tr("&Select"), picWidget, SLOT(pictureSelected())); + else { + contextMenu.addAction(SnapmaticWidget::tr("&Deselect"), picWidget, SLOT(pictureSelected())); + } + if (selectedCount != widgets.count()) { + contextMenu.addAction(SnapmaticWidget::tr("Select &All"), picWidget, SLOT(selectAllWidgets()), QKeySequence::fromString("Ctrl+A")); + } + if (selectedCount != 0) { + contextMenu.addAction(SnapmaticWidget::tr("&Deselect All"), picWidget, SLOT(deselectAllWidgets()), QKeySequence::fromString("Ctrl+D")); + } + contextMenuOpened = true; + contextMenu.exec(ev->globalPos()); + contextMenuOpened = false; + QTimer::singleShot(0, this, SLOT(hoverProfileWidgetCheck())); } - else - { - editMenu.addAction(SnapmaticWidget::tr("Hide &In-game"), picWidget, SLOT(makePictureHiddenSlot())); + else { + QMenu editMenu(SnapmaticWidget::tr("Edi&t"), picWidget); + editMenu.addAction(QApplication::translate("UserInterface", "&Qualify as Avatar"), this, SLOT(massToolQualify()), QKeySequence::fromString("Shift+Q")); + editMenu.addAction(QApplication::translate("UserInterface", "Change &Players..."), this, SLOT(massToolPlayers()), QKeySequence::fromString("Shift+P")); + editMenu.addAction(QApplication::translate("UserInterface", "Change &Crew..."), this, SLOT(massToolCrew()), QKeySequence::fromString("Shift+C")); + editMenu.addAction(QApplication::translate("UserInterface", "Change &Title..."), this, SLOT(massToolTitle()), QKeySequence::fromString("Shift+T")); + editMenu.addSeparator(); + editMenu.addAction(SnapmaticWidget::tr("Show &In-game"), this, SLOT(enableSelected()), QKeySequence::fromString("Shift+E")); + editMenu.addAction(SnapmaticWidget::tr("Hide &In-game"), this, SLOT(disableSelected()), QKeySequence::fromString("Shift+D")); + contextMenu.addMenu(&editMenu); + contextMenu.addAction(SavegameWidget::tr("&Export"), this, SLOT(exportSelected()), QKeySequence::fromString("Ctrl+E")); + contextMenu.addAction(SavegameWidget::tr("&Remove"), this, SLOT(deleteSelectedR()), QKeySequence::fromString("Ctrl+Del")); + contextMenu.addSeparator(); + if (!picWidget->isSelected()) { + contextMenu.addAction(SnapmaticWidget::tr("&Select"), picWidget, SLOT(pictureSelected())); + } + else { + contextMenu.addAction(SnapmaticWidget::tr("&Deselect"), picWidget, SLOT(pictureSelected())); + } + if (selectedCount != widgets.count()) { + contextMenu.addAction(SnapmaticWidget::tr("Select &All"), picWidget, SLOT(selectAllWidgets()), QKeySequence::fromString("Ctrl+A")); + } + if (selectedCount != 0) { + contextMenu.addAction(SnapmaticWidget::tr("&Deselect All"), picWidget, SLOT(deselectAllWidgets()), QKeySequence::fromString("Ctrl+D")); + } + contextMenuOpened = true; + contextMenu.exec(ev->globalPos()); + contextMenuOpened = false; + QTimer::singleShot(0, this, SLOT(hoverProfileWidgetCheck())); } - editMenu.addAction(SnapmaticWidget::tr("&Edit Properties..."), picWidget, SLOT(editSnapmaticProperties())); - QMenu exportMenu(SnapmaticWidget::tr("&Export"), this); - exportMenu.addAction(SnapmaticWidget::tr("Export as &JPG picture..."), picWidget, SLOT(on_cmdExport_clicked())); - exportMenu.addAction(SnapmaticWidget::tr("Export as >A Snapmatic..."), picWidget, SLOT(on_cmdCopy_clicked())); - contextMenu.addAction(SnapmaticWidget::tr("&View"), picWidget, SLOT(on_cmdView_clicked())); - contextMenu.addMenu(&editMenu); - contextMenu.addMenu(&exportMenu); - contextMenu.addAction(SnapmaticWidget::tr("&Remove"), picWidget, SLOT(on_cmdDelete_clicked())); - contextMenu.addSeparator(); - if (!picWidget->isSelected()) { contextMenu.addAction(SnapmaticWidget::tr("&Select"), picWidget, SLOT(pictureSelected())); } - if (picWidget->isSelected()) { contextMenu.addAction(SnapmaticWidget::tr("&Deselect"), picWidget, SLOT(pictureSelected())); } - if (selectedWidgets() != widgets.count()) - { - contextMenu.addAction(SnapmaticWidget::tr("Select &All"), picWidget, SLOT(selectAllWidgets()), QKeySequence::fromString("Ctrl+A")); - } - if (selectedWidgets() != 0) - { - contextMenu.addAction(SnapmaticWidget::tr("&Deselect All"), picWidget, SLOT(deselectAllWidgets()), QKeySequence::fromString("Ctrl+D")); - } - contextMenu.exec(ev->globalPos()); } void ProfileInterface::contextMenuTriggeredSGD(QContextMenuEvent *ev) { - SavegameWidget *sgdWidget = (SavegameWidget*)sender(); + SavegameWidget *sgdWidget = qobject_cast(sender()); + if (sgdWidget != previousWidget) { + if (previousWidget != nullptr) { + previousWidget->setStyleSheet(QLatin1String("")); + } + sgdWidget->setStyleSheet(QString("QFrame#SavegameFrame{background-color:palette(highlight)}QLabel#labSavegameStr{color:palette(highlighted-text)}")); + previousWidget = sgdWidget; + } QMenu contextMenu(sgdWidget); - contextMenu.addAction(SavegameWidget::tr("&View"), sgdWidget, SLOT(on_cmdView_clicked())); - contextMenu.addAction(SavegameWidget::tr("&Export"), sgdWidget, SLOT(on_cmdCopy_clicked())); - contextMenu.addAction(SavegameWidget::tr("&Remove"), sgdWidget, SLOT(on_cmdDelete_clicked())); - contextMenu.addSeparator(); - if (!sgdWidget->isSelected()) { contextMenu.addAction(SavegameWidget::tr("&Select"), sgdWidget, SLOT(savegameSelected())); } - if (sgdWidget->isSelected()) { contextMenu.addAction(SavegameWidget::tr("&Deselect"), sgdWidget, SLOT(savegameSelected())); } - if (selectedWidgets() != widgets.count()) - { - contextMenu.addAction(SavegameWidget::tr("Select &All"), sgdWidget, SLOT(selectAllWidgets()), QKeySequence::fromString("Ctrl+A")); + const int selectedCount = selectedWidgets(); + if (contentMode < 20 || selectedCount == 0) { + contextMenu.addAction(SavegameWidget::tr("&View"), sgdWidget, SLOT(on_cmdView_clicked())); + contextMenu.addAction(SavegameWidget::tr("&Export"), sgdWidget, SLOT(on_cmdCopy_clicked())); + contextMenu.addAction(SavegameWidget::tr("&Remove"), sgdWidget, SLOT(on_cmdDelete_clicked())); + contextMenu.addSeparator(); + if (!sgdWidget->isSelected()) { + contextMenu.addAction(SavegameWidget::tr("&Select"), sgdWidget, SLOT(savegameSelected())); + } + else { + contextMenu.addAction(SavegameWidget::tr("&Deselect"), sgdWidget, SLOT(savegameSelected())); + } + if (selectedCount != widgets.count()) { + contextMenu.addAction(SavegameWidget::tr("Select &All"), sgdWidget, SLOT(selectAllWidgets()), QKeySequence::fromString("Ctrl+A")); + } + if (selectedCount != 0) { + contextMenu.addAction(SavegameWidget::tr("&Deselect All"), sgdWidget, SLOT(deselectAllWidgets()), QKeySequence::fromString("Ctrl+D")); + } + contextMenuOpened = true; + contextMenu.exec(ev->globalPos()); + contextMenuOpened = false; + QTimer::singleShot(0, this, SLOT(hoverProfileWidgetCheck())); } - if (selectedWidgets() != 0) - { - contextMenu.addAction(SavegameWidget::tr("&Deselect All"), sgdWidget, SLOT(deselectAllWidgets()), QKeySequence::fromString("Ctrl+D")); + else { + QMenu editMenu(SnapmaticWidget::tr("Edi&t"), sgdWidget); + editMenu.addAction(QApplication::translate("UserInterface", "&Qualify as Avatar"), this, SLOT(massToolQualify()), QKeySequence::fromString("Shift+Q")); + editMenu.addAction(QApplication::translate("UserInterface", "Change &Players..."), this, SLOT(massToolPlayers()), QKeySequence::fromString("Shift+P")); + editMenu.addAction(QApplication::translate("UserInterface", "Change &Crew..."), this, SLOT(massToolCrew()), QKeySequence::fromString("Shift+C")); + editMenu.addAction(QApplication::translate("UserInterface", "Change &Title..."), this, SLOT(massToolTitle()), QKeySequence::fromString("Shift+T")); + editMenu.addSeparator(); + editMenu.addAction(SnapmaticWidget::tr("Show &In-game"), this, SLOT(enableSelected()), QKeySequence::fromString("Shift+E")); + editMenu.addAction(SnapmaticWidget::tr("Hide &In-game"), this, SLOT(disableSelected()), QKeySequence::fromString("Shift+D")); + contextMenu.addMenu(&editMenu); + contextMenu.addAction(SavegameWidget::tr("&Export"), this, SLOT(exportSelected()), QKeySequence::fromString("Ctrl+E")); + contextMenu.addAction(SavegameWidget::tr("&Remove"), this, SLOT(deleteSelectedR()), QKeySequence::fromString("Ctrl+Del")); + contextMenu.addSeparator(); + if (!sgdWidget->isSelected()) + contextMenu.addAction(SavegameWidget::tr("&Select"), sgdWidget, SLOT(savegameSelected())); + else { + contextMenu.addAction(SavegameWidget::tr("&Deselect"), sgdWidget, SLOT(savegameSelected())); + } + if (selectedCount != widgets.count()) { + contextMenu.addAction(SavegameWidget::tr("Select &All"), sgdWidget, SLOT(selectAllWidgets()), QKeySequence::fromString("Ctrl+A")); + } + if (selectedCount != 0) { + contextMenu.addAction(SavegameWidget::tr("&Deselect All"), sgdWidget, SLOT(deselectAllWidgets()), QKeySequence::fromString("Ctrl+D")); + } + contextMenuOpened = true; + contextMenu.exec(ev->globalPos()); + contextMenuOpened = false; + QTimer::singleShot(0, this, SLOT(hoverProfileWidgetCheck())); } - contextMenu.exec(ev->globalPos()); } void ProfileInterface::on_saProfileContent_dropped(const QMimeData *mimeData) { - if (!mimeData) return; - QStringList pathList; - QList urlList = mimeData->urls(); - - foreach(const QUrl ¤tUrl, urlList) - { - if (currentUrl.isLocalFile()) - { - pathList.append(currentUrl.toLocalFile()); - } + if (!mimeData) + return; + if (mimeData->hasImage()) { + QImage *snapmaticImage = new QImage(qvariant_cast(mimeData->imageData())); + importImage(snapmaticImage, QDateTime::currentDateTime()); } - - if (pathList.length() == 1) - { - QString selectedFile = pathList.at(0); - importFile(selectedFile, true, 0); - } - else if (pathList.length() > 1) - { - importFilesProgress(pathList); + else if (mimeData->hasUrls()) { + importUrls(mimeData); } } + +void ProfileInterface::retranslateUi() +{ + ui->retranslateUi(this); + QString appVersion = QApplication::applicationVersion(); + const char* literalBuildType = GTA5SYNC_BUILDTYPE; +#ifdef GTA5SYNC_COMMIT + if ((strcmp(literalBuildType, REL_BUILDTYPE) != 0) && !appVersion.contains("-")) + appVersion = appVersion % "-" % GTA5SYNC_COMMIT; +#endif + ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, appVersion)); +} + +bool ProfileInterface::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + if (isProfileLoaded) { + QKeyEvent *keyEvent = dynamic_cast(event); + switch (keyEvent->key()) { + case Qt::Key_V: + if (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && !QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) { + const QMimeData *clipboardData = QApplication::clipboard()->mimeData(); + if (clipboardData->hasImage()) { + QImage *snapmaticImage = new QImage(qvariant_cast(clipboardData->imageData())); + importImage(snapmaticImage, QDateTime::currentDateTime()); + } + else if (clipboardData->hasUrls()) { + if (clipboardData->urls().length() >= 2) { + importUrls(clipboardData); + } + else if (clipboardData->urls().length() == 1) { + QUrl clipboardUrl = clipboardData->urls().at(0); + if (clipboardUrl.isLocalFile()) { + importFile(clipboardUrl.toLocalFile(), QDateTime::currentDateTime(), true); + } + else { + importRemote(clipboardUrl); + } + } + } + else if (clipboardData->hasText()) { + QUrl clipboardUrl = QUrl::fromUserInput(clipboardData->text()); + if (clipboardUrl.isValid()) { + if (clipboardUrl.isLocalFile()) { + importFile(clipboardUrl.toLocalFile(), QDateTime::currentDateTime(), true); + } + else { + importRemote(clipboardUrl); + } + } + } + } + } + } + } + else if (event->type() == QEvent::MouseMove) { + if ((watched->objectName() == "SavegameWidget" || watched->objectName() == "SnapmaticWidget") && isProfileLoaded) { + ProfileWidget *pWidget = qobject_cast(watched); + if (pWidget->underMouse()) { + bool styleSheetChanged = false; + if (pWidget->getWidgetType() == "SnapmaticWidget") { + if (pWidget != previousWidget) { + pWidget->setStyleSheet(QString("QFrame#SnapmaticFrame{background-color:palette(highlight)}QLabel#labPicStr{color:palette(highlighted-text)}")); + styleSheetChanged = true; + } + } + else if (pWidget->getWidgetType() == "SavegameWidget") { + if (pWidget != previousWidget) { + pWidget->setStyleSheet(QString("QFrame#SavegameFrame{background-color:palette(highlight)}QLabel#labSavegameStr{color:palette(highlighted-text)}")); + styleSheetChanged = true; + } + } + if (styleSheetChanged) { + if (previousWidget != nullptr) { + previousWidget->setStyleSheet(QLatin1String("")); + } + previousWidget = pWidget; + } + } + return true; + } + } + else if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) { + if ((watched->objectName() == "SavegameWidget" || watched->objectName() == "SnapmaticWidget") && isProfileLoaded) { + ProfileWidget *pWidget = nullptr; + for (auto it = widgets.constBegin(); it != widgets.constEnd(); it++) { + ProfileWidget *widget = it.key(); + QPoint mousePos = widget->mapFromGlobal(QCursor::pos()); + if (widget->rect().contains(mousePos)) { + pWidget = widget; + break; + } + } + if (pWidget != nullptr) { + bool styleSheetChanged = false; + if (pWidget->getWidgetType() == "SnapmaticWidget") { + if (pWidget != previousWidget) { + pWidget->setStyleSheet(QString("QFrame#SnapmaticFrame{background-color:palette(highlight)}QLabel#labPicStr{color:palette(highlighted-text)}")); + styleSheetChanged = true; + } + } + else if (pWidget->getWidgetType() == "SavegameWidget") { + if (pWidget != previousWidget) { + pWidget->setStyleSheet(QString("QFrame#SavegameFrame{background-color:palette(highlight)}QLabel#labSavegameStr{color:palette(highlighted-text)}")); + styleSheetChanged = true; + } + } + if (styleSheetChanged) { + if (previousWidget != nullptr) { + previousWidget->setStyleSheet(QLatin1String("")); + } + previousWidget = pWidget; + } + } + } + } + else if (event->type() == QEvent::WindowDeactivate && isProfileLoaded) { + if (previousWidget != nullptr && watched == previousWidget) { + previousWidget->setStyleSheet(QLatin1String("")); + previousWidget = nullptr; + } + } + else if (event->type() == QEvent::Leave && isProfileLoaded && !contextMenuOpened) { + if (watched->objectName() == "SavegameWidget" || watched->objectName() == "SnapmaticWidget") { + ProfileWidget *pWidget = qobject_cast(watched); + QPoint mousePos = pWidget->mapFromGlobal(QCursor::pos()); + if (!pWidget->geometry().contains(mousePos)) { + if (previousWidget != nullptr) { + previousWidget->setStyleSheet(QLatin1String("")); + previousWidget = nullptr; + } + } + } + else if (watched->objectName() == "ProfileInterface") { + if (previousWidget != nullptr) { + previousWidget->setStyleSheet(QLatin1String("")); + previousWidget = nullptr; + } + } + } + return false; +} + +void ProfileInterface::hoverProfileWidgetCheck() +{ + ProfileWidget *pWidget = nullptr; + for (auto it = widgets.constBegin(); it != widgets.constEnd(); it++) { + ProfileWidget *widget = it.key(); + if (widget->underMouse()) { + pWidget = widget; + break; + } + } + if (pWidget != nullptr) { + bool styleSheetChanged = false; + if (pWidget->getWidgetType() == "SnapmaticWidget") { + if (pWidget != previousWidget) { + pWidget->setStyleSheet(QString("QFrame#SnapmaticFrame{background-color:palette(highlight)}QLabel#labPicStr{color:palette(highlighted-text)}")); + styleSheetChanged = true; + } + } + else if (pWidget->getWidgetType() == "SavegameWidget") { + if (pWidget != previousWidget) { + pWidget->setStyleSheet(QString("QFrame#SavegameFrame{background-color:palette(highlight)}QLabel#labSavegameStr{color:palette(highlighted-text)}")); + styleSheetChanged = true; + } + } + if (styleSheetChanged) { + if (previousWidget != nullptr) { + previousWidget->setStyleSheet(QLatin1String("")); + } + previousWidget = pWidget; + } + } + else { + if (previousWidget != nullptr) { + previousWidget->setStyleSheet(QLatin1String("")); + previousWidget = nullptr; + } + } +} + +void ProfileInterface::updatePalette() +{ + ui->saProfile->setStyleSheet(QString("QWidget#saProfileContent{background-color:palette(base)}")); + if (previousWidget != nullptr) { + if (previousWidget->getWidgetType() == "SnapmaticWidget") { + previousWidget->setStyleSheet(QString("QFrame#SnapmaticFrame{background-color:palette(highlight)}QLabel#labPicStr{color:palette(highlighted-text)}")); + } + else if (previousWidget->getWidgetType() == "SavegameWidget") { + previousWidget->setStyleSheet(QString("QFrame#SnapmaticFrame{background-color:palette(highlight)}QLabel#labPicStr{color:palette(highlighted-text)}")); + } + } +} + +bool ProfileInterface::isSupportedImageFile(QString selectedFileName) +{ + for (const QByteArray &imageFormat : QImageReader::supportedImageFormats()) { + QString imageFormatStr = QString(".") % QString::fromUtf8(imageFormat).toLower(); + if (selectedFileName.length() >= imageFormatStr.length() && selectedFileName.toLower().right(imageFormatStr.length()) == imageFormatStr) { + return true; + } + } + return false; +} + +void ProfileInterface::massTool(MassTool tool) +{ + switch(tool) { + case MassTool::Qualify: { + QList snapmaticWidgets; + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr) { + if (widget->isSelected()) { + if (widget->getWidgetType() == "SnapmaticWidget") { + SnapmaticWidget *snapmaticWidget = qobject_cast(widget); + snapmaticWidgets += snapmaticWidget; + } + } + } + } + + if (snapmaticWidgets.isEmpty()) { + QMessageBox::information(this, tr("Qualify as Avatar"), tr("No Snapmatic pictures are selected")); + return; + } + + // Prepare Progress + + int maximumId = snapmaticWidgets.length(); + int overallId = 0; + + QProgressDialog pbDialog(this); + pbDialog.setWindowFlags(pbDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); + pbDialog.setWindowTitle(tr("Patch selected...")); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(1), QString::number(maximumId))); + pbDialog.setRange(1, maximumId); + pbDialog.setValue(1); + pbDialog.setModal(true); + QList pbBtn = pbDialog.findChildren(); + pbBtn.at(0)->setDisabled(true); + QList pbBar = pbDialog.findChildren(); + pbBar.at(0)->setTextVisible(false); + pbDialog.setAutoClose(false); + pbDialog.show(); + + // Begin Progress + + QStringList fails; + for (SnapmaticWidget *snapmaticWidget : qAsConst(snapmaticWidgets)) { + // Update Progress + overallId++; + pbDialog.setValue(overallId); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(overallId), QString::number(maximumId))); + + SnapmaticPicture *picture = snapmaticWidget->getPicture(); + + SnapmaticProperties snapmaticProperties = picture->getSnapmaticProperties(); + snapmaticProperties.isSelfie = true; + snapmaticProperties.isMug = false; + snapmaticProperties.isFromRSEditor = false; + snapmaticProperties.isFromDirector = false; + snapmaticProperties.isMeme = false; + + QString currentFilePath = picture->getPictureFilePath(); + QString originalFilePath = picture->getOriginalPictureFilePath(); + QString backupFileName = originalFilePath % ".bak"; + if (!QFile::exists(backupFileName)) { + QFile::copy(currentFilePath, backupFileName); + } + SnapmaticProperties fallbackProperties = picture->getSnapmaticProperties(); + picture->setSnapmaticProperties(snapmaticProperties); + if (!picture->exportPicture(currentFilePath)) { + picture->setSnapmaticProperties(fallbackProperties); + fails << QString("%1 [%2]").arg(picture->getPictureTitle(), picture->getPictureString()); + } + else { + picture->emitUpdate(); + QApplication::processEvents(); + } + } + pbDialog.close(); + if (!fails.isEmpty()) { + QMessageBox::warning(this, tr("Qualify as Avatar"), tr("%1 failed with...\n\n%2", "Action failed with...").arg(tr("Qualify", "%1 failed with..."), fails.join(", "))); + } + } + break; + case MassTool::Players: { + QList snapmaticWidgets; + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr) { + if (widget->isSelected()) { + if (widget->getWidgetType() == "SnapmaticWidget") { + SnapmaticWidget *snapmaticWidget = qobject_cast(widget); + snapmaticWidgets += snapmaticWidget; + } + } + } + } + + if (snapmaticWidgets.isEmpty()) { + QMessageBox::information(this, tr("Change Players..."), tr("No Snapmatic pictures are selected")); + return; + } + + QStringList players; + if (snapmaticWidgets.length() == 1) { + players = snapmaticWidgets.at(0)->getPicture()->getSnapmaticProperties().playersList; + } + + PlayerListDialog *playerListDialog = new PlayerListDialog(players, profileDB, this); + playerListDialog->setModal(true); + playerListDialog->show(); + playerListDialog->exec(); + if (!playerListDialog->isListUpdated()) + return; + players = playerListDialog->getPlayerList(); + delete playerListDialog; + + // Prepare Progress + + int maximumId = snapmaticWidgets.length(); + int overallId = 0; + + QProgressDialog pbDialog(this); + pbDialog.setWindowFlags(pbDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); + pbDialog.setWindowTitle(tr("Patch selected...")); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(1), QString::number(maximumId))); + pbDialog.setRange(1, maximumId); + pbDialog.setValue(1); + pbDialog.setModal(true); + QList pbBtn = pbDialog.findChildren(); + pbBtn.at(0)->setDisabled(true); + QList pbBar = pbDialog.findChildren(); + pbBar.at(0)->setTextVisible(false); + pbDialog.setAutoClose(false); + pbDialog.show(); + + // Begin Progress + + QStringList fails; + for (SnapmaticWidget *snapmaticWidget : qAsConst(snapmaticWidgets)) { + // Update Progress + overallId++; + pbDialog.setValue(overallId); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(overallId), QString::number(maximumId))); + + SnapmaticPicture *picture = snapmaticWidget->getPicture(); + + SnapmaticProperties snapmaticProperties = picture->getSnapmaticProperties(); + snapmaticProperties.playersList = players; + + QString currentFilePath = picture->getPictureFilePath(); + QString originalFilePath = picture->getOriginalPictureFilePath(); + QString backupFileName = originalFilePath % ".bak"; + if (!QFile::exists(backupFileName)) { + QFile::copy(currentFilePath, backupFileName); + } + SnapmaticProperties fallbackProperties = picture->getSnapmaticProperties(); + picture->setSnapmaticProperties(snapmaticProperties); + if (!picture->exportPicture(currentFilePath)) { + picture->setSnapmaticProperties(fallbackProperties); + fails << QString("%1 [%2]").arg(picture->getPictureTitle(), picture->getPictureString()); + } + else { + picture->emitUpdate(); + QApplication::processEvents(); + } + } + pbDialog.close(); + if (!fails.isEmpty()) { + QMessageBox::warning(this, tr("Change Players..."), tr("%1 failed with...\n\n%2", "Action failed with...").arg(tr("Change Players", "%1 failed with..."), fails.join(", "))); + } + } + break; + case MassTool::Crew: { + QList snapmaticWidgets; + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr) { + if (widget->isSelected()) { + if (widget->getWidgetType() == "SnapmaticWidget") { + SnapmaticWidget *snapmaticWidget = qobject_cast(widget); + snapmaticWidgets += snapmaticWidget; + } + } + } + } + + if (snapmaticWidgets.isEmpty()) { + QMessageBox::information(this, tr("Change Crew..."), tr("No Snapmatic pictures are selected")); + return; + } + + int crewID = 0; + if (snapmaticWidgets.length() == 1) { + crewID = snapmaticWidgets.at(0)->getPicture()->getSnapmaticProperties().crewID; + } + { +preSelectionCrewID: + bool ok; + int indexNum = 0; + QStringList itemList; + QStringList crewList = crewDB->getCrews(); + if (!crewList.contains(QLatin1String("0"))) { + crewList += QLatin1String("0"); + } + crewList.sort(); + for (QString crew : qAsConst(crewList)) { + itemList += QString("%1 (%2)").arg(crew, crewDB->getCrewName(crew.toInt())); + } + if (crewList.contains(QString::number(crewID))) { + indexNum = crewList.indexOf(QString::number(crewID)); + } + QString newCrew = QInputDialog::getItem(this, QApplication::translate("SnapmaticEditor", "Snapmatic Crew"), QApplication::translate("SnapmaticEditor", "New Snapmatic crew:"), itemList, indexNum, true, &ok, windowFlags()^Qt::Dialog^Qt::WindowMinMaxButtonsHint); + if (ok && !newCrew.isEmpty()) { + if (newCrew.contains(" ")) newCrew = newCrew.split(" ").at(0); + if (newCrew.length() > 10) return; + for (const QChar &crewChar : qAsConst(newCrew)) { + if (!crewChar.isNumber()) { + QMessageBox::warning(this, tr("Change Crew..."), tr("Failed to enter a valid Snapmatic Crew ID")); + goto preSelectionCrewID; + } + } + if (!crewList.contains(newCrew)) { + crewDB->addCrew(crewID); + } + crewID = newCrew.toInt(); + } + else { + return; + } + } + + // Prepare Progress + + int maximumId = snapmaticWidgets.length(); + int overallId = 0; + + QProgressDialog pbDialog(this); + pbDialog.setWindowFlags(pbDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); + pbDialog.setWindowTitle(tr("Patch selected...")); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(1), QString::number(maximumId))); + pbDialog.setRange(1, maximumId); + pbDialog.setValue(1); + pbDialog.setModal(true); + QList pbBtn = pbDialog.findChildren(); + pbBtn.at(0)->setDisabled(true); + QList pbBar = pbDialog.findChildren(); + pbBar.at(0)->setTextVisible(false); + pbDialog.setAutoClose(false); + pbDialog.show(); + + // Begin Progress + + QStringList fails; + for (SnapmaticWidget *snapmaticWidget : qAsConst(snapmaticWidgets)) { + // Update Progress + overallId++; + pbDialog.setValue(overallId); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(overallId), QString::number(maximumId))); + + SnapmaticPicture *picture = snapmaticWidget->getPicture(); + + SnapmaticProperties snapmaticProperties = picture->getSnapmaticProperties(); + snapmaticProperties.crewID = crewID; + + QString currentFilePath = picture->getPictureFilePath(); + QString originalFilePath = picture->getOriginalPictureFilePath(); + QString backupFileName = originalFilePath % ".bak"; + if (!QFile::exists(backupFileName)) { + QFile::copy(currentFilePath, backupFileName); + } + SnapmaticProperties fallbackProperties = picture->getSnapmaticProperties(); + picture->setSnapmaticProperties(snapmaticProperties); + if (!picture->exportPicture(currentFilePath)) { + picture->setSnapmaticProperties(fallbackProperties); + fails << QString("%1 [%2]").arg(picture->getPictureTitle(), picture->getPictureString()); + } + else { + picture->emitUpdate(); + QApplication::processEvents(); + } + } + pbDialog.close(); + if (!fails.isEmpty()) { + QMessageBox::warning(this, tr("Change Crew..."), tr("%1 failed with...\n\n%2", "Action failed with...").arg(tr("Change Crew", "%1 failed with..."), fails.join(", "))); + } + } + break; + case MassTool::Title: { + QList snapmaticWidgets; + for (const QString &widgetStr : qAsConst(widgets)) { + ProfileWidget *widget = widgets.key(widgetStr, nullptr); + if (widget != nullptr) { + if (widget->isSelected()) { + if (widget->getWidgetType() == "SnapmaticWidget") { + SnapmaticWidget *snapmaticWidget = qobject_cast(widget); + snapmaticWidgets += snapmaticWidget; + } + } + } + } + + if (snapmaticWidgets.isEmpty()) { + QMessageBox::information(this, tr("Change Title..."), tr("No Snapmatic pictures are selected")); + return; + } + + QString snapmaticTitle; + if (snapmaticWidgets.length() == 1) { + snapmaticTitle = snapmaticWidgets.at(0)->getPicture()->getPictureTitle(); + } + { +preSelectionTitle: + bool ok; + QString newTitle = QInputDialog::getText(this, QApplication::translate("SnapmaticEditor", "Snapmatic Title"), QApplication::translate("SnapmaticEditor", "New Snapmatic title:"), QLineEdit::Normal, snapmaticTitle, &ok, windowFlags()^Qt::Dialog^Qt::WindowMinMaxButtonsHint); + if (ok && !newTitle.isEmpty()) { + if (!SnapmaticPicture::verifyTitle(newTitle)) { + QMessageBox::warning(this, tr("Change Title..."), tr("Failed to enter a valid Snapmatic title")); + goto preSelectionTitle; + } + snapmaticTitle = newTitle; + } + else { + return; + } + } + + // Prepare Progress + + int maximumId = snapmaticWidgets.length(); + int overallId = 0; + + QProgressDialog pbDialog(this); + pbDialog.setWindowFlags(pbDialog.windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); + pbDialog.setWindowTitle(tr("Patch selected...")); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(overallId), QString::number(maximumId))); + pbDialog.setRange(1, maximumId); + pbDialog.setValue(1); + pbDialog.setModal(true); + QList pbBtn = pbDialog.findChildren(); + pbBtn.at(0)->setDisabled(true); + QList pbBar = pbDialog.findChildren(); + pbBar.at(0)->setTextVisible(false); + pbDialog.setAutoClose(false); + pbDialog.show(); + + // Begin Progress + + QStringList fails; + for (SnapmaticWidget *snapmaticWidget : qAsConst(snapmaticWidgets)) { + // Update Progress + overallId++; + pbDialog.setValue(overallId); + pbDialog.setLabelText(tr("Patch file %1 of %2 files").arg(QString::number(overallId), QString::number(maximumId))); + + SnapmaticPicture *picture = snapmaticWidget->getPicture(); + + QString currentFilePath = picture->getPictureFilePath(); + QString originalFilePath = picture->getOriginalPictureFilePath(); + QString backupFileName = originalFilePath % ".bak"; + if (!QFile::exists(backupFileName)) { + QFile::copy(currentFilePath, backupFileName); + } + QString fallbackTitle = picture->getPictureTitle(); + picture->setPictureTitle(snapmaticTitle); + if (!picture->exportPicture(currentFilePath)) { + picture->setPictureTitle(fallbackTitle); + fails << QString("%1 [%2]").arg(picture->getPictureTitle(), picture->getPictureString()); + } + else { + picture->emitUpdate(); + QApplication::processEvents(); + } + } + pbDialog.close(); + if (!fails.isEmpty()) { + QMessageBox::warning(this, tr("Change Title..."), tr("%1 failed with...\n\n%2", "Action failed with...").arg(tr("Change Title", "%1 failed with..."), fails.join(", "))); + } + } + break; + } +} + +int ProfileInterface::getRandomUid() +{ + int random_int = pcg32_boundedrand_r(&rng, 2147483647); + return random_int; +} diff --git a/ProfileInterface.h b/ProfileInterface.h old mode 100755 new mode 100644 index f9dbc93..a3297ae --- a/ProfileInterface.h +++ b/ProfileInterface.h @@ -1,114 +1,153 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef PROFILEINTERFACE_H -#define PROFILEINTERFACE_H - -#include "SnapmaticPicture.h" -#include "SnapmaticWidget.h" -#include "ProfileDatabase.h" -#include "DatabaseThread.h" -#include "SavegameWidget.h" -#include "ProfileLoader.h" -#include "ProfileWidget.h" -#include "ExportThread.h" -#include "SavegameData.h" -#include "CrewDatabase.h" -#include -#include -#include -#include -#include - -namespace Ui { -class ProfileInterface; -} - -class ProfileInterface : public QWidget -{ - Q_OBJECT -public: - explicit ProfileInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent = 0); - void setProfileFolder(QString folder, QString profile); - void settingsApplied(int contentMode, QString language); - void setupProfileInterface(); - void disableSelected(); - void enableSelected(); - int selectedWidgets(); - ~ProfileInterface(); - -public slots: - void contextMenuTriggeredPIC(QContextMenuEvent* ev); - void contextMenuTriggeredSGD(QContextMenuEvent* ev); - void selectAllWidgets(); - void deselectAllWidgets(); - void exportSelected(); - void deleteSelected(); - void importFiles(); - -private slots: - void on_cmdCloseProfile_clicked(); - void on_cmdImport_clicked(); - void pictureLoaded_event(SnapmaticPicture *picture); - void savegameLoaded_event(SavegameData *savegame, QString savegamePath); - void loadingProgress(int value, int maximum); - void pictureDeleted_event(); - void savegameDeleted_event(); - void profileLoaded_p(); - void profileWidgetSelected(); - void profileWidgetDeselected(); - void dialogNextPictureRequested(QWidget *dialog); - void dialogPreviousPictureRequested(QWidget *dialog); - void on_saProfileContent_dropped(const QMimeData *mimeData); - -private: - ProfileDatabase *profileDB; - CrewDatabase *crewDB; - DatabaseThread *threadDB; - Ui::ProfileInterface *ui; - - ProfileLoader *profileLoader; - QList savegames; - QList pictures; - QMap widgets; - QSpacerItem *saSpacerItem; - QString enabledPicStr; - QString profileFolder; - QString profileName; - QString loadingStr; - int selectedWidgts; - int contentMode; - - bool importFile(QString selectedFile, bool notMultiple, int currentId); - void importFilesProgress(QStringList selectedFiles); - bool importSnapmaticPicture(SnapmaticPicture *picture, bool warn = true); - bool importSavegameData(SavegameData *savegame, QString sgdPath, bool warn = true); - void pictureLoaded(SnapmaticPicture *picture, bool inserted); - void savegameLoaded(SavegameData *savegame, QString savegamePath, bool inserted); - void savegameDeleted(SavegameWidget *sgdWidget, bool isRemoteEmited = false); - void pictureDeleted(SnapmaticWidget *picWidget, bool isRemoteEmited = false); - void insertSnapmaticIPI(QWidget *widget); - void insertSavegameIPI(QWidget *widget); - void sortingProfileInterface(); - -signals: - void profileLoaded(); - void profileClosed(); -}; - -#endif // PROFILEINTERFACE_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef PROFILEINTERFACE_H +#define PROFILEINTERFACE_H + +#include "SnapmaticPicture.h" +#include "SnapmaticWidget.h" +#include "ProfileDatabase.h" +#include "DatabaseThread.h" +#include "SavegameWidget.h" +#include "ProfileLoader.h" +#include "ProfileWidget.h" +#include "ExportThread.h" +#include "SavegameData.h" +#include "CrewDatabase.h" +#include "pcg_basic.h" +#include +#include +#include +#include +#include +#include +#include + +namespace Ui { +class ProfileInterface; +} + +enum class MassTool : int { Qualify = 0, Players = 1, Crew = 2, Title = 3 }; + +class ProfileInterface : public QWidget +{ + Q_OBJECT +public: + explicit ProfileInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent = 0); + void setProfileFolder(QString folder, QString profile); + void settingsApplied(int contentMode, bool languageChanged); + void setupProfileInterface(); + void massTool(MassTool tool); + int selectedWidgets(); + void retranslateUi(); + ~ProfileInterface(); + +public slots: + void contextMenuTriggeredPIC(QContextMenuEvent* ev); + void contextMenuTriggeredSGD(QContextMenuEvent* ev); + void hoverProfileWidgetCheck(); + void selectAllWidgets(); + void deselectAllWidgets(); + void disableSelected(); + void enableSelected(); + void exportSelected(); + void deleteSelected(); + void deleteSelectedR(); + void updatePalette(); + void importFiles(); + +private slots: + void on_cmdCloseProfile_clicked(); + void on_cmdImport_clicked(); + void pictureLoaded_event(SnapmaticPicture *picture); + void pictureFixed_event(SnapmaticPicture *picture); + void savegameLoaded_event(SavegameData *savegame, QString savegamePath); + void loadingProgress(int value, int maximum); + void pictureDeleted_event(); + void savegameDeleted_event(); + void profileLoaded_p(); + void profileWidgetSelected(); + void profileWidgetDeselected(); + void massToolQualify(); + void massToolPlayers(); + void massToolCrew(); + void massToolTitle(); + void dialogNextPictureRequested(QWidget *dialog); + void dialogPreviousPictureRequested(QWidget *dialog); + void on_saProfileContent_dropped(const QMimeData *mimeData); +#if QT_VERSION >= 0x050000 + void directoryChanged(const QString &path); + void directoryScanned(QVector savegameFiles, QVector snapmaticPics); +#endif + +protected: + bool eventFilter(QObject *watched, QEvent *event); + +private: + ProfileDatabase *profileDB; + CrewDatabase *crewDB; + DatabaseThread *threadDB; + Ui::ProfileInterface *ui; + + ProfileLoader *profileLoader; + ProfileWidget *previousWidget; + QList savegames; + QList pictures; + QMap widgets; +#if QT_VERSION >= 0x050000 + QFileSystemWatcher fileSystemWatcher; + QVector savegameFiles; + QVector snapmaticPics; +#endif + QSpacerItem *saSpacerItem; + QStringList fixedPictures; + QString enabledPicStr; + QString profileFolder; + QString profileName; + QString loadingStr; + QString language; + pcg32_random_t rng; + bool contextMenuOpened; + bool isProfileLoaded; + int selectedWidgts; + int contentMode; + + bool isSupportedImageFile(QString selectedFileName); + bool importFile(QString selectedFile, QDateTime importDateTime, bool notMultiple); + bool importUrls(const QMimeData *mimeData); + bool importRemote(QUrl remoteUrl); + bool importImage(QImage *snapmaticImage, QDateTime importDateTime); + bool importFilesProgress(QStringList selectedFiles); + bool importSnapmaticPicture(SnapmaticPicture *picture, bool warn = true); + bool importSavegameData(SavegameData *savegame, QString sgdPath, bool warn = true); + void pictureLoaded(SnapmaticPicture *picture, bool inserted); + void savegameLoaded(SavegameData *savegame, QString savegamePath, bool inserted); + void savegameDeleted(SavegameWidget *sgdWidget, bool isRemoteEmited = false); + void pictureDeleted(SnapmaticWidget *picWidget, bool isRemoteEmited = false); + void deleteSelectedL(bool isRemoteEmited = false); + void insertSnapmaticIPI(QWidget *widget); + void insertSavegameIPI(QWidget *widget); + void sortingProfileInterface(); + int getRandomUid(); + +signals: + void profileLoaded(); + void profileClosed(); +}; + +#endif // PROFILEINTERFACE_H diff --git a/ProfileInterface.ui b/ProfileInterface.ui old mode 100755 new mode 100644 index de37b79..066e636 --- a/ProfileInterface.ui +++ b/ProfileInterface.ui @@ -1,244 +1,244 @@ - - - ProfileInterface - - - - 0 - 0 - 400 - 300 - - - - Profile Interface - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - Loading file %1 of %2 files - - - Qt::AlignCenter - - - - - - - 0 - - - false - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - - - 0 - 0 - 398 - 257 - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - - - - - - - - - - - - - - - - - - - 6 - - - 9 - - - 9 - - - 9 - - - 9 - - - - - %1 %2 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - Import exported file - - - &Import... - - - true - - - - - - - - 0 - 0 - - - - Close profile - - - &Close - - - true - - - - - - - - - - UiModWidget - QWidget -
UiModWidget.h
- 1 - - dropped(QMimeData*) - -
-
- - -
+ + + ProfileInterface + + + + 0 + 0 + 400 + 300 + + + + Profile Interface + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Loading file %1 of %2 files + + + Qt::AlignCenter + + + + + + + 0 + + + false + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + + 0 + 0 + 398 + 257 + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + + + + + + + + + + + + + + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + %1 %2 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Import file + + + &Import... + + + true + + + + + + + + 0 + 0 + + + + Close profile + + + &Close + + + true + + + + + + + + + + UiModWidget + QWidget +
UiModWidget.h
+ 1 + + dropped(QMimeData*) + +
+
+ + +
diff --git a/ProfileLoader.cpp b/ProfileLoader.cpp old mode 100755 new mode 100644 index 73e3941..a4e4318 --- a/ProfileLoader.cpp +++ b/ProfileLoader.cpp @@ -1,103 +1,133 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "ProfileLoader.h" -#include "SnapmaticPicture.h" -#include "SavegameData.h" -#include "CrewDatabase.h" -#include -#include -#include -#include - -ProfileLoader::ProfileLoader(QString profileFolder, CrewDatabase *crewDB, QObject *parent) : QThread(parent), profileFolder(profileFolder), crewDB(crewDB) -{ - -} - -void ProfileLoader::run() -{ - int curFile = 1; - QDir profileDir; - QList crewList; - profileDir.setPath(profileFolder); - - // Seek pictures and savegames - profileDir.setNameFilters(QStringList("SGTA*")); - QStringList SavegameFiles = profileDir.entryList(QDir::Files | QDir::NoDot, QDir::NoSort); - QStringList BackupFiles = SavegameFiles.filter(".bak", Qt::CaseInsensitive); - profileDir.setNameFilters(QStringList("PGTA*")); - QStringList SnapmaticPics = profileDir.entryList(QDir::Files | QDir::NoDot, QDir::NoSort); - BackupFiles.append(SnapmaticPics.filter(".bak", Qt::CaseInsensitive)); - - SavegameFiles.removeDuplicates(); - SnapmaticPics.removeDuplicates(); - foreach(const QString &BackupFile, BackupFiles) - { - SavegameFiles.removeAll(BackupFile); - SnapmaticPics.removeAll(BackupFile); - } - - int maximumV = SavegameFiles.length() + SnapmaticPics.length(); - - // Loading pictures and savegames - emit loadingProgress(curFile, maximumV); - foreach(const QString &SavegameFile, SavegameFiles) - { - emit loadingProgress(curFile, maximumV); - QString sgdPath = profileFolder + QDir::separator() + SavegameFile; - SavegameData *savegame = new SavegameData(sgdPath); - if (savegame->readingSavegame()) - { - emit savegameLoaded(savegame, sgdPath); - } - curFile++; - } - foreach(const QString &SnapmaticPic, SnapmaticPics) - { - emit loadingProgress(curFile, maximumV); - QString picturePath = profileFolder + QDir::separator() + SnapmaticPic; - SnapmaticPicture *picture = new SnapmaticPicture(picturePath); - if (picture->readingPicture(true, true, true)) - { - emit pictureLoaded(picture); - int crewNumber = picture->getSnapmaticProperties().crewID; - if (!crewList.contains(crewNumber)) - { - crewList.append(crewNumber); - } - } - curFile++; - } - - // adding found crews - foreach(int crewID, crewList) - { - crewDB->addCrew(crewID); - } -} - -void ProfileLoader::preloaded() -{ - -} - -void ProfileLoader::loaded() -{ - -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "SnapmaticPicture.h" +#include "ProfileLoader.h" +#include "SavegameData.h" +#include "CrewDatabase.h" +#include "wrapper.h" +#include +#include +#include +#include +#ifdef Q_OS_WIN +#include +#include +#else +#include "sys/types.h" +#include "sys/stat.h" +#include "dirent.h" +#endif + +ProfileLoader::ProfileLoader(QString profileFolder, CrewDatabase *crewDB, QObject *parent) : QThread(parent), profileFolder(profileFolder), crewDB(crewDB) +{ +} + +void ProfileLoader::run() +{ + int curFile = 1; + int maximumV = 0; + QVector crewList; + QVector savegameFiles; + QVector snapmaticPics; + +#ifdef Q_OS_WIN + QDir dir(profileFolder); + const QStringList files = dir.entryList(QDir::Files); + for (const QString &fileName : files) { + if (fileName.startsWith("SGTA5") && !fileName.endsWith(".bak")) { + savegameFiles << fileName; + maximumV++; + } + if (fileName.startsWith("PGTA5") && !fileName.endsWith(".bak")) { + snapmaticPics << fileName; + maximumV++; + } + } +#else + DIR *dirp = opendir(profileFolder.toUtf8().constData()); + struct dirent *dp; + while ((dp = readdir(dirp)) != 0) { + const QString fileName = QString::fromUtf8(dp->d_name); + const QString filePath = profileFolder % "/" % fileName; + struct stat fileStat; + stat(filePath.toUtf8().constData(), &fileStat); + if (S_ISREG(fileStat.st_mode) != 0) { + if (fileName.startsWith("SGTA5") && !fileName.endsWith(".bak")) { + savegameFiles << fileName; + maximumV++; + } + if (fileName.startsWith("PGTA5") && !fileName.endsWith(".bak")) { + snapmaticPics << fileName; + maximumV++; + } + } + } + closedir(dirp); +#endif + + // Directory successfully scanned + emit directoryScanned(savegameFiles, snapmaticPics); + + // Loading pictures and savegames + emit loadingProgress(curFile, maximumV); + for (const QString &SavegameFile : qAsConst(savegameFiles)) { + emit loadingProgress(curFile, maximumV); + const QString sgdPath = profileFolder % "/" % SavegameFile; + SavegameData *savegame = new SavegameData(sgdPath); + if (savegame->readingSavegame()) { + emit savegameLoaded(savegame, sgdPath); + } + curFile++; + } + for (const QString &SnapmaticPic : qAsConst(snapmaticPics)) { + emit loadingProgress(curFile, maximumV); + const QString picturePath = profileFolder % "/" % SnapmaticPic; + SnapmaticPicture *picture = new SnapmaticPicture(picturePath); + if (picture->readingPicture(true)) { + if (picture->isFormatSwitched()) { + picture->setSnapmaticFormat(SnapmaticFormat::PGTA_Format); + if (picture->exportPicture(picturePath, SnapmaticFormat::PGTA_Format)) { + emit pictureFixed(picture); + } + } + emit pictureLoaded(picture); + int crewNumber = picture->getSnapmaticProperties().crewID; + if (!crewList.contains(crewNumber)) { + crewList += crewNumber; + } + } + curFile++; + } + + // adding found crews + crewDB->setAddingCrews(true); + for (int crewID : qAsConst(crewList)) { + crewDB->addCrew(crewID); + } + crewDB->setAddingCrews(false); +} + +void ProfileLoader::preloaded() +{ +} + +void ProfileLoader::loaded() +{ +} + diff --git a/ProfileLoader.h b/ProfileLoader.h old mode 100755 new mode 100644 index 23c4d77..6bde0ed --- a/ProfileLoader.h +++ b/ProfileLoader.h @@ -1,52 +1,54 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef PROFILELOADER_H -#define PROFILELOADER_H - -#include "SnapmaticPicture.h" -#include "SavegameData.h" -#include "CrewDatabase.h" -#include -#include - -class ProfileLoader : public QThread -{ - Q_OBJECT -public: - explicit ProfileLoader(QString profileFolder, CrewDatabase *crewDB, QObject *parent = 0); - -protected: - void run(); - -private: - QString profileFolder; - CrewDatabase *crewDB; - ProfileLoader *profileLoader; - -private slots: - void preloaded(); - void loaded(); - -signals: - void pictureLoaded(SnapmaticPicture *picture); - void savegameLoaded(SavegameData *savegame, QString savegamePath); - void loadingProgress(int value, int maximum); -}; - -#endif // PROFILELOADER_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef PROFILELOADER_H +#define PROFILELOADER_H + +#include "SnapmaticPicture.h" +#include "SavegameData.h" +#include "CrewDatabase.h" +#include +#include + +class ProfileLoader : public QThread +{ + Q_OBJECT +public: + explicit ProfileLoader(QString profileFolder, CrewDatabase *crewDB, QObject *parent = 0); + +protected: + void run(); + +private: + QString profileFolder; + CrewDatabase *crewDB; + ProfileLoader *profileLoader; + +private slots: + void preloaded(); + void loaded(); + +signals: + void pictureLoaded(SnapmaticPicture *picture); + void pictureFixed(SnapmaticPicture *picture); + void savegameLoaded(SavegameData *savegame, QString savegamePath); + void loadingProgress(int value, int maximum); + void directoryScanned(QVector savegameFiles, QVector snapmaticPics); +}; + +#endif // PROFILELOADER_H diff --git a/ProfileWidget.cpp b/ProfileWidget.cpp old mode 100755 new mode 100644 index 58828ef..a325d92 --- a/ProfileWidget.cpp +++ b/ProfileWidget.cpp @@ -1,61 +1,66 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "ProfileWidget.h" -#include - -ProfileWidget::ProfileWidget(QWidget *parent) : QWidget(parent) -{ - contentMode = 0; -} - -ProfileWidget::~ProfileWidget() -{ -} - -bool ProfileWidget::isSelected() -{ - qDebug() << "ProfileWidget::isSelected got used without overwrite"; - return false; -} - -void ProfileWidget::setSelected(bool isSelected) -{ - qDebug() << "ProfileWidget::setSelected got used without overwrite, result" << isSelected; -} - -void ProfileWidget::setSelectionMode(bool selectionMode) -{ - qDebug() << "ProfileWidget::setSelectionMode got used without overwrite, result:" << selectionMode; -} - -QString ProfileWidget::getWidgetType() -{ - qDebug() << "ProfileWidget::getWidgetType got used without overwrite"; - return "ProfileWidget"; -} - -int ProfileWidget::getContentMode() -{ - return contentMode; -} - -void ProfileWidget::setContentMode(int _contentMode) -{ - contentMode = _contentMode; -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "ProfileWidget.h" +#include + +ProfileWidget::ProfileWidget(QWidget *parent) : QWidget(parent) +{ + contentMode = 0; +} + +ProfileWidget::~ProfileWidget() +{ +} + +void ProfileWidget::retranslate() +{ + qDebug() << "ProfileWidget::retranslate got used without overwrite"; +} + +bool ProfileWidget::isSelected() +{ + qDebug() << "ProfileWidget::isSelected got used without overwrite"; + return false; +} + +void ProfileWidget::setSelected(bool isSelected) +{ + qDebug() << "ProfileWidget::setSelected got used without overwrite, result" << isSelected; +} + +void ProfileWidget::setSelectionMode(bool selectionMode) +{ + qDebug() << "ProfileWidget::setSelectionMode got used without overwrite, result:" << selectionMode; +} + +QString ProfileWidget::getWidgetType() +{ + qDebug() << "ProfileWidget::getWidgetType got used without overwrite"; + return "ProfileWidget"; +} + +int ProfileWidget::getContentMode() +{ + return contentMode; +} + +void ProfileWidget::setContentMode(int _contentMode) +{ + contentMode = _contentMode; +} diff --git a/ProfileWidget.h b/ProfileWidget.h old mode 100755 new mode 100644 index 6fab255..b7d2f77 --- a/ProfileWidget.h +++ b/ProfileWidget.h @@ -1,45 +1,46 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef PROFILEWIDGET_H -#define PROFILEWIDGET_H - -#include - -class ProfileWidget : public QWidget -{ - Q_OBJECT -public: - explicit ProfileWidget(QWidget *parent = 0); - virtual void setSelectionMode(bool selectionMode); - virtual void setContentMode(int contentMode); - virtual void setSelected(bool isSelected); - virtual bool isSelected(); - virtual QString getWidgetType(); - virtual int getContentMode(); - ~ProfileWidget(); - -private: - int contentMode; - -signals: - -public slots: -}; - -#endif // PROFILEWIDGET_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef PROFILEWIDGET_H +#define PROFILEWIDGET_H + +#include + +class ProfileWidget : public QWidget +{ + Q_OBJECT +public: + explicit ProfileWidget(QWidget *parent = 0); + virtual void setSelectionMode(bool selectionMode); + virtual void setContentMode(int contentMode); + virtual void setSelected(bool isSelected); + virtual bool isSelected(); + virtual QString getWidgetType(); + virtual int getContentMode(); + virtual void retranslate(); + ~ProfileWidget(); + +private: + int contentMode; + +signals: + +public slots: +}; + +#endif // PROFILEWIDGET_H diff --git a/README.md b/README.md index c81da0c..8831699 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,45 @@ ## gta5view -Grand Theft Auto V Savegame and Snapmatic viewer +Open Source Snapmatic and Savegame viewer/editor for GTA V -- Viewing Snapmatics and giving the ability to disable them in-game -- Import/Export Snapmatics and Savegames -- Choosing between multiple Social Club accounts as GTA V profiles IDs +- View Snapmatics with the ability to disable them in-game +- Edit Snapmatic pictures and properties in multiple ways +- Import/Export Snapmatics, Savegames and pictures +- Choose between multiple Social Club accounts as GTA V profiles IDs #### Screenshots - - - +![Snapmatic Picture Viewer](res/src/picture.png) +![User Interface](res/src/mainui.png) +![Snapmatic Properties](res/src/prop.png) -#### Build gta5view Debian/Ubuntu +#### Build gta5view for Windows - apt-get install git gcc g++ qtbase5-dev qt5-qmake make checkinstall - git clone https://github.com/SyDevTeam/gta5view - mkdir build && cd build - qmake -qt=5 ../gta5view.pro # or just qmake ../gta5view.pro - make - INSTALL_ROOT=/usr checkinstall --pkgname=gta5view --pkggroup=utility --requires=libqt5core5a,libqt5gui5,libqt5network5,libqt5widgets5 + # Note: Install Docker Community Edition and Git before continuing + docker pull sypingauto/gta5view-build:1.10-static + git clone https://gitlab.com/Syping/gta5view + docker run --rm -v "$PWD/gta5view:/gta5view" -it sypingauto/gta5view-build:1.10-static + mingw64-qt-cmake -B /gta5view/build /gta5view + cmake --build /gta5view/build -#### Build gta5view Windows +#### Build gta5view for Debian/Ubuntu -Downloading Qt Framework and install it.
-Take the Online Installer and choose the MinGW version or install Microsoft Visual Studio 2013/2015 Community
-Downloading Source Code over GitHub or with your Git client.
-Open the gta5view.pro file with Qt Creator and build it over Qt Creator.
+ sudo apt-get install cmake git gcc g++ libqt5svg5-dev make qtbase5-dev qttranslations5-l10n + git clone https://gitlab.com/Syping/gta5view + cmake -B gta5view-build gta5view + cmake --build gta5view-build + sudo cmake --install gta5view-build -#### Download Binary Releases +#### Build gta5view for Arch/Manjaro -Go to gta5view release and download the .exe file for Windows, .deb file for Debian/Ubuntu and .dmg file for OS X + sudo pacman -S cmake gcc git make qt5-base qt5-svg qt5-tools qt5-translations + git clone https://gitlab.com/Syping/gta5view + cmake -B gta5view-build gta5view + cmake --build gta5view-build + sudo cmake --install gta5view-build + +#### Build gta5view for Fedora/RHEL + + sudo dnf install cmake git gcc gcc-c++ make qt5-qtbase-devel qt5-qtsvg-devel qt5-qttranslations + git clone https://gitlab.com/Syping/gta5view + cmake -B gta5view-build gta5view + cmake --build gta5view-build + sudo cmake --install gta5view-build diff --git a/RagePhoto.cpp b/RagePhoto.cpp new file mode 100644 index 0000000..ba03159 --- /dev/null +++ b/RagePhoto.cpp @@ -0,0 +1,893 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2020-2022 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "RagePhoto.h" +#include +#include +#include +#if QT_VERSION < 0x060000 +#include +#else +#include +#include +#endif +#ifdef RAGEPHOTO_BENCHMARK +#include +#include +#endif + +inline quint32 joaatFromSI(const char *data, size_t size) +{ + quint32 val = 0xE47AB81CUL; + for (size_t i = 0; i != size; i++) { + val += data[i]; + val += (val << 10); + val ^= (val >> 6); + } + val += (val << 3); + val ^= (val >> 11); + val += (val << 15); + return val; +} + +RagePhoto::RagePhoto() +{ + p_photoFormat = PhotoFormat::Undefined; + p_isLoaded = false; + p_inputMode = -1; +} + +RagePhoto::RagePhoto(const QByteArray &data) : p_fileData(data) +{ + p_photoFormat = PhotoFormat::Undefined; + p_isLoaded = false; + p_inputMode = 0; +} + +RagePhoto::RagePhoto(const QString &filePath) : p_filePath(filePath) +{ + p_photoFormat = PhotoFormat::Undefined; + p_isLoaded = false; + p_inputMode = 1; +} + +RagePhoto::RagePhoto(QIODevice *ioDevice) : p_ioDevice(ioDevice) +{ + p_photoFormat = PhotoFormat::Undefined; + p_isLoaded = false; + p_inputMode = 2; +} + +bool RagePhoto::isLoaded() +{ + return p_isLoaded; +} + +bool RagePhoto::load() +{ + if (p_inputMode == -1) + return false; + + if (p_isLoaded) + clear(); + + if (p_inputMode == 1) { + QFile pictureFile(p_filePath); + if (pictureFile.open(QIODevice::ReadOnly)) { + p_fileData = pictureFile.readAll(); + } + pictureFile.close(); + } + else if (p_inputMode == 2) { + if (!p_ioDevice->isOpen()) { + if (!p_ioDevice->open(QIODevice::ReadOnly)) + return false; + } + p_fileData = p_ioDevice->readAll(); + } + + QBuffer dataBuffer(&p_fileData); + dataBuffer.open(QIODevice::ReadOnly); + +#ifdef RAGEPHOTO_BENCHMARK + auto benchmark_parse_start = std::chrono::high_resolution_clock::now(); +#endif + + char uInt32Buffer[4]; + qint64 size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + quint32 format = charToUInt32LE(uInt32Buffer); + + if (format == static_cast(PhotoFormat::GTA5)) { + char photoHeader[256]; + size = dataBuffer.read(photoHeader, 256); + if (size != 256) { + return false; + } + for (const QChar &photoChar : utf16LEToString(photoHeader, 256)) { + if (photoChar.isNull()) + break; + p_photoString += photoChar; + } + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_headerSum = charToUInt32LE(uInt32Buffer); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_endOfFile = charToUInt32LE(uInt32Buffer); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_jsonOffset = charToUInt32LE(uInt32Buffer); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_titlOffset = charToUInt32LE(uInt32Buffer); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_descOffset = charToUInt32LE(uInt32Buffer); + + char markerBuffer[4]; + size = dataBuffer.read(markerBuffer, 4); + if (size != 4) + return false; + if (strncmp(markerBuffer, "JPEG", 4) != 0) + return false; + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_photoBuffer = charToUInt32LE(uInt32Buffer); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + quint32 t_photoSize = charToUInt32LE(uInt32Buffer); + + char *photoData = static_cast(malloc(t_photoSize)); + if (!photoData) + return false; + size = dataBuffer.read(photoData, t_photoSize); + if (size != t_photoSize) { + free(photoData); + return false; + } + p_photoData = QByteArray(photoData, t_photoSize); + free(photoData); + + dataBuffer.seek(p_jsonOffset + 264); + size = dataBuffer.read(markerBuffer, 4); + if (size != 4) + return false; + if (strncmp(markerBuffer, "JSON", 4) != 0) + return false; + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_jsonBuffer = charToUInt32LE(uInt32Buffer); + + char *jsonBytes = static_cast(malloc(p_jsonBuffer)); + if (!jsonBytes) + return false; + size = dataBuffer.read(jsonBytes, p_jsonBuffer); + if (size != p_jsonBuffer) { + free(jsonBytes); + return false; + } + quint32 i; + for (i = 0; i != p_jsonBuffer; i++) { + if (jsonBytes[i] == '\x00') + break; + } + p_jsonData = QByteArray(jsonBytes, i); + free(jsonBytes); + QJsonDocument t_jsonDocument = QJsonDocument::fromJson(p_jsonData); + if (t_jsonDocument.isNull()) + return false; + p_jsonObject = t_jsonDocument.object(); + + dataBuffer.seek(p_titlOffset + 264); + size = dataBuffer.read(markerBuffer, 4); + if (size != 4) + return false; + if (strncmp(markerBuffer, "TITL", 4) != 0) + return false; + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_titlBuffer = charToUInt32LE(uInt32Buffer); + + char *titlBytes = static_cast(malloc(p_titlBuffer)); + if (!titlBytes) + return false; + size = dataBuffer.read(titlBytes, p_titlBuffer); + if (size != p_titlBuffer){ + free(titlBytes); + return false; + } + for (i = 0; i != p_titlBuffer; i++) { + if (titlBytes[i] == '\x00') + break; + } + p_titleString = QString::fromUtf8(titlBytes, i); + free(titlBytes); + + dataBuffer.seek(p_descOffset + 264); + size = dataBuffer.read(markerBuffer, 4); + if (size != 4) + return false; + if (strncmp(markerBuffer, "DESC", 4) != 0) + return false; + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_descBuffer = charToUInt32LE(uInt32Buffer); + + char *descBytes = static_cast(malloc(p_descBuffer)); + if (!descBytes) + return false; + size = dataBuffer.read(descBytes, p_descBuffer); + if (size != p_descBuffer) { + free(descBytes); + return false; + } + for (i = 0; i != p_descBuffer; i++) { + if (descBytes[i] == '\x00') + break; + } + p_descriptionString = QString::fromUtf8(descBytes, i); + free(descBytes); + + dataBuffer.seek(p_endOfFile + 260); + size = dataBuffer.read(markerBuffer, 4); + if (size != 4) + return false; + if (strncmp(markerBuffer, "JEND", 4) != 0) + return false; + +#ifdef RAGEPHOTO_BENCHMARK + auto benchmark_parse_end = std::chrono::high_resolution_clock::now(); + auto benchmark_ns = std::chrono::duration_cast(benchmark_parse_end - benchmark_parse_start); + if (p_inputMode == 1) { + QTextStream(stdout) << QFileInfo(p_filePath).fileName() << ": " << benchmark_ns.count() << "ns" << Qt::endl; + } + else { + QTextStream(stdout) << "PGTA5" << p_jsonObject.value("uid").toInt() << ": " << benchmark_ns.count() << "ns" << Qt::endl; + } +#endif + + if (p_photoFormat != PhotoFormat::G5EX) + p_photoFormat = PhotoFormat::GTA5; + + p_fileData.clear(); + p_isLoaded = true; + return true; + } + else if (format == static_cast(PhotoFormat::G5EX)) { + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + format = charToUInt32LE(uInt32Buffer); + if (format == static_cast(ExportFormat::G5E3P)) { + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + quint32 compressedSize = charToUInt32LE(uInt32Buffer); + + char *compressedPhotoHeader = static_cast(malloc(compressedSize)); + if (!compressedPhotoHeader) + return false; + size = dataBuffer.read(compressedPhotoHeader, compressedSize); + if (size != compressedSize) { + free(compressedPhotoHeader); + return false; + } + QByteArray t_photoHeader = QByteArray::fromRawData(compressedPhotoHeader, compressedSize); + t_photoHeader = qUncompress(t_photoHeader); + free(compressedPhotoHeader); + if (t_photoHeader.isEmpty()) + return false; + p_photoString = QString::fromUtf8(t_photoHeader); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_headerSum = charToUInt32LE(uInt32Buffer); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_photoBuffer = charToUInt32LE(uInt32Buffer); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + compressedSize = charToUInt32LE(uInt32Buffer); + + char *compressedPhoto = static_cast(malloc(compressedSize)); + if (!compressedPhoto) + return false; + size = dataBuffer.read(compressedPhoto, compressedSize); + if (size != compressedSize) { + free(compressedPhoto); + return false; + } + QByteArray t_photoData = QByteArray::fromRawData(compressedPhoto, compressedSize); + p_photoData = qUncompress(t_photoData); + free(compressedPhoto); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_jsonOffset = charToUInt32LE(uInt32Buffer); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_jsonBuffer = charToUInt32LE(uInt32Buffer); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + compressedSize = charToUInt32LE(uInt32Buffer); + + char *compressedJson = static_cast(malloc(compressedSize)); + if (!compressedJson) + return false; + size = dataBuffer.read(compressedJson, compressedSize); + if (size != compressedSize) { + free(compressedJson); + return false; + } + QByteArray t_jsonBytes = QByteArray::fromRawData(compressedJson, compressedSize); + p_jsonData = qUncompress(t_jsonBytes); + free(compressedJson); + if (p_jsonData.isEmpty()) + return false; + QJsonDocument t_jsonDocument = QJsonDocument::fromJson(p_jsonData); + if (t_jsonDocument.isNull()) + return false; + p_jsonObject = t_jsonDocument.object(); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_titlOffset = charToUInt32LE(uInt32Buffer); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_titlBuffer = charToUInt32LE(uInt32Buffer); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + compressedSize = charToUInt32LE(uInt32Buffer); + + char *compressedTitl = static_cast(malloc(compressedSize)); + if (!compressedTitl) + return false; + size = dataBuffer.read(compressedTitl, compressedSize); + if (size != compressedSize) { + free(compressedTitl); + return false; + } + QByteArray t_titlBytes = QByteArray::fromRawData(compressedTitl, compressedSize); + t_titlBytes = qUncompress(t_titlBytes); + free(compressedTitl); + p_titleString = QString::fromUtf8(t_titlBytes); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_descOffset = charToUInt32LE(uInt32Buffer); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_descBuffer = charToUInt32LE(uInt32Buffer); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + compressedSize = charToUInt32LE(uInt32Buffer); + + char *compressedDesc = static_cast(malloc(compressedSize)); + if (!compressedDesc) + return false; + size = dataBuffer.read(compressedDesc, compressedSize); + if (size != compressedSize) { + free(compressedDesc); + return false; + } + QByteArray t_descBytes = QByteArray::fromRawData(compressedDesc, compressedSize); + t_descBytes = qUncompress(t_descBytes); + free(compressedDesc); + p_descriptionString = QString::fromUtf8(t_descBytes); + + size = dataBuffer.read(uInt32Buffer, 4); + if (size != 4) + return false; + p_endOfFile = charToUInt32LE(uInt32Buffer); + +#ifdef RAGEPHOTO_BENCHMARK + auto benchmark_parse_end = std::chrono::high_resolution_clock::now(); + auto benchmark_ns = std::chrono::duration_cast(benchmark_parse_end - benchmark_parse_start); + if (p_inputMode == 1) { + QTextStream(stdout) << QFileInfo(p_filePath).fileName() << ": " << benchmark_ns.count() << "ns" << Qt::endl; + } + else { + QTextStream(stdout) << "PGTA5" << p_jsonObject.value("uid").toInt() << ": " << benchmark_ns.count() << "ns" << Qt::endl; + } +#endif + + p_photoFormat = PhotoFormat::G5EX; + + p_fileData.clear(); + p_isLoaded = true; + return true; + } + else if (format == static_cast(ExportFormat::G5E2P)) { + p_photoFormat = PhotoFormat::G5EX; + p_fileData = qUncompress(dataBuffer.readAll()); + if (p_fileData.isEmpty()) + return false; + p_inputMode = 0; + return load(); + } + else if (format == static_cast(ExportFormat::G5E1P)) { +#if QT_VERSION >= 0x050A00 + size = dataBuffer.skip(1); + if (size != 1) + return false; +#else + if (!dataBuffer.seek(dataBuffer.pos() + 1)) + return false; +#endif + + char length[1]; + size = dataBuffer.read(length, 1); + if (size != 1) + return false; + int i_length = QByteArray::number(static_cast(length[0]), 16).toInt() + 6; + +#if QT_VERSION >= 0x050A00 + size = dataBuffer.skip(i_length); + if (size != i_length) + return false; +#else + if (!dataBuffer.seek(dataBuffer.pos() + i_length)) + return false; +#endif + + p_photoFormat = PhotoFormat::G5EX; + p_fileData = qUncompress(dataBuffer.readAll()); + if (p_fileData.isEmpty()) + return false; + p_inputMode = 0; + return load(); + } + else { + return false; + } + } + else { + return false; + } +} + +void RagePhoto::clear() +{ + p_photoFormat = PhotoFormat::Undefined; + p_jsonObject = QJsonObject(); + p_descriptionString.clear(); + p_jsonData.clear(); + p_photoData.clear(); + p_photoString.clear(); + p_titleString.clear(); + p_headerSum = 0; + p_isLoaded = false; +} + +void RagePhoto::setDescription(const QString &description) +{ + p_descriptionString = description; +} + +void RagePhoto::setFileData(const QByteArray &data) +{ + p_fileData = data; + p_inputMode = 0; +} + +void RagePhoto::setFilePath(const QString &filePath) +{ + p_filePath = filePath; + p_inputMode = 1; +} + +void RagePhoto::setIODevice(QIODevice *ioDevice) +{ + p_ioDevice = ioDevice; + p_inputMode = 2; +} + +bool RagePhoto::setJsonData(const QByteArray &data) +{ + QJsonDocument t_jsonDocument = QJsonDocument::fromJson(data); + if (t_jsonDocument.isNull()) + return false; + p_jsonObject = t_jsonDocument.object(); + // serializer band-aid + QJsonObject t_jsonObject = p_jsonObject; + t_jsonObject["sign"] = "__gta5view.sign"; + t_jsonDocument.setObject(t_jsonObject); + p_jsonData = t_jsonDocument.toJson(QJsonDocument::Compact); + char sign_char[24]; + sprintf(sign_char, "%llu", (0x100000000000000ULL | joaatFromSI(p_photoData.constData(), p_photoData.size()))); + p_jsonData.replace("\"__gta5view.sign\"", sign_char); + return true; +} + +bool RagePhoto::setPhotoBuffer(quint32 size, bool moveOffsets) +{ + if (size < static_cast(p_photoData.size())) + return false; + p_photoBuffer = size; + if (moveOffsets) { + p_jsonOffset = size + 28; + p_titlOffset = p_jsonOffset + p_jsonBuffer + 8; + p_descOffset = p_titlOffset + p_titlBuffer + 8; + p_endOfFile = p_descOffset + p_descBuffer + 12; + } + return true; +} + +bool RagePhoto::setPhotoData(const QByteArray &data) +{ + quint32 size = data.size(); + if (size > p_photoBuffer) + return false; + p_photoData = data; + // serializer band-aid + setJsonData(p_jsonData); + return true; +} + +bool RagePhoto::setPhotoData(const char *data, int size) +{ + if (static_cast(size) > p_photoBuffer) + return false; + p_photoData = QByteArray(data, size); + // serializer band-aid + setJsonData(p_jsonData); + return true; +} + +void RagePhoto::setPhotoFormat(PhotoFormat photoFormat) +{ + p_photoFormat = photoFormat; +} + +void RagePhoto::setTitle(const QString &title) +{ + p_titleString = title; +} + +const QByteArray RagePhoto::jsonData(JsonFormat jsonFormat) +{ + if (jsonFormat == JsonFormat::Compact) { + return QJsonDocument(p_jsonObject).toJson(QJsonDocument::Compact); + } + else if (jsonFormat == JsonFormat::Indented) { + return QJsonDocument(p_jsonObject).toJson(QJsonDocument::Indented); + } + else { + return p_jsonData; + } +} + +const QJsonObject RagePhoto::jsonObject() +{ + return p_jsonObject; +} + +const QByteArray RagePhoto::photoData() +{ + return p_photoData; +} + +const QString RagePhoto::description() +{ + return p_descriptionString; +} + +const QString RagePhoto::photoString() +{ + return p_photoString; +} + +const QString RagePhoto::title() +{ + return p_titleString; +} + +quint32 RagePhoto::photoBuffer() +{ + return p_photoBuffer; +} + +quint32 RagePhoto::photoSize() +{ + return p_photoData.size(); +} + +RagePhoto::PhotoFormat RagePhoto::photoFormat() +{ + return p_photoFormat; +} + +QByteArray RagePhoto::save(PhotoFormat photoFormat) +{ + QByteArray data; + QBuffer dataBuffer(&data); + dataBuffer.open(QIODevice::WriteOnly); + save(&dataBuffer, photoFormat); + return data; +} + +void RagePhoto::save(QIODevice *ioDevice, PhotoFormat photoFormat) +{ + // serializer band-aid + setJsonData(p_jsonData); + + if (photoFormat == PhotoFormat::G5EX) { + char uInt32Buffer[4]; + quint32 format = static_cast(PhotoFormat::G5EX); + uInt32ToCharLE(format, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + format = static_cast(ExportFormat::G5E3P); + uInt32ToCharLE(format, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + QByteArray compressedData = qCompress(p_photoString.toUtf8(), 9); + quint32 compressedSize = compressedData.size(); + uInt32ToCharLE(compressedSize, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + ioDevice->write(compressedData); + + uInt32ToCharLE(p_headerSum, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + uInt32ToCharLE(p_photoBuffer, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + compressedData = qCompress(p_photoData, 9); + compressedSize = compressedData.size(); + uInt32ToCharLE(compressedSize, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + ioDevice->write(compressedData); + + uInt32ToCharLE(p_jsonOffset, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + uInt32ToCharLE(p_jsonBuffer, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + compressedData = qCompress(p_jsonData, 9); + compressedSize = compressedData.size(); + uInt32ToCharLE(compressedSize, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + ioDevice->write(compressedData); + + uInt32ToCharLE(p_titlOffset, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + uInt32ToCharLE(p_titlBuffer, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + compressedData = qCompress(p_titleString.toUtf8(), 9); + compressedSize = compressedData.size(); + uInt32ToCharLE(compressedSize, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + ioDevice->write(compressedData); + + uInt32ToCharLE(p_descOffset, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + uInt32ToCharLE(p_descBuffer, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + compressedData = qCompress(p_descriptionString.toUtf8(), 9); + compressedSize = compressedData.size(); + uInt32ToCharLE(compressedSize, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + ioDevice->write(compressedData); + + uInt32ToCharLE(p_endOfFile, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + } + else if (photoFormat == PhotoFormat::GTA5) { + char uInt32Buffer[4]; + quint32 format = static_cast(PhotoFormat::GTA5); + uInt32ToCharLE(format, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + QByteArray photoHeader = stringToUtf16LE(p_photoString); + if (photoHeader.startsWith("\xFF\xFE")) { + photoHeader.remove(0, 2); + } + qint64 photoHeaderSize = photoHeader.size(); + if (photoHeaderSize > 256) { + photoHeader = photoHeader.left(256); + photoHeaderSize = 256; + } + ioDevice->write(photoHeader); + for (qint64 size = photoHeaderSize; size < 256; size++) { + ioDevice->write("\x00", 1); + } + + uInt32ToCharLE(p_headerSum, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + uInt32ToCharLE(p_endOfFile, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + uInt32ToCharLE(p_jsonOffset, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + uInt32ToCharLE(p_titlOffset, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + uInt32ToCharLE(p_descOffset, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + ioDevice->write("JPEG", 4); + + uInt32ToCharLE(p_photoBuffer, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + quint32 t_photoSize = p_photoData.size(); + uInt32ToCharLE(t_photoSize, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + ioDevice->write(p_photoData); + for (qint64 size = t_photoSize; size < p_photoBuffer; size++) { + ioDevice->write("\x00", 1); + } + + ioDevice->seek(p_jsonOffset + 264); + ioDevice->write("JSON", 4); + + uInt32ToCharLE(p_jsonBuffer, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + qint64 dataSize = p_jsonData.size(); + ioDevice->write(p_jsonData); + for (qint64 size = dataSize; size < p_jsonBuffer; size++) { + ioDevice->write("\x00", 1); + } + + ioDevice->seek(p_titlOffset + 264); + ioDevice->write("TITL", 4); + + uInt32ToCharLE(p_titlBuffer, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + QByteArray data = p_titleString.toUtf8(); + dataSize = data.size(); + ioDevice->write(data); + for (qint64 size = dataSize; size < p_titlBuffer; size++) { + ioDevice->write("\x00", 1); + } + + ioDevice->seek(p_descOffset + 264); + ioDevice->write("DESC", 4); + + uInt32ToCharLE(p_descBuffer, uInt32Buffer); + ioDevice->write(uInt32Buffer, 4); + + data = p_descriptionString.toUtf8(); + dataSize = data.size(); + ioDevice->write(data); + for (qint64 size = dataSize; size < p_descBuffer; size++) { + ioDevice->write("\x00", 1); + } + + ioDevice->seek(p_endOfFile + 260); + ioDevice->write("JEND", 4); + } +} + +RagePhoto* RagePhoto::loadFile(const QString &filePath) +{ + RagePhoto *ragePhoto = new RagePhoto(filePath); + ragePhoto->load(); + return ragePhoto; +} + +quint32 RagePhoto::charToUInt32BE(char *x) +{ + return (static_cast(x[0]) << 24 | + static_cast(x[1]) << 16 | + static_cast(x[2]) << 8 | + static_cast(x[3])); +} + +quint32 RagePhoto::charToUInt32LE(char *x) +{ + return (static_cast(x[3]) << 24 | + static_cast(x[2]) << 16 | + static_cast(x[1]) << 8 | + static_cast(x[0])); +} + +void RagePhoto::uInt32ToCharBE(quint32 x, char *y) +{ + y[0] = x >> 24; + y[1] = x >> 16; + y[2] = x >> 8; + y[3] = x; +} + +void RagePhoto::uInt32ToCharLE(quint32 x, char *y) +{ + y[0] = x; + y[1] = x >> 8; + y[2] = x >> 16; + y[3] = x >> 24; +} + +const QByteArray RagePhoto::stringToUtf16LE(const QString &string) +{ +#if QT_VERSION >= 0x060000 + return QStringEncoder(QStringEncoder::Utf16LE)(string); +#else + return QTextCodec::codecForName("UTF-16LE")->fromUnicode(string); +#endif +} + +const QString RagePhoto::utf16LEToString(const QByteArray &data) +{ +#if QT_VERSION >= 0x060000 + return QStringDecoder(QStringDecoder::Utf16LE)(data); +#else + return QTextCodec::codecForName("UTF-16LE")->toUnicode(data); +#endif +} + +const QString RagePhoto::utf16LEToString(const char *data, int size) +{ +#if QT_VERSION >= 0x060000 + return QStringDecoder(QStringDecoder::Utf16LE)(QByteArray::fromRawData(data, size)); +#else + return QTextCodec::codecForName("UTF-16LE")->toUnicode(data, size); +#endif +} diff --git a/RagePhoto.h b/RagePhoto.h new file mode 100644 index 0000000..4f7a5ba --- /dev/null +++ b/RagePhoto.h @@ -0,0 +1,110 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2020 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef RAGEPHOTO_H +#define RAGEPHOTO_H + +#include +#include +#include + +class RagePhoto : public QObject +{ + Q_OBJECT +public: + enum class JsonFormat : quint8 { + Original = 0, + Compact = 1, + Indented = 2, + }; + enum class ExportFormat : quint32 { + G5E1P = 0x454C0010U, + G5E2P = 0x01000032U, + G5E2S = 0x02000032U, + G5E3P = 0x01000033U, + G5E3S = 0x02000033U, + Undefined = 0, + }; + enum class PhotoFormat : quint32 { + G5EX = 0x45354700U, + GTA5 = 0x01000000U, + RDR2 = 0x04000000U, + Undefined = 0, + }; + explicit RagePhoto(); + explicit RagePhoto(const QByteArray &data); + explicit RagePhoto(const QString &filePath); + explicit RagePhoto(QIODevice *ioDevice); + bool isLoaded(); + bool load(); + void clear(); + void setDescription(const QString &description); + void setFileData(const QByteArray &data); + void setFilePath(const QString &filePath); + void setIODevice(QIODevice *ioDevice); + bool setJsonData(const QByteArray &data); + bool setPhotoBuffer(quint32 size, bool moveOffsets = true); + bool setPhotoData(const QByteArray &data); + bool setPhotoData(const char *data, int size); + void setPhotoFormat(PhotoFormat photoFormat); + void setTitle(const QString &title); + const QJsonObject jsonObject(); + const QByteArray jsonData(JsonFormat jsonFormat = JsonFormat::Original); + const QByteArray photoData(); + const QString description(); + const QString photoString(); + const QString title(); + quint32 photoBuffer(); + quint32 photoSize(); + PhotoFormat photoFormat(); + QByteArray save(PhotoFormat photoFormat); + void save(QIODevice *ioDevice, PhotoFormat photoFormat); + static RagePhoto* loadFile(const QString &filePath); + +private: + inline quint32 charToUInt32BE(char *x); + inline quint32 charToUInt32LE(char *x); + inline void uInt32ToCharBE(quint32 x, char *y); + inline void uInt32ToCharLE(quint32 x, char *y); + inline const QByteArray stringToUtf16LE(const QString &string); + inline const QString utf16LEToString(const QByteArray &data); + inline const QString utf16LEToString(const char *data, int size); + PhotoFormat p_photoFormat; + QJsonObject p_jsonObject; + QByteArray p_fileData; + QByteArray p_jsonData; + QByteArray p_photoData; + QIODevice *p_ioDevice; + QString p_descriptionString; + QString p_filePath; + QString p_photoString; + QString p_titleString; + quint32 p_descBuffer; + quint32 p_descOffset; + quint32 p_endOfFile; + quint32 p_headerSum; + quint32 p_jsonBuffer; + quint32 p_jsonOffset; + quint32 p_photoBuffer; + quint32 p_titlBuffer; + quint32 p_titlOffset; + bool p_isLoaded; + int p_inputMode; +}; + +#endif // RAGEPHOTO_H diff --git a/SavegameCopy.cpp b/SavegameCopy.cpp old mode 100755 new mode 100644 index fc0e3e0..9ebbe66 --- a/SavegameCopy.cpp +++ b/SavegameCopy.cpp @@ -1,100 +1,108 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "SidebarGenerator.h" -#include "SavegameWidget.h" -#include "SavegameCopy.h" -#include "config.h" -#include -#include -#include - -SavegameCopy::SavegameCopy() -{ - -} - -void SavegameCopy::copySavegame(QWidget *parent, QString sgdPath) -{ - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - settings.beginGroup("FileDialogs"); - -fileDialogPreSave: //Work? - QFileInfo sgdFileInfo(sgdPath); - QFileDialog fileDialog(parent); - fileDialog.setFileMode(QFileDialog::AnyFile); - fileDialog.setViewMode(QFileDialog::Detail); - fileDialog.setAcceptMode(QFileDialog::AcceptSave); - fileDialog.setOption(QFileDialog::DontUseNativeDialog, false); - fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true); - fileDialog.setDefaultSuffix(""); - fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); - fileDialog.setWindowTitle(SavegameWidget::tr(("Export Savegame..."))); - fileDialog.setLabelText(QFileDialog::Accept, SavegameWidget::tr("Export")); - - QStringList filters; - filters << SavegameWidget::tr("Savegame files (SGTA*)"); - filters << SavegameWidget::tr("All files (**)"); - fileDialog.setNameFilters(filters); - - QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); - - fileDialog.setSidebarUrls(sidebarUrls); - fileDialog.restoreState(settings.value("CopySavegame","").toByteArray()); - fileDialog.selectFile(sgdFileInfo.fileName()); - - if (fileDialog.exec()) - { - QStringList selectedFiles = fileDialog.selectedFiles(); - if (selectedFiles.length() == 1) - { - QString selectedFile = selectedFiles.at(0); - - if (QFile::exists(selectedFile)) - { - if (QMessageBox::Yes == QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("Overwrite %1 with current Savegame?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)) - { - if (!QFile::remove(selectedFile)) - { - QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("Failed to overwrite %1 with current Savegame").arg("\""+selectedFile+"\"")); - goto fileDialogPreSave; //Work? - } - } - else - { - goto fileDialogPreSave; //Work? - } - } - - bool isCopied = QFile::copy(sgdPath, selectedFile); - if (!isCopied) - { - QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("Failed to export current Savegame")); - goto fileDialogPreSave; //Work? - } - } - else - { - QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("No valid file is selected")); - goto fileDialogPreSave; //Work? - } - } - - settings.setValue("CopySavegame", fileDialog.saveState()); - settings.endGroup(); -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "SidebarGenerator.h" +#include "SavegameWidget.h" +#include "StandardPaths.h" +#include "SavegameCopy.h" +#include "config.h" +#include +#include +#include +#include + +SavegameCopy::SavegameCopy() +{ + +} + +void SavegameCopy::copySavegame(QWidget *parent, QString sgdPath) +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + + settings.beginGroup("FileDialogs"); + bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool(); + settings.beginGroup("SavegameCopy"); + +fileDialogPreSave: //Work? + QFileInfo sgdFileInfo(sgdPath); + QFileDialog fileDialog(parent); + fileDialog.setFileMode(QFileDialog::AnyFile); + fileDialog.setViewMode(QFileDialog::Detail); + fileDialog.setAcceptMode(QFileDialog::AcceptSave); + fileDialog.setOption(QFileDialog::DontUseNativeDialog, dontUseNativeDialog); + fileDialog.setOption(QFileDialog::DontConfirmOverwrite, true); + fileDialog.setDefaultSuffix(""); + fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); + fileDialog.setWindowTitle(SavegameWidget::tr(("Export Savegame..."))); + fileDialog.setLabelText(QFileDialog::Accept, SavegameWidget::tr("Export")); + + QStringList filters; + filters << SavegameWidget::tr("Savegame files (SGTA*)"); + filters << SavegameWidget::tr("All files (**)"); + fileDialog.setNameFilters(filters); + + QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); + + fileDialog.setSidebarUrls(sidebarUrls); + fileDialog.setDirectory(settings.value("Directory", StandardPaths::picturesLocation()).toString()); + fileDialog.restoreGeometry(settings.value(parent->objectName() % "+Geometry", "").toByteArray()); + fileDialog.selectFile(sgdFileInfo.fileName()); + + if (fileDialog.exec()) + { + QStringList selectedFiles = fileDialog.selectedFiles(); + if (selectedFiles.length() == 1) + { + QString selectedFile = selectedFiles.at(0); + + if (QFile::exists(selectedFile)) + { + if (QMessageBox::Yes == QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("Overwrite %1 with current Savegame?").arg("\""+selectedFile+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)) + { + if (!QFile::remove(selectedFile)) + { + QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("Failed to overwrite %1 with current Savegame").arg("\""+selectedFile+"\"")); + goto fileDialogPreSave; //Work? + } + } + else + { + goto fileDialogPreSave; //Work? + } + } + + bool isCopied = QFile::copy(sgdPath, selectedFile); + if (!isCopied) + { + QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("Failed to export current Savegame")); + goto fileDialogPreSave; //Work? + } + } + else + { + QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("No valid file is selected")); + goto fileDialogPreSave; //Work? + } + } + + settings.setValue(parent->objectName() % "+Geometry", fileDialog.saveGeometry()); + settings.setValue("Directory", fileDialog.directory().absolutePath()); + settings.endGroup(); + settings.endGroup(); +} diff --git a/SavegameCopy.h b/SavegameCopy.h old mode 100755 new mode 100644 index 1ea5cf6..f5550ba --- a/SavegameCopy.h +++ b/SavegameCopy.h @@ -1,32 +1,32 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef SAVEGAMECOPY_H -#define SAVEGAMECOPY_H - -#include -#include - -class SavegameCopy -{ -public: - SavegameCopy(); - static void copySavegame(QWidget *parent, QString sgdPath); -}; - -#endif // SAVEGAMECOPY_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef SAVEGAMECOPY_H +#define SAVEGAMECOPY_H + +#include +#include + +class SavegameCopy +{ +public: + SavegameCopy(); + static void copySavegame(QWidget *parent, QString sgdPath); +}; + +#endif // SAVEGAMECOPY_H diff --git a/SavegameData.cpp b/SavegameData.cpp old mode 100755 new mode 100644 index b679325..42a9ae3 --- a/SavegameData.cpp +++ b/SavegameData.cpp @@ -1,119 +1,120 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "StringParser.h" -#include "SavegameData.h" -#include -#include -#include -#include - -#define savegameHeaderLength 260 -#define verificationValue QByteArray::fromHex("00000001") - -SavegameData::SavegameData(const QString &fileName, QObject *parent) : QObject(parent), savegameFileName(fileName) -{ - // INIT SAVEGAME - savegameStr = ""; - savegameOk = 0; -} - -bool SavegameData::readingSavegame() -{ - // Start opening file - // lastStep is like currentStep - - QFile *saveFile = new QFile(savegameFileName); - if (!saveFile->open(QFile::ReadOnly)) - { - lastStep = "1;/1,OpenFile," + StringParser::convertDrawStringForLog(savegameFileName); - saveFile->deleteLater(); - delete saveFile; - return false; - } - - // Reading Savegame Header - if (!saveFile->isReadable()) - { - lastStep = "2;/3,ReadingFile," + StringParser::convertDrawStringForLog(savegameFileName) + ",1,NOHEADER"; - saveFile->close(); - saveFile->deleteLater(); - delete saveFile; - return false; - } - QByteArray savegameHeaderLine = saveFile->read(savegameHeaderLength); - if (savegameHeaderLine.left(4) == verificationValue) - { - savegameStr = getSavegameDataString(savegameHeaderLine); - if (savegameStr.length() >= 1) - { - savegameOk = true; - } - } - saveFile->close(); - saveFile->deleteLater(); - delete saveFile; - return savegameOk; -} - -QString SavegameData::getSavegameDataString(const QByteArray &savegameHeader) -{ - QByteArray savegameBytes = savegameHeader.left(savegameHeaderLength); - QList savegameBytesList = savegameBytes.split(char(0x01)); - savegameBytes = savegameBytesList.at(1); - savegameBytesList.clear(); - return StringParser::parseTitleString(savegameBytes, savegameBytes.length()); -} - -bool SavegameData::readingSavegameFromFile(const QString &fileName) -{ - if (fileName != "") - { - savegameFileName = fileName; - return readingSavegame(); - } - else - { - return false; - } -} - -bool SavegameData::isSavegameOk() -{ - return savegameOk; -} - -QString SavegameData::getSavegameFileName() -{ - return savegameFileName; -} - -QString SavegameData::getSavegameStr() -{ - return savegameStr; -} - -QString SavegameData::getLastStep() -{ - return lastStep; -} - -void SavegameData::setSavegameFileName(QString savegameFileName_) -{ - savegameFileName = savegameFileName_; -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "SnapmaticPicture.h" +#include "StringParser.h" +#include "SavegameData.h" +#include +#include +#include +#include + +#define savegameHeaderLength 260 +#define verificationValue QByteArray::fromHex("00000001") + +SavegameData::SavegameData(const QString &fileName, QObject *parent) : QObject(parent), savegameFileName(fileName) +{ + // INIT SAVEGAME + savegameStr = ""; + savegameOk = 0; +} + +bool SavegameData::readingSavegame() +{ + // Start opening file + // lastStep is like currentStep + + QFile *saveFile = new QFile(savegameFileName); + if (!saveFile->open(QFile::ReadOnly)) + { + lastStep = "1;/1,OpenFile," % SnapmaticPicture::convertDrawStringForLog(savegameFileName); + saveFile->deleteLater(); + delete saveFile; + return false; + } + + // Reading Savegame Header + if (!saveFile->isReadable()) + { + lastStep = "2;/3,ReadingFile," % SnapmaticPicture::convertDrawStringForLog(savegameFileName) % ",1,NOHEADER"; + saveFile->close(); + saveFile->deleteLater(); + delete saveFile; + return false; + } + QByteArray savegameHeaderLine = saveFile->read(savegameHeaderLength); + if (savegameHeaderLine.left(4) == verificationValue) + { + savegameStr = getSavegameDataString(savegameHeaderLine); + if (savegameStr.length() >= 1) + { + savegameOk = true; + } + } + saveFile->close(); + saveFile->deleteLater(); + delete saveFile; + return savegameOk; +} + +QString SavegameData::getSavegameDataString(const QByteArray &savegameHeader) +{ + QByteArray savegameBytes = savegameHeader.left(savegameHeaderLength); + QList savegameBytesList = savegameBytes.split(char(0x01)); + savegameBytes = savegameBytesList.at(1); + savegameBytesList.clear(); + return SnapmaticPicture::parseTitleString(savegameBytes, savegameBytes.length()); +} + +bool SavegameData::readingSavegameFromFile(const QString &fileName) +{ + if (fileName != "") + { + savegameFileName = fileName; + return readingSavegame(); + } + else + { + return false; + } +} + +bool SavegameData::isSavegameOk() +{ + return savegameOk; +} + +QString SavegameData::getSavegameFileName() +{ + return savegameFileName; +} + +QString SavegameData::getSavegameStr() +{ + return savegameStr; +} + +QString SavegameData::getLastStep() +{ + return lastStep; +} + +void SavegameData::setSavegameFileName(QString savegameFileName_) +{ + savegameFileName = savegameFileName_; +} diff --git a/SavegameData.h b/SavegameData.h old mode 100755 new mode 100644 index 20f87b3..bedb57c --- a/SavegameData.h +++ b/SavegameData.h @@ -1,45 +1,45 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef SAVEGAMEDATA_H -#define SAVEGAMEDATA_H - -#include - -class SavegameData : public QObject -{ - Q_OBJECT -public: - explicit SavegameData(const QString &fileName = "", QObject *parent = 0); - bool readingSavegameFromFile(const QString &fileName); - bool readingSavegame(); - bool isSavegameOk(); - QString getLastStep(); - QString getSavegameStr(); - QString getSavegameFileName(); - void setSavegameFileName(QString savegameFileName); - -private: - QString getSavegameDataString(const QByteArray &savegameHeader); - QString savegameFileName; - QString savegameStr; - QString lastStep; - bool savegameOk; -}; - -#endif // SAVEGAMEDATA_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef SAVEGAMEDATA_H +#define SAVEGAMEDATA_H + +#include + +class SavegameData : public QObject +{ + Q_OBJECT +public: + explicit SavegameData(const QString &fileName = "", QObject *parent = 0); + bool readingSavegameFromFile(const QString &fileName); + bool readingSavegame(); + bool isSavegameOk(); + QString getLastStep(); + QString getSavegameStr(); + QString getSavegameFileName(); + void setSavegameFileName(QString savegameFileName); + +private: + QString getSavegameDataString(const QByteArray &savegameHeader); + QString savegameFileName; + QString savegameStr; + QString lastStep; + bool savegameOk; +}; + +#endif // SAVEGAMEDATA_H diff --git a/SavegameDialog.cpp b/SavegameDialog.cpp old mode 100755 new mode 100644 index 99447ce..27b0229 --- a/SavegameDialog.cpp +++ b/SavegameDialog.cpp @@ -1,53 +1,104 @@ -#include "SavegameDialog.h" -#include "ui_SavegameDialog.h" -#include "SavegameCopy.h" -#include "AppEnv.h" -#include - -SavegameDialog::SavegameDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::SavegameDialog) -{ - // Set Window Flags - setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); - - // Setup User Interface - ui->setupUi(this); - savegameLabStr = ui->labSavegameText->text(); - - if (QIcon::hasThemeIcon("dialog-close")) - { - ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); - } - - // DPI calculation - qreal screenRatio = AppEnv::screenRatio(); - resize(400 * screenRatio, 105 * screenRatio); -} - -SavegameDialog::~SavegameDialog() -{ - delete ui; -} - -void SavegameDialog::setSavegameData(SavegameData *savegame, QString savegamePath, bool readOk) -{ - // Showing error if reading error - if (!readOk) - { - QMessageBox::warning(this,tr("Savegame Viewer"),tr("Failed at %1").arg(savegame->getLastStep())); - return; - } - sgdPath = savegamePath; - ui->labSavegameText->setText(savegameLabStr.arg(savegame->getSavegameStr())); -} - -void SavegameDialog::on_cmdClose_clicked() -{ - this->close(); -} - -void SavegameDialog::on_cmdCopy_clicked() -{ - SavegameCopy::copySavegame(this, sgdPath); -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2018 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "SavegameDialog.h" +#include "ui_SavegameDialog.h" +#include "SavegameCopy.h" +#include "AppEnv.h" +#include +#include + +SavegameDialog::SavegameDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::SavegameDialog) +{ + // Set Window Flags +#if QT_VERSION >= 0x050900 + setWindowFlag(Qt::WindowContextHelpButtonHint, false); +#else + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); +#endif + + // Setup User Interface + ui->setupUi(this); + ui->cmdClose->setFocus(); + savegameLabStr = ui->labSavegameText->text(); + + // Set Icon for Close Button + if (QIcon::hasThemeIcon("dialog-close")) + { + ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); + } + else if (QIcon::hasThemeIcon("gtk-close")) + { + ui->cmdClose->setIcon(QIcon::fromTheme("gtk-close")); + } + + // Set Icon for Export Button + if (QIcon::hasThemeIcon("document-export")) + { + ui->cmdCopy->setIcon(QIcon::fromTheme("document-export")); + } + else if (QIcon::hasThemeIcon("document-save")) + { + ui->cmdCopy->setIcon(QIcon::fromTheme("document-save")); + } + + refreshWindowSize(); +} + +SavegameDialog::~SavegameDialog() +{ + delete ui; +} + +void SavegameDialog::refreshWindowSize() +{ + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + int dpiWindowWidth = 400 * screenRatio; + int dpiWindowHeight = 105 * screenRatio; + if (dpiWindowHeight < heightForWidth(dpiWindowWidth)) + { + dpiWindowHeight = heightForWidth(dpiWindowWidth); + } + resize(dpiWindowWidth, dpiWindowHeight); +} + +void SavegameDialog::setSavegameData(SavegameData *savegame, QString savegamePath, bool readOk) +{ + // Showing error if reading error + if (!readOk) + { + QMessageBox::warning(this,tr("Savegame Viewer"),tr("Failed at %1").arg(savegame->getLastStep())); + return; + } + sgdPath = savegamePath; + ui->labSavegameText->setText(savegameLabStr.arg(savegame->getSavegameStr())); + refreshWindowSize(); +} + +void SavegameDialog::on_cmdClose_clicked() +{ + this->close(); +} + +void SavegameDialog::on_cmdCopy_clicked() +{ + SavegameCopy::copySavegame(this, sgdPath); +} diff --git a/SavegameDialog.h b/SavegameDialog.h old mode 100755 new mode 100644 index 0b3a900..4abbba4 --- a/SavegameDialog.h +++ b/SavegameDialog.h @@ -1,29 +1,48 @@ -#ifndef SAVEGAMEDIALOG_H -#define SAVEGAMEDIALOG_H - -#include "SavegameData.h" -#include - -namespace Ui { -class SavegameDialog; -} - -class SavegameDialog : public QDialog -{ - Q_OBJECT -public: - explicit SavegameDialog(QWidget *parent = 0); - void setSavegameData(SavegameData *savegame, QString sgdPath, bool readOk); - ~SavegameDialog(); - -private slots: - void on_cmdClose_clicked(); - void on_cmdCopy_clicked(); - -private: - Ui::SavegameDialog *ui; - QString savegameLabStr; - QString sgdPath; -}; - -#endif // SAVEGAMEDIALOG_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2018 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef SAVEGAMEDIALOG_H +#define SAVEGAMEDIALOG_H + +#include "SavegameData.h" +#include + +namespace Ui { +class SavegameDialog; +} + +class SavegameDialog : public QDialog +{ + Q_OBJECT +public: + explicit SavegameDialog(QWidget *parent = 0); + void setSavegameData(SavegameData *savegame, QString sgdPath, bool readOk); + ~SavegameDialog(); + +private slots: + void on_cmdClose_clicked(); + void on_cmdCopy_clicked(); + void refreshWindowSize(); + +private: + Ui::SavegameDialog *ui; + QString savegameLabStr; + QString sgdPath; +}; + +#endif // SAVEGAMEDIALOG_H diff --git a/SavegameDialog.ui b/SavegameDialog.ui old mode 100755 new mode 100644 index a0a5e52..ac9481e --- a/SavegameDialog.ui +++ b/SavegameDialog.ui @@ -1,86 +1,93 @@ - - - SavegameDialog - - - - 0 - 0 - 400 - 105 - - - - Savegame Viewer - - - true - - - - - - - 0 - 0 - - - - <span style=" font-weight:600;">Savegame</span><br><br>%1 - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - &Export - - - - - - - - 0 - 0 - - - - &Close - - - - - - - - - - + + + SavegameDialog + + + + 0 + 0 + 400 + 112 + + + + Savegame Viewer + + + true + + + + + + <span style="font-weight:600">Savegame</span><br><br>%1 + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + &Export + + + + + + + + 0 + 0 + + + + &Close + + + + + + + + + + diff --git a/SavegameWidget.cpp b/SavegameWidget.cpp old mode 100755 new mode 100644 index 6783bf9..cede630 --- a/SavegameWidget.cpp +++ b/SavegameWidget.cpp @@ -1,261 +1,314 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "SavegameWidget.h" -#include "ui_SavegameWidget.h" -#include "SidebarGenerator.h" -#include "SavegameDialog.h" -#include "StandardPaths.h" -#include "SavegameData.h" -#include "SavegameCopy.h" -#include "AppEnv.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -SavegameWidget::SavegameWidget(QWidget *parent) : - ProfileWidget(parent), - ui(new Ui::SavegameWidget) -{ - ui->setupUi(this); - ui->cmdCopy->setVisible(false); - ui->cmdView->setVisible(false); - ui->cmdDelete->setVisible(false); - ui->cbSelected->setVisible(false); - - qreal screenRatio = AppEnv::screenRatio(); - ui->labSavegamePic->setFixedSize(48 * screenRatio, 27 * screenRatio); - - QPixmap savegamePixmap(":/img/savegame.png"); - if (screenRatio != 1) savegamePixmap = savegamePixmap.scaledToHeight(ui->labSavegamePic->height(), Qt::SmoothTransformation); - ui->labSavegamePic->setPixmap(savegamePixmap); - - QString exportSavegameStr = tr("Export Savegame..."); - Q_UNUSED(exportSavegameStr) - - QPalette palette; - highlightBackColor = palette.highlight().color(); - highlightTextColor = palette.highlightedText().color(); - - labelAutosaveStr = tr("AUTOSAVE - %1\n%2"); - labelSaveStr = tr("SAVE %3 - %1\n%2"); - snwgt = parent; - sgdPath = ""; - sgdStr = ""; - sgdata = 0; - - installEventFilter(this); -} - -SavegameWidget::~SavegameWidget() -{ - delete ui; -} - -bool SavegameWidget::eventFilter(QObject *obj, QEvent *ev) -{ - if (obj == this) - { - if (ev->type() == QEvent::Enter) - { - setStyleSheet(QString("QFrame#SavegameFrame{background-color: rgb(%1, %2, %3)}QLabel#labSavegameStr{color: rgb(%4, %5, %6)}").arg(QString::number(highlightBackColor.red()), QString::number(highlightBackColor.green()), QString::number(highlightBackColor.blue()), QString::number(highlightTextColor.red()), QString::number(highlightTextColor.green()), QString::number(highlightTextColor.blue()))); - return true; - } - else if(ev->type() == QEvent::Leave) - { - setStyleSheet(""); - return true; - } - } - return false; -} - -void SavegameWidget::setSavegameData(SavegameData *savegame, QString savegamePath) -{ - // BETA CODE - bool validNumber; - QString savegameName = tr("WRONG FORMAT"); - QString savegameDate = tr("WRONG FORMAT"); - QString savegameString = savegame->getSavegameStr(); - QString fileName = QFileInfo(savegame->getSavegameFileName()).fileName(); - QStringList savegameNDL = QString(savegameString).split(" - "); - if (savegameNDL.length() >= 2) - { - savegameDate = savegameNDL.at(savegameNDL.length() - 1); - savegameName = QString(savegameString).remove(savegameString.length() - savegameDate.length() - 3, savegameDate.length() + 3); - } - int savegameNumber = QString(fileName).remove(0,5).toInt(&validNumber) + 1; - if (validNumber) - { - if (savegameNumber == 16) - { - ui->labSavegameStr->setText(labelAutosaveStr.arg(savegameDate, savegameName)); - } - else - { - ui->labSavegameStr->setText(labelSaveStr.arg(savegameDate, savegameName, QString::number(savegameNumber))); - } - } - else - { - ui->labSavegameStr->setText(labelSaveStr.arg(savegameDate, savegameName, tr("UNKNOWN"))); - } - sgdStr = savegameString; - sgdPath = savegamePath; - sgdata = savegame; -} - -void SavegameWidget::on_cmdCopy_clicked() -{ - SavegameCopy::copySavegame(this, sgdPath); -} - -void SavegameWidget::on_cmdDelete_clicked() -{ - int uchoice = QMessageBox::question(this, tr("Delete savegame"), tr("Are you sure to delete %1 from your savegames?").arg("\""+sgdStr+"\""), QMessageBox::No | QMessageBox::Yes, QMessageBox::No); - if (uchoice == QMessageBox::Yes) - { - if (!QFile::exists(sgdPath)) - { - emit savegameDeleted(); - } - else if(QFile::remove(sgdPath)) - { - emit savegameDeleted(); - } - else - { - QMessageBox::warning(this, tr("Delete savegame"), tr("Failed at deleting %1 from your savegames").arg("\""+sgdStr+"\"")); - } - } -} - -void SavegameWidget::on_cmdView_clicked() -{ - SavegameDialog *savegameDialog = new SavegameDialog(this); - savegameDialog->setSavegameData(sgdata, sgdPath, true); - savegameDialog->setModal(true); -#ifdef Q_OS_ANDROID - // Android ... - savegameDialog->showMaximized(); -#else - savegameDialog->show(); -#endif - savegameDialog->exec(); - delete savegameDialog; -} - -void SavegameWidget::mousePressEvent(QMouseEvent *ev) -{ - ProfileWidget::mousePressEvent(ev); -} - -void SavegameWidget::mouseReleaseEvent(QMouseEvent *ev) -{ - ProfileWidget::mouseReleaseEvent(ev); - if (ui->cbSelected->isVisible()) - { - if (rect().contains(ev->pos()) && ev->button() == Qt::LeftButton) - { - ui->cbSelected->setChecked(!ui->cbSelected->isChecked()); - } - } - else - { - if (getContentMode() == 0 && rect().contains(ev->pos()) && ev->button() == Qt::LeftButton) - { - on_cmdView_clicked(); - } - } -} - -void SavegameWidget::mouseDoubleClickEvent(QMouseEvent *ev) -{ - ProfileWidget::mouseDoubleClickEvent(ev); - - if (!ui->cbSelected->isVisible() && getContentMode() == 1 && ev->button() == Qt::LeftButton) - { - on_cmdView_clicked(); - } -} - -void SavegameWidget::setSelected(bool isSelected) -{ - ui->cbSelected->setChecked(isSelected); -} - -void SavegameWidget::savegameSelected() -{ - setSelected(!ui->cbSelected->isChecked()); -} - -void SavegameWidget::contextMenuEvent(QContextMenuEvent *ev) -{ - emit contextMenuTriggered(ev); -} - -void SavegameWidget::on_cbSelected_stateChanged(int arg1) -{ - if (arg1 == Qt::Checked) - { - emit widgetSelected(); - } - else if (arg1 == Qt::Unchecked) - { - emit widgetDeselected(); - } -} - -bool SavegameWidget::isSelected() -{ - return ui->cbSelected->isChecked(); -} - -void SavegameWidget::setSelectionMode(bool selectionMode) -{ - ui->cbSelected->setVisible(selectionMode); -} - -void SavegameWidget::selectAllWidgets() -{ - emit allWidgetsSelected(); -} - -void SavegameWidget::deselectAllWidgets() -{ - emit allWidgetsDeselected(); -} - -SavegameData* SavegameWidget::getSavegame() -{ - return sgdata; -} - -QString SavegameWidget::getWidgetType() -{ - return "SavegameWidget"; -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "SavegameWidget.h" +#include "ui_SavegameWidget.h" +#include "SidebarGenerator.h" +#include "SavegameDialog.h" +#include "StandardPaths.h" +#include "SavegameData.h" +#include "SavegameCopy.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GTA5SYNC_TELEMETRY +#include "TelemetryClass.h" +#include +#include +#include +#endif + +SavegameWidget::SavegameWidget(QWidget *parent) : + ProfileWidget(parent), + ui(new Ui::SavegameWidget) +{ + ui->setupUi(this); + ui->cmdCopy->setVisible(false); + ui->cmdView->setVisible(false); + ui->cmdDelete->setVisible(false); + ui->cbSelected->setVisible(false); + + qreal screenRatio = AppEnv::screenRatio(); + ui->labSavegamePic->setFixedSize(48 * screenRatio, 27 * screenRatio); + + ui->labSavegamePic->setScaledContents(true); + ui->labSavegamePic->setPixmap(QPixmap(AppEnv::getImagesFolder() % "/savegame.svgz")); + + QString exportSavegameStr = tr("Export Savegame..."); + Q_UNUSED(exportSavegameStr) + + labelAutosaveStr = tr("AUTOSAVE - %1\n%2"); + labelSaveStr = tr("SAVE %3 - %1\n%2"); + + ui->SavegameFrame->setMouseTracking(true); + ui->labSavegamePic->setMouseTracking(true); + ui->labSavegameStr->setMouseTracking(true); + ui->cbSelected->setMouseTracking(true); + sgdata = nullptr; +} + +SavegameWidget::~SavegameWidget() +{ + delete ui; +} + +void SavegameWidget::setSavegameData(SavegameData *savegame, QString savegamePath) +{ + // BETA CODE + QString savegameString = savegame->getSavegameStr(); + QString fileName = QFileInfo(savegame->getSavegameFileName()).fileName(); + renderString(savegameString, fileName); + sgdStr = savegameString; + sgdPath = savegamePath; + sgdata = savegame; +} + +void SavegameWidget::renderString(const QString &savegameString, const QString &fileName) +{ + bool validNumber; + QString savegameName = tr("WRONG FORMAT"); + QString savegameDate = tr("WRONG FORMAT"); + QStringList savegameNDL = QString(savegameString).split(" - "); + if (savegameNDL.length() >= 2) + { + savegameDate = savegameNDL.at(savegameNDL.length() - 1); + savegameName = QString(savegameString).remove(savegameString.length() - savegameDate.length() - 3, savegameDate.length() + 3); + } + int savegameNumber = QString(fileName).remove(0,5).toInt(&validNumber) + 1; + if (validNumber) + { + if (savegameNumber == 16) + { + ui->labSavegameStr->setText(labelAutosaveStr.arg(savegameDate, savegameName)); + } + else + { + ui->labSavegameStr->setText(labelSaveStr.arg(savegameDate, savegameName, QString::number(savegameNumber))); + } + } + else + { + ui->labSavegameStr->setText(labelSaveStr.arg(savegameDate, savegameName, tr("UNKNOWN"))); + } +} + +void SavegameWidget::retranslate() +{ + labelAutosaveStr = tr("AUTOSAVE - %1\n%2"); + labelSaveStr = tr("SAVE %3 - %1\n%2"); + + QString fileName = QFileInfo(sgdata->getSavegameFileName()).fileName(); + renderString(sgdStr, fileName); +} + +void SavegameWidget::on_cmdCopy_clicked() +{ + SavegameCopy::copySavegame(this, sgdPath); +} + +void SavegameWidget::on_cmdDelete_clicked() +{ + int uchoice = QMessageBox::question(this, tr("Delete Savegame"), tr("Are you sure to delete %1 from your savegames?").arg("\""+sgdStr+"\""), QMessageBox::No | QMessageBox::Yes, QMessageBox::No); + if (uchoice == QMessageBox::Yes) + { + if (!QFile::exists(sgdPath)) + { + emit savegameDeleted(); +#ifdef GTA5SYNC_TELEMETRY + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool(); + telemetrySettings.endGroup(); + if (pushUsageData && Telemetry->canPush()) + { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "DeleteSuccess"; + jsonObject["ExtraFlags"] = "Savegame"; +#if QT_VERSION >= 0x060000 + jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } +#endif + } + else if (QFile::remove(sgdPath)) + { +#ifdef GTA5SYNC_TELEMETRY + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool(); + telemetrySettings.endGroup(); + if (pushUsageData && Telemetry->canPush()) + { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "DeleteSuccess"; + jsonObject["ExtraFlags"] = "Savegame"; +#if QT_VERSION >= 0x060000 + jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } +#endif + emit savegameDeleted(); + } + else + { + QMessageBox::warning(this, tr("Delete Savegame"), tr("Failed at deleting %1 from your savegames").arg("\""+sgdStr+"\"")); + } + } +} + +void SavegameWidget::on_cmdView_clicked() +{ + SavegameDialog *savegameDialog = new SavegameDialog(this); + savegameDialog->setSavegameData(sgdata, sgdPath, true); + savegameDialog->setModal(true); +#ifdef Q_OS_ANDROID + // Android ... + savegameDialog->showMaximized(); +#else + savegameDialog->show(); +#endif + savegameDialog->exec(); + delete savegameDialog; +} + +void SavegameWidget::mousePressEvent(QMouseEvent *ev) +{ + ProfileWidget::mousePressEvent(ev); +} + +void SavegameWidget::mouseReleaseEvent(QMouseEvent *ev) +{ + ProfileWidget::mouseReleaseEvent(ev); + if (ui->cbSelected->isVisible()) + { + if (rect().contains(ev->pos()) && ev->button() == Qt::LeftButton) + { + ui->cbSelected->setChecked(!ui->cbSelected->isChecked()); + } + } + else + { + const int contentMode = getContentMode(); + if ((contentMode == 0 || contentMode == 10 || contentMode == 20) && rect().contains(ev->pos()) && ev->button() == Qt::LeftButton) + { + if (ev->modifiers().testFlag(Qt::ShiftModifier)) + { + ui->cbSelected->setChecked(!ui->cbSelected->isChecked()); + } + else + { + on_cmdView_clicked(); + } + } + else if (!ui->cbSelected->isVisible() && (contentMode == 1 || contentMode == 11 || contentMode == 21) && ev->button() == Qt::LeftButton && ev->modifiers().testFlag(Qt::ShiftModifier)) + { + ui->cbSelected->setChecked(!ui->cbSelected->isChecked()); + } + } +} + +void SavegameWidget::mouseDoubleClickEvent(QMouseEvent *ev) +{ + ProfileWidget::mouseDoubleClickEvent(ev); + + const int contentMode = getContentMode(); + if (!ui->cbSelected->isVisible() && (contentMode == 1 || contentMode == 11 || contentMode == 21) && ev->button() == Qt::LeftButton) + { + on_cmdView_clicked(); + } +} + +void SavegameWidget::setSelected(bool isSelected) +{ + ui->cbSelected->setChecked(isSelected); +} + +void SavegameWidget::savegameSelected() +{ + setSelected(!ui->cbSelected->isChecked()); +} + +void SavegameWidget::contextMenuEvent(QContextMenuEvent *ev) +{ + emit contextMenuTriggered(ev); +} + +void SavegameWidget::on_cbSelected_stateChanged(int arg1) +{ + if (arg1 == Qt::Checked) + { + emit widgetSelected(); + } + else if (arg1 == Qt::Unchecked) + { + emit widgetDeselected(); + } +} + +bool SavegameWidget::isSelected() +{ + return ui->cbSelected->isChecked(); +} + +void SavegameWidget::setSelectionMode(bool selectionMode) +{ + ui->cbSelected->setVisible(selectionMode); +} + +void SavegameWidget::selectAllWidgets() +{ + emit allWidgetsSelected(); +} + +void SavegameWidget::deselectAllWidgets() +{ + emit allWidgetsDeselected(); +} + +SavegameData* SavegameWidget::getSavegame() +{ + return sgdata; +} + +QString SavegameWidget::getWidgetType() +{ + return "SavegameWidget"; +} diff --git a/SavegameWidget.h b/SavegameWidget.h old mode 100755 new mode 100644 index f0f62c4..103fa55 --- a/SavegameWidget.h +++ b/SavegameWidget.h @@ -1,82 +1,80 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef SAVEGAMEWIDGET_H -#define SAVEGAMEWIDGET_H -#include "ProfileWidget.h" -#include "SavegameData.h" -#include -#include -#include -#include - -namespace Ui { -class SavegameWidget; -} - -class SavegameWidget : public ProfileWidget -{ - Q_OBJECT - -public: - SavegameWidget(QWidget *parent = 0); - void setSavegameData(SavegameData *savegame, QString savegamePath); - void setSelectionMode(bool selectionMode); - void setSelected(bool isSelected); - SavegameData* getSavegame(); - QString getWidgetType(); - bool isSelected(); - ~SavegameWidget(); - -private slots: - void on_cmdView_clicked(); - void on_cmdCopy_clicked(); - void on_cmdDelete_clicked(); - void on_cbSelected_stateChanged(int arg1); - void savegameSelected(); - void selectAllWidgets(); - void deselectAllWidgets(); - -protected: - bool eventFilter(QObject *obj, QEvent *ev); - void mouseDoubleClickEvent(QMouseEvent *ev); - void mouseReleaseEvent(QMouseEvent *ev); - void mousePressEvent(QMouseEvent *ev); - void contextMenuEvent(QContextMenuEvent *ev); - -private: - Ui::SavegameWidget *ui; - SavegameData *sgdata; - QColor highlightBackColor; - QColor highlightTextColor; - QString labelAutosaveStr; - QString labelSaveStr; - QString sgdPath; - QString sgdStr; - QWidget *snwgt; - -signals: - void savegameDeleted(); - void widgetSelected(); - void widgetDeselected(); - void allWidgetsSelected(); - void allWidgetsDeselected(); - void contextMenuTriggered(QContextMenuEvent *ev); -}; - -#endif // SAVEGAMEWIDGET_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef SAVEGAMEWIDGET_H +#define SAVEGAMEWIDGET_H +#include "ProfileWidget.h" +#include "SavegameData.h" +#include +#include +#include +#include + +namespace Ui { +class SavegameWidget; +} + +class SavegameWidget : public ProfileWidget +{ + Q_OBJECT + +public: + SavegameWidget(QWidget *parent = 0); + void setSavegameData(SavegameData *savegame, QString savegamePath); + void setSelectionMode(bool selectionMode); + void setSelected(bool isSelected); + SavegameData* getSavegame(); + QString getWidgetType(); + bool isSelected(); + void retranslate(); + ~SavegameWidget(); + +private slots: + void on_cmdView_clicked(); + void on_cmdCopy_clicked(); + void on_cmdDelete_clicked(); + void on_cbSelected_stateChanged(int arg1); + void savegameSelected(); + void selectAllWidgets(); + void deselectAllWidgets(); + +protected: + void mouseDoubleClickEvent(QMouseEvent *ev); + void mouseReleaseEvent(QMouseEvent *ev); + void mousePressEvent(QMouseEvent *ev); + void contextMenuEvent(QContextMenuEvent *ev); + +private: + Ui::SavegameWidget *ui; + SavegameData *sgdata; + QString labelAutosaveStr; + QString labelSaveStr; + QString sgdPath; + QString sgdStr; + void renderString(const QString &savegameString, const QString &fileName); + +signals: + void savegameDeleted(); + void widgetSelected(); + void widgetDeselected(); + void allWidgetsSelected(); + void allWidgetsDeselected(); + void contextMenuTriggered(QContextMenuEvent *ev); +}; + +#endif // SAVEGAMEWIDGET_H diff --git a/SavegameWidget.ui b/SavegameWidget.ui old mode 100755 new mode 100644 index 2f857c7..ea5e3c6 --- a/SavegameWidget.ui +++ b/SavegameWidget.ui @@ -1,135 +1,135 @@ - - - SavegameWidget - - - - 0 - 0 - 405 - 46 - - - - Savegame Widget - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - - - Qt::NoFocus - - - - - - - - - - - - - - - - - - 0 - 0 - - - - SAVE %3 - %1<br>%2 - - - true - - - - - - - - 0 - 0 - - - - View savegame - - - View - - - - - - - - 0 - 0 - - - - Copy savegame - - - Export - - - true - - - - - - - - 0 - 0 - - - - Delete savegame - - - Delete - - - true - - - - - - - - - - - + + + SavegameWidget + + + + 0 + 0 + 405 + 46 + + + + Savegame Widget + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + + + Qt::NoFocus + + + + + + + + + + + + + + + + + + 0 + 0 + + + + SAVE %3 - %1<br>%2 + + + true + + + + + + + + 0 + 0 + + + + View savegame + + + View + + + + + + + + 0 + 0 + + + + Copy savegame + + + Export + + + true + + + + + + + + 0 + 0 + + + + Delete savegame + + + Delete + + + true + + + + + + + + + + + diff --git a/SidebarGenerator.cpp b/SidebarGenerator.cpp old mode 100755 new mode 100644 index 2675dae..e93474c --- a/SidebarGenerator.cpp +++ b/SidebarGenerator.cpp @@ -1,61 +1,61 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "SidebarGenerator.h" -#include "StandardPaths.h" -#include "AppEnv.h" -#include -#include -#include - -SidebarGenerator::SidebarGenerator() -{ - -} - -QList SidebarGenerator::generateSidebarUrls(QList sidebarUrls) -{ - QDir dir; - - dir.setPath(StandardPaths::picturesLocation()); - if (dir.exists()) - { - sidebarUrls << QUrl::fromLocalFile(dir.absolutePath()); - } - - dir.setPath(StandardPaths::documentsLocation()); - if (dir.exists()) - { - sidebarUrls << QUrl::fromLocalFile(dir.absolutePath()); - } - - bool gameFolderExists; - QString gameFolder = AppEnv::getGameFolder(&gameFolderExists); - if (gameFolderExists) - { - sidebarUrls << QUrl::fromLocalFile(gameFolder); - } - - dir.setPath(StandardPaths::desktopLocation()); - if (dir.exists()) - { - sidebarUrls << QUrl::fromLocalFile(dir.absolutePath()); - } - - return sidebarUrls; -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "SidebarGenerator.h" +#include "StandardPaths.h" +#include "AppEnv.h" +#include +#include +#include + +SidebarGenerator::SidebarGenerator() +{ + +} + +QList SidebarGenerator::generateSidebarUrls(QList sidebarUrls) +{ + QDir dir; + + dir.setPath(StandardPaths::picturesLocation()); + if (dir.exists()) + { + sidebarUrls += QUrl::fromLocalFile(dir.absolutePath()); + } + + dir.setPath(StandardPaths::documentsLocation()); + if (dir.exists()) + { + sidebarUrls += QUrl::fromLocalFile(dir.absolutePath()); + } + + bool gameFolderExists; + QString gameFolder = AppEnv::getGameFolder(&gameFolderExists); + if (gameFolderExists) + { + sidebarUrls += QUrl::fromLocalFile(gameFolder); + } + + dir.setPath(StandardPaths::desktopLocation()); + if (dir.exists()) + { + sidebarUrls += QUrl::fromLocalFile(dir.absolutePath()); + } + + return sidebarUrls; +} diff --git a/SidebarGenerator.h b/SidebarGenerator.h old mode 100755 new mode 100644 index 35716c0..446f73c --- a/SidebarGenerator.h +++ b/SidebarGenerator.h @@ -1,32 +1,32 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef SIDEBARGENERATOR_H -#define SIDEBARGENERATOR_H - -#include -#include - -class SidebarGenerator -{ -public: - SidebarGenerator(); - static QList generateSidebarUrls(QList sidebarUrls); -}; - -#endif // SIDEBARGENERATOR_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef SIDEBARGENERATOR_H +#define SIDEBARGENERATOR_H + +#include +#include + +class SidebarGenerator +{ +public: + SidebarGenerator(); + static QList generateSidebarUrls(QList sidebarUrls); +}; + +#endif // SIDEBARGENERATOR_H diff --git a/SnapmaticEditor.cpp b/SnapmaticEditor.cpp index c4ce38f..f07346e 100644 --- a/SnapmaticEditor.cpp +++ b/SnapmaticEditor.cpp @@ -1,340 +1,427 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "SnapmaticEditor.h" -#include "ui_SnapmaticEditor.h" -#include "SnapmaticPicture.h" -#include "StringParser.h" -#include "AppEnv.h" -#include -#include -#include -#include -#include -#include - -SnapmaticEditor::SnapmaticEditor(CrewDatabase *crewDB, QWidget *parent) : - QDialog(parent), crewDB(crewDB), - ui(new Ui::SnapmaticEditor) -{ - ui->setupUi(this); - ui->cmdApply->setDefault(true); - - if (QIcon::hasThemeIcon("dialog-apply")) - { - ui->cmdApply->setIcon(QIcon::fromTheme("dialog-apply")); - } - if (QIcon::hasThemeIcon("dialog-cancel")) - { - ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel")); - } - - snapmaticTitle = ""; - smpic = 0; - - // DPI calculation - qreal screenRatio = AppEnv::screenRatio(); - resize(400 * screenRatio, 360 * screenRatio); -} - -SnapmaticEditor::~SnapmaticEditor() -{ - delete ui; -} - -void SnapmaticEditor::selfie_toggled(bool checked) -{ - if (checked) - { - isSelfie = true; - } - else - { - isSelfie = false; - } -} - - -void SnapmaticEditor::mugshot_toggled(bool checked) -{ - if (checked) - { - isMugshot = true; - ui->cbDirector->setEnabled(false); - ui->cbDirector->setChecked(false); - } - else - { - isMugshot = false; - ui->cbDirector->setEnabled(true); - } -} - -void SnapmaticEditor::editor_toggled(bool checked) -{ - if (checked) - { - isEditor = true; - ui->cbDirector->setEnabled(false); - ui->cbDirector->setChecked(false); - } - else - { - isEditor = false; - ui->cbDirector->setEnabled(true); - } -} - -void SnapmaticEditor::on_rbSelfie_toggled(bool checked) -{ - if (checked) - { - mugshot_toggled(false); - editor_toggled(false); - selfie_toggled(true); - } -} - -void SnapmaticEditor::on_rbMugshot_toggled(bool checked) -{ - if (checked) - { - selfie_toggled(false); - editor_toggled(false); - mugshot_toggled(true); - } -} - -void SnapmaticEditor::on_rbEditor_toggled(bool checked) -{ - if (checked) - { - selfie_toggled(false); - mugshot_toggled(false); - editor_toggled(true); - } -} - -void SnapmaticEditor::on_rbCustom_toggled(bool checked) -{ - if (checked) - { - selfie_toggled(false); - mugshot_toggled(false); - editor_toggled(false); - } -} - -void SnapmaticEditor::setSnapmaticPicture(SnapmaticPicture *picture) -{ - smpic = picture; - localSpJson = smpic->getSnapmaticProperties(); - ui->rbCustom->setChecked(true); - crewID = localSpJson.crewID; - isSelfie = localSpJson.isSelfie; - isMugshot = localSpJson.isMug; - isEditor = localSpJson.isFromRSEditor; - ui->cbDirector->setChecked(localSpJson.isFromDirector); - ui->cbMeme->setChecked(localSpJson.isMeme); - if (isSelfie) - { - ui->rbSelfie->setChecked(true); - } - else if (isMugshot) - { - ui->rbMugshot->setChecked(true); - } - else if (isEditor) - { - ui->rbEditor->setChecked(true); - } - else - { - ui->rbCustom->setChecked(true); - } - setSnapmaticCrew(returnCrewName(crewID)); - setSnapmaticTitle(picture->getPictureTitle()); -} - -void SnapmaticEditor::setSnapmaticTitle(const QString &title) -{ - if (title.length() > 39) - { - snapmaticTitle = title.left(39); - } - else - { - snapmaticTitle = title; - } - QString editStr = QString("%1").arg(tr("Edit")); - QString titleStr = tr("Title: %1 (%2)").arg(StringParser::escapeString(snapmaticTitle), editStr); - ui->labTitle->setText(titleStr); - if (SnapmaticPicture::verifyTitle(snapmaticTitle)) - { - ui->labAppropriate->setText(tr("Appropriate: %1").arg(QString("%1").arg(tr("Yes", "Yes, should work fine")))); - } - else - { - ui->labAppropriate->setText(tr("Appropriate: %1").arg(QString("%1").arg(tr("No", "No, could lead to issues")))); - } -} - -void SnapmaticEditor::setSnapmaticCrew(const QString &crew) -{ - QString editStr = QString("%1").arg(tr("Edit")); - QString crewStr = tr("Crew: %1 (%2)").arg(StringParser::escapeString(crew), editStr); - ui->labCrew->setText(crewStr); -} - -QString SnapmaticEditor::returnCrewName(int crewID_) -{ - return crewDB->getCrewName(crewID_); -} - -void SnapmaticEditor::on_cmdCancel_clicked() -{ - close(); -} - -void SnapmaticEditor::on_cmdApply_clicked() -{ - if (ui->cbQualify->isChecked()) - { - qualifyAvatar(); - } - localSpJson.crewID = crewID; - localSpJson.isSelfie = isSelfie; - localSpJson.isMug = isMugshot; - localSpJson.isFromRSEditor = isEditor; - localSpJson.isFromDirector = ui->cbDirector->isChecked(); - localSpJson.isMeme = ui->cbMeme->isChecked(); - if (smpic) - { - QString originalFileName = smpic->getPictureFilePath(); - QString adjustedFileName = originalFileName; - if (adjustedFileName.right(7) == ".hidden") // for the hidden file system - { - adjustedFileName.remove(adjustedFileName.length() - 7, 7); - } - QString backupFileName = adjustedFileName % ".bak"; - if (!QFile::exists(backupFileName)) - { - QFile::copy(adjustedFileName, backupFileName); - } - SnapmaticProperties fallbackProperties = smpic->getSnapmaticProperties(); - QString fallbackTitle = smpic->getPictureTitle(); - smpic->setSnapmaticProperties(localSpJson); - smpic->setPictureTitle(snapmaticTitle); - if (!smpic->exportPicture(originalFileName)) - { - QMessageBox::warning(this, tr("Snapmatic Properties"), tr("Patching of Snapmatic Properties failed because of I/O Error")); - smpic->setSnapmaticProperties(fallbackProperties); - smpic->setPictureTitle(fallbackTitle); - } - else - { - smpic->emitUpdate(); - } - } - close(); -} - -void SnapmaticEditor::qualifyAvatar() -{ - ui->rbSelfie->setChecked(true); - ui->cbDirector->setChecked(false); - ui->cbMeme->setChecked(false); - ui->cmdApply->setDefault(true); -} - -void SnapmaticEditor::on_cbQualify_toggled(bool checked) -{ - if (checked) - { - ui->cbMeme->setEnabled(false); - ui->cbDirector->setEnabled(false); - ui->rbCustom->setEnabled(false); - ui->rbSelfie->setEnabled(false); - ui->rbEditor->setEnabled(false); - ui->rbMugshot->setEnabled(false); - } - else - { - ui->cbMeme->setEnabled(true); - ui->rbCustom->setEnabled(true); - ui->rbSelfie->setEnabled(true); - ui->rbEditor->setEnabled(true); - ui->rbMugshot->setEnabled(true); - if (ui->rbSelfie->isChecked() || ui->rbCustom->isChecked()) - { - ui->cbDirector->setEnabled(true); - } - } -} - -void SnapmaticEditor::on_labTitle_linkActivated(const QString &link) -{ - if (link == "g5e://edittitle") - { - bool ok; - QString newTitle = QInputDialog::getText(this, tr("Snapmatic Title"), tr("New Snapmatic title:"), QLineEdit::Normal, snapmaticTitle, &ok, windowFlags()); - if (ok && !newTitle.isEmpty()) - { - setSnapmaticTitle(newTitle); - } - } -} - -void SnapmaticEditor::on_labCrew_linkActivated(const QString &link) -{ - if (link == "g5e://editcrew") - { - bool ok; - int indexNum = 0; - QStringList itemList; - QStringList crewList = crewDB->getCrews(); - if (!crewList.contains(QLatin1String("0"))) - { - crewList.append(QLatin1String("0")); - } - crewList.sort(); - foreach(const QString &crew, crewList) - { - itemList.append(QString("%1 (%2)").arg(crew, returnCrewName(crew.toInt()))); - } - if (crewList.contains(QString::number(crewID))) - { - indexNum = crewList.indexOf(QRegExp(QString::number(crewID))); - } - QString newCrew = QInputDialog::getItem(this, tr("Snapmatic Crew"), tr("New Snapmatic crew:"), itemList, indexNum, true, &ok, windowFlags()); - if (ok && !newCrew.isEmpty()) - { - if (newCrew.contains(" ")) newCrew = newCrew.split(" ").at(0); - if (newCrew.length() > 10) return; - foreach (const QChar &crewChar, newCrew) - { - if (!crewChar.isNumber()) - { - return; - } - } - crewID = newCrew.toInt(); - setSnapmaticCrew(returnCrewName(crewID)); - } - } -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "SnapmaticEditor.h" +#include "ui_SnapmaticEditor.h" +#include "SnapmaticPicture.h" +#include "PlayerListDialog.h" +#include "StringParser.h" +#include "wrapper.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include + +#ifdef GTA5SYNC_TELEMETRY +#include "TelemetryClass.h" +#include +#include +#endif + +SnapmaticEditor::SnapmaticEditor(CrewDatabase *crewDB, ProfileDatabase *profileDB, QWidget *parent) : + QDialog(parent), crewDB(crewDB), profileDB(profileDB), + ui(new Ui::SnapmaticEditor) +{ + // Set Window Flags +#if QT_VERSION >= 0x050900 + setWindowFlag(Qt::WindowContextHelpButtonHint, false); +#else + setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint); +#endif + + ui->setupUi(this); + ui->cmdCancel->setDefault(true); + ui->cmdCancel->setFocus(); + + // Set Icon for Apply Button + if (QIcon::hasThemeIcon("dialog-ok-apply")) { + ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok-apply")); + } + else if (QIcon::hasThemeIcon("dialog-apply")) { + ui->cmdApply->setIcon(QIcon::fromTheme("dialog-apply")); + } + else if (QIcon::hasThemeIcon("gtk-apply")) { + ui->cmdApply->setIcon(QIcon::fromTheme("gtk-apply")); + } + else if (QIcon::hasThemeIcon("dialog-ok")) { + ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok")); + } + else if (QIcon::hasThemeIcon("gtk-ok")) { + ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok")); + } + + // Set Icon for Cancel Button + if (QIcon::hasThemeIcon("dialog-cancel")) { + ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel")); + } + else if (QIcon::hasThemeIcon("gtk-cancel")) { + ui->cmdCancel->setIcon(QIcon::fromTheme("gtk-cancel")); + } + + snapmaticTitle = QString(); + smpic = 0; + +#ifndef Q_OS_ANDROID + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); + resize(400 * screenRatio, 360 * screenRatio); +#endif +} + +SnapmaticEditor::~SnapmaticEditor() +{ + delete ui; +} + +void SnapmaticEditor::selfie_toggled(bool checked) +{ + isSelfie = checked; +} + + +void SnapmaticEditor::mugshot_toggled(bool checked) +{ + if (checked) { + isMugshot = true; + ui->cbDirector->setEnabled(false); + ui->cbDirector->setChecked(false); + } + else { + isMugshot = false; + ui->cbDirector->setEnabled(true); + } +} + +void SnapmaticEditor::editor_toggled(bool checked) +{ + if (checked) { + isEditor = true; + ui->cbDirector->setEnabled(false); + ui->cbDirector->setChecked(false); + } + else { + isEditor = false; + ui->cbDirector->setEnabled(true); + } +} + +void SnapmaticEditor::on_rbSelfie_toggled(bool checked) +{ + if (checked) { + mugshot_toggled(false); + editor_toggled(false); + selfie_toggled(true); + } +} + +void SnapmaticEditor::on_rbMugshot_toggled(bool checked) +{ + if (checked) { + selfie_toggled(false); + editor_toggled(false); + mugshot_toggled(true); + } +} + +void SnapmaticEditor::on_rbEditor_toggled(bool checked) +{ + if (checked) { + selfie_toggled(false); + mugshot_toggled(false); + editor_toggled(true); + } +} + +void SnapmaticEditor::on_rbCustom_toggled(bool checked) +{ + if (checked) { + selfie_toggled(false); + mugshot_toggled(false); + editor_toggled(false); + } +} + +void SnapmaticEditor::setSnapmaticPicture(SnapmaticPicture *picture) +{ + smpic = picture; + snapmaticProperties = smpic->getSnapmaticProperties(); + ui->rbCustom->setChecked(true); + crewID = snapmaticProperties.crewID; + isSelfie = snapmaticProperties.isSelfie; + isMugshot = snapmaticProperties.isMug; + isEditor = snapmaticProperties.isFromRSEditor; + playersList = snapmaticProperties.playersList; + ui->cbDirector->setChecked(snapmaticProperties.isFromDirector); + ui->cbMeme->setChecked(snapmaticProperties.isMeme); + if (isSelfie) { + ui->rbSelfie->setChecked(true); + } + else if (isMugshot) { + ui->rbMugshot->setChecked(true); + } + else if (isEditor) { + ui->rbEditor->setChecked(true); + } + else { + ui->rbCustom->setChecked(true); + } + setSnapmaticCrew(returnCrewName(crewID)); + setSnapmaticTitle(picture->getPictureTitle()); + setSnapmaticPlayers(insertPlayerNames(playersList)); +} + +void SnapmaticEditor::insertPlayerNames(QStringList *players) +{ + for (int i = 0; i < players->size(); ++i) { + players->replace(i, profileDB->getPlayerName(players->at(i))); + } +} + +QStringList SnapmaticEditor::insertPlayerNames(const QStringList &players) +{ + QStringList playersWI = players; + insertPlayerNames(&playersWI); + return playersWI; +} + +void SnapmaticEditor::setSnapmaticPlayers(const QStringList &players) +{ + QString editStr = QString("%1").arg(tr("Edit")); + QString playersStr; + if (players.length() != 1) { + playersStr = tr("Players: %1 (%2)", "Multiple Player are inserted here"); + } + else { + playersStr = tr("Player: %1 (%2)", "One Player is inserted here"); + } + if (players.length() != 0) { + ui->labPlayers->setText(playersStr.arg(players.join(", "), editStr)); + } + else { + ui->labPlayers->setText(playersStr.arg(QApplication::translate("PictureDialog", "No Players"), editStr)); + } +#ifndef Q_OS_ANDROID + ui->gbValues->resize(ui->gbValues->width(), ui->gbValues->heightForWidth(ui->gbValues->width())); + ui->frameWidget->resize(ui->gbValues->width(), ui->frameWidget->heightForWidth(ui->frameWidget->width())); + if (heightForWidth(width()) > height()) + resize(width(), heightForWidth(width())); +#endif +} + +void SnapmaticEditor::setSnapmaticTitle(const QString &title) +{ + if (title.length() > 39) { + snapmaticTitle = title.left(39); + } + else { + snapmaticTitle = title; + } + QString editStr = QString("%1").arg(tr("Edit")); + QString titleStr = tr("Title: %1 (%2)").arg(StringParser::escapeString(snapmaticTitle), editStr); + ui->labTitle->setText(titleStr); + if (SnapmaticPicture::verifyTitle(snapmaticTitle)) { + ui->labAppropriate->setText(tr("Appropriate: %1").arg(QString("%1").arg(tr("Yes", "Yes, should work fine")))); + } + else { + ui->labAppropriate->setText(tr("Appropriate: %1").arg(QString("%1").arg(tr("No", "No, could lead to issues")))); + } +#ifndef Q_OS_ANDROID + ui->gbValues->resize(ui->gbValues->width(), ui->gbValues->heightForWidth(ui->gbValues->width())); + ui->frameWidget->resize(ui->gbValues->width(), ui->frameWidget->heightForWidth(ui->frameWidget->width())); + if (heightForWidth(width()) > height()) + resize(width(), heightForWidth(width())); +#endif +} + +void SnapmaticEditor::setSnapmaticCrew(const QString &crew) +{ + QString editStr = QString("%1").arg(tr("Edit")); + QString crewStr = tr("Crew: %1 (%2)").arg(StringParser::escapeString(crew), editStr); + ui->labCrew->setText(crewStr); +#ifndef Q_OS_ANDROID + ui->gbValues->resize(ui->gbValues->width(), ui->gbValues->heightForWidth(ui->gbValues->width())); + ui->frameWidget->resize(ui->gbValues->width(), ui->frameWidget->heightForWidth(ui->frameWidget->width())); + if (heightForWidth(width()) > height()) + resize(width(), heightForWidth(width())); +#endif +} + +QString SnapmaticEditor::returnCrewName(int crewID_) +{ + return crewDB->getCrewName(crewID_); +} + +void SnapmaticEditor::on_cmdCancel_clicked() +{ + close(); +} + +void SnapmaticEditor::on_cmdApply_clicked() +{ + if (ui->cbQualify->isChecked()) { + qualifyAvatar(); + } + snapmaticProperties.crewID = crewID; + snapmaticProperties.isSelfie = isSelfie; + snapmaticProperties.isMug = isMugshot; + snapmaticProperties.isFromRSEditor = isEditor; + snapmaticProperties.isFromDirector = ui->cbDirector->isChecked(); + snapmaticProperties.isMeme = ui->cbMeme->isChecked(); + snapmaticProperties.playersList = playersList; + if (smpic) { + QString currentFilePath = smpic->getPictureFilePath(); + QString originalFilePath = smpic->getOriginalPictureFilePath(); + QString backupFileName = originalFilePath % ".bak"; + if (!QFile::exists(backupFileName)) { + QFile::copy(currentFilePath, backupFileName); + } + SnapmaticProperties fallbackProperties = smpic->getSnapmaticProperties(); + QString fallbackTitle = smpic->getPictureTitle(); + smpic->setSnapmaticProperties(snapmaticProperties); + smpic->setPictureTitle(snapmaticTitle); + if (!smpic->exportPicture(currentFilePath)) { + QMessageBox::warning(this, tr("Snapmatic Properties"), tr("Patching of Snapmatic Properties failed because of I/O Error")); + smpic->setSnapmaticProperties(fallbackProperties); + smpic->setPictureTitle(fallbackTitle); + } + else { + smpic->updateStrings(); + smpic->emitUpdate(); +#ifdef GTA5SYNC_TELEMETRY + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool(); + telemetrySettings.endGroup(); + if (pushUsageData && Telemetry->canPush()) { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "PropertyEdited"; + jsonObject["EditedSize"] = QString::number(smpic->getContentMaxLength()); +#if QT_VERSION >= 0x060000 + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } +#endif + } + } + close(); +} + +void SnapmaticEditor::qualifyAvatar() +{ + ui->rbSelfie->setChecked(true); + ui->cbDirector->setChecked(false); + ui->cbMeme->setChecked(false); + ui->cmdApply->setDefault(true); +} + +void SnapmaticEditor::on_cbQualify_toggled(bool checked) +{ + if (checked) { + ui->cbMeme->setEnabled(false); + ui->cbDirector->setEnabled(false); + ui->rbCustom->setEnabled(false); + ui->rbSelfie->setEnabled(false); + ui->rbEditor->setEnabled(false); + ui->rbMugshot->setEnabled(false); + } + else { + ui->cbMeme->setEnabled(true); + ui->rbCustom->setEnabled(true); + ui->rbSelfie->setEnabled(true); + ui->rbEditor->setEnabled(true); + ui->rbMugshot->setEnabled(true); + if (ui->rbSelfie->isChecked() || ui->rbCustom->isChecked()) { + ui->cbDirector->setEnabled(true); + } + } +} + +void SnapmaticEditor::on_labPlayers_linkActivated(const QString &link) +{ + if (link == "g5e://editplayers") { + PlayerListDialog *playerListDialog = new PlayerListDialog(playersList, profileDB, this); + connect(playerListDialog, SIGNAL(playerListUpdated(QStringList)), this, SLOT(playerListUpdated(QStringList))); + playerListDialog->setModal(true); + playerListDialog->show(); + playerListDialog->exec(); + delete playerListDialog; + } +} + +void SnapmaticEditor::on_labTitle_linkActivated(const QString &link) +{ + if (link == "g5e://edittitle") { + bool ok; + QString newTitle = QInputDialog::getText(this, tr("Snapmatic Title"), tr("New Snapmatic title:"), QLineEdit::Normal, snapmaticTitle, &ok, windowFlags()); + if (ok && !newTitle.isEmpty()) { + setSnapmaticTitle(newTitle); + } + } +} + +void SnapmaticEditor::on_labCrew_linkActivated(const QString &link) +{ + if (link == "g5e://editcrew") { + bool ok; + int indexNum = 0; + QStringList itemList; + QStringList crewList = crewDB->getCrews(); + if (!crewList.contains(QLatin1String("0"))) { + crewList += QLatin1String("0"); + } + crewList.sort(); + for (const QString &crew : crewList) { + itemList += QString("%1 (%2)").arg(crew, returnCrewName(crew.toInt())); + } + if (crewList.contains(QString::number(crewID))) { + indexNum = crewList.indexOf(QString::number(crewID)); + } + QString newCrew = QInputDialog::getItem(this, tr("Snapmatic Crew"), tr("New Snapmatic crew:"), itemList, indexNum, true, &ok, windowFlags()); + if (ok && !newCrew.isEmpty()) { + if (newCrew.contains(" ")) + newCrew = newCrew.split(" ").at(0); + if (newCrew.length() > 10) + return; + for (const QChar &crewChar : qAsConst(newCrew)) { + if (!crewChar.isNumber()) { + return; + } + } + if (!crewList.contains(newCrew)) { + crewDB->addCrew(crewID); + } + crewID = newCrew.toInt(); + setSnapmaticCrew(returnCrewName(crewID)); + } + } +} + +void SnapmaticEditor::playerListUpdated(QStringList playerList) +{ + playersList = playerList; + setSnapmaticPlayers(insertPlayerNames(playerList)); +} diff --git a/SnapmaticEditor.h b/SnapmaticEditor.h index d2f41ee..98ed2d6 100644 --- a/SnapmaticEditor.h +++ b/SnapmaticEditor.h @@ -1,69 +1,77 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef SNAPMATICEDITOR_H -#define SNAPMATICEDITOR_H - -#include -#include "CrewDatabase.h" -#include "SnapmaticPicture.h" - -namespace Ui { -class SnapmaticEditor; -} - -class SnapmaticEditor : public QDialog -{ - Q_OBJECT - -public: - explicit SnapmaticEditor(CrewDatabase *crewDB, QWidget *parent = 0); - void setSnapmaticPicture(SnapmaticPicture *picture); - void setSnapmaticTitle(const QString &title); - void setSnapmaticCrew(const QString &crew = ""); - QString returnCrewName(int crewID); - ~SnapmaticEditor(); - -private slots: - void on_rbSelfie_toggled(bool checked); - void on_rbMugshot_toggled(bool checked); - void on_rbEditor_toggled(bool checked); - void on_rbCustom_toggled(bool checked); - void on_cmdCancel_clicked(); - void on_cmdApply_clicked(); - void on_cbQualify_toggled(bool checked); - void on_labTitle_linkActivated(const QString &link); - void on_labCrew_linkActivated(const QString &link); - -private: - CrewDatabase *crewDB; - Ui::SnapmaticEditor *ui; - SnapmaticProperties localSpJson; - SnapmaticPicture *smpic; - QString snapmaticTitle; - int crewID; - bool isSelfie; - bool isMugshot; - bool isEditor; - void selfie_toggled(bool checked); - void mugshot_toggled(bool checked); - void editor_toggled(bool checked); - void qualifyAvatar(); -}; - -#endif // SNAPMATICEDITOR_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef SNAPMATICEDITOR_H +#define SNAPMATICEDITOR_H + +#include +#include "CrewDatabase.h" +#include "ProfileDatabase.h" +#include "SnapmaticPicture.h" + +namespace Ui { +class SnapmaticEditor; +} + +class SnapmaticEditor : public QDialog +{ + Q_OBJECT + +public: + explicit SnapmaticEditor(CrewDatabase *crewDB, ProfileDatabase *profileDB, QWidget *parent = 0); + void setSnapmaticPicture(SnapmaticPicture *picture); + void setSnapmaticPlayers(const QStringList &players); + void setSnapmaticTitle(const QString &title); + void setSnapmaticCrew(const QString &crew = ""); + QString returnCrewName(int crewID); + ~SnapmaticEditor(); + +private slots: + void on_rbSelfie_toggled(bool checked); + void on_rbMugshot_toggled(bool checked); + void on_rbEditor_toggled(bool checked); + void on_rbCustom_toggled(bool checked); + void on_cmdCancel_clicked(); + void on_cmdApply_clicked(); + void on_cbQualify_toggled(bool checked); + void on_labPlayers_linkActivated(const QString &link); + void on_labTitle_linkActivated(const QString &link); + void on_labCrew_linkActivated(const QString &link); + void playerListUpdated(QStringList playerList); + +private: + CrewDatabase *crewDB; + ProfileDatabase *profileDB; + Ui::SnapmaticEditor *ui; + SnapmaticProperties snapmaticProperties; + SnapmaticPicture *smpic; + QStringList playersList; + QString snapmaticTitle; + int crewID; + bool isSelfie; + bool isMugshot; + bool isEditor; + void selfie_toggled(bool checked); + void mugshot_toggled(bool checked); + void editor_toggled(bool checked); + void qualifyAvatar(); + void insertPlayerNames(QStringList *players); + QStringList insertPlayerNames(const QStringList &players); +}; + +#endif // SNAPMATICEDITOR_H diff --git a/SnapmaticEditor.ui b/SnapmaticEditor.ui index 46ffa60..fc9ede9 100644 --- a/SnapmaticEditor.ui +++ b/SnapmaticEditor.ui @@ -1,248 +1,276 @@ - - - SnapmaticEditor - - - - 0 - 0 - 400 - 362 - - - - Snapmatic Properties - - - true - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Snapmatic Type - - - - - - Editor - - - - - - - Selfie - - - - - - - Regular - - - - - - - Mugshot - - - - - - - - - - Snapmatic Properties - - - - - - Meme - - - - - - - Director - - - - - - - - - - Snapmatic Values - - - - - - Qt::NoContextMenu - - - Crew: %1 (%2) - - - true - - - - - - - Qt::NoContextMenu - - - Title: %1 (%2) - - - true - - - - - - - Appropriate: %1 - - - true - - - - - - - - - - Extras - - - - - - Qualify as Avatar automatically at apply - - - - - - - - 0 - 0 - - - - Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture - - - true - - - - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - &Apply - - - - - - - - 0 - 0 - - - - &Cancel - - - - - - - - - - UiModLabel - QLabel -
UiModLabel.h
-
-
- - -
+ + + SnapmaticEditor + + + + 0 + 0 + 400 + 381 + + + + Snapmatic Properties + + + true + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Snapmatic Type + + + + + + Editor + + + + + + + Selfie + + + + + + + Regular + + + + + + + Mugshot + + + + + + + + + + Snapmatic Properties + + + + + + Meme + + + + + + + Director + + + + + + + + + + Snapmatic Values + + + + + + Qt::NoContextMenu + + + + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + + + + Qt::NoContextMenu + + + + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + + + + Qt::NoContextMenu + + + + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + + + + + + + true + + + + + + + + + + Extras + + + + + + Qualify as Avatar automatically at apply + + + + + + + + 0 + 0 + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + + + true + + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Apply changes + + + &Apply + + + + + + + + 0 + 0 + + + + Discard changes + + + &Cancel + + + + + + + + + + UiModLabel + QLabel +
UiModLabel.h
+
+
+ + +
diff --git a/SnapmaticPicture.cpp b/SnapmaticPicture.cpp old mode 100755 new mode 100644 index 1ded9ad..c3664fd --- a/SnapmaticPicture.cpp +++ b/SnapmaticPicture.cpp @@ -1,972 +1,836 @@ -/***************************************************************************** -* gta5sync-spv Grand Theft Auto Snapmatic Picture Viewer -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "SnapmaticPicture.h" -#include "StringParser.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// PARSER ALLOCATIONS -#define snapmaticHeaderLength 278 -#define snapmaticUsefulLength 260 -#define snapmaticFileMaxSize 528192 -#define jpegHeaderLineDifStr 2 -#define jpegPreHeaderLength 14 -#define jpegPicStreamLength 524288 -#define jsonStreamLength 3076 -#define tideStreamLength 260 - -// EDITOR ALLOCATIONS -#define jpegStreamEditorBegin 292 -#define jsonStreamEditorBegin 524588 -#define jsonStreamEditorLength 3072 -#define titlStreamEditorBegin 527668 -#define titlStreamEditorLength 256 -#define titlStreamCharacterMax 39 - -// IMAGES VALUES -#define snapmaticResolutionW 960 -#define snapmaticResolutionH 536 -#define snapmaticResolution QSize(snapmaticResolutionW, snapmaticResolutionH) - -SnapmaticPicture::SnapmaticPicture(const QString &fileName, QObject *parent) : QObject(parent), picFilePath(fileName) -{ - reset(); -} - -SnapmaticPicture::~SnapmaticPicture() -{ -} - -void SnapmaticPicture::reset() -{ - // INIT PIC - rawPicContent = ""; - cachePicture = QImage(); - jpegRawContentSizeE = 0; - jpegRawContentSize = 0; - picExportFileName = ""; - isCustomFormat = 0; - isLoadedInRAM = 0; - pictureHead = ""; - pictureStr = ""; - lowRamMode = 0; - lastStep = ""; - sortStr = ""; - titlStr = ""; - descStr = ""; - picOk = 0; - - // INIT JSON - jsonOk = 0; - jsonStr = ""; - - // SNAPMATIC PROPERTIES - localSpJson = {}; -} - -bool SnapmaticPicture::preloadFile() -{ - QFile *picFile = new QFile(picFilePath); - picFileName = QFileInfo(picFilePath).fileName(); - - if (!picFile->open(QFile::ReadOnly)) - { - lastStep = "1;/1,OpenFile," % StringParser::convertDrawStringForLog(picFilePath); - delete picFile; - return false; - } - if (picFilePath.right(4) != QLatin1String(".g5e")) - { - rawPicContent = picFile->read(snapmaticFileMaxSize); - picFile->close(); - delete picFile; - - // Setting is values - isCustomFormat = false; - isLoadedInRAM = true; - } - else - { - QByteArray g5eContent = picFile->read(snapmaticFileMaxSize + 1024); - picFile->close(); - delete picFile; - - // Set Custom Format - isCustomFormat = true; - - // Reading g5e Content - g5eContent.remove(0, 1); - if (g5eContent.left(3) == QByteArray("G5E")) - { - g5eContent.remove(0, 3); - if (g5eContent.left(2).toHex() == QByteArray("1000")) - { - g5eContent.remove(0, 2); - if (g5eContent.left(3) == QByteArray("LEN")) - { - g5eContent.remove(0, 3); - int fileNameLength = g5eContent.left(1).toHex().toInt(); - g5eContent.remove(0, 1); - if (g5eContent.left(3) == QByteArray("FIL")) - { - g5eContent.remove(0, 3); - picFileName = g5eContent.left(fileNameLength); - g5eContent.remove(0, fileNameLength); - if (g5eContent.left(3) == QByteArray("COM")) - { - g5eContent.remove(0, 3); - rawPicContent = qUncompress(g5eContent); - - // Setting is values - isLoadedInRAM = true; - } - else - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",4,G5E_FORMATERROR"; - return false; - } - } - else - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",3,G5E_FORMATERROR"; - return false; - } - } - else - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",2,G5E_FORMATERROR"; - return false; - } - } - else - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",1,G5E_NOTCOMPATIBLE"; - return false; - } - } - else - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",1,G5E_FORMATERROR"; - return false; - } - } - emit preloaded(); - return true; -} - -bool SnapmaticPicture::readingPicture(bool writeEnabled_, bool cacheEnabled_, bool fastLoad, bool lowRamMode_) -{ - // Start opening file - // lastStep is like currentStep - - // Set boolean values - writeEnabled = writeEnabled_; - cacheEnabled = cacheEnabled_; - lowRamMode = lowRamMode_; - if (!writeEnabled) { lowRamMode = false; } // Low RAM Mode only works when writeEnabled is true - - QIODevice *picStream; - - if (!isLoadedInRAM) { preloadFile(); } - - picStream = new QBuffer(&rawPicContent); - picStream->open(QIODevice::ReadWrite); - - // Reading Snapmatic Header - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",1,NOHEADER"; - picStream->close(); - delete picStream; - return false; - } - QByteArray snapmaticHeaderLine = picStream->read(snapmaticHeaderLength); - pictureHead = getSnapmaticHeaderString(snapmaticHeaderLine); - - // Reading JPEG Header Line - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",2,NOHEADER"; - picStream->close(); - delete picStream; - return false; - } - QByteArray jpegHeaderLine = picStream->read(jpegPreHeaderLength); - - // Checking for JPEG - jpegHeaderLine.remove(0, jpegHeaderLineDifStr); - if (jpegHeaderLine.left(4) != QByteArray("JPEG")) - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",2,NOJPEG"; - picStream->close(); - delete picStream; - return false; - } - - // Read JPEG Stream - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",2,NOPIC"; - picStream->close(); - delete picStream; - return false; - } - QByteArray jpegRawContent = picStream->read(jpegPicStreamLength); - if (jpegRawContent.contains("\xFF\xD9")) - { - int jpegRawContentSizeT = jpegRawContent.indexOf("\xFF\xD9") + 2; - jpegRawContentSizeE = jpegRawContentSizeT; - jpegRawContentSize = jpegRawContentSizeT; - if (jpegRawContent.contains("\xFF\x45\x4F\x49")) - { - jpegRawContentSizeT = jpegRawContent.indexOf("\xFF\x45\x4F\x49"); - } - jpegRawContent = jpegRawContent.left(jpegRawContentSize); - jpegRawContentSize = jpegRawContentSizeT; - } - if (cacheEnabled) picOk = cachePicture.loadFromData(jpegRawContent, "JPEG"); - if (!cacheEnabled) - { - QImage tempPicture; - picOk = tempPicture.loadFromData(jpegRawContent, "JPEG"); - } - else if (!fastLoad) - { - QImage tempPicture = QImage(snapmaticResolution, QImage::Format_RGB888); - QPainter tempPainter(&tempPicture); - if (cachePicture.size() == snapmaticResolution) - { - tempPainter.drawImage(0, 0, cachePicture); - } - else - { - tempPainter.drawImage(0, 0, cachePicture.scaled(snapmaticResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - } - tempPainter.end(); - cachePicture = tempPicture; - } - - // Read JSON Stream - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",3,NOJSON"; - picStream->close(); - delete picStream; - return false; - } - else if (picStream->read(4) != QByteArray("JSON")) - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",3,CTJSON"; - picStream->close(); - delete picStream; - return false; - } - QByteArray jsonRawContent = picStream->read(jsonStreamLength); - jsonStr = getSnapmaticJSONString(jsonRawContent); - parseJsonContent(); // JSON parsing is own function - - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",4,NOTITL"; - picStream->close(); - delete picStream; - return false; - } - else if (picStream->read(4) != QByteArray("TITL")) - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",4,CTTITL"; - picStream->close(); - delete picStream; - return false; - } - QByteArray titlRawContent = picStream->read(tideStreamLength); - titlStr = getSnapmaticTIDEString(titlRawContent); - - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",5,NODESC"; - picStream->close(); - delete picStream; - return picOk; - } - else if (picStream->read(4) != QByteArray("DESC")) - { - lastStep = "2;/3,ReadingFile," % StringParser::convertDrawStringForLog(picFilePath) % ",5,CTDESC"; - picStream->close(); - delete picStream; - return false; - } - QByteArray descRawContent = picStream->read(tideStreamLength); - descStr = getSnapmaticTIDEString(descRawContent); - - updateStrings(); - - picStream->close(); - delete picStream; - - if (!writeEnabled) { rawPicContent.clear(); } - else if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } - - emit loaded(); - return picOk; -} - -QString SnapmaticPicture::getSnapmaticHeaderString(const QByteArray &snapmaticHeader) -{ - QByteArray snapmaticBytes = snapmaticHeader.left(snapmaticUsefulLength); - QList snapmaticBytesList = snapmaticBytes.split('\x01'); - snapmaticBytes = snapmaticBytesList.at(1); - snapmaticBytesList.clear(); - return StringParser::parseTitleString(snapmaticBytes, snapmaticBytes.length()); -} - -QString SnapmaticPicture::getSnapmaticJSONString(const QByteArray &jsonBytes) -{ - QByteArray jsonUsefulBytes = jsonBytes; - jsonUsefulBytes.replace('\x00', ""); - jsonUsefulBytes.replace('\x0c', ""); - return QString::fromUtf8(jsonUsefulBytes).trimmed(); -} - -QString SnapmaticPicture::getSnapmaticTIDEString(const QByteArray &tideBytes) -{ - QByteArray tideUsefulBytes = tideBytes; - tideUsefulBytes.remove(0,4); - QList tideUsefulBytesList = tideUsefulBytes.split('\x00'); - return QString::fromUtf8(tideUsefulBytesList.at(0)).trimmed(); -} - -void SnapmaticPicture::updateStrings() -{ - QString cmpPicTitl = titlStr; - cmpPicTitl.replace('\"', "''"); - cmpPicTitl.replace(' ', '_'); - cmpPicTitl.replace(':', '-'); - cmpPicTitl.remove('\\'); - cmpPicTitl.remove('{'); - cmpPicTitl.remove('}'); - cmpPicTitl.remove('/'); - cmpPicTitl.remove('<'); - cmpPicTitl.remove('>'); - cmpPicTitl.remove('*'); - cmpPicTitl.remove('?'); - cmpPicTitl.remove('.'); - pictureStr = tr("PHOTO - %1").arg(localSpJson.createdDateTime.toString("MM/dd/yy HH:mm:ss")); - sortStr = localSpJson.createdDateTime.toString("yyMMddHHmmss") % QString::number(localSpJson.uid); - picExportFileName = sortStr % "_" % cmpPicTitl; -} - -bool SnapmaticPicture::readingPictureFromFile(const QString &fileName, bool writeEnabled_, bool cacheEnabled_, bool fastLoad, bool lowRamMode_) -{ - if (!fileName.isEmpty()) - { - picFilePath = fileName; - return readingPicture(writeEnabled_, cacheEnabled_, fastLoad, lowRamMode_); - } - else - { - return false; - } -} - -bool SnapmaticPicture::setImage(const QImage &picture) // dirty method -{ - if (writeEnabled) - { - QByteArray picByteArray; - int comLvl = 100; - bool saveSuccess = false; - while (comLvl != 0 && !saveSuccess) - { - QByteArray picByteArrayT; - QBuffer picStreamT(&picByteArrayT); - picStreamT.open(QIODevice::WriteOnly); - saveSuccess = picture.save(&picStreamT, "JPEG", comLvl); - picStreamT.close(); - if (saveSuccess) - { - if (picByteArrayT.length() > jpegRawContentSize) - { - comLvl--; - saveSuccess = false; - } - else - { - picByteArray = picByteArrayT; - } - } - } - if (saveSuccess) return setPictureStream(picByteArray); - } - return false; -} - -bool SnapmaticPicture::setPictureStream(const QByteArray &picByteArray_) // clean method -{ - if (writeEnabled) - { - bool customEOI = false; - QByteArray picByteArray = picByteArray_; - if (lowRamMode) { rawPicContent = qUncompress(rawPicContent); } - QBuffer snapmaticStream(&rawPicContent); - snapmaticStream.open(QIODevice::ReadWrite); - if (!snapmaticStream.seek(jpegStreamEditorBegin)) return false; - if (picByteArray.length() > jpegPicStreamLength) return false; - if (picByteArray.length() < jpegRawContentSize && jpegRawContentSize + 4 < jpegPicStreamLength) - { - customEOI = true; - } - while (picByteArray.length() != jpegPicStreamLength) - { - picByteArray += '\x00'; - } - if (customEOI) - { - picByteArray.replace(jpegRawContentSize, 4, "\xFF\x45\x4F\x49"); - } - int result = snapmaticStream.write(picByteArray); - snapmaticStream.close(); - if (result != 0) - { - if (cacheEnabled) - { - QImage replacedPicture; - replacedPicture.loadFromData(picByteArray); - cachePicture = replacedPicture; - } - if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } - return true; - } - if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } - return false; - } - return false; -} - -bool SnapmaticPicture::setPictureTitl(const QString &newTitle_) -{ - if (writeEnabled) - { - QString newTitle = newTitle_; - if (lowRamMode) { rawPicContent = qUncompress(rawPicContent); } - QBuffer snapmaticStream(&rawPicContent); - snapmaticStream.open(QIODevice::ReadWrite); - if (!snapmaticStream.seek(titlStreamEditorBegin)) return false; - if (newTitle.length() > titlStreamCharacterMax) - { - newTitle = newTitle.left(titlStreamCharacterMax); - } - QByteArray newTitleArray = newTitle.toUtf8(); - while (newTitleArray.length() != titlStreamEditorLength) - { - newTitleArray += '\x00'; - } - int result = snapmaticStream.write(newTitleArray); - snapmaticStream.close(); - if (result != 0) - { - titlStr = newTitle; - if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } - return true; - } - if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } - return false; - } - return false; -} - -QString SnapmaticPicture::getExportPictureFileName() -{ - return picExportFileName; -} - -QString SnapmaticPicture::getPictureFileName() -{ - return picFileName; -} - -QString SnapmaticPicture::getPictureFilePath() -{ - return picFilePath; -} - -QString SnapmaticPicture::getPictureSortStr() -{ - return sortStr; -} - -QString SnapmaticPicture::getPictureDesc() -{ - return descStr; -} - -QString SnapmaticPicture::getPictureTitl() -{ - return titlStr; -} - -QString SnapmaticPicture::getPictureHead() -{ - return pictureHead; -} - -QString SnapmaticPicture::getPictureStr() -{ - return pictureStr; -} - -QString SnapmaticPicture::getLastStep() -{ - return lastStep; -} - -QImage SnapmaticPicture::getImage() -{ - if (cacheEnabled) - { - return cachePicture; - } - else if (writeEnabled) - { - bool returnOk = 0; - QImage tempPicture; - QImage returnPicture(snapmaticResolution, QImage::Format_RGB888); - - if (lowRamMode) { rawPicContent = qUncompress(rawPicContent); } - QBuffer snapmaticStream(&rawPicContent); - snapmaticStream.open(QIODevice::ReadOnly); - if (snapmaticStream.seek(jpegStreamEditorBegin)) - { - QByteArray jpegRawContent = snapmaticStream.read(jpegPicStreamLength); - returnOk = tempPicture.loadFromData(jpegRawContent, "JPEG"); - } - snapmaticStream.close(); - if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } - - if (returnOk) - { - QPainter returnPainter(&returnPicture); - if (tempPicture.size() == snapmaticResolution) - { - returnPainter.drawImage(0, 0, tempPicture); - } - else - { - returnPainter.drawImage(0, 0, tempPicture.scaled(snapmaticResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - } - returnPainter.end(); - return returnPicture; - } - } - else - { - bool returnOk = 0; - QImage returnPicture; - QIODevice *picStream; - - QFile *picFile = new QFile(picFilePath); - if (!picFile->open(QFile::ReadOnly)) - { - lastStep = "1;/1,OpenFile," % StringParser::convertDrawStringForLog(picFilePath); - delete picFile; - return QImage(0, 0, QImage::Format_RGB888); - } - rawPicContent = picFile->read(snapmaticFileMaxSize); - picFile->close(); - delete picFile; - - picStream = new QBuffer(&rawPicContent); - picStream->open(QIODevice::ReadWrite); - if (picStream->seek(jpegStreamEditorBegin)) - { - QByteArray jpegRawContent = picStream->read(jpegPicStreamLength); - returnOk = returnPicture.loadFromData(jpegRawContent, "JPEG"); - } - picStream->close(); - delete picStream; - - if (returnOk) - { - return returnPicture; - } - } - return QImage(0, 0, QImage::Format_RGB888); -} - -int SnapmaticPicture::getContentMaxLength() -{ - return jpegRawContentSize; -} - -bool SnapmaticPicture::isPicOk() -{ - return picOk; -} - -void SnapmaticPicture::clearCache() -{ - cacheEnabled = false; - cachePicture = QImage(); -} - -void SnapmaticPicture::emitUpdate() -{ - emit updated(); -} - -// JSON part - -bool SnapmaticPicture::isJsonOk() -{ - return jsonOk; -} - -QString SnapmaticPicture::getJsonStr() -{ - return jsonStr; -} - -SnapmaticProperties SnapmaticPicture::getSnapmaticProperties() -{ - return localSpJson; -} - -void SnapmaticPicture::parseJsonContent() -{ - QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonStr.toUtf8()); - QJsonObject jsonObject = jsonDocument.object(); - QVariantMap jsonMap = jsonObject.toVariantMap(); // backward compatibility - - if (jsonObject.contains("loc")) - { - QJsonObject locObject = jsonObject["loc"].toObject(); - if (locObject.contains("x")) { localSpJson.location.x = locObject["x"].toDouble(); } - if (locObject.contains("y")) { localSpJson.location.y = locObject["y"].toDouble(); } - if (locObject.contains("z")) { localSpJson.location.z = locObject["z"].toDouble(); } - } - if (jsonObject.contains("uid")) - { - localSpJson.uid = jsonObject["uid"].toInt(); - } - if (jsonObject.contains("area")) - { - localSpJson.location.area = jsonObject["area"].toString(); - } - if (jsonObject.contains("crewid")) - { - localSpJson.crewID = jsonObject["crewid"].toInt(); - } - if (jsonObject.contains("creat")) - { - QDateTime createdTimestamp; - localSpJson.createdTimestamp = jsonMap["creat"].toUInt(); - createdTimestamp.setTime_t(localSpJson.createdTimestamp); - localSpJson.createdDateTime = createdTimestamp; - } - if (jsonObject.contains("plyrs")) - { - localSpJson.playersList = jsonMap["plyrs"].toStringList(); - } - if (jsonObject.contains("meme")) - { - localSpJson.isMeme = jsonObject["meme"].toBool(); - } - if (jsonObject.contains("mug")) - { - localSpJson.isMug = jsonObject["mug"].toBool(); - } - if (jsonObject.contains("slf")) - { - localSpJson.isSelfie = jsonObject["slf"].toBool(); - } - if (jsonObject.contains("drctr")) - { - localSpJson.isFromDirector = jsonObject["drctr"].toBool(); - } - if (jsonObject.contains("rsedtr")) - { - localSpJson.isFromRSEditor = jsonObject["rsedtr"].toBool(); - } - - jsonOk = true; -} - -bool SnapmaticPicture::setSnapmaticProperties(SnapmaticProperties newSpJson) -{ - QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonStr.toUtf8()); - QJsonObject jsonObject = jsonDocument.object(); - - QJsonObject locObject; - locObject["x"] = newSpJson.location.x; - locObject["y"] = newSpJson.location.y; - locObject["z"] = newSpJson.location.z; - - jsonObject["loc"] = locObject; - jsonObject["uid"] = newSpJson.uid; - jsonObject["area"] = newSpJson.location.area; - jsonObject["crewid"] = newSpJson.crewID; - jsonObject["creat"] = QJsonValue::fromVariant(newSpJson.createdTimestamp); - jsonObject["plyrs"] = QJsonValue::fromVariant(newSpJson.playersList); - jsonObject["meme"] = newSpJson.isMeme; - jsonObject["mug"] = newSpJson.isMug; - jsonObject["slf"] = newSpJson.isSelfie; - jsonObject["drctr"] = newSpJson.isFromDirector; - jsonObject["rsedtr"] = newSpJson.isFromRSEditor; - - jsonDocument.setObject(jsonObject); - - QString newJsonStr = QString::fromUtf8(jsonDocument.toJson(QJsonDocument::Compact)); - if (newJsonStr.length() < jsonStreamEditorLength) - { - if (writeEnabled) - { - QByteArray jsonByteArray = newJsonStr.toUtf8(); - while (jsonByteArray.length() != jsonStreamEditorLength) - { - jsonByteArray += '\x00'; - } - if (lowRamMode) { rawPicContent = qUncompress(rawPicContent); } - QBuffer snapmaticStream(&rawPicContent); - snapmaticStream.open(QIODevice::ReadWrite); - if (!snapmaticStream.seek(jsonStreamEditorBegin)) - { - snapmaticStream.close(); - return false; - } - int result = snapmaticStream.write(jsonByteArray); - snapmaticStream.close(); - if (result != 0) - { - localSpJson = newSpJson; - jsonStr = newJsonStr; - if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } - return true; - } - else - { - if (lowRamMode) { rawPicContent = qCompress(rawPicContent, 9); } - return false; - } - } - else - { - return false; - } - } - else - { - return false; - } - - return true; -} - -// FILE MANAGEMENT - -bool SnapmaticPicture::exportPicture(const QString &fileName, const QString format) -{ - QFile *picFile = new QFile(fileName); - if (picFile->open(QIODevice::WriteOnly)) - { - if (format == QLatin1String("G5E")) - { - // Modern compressed export - QByteArray stockFileNameUTF8 = picFileName.toUtf8(); - QByteArray numberLength = QByteArray::number(stockFileNameUTF8.length()); - if (numberLength.length() == 1) - { - numberLength.insert(0, "0"); - } - else if (numberLength.length() != 2) - { - numberLength = "00"; - } - QByteArray g5eHeader; - g5eHeader.reserve(stockFileNameUTF8.length() + 16); - g5eHeader += '\x00'; // First Null Byte - g5eHeader += QByteArray("G5E"); // GTA 5 Export - g5eHeader += '\x10'; g5eHeader += '\x00'; // 2 byte GTA 5 Export Version - g5eHeader += QByteArray("LEN"); // Before Length - g5eHeader += QByteArray::fromHex(numberLength); // Length in HEX before Compressed - g5eHeader += QByteArray("FIL"); // Before File Name - g5eHeader += stockFileNameUTF8; // File Name - g5eHeader += QByteArray("COM"); // Before Compressed - picFile->write(g5eHeader); - if (!lowRamMode) - { - picFile->write(qCompress(rawPicContent, 9)); // Compressed Snapmatic - } - else - { - picFile->write(rawPicContent); - } - picFile->close(); - delete picFile; - } - else if (format == QLatin1String("JPG")) - { - // JPEG export - QBuffer snapmaticStream(&rawPicContent); - snapmaticStream.open(QIODevice::ReadOnly); - if (snapmaticStream.seek(jpegStreamEditorBegin)) - { - QByteArray jpegRawContent = snapmaticStream.read(jpegPicStreamLength); - if (jpegRawContentSizeE != 0) - { - jpegRawContent = jpegRawContent.left(jpegRawContentSizeE); - } - picFile->write(jpegRawContent); - } - picFile->close(); - delete picFile; - } - else - { - // Classic straight export - if (!lowRamMode) - { - picFile->write(rawPicContent); - } - else - { - picFile->write(qUncompress(rawPicContent)); - } - picFile->close(); - delete picFile; - } - return true; - } - else - { - delete picFile; - return false; - } -} - -void SnapmaticPicture::setPicFileName(const QString &picFileName_) -{ - picFileName = picFileName_; -} - -void SnapmaticPicture::setPicFilePath(const QString &picFilePath_) -{ - picFilePath = picFilePath_; -} - -bool SnapmaticPicture::deletePicFile() -{ - if (!QFile::exists(picFilePath)) return true; - if (QFile::remove(picFilePath)) return true; - return false; -} - -// VISIBILITY - -bool SnapmaticPicture::isHidden() -{ - if (picFilePath.right(7) == QLatin1String(".hidden")) - { - return true; - } - return false; -} - -bool SnapmaticPicture::setPictureHidden() -{ - if (isCustomFormat) - { - return false; - } - if (!isHidden()) - { - QString newPicFilePath = QString(picFilePath % ".hidden"); - if (QFile::rename(picFilePath, newPicFilePath)) - { - picFilePath = newPicFilePath; - return true; - } - return false; - } - return true; -} - -bool SnapmaticPicture::setPictureVisible() -{ - if (isCustomFormat) - { - return false; - } - if (isHidden()) - { - QString newPicFilePath = QString(picFilePath).remove(picFilePath.length() - 7, 7); - if (QFile::rename(picFilePath, newPicFilePath)) - { - picFilePath = newPicFilePath; - return true; - } - return false; - } - return true; -} - -// PREDEFINED PROPERTIES - -QSize SnapmaticPicture::getSnapmaticResolution() -{ - return snapmaticResolution; -} - -// VERIFY CONTENT - -bool SnapmaticPicture::verifyTitle(const QString &title) -{ - // VERIFY TITLE FOR BE A VALID SNAPMATIC TITLE - if (title.length() <= titlStreamCharacterMax) - { - foreach(const QChar &titleChar, title) - { - if (!verifyTitleChar(titleChar)) return false; - } - return true; - } - return false; -} - -bool SnapmaticPicture::verifyTitleChar(const QChar &titleChar) -{ - // VERIFY CHAR FOR BE A VALID SNAPMATIC CHARACTER - if (titleChar.isLetterOrNumber() || titleChar.isPrint()) - { - if (titleChar == '<' || titleChar == '>' || titleChar == '\\') return false; - return true; - } - return false; -} +/***************************************************************************** +* gta5spv Grand Theft Auto Snapmatic Picture Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "SnapmaticPicture.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION < 0x060000 +#include +#else +#include +#endif + +#if QT_VERSION >= 0x050000 +#include +#else +#include "StandardPaths.h" +#endif + +// IMAGES VALUES +#define snapmaticResolutionW 960 +#define snapmaticResolutionH 536 +#define snapmaticResolution QSize(snapmaticResolutionW, snapmaticResolutionH) + +SnapmaticPicture::SnapmaticPicture(const QString &fileName, QObject *parent) : QObject(parent), picFilePath(fileName) +{ + reset(); +} + +SnapmaticPicture::~SnapmaticPicture() +{ +} + +void SnapmaticPicture::reset() +{ + // INIT PIC + p_ragePhoto.clear(); + cachePicture = QImage(); + picExportFileName = QString(); + pictureStr = QString(); + lastStep = QString(); + sortStr = QString(); + + // INIT PIC BOOLS + isFormatSwitch = false; + picOk = false; + + // INIT JSON + jsonOk = false; + + // SNAPMATIC PROPERTIES + localProperties = {}; +} + +bool SnapmaticPicture::preloadFile() +{ + QFile *picFile = new QFile(picFilePath); + picFileName = QFileInfo(picFilePath).fileName(); + + isFormatSwitch = false; + + if (!picFile->open(QFile::ReadOnly)) { + lastStep = "1;/1,OpenFile," % convertDrawStringForLog(picFilePath); + delete picFile; + return false; + } + + p_ragePhoto.setIODevice(picFile); + bool ok = p_ragePhoto.load(); + picFile->close(); + delete picFile; + if (!ok) + return false; + + if (picFilePath.right(4) != QLatin1String(".g5e")) { + if (p_ragePhoto.photoFormat() == RagePhoto::PhotoFormat::G5EX) + isFormatSwitch = true; + } + emit preloaded(); + return ok; +} + +bool SnapmaticPicture::readingPicture(bool cacheEnabled_) +{ + // Start opening file + // lastStep is like currentStep + + // Set boolean values + cacheEnabled = cacheEnabled_; + + bool ok = true; + if (!p_ragePhoto.isLoaded()) + ok = preloadFile(); + + if (!ok) + return false; + + if (cacheEnabled) + picOk = cachePicture.loadFromData(p_ragePhoto.photoData(), "JPEG"); + if (!cacheEnabled) { + QImage tempPicture; + picOk = tempPicture.loadFromData(p_ragePhoto.photoData(), "JPEG"); + } + + parseJsonContent(); // JSON parsing is own function + updateStrings(); + + emit loaded(); + return picOk; +} + +void SnapmaticPicture::updateStrings() +{ + QString cmpPicTitl = p_ragePhoto.title(); + cmpPicTitl.replace('\"', "''"); + cmpPicTitl.replace(' ', '_'); + cmpPicTitl.replace(':', '-'); + cmpPicTitl.remove('\\'); + cmpPicTitl.remove('{'); + cmpPicTitl.remove('}'); + cmpPicTitl.remove('/'); + cmpPicTitl.remove('<'); + cmpPicTitl.remove('>'); + cmpPicTitl.remove('*'); + cmpPicTitl.remove('?'); + cmpPicTitl.remove('.'); + pictureStr = tr("PHOTO - %1").arg(localProperties.createdDateTime.toString("MM/dd/yy HH:mm:ss")); + sortStr = localProperties.createdDateTime.toString("yyMMddHHmmss") % QString::number(localProperties.uid); + QString exportStr = localProperties.createdDateTime.toString("yyyyMMdd") % "-" % QString::number(localProperties.uid); + if (getSnapmaticFormat() == SnapmaticFormat::G5E_Format) + picFileName = "PGTA5" % QString::number(localProperties.uid); + picExportFileName = exportStr % "_" % cmpPicTitl; +} + +bool SnapmaticPicture::readingPictureFromFile(const QString &fileName, bool cacheEnabled_) +{ + if (!fileName.isEmpty()) { + picFilePath = fileName; + return readingPicture(cacheEnabled_); + } + else { + return false; + } +} + +bool SnapmaticPicture::setImage(const QImage &picture, bool eXtendMode) +{ +#ifdef GTA5SYNC_DYNAMIC_PHOTOBUFFER + quint32 jpegPicStreamLength = p_ragePhoto.photoBuffer(); +#else + quint32 jpegPicStreamLength = 524288U; +#endif + QByteArray picByteArray; + int comLvl = 100; + bool saveSuccess = false; + while (comLvl != 0 && !saveSuccess) { + QByteArray picByteArrayT; + QBuffer picStreamT(&picByteArrayT); + picStreamT.open(QIODevice::WriteOnly); + saveSuccess = picture.save(&picStreamT, "JPEG", comLvl); + picStreamT.close(); + if (saveSuccess) { + quint32 size = picByteArrayT.length(); + if (size > jpegPicStreamLength) { + if (!eXtendMode) { + comLvl--; + saveSuccess = false; + } + else { + p_ragePhoto.setPhotoBuffer(size, true); + picByteArray = picByteArrayT; + } + } + else { +#ifndef GTA5SYNC_DYNAMIC_PHOTOBUFFER + if (p_ragePhoto.photoBuffer() != jpegPicStreamLength) + p_ragePhoto.setPhotoData(QByteArray()); // avoid buffer set fail + p_ragePhoto.setPhotoBuffer(jpegPicStreamLength, true); +#endif + picByteArray = picByteArrayT; + } + } + } + if (saveSuccess) + return setPictureStream(picByteArray); + return false; +} + +bool SnapmaticPicture::setPictureStream(const QByteArray &streamArray) // clean method +{ + bool success = p_ragePhoto.setPhotoData(streamArray); + if (success) { + if (cacheEnabled) { + QImage replacedPicture; + replacedPicture.loadFromData(streamArray); + cachePicture = replacedPicture; + } + return true; + } + else { + return false; + } +} + +bool SnapmaticPicture::setPictureTitl(const QString &newTitle_) +{ + QString newTitle = newTitle_; + if (newTitle.length() > 39) { + newTitle = newTitle.left(39); + } + p_ragePhoto.setTitle(newTitle); + return true; +} + +int SnapmaticPicture::getContentMaxLength() +{ + return p_ragePhoto.photoBuffer(); +} + +QString SnapmaticPicture::getExportPictureFileName() +{ + return picExportFileName; +} + +QString SnapmaticPicture::getOriginalPictureFileName() +{ + QString newPicFileName = picFileName; + if (picFileName.right(4) == ".bak") { + newPicFileName = QString(picFileName).remove(picFileName.length() - 4, 4); + } + if (picFileName.right(7) == ".hidden") { + newPicFileName = QString(picFileName).remove(picFileName.length() - 7, 7); + } + return newPicFileName; +} + +QString SnapmaticPicture::getOriginalPictureFilePath() +{ + QString newPicFilePath = picFilePath; + if (picFilePath.right(4) == ".bak") { + newPicFilePath = QString(picFilePath).remove(picFilePath.length() - 4, 4); + } + if (picFilePath.right(7) == ".hidden") { + newPicFilePath = QString(picFilePath).remove(picFilePath.length() - 7, 7); + } + return newPicFilePath; +} + +QString SnapmaticPicture::getPictureFileName() +{ + return picFileName; +} + +QString SnapmaticPicture::getPictureFilePath() +{ + return picFilePath; +} + +QString SnapmaticPicture::getPictureSortStr() +{ + return sortStr; +} + +QString SnapmaticPicture::getPictureTitl() +{ + return p_ragePhoto.title(); +} + +QString SnapmaticPicture::getPictureStr() +{ + return pictureStr; +} + +QString SnapmaticPicture::getLastStep(bool readable) +{ + if (readable) { + QStringList lastStepList = lastStep.split(";/"); + if (lastStepList.length() < 2) + return lastStep; + bool intOk; + QStringList descStepList = lastStepList.at(1).split(","); + if (descStepList.length() < 1) + return lastStep; + int argsCount = descStepList.at(0).toInt(&intOk); + if (!intOk) { return lastStep; } + if (argsCount == 1) { + QString currentAction = descStepList.at(1); + QString actionFile = descStepList.at(2); + if (currentAction == "OpenFile") { + return tr("open file %1").arg(actionFile); + } + } + else if (argsCount == 3 || argsCount == 4) { + QString currentAction = descStepList.at(1); + QString actionFile = descStepList.at(2); + QString actionError = descStepList.at(4); + QString actionError2; + if (argsCount == 4) { actionError2 = descStepList.at(5); } + if (currentAction == "ReadingFile") { + QString readableError = actionError; + if (actionError == "NOHEADER") { + readableError = tr("header not exists"); + } + else if (actionError == "MALFORMEDHEADER") { + readableError = tr("header is malformed"); + } + else if (actionError == "NOJPEG" || actionError == "NOPIC") { + readableError = tr("picture not exists (%1)").arg(actionError); + } + else if (actionError == "NOJSON" || actionError == "CTJSON") { + readableError = tr("JSON not exists (%1)").arg(actionError); + } + else if (actionError == "NOTITL" || actionError == "CTTITL") { + readableError = tr("title not exists (%1)").arg(actionError); + } + else if (actionError == "NODESC" || actionError == "CTDESC") { + readableError = tr("description not exists (%1)").arg(actionError); + } + else if (actionError == "JSONINCOMPLETE" && actionError2 == "JSONERROR") { + readableError = tr("JSON is incomplete and malformed"); + } + else if (actionError == "JSONINCOMPLETE") { + readableError = tr("JSON is incomplete"); + } + else if (actionError == "JSONERROR") { + readableError = tr("JSON is malformed"); + } + return tr("reading file %1 because of %2", "Example for %2: JSON is malformed error").arg(actionFile, readableError); + } + else { + return lastStep; + } + } + else { + return lastStep; + } + } + return lastStep; + +} + +QImage SnapmaticPicture::getImage() +{ + if (cacheEnabled) { + return cachePicture; + } + else { + return QImage::fromData(p_ragePhoto.photoData(), "JPEG"); + } + return QImage(); +} + +QByteArray SnapmaticPicture::getPictureStream() +{ + return p_ragePhoto.photoData(); +} + +bool SnapmaticPicture::isPicOk() +{ + return picOk; +} + +void SnapmaticPicture::clearCache() +{ + cacheEnabled = false; + cachePicture = QImage(); +} + +void SnapmaticPicture::emitUpdate() +{ + emit updated(); +} + +void SnapmaticPicture::emitCustomSignal(const QString &signal) +{ + emit customSignal(signal); +} + +// JSON part + +bool SnapmaticPicture::isJsonOk() +{ + return jsonOk; +} + +QString SnapmaticPicture::getJsonStr() +{ + return QString::fromUtf8(p_ragePhoto.jsonData()); +} + +SnapmaticProperties SnapmaticPicture::getSnapmaticProperties() +{ + return localProperties; +} + +void SnapmaticPicture::parseJsonContent() +{ + QJsonObject jsonObject = p_ragePhoto.jsonObject(); + QVariantMap jsonMap = jsonObject.toVariantMap(); + + bool jsonIncomplete = false; + bool jsonError = false; + if (jsonObject.contains("loc")) { + if (jsonObject["loc"].isObject()) { + QJsonObject locObject = jsonObject["loc"].toObject(); + if (locObject.contains("x")) { + if (locObject["x"].isDouble()) { localProperties.location.x = locObject["x"].toDouble(); } + else { jsonError = true; } + } + else { jsonIncomplete = true; } + if (locObject.contains("y")) { + if (locObject["y"].isDouble()) { localProperties.location.y = locObject["y"].toDouble(); } + else { jsonError = true; } + } + else { jsonIncomplete = true; } + if (locObject.contains("z")) { + if (locObject["z"].isDouble()) { localProperties.location.z = locObject["z"].toDouble(); } + else { jsonError = true; } + } + else { jsonIncomplete = true; } + } + else { jsonError = true; } + } + else { jsonIncomplete = true; } + if (jsonObject.contains("uid")) { + bool uidOk; + localProperties.uid = jsonMap["uid"].toInt(&uidOk); + if (!uidOk) { jsonError = true; } + } + else { jsonIncomplete = true; } + if (jsonObject.contains("area")) { + if (jsonObject["area"].isString()) { localProperties.location.area = jsonObject["area"].toString(); } + else { jsonError = true; } + } + else { jsonIncomplete = true; } + if (jsonObject.contains("crewid")) { + bool crewIDOk; + localProperties.crewID = jsonMap["crewid"].toInt(&crewIDOk); + if (!crewIDOk) { jsonError = true; } + } + else { jsonIncomplete = true; } + if (jsonObject.contains("street")) { + bool streetIDOk; + localProperties.streetID = jsonMap["street"].toInt(&streetIDOk); + if (!streetIDOk) { jsonError = true; } + } + else { jsonIncomplete = true; } + if (jsonObject.contains("creat")) { + bool timestampOk; + QDateTime createdTimestamp; + localProperties.createdTimestamp = jsonMap["creat"].toUInt(×tampOk); +#if QT_VERSION >= 0x060000 + createdTimestamp.setSecsSinceEpoch(localProperties.createdTimestamp); +#else + createdTimestamp.setTime_t(localProperties.createdTimestamp); +#endif + localProperties.createdDateTime = createdTimestamp; + if (!timestampOk) { jsonError = true; } + } + else { jsonIncomplete = true; } + if (jsonObject.contains("plyrs")) { + if (jsonObject["plyrs"].isArray()) { localProperties.playersList = jsonMap["plyrs"].toStringList(); } + else { jsonError = true; } + } + // else { jsonIncomplete = true; } // 2016 Snapmatic pictures left out plyrs when none are captured, so don't force exists on that one + if (jsonObject.contains("meme")) { + if (jsonObject["meme"].isBool()) { localProperties.isMeme = jsonObject["meme"].toBool(); } + else { jsonError = true; } + } + else { jsonIncomplete = true; } + if (jsonObject.contains("mug")) { + if (jsonObject["mug"].isBool()) { localProperties.isMug = jsonObject["mug"].toBool(); } + else { jsonError = true; } + } + else { jsonIncomplete = true; } + if (jsonObject.contains("slf")) { + if (jsonObject["slf"].isBool()) { localProperties.isSelfie = jsonObject["slf"].toBool(); } + else { jsonError = true; } + } + else { jsonIncomplete = true; } + if (jsonObject.contains("drctr")) { + if (jsonObject["drctr"].isBool()) { localProperties.isFromDirector = jsonObject["drctr"].toBool(); } + else { jsonError = true; } + } + else { jsonIncomplete = true; } + if (jsonObject.contains("rsedtr")) { + if (jsonObject["rsedtr"].isBool()) { localProperties.isFromRSEditor = jsonObject["rsedtr"].toBool(); } + else { jsonError = true; } + } + else { localProperties.isFromRSEditor = false; } + if (jsonObject.contains("onislandx")) { + if (jsonObject["onislandx"].isBool()) { localProperties.location.isCayoPerico = jsonObject["onislandx"].toBool(); } + else { jsonError = true; } + } + else { localProperties.location.isCayoPerico = false; } + + if (!jsonIncomplete && !jsonError) { + jsonOk = true; + } + else { + if (jsonIncomplete && jsonError) { + lastStep = "2;/4,ReadingFile," % convertDrawStringForLog(picFilePath) % ",3,JSONINCOMPLETE,JSONERROR"; + } + else if (jsonIncomplete) { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",3,JSONINCOMPLETE"; + } + else if (jsonError) { + lastStep = "2;/3,ReadingFile," % convertDrawStringForLog(picFilePath) % ",3,JSONERROR"; + } + jsonOk = false; + } +} + +bool SnapmaticPicture::setSnapmaticProperties(SnapmaticProperties properties) +{ + QJsonObject jsonObject = p_ragePhoto.jsonObject(); + + QJsonObject locObject; + locObject["x"] = properties.location.x; + locObject["y"] = properties.location.y; + locObject["z"] = properties.location.z; + + jsonObject["loc"] = locObject; + jsonObject["uid"] = properties.uid; + jsonObject["area"] = properties.location.area; + jsonObject["crewid"] = properties.crewID; + jsonObject["street"] = properties.streetID; + jsonObject["creat"] = QJsonValue::fromVariant(properties.createdTimestamp); + jsonObject["plyrs"] = QJsonValue::fromVariant(properties.playersList); + jsonObject["meme"] = properties.isMeme; + jsonObject["mug"] = properties.isMug; + jsonObject["slf"] = properties.isSelfie; + jsonObject["drctr"] = properties.isFromDirector; + jsonObject["rsedtr"] = properties.isFromRSEditor; + jsonObject["onislandx"] = properties.location.isCayoPerico; + + QJsonDocument jsonDocument(jsonObject); + if (setJsonStr(QString::fromUtf8(jsonDocument.toJson(QJsonDocument::Compact)))) { + localProperties = properties; + return true; + } + return false; +} + +bool SnapmaticPicture::setJsonStr(const QString &newJsonStr, bool updateProperties) +{ + if (p_ragePhoto.setJsonData(newJsonStr.toUtf8())) { + if (updateProperties) + parseJsonContent(); + return true; + } + else { + return false; + } +} + +// FILE MANAGEMENT + +bool SnapmaticPicture::exportPicture(const QString &fileName, SnapmaticFormat format_) +{ + // Keep current format when Auto_Format is used + SnapmaticFormat format = format_; + if (format_ == SnapmaticFormat::Auto_Format) { + if (p_ragePhoto.photoFormat() == RagePhoto::PhotoFormat::G5EX) { + format = SnapmaticFormat::G5E_Format; + } + else { + format = SnapmaticFormat::PGTA_Format; + } + } + + bool saveSuccess = false; +#if QT_VERSION >= 0x050000 + QSaveFile *picFile = new QSaveFile(fileName); +#else + QFile *picFile = new QFile(StandardPaths::tempLocation() % "/" % QFileInfo(fileName).fileName() % ".tmp"); +#endif + if (picFile->open(QIODevice::WriteOnly)) { + if (format == SnapmaticFormat::G5E_Format) { + p_ragePhoto.save(picFile, RagePhoto::PhotoFormat::G5EX); +#if QT_VERSION >= 0x050000 + saveSuccess = picFile->commit(); +#else + saveSuccess = true; + picFile->close(); +#endif + delete picFile; + } + else if (format == SnapmaticFormat::JPEG_Format) { + picFile->write(p_ragePhoto.photoData()); +#if QT_VERSION >= 0x050000 + saveSuccess = picFile->commit(); +#else + saveSuccess = true; + picFile->close(); +#endif + delete picFile; + } + else { + p_ragePhoto.save(picFile, RagePhoto::PhotoFormat::GTA5); +#if QT_VERSION >= 0x050000 + saveSuccess = picFile->commit(); +#else + saveSuccess = true; + picFile->close(); +#endif + delete picFile; + } +#if QT_VERSION <= 0x050000 + if (saveSuccess) { + bool tempBakCreated = false; + if (QFile::exists(fileName)) { + if (!QFile::rename(fileName, fileName % ".tmp")) { + QFile::remove(StandardPaths::tempLocation() % "/" % QFileInfo(fileName).fileName() % ".tmp"); + return false; + } + tempBakCreated = true; + } + if (!QFile::rename(StandardPaths::tempLocation() % "/" % QFileInfo(fileName).fileName() % ".tmp", fileName)) { + QFile::remove(StandardPaths::tempLocation() % "/" % QFileInfo(fileName).fileName() % ".tmp"); + if (tempBakCreated) + QFile::rename(fileName % ".tmp", fileName); + return false; + } + if (tempBakCreated) + QFile::remove(fileName % ".tmp"); + } +#endif + return saveSuccess; + } + else { + delete picFile; + return saveSuccess; + } +} + +void SnapmaticPicture::setPicFileName(const QString &picFileName_) +{ + picFileName = picFileName_; +} + +void SnapmaticPicture::setPicFilePath(const QString &picFilePath_) +{ + picFilePath = picFilePath_; +} + +bool SnapmaticPicture::deletePicFile() +{ + bool success = false; + if (!QFile::exists(picFilePath)) { + success = true; + } + else if (QFile::remove(picFilePath)) { + success = true; + } + if (isHidden()) { + const QString picBakPath = QString(picFilePath).remove(picFilePath.length() - 7, 7) % ".bak"; + if (QFile::exists(picBakPath)) QFile::remove(picBakPath); + } + else { + const QString picBakPath = picFilePath % ".bak"; + if (QFile::exists(picBakPath)) QFile::remove(picBakPath); + } + return success; +} + +// VISIBILITY + +bool SnapmaticPicture::isHidden() +{ + if (picFilePath.right(7) == QLatin1String(".hidden")) { + return true; + } + return false; +} + +bool SnapmaticPicture::isVisible() +{ + if (picFilePath.right(7) == QLatin1String(".hidden")) { + return false; + } + return true; +} + +bool SnapmaticPicture::setPictureHidden() +{ + if (p_ragePhoto.photoFormat() == RagePhoto::PhotoFormat::G5EX) { + return false; + } + if (!isHidden()) { + QString newPicFilePath = QString(picFilePath % ".hidden"); + if (QFile::rename(picFilePath, newPicFilePath)) { + picFilePath = newPicFilePath; + return true; + } + return false; + } + return true; +} + +bool SnapmaticPicture::setPictureVisible() +{ + if (p_ragePhoto.photoFormat() == RagePhoto::PhotoFormat::G5EX) { + return false; + } + if (isHidden()) { + QString newPicFilePath = QString(picFilePath).remove(picFilePath.length() - 7, 7); + if (QFile::rename(picFilePath, newPicFilePath)) { + picFilePath = newPicFilePath; + return true; + } + return false; + } + return true; +} + +// PREDEFINED PROPERTIES + +QSize SnapmaticPicture::getSnapmaticResolution() +{ + return snapmaticResolution; +} + +// SNAPMATIC FORMAT + +SnapmaticFormat SnapmaticPicture::getSnapmaticFormat() +{ + if (p_ragePhoto.photoFormat() == RagePhoto::PhotoFormat::G5EX) { + return SnapmaticFormat::G5E_Format; + } + return SnapmaticFormat::PGTA_Format; +} + +void SnapmaticPicture::setSnapmaticFormat(SnapmaticFormat format) +{ + if (format == SnapmaticFormat::G5E_Format) { + p_ragePhoto.setPhotoFormat(RagePhoto::PhotoFormat::G5EX); + return; + } + else if (format == SnapmaticFormat::PGTA_Format) { + p_ragePhoto.setPhotoFormat(RagePhoto::PhotoFormat::GTA5); + return; + } + qDebug() << "setSnapmaticFormat: Invalid SnapmaticFormat defined, valid SnapmaticFormats are G5E_Format and PGTA_Format"; +} + +bool SnapmaticPicture::isFormatSwitched() +{ + return isFormatSwitch; +} + +// VERIFY CONTENT + +bool SnapmaticPicture::verifyTitle(const QString &title) +{ + // VERIFY TITLE FOR BE A VALID SNAPMATIC TITLE + if (title.length() <= 39 && title.length() > 0) { + for (const QChar &titleChar : title) { + if (!verifyTitleChar(titleChar)) return false; + } + return true; + } + return false; +} + +bool SnapmaticPicture::verifyTitleChar(const QChar &titleChar) +{ + // VERIFY CHAR FOR BE A VALID SNAPMATIC CHARACTER + if (titleChar.isLetterOrNumber() || titleChar.isPrint()) { + if (titleChar == '<' || titleChar == '>' || titleChar == '\\') return false; + return true; + } + return false; +} + +// STRING OPERATIONS + +QString SnapmaticPicture::parseTitleString(const QByteArray &commitBytes, int maxLength) +{ + Q_UNUSED(maxLength) +#if QT_VERSION >= 0x060000 + QStringDecoder strDecoder = QStringDecoder(QStringDecoder::Utf16LE); + QString retStr = strDecoder(commitBytes); + retStr = retStr.trimmed(); +#else + QString retStr = QTextCodec::codecForName("UTF-16LE")->toUnicode(commitBytes).trimmed(); +#endif + retStr.remove(QChar('\x00')); + return retStr; +} + +QString SnapmaticPicture::convertDrawStringForLog(const QString &inputStr) +{ + QString outputStr = inputStr; + return outputStr.replace("&","&u;").replace(",", "&c;"); +} + +QString SnapmaticPicture::convertLogStringForDraw(const QString &inputStr) +{ + QString outputStr = inputStr; + return outputStr.replace("&c;",",").replace("&u;", "&"); +} + +// RAGEPHOTO + +RagePhoto* SnapmaticPicture::ragePhoto() +{ + return &p_ragePhoto; +} diff --git a/SnapmaticPicture.h b/SnapmaticPicture.h old mode 100755 new mode 100644 index ab0ba00..8113cdc --- a/SnapmaticPicture.h +++ b/SnapmaticPicture.h @@ -1,150 +1,172 @@ -/***************************************************************************** -* gta5sync-spv Grand Theft Auto Snapmatic Picture Viewer -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef SNAPMATICPICTURE_H -#define SNAPMATICPICTURE_H - -#include -#include -#include -#include -#include -#include - -struct SnapmaticProperties { - struct SnapmaticLocation { - QString area; - double x; - double y; - double z; - }; - int uid; - int crewID; - QStringList playersList; - uint createdTimestamp; - QDateTime createdDateTime; - bool isMeme; - bool isMug; - bool isSelfie; - bool isFromDirector; - bool isFromRSEditor; - SnapmaticLocation location; -}; - -class SnapmaticPicture : public QObject -{ - Q_OBJECT -public: - explicit SnapmaticPicture(const QString &fileName = "", QObject *parent = 0); - ~SnapmaticPicture(); - void reset(); - bool preloadFile(); - bool readingPictureFromFile(const QString &fileName, bool writeEnabled = true, bool cacheEnabled = false, bool fastLoad = true, bool lowRamMode = false); - bool readingPicture(bool writeEnabled = true, bool cacheEnabled = false, bool fastLoad = true, bool lowRamMode = false); - bool isPicOk(); - void clearCache(); - QImage getImage(); - QString getLastStep(); - QString getPictureStr(); - QString getPictureHead(); - QString getPictureTitl(); - QString getPictureDesc(); - QString getPictureSortStr(); - QString getPictureFileName(); - QString getPictureFilePath(); - QString getExportPictureFileName(); - int getContentMaxLength(); - bool setImage(const QImage &picture); - bool setPictureTitl(const QString &newTitle); - bool setPictureStream(const QByteArray &picByteArray); - void updateStrings(); - void emitUpdate(); - - // FILE MANAGEMENT - bool exportPicture(const QString &fileName, const QString format = "PGTA"); - void setPicFileName(const QString &picFileName); - void setPicFilePath(const QString &picFilePath); - bool deletePicFile(); - - // ALTERNATIVES - QString getPictureTitle() { return getPictureTitl(); } - QString getPictureString() { return getPictureStr(); } - QString getPictureDescription() { return getPictureDesc(); } - bool setPictureTitle(const QString &newTitle) { return setPictureTitl(newTitle); } - - // JSON - bool isJsonOk(); - QString getJsonStr(); - SnapmaticProperties getSnapmaticProperties(); - bool setSnapmaticProperties(SnapmaticProperties newSpJson); - - // VISIBILITY - bool isHidden(); - bool setPictureHidden(); - bool setPictureVisible(); - - // PREDEFINED PROPERTIES - QSize getSnapmaticResolution(); - - // VERIFY CONTENT - static bool verifyTitle(const QString &title); - -private: - QString getSnapmaticHeaderString(const QByteArray &snapmaticHeader); - QString getSnapmaticJSONString(const QByteArray &jsonBytes); - QString getSnapmaticTIDEString(const QByteArray &tideBytes); - QImage cachePicture; - QString picExportFileName; - QString picFileName; - QString picFilePath; - QString pictureHead; - QString pictureStr; - QString lastStep; - QString sortStr; - QString titlStr; - QString descStr; - bool picOk; - bool lowRamMode; - bool writeEnabled; - bool cacheEnabled; - bool isLoadedInRAM; - bool isCustomFormat; - int jpegRawContentSize; - int jpegRawContentSizeE; - - // PICTURE STREAM - QByteArray rawPicContent; - - // JSON - void parseJsonContent(); - bool jsonOk; - QString jsonStr; - SnapmaticProperties localSpJson; - - // VERIFY CONTENT - static bool verifyTitleChar(const QChar &titleChar); - -signals: - void preloaded(); - void updated(); - void loaded(); - -public slots: -}; - -#endif // SNAPMATICPICTURE_H +/***************************************************************************** +* gta5spv Grand Theft Auto Snapmatic Picture Viewer +* Copyright (C) 2016-2020 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef SNAPMATICPICTURE_H +#define SNAPMATICPICTURE_H + +#include "RagePhoto.h" +#include +#include +#include +#include +#include +#include + +enum class SnapmaticFormat : int { Auto_Format = 0, PGTA_Format = 1, JPEG_Format = 2, G5E_Format = 3 }; + +struct SnapmaticProperties { + struct SnapmaticLocation { + QString area; + double x; + double y; + double z; + bool isCayoPerico; + }; + int uid; + int crewID; + int streetID; + QStringList playersList; + uint createdTimestamp; + QDateTime createdDateTime; + bool isMeme; + bool isMug; + bool isSelfie; + bool isFromDirector; + bool isFromRSEditor; + SnapmaticLocation location; +}; + +class SnapmaticPicture : public QObject +{ + Q_OBJECT +public: + explicit SnapmaticPicture(const QString &fileName = "", QObject *parent = 0); + ~SnapmaticPicture(); + void reset(); + bool preloadFile(); + bool readingPictureFromFile(const QString &fileName, bool cacheEnabled = false); + bool readingPicture(bool cacheEnabled = false); + bool isPicOk(); // Please use isPictureOk instead + void clearCache(); + QImage getImage(); + QByteArray getPictureStream(); + QString getLastStep(bool readable = true); + QString getPictureStr(); + QString getPictureTitl(); + QString getPictureSortStr(); + QString getPictureFileName(); + QString getPictureFilePath(); + QString getExportPictureFileName(); + QString getOriginalPictureFileName(); + QString getOriginalPictureFilePath(); + int getContentMaxLength(); + bool setImage(const QImage &picture, bool eXtendMode = false); + bool setPictureTitl(const QString &newTitle); // Please use setPictureTitle instead + bool setPictureStream(const QByteArray &streamArray); + void updateStrings(); + void emitUpdate(); + void emitCustomSignal(const QString &signal); + + // FILE MANAGEMENT + bool exportPicture(const QString &fileName, SnapmaticFormat format = SnapmaticFormat::Auto_Format); + void setPicFileName(const QString &picFileName); // Please use setPictureFileName instead + void setPicFilePath(const QString &picFilePath); // Please use setPictureFilePath instead + bool deletePicFile(); // Please use deletePictureFile instead + + // JSON + bool isJsonOk(); + QString getJsonStr(); // Please use getPictureJson instead + SnapmaticProperties getSnapmaticProperties(); + bool setSnapmaticProperties(SnapmaticProperties properties); + bool setJsonStr(const QString &jsonStr, bool updateProperties = false); // Please use setPictureJson instead + + // VISIBILITY + bool isHidden(); // Please use isPictureHidden instead + bool isVisible(); // Please use isPictureVisible instead + bool setPictureHidden(); + bool setPictureVisible(); + + // ALTERNATIVES (MORE DEVELOPER FRIENDLY FUNCTION CALLS) + QString getJsonString() { return getJsonStr(); } // Please use getPictureJson instead + QString getPictureJson() { return getJsonStr(); } + QString getPictureTitle() { return getPictureTitl(); } + QString getPictureString() { return getPictureStr(); } + bool setJsonString(const QString &jsonString, bool updateProperties = false) { return setJsonStr(jsonString, updateProperties); } // Please use setPictureJson instead + bool setPictureJson(const QString &json, bool updateProperties = false) { return setJsonStr(json, updateProperties); } + bool setPictureTitle(const QString &title) { return setPictureTitl(title); } + void setPictureFileName(const QString &fileName) { return setPicFileName(fileName); } + void setPictureFilePath(const QString &filePath) { return setPicFilePath(filePath); } + bool deletePictureFile() { return deletePicFile(); } + bool isPictureOk() { return isPicOk(); } + bool isPictureHidden() { return isHidden(); } + bool isPictureVisible() { return isVisible(); } + bool setHidden() { return setPictureHidden(); } // Please use setPictureHidden instead + bool setVisible() { return setPictureVisible(); } // Please use setPictureVisible instead + + // PREDEFINED PROPERTIES + static QSize getSnapmaticResolution(); + + // SNAPMATIC FORMAT + SnapmaticFormat getSnapmaticFormat(); + void setSnapmaticFormat(SnapmaticFormat format); + bool isFormatSwitched(); + + // VERIFY CONTENT + static bool verifyTitle(const QString &title); + + // STRING OPERATIONS + static QString parseTitleString(const QByteArray &commitBytes, int maxLength); + static QString convertDrawStringForLog(const QString &inputStr); + static QString convertLogStringForDraw(const QString &inputStr); + + // RAGEPHOTO + RagePhoto* ragePhoto(); + +private: + QImage cachePicture; + QString picExportFileName; + QString picFileName; + QString picFilePath; + QString pictureStr; + QString lastStep; + QString sortStr; + bool picOk; + bool cacheEnabled; + bool isFormatSwitch; + + // JSON + void parseJsonContent(); + bool jsonOk; + SnapmaticProperties localProperties; + + // VERIFY CONTENT + static bool verifyTitleChar(const QChar &titleChar); + + // RAGEPHOTO + RagePhoto p_ragePhoto; + +signals: + void customSignal(QString signal); + void preloaded(); + void updated(); + void loaded(); + +public slots: +}; + +#endif // SNAPMATICPICTURE_H diff --git a/SnapmaticWidget.cpp b/SnapmaticWidget.cpp old mode 100755 new mode 100644 index ddb9b73..db718a7 --- a/SnapmaticWidget.cpp +++ b/SnapmaticWidget.cpp @@ -1,345 +1,495 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "SnapmaticWidget.h" -#include "ui_SnapmaticWidget.h" -#include "SnapmaticPicture.h" -#include "SnapmaticEditor.h" -#include "DatabaseThread.h" -#include "PictureDialog.h" -#include "PictureExport.h" -#include "StringParser.h" -#include "AppEnv.h" -#include "config.h" -#include -#include -#include -#include -#include -#include - -SnapmaticWidget::SnapmaticWidget(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent) : - ProfileWidget(parent), profileDB(profileDB), crewDB(crewDB), threadDB(threadDB), - ui(new Ui::SnapmaticWidget) -{ - ui->setupUi(this); - ui->cmdView->setVisible(false); - ui->cmdCopy->setVisible(false); - ui->cmdExport->setVisible(false); - ui->cmdDelete->setVisible(false); - ui->cbSelected->setVisible(false); - - QPalette palette; - highlightBackColor = palette.highlight().color(); - highlightTextColor = palette.highlightedText().color(); - palette.setCurrentColorGroup(QPalette::Disabled); - highlightHiddenColor = palette.text().color(); - - picPath = ""; - picStr = ""; - smpic = 0; - - installEventFilter(this); -} - -SnapmaticWidget::~SnapmaticWidget() -{ - delete ui; -} - -bool SnapmaticWidget::eventFilter(QObject *obj, QEvent *ev) -{ - if (obj == this) - { - if (ev->type() == QEvent::Enter) - { - setStyleSheet(QString("QFrame#SnapmaticFrame{background-color: rgb(%1, %2, %3)}QLabel#labPicStr{color: rgb(%4, %5, %6)}").arg(QString::number(highlightBackColor.red()), QString::number(highlightBackColor.green()), QString::number(highlightBackColor.blue()), QString::number(highlightTextColor.red()), QString::number(highlightTextColor.green()), QString::number(highlightTextColor.blue()))); - return true; - } - else if(ev->type() == QEvent::Leave) - { - setStyleSheet(""); - return true; - } - } - return false; -} - -void SnapmaticWidget::setSnapmaticPicture(SnapmaticPicture *picture) -{ - smpic = picture; - picPath = picture->getPictureFilePath(); - picTitl = picture->getPictureTitl(); - picStr = picture->getPictureStr(); - QObject::connect(picture, SIGNAL(updated()), this, SLOT(snapmaticUpdated())); - - qreal screenRatio = AppEnv::screenRatio(); - ui->labPicture->setFixedSize(48 * screenRatio, 27 * screenRatio); - - QPixmap SnapmaticPixmap = QPixmap::fromImage(picture->getImage().scaled(ui->labPicture->width(), ui->labPicture->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::AutoColor); - ui->labPicStr->setText(picStr + "\n" + picTitl + ""); - ui->labPicture->setPixmap(SnapmaticPixmap); - - picture->clearCache(); - - adjustTextColor(); -} - -void SnapmaticWidget::snapmaticUpdated() -{ - // Current only strings get updated - picPath = smpic->getPictureFilePath(); - picTitl = smpic->getPictureTitl(); - picStr = smpic->getPictureStr(); - ui->labPicStr->setText(picStr + "\n" + picTitl + ""); -} - -void SnapmaticWidget::on_cmdView_clicked() -{ - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - settings.beginGroup("Interface"); - bool navigationBar = settings.value("NavigationBar", false).toBool(); - settings.endGroup(); - - PictureDialog *picDialog = new PictureDialog(profileDB, crewDB, this); - picDialog->setSnapmaticPicture(smpic, true); - picDialog->setModal(true); - - // be ready for playerName updated - QObject::connect(threadDB, SIGNAL(playerNameUpdated()), picDialog, SLOT(playerNameUpdated())); - QObject::connect(picDialog, SIGNAL(nextPictureRequested()), this, SLOT(dialogNextPictureRequested())); - QObject::connect(picDialog, SIGNAL(previousPictureRequested()), this, SLOT(dialogPreviousPictureRequested())); - - // add previous next buttons - if (navigationBar) picDialog->addPreviousNextButtons(); - - // show picture dialog -#ifdef Q_OS_ANDROID - // Android ... - picDialog->showMaximized(); -#else - picDialog->show(); - if (navigationBar) picDialog->stylizeDialog(); - //picDialog->adaptNewDialogSize(); - picDialog->setMinimumSize(picDialog->size()); - picDialog->setMaximumSize(picDialog->size()); -#endif - picDialog->exec(); - delete picDialog; -} - -void SnapmaticWidget::on_cmdCopy_clicked() -{ - PictureExport::exportAsSnapmatic(this, smpic); -} - -void SnapmaticWidget::on_cmdExport_clicked() -{ - PictureExport::exportAsPicture(this, smpic); -} - -void SnapmaticWidget::on_cmdDelete_clicked() -{ - if (deletePicture()) emit pictureDeleted(); -} - -bool SnapmaticWidget::deletePicture() -{ - int uchoice = QMessageBox::question(this, tr("Delete picture"), tr("Are you sure to delete %1 from your Snapmatic pictures?").arg("\""+picStr+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if (uchoice == QMessageBox::Yes) - { - if (smpic->deletePicFile()) - { - return true; - } - else - { - QMessageBox::warning(this, tr("Delete picture"), tr("Failed at deleting %1 from your Snapmatic pictures").arg("\""+picStr+"\"")); - } - } - return false; -} - -void SnapmaticWidget::mousePressEvent(QMouseEvent *ev) -{ - ProfileWidget::mousePressEvent(ev); -} - -void SnapmaticWidget::mouseReleaseEvent(QMouseEvent *ev) -{ - ProfileWidget::mouseReleaseEvent(ev); - if (ui->cbSelected->isVisible()) - { - if (rect().contains(ev->pos()) && ev->button() == Qt::LeftButton) - { - ui->cbSelected->setChecked(!ui->cbSelected->isChecked()); - } - } - else - { - if (getContentMode() == 0 && rect().contains(ev->pos()) && ev->button() == Qt::LeftButton) - { - on_cmdView_clicked(); - } - } -} - -void SnapmaticWidget::mouseDoubleClickEvent(QMouseEvent *ev) -{ - ProfileWidget::mouseDoubleClickEvent(ev); - - if (!ui->cbSelected->isVisible() && getContentMode() == 1 && ev->button() == Qt::LeftButton) - { - on_cmdView_clicked(); - } -} - -void SnapmaticWidget::setSelected(bool isSelected) -{ - ui->cbSelected->setChecked(isSelected); -} - -void SnapmaticWidget::pictureSelected() -{ - setSelected(!ui->cbSelected->isChecked()); -} - -void SnapmaticWidget::contextMenuEvent(QContextMenuEvent *ev) -{ - emit contextMenuTriggered(ev); -} - -void SnapmaticWidget::dialogNextPictureRequested() -{ - emit nextPictureRequested((QWidget*)sender()); -} - -void SnapmaticWidget::dialogPreviousPictureRequested() -{ - emit previousPictureRequested((QWidget*)sender()); -} - -void SnapmaticWidget::on_cbSelected_stateChanged(int arg1) -{ - if (arg1 == Qt::Checked) - { - emit widgetSelected(); - } - else if (arg1 == Qt::Unchecked) - { - emit widgetDeselected(); - } -} - -void SnapmaticWidget::adjustTextColor() -{ - if (isHidden()) - { - ui->labPicStr->setStyleSheet(QString("QLabel{color: rgb(%1, %2, %3);}").arg(QString::number(highlightHiddenColor.red()), QString::number(highlightHiddenColor.green()), QString::number(highlightHiddenColor.blue()))); - } - else - { - ui->labPicStr->setStyleSheet(""); - } -} - -bool SnapmaticWidget::makePictureHidden() -{ - if (smpic->setPictureHidden()) - { - picPath = smpic->getPictureFilePath(); - adjustTextColor(); - return true; - } - return false; -} - -bool SnapmaticWidget::makePictureVisible() -{ - if (smpic->setPictureVisible()) - { - picPath = smpic->getPictureFilePath(); - adjustTextColor(); - return true; - } - return false; -} - -void SnapmaticWidget::makePictureHiddenSlot() -{ - makePictureHidden(); -} - -void SnapmaticWidget::makePictureVisibleSlot() -{ - makePictureVisible(); -} - -void SnapmaticWidget::editSnapmaticProperties() -{ - SnapmaticEditor *snapmaticEditor = new SnapmaticEditor(crewDB, this); - snapmaticEditor->setWindowFlags(snapmaticEditor->windowFlags()^Qt::WindowContextHelpButtonHint); - snapmaticEditor->setSnapmaticPicture(smpic); - snapmaticEditor->setModal(true); - snapmaticEditor->exec(); - delete snapmaticEditor; -} - -bool SnapmaticWidget::isSelected() -{ - return ui->cbSelected->isChecked(); -} - -bool SnapmaticWidget::isHidden() -{ - if (picPath.right(7) == ".hidden") - { - return true; - } - return false; -} - -void SnapmaticWidget::setSelectionMode(bool selectionMode) -{ - ui->cbSelected->setVisible(selectionMode); -} - -void SnapmaticWidget::selectAllWidgets() -{ - emit allWidgetsSelected(); -} - -void SnapmaticWidget::deselectAllWidgets() -{ - emit allWidgetsDeselected(); -} - -SnapmaticPicture* SnapmaticWidget::getPicture() -{ - return smpic; -} - -QString SnapmaticWidget::getPicturePath() -{ - return picPath; -} - -QString SnapmaticWidget::getWidgetType() -{ - return "SnapmaticWidget"; -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "SnapmaticWidget.h" +#include "ui_SnapmaticWidget.h" +#include "MapLocationDialog.h" +#include "JsonEditorDialog.h" +#include "SnapmaticPicture.h" +#include "SnapmaticEditor.h" +#include "DatabaseThread.h" +#include "PictureDialog.h" +#include "PictureExport.h" +#include "StringParser.h" +#include "ImportDialog.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include + +#ifdef GTA5SYNC_TELEMETRY +#include "TelemetryClass.h" +#include +#include +#endif + +SnapmaticWidget::SnapmaticWidget(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QString profileName, QWidget *parent) : + ProfileWidget(parent), profileDB(profileDB), crewDB(crewDB), threadDB(threadDB), profileName(profileName), + ui(new Ui::SnapmaticWidget) +{ + ui->setupUi(this); + ui->cmdView->setVisible(false); + ui->cmdCopy->setVisible(false); + ui->cmdExport->setVisible(false); + ui->cmdDelete->setVisible(false); + ui->cbSelected->setVisible(false); + + QPalette palette; + palette.setCurrentColorGroup(QPalette::Disabled); + highlightHiddenColor = palette.text().color(); + + ui->SnapmaticFrame->setMouseTracking(true); + ui->labPicture->setMouseTracking(true); + ui->labPicStr->setMouseTracking(true); + ui->cbSelected->setMouseTracking(true); + smpic = nullptr; +} + +SnapmaticWidget::~SnapmaticWidget() +{ + delete ui; +} + +void SnapmaticWidget::setSnapmaticPicture(SnapmaticPicture *picture) +{ + smpic = picture; + QObject::connect(picture, SIGNAL(updated()), this, SLOT(snapmaticUpdated())); + QObject::connect(picture, SIGNAL(customSignal(QString)), this, SLOT(customSignal(QString))); + + const qreal screenRatio = AppEnv::screenRatio(); + const qreal screenRatioPR = AppEnv::screenRatioPR(); + const QSize renderResolution(48 * screenRatio * screenRatioPR, 27 * screenRatio * screenRatioPR); + ui->labPicture->setFixedSize(48 * screenRatio, 27 * screenRatio); + ui->labPicture->setScaledContents(true); + + QPixmap renderPixmap(renderResolution); + renderPixmap.fill(Qt::transparent); + QPainter renderPainter(&renderPixmap); + const QImage originalImage = picture->getImage(); + const QImage renderImage = originalImage.scaled(renderResolution, Qt::KeepAspectRatio, Qt::SmoothTransformation); // Stack smash + if (renderImage.width() < renderResolution.width()) { + renderPainter.drawImage((renderResolution.width() - renderImage.width()) / 2, 0, renderImage, Qt::AutoColor); + } + else if (renderImage.height() < renderResolution.height()) { + renderPainter.drawImage(0, (renderResolution.height() - renderImage.height()) / 2, renderImage, Qt::AutoColor); + } + else { + renderPainter.drawImage(0, 0, renderImage, Qt::AutoColor); + } + renderPainter.end(); +#if QT_VERSION >= 0x050600 + renderPixmap.setDevicePixelRatio(screenRatioPR); +#endif + + ui->labPicStr->setText(smpic->getPictureStr() % "\n" % smpic->getPictureTitl()); + ui->labPicture->setPixmap(renderPixmap); + + picture->clearCache(); + + adjustTextColor(); +} + +void SnapmaticWidget::snapmaticUpdated() +{ + ui->labPicStr->setText(smpic->getPictureStr() % "\n" % smpic->getPictureTitl()); +} + +void SnapmaticWidget::customSignal(QString signal) +{ + if (signal == "PictureUpdated") { + QPixmap SnapmaticPixmap = QPixmap::fromImage(smpic->getImage().scaled(ui->labPicture->width(), ui->labPicture->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::AutoColor); + ui->labPicture->setPixmap(SnapmaticPixmap); + } +} + +void SnapmaticWidget::retranslate() +{ + smpic->updateStrings(); + ui->labPicStr->setText(smpic->getPictureStr() % "\n" % smpic->getPictureTitl()); +} + +void SnapmaticWidget::on_cmdView_clicked() +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("Interface"); + bool navigationBar = settings.value("NavigationBar", true).toBool(); + settings.endGroup(); + + PictureDialog *picDialog = new PictureDialog(profileDB, crewDB, profileName, this); + picDialog->setSnapmaticPicture(smpic, true); + picDialog->setModal(true); + + // be ready for crewName and playerName updated + QObject::connect(threadDB, SIGNAL(crewNameUpdated()), picDialog, SLOT(crewNameUpdated())); + QObject::connect(threadDB, SIGNAL(playerNameUpdated()), picDialog, SLOT(playerNameUpdated())); + QObject::connect(picDialog, SIGNAL(nextPictureRequested()), this, SLOT(dialogNextPictureRequested())); + QObject::connect(picDialog, SIGNAL(previousPictureRequested()), this, SLOT(dialogPreviousPictureRequested())); + + // add previous next buttons + if (navigationBar) + picDialog->addPreviousNextButtons(); + + // show picture dialog +#ifdef Q_OS_ANDROID + // Android ... + picDialog->showMaximized(); +#else + picDialog->show(); + if (navigationBar) picDialog->styliseDialog(); + //picDialog->adaptNewDialogSize(); + picDialog->setMinimumSize(picDialog->size()); + picDialog->setMaximumSize(picDialog->size()); +#endif + picDialog->exec(); + delete picDialog; +} + +void SnapmaticWidget::on_cmdCopy_clicked() +{ + PictureExport::exportAsSnapmatic(this, smpic); +} + +void SnapmaticWidget::on_cmdExport_clicked() +{ + PictureExport::exportAsPicture(this, smpic); +} + +void SnapmaticWidget::on_cmdDelete_clicked() +{ + if (deletePicture()) + emit pictureDeleted(); +} + +bool SnapmaticWidget::deletePicture() +{ + int uchoice = QMessageBox::question(this, tr("Delete picture"), tr("Are you sure to delete %1 from your Snapmatic pictures?").arg("\""+smpic->getPictureTitle()+"\""), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if (uchoice == QMessageBox::Yes) { + if (smpic->deletePictureFile()) { +#ifdef GTA5SYNC_TELEMETRY + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool(); + telemetrySettings.endGroup(); + if (pushUsageData && Telemetry->canPush()) { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "DeleteSuccess"; + jsonObject["ExtraFlags"] = "Snapmatic"; + jsonObject["DeletedSize"] = QString::number(smpic->getContentMaxLength()); +#if QT_VERSION >= 0x060000 + jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["DeletedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } +#endif + return true; + } + else { + QMessageBox::warning(this, tr("Delete picture"), tr("Failed at deleting %1 from your Snapmatic pictures").arg("\""+smpic->getPictureTitle()+"\"")); + } + } + return false; +} + +void SnapmaticWidget::mousePressEvent(QMouseEvent *ev) +{ + ProfileWidget::mousePressEvent(ev); +} + +void SnapmaticWidget::mouseReleaseEvent(QMouseEvent *ev) +{ + ProfileWidget::mouseReleaseEvent(ev); + if (ui->cbSelected->isVisible()) { + if (rect().contains(ev->pos()) && ev->button() == Qt::LeftButton) { + ui->cbSelected->setChecked(!ui->cbSelected->isChecked()); + } + } + else { + const int contentMode = getContentMode(); + if ((contentMode == 0 || contentMode == 10 || contentMode == 20) && rect().contains(ev->pos()) && ev->button() == Qt::LeftButton) { + if (ev->modifiers().testFlag(Qt::ShiftModifier)) { + ui->cbSelected->setChecked(!ui->cbSelected->isChecked()); + } + else { + on_cmdView_clicked(); + } + } + else if (!ui->cbSelected->isVisible() && (contentMode == 1 || contentMode == 11 || contentMode == 21) && ev->button() == Qt::LeftButton && ev->modifiers().testFlag(Qt::ShiftModifier)) { + ui->cbSelected->setChecked(!ui->cbSelected->isChecked()); + } + } +} + +void SnapmaticWidget::mouseDoubleClickEvent(QMouseEvent *ev) +{ + ProfileWidget::mouseDoubleClickEvent(ev); + + const int contentMode = getContentMode(); + if (!ui->cbSelected->isVisible() && (contentMode == 1 || contentMode == 11 || contentMode == 21) && ev->button() == Qt::LeftButton) { + on_cmdView_clicked(); + } +} + +void SnapmaticWidget::setSelected(bool isSelected) +{ + ui->cbSelected->setChecked(isSelected); +} + +void SnapmaticWidget::pictureSelected() +{ + setSelected(!ui->cbSelected->isChecked()); +} + +void SnapmaticWidget::contextMenuEvent(QContextMenuEvent *ev) +{ + emit contextMenuTriggered(ev); +} + +void SnapmaticWidget::dialogNextPictureRequested() +{ + emit nextPictureRequested((QWidget*)sender()); +} + +void SnapmaticWidget::dialogPreviousPictureRequested() +{ + emit previousPictureRequested((QWidget*)sender()); +} + +void SnapmaticWidget::on_cbSelected_stateChanged(int arg1) +{ + if (arg1 == Qt::Checked) { + emit widgetSelected(); + } + else if (arg1 == Qt::Unchecked) { + emit widgetDeselected(); + } +} + +void SnapmaticWidget::adjustTextColor() +{ + if (isHidden()) { + ui->labPicStr->setStyleSheet(QString("QLabel{color: rgb(%1, %2, %3);}").arg(QString::number(highlightHiddenColor.red()), QString::number(highlightHiddenColor.green()), QString::number(highlightHiddenColor.blue()))); + } + else { + ui->labPicStr->setStyleSheet(""); + } +} + +bool SnapmaticWidget::makePictureHidden() +{ + if (smpic->setPictureHidden()) { + adjustTextColor(); + return true; + } + return false; +} + +bool SnapmaticWidget::makePictureVisible() +{ + if (smpic->setPictureVisible()) { + adjustTextColor(); + return true; + } + return false; +} + +void SnapmaticWidget::makePictureHiddenSlot() +{ + if (!makePictureHidden()) + QMessageBox::warning(this, QApplication::translate("UserInterface", "Hide In-game"), QApplication::translate("SnapmaticWidget", "Failed to hide %1 In-game from your Snapmatic pictures").arg("\""+smpic->getPictureTitle()+"\"")); +} + +void SnapmaticWidget::makePictureVisibleSlot() +{ + if (!makePictureVisible()) + QMessageBox::warning(this, QApplication::translate("UserInterface", "Show In-game"), QApplication::translate("SnapmaticWidget", "Failed to show %1 In-game from your Snapmatic pictures").arg("\""+smpic->getPictureTitle()+"\"")); +} + +void SnapmaticWidget::editSnapmaticProperties() +{ + SnapmaticEditor *snapmaticEditor = new SnapmaticEditor(crewDB, profileDB, this); + snapmaticEditor->setSnapmaticPicture(smpic); + snapmaticEditor->setModal(true); + snapmaticEditor->show(); + snapmaticEditor->exec(); + delete snapmaticEditor; +} + +void SnapmaticWidget::editSnapmaticRawJson() +{ + JsonEditorDialog *jsonEditor = new JsonEditorDialog(smpic, this); + jsonEditor->setModal(true); + jsonEditor->show(); + jsonEditor->exec(); + delete jsonEditor; +} + +void SnapmaticWidget::editSnapmaticImage() +{ + QImage *currentImage = new QImage(smpic->getImage()); + ImportDialog *importDialog = new ImportDialog(profileName, this); + importDialog->setImage(currentImage); + importDialog->enableOverwriteMode(); + importDialog->setModal(true); + importDialog->exec(); + if (importDialog->isImportAgreed()) { + const QByteArray previousPicture = smpic->getPictureStream(); + bool success = smpic->setImage(importDialog->image(), importDialog->isUnlimitedBuffer()); + if (success) { + QString currentFilePath = smpic->getPictureFilePath(); + QString originalFilePath = smpic->getOriginalPictureFilePath(); + QString backupFileName = originalFilePath % ".bak"; + if (!QFile::exists(backupFileName)) { + QFile::copy(currentFilePath, backupFileName); + } + if (!smpic->exportPicture(currentFilePath)) { + smpic->setPictureStream(previousPicture); + QMessageBox::warning(this, QApplication::translate("ImageEditorDialog", "Snapmatic Image Editor"), QApplication::translate("ImageEditorDialog", "Patching of Snapmatic Image failed because of I/O Error")); + return; + } + smpic->emitCustomSignal("PictureUpdated"); +#ifdef GTA5SYNC_TELEMETRY + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool(); + telemetrySettings.endGroup(); + if (pushUsageData && Telemetry->canPush()) { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "ImageEdited"; + jsonObject["ExtraFlags"] = "Interface"; + jsonObject["EditedSize"] = QString::number(smpic->getContentMaxLength()); +#if QT_VERSION >= 0x060000 + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } +#endif + } + else { + QMessageBox::warning(this, QApplication::translate("ImageEditorDialog", "Snapmatic Image Editor"), QApplication::translate("ImageEditorDialog", "Patching of Snapmatic Image failed because of Image Error")); + return; + } + } + delete importDialog; +} + +void SnapmaticWidget::openMapViewer() +{ + SnapmaticPicture *picture = smpic; + SnapmaticProperties currentProperties = picture->getSnapmaticProperties(); + MapLocationDialog *mapLocDialog = new MapLocationDialog(currentProperties.location.x, currentProperties.location.y, this); + mapLocDialog->setCayoPerico(currentProperties.location.isCayoPerico); + mapLocDialog->setModal(true); + mapLocDialog->show(); + mapLocDialog->exec(); + if (mapLocDialog->propUpdated()) { + // Update Snapmatic Properties + currentProperties.location.x = mapLocDialog->getXpos(); + currentProperties.location.y = mapLocDialog->getYpos(); + currentProperties.location.z = 0; + + // Update Snapmatic Picture + QString currentFilePath = picture->getPictureFilePath(); + QString originalFilePath = picture->getOriginalPictureFilePath(); + QString backupFileName = originalFilePath % ".bak"; + if (!QFile::exists(backupFileName)) { + QFile::copy(currentFilePath, backupFileName); + } + SnapmaticProperties fallbackProperties = picture->getSnapmaticProperties(); + picture->setSnapmaticProperties(currentProperties); + if (!picture->exportPicture(currentFilePath)) { + QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("Patching of Snapmatic Properties failed because of I/O Error")); + picture->setSnapmaticProperties(fallbackProperties); + } +#ifdef GTA5SYNC_TELEMETRY + else { + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + bool pushUsageData = telemetrySettings.value("PushUsageData", false).toBool(); + telemetrySettings.endGroup(); + if (pushUsageData && Telemetry->canPush()) { + QJsonDocument jsonDocument; + QJsonObject jsonObject; + jsonObject["Type"] = "LocationEdited"; + jsonObject["ExtraFlags"] = "Interface"; + jsonObject["EditedSize"] = QString::number(picture->getContentMaxLength()); +#if QT_VERSION >= 0x060000 + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); +#else + jsonObject["EditedTime"] = QString::number(QDateTime::currentDateTimeUtc().toTime_t()); +#endif + jsonDocument.setObject(jsonObject); + Telemetry->push(TelemetryCategory::PersonalData, jsonDocument); + } + } +#endif + } + delete mapLocDialog; +} + +bool SnapmaticWidget::isSelected() +{ + return ui->cbSelected->isChecked(); +} + +bool SnapmaticWidget::isHidden() +{ + return smpic->isHidden(); +} + +void SnapmaticWidget::setSelectionMode(bool selectionMode) +{ + ui->cbSelected->setVisible(selectionMode); +} + +void SnapmaticWidget::selectAllWidgets() +{ + emit allWidgetsSelected(); +} + +void SnapmaticWidget::deselectAllWidgets() +{ + emit allWidgetsDeselected(); +} + +SnapmaticPicture* SnapmaticWidget::getPicture() +{ + return smpic; +} + +QString SnapmaticWidget::getPicturePath() +{ + return smpic->getPictureFilePath(); +} + +QString SnapmaticWidget::getWidgetType() +{ + return "SnapmaticWidget"; +} diff --git a/SnapmaticWidget.h b/SnapmaticWidget.h old mode 100755 new mode 100644 index a25b638..8c28f12 --- a/SnapmaticWidget.h +++ b/SnapmaticWidget.h @@ -1,104 +1,103 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef SNAPMATICWIDGET_H -#define SNAPMATICWIDGET_H - -#include "SnapmaticPicture.h" -#include "ProfileDatabase.h" -#include "DatabaseThread.h" -#include "ProfileWidget.h" -#include "CrewDatabase.h" -#include -#include -#include -#include - -namespace Ui { -class SnapmaticWidget; -} - -class SnapmaticWidget : public ProfileWidget -{ - Q_OBJECT - -public: - SnapmaticWidget(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent = 0); - void setSnapmaticPicture(SnapmaticPicture *picture); - void setSelectionMode(bool selectionMode); - void setSelected(bool isSelected); - bool deletePicture(); - bool makePictureVisible(); - bool makePictureHidden(); - SnapmaticPicture *getPicture(); - QString getPicturePath(); - QString getWidgetType(); - bool isSelected(); - bool isHidden(); - ~SnapmaticWidget(); - -private slots: - void on_cmdView_clicked(); - void on_cmdCopy_clicked(); - void on_cmdExport_clicked(); - void on_cmdDelete_clicked(); - void on_cbSelected_stateChanged(int arg1); - void adjustTextColor(); - void pictureSelected(); - void selectAllWidgets(); - void deselectAllWidgets(); - void dialogNextPictureRequested(); - void dialogPreviousPictureRequested(); - void makePictureVisibleSlot(); - void makePictureHiddenSlot(); - void editSnapmaticProperties(); - void snapmaticUpdated(); - -protected: - bool eventFilter(QObject *obj, QEvent *ev); - void mouseDoubleClickEvent(QMouseEvent *ev); - void mouseReleaseEvent(QMouseEvent *ev); - void mousePressEvent(QMouseEvent *ev); - void contextMenuEvent(QContextMenuEvent *ev); - -private: - ProfileDatabase *profileDB; - CrewDatabase *crewDB; - DatabaseThread *threadDB; - Ui::SnapmaticWidget *ui; - SnapmaticPicture *smpic; - QColor highlightBackColor; - QColor highlightTextColor; - QColor highlightHiddenColor; - QString picPath; - QString picTitl; - QString picStr; - QWidget *snwgt; - -signals: - void pictureDeleted(); - void widgetSelected(); - void widgetDeselected(); - void allWidgetsSelected(); - void allWidgetsDeselected(); - void nextPictureRequested(QWidget *dialog); - void previousPictureRequested(QWidget *dialog); - void contextMenuTriggered(QContextMenuEvent *ev); -}; - -#endif // SNAPMATICWIDGET_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef SNAPMATICWIDGET_H +#define SNAPMATICWIDGET_H + +#include "SnapmaticPicture.h" +#include "ProfileDatabase.h" +#include "DatabaseThread.h" +#include "ProfileWidget.h" +#include "CrewDatabase.h" +#include +#include +#include +#include + +namespace Ui { +class SnapmaticWidget; +} + +class SnapmaticWidget : public ProfileWidget +{ + Q_OBJECT + +public: + SnapmaticWidget(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QString profileName, QWidget *parent = 0); + void setSnapmaticPicture(SnapmaticPicture *picture); + void setSelectionMode(bool selectionMode); + void setSelected(bool isSelected); + bool deletePicture(); + bool makePictureVisible(); + bool makePictureHidden(); + SnapmaticPicture *getPicture(); + QString getPicturePath(); + QString getWidgetType(); + bool isSelected(); + bool isHidden(); + void retranslate(); + ~SnapmaticWidget(); + +private slots: + void on_cmdView_clicked(); + void on_cmdCopy_clicked(); + void on_cmdExport_clicked(); + void on_cmdDelete_clicked(); + void on_cbSelected_stateChanged(int arg1); + void adjustTextColor(); + void pictureSelected(); + void selectAllWidgets(); + void deselectAllWidgets(); + void dialogNextPictureRequested(); + void dialogPreviousPictureRequested(); + void makePictureVisibleSlot(); + void makePictureHiddenSlot(); + void editSnapmaticProperties(); + void editSnapmaticRawJson(); + void editSnapmaticImage(); + void openMapViewer(); + void snapmaticUpdated(); + void customSignal(QString signal); + +protected: + void mouseDoubleClickEvent(QMouseEvent *ev); + void mouseReleaseEvent(QMouseEvent *ev); + void mousePressEvent(QMouseEvent *ev); + void contextMenuEvent(QContextMenuEvent *ev); + +private: + ProfileDatabase *profileDB; + CrewDatabase *crewDB; + DatabaseThread *threadDB; + QString profileName; + Ui::SnapmaticWidget *ui; + SnapmaticPicture *smpic; + QColor highlightHiddenColor; + +signals: + void pictureDeleted(); + void widgetSelected(); + void widgetDeselected(); + void allWidgetsSelected(); + void allWidgetsDeselected(); + void nextPictureRequested(QWidget *dialog); + void previousPictureRequested(QWidget *dialog); + void contextMenuTriggered(QContextMenuEvent *ev); +}; + +#endif // SNAPMATICWIDGET_H diff --git a/SnapmaticWidget.ui b/SnapmaticWidget.ui old mode 100755 new mode 100644 index 5c720c8..a3e7e89 --- a/SnapmaticWidget.ui +++ b/SnapmaticWidget.ui @@ -1,169 +1,169 @@ - - - SnapmaticWidget - - - - 0 - 0 - 490 - 45 - - - - Snapmatic Widget - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - - - Qt::NoFocus - - - - - - - - - - - 48 - 27 - - - - - 48 - 27 - - - - 0 - - - - - - true - - - - - - - - 0 - 0 - - - - PHOTO - 00/00/00 00:00:00 - - - true - - - - - - - - 0 - 0 - - - - View picture - - - View - - - true - - - - - - - - 0 - 0 - - - - Copy picture - - - Copy - - - - - - - - 0 - 0 - - - - Export picture - - - Export - - - - - - - - 0 - 0 - - - - Delete picture - - - Delete - - - true - - - - - - - - - - - + + + SnapmaticWidget + + + + 0 + 0 + 490 + 45 + + + + Snapmatic Widget + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + + + Qt::NoFocus + + + + + + + + + + + 48 + 27 + + + + + 48 + 27 + + + + 0 + + + + + + true + + + + + + + + 0 + 0 + + + + PHOTO - 00/00/00 00:00:00 + + + true + + + + + + + + 0 + 0 + + + + View picture + + + View + + + true + + + + + + + + 0 + 0 + + + + Copy picture + + + Copy + + + + + + + + 0 + 0 + + + + Export picture + + + Export + + + + + + + + 0 + 0 + + + + Delete picture + + + Delete + + + true + + + + + + + + + + + diff --git a/StandardPaths.cpp b/StandardPaths.cpp old mode 100755 new mode 100644 index dda73a3..c2f8111 --- a/StandardPaths.cpp +++ b/StandardPaths.cpp @@ -1,128 +1,125 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "StandardPaths.h" -#if QT_VERSION >= 0x050000 -#include -#else -#include -#endif - -StandardPaths::StandardPaths() -{ - -} - -QString StandardPaths::applicationsLocation() -{ -#if QT_VERSION >= 0x050000 - return QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); -#else - return QDesktopServices::storageLocation(QDesktopServices::ApplicationsLocation); -#endif -} - -QString StandardPaths::cacheLocation() -{ -#if QT_VERSION >= 0x050000 - return QStandardPaths::writableLocation(QStandardPaths::CacheLocation); -#else - return QDesktopServices::storageLocation(QDesktopServices::CacheLocation); -#endif -} - -QString StandardPaths::dataLocation() -{ -#if QT_VERSION >= 0x050000 - return QStandardPaths::writableLocation(QStandardPaths::DataLocation); -#else - return QDesktopServices::storageLocation(QDesktopServices::DataLocation); -#endif -} - -QString StandardPaths::desktopLocation() -{ -#if QT_VERSION >= 0x050000 - return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); -#else - return QDesktopServices::storageLocation(QDesktopServices::DesktopLocation); -#endif -} - -QString StandardPaths::documentsLocation() -{ -#if QT_VERSION >= 0x050000 - return QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); -#else - return QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); -#endif -} - -QString StandardPaths::moviesLocation() -{ -#if QT_VERSION >= 0x050000 - return QStandardPaths::writableLocation(QStandardPaths::MoviesLocation); -#else - return QDesktopServices::storageLocation(QDesktopServices::MoviesLocation); -#endif -} - -QString StandardPaths::picturesLocation() -{ -#if QT_VERSION >= 0x050000 - return QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); -#else - return QDesktopServices::storageLocation(QDesktopServices::PicturesLocation); -#endif -} - -QString StandardPaths::fontsLocation() -{ -#if QT_VERSION >= 0x050000 - return QStandardPaths::writableLocation(QStandardPaths::FontsLocation); -#else - return QDesktopServices::storageLocation(QDesktopServices::FontsLocation); -#endif -} - -QString StandardPaths::homeLocation() -{ -#if QT_VERSION >= 0x050000 - return QStandardPaths::writableLocation(QStandardPaths::HomeLocation); -#else - return QDesktopServices::storageLocation(QDesktopServices::HomeLocation); -#endif -} - -QString StandardPaths::musicLocation() -{ -#if QT_VERSION >= 0x050000 - return QStandardPaths::writableLocation(QStandardPaths::MusicLocation); -#else - return QDesktopServices::storageLocation(QDesktopServices::MusicLocation); -#endif -} - -QString StandardPaths::tempLocation() -{ -#if QT_VERSION >= 0x050000 - return QStandardPaths::writableLocation(QStandardPaths::TempLocation); -#else - return QDesktopServices::storageLocation(QDesktopServices::TempLocation); -#endif -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "StandardPaths.h" +#if QT_VERSION >= 0x050000 +#include +#else +#include +#endif + +QString StandardPaths::applicationsLocation() +{ +#if QT_VERSION >= 0x050000 + return QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); +#else + return QDesktopServices::storageLocation(QDesktopServices::ApplicationsLocation); +#endif +} + +QString StandardPaths::cacheLocation() +{ +#if QT_VERSION >= 0x050000 + return QStandardPaths::writableLocation(QStandardPaths::CacheLocation); +#else + return QDesktopServices::storageLocation(QDesktopServices::CacheLocation); +#endif +} + +QString StandardPaths::dataLocation() +{ +#if QT_VERSION >= 0x060000 + return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); +#elif QT_VERSION >= 0x050000 + return QStandardPaths::writableLocation(QStandardPaths::DataLocation); +#else + return QDesktopServices::storageLocation(QDesktopServices::DataLocation); +#endif +} + +QString StandardPaths::desktopLocation() +{ +#if QT_VERSION >= 0x050000 + return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); +#else + return QDesktopServices::storageLocation(QDesktopServices::DesktopLocation); +#endif +} + +QString StandardPaths::documentsLocation() +{ +#if QT_VERSION >= 0x050000 + return QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#else + return QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#endif +} + +QString StandardPaths::moviesLocation() +{ +#if QT_VERSION >= 0x050000 + return QStandardPaths::writableLocation(QStandardPaths::MoviesLocation); +#else + return QDesktopServices::storageLocation(QDesktopServices::MoviesLocation); +#endif +} + +QString StandardPaths::picturesLocation() +{ +#if QT_VERSION >= 0x050000 + return QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); +#else + return QDesktopServices::storageLocation(QDesktopServices::PicturesLocation); +#endif +} + +QString StandardPaths::fontsLocation() +{ +#if QT_VERSION >= 0x050000 + return QStandardPaths::writableLocation(QStandardPaths::FontsLocation); +#else + return QDesktopServices::storageLocation(QDesktopServices::FontsLocation); +#endif +} + +QString StandardPaths::homeLocation() +{ +#if QT_VERSION >= 0x050000 + return QStandardPaths::writableLocation(QStandardPaths::HomeLocation); +#else + return QDesktopServices::storageLocation(QDesktopServices::HomeLocation); +#endif +} + +QString StandardPaths::musicLocation() +{ +#if QT_VERSION >= 0x050000 + return QStandardPaths::writableLocation(QStandardPaths::MusicLocation); +#else + return QDesktopServices::storageLocation(QDesktopServices::MusicLocation); +#endif +} + +QString StandardPaths::tempLocation() +{ +#if QT_VERSION >= 0x050000 + return QStandardPaths::writableLocation(QStandardPaths::TempLocation); +#else + return QDesktopServices::storageLocation(QDesktopServices::TempLocation); +#endif +} diff --git a/StandardPaths.h b/StandardPaths.h old mode 100755 new mode 100644 index 203953b..590e0dc --- a/StandardPaths.h +++ b/StandardPaths.h @@ -1,41 +1,40 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef STANDARDPATHS_H -#define STANDARDPATHS_H - -#include - -class StandardPaths -{ -public: - StandardPaths(); - static QString applicationsLocation(); - static QString cacheLocation(); - static QString dataLocation(); - static QString desktopLocation(); - static QString documentsLocation(); - static QString fontsLocation(); - static QString homeLocation(); - static QString moviesLocation(); - static QString picturesLocation(); - static QString musicLocation(); - static QString tempLocation(); -}; - -#endif // STANDARDPATHS_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef STANDARDPATHS_H +#define STANDARDPATHS_H + +#include + +class StandardPaths +{ +public: + static QString applicationsLocation(); + static QString cacheLocation(); + static QString dataLocation(); + static QString desktopLocation(); + static QString documentsLocation(); + static QString fontsLocation(); + static QString homeLocation(); + static QString moviesLocation(); + static QString picturesLocation(); + static QString musicLocation(); + static QString tempLocation(); +}; + +#endif // STANDARDPATHS_H diff --git a/StringParser.cpp b/StringParser.cpp old mode 100755 new mode 100644 index 6addd10..4ac3fba --- a/StringParser.cpp +++ b/StringParser.cpp @@ -1,76 +1,54 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "StringParser.h" -#include "config.h" -#include -#ifndef GTA5VIEW_CMD -#include -#endif -#include -#include -#include -#include -#include -#include - -StringParser::StringParser() -{ - -} - -QString StringParser::parseTitleString(const QByteArray &commitBytes, int maxLength) -{ - Q_UNUSED(maxLength) - QString retStr = QTextCodec::codecForName("UTF-16LE")->toUnicode(commitBytes).trimmed(); - retStr.remove(QChar('\x00')); - return retStr; -} - -QString StringParser::convertDrawStringForLog(const QString &inputStr) -{ - QString outputStr = inputStr; - return outputStr.replace("&","&u;").replace(",","&c;"); -} - -QString StringParser::convertLogStringForDraw(const QString &inputStr) -{ - QString outputStr = inputStr; - return outputStr.replace("&c;",",").replace("&u;","&"); -} - -#ifndef GTA5VIEW_CMD -QString StringParser::convertBuildedString(const QString &buildedStr) -{ - QString outputStr = buildedStr; - QByteArray sharePath = GTA5SYNC_SHARE; - outputStr.replace("$SHAREDIR", QString::fromUtf8(sharePath)); - outputStr.replace("$RUNDIR", QFileInfo(qApp->applicationFilePath()).absoluteDir().absolutePath()); - outputStr.replace("$SEPARATOR", QDir::separator()); - return outputStr; -} -#endif - -QString StringParser::escapeString(const QString &toEscape) -{ -#if QT_VERSION >= 0x050000 - return toEscape.toHtmlEscaped(); -#else - return Qt::escape(toEscape); -#endif -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "StringParser.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +QString StringParser::escapeString(const QString &toEscape) +{ +#if QT_VERSION >= 0x050000 + return toEscape.toHtmlEscaped(); +#else + return Qt::escape(toEscape); +#endif +} + +QString StringParser::convertBuildedString(const QString &buildedStr) +{ + QString outputStr = buildedStr; + outputStr.replace("APPNAME:", QString::fromUtf8(GTA5SYNC_APPSTR)); + outputStr.replace("SHAREDDIR:", QString::fromUtf8(GTA5SYNC_SHARE)); + outputStr.replace("RUNDIR:", QFileInfo(QApplication::applicationFilePath()).canonicalPath()); +#if QT_VERSION >= 0x060000 + outputStr.replace("QCONFLANG:", QLibraryInfo::path(QLibraryInfo::TranslationsPath)); + outputStr.replace("QCONFPLUG:", QLibraryInfo::path(QLibraryInfo::PluginsPath)); +#else + outputStr.replace("QCONFLANG:", QLibraryInfo::location(QLibraryInfo::TranslationsPath)); + outputStr.replace("QCONFPLUG:", QLibraryInfo::location(QLibraryInfo::PluginsPath)); +#endif + outputStr.replace("SEPARATOR:", QDir::separator()); + return outputStr; +} diff --git a/StringParser.h b/StringParser.h old mode 100755 new mode 100644 index a9505f6..7dac436 --- a/StringParser.h +++ b/StringParser.h @@ -1,38 +1,32 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef STRINGPARSER_H -#define STRINGPARSER_H - -#include -#include - -class StringParser -{ -public: - StringParser(); - static QString parseTitleString(const QByteArray &commitBytes, int maxLength); - static QString convertDrawStringForLog(const QString &inputStr); - static QString convertLogStringForDraw(const QString &inputStr); -#ifndef GTA5VIEW_CMD - static QString convertBuildedString(const QString &buildedStr); -#endif - static QString escapeString(const QString &toEscape); -}; - -#endif // STRINGPARSER_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef STRINGPARSER_H +#define STRINGPARSER_H + +#include +#include + +class StringParser +{ +public: + static QString escapeString(const QString &toEscape); + static QString convertBuildedString(const QString &buildedStr); +}; + +#endif // STRINGPARSER_H diff --git a/TelemetryClass.cpp b/TelemetryClass.cpp new file mode 100644 index 0000000..724e0c3 --- /dev/null +++ b/TelemetryClass.cpp @@ -0,0 +1,596 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2018 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "TelemetryClassAuthenticator.h" +#include "TelemetryClass.h" +#include "StandardPaths.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef GTA5SYNC_TELEMETRY_WEBURL +#define GTA5SYNC_TELEMETRY_WEBURL "" +#endif + +#ifdef Q_OS_WIN +#include "windows.h" +#include "intrin.h" +#include "d3d9.h" +#endif + +TelemetryClass TelemetryClass::telemetryClassInstance; + +void TelemetryClass::init() +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("Telemetry"); + telemetryEnabled = true; + telemetryStateForced = true; + QString telemetryLegacyClientID = settings.value("ClientID", QString()).toString(); + if (telemetryLegacyClientID.isEmpty() || telemetryLegacyClientID == "v2+") + { + telemetryClientID = QString::fromUtf8(QByteArray::fromBase64(settings.value("Identification", QByteArray()).toByteArray())); + } + else + { + QDir dir; + dir.mkpath(StandardPaths::dataLocation()); + dir.setPath(StandardPaths::dataLocation()); + QString dirPath = dir.absolutePath(); + QString portLoc = dirPath % "/.ported"; + bool telemetryPortedKey = settings.value("IsPorted", false).toBool(); + bool telemetryPortedFile = QFile::exists(portLoc); + if (!telemetryPortedKey && !telemetryPortedFile) + { + QFile portFile(portLoc); + if (portFile.open(QFile::WriteOnly)) + { + portFile.write("\n"); + portFile.flush(); + } + portFile.close(); + telemetryClientID = telemetryLegacyClientID; + settings.setValue("Identification", telemetryLegacyClientID.toUtf8().toBase64()); + settings.setValue("IsPorted", true); + settings.setValue("ClientID", "v2+"); + } + else + { + telemetryClientID = QString(); + } + } + telemetryPushAppConf = settings.value("PushAppConf", false).toBool(); + settings.endGroup(); +} + +void TelemetryClass::refresh() +{ + init(); +} + +bool TelemetryClass::canPush() +{ + if (!isEnabled() || !isRegistered() || !TelemetryClassAuthenticator::havePushURL()) return false; + return true; +} + +bool TelemetryClass::canRegister() +{ + QDir dir; + dir.mkpath(StandardPaths::dataLocation()); + dir.setPath(StandardPaths::dataLocation()); + QString dirPath = dir.absolutePath(); + QString regLoc = dirPath % "/.reg"; + if (QFile::exists(regLoc)) return false; + if (!isEnabled() || isRegistered() || !TelemetryClassAuthenticator::haveRegURL()) return false; + return true; +} + +bool TelemetryClass::isEnabled() +{ + return telemetryEnabled; +} + +bool TelemetryClass::isStateForced() +{ + return telemetryStateForced; +} + +bool TelemetryClass::isRegistered() +{ + return !telemetryClientID.isEmpty(); +} + +QString TelemetryClass::getRegisteredID() +{ + return telemetryClientID; +} + +void TelemetryClass::setEnabled(bool enabled) +{ + telemetryEnabled = enabled; + telemetryStateForced = true; +} + +void TelemetryClass::setDisabled(bool disabled) +{ + telemetryEnabled = !disabled; + telemetryStateForced = true; +} + +void TelemetryClass::push(TelemetryCategory category) +{ + if (!canPush()) return; + switch (category) + { + case TelemetryCategory::OperatingSystemSpec: + push(category, getOperatingSystem()); + break; + case TelemetryCategory::HardwareSpec: + push(category, getSystemHardware()); + break; + case TelemetryCategory::UserLocaleData: + push(category, getSystemLocaleList()); + break; + case TelemetryCategory::ApplicationConf: + push(category, getApplicationConf()); + break; + case TelemetryCategory::ApplicationSpec: + push(category, getApplicationSpec()); + break; + case TelemetryCategory::UserFeedback: + break; + case TelemetryCategory::PersonalData: + break; + case TelemetryCategory::CustomEmitted: + break; + } +} + +void TelemetryClass::push(TelemetryCategory category, QJsonDocument json) +{ + if (!canPush()) return; + + QJsonDocument jsonDocument(json); + QJsonObject jsonObject = jsonDocument.object(); + jsonObject["ClientID"] = telemetryClientID; + jsonDocument.setObject(jsonObject); + + QHttpMultiPart *httpMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + QHttpPart categoryPart; + categoryPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"json-category\"")); + categoryPart.setBody(categoryToString(category).toUtf8()); + + QHttpPart jsonPart; + jsonPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); + jsonPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"json-deflated\"")); + jsonPart.setBody(qCompress(jsonDocument.toJson(QJsonDocument::Compact))); + + httpMultiPart->append(categoryPart); + httpMultiPart->append(jsonPart); + + QNetworkAccessManager *netManager = new QNetworkAccessManager(); + QNetworkRequest netRequest(TelemetryClassAuthenticator::getTrackingPushURL()); + netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent()); + QNetworkReply *netReply = netManager->post(netRequest, httpMultiPart); + httpMultiPart->setParent(netReply); + + connect(netManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(pushFinished(QNetworkReply*))); +} + +QJsonDocument TelemetryClass::getOperatingSystem() +{ + QJsonDocument jsonDocument; + QJsonObject jsonObject; +#if QT_VERSION >= 0x050400 + jsonObject["KernelType"] = QSysInfo::kernelType(); + jsonObject["KernelVersion"] = QSysInfo::kernelVersion(); + jsonObject["ProductType"] = QSysInfo::productType(); + jsonObject["ProductVersion"] = QSysInfo::productVersion(); + jsonObject["OSName"] = QSysInfo::prettyProductName(); + jsonObject["OSArch"] = QSysInfo::currentCpuArchitecture(); +#endif + jsonDocument.setObject(jsonObject); + return jsonDocument; +} + +QJsonDocument TelemetryClass::getSystemHardware() +{ + QJsonDocument jsonDocument; + QJsonObject jsonObject; +#ifdef Q_OS_WIN + { + int CPUInfo[4] = {-1}; + unsigned nExIds, ic = 0; + char CPUBrandString[0x40]; + __cpuid(CPUInfo, 0x80000000); + nExIds = CPUInfo[0]; + for (ic = 0x80000000; ic <= nExIds; ic++) + { + __cpuid(CPUInfo, ic); + if (ic == 0x80000002) { memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo)); } + else if (ic == 0x80000003) { memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo)); } + else if (ic == 0x80000004) { memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo)); } + } + jsonObject["CPUName"] = QString::fromLatin1(CPUBrandString).simplified(); + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + jsonObject["CPUThreads"] = QString::number(sysInfo.dwNumberOfProcessors); + MEMORYSTATUSEX statex; + statex.dwLength = sizeof(statex); + GlobalMemoryStatusEx(&statex); + jsonObject["SystemRAM"] = QString(QString::number((statex.ullTotalPhys / 1024) / 1024) % "MB"); + QStringList gpusList; + IDirect3D9 *pD3D = Direct3DCreate9(D3D_SDK_VERSION); + int adapters = pD3D->GetAdapterCount(); + for (int ia = 0; ia < adapters; ia++) + { + D3DADAPTER_IDENTIFIER9 d3dIdent; + HRESULT result = pD3D->GetAdapterIdentifier(ia, 0, &d3dIdent); + if (result == D3D_OK) + { + QString gpuAdapter = QString::fromLatin1(d3dIdent.Description); + if (!gpusList.contains(gpuAdapter)) { gpusList << gpuAdapter; } + } + } + pD3D->Release(); + jsonObject["GPUs"] = QJsonValue::fromVariant(gpusList); + } +#else + QDir procDir("/proc"); + if (procDir.exists()) + { + QFile cpuInfo("/proc/cpuinfo"); + if (cpuInfo.open(QFile::ReadOnly)) + { + QByteArray cpuInfoArray = cpuInfo.readAll(); + QBuffer cpuInfoBuffer(&cpuInfoArray); + if (cpuInfoBuffer.open(QBuffer::ReadOnly)) + { + QByteArray toFind = "model name"; + while (cpuInfoBuffer.canReadLine()) + { + QByteArray cpuData = cpuInfoBuffer.readLine(); + if (cpuData.left(toFind.length()) == toFind) + { + jsonObject["CPUName"] = QString::fromUtf8(cpuData).split(':').at(1).simplified(); + break; + } + } + int cpuThreads = 0; + toFind = "processor"; + cpuInfoBuffer.seek(0); + while (cpuInfoBuffer.canReadLine()) + { + QByteArray cpuData = cpuInfoBuffer.readLine(); + if (cpuData.left(toFind.length()) == toFind) + { + cpuThreads++; + } + } + jsonObject["CPUThreads"] = QString::number(cpuThreads); + } + } + + QFile memInfo("/proc/meminfo"); + if (memInfo.open(QFile::ReadOnly)) + { + QByteArray memInfoArray = memInfo.readAll(); + QBuffer memInfoBuffer(&memInfoArray); + if (memInfoBuffer.open(QBuffer::ReadOnly)) + { + QByteArray toFind = "MemTotal:"; + while (memInfoBuffer.canReadLine()) + { + QByteArray memData = memInfoBuffer.readLine(); + if (memData.left(toFind.length()) == toFind) + { + QByteArray memDataVal = memData.mid(toFind.length()).trimmed(); + int totalMemoryInKB = memDataVal.left(memDataVal.length() - 3).toInt(); + jsonObject["SystemRAM"] = QString(QString::number(totalMemoryInKB / 1024) % "MB"); + break; + } + } + } + } + } +#endif + + jsonDocument.setObject(jsonObject); + return jsonDocument; +} + +QJsonDocument TelemetryClass::getApplicationSpec() +{ + QJsonDocument jsonDocument; + QJsonObject jsonObject; +#if QT_VERSION >= 0x050400 + jsonObject["Arch"] = QSysInfo::buildCpuArchitecture(); +#endif + jsonObject["Name"] = GTA5SYNC_APPSTR; +#ifdef GTA5SYNC_COMMIT + jsonObject["Commit"] = GTA5SYNC_COMMIT; +#endif + jsonObject["Version"] = GTA5SYNC_APPVER; + jsonObject["BuildDateTime"] = AppEnv::getBuildDateTime(); + jsonObject["BuildType"] = GTA5SYNC_BUILDTYPE; + jsonObject["BuildCode"] = AppEnv::getBuildCode(); + jsonObject["QtVersion"] = qVersion(); + jsonDocument.setObject(jsonObject); + return jsonDocument; +} + +QJsonDocument TelemetryClass::getApplicationConf() +{ + QJsonDocument jsonDocument; + QJsonObject jsonObject; + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + + settings.beginGroup("Interface"); + QJsonObject interfaceObject; + interfaceObject["AreaLanguage"] = settings.value("AreaLanguage", "Auto").toString(); + interfaceObject["Language"] = settings.value("Language", "System").toString(); + interfaceObject["NavigationBar"] = settings.value("NavigationBar", false).toBool(); + jsonObject["Interface"] = interfaceObject; + settings.endGroup(); + + settings.beginGroup("Pictures"); + QJsonObject picturesObject; + picturesObject["AspectRatio"] = ((Qt::AspectRatioMode)settings.value("AspectRatio").toInt() == Qt::IgnoreAspectRatio) ? "IgnoreAspectRatio" : "KeepAspectRatio"; + picturesObject["CustomQuality"] = settings.value("CustomQuality", 100).toInt(); + picturesObject["CustomQualityEnabled"] = settings.value("CustomQualityEnabled", false).toBool(); + picturesObject["ExportSizeMode"] = settings.value("ExportSizeMode", "Default").toString(); + jsonObject["Pictures"] = picturesObject; + settings.endGroup(); + + settings.beginGroup("Profile"); + QJsonObject profileObject; + int contentMode = settings.value("ContentMode", 0).toInt(); + switch (contentMode) + { + case 0: + profileObject["ContentMode"] = "OpenWithSingleClick"; + break; + case 1: + profileObject["ContentMode"] = "OpenWithDoubleClick"; + break; + case 2: + profileObject["ContentMode"] = "SelectWithSingleClick"; + break; + } + jsonObject["Profile"] = profileObject; + settings.endGroup(); + + settings.beginGroup("Startup"); + QJsonObject startupObject; + startupObject["AppStyle"] = settings.value("AppStyle", "System").toString(); + startupObject["CustomStyle"] = settings.value("CustomStyle", false).toBool(); + startupObject["StartCount"] = QString::number(settings.value("StartCount", 0).toUInt()); + jsonObject["Startup"] = startupObject; + settings.endGroup(); + + jsonDocument.setObject(jsonObject); + return jsonDocument; +} + +QJsonDocument TelemetryClass::getSystemLocaleList() +{ + QJsonDocument jsonDocument; + QJsonObject jsonObject; + QStringList languagesList = QLocale::system().uiLanguages(); + if (languagesList.length() >= 1) + { + jsonObject["PrimaryLanguage"] = languagesList.at(0); + } + if (languagesList.length() >= 2) + { + languagesList.removeAt(0); + jsonObject["SecondaryLanguages"] = QJsonValue::fromVariant(languagesList); + } + jsonDocument.setObject(jsonObject); + return jsonDocument; +} + +QString TelemetryClass::categoryToString(TelemetryCategory category) +{ + switch (category) + { + case TelemetryCategory::OperatingSystemSpec: + return QString("OperatingSystemSpec"); + break; + case TelemetryCategory::HardwareSpec: + return QString("HardwareSpec"); + break; + case TelemetryCategory::UserLocaleData: + return QString("UserLocaleData"); + break; + case TelemetryCategory::ApplicationConf: + return QString("ApplicationConf"); + break; + case TelemetryCategory::ApplicationSpec: + return QString("ApplicationSpec"); + break; + case TelemetryCategory::UserFeedback: + return QString("UserFeedback"); + break; + case TelemetryCategory::PersonalData: + return QString("PersonalData"); + break; + case TelemetryCategory::CustomEmitted: + return QString("CustomEmitted"); + break; + default: + return QString("UnknownCategory"); + break; + } +} + +QUrl TelemetryClass::getWebURL() +{ + return QUrl(GTA5SYNC_TELEMETRY_WEBURL); +} + +void TelemetryClass::registerClient() +{ + QNetworkAccessManager *netManager = new QNetworkAccessManager(); + QNetworkRequest netRequest(TelemetryClassAuthenticator::getTrackingRegURL()); + netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent()); + netManager->get(netRequest); + + connect(netManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(registerFinished(QNetworkReply*))); +} + +void TelemetryClass::work() +{ + if (!canPush() && canRegister()) + { + connect(this, SIGNAL(registered(bool)), this, SLOT(work_pd(bool))); + registerClient(); + } + else if (canPush()) + { + work_p(true); + } +} + +void TelemetryClass::work_p(bool doWork) +{ + if (doWork) + { + push(TelemetryCategory::ApplicationSpec); + push(TelemetryCategory::UserLocaleData); + push(TelemetryCategory::OperatingSystemSpec); + push(TelemetryCategory::HardwareSpec); + if (telemetryPushAppConf) + { + push(TelemetryCategory::ApplicationConf); + } + else + { + push(TelemetryCategory::ApplicationConf, QJsonDocument()); + } + } +} + +void TelemetryClass::work_pd(bool doWork) +{ + disconnect(this, SIGNAL(registered(bool)), this, SLOT(work_pd(bool))); + work_p(doWork); +} + +void TelemetryClass::pushFinished(QNetworkReply *reply) +{ + bool isSuccessful = false; + if (reply->canReadLine()) + { + QByteArray readedData = reply->readLine(); + if (QString::fromUtf8(readedData).trimmed() == QString("Submit success!")) + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "Telemetry" << QString("Submit success!"); +#endif + isSuccessful = true; +#ifdef GTA5SYNC_DEBUG + if (reply->isReadable()) + { + readedData = reply->readAll().trimmed(); + if (!readedData.isEmpty()) { qDebug() << "Telemetry Push" << readedData; } + } +#endif + } + else + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "Telemetry" << QString("Submit failed!"); +#endif + } + } + else + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "Telemetry" << QString("Submit failed!"); +#endif + } + reply->deleteLater(); + sender()->deleteLater(); + emit pushed(isSuccessful); +} + +void TelemetryClass::registerFinished(QNetworkReply *reply) +{ + bool isSuccessful = false; + if (reply->canReadLine()) + { + QByteArray readedData = reply->readLine(); + if (QString::fromUtf8(readedData).trimmed() == QString("Registration success!") && reply->canReadLine()) + { + QDir dir; + dir.mkpath(StandardPaths::dataLocation()); + dir.setPath(StandardPaths::dataLocation()); + QString dirPath = dir.absolutePath(); + QString regLoc = dirPath % "/.reg"; + readedData = reply->readLine(); + telemetryClientID = QString::fromUtf8(readedData).trimmed(); + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("Telemetry"); + settings.setValue("Identification", telemetryClientID.toUtf8().toBase64()); + settings.endGroup(); + QFile regFile(regLoc); + if (regFile.open(QFile::WriteOnly)) + { + regFile.write("\n"); + regFile.flush(); + } + regFile.close(); +#ifdef GTA5SYNC_DEBUG + qDebug() << "Telemetry" << QString("Registration success!"); +#endif + isSuccessful = true; + } + else + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "Telemetry" << QString("Registration failed!"); +#endif + } + } + else + { +#ifdef GTA5SYNC_DEBUG + qDebug() << "Telemetry" << QString("Registration failed!"); +#endif + } + reply->deleteLater(); + sender()->deleteLater(); + emit registered(isSuccessful); +} diff --git a/TelemetryClass.h b/TelemetryClass.h new file mode 100644 index 0000000..834c3ed --- /dev/null +++ b/TelemetryClass.h @@ -0,0 +1,80 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2018 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef TELEMETRYCLASS_H +#define TELEMETRYCLASS_H + +#include +#include +#include +#include +#include + +enum class TelemetryCategory : int { OperatingSystemSpec = 0, HardwareSpec = 1, UserLocaleData = 2, ApplicationConf = 3, UserFeedback = 4, ApplicationSpec = 5, PersonalData = 6, CustomEmitted = 99 }; + +class TelemetryClass : public QObject +{ + Q_OBJECT +public: + static TelemetryClass* getInstance() { return &telemetryClassInstance; } + static QString categoryToString(TelemetryCategory category); + static QUrl getWebURL(); + bool canPush(); + bool canRegister(); + bool isEnabled(); + bool isStateForced(); + bool isRegistered(); + void init(); + void work(); + void refresh(); + void setEnabled(bool enabled); + void setDisabled(bool disabled); + void push(TelemetryCategory category); + void push(TelemetryCategory category, const QJsonDocument json); + void registerClient(); + QString getRegisteredID(); + +private: + static TelemetryClass telemetryClassInstance; + QString telemetryClientID; + bool telemetryEnabled; + bool telemetryStateForced; + bool telemetryPushAppConf; + + void work_p(bool doWork); + QJsonDocument getOperatingSystem(); + QJsonDocument getSystemHardware(); + QJsonDocument getApplicationSpec(); + QJsonDocument getApplicationConf(); + QJsonDocument getSystemLocaleList(); + +private slots: + void pushFinished(QNetworkReply *reply); + void registerFinished(QNetworkReply *reply); + void work_pd(bool doWork); + +signals: + void pushed(bool isSucessful); + void registered(bool isSucessful); +}; + +extern TelemetryClass telemetryClass; + +#define Telemetry TelemetryClass::getInstance() + +#endif // TELEMETRYCLASS_H diff --git a/TranslationClass.cpp b/TranslationClass.cpp new file mode 100644 index 0000000..06450d1 --- /dev/null +++ b/TranslationClass.cpp @@ -0,0 +1,605 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2017-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "TranslationClass.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x050000 +#define QtBaseTranslationFormat "qtbase_" +#else +#define QtBaseTranslationFormat "qt_" +#endif + +TranslationClass TranslationClass::translationClassInstance; + +void TranslationClass::initUserLanguage() +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("Interface"); + userLanguage = settings.value("Language", "System").toString(); + userAreaLanguage = settings.value("AreaLanguage", "Auto").toString(); + settings.endGroup(); +} + +void TranslationClass::loadTranslation(QApplication *app) +{ + if (isLangLoaded) { + unloadTranslation(app); + } + else { + currentLangIndex = 0; + } + const QString exLangPath = AppEnv::getExLangFolder(); + const QString inLangPath = AppEnv::getInLangFolder(); + if (userLanguage == "en" || userLanguage == "en_GB") { + currentLanguage = "en_GB"; + if (loadQtTranslation_p(exLangPath, &exQtTranslator)) { + app->installTranslator(&exQtTranslator); + } + else if (loadQtTranslation_p(inLangPath, &inQtTranslator)) { + app->installTranslator(&inQtTranslator); + } +#if QT_VERSION >= 0x060000 + QLocale::setDefault(QLocale(currentLanguage)); +#else + QLocale::setDefault(currentLanguage); +#endif + isLangLoaded = true; + return; + } +#ifndef GTA5SYNC_QCONF // Classic modable loading method + QString externalLanguageStr; + bool externalLanguageReady = false; + bool externalEnglishMode = false; + bool loadInternalLang = false; + bool trLoadSuccess = false; + if (isUserLanguageSystem_p()) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadExSystemLanguage"; +#endif + trLoadSuccess = loadSystemTranslation_p(exLangPath, &exAppTranslator); + } + else { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadExUserLanguage"; +#endif + trLoadSuccess = loadUserTranslation_p(exLangPath, &exAppTranslator); + if (!trLoadSuccess) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadInUserLanguage"; +#endif + trLoadSuccess = loadUserTranslation_p(inLangPath, &inAppTranslator); + if (!trLoadSuccess) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadUserLanguageFailed"; +#endif + } + else { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadUserLanguageSuccess"; +#endif + loadInternalLang = true; + isLangLoaded = true; + } + } + else { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadUserLanguageSuccess"; +#endif + isLangLoaded = true; + } + } + if (trLoadSuccess) { + // Don't install the language until we know we not have a better language for the user + if (currentLangIndex != 0 || isEnglishMode) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "externalLanguageReady" << currentLanguage; +#endif + externalEnglishMode = isEnglishMode; + externalLanguageStr = currentLanguage; + externalLanguageReady = true; + } + else { +#ifdef GTA5SYNC_DEBUG + qDebug() << "installTranslation"; +#endif + if (loadInternalLang) { + app->installTranslator(&inAppTranslator); + } + else { + app->installTranslator(&exAppTranslator); + } + if (loadQtTranslation_p(exLangPath, &exQtTranslator)) { + app->installTranslator(&exQtTranslator); + } + else if (loadQtTranslation_p(inLangPath, &inQtTranslator)) { + app->installTranslator(&inQtTranslator); + } +#if QT_VERSION >= 0x060000 + QLocale::setDefault(QLocale(currentLanguage)); +#else + QLocale::setDefault(currentLanguage); +#endif + isLangLoaded = true; + } + } + if (externalLanguageReady) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadInSystemLanguage"; +#endif + int externalLangIndex = currentLangIndex; + trLoadSuccess = loadSystemTranslation_p(inLangPath, &inAppTranslator); +#ifdef GTA5SYNC_DEBUG + qDebug() << "externalLangIndex" << externalLangIndex << "internalLangIndex" << currentLangIndex; + qDebug() << "externalEnglishMode" << externalEnglishMode << "internalEnglishMode" << isEnglishMode; +#endif + if ((trLoadSuccess && externalLangIndex > currentLangIndex) || (trLoadSuccess && externalEnglishMode && !isEnglishMode)) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "installInternalTranslation"; +#endif + app->installTranslator(&inAppTranslator); + if (loadQtTranslation_p(exLangPath, &exQtTranslator)) { + app->installTranslator(&exQtTranslator); + } + else if (loadQtTranslation_p(inLangPath, &inQtTranslator)) { + app->installTranslator(&inQtTranslator); + } +#if QT_VERSION >= 0x060000 + QLocale::setDefault(QLocale(currentLanguage)); +#else + QLocale::setDefault(currentLanguage); +#endif + isLangLoaded = true; + } + else { +#ifdef GTA5SYNC_DEBUG + qDebug() << "installExternalTranslation"; +#endif + isEnglishMode = externalEnglishMode; + currentLanguage = externalLanguageStr; + app->installTranslator(&exAppTranslator); + if (loadQtTranslation_p(exLangPath, &exQtTranslator)) { + app->installTranslator(&exQtTranslator); + } + else if (loadQtTranslation_p(inLangPath, &inQtTranslator)) { + app->installTranslator(&inQtTranslator); + } +#if QT_VERSION >= 0x060000 + QLocale::setDefault(QLocale(currentLanguage)); +#else + QLocale::setDefault(currentLanguage); +#endif + isLangLoaded = true; + } + } + else if (!isLangLoaded) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadInSystemLanguage"; +#endif + trLoadSuccess = loadSystemTranslation_p(inLangPath, &inAppTranslator); + if (trLoadSuccess) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "installInternalTranslation"; +#endif + app->installTranslator(&inAppTranslator); + if (loadQtTranslation_p(exLangPath, &exQtTranslator)) { + app->installTranslator(&exQtTranslator); + } + else if (loadQtTranslation_p(inLangPath, &inQtTranslator)) { + app->installTranslator(&inQtTranslator); + } +#if QT_VERSION >= 0x060000 + QLocale::setDefault(QLocale(currentLanguage)); +#else + QLocale::setDefault(currentLanguage); +#endif + isLangLoaded = true; + } + else if (!trLoadSuccess) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "fallbackToDefaultApplicationLanguage"; +#endif + currentLanguage = "en_GB"; + if (loadQtTranslation_p(exLangPath, &exQtTranslator)) { + app->installTranslator(&exQtTranslator); + } + else if (loadQtTranslation_p(inLangPath, &inQtTranslator)) { + app->installTranslator(&inQtTranslator); + } +#if QT_VERSION >= 0x060000 + QLocale::setDefault(QLocale(currentLanguage)); +#else + QLocale::setDefault(currentLanguage); +#endif + isLangLoaded = true; + } + } +#else // New qconf loading method + bool trLoadSuccess; + if (isUserLanguageSystem_p()) { + trLoadSuccess = loadSystemTranslation_p(inLangPath, &inAppTranslator); + } + else { + trLoadSuccess = loadUserTranslation_p(inLangPath, &inAppTranslator); + } + if (!trLoadSuccess && !isUserLanguageSystem_p()) { + trLoadSuccess = loadSystemTranslation_p(inLangPath, &inAppTranslator); + } + if (trLoadSuccess) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "installTranslation" << currentLanguage; +#endif + app->installTranslator(&inAppTranslator); + if (loadQtTranslation_p(exLangPath, &exQtTranslator)) { + app->installTranslator(&exQtTranslator); + } + else if (loadQtTranslation_p(inLangPath, &inQtTranslator)) { + app->installTranslator(&inQtTranslator); + } +#if QT_VERSION >= 0x060000 + QLocale::setDefault(QLocale(currentLanguage)); +#else + QLocale::setDefault(currentLanguage); +#endif + isLangLoaded = true; + } +#endif +} + +QStringList TranslationClass::listTranslations(const QString &langPath) +{ + QDir langDir; + langDir.setNameFilters(QStringList("gta5sync_*.qm")); + langDir.setPath(langPath); + QStringList availableLanguages; + for (const QString &lang : langDir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::NoSort)) { + availableLanguages << QString(lang).remove("gta5sync_").remove(".qm"); + } + return availableLanguages; +} + +QStringList TranslationClass::listAreaTranslations() +{ + QDir langDir; + langDir.setNameFilters(QStringList("global.*.ini")); + langDir.setPath(":/global"); + QStringList availableLanguages; + for (const QString &lang : langDir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::NoSort)) { + availableLanguages << QString(lang).remove("global.").remove(".ini"); + } + return availableLanguages; +} + +bool TranslationClass::loadSystemTranslation_p(const QString &langPath, QTranslator *appTranslator) +{ +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadSystemTranslation_p"; +#endif + int currentLangCounter = 0; + for (const QString &languageName : QLocale::system().uiLanguages()) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguage" << languageName; +#endif + const QStringList langList = QString(languageName).replace("-","_").split("_"); + if (langList.length() == 2) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFile" << QString(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm"); +#endif + if (QFile::exists(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm")) { + if (appTranslator->load(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm")) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm"); +#endif + isEnglishMode = false; + currentLanguage = languageName; + currentLangIndex = currentLangCounter; + return true; + } + } +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFile" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm"); +#endif + if (QFile::exists(langPath % "/gta5sync_" % langList.at(0) % ".qm")) { + if (appTranslator->load(langPath % "/gta5sync_" % langList.at(0) % ".qm")) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm"); +#endif + isEnglishMode = false; + currentLanguage = languageName; + currentLangIndex = currentLangCounter; + return true; + } + else if (langList.at(0) == "en") { +#ifdef GTA5SYNC_DEBUG + qDebug() << "languageEnglishMode index" << currentLangCounter; +#endif + isEnglishMode = true; + currentLanguage = languageName; + currentLangIndex = currentLangCounter; + return true; + } + } + else if (langList.at(0) == "en") { +#ifdef GTA5SYNC_DEBUG + qDebug() << "languageEnglishMode index" << currentLangCounter; +#endif + isEnglishMode = true; + currentLanguage = languageName; + currentLangIndex = currentLangCounter; + return true; + } + } + else if (langList.length() == 1) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFile" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm"); +#endif + if (QFile::exists(langPath % "/gta5sync_" % langList.at(0) % ".qm")) { + if (appTranslator->load(langPath % "/gta5sync_" % langList.at(0) % ".qm")) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm"); +#endif + isEnglishMode = false; + currentLanguage = languageName; + currentLangIndex = currentLangCounter; + return true; + } + } + } +#ifdef GTA5SYNC_DEBUG + qDebug() << "currentLangCounter bump"; +#endif + currentLangCounter++; + } + return false; +} + +bool TranslationClass::loadUserTranslation_p(const QString &langPath, QTranslator *appTranslator) +{ +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadUserTranslation_p"; +#endif + const QString languageName = userLanguage; + const QStringList langList = QString(languageName).replace("-","_").split("_"); + if (langList.length() == 2) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFile" << QString(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm"); +#endif + if (QFile::exists(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm")) { + if (appTranslator->load(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm")) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % "/gta5sync_" % langList.at(0) % "_" % langList.at(1) % ".qm"); +#endif + currentLanguage = languageName; + return true; + } + } +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFile" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm"); +#endif + if (QFile::exists(langPath % "/gta5sync_" % langList.at(0) % ".qm")) { + if (appTranslator->load(langPath % "/gta5sync_" % langList.at(0) % ".qm")) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm"); +#endif + currentLanguage = languageName; + return true; + } + } + } + else if (langList.length() == 1) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFile" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm"); +#endif + if (QFile::exists(langPath % "/gta5sync_" % langList.at(0) % ".qm")) { + if (appTranslator->load(langPath % "/gta5sync_" % langList.at(0) % ".qm")) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % "/gta5sync_" % langList.at(0) % ".qm"); +#endif + currentLanguage = languageName; + return true; + } + } + } + return false; +} + +bool TranslationClass::loadQtTranslation_p(const QString &langPath, QTranslator *qtTranslator) +{ +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadQtTranslation_p" << currentLanguage; +#endif + const QString languageName = currentLanguage; + const QStringList langList = QString(languageName).replace("-","_").split("_"); + if (langList.length() == 2) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFile" << QString(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % "_" % langList.at(1) % ".qm"); +#endif + if (QFile::exists(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % "_" % langList.at(1) % ".qm")) { + if (qtTranslator->load(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % "_" % langList.at(1) % ".qm")) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % "_" % langList.at(1) % ".qm"); +#endif + return true; + } + } +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFile" << QString(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm"); +#endif + if (QFile::exists(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm")) { + if (qtTranslator->load(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm")) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm"); +#endif + return true; + } + } + } + else if (langList.length() == 1) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFile" << QString(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm"); +#endif + if (QFile::exists(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm")) { + if (qtTranslator->load(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm")) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "loadLanguageFileSuccess" << QString(langPath % QDir::separator() % QtBaseTranslationFormat % langList.at(0) % ".qm"); +#endif + return true; + } + } + } + return false; +} + +bool TranslationClass::isUserLanguageSystem_p() +{ + return (userLanguage == "System" || userLanguage.trimmed().isEmpty()); +} + +QString TranslationClass::getCurrentAreaLanguage() +{ + const QStringList areaTranslations = listAreaTranslations(); + if (userAreaLanguage == "Auto" || userAreaLanguage.trimmed().isEmpty()) { + const GameLanguage gameLanguage = AppEnv::getGameLanguage(AppEnv::getGameVersion()); + if (gameLanguage == GameLanguage::Undefined) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "autoAreaLanguageModeInterface"; +#endif + QString langCode = QString(currentLanguage).replace("-", "_"); + if (areaTranslations.contains(langCode)) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "autoAreaLanguageSelected" << langCode; +#endif + return langCode; + } + else if (langCode.contains("_")) { + langCode = langCode.split("_").at(0); + if (!areaTranslations.contains(langCode)) + goto outputDefaultLanguage; +#ifdef GTA5SYNC_DEBUG + qDebug() << "autoAreaLanguageSelected" << langCode; +#endif + return langCode; + } + } + else { +#ifdef GTA5SYNC_DEBUG + qDebug() << "autoAreaLanguageModeGame"; +#endif + QString langCode = AppEnv::gameLanguageToString(gameLanguage).replace("-", "_"); + if (areaTranslations.contains(langCode)) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "autoAreaLanguageSelected" << langCode; +#endif + return langCode; + } + else if (langCode.contains("_")) { + langCode = langCode.split("_").at(0); + if (!areaTranslations.contains(langCode)) + goto outputDefaultLanguage; +#ifdef GTA5SYNC_DEBUG + qDebug() << "autoAreaLanguageSelected" << langCode; +#endif + return langCode; + } + } + } + else if (areaTranslations.contains(userAreaLanguage)) { +#ifdef GTA5SYNC_DEBUG + qDebug() << "userAreaLanguageSelected" << userAreaLanguage; +#endif + return userAreaLanguage; + } + else if (userAreaLanguage.contains("_")) { + const QString langCode = QString(userAreaLanguage).replace("-", "_").split("_").at(0); + if (!areaTranslations.contains(langCode)) + goto outputDefaultLanguage; +#ifdef GTA5SYNC_DEBUG + qDebug() << "userAreaLanguageSelected" << langCode; +#endif + return langCode; + } +outputDefaultLanguage: +#ifdef GTA5SYNC_DEBUG + qDebug() << "defaultAreaLanguageSelected"; +#endif + return "en"; +} + +QString TranslationClass::getCurrentLanguage() +{ + return currentLanguage; +} + +bool TranslationClass::isLanguageLoaded() +{ + return isLangLoaded; +} + +void TranslationClass::unloadTranslation(QApplication *app) +{ + if (isLangLoaded) { +#ifndef GTA5SYNC_QCONF + app->removeTranslator(&exAppTranslator); + app->removeTranslator(&exQtTranslator); + app->removeTranslator(&inAppTranslator); + app->removeTranslator(&inQtTranslator); +#else + app->removeTranslator(&inAppTranslator); + app->removeTranslator(&exQtTranslator); +#endif + currentLangIndex = 0; + currentLanguage = QString(); + QLocale::setDefault(QLocale::c()); + isEnglishMode = false; + isLangLoaded = false; + } +#ifdef _MSC_VER // Fix dumb Microsoft compiler warning + Q_UNUSED(app) +#endif +} + +QString TranslationClass::getCountryCode(QLocale::Country country) +{ + const QList locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, country); + if (!locales.isEmpty()) { + const QStringList localeStrList = locales.at(0).name().split("_"); + if (localeStrList.length() >= 2) { + return localeStrList.at(1).toLower(); + } + } + return QString(); +} + +QString TranslationClass::getCountryCode(QLocale locale) +{ + QStringList localeStrList = locale.name().split("_"); + if (localeStrList.length() >= 2) { + return localeStrList.at(1).toLower(); + } + return QString(); +} diff --git a/TranslationClass.h b/TranslationClass.h new file mode 100644 index 0000000..963eaff --- /dev/null +++ b/TranslationClass.h @@ -0,0 +1,67 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef TRANSLATIONCLASS_H +#define TRANSLATIONCLASS_H + +#include +#include +#include +#include +#include +#include + +class TranslationClass : public QObject +{ + Q_OBJECT +public: + static TranslationClass* getInstance() { return &translationClassInstance; } + static QString getCountryCode(QLocale::Country country); + static QString getCountryCode(QLocale locale); + void initUserLanguage(); + void loadTranslation(QApplication *app); + void unloadTranslation(QApplication *app); + static QStringList listTranslations(const QString &langPath); + static QStringList listAreaTranslations(); + QString getCurrentAreaLanguage(); + QString getCurrentLanguage(); + bool isLanguageLoaded(); + +private: + static TranslationClass translationClassInstance; + bool loadSystemTranslation_p(const QString &langPath, QTranslator *appTranslator); + bool loadUserTranslation_p(const QString &langPath, QTranslator *appTranslator); + bool loadQtTranslation_p(const QString &langPath, QTranslator *qtTranslator); + bool isUserLanguageSystem_p(); + QTranslator exAppTranslator; + QTranslator exQtTranslator; + QTranslator inAppTranslator; + QTranslator inQtTranslator; + QString userAreaLanguage; + QString currentLanguage; + QString userLanguage; + int currentLangIndex; + bool isEnglishMode; + bool isLangLoaded; +}; + +extern TranslationClass translationClass; + +#define Translator TranslationClass::getInstance() + +#endif // TRANSLATIONCLASS_H diff --git a/UserInterface.cpp b/UserInterface.cpp old mode 100755 new mode 100644 index b029c6e..a990b51 --- a/UserInterface.cpp +++ b/UserInterface.cpp @@ -1,529 +1,920 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "UserInterface.h" -#include "ui_UserInterface.h" -#include "ProfileInterface.h" -#include "SnapmaticPicture.h" -#include "SidebarGenerator.h" -#include "SavegameDialog.h" -#include "StandardPaths.h" -#include "OptionsDialog.h" -#include "PictureDialog.h" -#include "SavegameData.h" -#include "AboutDialog.h" -#include "IconLoader.h" -#include "AppEnv.h" -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -UserInterface::UserInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent) : - QMainWindow(parent), profileDB(profileDB), crewDB(crewDB), threadDB(threadDB), - ui(new Ui::UserInterface) -{ - ui->setupUi(this); - contentMode = 0; - profileOpen = 0; - profileUI = 0; - ui->menuProfile->setEnabled(false); - ui->actionSelect_profile->setEnabled(false); - ui->actionAbout_gta5sync->setIcon(IconLoader::loadingAppIcon()); - ui->actionAbout_gta5sync->setText(tr("&About %1").arg(GTA5SYNC_APPSTR)); - defaultWindowTitle = tr("%2 - %1").arg("%1", GTA5SYNC_APPSTR); - - this->setWindowTitle(defaultWindowTitle.arg(tr("Select Profile"))); - ui->labVersion->setText(ui->labVersion->text().arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER)); - - if (QIcon::hasThemeIcon("dialog-close")) - { - ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); - } - if (QIcon::hasThemeIcon("preferences-system")) - { -#ifndef Q_OS_MACOS // Setting icon for preferences/settings/options lead to a crash in Mac OS X - ui->actionOptions->setIcon(QIcon::fromTheme("preferences-system")); -#endif - } - if (QIcon::hasThemeIcon("application-exit")) - { -#ifndef Q_OS_MACOS // Setting icon for exit/quit lead to a crash in Mac OS X - ui->actionExit->setIcon(QIcon::fromTheme("application-exit")); -#endif - } - - // DPI calculation - qreal screenRatio = AppEnv::screenRatio(); - resize(625 * screenRatio, 500 * screenRatio); - ui->vlUserInterface->setSpacing(6 * screenRatio); - ui->vlUserInterface->setContentsMargins(9 * screenRatio, 9 * screenRatio, 9 * screenRatio, 9 * screenRatio); -} - -void UserInterface::setupDirEnv() -{ - // settings init - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - - bool folderExists; - GTAV_Folder = AppEnv::getGameFolder(&folderExists); - if (folderExists) - { - QDir::setCurrent(GTAV_Folder); - } - else - { - GTAV_Folder = QFileDialog::getExistingDirectory(this, tr("Select GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly); - if (QFileInfo(GTAV_Folder).exists()) - { - folderExists = true; - QDir::setCurrent(GTAV_Folder); - AppEnv::setGameFolder(GTAV_Folder); - - // First time folder selection save - settings.beginGroup("dir"); - if (settings.value("dir", "").toString().isEmpty()) - { - settings.setValue("dir", GTAV_Folder); - } - settings.endGroup(); - } - } - - // profiles init - settings.beginGroup("Profile"); - QString defaultProfile = settings.value("Default", "").toString(); - - bool contentModeOk; - contentMode = settings.value("ContentMode", 0).toInt(&contentModeOk); - if (contentMode != 0 && contentMode != 1 && contentMode != 2) - { - contentMode = 0; - } - - if (folderExists) - { - QDir GTAV_ProfilesDir; - GTAV_ProfilesFolder = GTAV_Folder + QDir::separator() + "Profiles"; - GTAV_ProfilesDir.setPath(GTAV_ProfilesFolder); - - GTAV_Profiles = GTAV_ProfilesDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::NoSort); - setupProfileUi(); - - if (GTAV_Profiles.length() == 1) - { - openProfile(GTAV_Profiles.at(0)); - } - else if(GTAV_Profiles.contains(defaultProfile)) - { - openProfile(defaultProfile); - } - } - else - { - GTAV_Profiles = QStringList(); - setupProfileUi(); - } - settings.endGroup(); -} - -void UserInterface::setupProfileUi() -{ - qreal screenRatio = AppEnv::screenRatio(); - if (GTAV_Profiles.length() == 0) - { - QPushButton *changeDirBtn = new QPushButton(tr("Select >A V Folder..."), ui->swSelection); - changeDirBtn->setObjectName("cmdChangeDir"); - changeDirBtn->setMinimumSize(0, 40 * screenRatio); - changeDirBtn->setAutoDefault(true); - ui->vlButtons->addWidget(changeDirBtn); - profileBtns.append(changeDirBtn); - - QObject::connect(changeDirBtn, SIGNAL(clicked(bool)), this, SLOT(changeFolder_clicked())); - } - else foreach(const QString >AV_Profile, GTAV_Profiles) - { - QPushButton *profileBtn = new QPushButton(GTAV_Profile, ui->swSelection); - profileBtn->setObjectName(GTAV_Profile); - profileBtn->setMinimumSize(0, 40 * screenRatio); - profileBtn->setAutoDefault(true); - ui->vlButtons->addWidget(profileBtn); - profileBtns.append(profileBtn); - - QObject::connect(profileBtn, SIGNAL(clicked(bool)), this, SLOT(profileButton_clicked())); - } - profileBtns.at(0)->setFocus(); -} - -void UserInterface::changeFolder_clicked() -{ - on_actionSelect_GTA_Folder_triggered(); -} - -void UserInterface::on_cmdReload_clicked() -{ - foreach(QPushButton *profileBtn, profileBtns) - { - ui->vlButtons->removeWidget(profileBtn); - profileBtns.removeAll(profileBtn); - delete profileBtn; - } - setupDirEnv(); -} - -void UserInterface::profileButton_clicked() -{ - QPushButton *profileBtn = (QPushButton*)sender(); - openProfile(profileBtn->objectName()); -} - -void UserInterface::openProfile(QString profileName) -{ - profileOpen = true; - profileUI = new ProfileInterface(profileDB, crewDB, threadDB); - ui->swProfile->addWidget(profileUI); - ui->swProfile->setCurrentWidget(profileUI); - profileUI->setProfileFolder(GTAV_ProfilesFolder + QDir::separator() + profileName, profileName); - profileUI->settingsApplied(contentMode, language); - profileUI->setupProfileInterface(); - QObject::connect(profileUI, SIGNAL(profileClosed()), this, SLOT(closeProfile())); - QObject::connect(profileUI, SIGNAL(profileLoaded()), this, SLOT(profileLoaded())); - this->setWindowTitle(defaultWindowTitle.arg(profileName)); -} - -void UserInterface::closeProfile() -{ - if (profileOpen) - { - profileOpen = false; - ui->menuProfile->setEnabled(false); - ui->actionSelect_profile->setEnabled(false); - ui->swProfile->removeWidget(profileUI); - delete profileUI; - } - this->setWindowTitle(defaultWindowTitle.arg(tr("Select Profile"))); -} - -void UserInterface::closeEvent(QCloseEvent *ev) -{ - Q_UNUSED(ev) - threadDB->doEndThread(); -} - -UserInterface::~UserInterface() -{ - foreach (QPushButton *profileBtn, profileBtns) - { - delete profileBtn; - } - delete ui; -} - -void UserInterface::on_actionExit_triggered() -{ - this->close(); -} - -void UserInterface::on_actionSelect_profile_triggered() -{ - closeProfile(); - openSelectProfile(); -} - -void UserInterface::openSelectProfile() -{ - // not needed right now -} - -void UserInterface::on_actionAbout_gta5sync_triggered() -{ - AboutDialog *aboutDialog = new AboutDialog(this); - aboutDialog->setWindowIcon(windowIcon()); - aboutDialog->setModal(true); -#ifdef Q_OS_ANDROID - // Android ... - aboutDialog->showMaximized(); -#else - aboutDialog->show(); -#endif - aboutDialog->exec(); - delete aboutDialog; -} - -void UserInterface::profileLoaded() -{ - ui->menuProfile->setEnabled(true); - ui->actionSelect_profile->setEnabled(true); -} - -void UserInterface::on_actionSelect_all_triggered() -{ - if (profileOpen) - { - profileUI->selectAllWidgets(); - } -} - -void UserInterface::on_actionDeselect_all_triggered() -{ - if (profileOpen) - { - profileUI->deselectAllWidgets(); - } -} - -void UserInterface::on_actionExport_selected_triggered() -{ - if (profileOpen) - { - profileUI->exportSelected(); - } -} - -void UserInterface::on_actionDelete_selected_triggered() -{ - if (profileOpen) - { - profileUI->deleteSelected(); - } -} - -void UserInterface::on_actionOptions_triggered() -{ - OptionsDialog *optionsDialog = new OptionsDialog(profileDB, this); - optionsDialog->setWindowIcon(windowIcon()); - optionsDialog->commitProfiles(GTAV_Profiles); - QObject::connect(optionsDialog, SIGNAL(settingsApplied(int, QString)), this, SLOT(settingsApplied(int, QString))); - - optionsDialog->setModal(true); -#ifdef Q_OS_ANDROID - // Android ... - optionsDialog->showMaximized(); -#else - optionsDialog->show(); -#endif - optionsDialog->exec(); - - delete optionsDialog; -} - -void UserInterface::on_action_Import_triggered() -{ - if (profileOpen) - { - profileUI->importFiles(); - } -} - -void UserInterface::on_actionOpen_File_triggered() -{ - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - settings.beginGroup("FileDialogs"); - -fileDialogPreOpen: - QFileDialog fileDialog(this); - fileDialog.setFileMode(QFileDialog::ExistingFiles); - fileDialog.setViewMode(QFileDialog::Detail); - fileDialog.setAcceptMode(QFileDialog::AcceptOpen); - fileDialog.setOption(QFileDialog::DontUseNativeDialog, false); - fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); - fileDialog.setWindowTitle(tr("Open File...")); - - QStringList filters; - filters << ProfileInterface::tr("All profile files (*.g5e SGTA* PGTA*)"); - filters << ProfileInterface::tr("GTA V Export (*.g5e)"); - filters << ProfileInterface::tr("Savegames files (SGTA*)"); - filters << ProfileInterface::tr("Snapmatic pictures (PGTA*)"); - filters << ProfileInterface::tr("All files (**)"); - fileDialog.setNameFilters(filters); - - QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); - - fileDialog.setSidebarUrls(sidebarUrls); - fileDialog.setDirectory(settings.value("OpenDialogDirectory", StandardPaths::documentsLocation()).toString()); - fileDialog.restoreGeometry(settings.value("OpenDialogGeometry","").toByteArray()); - - if (fileDialog.exec()) - { - QStringList selectedFiles = fileDialog.selectedFiles(); - if (selectedFiles.length() == 1) - { - QString selectedFile = selectedFiles.at(0); - if (!openFile(selectedFile, true)) goto fileDialogPreOpen; - } - } - - settings.setValue("OpenDialogGeometry", fileDialog.saveGeometry()); - settings.setValue("OpenDialogDirectory", fileDialog.directory().absolutePath()); - settings.endGroup(); -} - -bool UserInterface::openFile(QString selectedFile, bool warn) -{ - QString selectedFileName = QFileInfo(selectedFile).fileName(); - if (QFile::exists(selectedFile)) - { - if (selectedFileName.left(4) == "PGTA" || selectedFileName.right(4) == ".g5e") - { - SnapmaticPicture *picture = new SnapmaticPicture(selectedFile); - if (picture->readingPicture()) - { - openSnapmaticFile(picture); - delete picture; - return true; - } - else - { - if (warn) QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("Failed to read Snapmatic picture")); - delete picture; - return false; - } - } - else if (selectedFileName.left(4) == "SGTA") - { - SavegameData *savegame = new SavegameData(selectedFile); - if (savegame->readingSavegame()) - { - openSavegameFile(savegame); - delete savegame; - return true; - } - else - { - if (warn) QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("Failed to read Savegame file")); - delete savegame; - return false; - } - } - else - { - SnapmaticPicture *picture = new SnapmaticPicture(selectedFile); - SavegameData *savegame = new SavegameData(selectedFile); - if (picture->readingPicture()) - { - delete savegame; - openSnapmaticFile(picture); - delete picture; - return true; - } - else if (savegame->readingSavegame()) - { - delete picture; - openSavegameFile(savegame); - delete savegame; - return true; - } - else - { - delete savegame; - delete picture; - if (warn) QMessageBox::warning(this, tr("Open File"), tr("Can't open %1 because of not valid file format").arg("\""+selectedFileName+"\"")); - return false; - } - } - } - if (warn) QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("No valid file is selected")); - return false; -} - -void UserInterface::openSnapmaticFile(SnapmaticPicture *picture) -{ - PictureDialog picDialog(profileDB, crewDB, this); - picDialog.setSnapmaticPicture(picture, true); - picDialog.setModal(true); - - int crewID = picture->getSnapmaticProperties().crewID; - if (crewID != 0) { crewDB->addCrew(crewID); } - - QObject::connect(threadDB, SIGNAL(playerNameFound(int, QString)), profileDB, SLOT(setPlayerName(int, QString))); - QObject::connect(threadDB, SIGNAL(playerNameUpdated()), &picDialog, SLOT(playerNameUpdated())); - -#ifdef Q_OS_ANDROID - // Android optimization should be put here - picDialog.showMaximized(); -#else - picDialog.show(); - picDialog.setMinimumSize(picDialog.size()); - picDialog.setMaximumSize(picDialog.size()); -#endif - - picDialog.exec(); -} - -void UserInterface::openSavegameFile(SavegameData *savegame) -{ - SavegameDialog sgdDialog(this); - sgdDialog.setSavegameData(savegame, savegame->getSavegameFileName(), true); - sgdDialog.setModal(true); -#ifdef Q_OS_ANDROID - // Android optimization should be put here - sgdDialog.showMaximized(); -#else - sgdDialog.show(); -#endif - sgdDialog.exec(); -} - -void UserInterface::settingsApplied(int _contentMode, QString _language) -{ - language = _language; - contentMode = _contentMode; - if (profileOpen) - { - profileUI->settingsApplied(contentMode, language); - } -} - -void UserInterface::on_actionSelect_GTA_Folder_triggered() -{ - QString GTAV_Folder_Temp = QFileDialog::getExistingDirectory(this, tr("Select GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly); - if (QFileInfo(GTAV_Folder_Temp).exists()) - { - GTAV_Folder = GTAV_Folder_Temp; - QDir::setCurrent(GTAV_Folder); - AppEnv::setGameFolder(GTAV_Folder); - on_cmdReload_clicked(); - } -} - -void UserInterface::on_action_Enable_In_game_triggered() -{ - if (profileOpen) - { - profileUI->enableSelected(); - } -} - -void UserInterface::on_action_Disable_In_game_triggered() -{ - if (profileOpen) - { - profileUI->disableSelected(); - } -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "UserInterface.h" +#include "ui_UserInterface.h" +#include "ProfileInterface.h" +#include "SnapmaticPicture.h" +#include "SidebarGenerator.h" +#include "SavegameDialog.h" +#include "StandardPaths.h" +#include "OptionsDialog.h" +#include "PictureDialog.h" +#include "SavegameData.h" +#include "AboutDialog.h" +#include "IconLoader.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GTA5SYNC_DONATE +#ifdef GTA5SYNC_DONATE_ADDRESSES +#include +#include +#include +#include "QrCode.h" +using namespace qrcodegen; +#endif +#endif + +#ifdef GTA5SYNC_MOTD +UserInterface::UserInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, MessageThread *threadMessage, QWidget *parent) : + QMainWindow(parent), profileDB(profileDB), crewDB(crewDB), threadDB(threadDB), threadMessage(threadMessage), + ui(new Ui::UserInterface) + #else +UserInterface::UserInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent) : + QMainWindow(parent), profileDB(profileDB), crewDB(crewDB), threadDB(threadDB), + ui(new Ui::UserInterface) + #endif +{ + ui->setupUi(this); + contentMode = 0; + profileOpen = 0; + profileUI = 0; + ui->menuProfile->setEnabled(false); + ui->actionSelect_profile->setEnabled(false); + ui->actionAbout_gta5sync->setIcon(IconLoader::loadingAppIcon()); +#ifdef Q_OS_MAC + ui->actionAbout_gta5sync->setText(QApplication::translate("MAC_APPLICATION_MENU", "About %1").arg(GTA5SYNC_APPSTR)); + ui->actionOptions->setText(QApplication::translate("MAC_APPLICATION_MENU", "Preferences...")); +#else + ui->actionAbout_gta5sync->setText(tr("&About %1").arg(GTA5SYNC_APPSTR)); +#endif + ui->cmdClose->setToolTip(ui->cmdClose->toolTip().arg(GTA5SYNC_APPSTR)); + defaultWindowTitle = tr("%2 - %1").arg("%1", GTA5SYNC_APPSTR); + + setWindowTitle(defaultWindowTitle.arg(tr("Select Profile"))); + QString appVersion = QApplication::applicationVersion(); + const char* literalBuildType = GTA5SYNC_BUILDTYPE; +#ifdef GTA5SYNC_COMMIT + if ((strcmp(literalBuildType, REL_BUILDTYPE) != 0) && !appVersion.contains("-")) + appVersion = appVersion % "-" % GTA5SYNC_COMMIT; +#endif + ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, appVersion)); + + // Set Icon for Close Button + if (QIcon::hasThemeIcon("dialog-close")) { + ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close")); + } + else if (QIcon::hasThemeIcon("gtk-close")) { + ui->cmdClose->setIcon(QIcon::fromTheme("gtk-close")); + } + + // Set Icon for Reload Button + if (QIcon::hasThemeIcon("view-refresh")) { + ui->cmdReload->setIcon(QIcon::fromTheme("view-refresh")); + } + else if (QIcon::hasThemeIcon("reload")) { + ui->cmdReload->setIcon(QIcon::fromTheme("reload")); + } + + // Set Icon for Choose GTA V Folder Menu Item + if (QIcon::hasThemeIcon("document-open-folder")) { + ui->actionSelect_GTA_Folder->setIcon(QIcon::fromTheme("document-open-folder")); + } + else if (QIcon::hasThemeIcon("gtk-directory")) { + ui->actionSelect_GTA_Folder->setIcon(QIcon::fromTheme("gtk-directory")); + } + + // Set Icon for Open File Menu Item + if (QIcon::hasThemeIcon("document-open")) { + ui->actionOpen_File->setIcon(QIcon::fromTheme("document-open")); + } + + // Set Icon for Close Profile Menu Item + if (QIcon::hasThemeIcon("dialog-close")) { + ui->actionSelect_profile->setIcon(QIcon::fromTheme("dialog-close")); + } + else if (QIcon::hasThemeIcon("gtk-close")) { + ui->actionSelect_profile->setIcon(QIcon::fromTheme("gtk-close")); + } + + // Set Icon for Exit Menu Item + if (QIcon::hasThemeIcon("application-exit")) { +#ifndef Q_OS_MACOS // Setting icon for exit/quit lead to a crash in Mac OS X + ui->actionExit->setIcon(QIcon::fromTheme("application-exit")); +#endif + } + + // Set Icon for Preferences Menu Item + if (QIcon::hasThemeIcon("preferences-system")) { +#ifndef Q_OS_MACOS // Setting icon for preferences/settings/options lead to a crash in Mac OS X + ui->actionOptions->setIcon(QIcon::fromTheme("preferences-system")); +#endif + } + else if (QIcon::hasThemeIcon("configure")) { +#ifndef Q_OS_MACOS // Setting icon for preferences/settings/options lead to a crash in Mac OS X + ui->actionOptions->setIcon(QIcon::fromTheme("configure")); +#endif + } + + // Set Icon for Profile Import Menu Item + if (QIcon::hasThemeIcon("document-import")) { + ui->action_Import->setIcon(QIcon::fromTheme("document-import")); + } + else if (QIcon::hasThemeIcon("document-open")) { + ui->action_Import->setIcon(QIcon::fromTheme("document-open")); + } + + // Set Icon for Profile Export Menu Item + if (QIcon::hasThemeIcon("document-export")) { + ui->actionExport_selected->setIcon(QIcon::fromTheme("document-export")); + } + else if (QIcon::hasThemeIcon("document-save")) { + ui->actionExport_selected->setIcon(QIcon::fromTheme("document-save")); + } + + // Set Icon for Profile Remove Menu Item + if (QIcon::hasThemeIcon("remove")) { + ui->actionDelete_selected->setIcon(QIcon::fromTheme("remove")); + } + +#ifdef GTA5SYNC_DONATE +#ifdef GTA5SYNC_DONATE_ADDRESSES + donateAction = new QAction(tr("&Donate"), this); + if (QIcon::hasThemeIcon("help-donate")) { + donateAction->setIcon(QIcon::fromTheme("help-donate")); + } + else if (QIcon::hasThemeIcon("taxes-finances")) { + donateAction->setIcon(QIcon::fromTheme("taxes-finances")); + } + else { + donateAction->setIcon(QIcon(":/img/donate.svgz")); + } + ui->menuHelp->insertAction(ui->actionAbout_gta5sync, donateAction); + QObject::connect(donateAction, &QAction::triggered, this, [=](){ + QDialog *donateDialog = new QDialog(this); + donateDialog->setWindowTitle(QString("%1 - %2").arg(GTA5SYNC_APPSTR, tr("Donate"))); +#if QT_VERSION >= 0x050900 + donateDialog->setWindowFlag(Qt::WindowContextHelpButtonHint, false); +#else + donateDialog->setWindowFlags(donateDialog->windowFlags()^Qt::WindowContextHelpButtonHint); +#endif + QVBoxLayout *donateLayout = new QVBoxLayout; + donateDialog->setLayout(donateLayout); + QLabel *methodsLabel = new QLabel(QString("%1").arg(tr("Donation methods").toHtmlEscaped()), donateDialog); + methodsLabel->setWordWrap(true); + donateLayout->addWidget(methodsLabel); + QHBoxLayout *currencyLayout = new QHBoxLayout; + donateLayout->addLayout(currencyLayout); + const QStringList addressList = QString::fromUtf8(GTA5SYNC_DONATE_ADDRESSES).split(','); + for (const QString &address : addressList) { + const QStringList addressList = address.split(':'); + if (addressList.length() == 2) { + const QString currency = addressList.at(0); + const QString address = addressList.at(1); + QString currencyStr = currency; + const QString strPath = QString(":/donate/%1.str").arg(currency); + if (QFile::exists(strPath)) { + QFile strFile(strPath); + if (strFile.open(QIODevice::ReadOnly)) { + currencyStr = QString::fromUtf8(strFile.readAll()); + strFile.close(); + } + } + const QString iconPath = QString(":/donate/%1.svgz").arg(currency); + QPushButton *currencyButton = new QPushButton(currencyStr, donateDialog); + currencyButton->setToolTip(currencyStr); + if (QFile::exists(iconPath)) { + currencyButton->setIconSize(QSize(32, 32)); + currencyButton->setIcon(QIcon(iconPath)); + } + currencyLayout->addWidget(currencyButton); + QObject::connect(currencyButton, &QPushButton::pressed, donateDialog, [=](){ + QDialog *addressDialog = new QDialog(donateDialog); + addressDialog->setWindowTitle(currencyStr); +#if QT_VERSION >= 0x050900 + addressDialog->setWindowFlag(Qt::WindowContextHelpButtonHint, false); +#else + addressDialog->setWindowFlags(donateDialog->windowFlags()^Qt::WindowContextHelpButtonHint); +#endif + QVBoxLayout *addressLayout = new QVBoxLayout; + addressDialog->setLayout(addressLayout); + QLabel *addressLabel = new QLabel(address, addressDialog); + addressLabel->setAlignment(Qt::AlignCenter); + addressLabel->setTextFormat(Qt::PlainText); + addressLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); + addressLayout->addWidget(addressLabel); + QHBoxLayout *qrLayout = new QHBoxLayout; + qrLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); + QrCode qr = QrCode::encodeText(address.toUtf8().constData(), QrCode::Ecc::MEDIUM); + const std::string svgString = qr.toSvgString(0); + QSvgRenderer svgRenderer(QByteArray::fromRawData(svgString.c_str(), svgString.size())); + qreal screenRatioPR = AppEnv::screenRatioPR(); + const QSize widgetSize = QSize(200, 200); + const QSize pixmapSize = widgetSize * screenRatioPR; + QPixmap qrPixmap(pixmapSize); + qrPixmap.fill(Qt::white); + QPainter qrPainter(&qrPixmap); + svgRenderer.render(&qrPainter, QRectF(QPointF(0, 0), pixmapSize)); + qrPainter.end(); +#if QT_VERSION >= 0x050600 + qrPixmap.setDevicePixelRatio(screenRatioPR); +#endif + QLabel *qrLabel = new QLabel(addressDialog); + qrLabel->setFixedSize(widgetSize); + qrLabel->setPixmap(qrPixmap); + qrLayout->addWidget(qrLabel); + qrLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); + addressLayout->addLayout(qrLayout); + QHBoxLayout *buttonLayout = new QHBoxLayout; + buttonLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); + QPushButton *copyAddressButton = new QPushButton(tr("&Copy"), addressDialog); + if (QIcon::hasThemeIcon("edit-copy")) { + copyAddressButton->setIcon(QIcon::fromTheme("edit-copy")); + } + QObject::connect(copyAddressButton, &QPushButton::pressed, addressDialog, [=](){ + QApplication::clipboard()->setText(address); + }); + buttonLayout->addWidget(copyAddressButton); + QPushButton *closeButton = new QPushButton(tr("&Close"), addressDialog); + if (QIcon::hasThemeIcon("dialog-close")) { + closeButton->setIcon(QIcon::fromTheme("dialog-close")); + } + else if (QIcon::hasThemeIcon("gtk-close")) { + closeButton->setIcon(QIcon::fromTheme("gtk-close")); + } + closeButton->setDefault(true); + buttonLayout->addWidget(closeButton); + buttonLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); + addressLayout->addLayout(buttonLayout); + QObject::connect(closeButton, &QPushButton::clicked, addressDialog, &QDialog::accept); + QObject::connect(addressDialog, &QDialog::finished, addressDialog, &QDialog::deleteLater); + QTimer::singleShot(0, addressDialog, [=](){ + addressDialog->setFocus(); + }); + addressDialog->open(); + addressDialog->setFixedSize(addressDialog->size()); + }); + } + } + QHBoxLayout *buttonLayout = new QHBoxLayout; + buttonLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); + QPushButton *closeButton = new QPushButton(donateDialog); + closeButton->setText(tr("&Close")); + if (QIcon::hasThemeIcon("dialog-close")) { + closeButton->setIcon(QIcon::fromTheme("dialog-close")); + } + else if (QIcon::hasThemeIcon("gtk-close")) { + closeButton->setIcon(QIcon::fromTheme("gtk-close")); + } + closeButton->setDefault(true); + buttonLayout->addWidget(closeButton); + donateLayout->addLayout(buttonLayout); + QObject::connect(closeButton, &QPushButton::clicked, donateDialog, &QDialog::accept); + QObject::connect(donateDialog, &QDialog::finished, donateDialog, &QDialog::deleteLater); + QTimer::singleShot(0, donateDialog, [=](){ + donateDialog->setFocus(); + }); + donateDialog->open(); + donateDialog->setFixedSize(donateDialog->size()); + }); +#endif +#endif + + // DPI calculation + qreal screenRatio = AppEnv::screenRatio(); +#ifndef Q_QS_ANDROID + resize(625 * screenRatio, 500 * screenRatio); +#endif + ui->vlUserInterface->setSpacing(6 * screenRatio); + ui->vlUserInterface->setContentsMargins(9 * screenRatio, 9 * screenRatio, 9 * screenRatio, 9 * screenRatio); +} + +void UserInterface::setupDirEnv(bool showFolderDialog) +{ + // settings init + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + + bool folderExists; + GTAV_Folder = AppEnv::getGameFolder(&folderExists); + if (folderExists) { + QDir::setCurrent(GTAV_Folder); + } + else if (showFolderDialog) { + GTAV_Folder = QFileDialog::getExistingDirectory(this, tr("Select GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly); + if (QDir(GTAV_Folder).exists()) { + folderExists = true; + QDir::setCurrent(GTAV_Folder); + AppEnv::setGameFolder(GTAV_Folder); + + // First time folder selection save + settings.beginGroup("dir"); + if (settings.value("dir", "").toString().isEmpty()) { + settings.setValue("dir", GTAV_Folder); + } + settings.endGroup(); + } + } + + // profiles init + settings.beginGroup("Profile"); + QString defaultProfile = settings.value("Default", "").toString(); + + contentMode = settings.value("ContentMode", 0).toInt(); + if (contentMode == 1) { + contentMode = 21; + } + else if (contentMode != 10 && contentMode != 11 && contentMode != 20 && contentMode != 21) { + contentMode = 20; + } + + if (folderExists) { + QDir GTAV_ProfilesDir; + GTAV_ProfilesFolder = GTAV_Folder % "/Profiles"; + GTAV_ProfilesDir.setPath(GTAV_ProfilesFolder); + + GTAV_Profiles = GTAV_ProfilesDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::NoSort); + setupProfileUi(); + + if (GTAV_Profiles.length() == 1) { + openProfile(GTAV_Profiles.at(0)); + } + else if(GTAV_Profiles.contains(defaultProfile)) { + openProfile(defaultProfile); + } + } + else { + GTAV_Profiles = QStringList(); + setupProfileUi(); + } + settings.endGroup(); +} + +void UserInterface::setupProfileUi() +{ + qreal screenRatio = AppEnv::screenRatio(); + if (GTAV_Profiles.isEmpty()) { + QPushButton *changeDirBtn = new QPushButton(tr("Select >A V Folder..."), ui->swSelection); + changeDirBtn->setObjectName("cmdChangeDir"); + changeDirBtn->setMinimumSize(0, 40 * screenRatio); + changeDirBtn->setAutoDefault(true); + ui->vlButtons->addWidget(changeDirBtn); + profileBtns += changeDirBtn; + + QObject::connect(changeDirBtn, SIGNAL(clicked(bool)), this, SLOT(changeFolder_clicked())); + } + else for (const QString >AV_Profile : GTAV_Profiles) { + QPushButton *profileBtn = new QPushButton(GTAV_Profile, ui->swSelection); + profileBtn->setObjectName(GTAV_Profile); + profileBtn->setMinimumSize(0, 40 * screenRatio); + profileBtn->setAutoDefault(true); + ui->vlButtons->addWidget(profileBtn); + profileBtns += profileBtn; + + QObject::connect(profileBtn, SIGNAL(clicked(bool)), this, SLOT(profileButton_clicked())); + } + profileBtns.at(0)->setFocus(); +} + +void UserInterface::changeFolder_clicked() +{ + on_actionSelect_GTA_Folder_triggered(); +} + +void UserInterface::on_cmdReload_clicked() +{ + for (QPushButton *profileBtn : profileBtns) { + ui->vlButtons->removeWidget(profileBtn); + delete profileBtn; + } + profileBtns.clear(); + setupDirEnv(); +} + +void UserInterface::profileButton_clicked() +{ + QPushButton *profileBtn = (QPushButton*)sender(); + openProfile(profileBtn->objectName()); +} + +void UserInterface::openProfile(const QString &profileName_) +{ + profileOpen = true; + profileName = profileName_; + profileUI = new ProfileInterface(profileDB, crewDB, threadDB); + ui->swProfile->addWidget(profileUI); + ui->swProfile->setCurrentWidget(profileUI); + profileUI->setProfileFolder(GTAV_ProfilesFolder % QDir::separator() % profileName, profileName); + profileUI->settingsApplied(contentMode, false); + profileUI->setupProfileInterface(); + QObject::connect(profileUI, SIGNAL(profileClosed()), this, SLOT(closeProfile())); + QObject::connect(profileUI, SIGNAL(profileLoaded()), this, SLOT(profileLoaded())); + setWindowTitle(defaultWindowTitle.arg(profileName)); +} + +void UserInterface::closeProfile() +{ + if (profileOpen) { + closeProfile_p(); + } + setWindowTitle(defaultWindowTitle.arg(tr("Select Profile"))); +} + +void UserInterface::closeProfile_p() +{ + profileOpen = false; + profileName.clear(); + profileName.squeeze(); + ui->menuProfile->setEnabled(false); + ui->actionSelect_profile->setEnabled(false); + ui->swProfile->removeWidget(profileUI); + profileUI->disconnect(); + delete profileUI; +} + +void UserInterface::closeEvent(QCloseEvent *ev) +{ + Q_UNUSED(ev) +#ifdef GTA5SYNC_MOTD + threadMessage->terminateThread(); +#else + threadDB->terminateThread(); +#endif +} + +UserInterface::~UserInterface() +{ + if (profileOpen) + closeProfile_p(); + for (QPushButton *profileBtn : profileBtns) { + delete profileBtn; + } + profileBtns.clear(); + delete ui; +} + +void UserInterface::on_actionExit_triggered() +{ + close(); +} + +void UserInterface::on_actionSelect_profile_triggered() +{ + closeProfile(); + openSelectProfile(); +} + +void UserInterface::openSelectProfile() +{ + // not needed right now +} + +void UserInterface::on_actionAbout_gta5sync_triggered() +{ + AboutDialog *aboutDialog = new AboutDialog(this); + aboutDialog->setWindowIcon(windowIcon()); + aboutDialog->setModal(true); +#ifdef Q_OS_ANDROID + // Android ... + aboutDialog->showMaximized(); +#else + aboutDialog->show(); +#endif + aboutDialog->exec(); + delete aboutDialog; +} + +void UserInterface::profileLoaded() +{ + ui->menuProfile->setEnabled(true); + ui->actionSelect_profile->setEnabled(true); +} + +void UserInterface::on_actionSelect_all_triggered() +{ + if (profileOpen) + profileUI->selectAllWidgets(); +} + +void UserInterface::on_actionDeselect_all_triggered() +{ + if (profileOpen) + profileUI->deselectAllWidgets(); +} + +void UserInterface::on_actionExport_selected_triggered() +{ + if (profileOpen) + profileUI->exportSelected(); +} + +void UserInterface::on_actionDelete_selected_triggered() +{ + if (profileOpen) + profileUI->deleteSelected(); +} + +void UserInterface::on_actionOptions_triggered() +{ + OptionsDialog *optionsDialog = new OptionsDialog(profileDB, this); + optionsDialog->setWindowIcon(windowIcon()); + optionsDialog->commitProfiles(GTAV_Profiles); + QObject::connect(optionsDialog, SIGNAL(settingsApplied(int, bool)), this, SLOT(settingsApplied(int, bool))); + + optionsDialog->setModal(true); +#ifdef Q_OS_ANDROID + // Android ... + optionsDialog->showMaximized(); +#else + optionsDialog->show(); +#endif + optionsDialog->exec(); + + delete optionsDialog; +} + +void UserInterface::on_action_Import_triggered() +{ + if (profileOpen) + profileUI->importFiles(); +} + +void UserInterface::on_actionOpen_File_triggered() +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("FileDialogs"); + +fileDialogPreOpen: + QFileDialog fileDialog(this); + fileDialog.setFileMode(QFileDialog::ExistingFiles); + fileDialog.setViewMode(QFileDialog::Detail); + fileDialog.setAcceptMode(QFileDialog::AcceptOpen); + fileDialog.setOption(QFileDialog::DontUseNativeDialog, false); +#if QT_VERSION >= 0x050900 + fileDialog.setWindowFlag(Qt::WindowContextHelpButtonHint, false); +#else + fileDialog.setWindowFlags(fileDialog.windowFlags()^Qt::WindowContextHelpButtonHint); +#endif + fileDialog.setWindowTitle(tr("Open File...")); + + QStringList filters; + filters << ProfileInterface::tr("All profile files (*.g5e SGTA* PGTA*)"); + filters << ProfileInterface::tr("GTA V Export (*.g5e)"); + filters << ProfileInterface::tr("Savegames files (SGTA*)"); + filters << ProfileInterface::tr("Snapmatic pictures (PGTA*)"); + filters << ProfileInterface::tr("All files (**)"); + fileDialog.setNameFilters(filters); + + QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); + + fileDialog.setSidebarUrls(sidebarUrls); + fileDialog.setDirectory(settings.value("OpenDialogDirectory", StandardPaths::documentsLocation()).toString()); + fileDialog.restoreGeometry(settings.value("OpenDialogGeometry","").toByteArray()); + + if (fileDialog.exec()) { + QStringList selectedFiles = fileDialog.selectedFiles(); + if (selectedFiles.length() == 1) { + QString selectedFile = selectedFiles.at(0); + if (!openFile(selectedFile, true)) goto fileDialogPreOpen; + } + } + + settings.setValue("OpenDialogGeometry", fileDialog.saveGeometry()); + settings.setValue("OpenDialogDirectory", fileDialog.directory().absolutePath()); + settings.endGroup(); +} + +bool UserInterface::openFile(QString selectedFile, bool warn) +{ + QString selectedFileName = QFileInfo(selectedFile).fileName(); + if (QFile::exists(selectedFile)) { + if (selectedFileName.left(4) == "PGTA" || selectedFileName.right(4) == ".g5e") { + SnapmaticPicture *picture = new SnapmaticPicture(selectedFile); + if (picture->readingPicture()) { + openSnapmaticFile(picture); + delete picture; + return true; + } + else { + if (warn) + QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("Failed to read Snapmatic picture")); + delete picture; + return false; + } + } + else if (selectedFileName.left(4) == "SGTA") { + SavegameData *savegame = new SavegameData(selectedFile); + if (savegame->readingSavegame()) { + openSavegameFile(savegame); + delete savegame; + return true; + } + else { + if (warn) + QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("Failed to read Savegame file")); + delete savegame; + return false; + } + } + else { + SnapmaticPicture *picture = new SnapmaticPicture(selectedFile); + SavegameData *savegame = new SavegameData(selectedFile); + if (picture->readingPicture()) { + delete savegame; + openSnapmaticFile(picture); + delete picture; + return true; + } + else if (savegame->readingSavegame()) { + delete picture; + openSavegameFile(savegame); + delete savegame; + return true; + } + else { + delete savegame; + delete picture; + if (warn) + QMessageBox::warning(this, tr("Open File"), tr("Can't open %1 because of not valid file format").arg("\""+selectedFileName+"\"")); + return false; + } + } + } + if (warn) + QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("No valid file is selected")); + return false; +} + +void UserInterface::openSnapmaticFile(SnapmaticPicture *picture) +{ + PictureDialog picDialog(profileDB, crewDB, this); + picDialog.setSnapmaticPicture(picture, true); + picDialog.setModal(true); + + int crewID = picture->getSnapmaticProperties().crewID; + if (crewID != 0) + crewDB->addCrew(crewID); + + QObject::connect(threadDB, SIGNAL(crewNameUpdated()), &picDialog, SLOT(crewNameUpdated())); + QObject::connect(threadDB, SIGNAL(playerNameUpdated()), &picDialog, SLOT(playerNameUpdated())); + +#ifdef Q_OS_ANDROID + // Android optimization should be put here + picDialog.showMaximized(); +#else + picDialog.show(); + picDialog.setMinimumSize(picDialog.size()); + picDialog.setMaximumSize(picDialog.size()); +#endif + + picDialog.exec(); +} + +void UserInterface::openSavegameFile(SavegameData *savegame) +{ + SavegameDialog sgdDialog(this); + sgdDialog.setSavegameData(savegame, savegame->getSavegameFileName(), true); + sgdDialog.setModal(true); +#ifdef Q_OS_ANDROID + // Android optimization should be put here + sgdDialog.showMaximized(); +#else + sgdDialog.show(); +#endif + sgdDialog.exec(); +} + +void UserInterface::settingsApplied(int _contentMode, bool languageChanged) +{ + if (languageChanged) { + retranslateUi(); + } + contentMode = _contentMode; + if (profileOpen) { + profileUI->settingsApplied(contentMode, languageChanged); + } +} + +#ifdef GTA5SYNC_MOTD +void UserInterface::messagesArrived(const QJsonObject &object) +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("Messages"); + QJsonObject::const_iterator it = object.constBegin(); + QJsonObject::const_iterator end = object.constEnd(); + QStringList messages; + while (it != end) { + const QString key = it.key(); + const QJsonValue value = it.value(); + bool uintOk; + uint messageId = key.toUInt(&uintOk); + if (uintOk && value.isString()) { + const QString valueStr = value.toString(); + settings.setValue(QString::number(messageId), valueStr); + messages << valueStr; + } + it++; + } + settings.endGroup(); + if (!messages.isEmpty()) + showMessages(messages); +} + +void UserInterface::showMessages(const QStringList messages) +{ + QDialog *messageDialog = new QDialog(this); + messageDialog->setWindowTitle(tr("%1 - Messages").arg(GTA5SYNC_APPSTR)); +#if QT_VERSION >= 0x050900 + messageDialog->setWindowFlag(Qt::WindowContextHelpButtonHint, false); +#else + messageDialog->setWindowFlags(messageDialog->windowFlags()^Qt::WindowContextHelpButtonHint); +#endif + QVBoxLayout *messageLayout = new QVBoxLayout; + messageDialog->setLayout(messageLayout); + QStackedWidget *stackWidget = new QStackedWidget(messageDialog); + for (const QString message : messages) { + QLabel *messageLabel = new QLabel(messageDialog); + messageLabel->setText(message); + messageLabel->setWordWrap(true); + stackWidget->addWidget(messageLabel); + } + messageLayout->addWidget(stackWidget); + QHBoxLayout *buttonLayout = new QHBoxLayout; + QPushButton *backButton = new QPushButton(messageDialog); + QPushButton *nextButton = new QPushButton(messageDialog); + if (QIcon::hasThemeIcon("go-previous") && QIcon::hasThemeIcon("go-next") && QIcon::hasThemeIcon("list-add")) { + backButton->setIcon(QIcon::fromTheme("go-previous")); + nextButton->setIcon(QIcon::fromTheme("go-next")); + } + else { + backButton->setIcon(QIcon(AppEnv::getImagesFolder() % "/back.svgz")); + nextButton->setIcon(QIcon(AppEnv::getImagesFolder() % "/next.svgz")); + } + backButton->setEnabled(false); + if (stackWidget->count() <= 1) { + nextButton->setEnabled(false); + } + buttonLayout->addWidget(backButton); + buttonLayout->addWidget(nextButton); + buttonLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); + QPushButton *closeButton = new QPushButton(messageDialog); + closeButton->setText(tr("&Close")); + if (QIcon::hasThemeIcon("dialog-close")) { + closeButton->setIcon(QIcon::fromTheme("dialog-close")); + } + else if (QIcon::hasThemeIcon("gtk-close")) { + closeButton->setIcon(QIcon::fromTheme("gtk-close")); + } + buttonLayout->addWidget(closeButton); + messageLayout->addLayout(buttonLayout); + QObject::connect(backButton, &QPushButton::clicked, [stackWidget,backButton,nextButton,closeButton]() { + int index = stackWidget->currentIndex(); + if (index > 0) { + index--; + stackWidget->setCurrentIndex(index); + nextButton->setEnabled(true); + if (index > 0) { + backButton->setEnabled(true); + } + else { + backButton->setEnabled(false); + closeButton->setFocus(); + } + } + }); + QObject::connect(nextButton, &QPushButton::clicked, [stackWidget,backButton,nextButton,closeButton]() { + int index = stackWidget->currentIndex(); + if (index < stackWidget->count()-1) { + index++; + stackWidget->setCurrentIndex(index); + backButton->setEnabled(true); + if (index < stackWidget->count()-1) { + nextButton->setEnabled(true); + } + else { + nextButton->setEnabled(false); + closeButton->setFocus(); + } + } + }); + QObject::connect(closeButton, &QPushButton::clicked, messageDialog, &QDialog::accept); + QObject::connect(messageDialog, &QDialog::finished, messageDialog, &QDialog::deleteLater); + QTimer::singleShot(0, closeButton, [=](){ + closeButton->setFocus(); + }); + messageDialog->show(); +} + +void UserInterface::updateCacheId(uint cacheId) +{ + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("Messages"); + settings.setValue("CacheId", cacheId); + settings.endGroup(); +} +#endif + +void UserInterface::on_actionSelect_GTA_Folder_triggered() +{ + QString GTAV_Folder_Temp = QFileDialog::getExistingDirectory(this, tr("Select GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly); + if (QDir(GTAV_Folder_Temp).exists()) { + if (profileOpen) { + closeProfile_p(); + } + GTAV_Folder = GTAV_Folder_Temp; + QDir::setCurrent(GTAV_Folder); + AppEnv::setGameFolder(GTAV_Folder); + on_cmdReload_clicked(); + } +} + +void UserInterface::on_action_Enable_In_game_triggered() +{ + if (profileOpen) + profileUI->enableSelected(); +} + +void UserInterface::on_action_Disable_In_game_triggered() +{ + if (profileOpen) + profileUI->disableSelected(); +} + +void UserInterface::retranslateUi() +{ + ui->retranslateUi(this); +#ifdef GTA5SYNC_DONATE +#ifdef GTA5SYNC_DONATE_ADDRESSES + donateAction->setText(tr("&Donate")); +#endif +#endif +#ifdef Q_OS_MAC + ui->actionAbout_gta5sync->setText(QApplication::translate("MAC_APPLICATION_MENU", "About %1").arg(GTA5SYNC_APPSTR)); + ui->actionOptions->setText(QApplication::translate("MAC_APPLICATION_MENU", "Preferences...")); +#else + ui->actionAbout_gta5sync->setText(tr("&About %1").arg(GTA5SYNC_APPSTR)); +#endif + QString appVersion = QApplication::applicationVersion(); + const char* literalBuildType = GTA5SYNC_BUILDTYPE; +#ifdef GTA5SYNC_COMMIT + if ((strcmp(literalBuildType, REL_BUILDTYPE) != 0) && !appVersion.contains("-")) + appVersion = appVersion % "-" % GTA5SYNC_COMMIT; +#endif + ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, appVersion)); + if (profileOpen) { + setWindowTitle(defaultWindowTitle.arg(profileName)); + } + else { + setWindowTitle(defaultWindowTitle.arg(tr("Select Profile"))); + } +} + +void UserInterface::on_actionQualify_as_Avatar_triggered() +{ + profileUI->massTool(MassTool::Qualify); +} + +void UserInterface::on_actionChange_Players_triggered() +{ + profileUI->massTool(MassTool::Players); +} + +void UserInterface::on_actionSet_Crew_triggered() +{ + profileUI->massTool(MassTool::Crew); +} + +void UserInterface::on_actionSet_Title_triggered() +{ + profileUI->massTool(MassTool::Title); +} diff --git a/UserInterface.h b/UserInterface.h old mode 100755 new mode 100644 index 028cf6e..12abc90 --- a/UserInterface.h +++ b/UserInterface.h @@ -1,93 +1,122 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef USERINTERFACE_H -#define USERINTERFACE_H - -#include "SnapmaticPicture.h" -#include "ProfileInterface.h" -#include "ProfileDatabase.h" -#include "DatabaseThread.h" -#include "CrewDatabase.h" -#include "SavegameData.h" -#include -#include -#include -#include - -namespace Ui { -class UserInterface; -} - -class UserInterface : public QMainWindow -{ - Q_OBJECT -public: - explicit UserInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent = 0); - void setupDirEnv(); - ~UserInterface(); - -private slots: - void closeProfile(); - void profileLoaded(); - void changeFolder_clicked(); - void profileButton_clicked(); - void on_cmdReload_clicked(); - void on_actionExit_triggered(); - void on_actionSelect_profile_triggered(); - void on_actionAbout_gta5sync_triggered(); - void on_actionSelect_all_triggered(); - void on_actionDeselect_all_triggered(); - void on_actionExport_selected_triggered(); - void on_actionDelete_selected_triggered(); - void on_actionOptions_triggered(); - void on_action_Import_triggered(); - void on_actionOpen_File_triggered(); - void on_actionSelect_GTA_Folder_triggered(); - void on_action_Enable_In_game_triggered(); - void on_action_Disable_In_game_triggered(); - void settingsApplied(int contentMode, QString language); - -protected: - void closeEvent(QCloseEvent *ev); - -private: - ProfileDatabase *profileDB; - CrewDatabase *crewDB; - DatabaseThread *threadDB; - Ui::UserInterface *ui; - ProfileInterface *profileUI; - QList profileBtns; - bool profileOpen; - int contentMode; - QString language; - QString defaultWindowTitle; - QString GTAV_Folder; - QString GTAV_ProfilesFolder; - QStringList GTAV_Profiles; - void setupProfileUi(); - void openProfile(QString profileName); - void openSelectProfile(); - - // Open File - bool openFile(QString selectedFile, bool warn = true); - void openSavegameFile(SavegameData *savegame); - void openSnapmaticFile(SnapmaticPicture *picture); -}; - -#endif // USERINTERFACE_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef USERINTERFACE_H +#define USERINTERFACE_H + +#include "SnapmaticPicture.h" +#include "ProfileInterface.h" +#include "ProfileDatabase.h" +#include "DatabaseThread.h" +#include "CrewDatabase.h" +#include "SavegameData.h" +#include +#include +#include +#include +#include + +#ifdef GTA5SYNC_MOTD +#include "MessageThread.h" +#endif + +namespace Ui { +class UserInterface; +} + +class UserInterface : public QMainWindow +{ + Q_OBJECT +public: +#ifdef GTA5SYNC_MOTD + explicit UserInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, MessageThread *messageThread, QWidget *parent = 0); +#else + explicit UserInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, DatabaseThread *threadDB, QWidget *parent = 0); +#endif + void setupDirEnv(bool showFolderDialog = true); + ~UserInterface(); + +private slots: + void closeProfile(); + void profileLoaded(); + void changeFolder_clicked(); + void profileButton_clicked(); + void on_cmdReload_clicked(); + void on_actionExit_triggered(); + void on_actionSelect_profile_triggered(); + void on_actionAbout_gta5sync_triggered(); + void on_actionSelect_all_triggered(); + void on_actionDeselect_all_triggered(); + void on_actionExport_selected_triggered(); + void on_actionDelete_selected_triggered(); + void on_actionOptions_triggered(); + void on_action_Import_triggered(); + void on_actionOpen_File_triggered(); + void on_actionSelect_GTA_Folder_triggered(); + void on_action_Enable_In_game_triggered(); + void on_action_Disable_In_game_triggered(); + void on_actionQualify_as_Avatar_triggered(); + void on_actionChange_Players_triggered(); + void on_actionSet_Crew_triggered(); + void on_actionSet_Title_triggered(); + void settingsApplied(int contentMode, bool languageChanged); +#ifdef GTA5SYNC_MOTD + void messagesArrived(const QJsonObject &object); + void showMessages(const QStringList messages); + void updateCacheId(uint cacheId); +#endif + +protected: + void closeEvent(QCloseEvent *ev); + +private: + ProfileDatabase *profileDB; + CrewDatabase *crewDB; + DatabaseThread *threadDB; +#ifdef GTA5SYNC_MOTD + MessageThread *threadMessage; +#endif +#ifdef GTA5SYNC_DONATE +#ifdef GTA5SYNC_DONATE_ADDRESSES + QAction *donateAction; +#endif +#endif + Ui::UserInterface *ui; + ProfileInterface *profileUI; + QList profileBtns; + QString profileName; + bool profileOpen; + int contentMode; + QString language; + QString defaultWindowTitle; + QString GTAV_Folder; + QString GTAV_ProfilesFolder; + QStringList GTAV_Profiles; + void setupProfileUi(); + void openProfile(const QString &profileName); + void closeProfile_p(); + void openSelectProfile(); + void retranslateUi(); + + // Open File + bool openFile(QString selectedFile, bool warn = true); + void openSavegameFile(SavegameData *savegame); + void openSnapmaticFile(SnapmaticPicture *picture); +}; + +#endif // USERINTERFACE_H diff --git a/UserInterface.ui b/UserInterface.ui old mode 100755 new mode 100644 index 5a34fce..7927dcc --- a/UserInterface.ui +++ b/UserInterface.ui @@ -1,348 +1,396 @@ - - - UserInterface - - - - 0 - 0 - 625 - 500 - - - - - 625 - 500 - - - - %2 - %1 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - - - 6 - - - 9 - - - 9 - - - 9 - - - 9 - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - Select profile - - - Qt::AlignCenter - - - true - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - %1 %2 - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - &Reload - - - true - - - - - - - - 0 - 0 - - - - &Close - - - true - - - - - - - - - - - - - - - 0 - 0 - 625 - 21 - - - - - &File - - - - - - - - - - &Help - - - - - - &Edit - - - - - - &Profile - - - - &Selection visibility - - - - - - - - - - - - - - - - - - - - - &About %1 - - - Ctrl+P - - - - - &Exit - - - Exit - - - Ctrl+Q - - - - - Close &Profile - - - Ctrl+End - - - - - &Settings - - - Ctrl+S - - - - - Select &All - - - Ctrl+A - - - - - &Deselect All - - - Ctrl+D - - - - - &Export selected... - - - Ctrl+E - - - - - &Remove selected - - - Ctrl+Del - - - - - &Import files... - - - Ctrl+I - - - - - &Open File... - - - Ctrl+O - - - - - Select &GTA V Folder... - - - Select GTA V Folder... - - - Ctrl+G - - - - - Show In-gam&e - - - Shift+E - - - - - Hi&de In-game - - - Shift+D - - - - - - - cmdClose - clicked() - UserInterface - close() - - - 572 - 476 - - - 312 - 249 - - - - - + + + UserInterface + + + + 0 + 0 + 625 + 500 + + + + + 625 + 500 + + + + %2 - %1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Select profile + + + Qt::AlignCenter + + + true + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + %1 %2 + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Reload profile overview + + + &Reload + + + true + + + + + + + + 0 + 0 + + + + Close %1 + + + &Close + + + true + + + + + + + + + + + + + + + 0 + 0 + 625 + 23 + + + + + &File + + + + + + + + + + &Help + + + + + + &Edit + + + + + + &Profile + + + + &Selection visibility + + + + + + + Selection &mass tools + + + + + + + + + + + + + + + + + + + + + + + + &About %1 + + + Ctrl+P + + + + + &Exit + + + Exit + + + Ctrl+Q + + + + + Close &Profile + + + Ctrl+End + + + + + &Settings + + + Ctrl+S + + + + + Select &All + + + Ctrl+A + + + + + &Deselect All + + + Ctrl+D + + + + + &Export selected... + + + Ctrl+E + + + + + &Remove selected + + + Ctrl+Del + + + + + &Import files... + + + Ctrl+I + + + + + &Open File... + + + Ctrl+O + + + + + Select &GTA V Folder... + + + Select GTA V Folder... + + + Ctrl+G + + + + + Show In-gam&e + + + Shift+E + + + + + Hi&de In-game + + + Shift+D + + + + + Change &Title... + + + Shift+T + + + + + Change &Crew... + + + Shift+C + + + + + &Qualify as Avatar + + + Shift+Q + + + + + Change &Players... + + + Shift+P + + + + + + + cmdClose + clicked() + UserInterface + close() + + + 572 + 476 + + + 312 + 249 + + + + + diff --git a/anpro/QrCode.cpp b/anpro/QrCode.cpp new file mode 100644 index 0000000..6d67ee9 --- /dev/null +++ b/anpro/QrCode.cpp @@ -0,0 +1,862 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "QrCode.h" + +using std::int8_t; +using std::uint8_t; +using std::size_t; +using std::vector; + + +namespace qrcodegen { + +QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : + modeBits(mode) { + numBitsCharCount[0] = cc0; + numBitsCharCount[1] = cc1; + numBitsCharCount[2] = cc2; +} + + +int QrSegment::Mode::getModeBits() const { + return modeBits; +} + + +int QrSegment::Mode::numCharCountBits(int ver) const { + return numBitsCharCount[(ver + 7) / 17]; +} + + +const QrSegment::Mode QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); +const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); +const QrSegment::Mode QrSegment::Mode::BYTE (0x4, 8, 16, 16); +const QrSegment::Mode QrSegment::Mode::KANJI (0x8, 8, 10, 12); +const QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0); + + +QrSegment QrSegment::makeBytes(const vector &data) { + if (data.size() > static_cast(INT_MAX)) + throw std::length_error("Data too long"); + BitBuffer bb; + for (uint8_t b : data) + bb.appendBits(b, 8); + return QrSegment(Mode::BYTE, static_cast(data.size()), std::move(bb)); +} + + +QrSegment QrSegment::makeNumeric(const char *digits) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *digits != '\0'; digits++, charCount++) { + char c = *digits; + if (c < '0' || c > '9') + throw std::domain_error("String contains non-numeric characters"); + accumData = accumData * 10 + (c - '0'); + accumCount++; + if (accumCount == 3) { + bb.appendBits(static_cast(accumData), 10); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + bb.appendBits(static_cast(accumData), accumCount * 3 + 1); + return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); +} + + +QrSegment QrSegment::makeAlphanumeric(const char *text) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *text != '\0'; text++, charCount++) { + const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); + if (temp == nullptr) + throw std::domain_error("String contains unencodable characters in alphanumeric mode"); + accumData = accumData * 45 + static_cast(temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + bb.appendBits(static_cast(accumData), 11); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + bb.appendBits(static_cast(accumData), 6); + return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); +} + + +vector QrSegment::makeSegments(const char *text) { + // Select the most efficient segment encoding automatically + vector result; + if (*text == '\0'); // Leave result empty + else if (isNumeric(text)) + result.push_back(makeNumeric(text)); + else if (isAlphanumeric(text)) + result.push_back(makeAlphanumeric(text)); + else { + vector bytes; + for (; *text != '\0'; text++) + bytes.push_back(static_cast(*text)); + result.push_back(makeBytes(bytes)); + } + return result; +} + + +QrSegment QrSegment::makeEci(long assignVal) { + BitBuffer bb; + if (assignVal < 0) + throw std::domain_error("ECI assignment value out of range"); + else if (assignVal < (1 << 7)) + bb.appendBits(static_cast(assignVal), 8); + else if (assignVal < (1 << 14)) { + bb.appendBits(2, 2); + bb.appendBits(static_cast(assignVal), 14); + } else if (assignVal < 1000000L) { + bb.appendBits(6, 3); + bb.appendBits(static_cast(assignVal), 21); + } else + throw std::domain_error("ECI assignment value out of range"); + return QrSegment(Mode::ECI, 0, std::move(bb)); +} + + +QrSegment::QrSegment(Mode md, int numCh, const std::vector &dt) : + mode(md), + numChars(numCh), + data(dt) { + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + + +QrSegment::QrSegment(Mode md, int numCh, std::vector &&dt) : + mode(md), + numChars(numCh), + data(std::move(dt)) { + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + + +int QrSegment::getTotalBits(const vector &segs, int version) { + int result = 0; + for (const QrSegment &seg : segs) { + int ccbits = seg.mode.numCharCountBits(version); + if (seg.numChars >= (1L << ccbits)) + return -1; // The segment's length doesn't fit the field's bit width + if (4 + ccbits > INT_MAX - result) + return -1; // The sum will overflow an int type + result += 4 + ccbits; + if (seg.data.size() > static_cast(INT_MAX - result)) + return -1; // The sum will overflow an int type + result += static_cast(seg.data.size()); + } + return result; +} + + +bool QrSegment::isAlphanumeric(const char *text) { + for (; *text != '\0'; text++) { + if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) + return false; + } + return true; +} + + +bool QrSegment::isNumeric(const char *text) { + for (; *text != '\0'; text++) { + char c = *text; + if (c < '0' || c > '9') + return false; + } + return true; +} + + +QrSegment::Mode QrSegment::getMode() const { + return mode; +} + + +int QrSegment::getNumChars() const { + return numChars; +} + + +const std::vector &QrSegment::getData() const { + return data; +} + + +const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + + + +int QrCode::getFormatBits(Ecc ecl) { + switch (ecl) { + case Ecc::LOW : return 1; + case Ecc::MEDIUM : return 0; + case Ecc::QUARTILE: return 3; + case Ecc::HIGH : return 2; + default: throw std::logic_error("Assertion error"); + } +} + + +QrCode QrCode::encodeText(const char *text, Ecc ecl) { + vector segs = QrSegment::makeSegments(text); + return encodeSegments(segs, ecl); +} + + +QrCode QrCode::encodeBinary(const vector &data, Ecc ecl) { + vector segs{QrSegment::makeBytes(data)}; + return encodeSegments(segs, ecl); +} + + +QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, + int minVersion, int maxVersion, int mask, bool boostEcl) { + if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) + throw std::invalid_argument("Invalid value"); + + // Find the minimal version number to use + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = QrSegment::getTotalBits(segs, version); + if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + if (version >= maxVersion) { // All versions in the range could not fit the given data + std::ostringstream sb; + if (dataUsedBits == -1) + sb << "Segment too long"; + else { + sb << "Data length = " << dataUsedBits << " bits, "; + sb << "Max capacity = " << dataCapacityBits << " bits"; + } + throw data_too_long(sb.str()); + } + } + if (dataUsedBits == -1) + throw std::logic_error("Assertion error"); + + // Increase the error correction level while the data still fits in the current version number + for (Ecc newEcl : vector{Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) + ecl = newEcl; + } + + // Concatenate all segments to create the data bit string + BitBuffer bb; + for (const QrSegment &seg : segs) { + bb.appendBits(static_cast(seg.getMode().getModeBits()), 4); + bb.appendBits(static_cast(seg.getNumChars()), seg.getMode().numCharCountBits(version)); + bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); + } + if (bb.size() != static_cast(dataUsedBits)) + throw std::logic_error("Assertion error"); + + // Add terminator and pad up to a byte if applicable + size_t dataCapacityBits = static_cast(getNumDataCodewords(version, ecl)) * 8; + if (bb.size() > dataCapacityBits) + throw std::logic_error("Assertion error"); + bb.appendBits(0, std::min(4, static_cast(dataCapacityBits - bb.size()))); + bb.appendBits(0, (8 - static_cast(bb.size() % 8)) % 8); + if (bb.size() % 8 != 0) + throw std::logic_error("Assertion error"); + + // Pad with alternating bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + + // Pack bits into bytes in big endian + vector dataCodewords(bb.size() / 8); + for (size_t i = 0; i < bb.size(); i++) + dataCodewords[i >> 3] |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); + + // Create the QR Code object + return QrCode(version, ecl, dataCodewords, mask); +} + + +QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int msk) : + // Initialize fields and check arguments + version(ver), + errorCorrectionLevel(ecl) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw std::domain_error("Version value out of range"); + if (msk < -1 || msk > 7) + throw std::domain_error("Mask value out of range"); + size = ver * 4 + 17; + size_t sz = static_cast(size); + modules = vector >(sz, vector(sz)); // Initially all white + isFunction = vector >(sz, vector(sz)); + + // Compute ECC, draw modules + drawFunctionPatterns(); + const vector allCodewords = addEccAndInterleave(dataCodewords); + drawCodewords(allCodewords); + + // Do masking + if (msk == -1) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + applyMask(i); + drawFormatBits(i); + long penalty = getPenaltyScore(); + if (penalty < minPenalty) { + msk = i; + minPenalty = penalty; + } + applyMask(i); // Undoes the mask due to XOR + } + } + if (msk < 0 || msk > 7) + throw std::logic_error("Assertion error"); + this->mask = msk; + applyMask(msk); // Apply the final choice of mask + drawFormatBits(msk); // Overwrite old format bits + + isFunction.clear(); + isFunction.shrink_to_fit(); +} + + +int QrCode::getVersion() const { + return version; +} + + +int QrCode::getSize() const { + return size; +} + + +QrCode::Ecc QrCode::getErrorCorrectionLevel() const { + return errorCorrectionLevel; +} + + +int QrCode::getMask() const { + return mask; +} + + +bool QrCode::getModule(int x, int y) const { + return 0 <= x && x < size && 0 <= y && y < size && module(x, y); +} + + +std::string QrCode::toSvgString(int border) const { + if (border < 0) + throw std::domain_error("Border must be non-negative"); + if (border > INT_MAX / 2 || border * 2 > INT_MAX - size) + throw std::overflow_error("Border too large"); + + std::ostringstream sb; + sb << "\n"; + sb << "\n"; + sb << "\n"; + sb << "\t\n"; + sb << "\t\n"; + sb << "\n"; + return sb.str(); +} + + +void QrCode::drawFunctionPatterns() { + // Draw horizontal and vertical timing patterns + for (int i = 0; i < size; i++) { + setFunctionModule(6, i, i % 2 == 0); + setFunctionModule(i, 6, i % 2 == 0); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + drawFinderPattern(3, 3); + drawFinderPattern(size - 4, 3); + drawFinderPattern(3, size - 4); + + // Draw numerous alignment patterns + const vector alignPatPos = getAlignmentPatternPositions(); + size_t numAlign = alignPatPos.size(); + for (size_t i = 0; i < numAlign; i++) { + for (size_t j = 0; j < numAlign; j++) { + // Don't draw on the three finder corners + if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) + drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); + } + } + + // Draw configuration data + drawFormatBits(0); // Dummy mask value; overwritten later in the constructor + drawVersion(); +} + + +void QrCode::drawFormatBits(int msk) { + // Calculate error correction code and pack bits + int data = getFormatBits(errorCorrectionLevel) << 3 | msk; // errCorrLvl is uint2, msk is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + int bits = (data << 10 | rem) ^ 0x5412; // uint15 + if (bits >> 15 != 0) + throw std::logic_error("Assertion error"); + + // Draw first copy + for (int i = 0; i <= 5; i++) + setFunctionModule(8, i, getBit(bits, i)); + setFunctionModule(8, 7, getBit(bits, 6)); + setFunctionModule(8, 8, getBit(bits, 7)); + setFunctionModule(7, 8, getBit(bits, 8)); + for (int i = 9; i < 15; i++) + setFunctionModule(14 - i, 8, getBit(bits, i)); + + // Draw second copy + for (int i = 0; i < 8; i++) + setFunctionModule(size - 1 - i, 8, getBit(bits, i)); + for (int i = 8; i < 15; i++) + setFunctionModule(8, size - 15 + i, getBit(bits, i)); + setFunctionModule(8, size - 8, true); // Always black +} + + +void QrCode::drawVersion() { + if (version < 7) + return; + + // Calculate error correction code and pack bits + int rem = version; // version is uint6, in the range [7, 40] + for (int i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + long bits = static_cast(version) << 12 | rem; // uint18 + if (bits >> 18 != 0) + throw std::logic_error("Assertion error"); + + // Draw two copies + for (int i = 0; i < 18; i++) { + bool bit = getBit(bits, i); + int a = size - 11 + i % 3; + int b = i / 3; + setFunctionModule(a, b, bit); + setFunctionModule(b, a, bit); + } +} + + +void QrCode::drawFinderPattern(int x, int y) { + for (int dy = -4; dy <= 4; dy++) { + for (int dx = -4; dx <= 4; dx++) { + int dist = std::max(std::abs(dx), std::abs(dy)); // Chebyshev/infinity norm + int xx = x + dx, yy = y + dy; + if (0 <= xx && xx < size && 0 <= yy && yy < size) + setFunctionModule(xx, yy, dist != 2 && dist != 4); + } + } +} + + +void QrCode::drawAlignmentPattern(int x, int y) { + for (int dy = -2; dy <= 2; dy++) { + for (int dx = -2; dx <= 2; dx++) + setFunctionModule(x + dx, y + dy, std::max(std::abs(dx), std::abs(dy)) != 1); + } +} + + +void QrCode::setFunctionModule(int x, int y, bool isBlack) { + size_t ux = static_cast(x); + size_t uy = static_cast(y); + modules .at(uy).at(ux) = isBlack; + isFunction.at(uy).at(ux) = true; +} + + +bool QrCode::module(int x, int y) const { + return modules.at(static_cast(y)).at(static_cast(x)); +} + + +vector QrCode::addEccAndInterleave(const vector &data) const { + if (data.size() != static_cast(getNumDataCodewords(version, errorCorrectionLevel))) + throw std::invalid_argument("Invalid argument"); + + // Calculate parameter numbers + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast(errorCorrectionLevel)][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK [static_cast(errorCorrectionLevel)][version]; + int rawCodewords = getNumRawDataModules(version) / 8; + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockLen = rawCodewords / numBlocks; + + // Split data into blocks and append ECC to each block + vector > blocks; + const vector rsDiv = reedSolomonComputeDivisor(blockEccLen); + for (int i = 0, k = 0; i < numBlocks; i++) { + vector dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); + k += static_cast(dat.size()); + const vector ecc = reedSolomonComputeRemainder(dat, rsDiv); + if (i < numShortBlocks) + dat.push_back(0); + dat.insert(dat.end(), ecc.cbegin(), ecc.cend()); + blocks.push_back(std::move(dat)); + } + + // Interleave (not concatenate) the bytes from every block into a single sequence + vector result; + for (size_t i = 0; i < blocks.at(0).size(); i++) { + for (size_t j = 0; j < blocks.size(); j++) { + // Skip the padding byte in short blocks + if (i != static_cast(shortBlockLen - blockEccLen) || j >= static_cast(numShortBlocks)) + result.push_back(blocks.at(j).at(i)); + } + } + if (result.size() != static_cast(rawCodewords)) + throw std::logic_error("Assertion error"); + return result; +} + + +void QrCode::drawCodewords(const vector &data) { + if (data.size() != static_cast(getNumRawDataModules(version) / 8)) + throw std::invalid_argument("Invalid argument"); + + size_t i = 0; // Bit index into the data + // Do the funny zigzag scan + for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (int vert = 0; vert < size; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + size_t x = static_cast(right - j); // Actual x coordinate + bool upward = ((right + 1) & 2) == 0; + size_t y = static_cast(upward ? size - 1 - vert : vert); // Actual y coordinate + if (!isFunction.at(y).at(x) && i < data.size() * 8) { + modules.at(y).at(x) = getBit(data.at(i >> 3), 7 - static_cast(i & 7)); + i++; + } + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/white by the constructor and are left unchanged by this method + } + } + } + if (i != data.size() * 8) + throw std::logic_error("Assertion error"); +} + + +void QrCode::applyMask(int msk) { + if (msk < 0 || msk > 7) + throw std::domain_error("Mask value out of range"); + size_t sz = static_cast(size); + for (size_t y = 0; y < sz; y++) { + for (size_t x = 0; x < sz; x++) { + bool invert; + switch (msk) { + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (x / 3 + y / 2) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + default: throw std::logic_error("Assertion error"); + } + modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); + } + } +} + + +long QrCode::getPenaltyScore() const { + long result = 0; + + // Adjacent modules in row having same color, and finder-like patterns + for (int y = 0; y < size; y++) { + bool runColor = false; + int runX = 0; + std::array runHistory = {}; + for (int x = 0; x < size; x++) { + if (module(x, y) == runColor) { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } else { + finderPenaltyAddHistory(runX, runHistory); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; + runColor = module(x, y); + runX = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3; + } + // Adjacent modules in column having same color, and finder-like patterns + for (int x = 0; x < size; x++) { + bool runColor = false; + int runY = 0; + std::array runHistory = {}; + for (int y = 0; y < size; y++) { + if (module(x, y) == runColor) { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } else { + finderPenaltyAddHistory(runY, runHistory); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; + runColor = module(x, y); + runY = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3; + } + + // 2*2 blocks of modules having same color + for (int y = 0; y < size - 1; y++) { + for (int x = 0; x < size - 1; x++) { + bool color = module(x, y); + if ( color == module(x + 1, y) && + color == module(x, y + 1) && + color == module(x + 1, y + 1)) + result += PENALTY_N2; + } + } + + // Balance of black and white modules + int black = 0; + for (const vector &row : modules) { + for (bool color : row) { + if (color) + black++; + } + } + int total = size * size; // Note that size is odd, so black/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% + int k = static_cast((std::abs(black * 20L - total * 10L) + total - 1) / total) - 1; + result += k * PENALTY_N4; + return result; +} + + +vector QrCode::getAlignmentPatternPositions() const { + if (version == 1) + return vector(); + else { + int numAlign = version / 7 + 2; + int step = (version == 32) ? 26 : + (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; + vector result; + for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) + result.insert(result.begin(), pos); + result.insert(result.begin(), 6); + return result; + } +} + + +int QrCode::getNumRawDataModules(int ver) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw std::domain_error("Version number out of range"); + int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 36; + } + if (!(208 <= result && result <= 29648)) + throw std::logic_error("Assertion error"); + return result; +} + + +int QrCode::getNumDataCodewords(int ver, Ecc ecl) { + return getNumRawDataModules(ver) / 8 + - ECC_CODEWORDS_PER_BLOCK [static_cast(ecl)][ver] + * NUM_ERROR_CORRECTION_BLOCKS[static_cast(ecl)][ver]; +} + + +vector QrCode::reedSolomonComputeDivisor(int degree) { + if (degree < 1 || degree > 255) + throw std::domain_error("Degree out of range"); + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + vector result(static_cast(degree)); + result.at(result.size() - 1) = 1; // Start off with the monomial x^0 + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // and drop the highest monomial term which is always 1x^degree. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + uint8_t root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (size_t j = 0; j < result.size(); j++) { + result.at(j) = reedSolomonMultiply(result.at(j), root); + if (j + 1 < result.size()) + result.at(j) ^= result.at(j + 1); + } + root = reedSolomonMultiply(root, 0x02); + } + return result; +} + + +vector QrCode::reedSolomonComputeRemainder(const vector &data, const vector &divisor) { + vector result(divisor.size()); + for (uint8_t b : data) { // Polynomial division + uint8_t factor = b ^ result.at(0); + result.erase(result.begin()); + result.push_back(0); + for (size_t i = 0; i < result.size(); i++) + result.at(i) ^= reedSolomonMultiply(divisor.at(i), factor); + } + return result; +} + + +uint8_t QrCode::reedSolomonMultiply(uint8_t x, uint8_t y) { + // Russian peasant multiplication + int z = 0; + for (int i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >> 7) * 0x11D); + z ^= ((y >> i) & 1) * x; + } + if (z >> 8 != 0) + throw std::logic_error("Assertion error"); + return static_cast(z); +} + + +int QrCode::finderPenaltyCountPatterns(const std::array &runHistory) const { + int n = runHistory.at(1); + if (n > size * 3) + throw std::logic_error("Assertion error"); + bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 && runHistory.at(4) == n && runHistory.at(5) == n; + return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0) + + (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0); +} + + +int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array &runHistory) const { + if (currentRunColor) { // Terminate black run + finderPenaltyAddHistory(currentRunLength, runHistory); + currentRunLength = 0; + } + currentRunLength += size; // Add white border to final run + finderPenaltyAddHistory(currentRunLength, runHistory); + return finderPenaltyCountPatterns(runHistory); +} + + +void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const { + if (runHistory.at(0) == 0) + currentRunLength += size; // Add white border to initial run + std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end()); + runHistory.at(0) = currentRunLength; +} + + +bool QrCode::getBit(long x, int i) { + return ((x >> i) & 1) != 0; +} + + +/*---- Tables of constants ----*/ + +const int QrCode::PENALTY_N1 = 3; +const int QrCode::PENALTY_N2 = 3; +const int QrCode::PENALTY_N3 = 40; +const int QrCode::PENALTY_N4 = 10; + + +const int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low + {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium + {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile + {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High +}; + +const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High +}; + + +data_too_long::data_too_long(const std::string &msg) : + std::length_error(msg) {} + + + +BitBuffer::BitBuffer() + : std::vector() {} + + +void BitBuffer::appendBits(std::uint32_t val, int len) { + if (len < 0 || len > 31 || val >> len != 0) + throw std::domain_error("Value out of range"); + for (int i = len - 1; i >= 0; i--) // Append bit by bit + this->push_back(((val >> i) & 1) != 0); +} + +} diff --git a/anpro/QrCode.h b/anpro/QrCode.h new file mode 100644 index 0000000..7341e41 --- /dev/null +++ b/anpro/QrCode.h @@ -0,0 +1,556 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#pragma once + +#include +#include +#include +#include +#include + + +namespace qrcodegen { + +/* + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this class are immutable. + * The mid-level way to create a segment is to take the payload data + * and call a static factory function such as QrSegment::makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and call the QrSegment() constructor with appropriate values. + * This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ +class QrSegment final { + + /*---- Public helper enumeration ----*/ + + /* + * Describes how a segment's data bits are interpreted. Immutable. + */ + public: class Mode final { + + /*-- Constants --*/ + + public: static const Mode NUMERIC; + public: static const Mode ALPHANUMERIC; + public: static const Mode BYTE; + public: static const Mode KANJI; + public: static const Mode ECI; + + + /*-- Fields --*/ + + // The mode indicator bits, which is a uint4 value (range 0 to 15). + private: int modeBits; + + // Number of character count bits for three different version ranges. + private: int numBitsCharCount[3]; + + + /*-- Constructor --*/ + + private: Mode(int mode, int cc0, int cc1, int cc2); + + + /*-- Methods --*/ + + /* + * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15). + */ + public: int getModeBits() const; + + /* + * (Package-private) Returns the bit width of the character count field for a segment in + * this mode in a QR Code at the given version number. The result is in the range [0, 16]. + */ + public: int numCharCountBits(int ver) const; + + }; + + + + /*---- Static factory functions (mid level) ----*/ + + /* + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte vectors are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. + */ + public: static QrSegment makeBytes(const std::vector &data); + + + /* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ + public: static QrSegment makeNumeric(const char *digits); + + + /* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + public: static QrSegment makeAlphanumeric(const char *text); + + + /* + * Returns a list of zero or more segments to represent the given text string. The result + * may use various segment modes and switch modes to optimize the length of the bit stream. + */ + public: static std::vector makeSegments(const char *text); + + + /* + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the given assignment value. + */ + public: static QrSegment makeEci(long assignVal); + + + /*---- Public static helper functions ----*/ + + /* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + public: static bool isAlphanumeric(const char *text); + + + /* + * Tests whether the given string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. + */ + public: static bool isNumeric(const char *text); + + + + /*---- Instance fields ----*/ + + /* The mode indicator of this segment. Accessed through getMode(). */ + private: Mode mode; + + /* The length of this segment's unencoded data. Measured in characters for + * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + * Always zero or positive. Not the same as the data's bit length. + * Accessed through getNumChars(). */ + private: int numChars; + + /* The data bits of this segment. Accessed through getData(). */ + private: std::vector data; + + + /*---- Constructors (low level) ----*/ + + /* + * Creates a new QR Code segment with the given attributes and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is copied and stored. + */ + public: QrSegment(Mode md, int numCh, const std::vector &dt); + + + /* + * Creates a new QR Code segment with the given parameters and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is moved and stored. + */ + public: QrSegment(Mode md, int numCh, std::vector &&dt); + + + /*---- Methods ----*/ + + /* + * Returns the mode field of this segment. + */ + public: Mode getMode() const; + + + /* + * Returns the character count field of this segment. + */ + public: int getNumChars() const; + + + /* + * Returns the data bits of this segment. + */ + public: const std::vector &getData() const; + + + // (Package-private) Calculates the number of bits needed to encode the given segments at + // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a + // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX. + public: static int getTotalBits(const std::vector &segs, int version); + + + /*---- Private constant ----*/ + + /* The set of all legal characters in alphanumeric mode, where + * each character value maps to the index in the string. */ + private: static const char *ALPHANUMERIC_CHARSET; + +}; + + + +/* + * A QR Code symbol, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * Instances of this class represent an immutable square grid of black and white cells. + * The class provides static factory functions to create a QR Code from text or binary data. + * The class covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call QrCode::encodeText() or QrCode::encodeBinary(). + * - Mid level: Custom-make the list of segments and call QrCode::encodeSegments(). + * - Low level: Custom-make the array of data codeword bytes (including + * segment headers and final padding, excluding error correction codewords), + * supply the appropriate version number, and call the QrCode() constructor. + * (Note that all ways require supplying the desired error correction level.) + */ +class QrCode final { + + /*---- Public helper enumeration ----*/ + + /* + * The error correction level in a QR Code symbol. + */ + public: enum class Ecc { + LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords + MEDIUM , // The QR Code can tolerate about 15% erroneous codewords + QUARTILE, // The QR Code can tolerate about 25% erroneous codewords + HIGH , // The QR Code can tolerate about 30% erroneous codewords + }; + + + // Returns a value in the range 0 to 3 (unsigned 2-bit integer). + private: static int getFormatBits(Ecc ecl); + + + + /*---- Static factory functions (high level) ----*/ + + /* + * Returns a QR Code representing the given Unicode text string at the given error correction level. + * As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer + * UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible + * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than + * the ecl argument if it can be done without increasing the version. + */ + public: static QrCode encodeText(const char *text, Ecc ecl); + + + /* + * Returns a QR Code representing the given binary data at the given error correction level. + * This function always encodes using the binary segment mode, not any text mode. The maximum number of + * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + */ + public: static QrCode encodeBinary(const std::vector &data, Ecc ecl); + + + /*---- Static factory functions (mid level) ----*/ + + /* + * Returns a QR Code representing the given segments with the given encoding parameters. + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask number is either between 0 to 7 (inclusive) to force that + * mask, or -1 to automatically choose an appropriate mask (which may be slow). + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a mid-level API; the high-level API is encodeText() and encodeBinary(). + */ + public: static QrCode encodeSegments(const std::vector &segs, Ecc ecl, + int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true); // All optional parameters + + + + /*---- Instance fields ----*/ + + // Immutable scalar parameters: + + /* The version number of this QR Code, which is between 1 and 40 (inclusive). + * This determines the size of this barcode. */ + private: int version; + + /* The width and height of this QR Code, measured in modules, between + * 21 and 177 (inclusive). This is equal to version * 4 + 17. */ + private: int size; + + /* The error correction level used in this QR Code. */ + private: Ecc errorCorrectionLevel; + + /* The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive). + * Even if a QR Code is created with automatic masking requested (mask = -1), + * the resulting object still has a mask value between 0 and 7. */ + private: int mask; + + // Private grids of modules/pixels, with dimensions of size*size: + + // The modules of this QR Code (false = white, true = black). + // Immutable after constructor finishes. Accessed through getModule(). + private: std::vector > modules; + + // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. + private: std::vector > isFunction; + + + + /*---- Constructor (low level) ----*/ + + /* + * Creates a new QR Code with the given version number, + * error correction level, data codeword bytes, and mask number. + * This is a low-level API that most users should not use directly. + * A mid-level API is the encodeSegments() function. + */ + public: QrCode(int ver, Ecc ecl, const std::vector &dataCodewords, int msk); + + + + /*---- Public instance methods ----*/ + + /* + * Returns this QR Code's version, in the range [1, 40]. + */ + public: int getVersion() const; + + + /* + * Returns this QR Code's size, in the range [21, 177]. + */ + public: int getSize() const; + + + /* + * Returns this QR Code's error correction level. + */ + public: Ecc getErrorCorrectionLevel() const; + + + /* + * Returns this QR Code's mask, in the range [0, 7]. + */ + public: int getMask() const; + + + /* + * Returns the color of the module (pixel) at the given coordinates, which is false + * for white or true for black. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (white) is returned. + */ + public: bool getModule(int x, int y) const; + + + /* + * Returns a string of SVG code for an image depicting this QR Code, with the given number + * of border modules. The string always uses Unix newlines (\n), regardless of the platform. + */ + public: std::string toSvgString(int border) const; + + + + /*---- Private helper methods for constructor: Drawing function modules ----*/ + + // Reads this object's version field, and draws and marks all function modules. + private: void drawFunctionPatterns(); + + + // Draws two copies of the format bits (with its own error correction code) + // based on the given mask and this object's error correction level field. + private: void drawFormatBits(int msk); + + + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field, iff 7 <= version <= 40. + private: void drawVersion(); + + + // Draws a 9*9 finder pattern including the border separator, + // with the center module at (x, y). Modules can be out of bounds. + private: void drawFinderPattern(int x, int y); + + + // Draws a 5*5 alignment pattern, with the center module + // at (x, y). All modules must be in bounds. + private: void drawAlignmentPattern(int x, int y); + + + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in bounds. + private: void setFunctionModule(int x, int y, bool isBlack); + + + // Returns the color of the module at the given coordinates, which must be in range. + private: bool module(int x, int y) const; + + + /*---- Private helper methods for constructor: Codewords and masking ----*/ + + // Returns a new byte string representing the given data with the appropriate error correction + // codewords appended to it, based on this object's version and error correction level. + private: std::vector addEccAndInterleave(const std::vector &data) const; + + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code. Function modules need to be marked off before this is called. + private: void drawCodewords(const std::vector &data); + + + // XORs the codeword modules in this QR Code with the given mask pattern. + // The function modules must be marked and the codeword bits must be drawn + // before masking. Due to the arithmetic of XOR, calling applyMask() with + // the same mask value a second time will undo the mask. A final well-formed + // QR Code needs exactly one (not zero, two, etc.) mask applied. + private: void applyMask(int msk); + + + // Calculates and returns the penalty score based on state of this QR Code's current modules. + // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. + private: long getPenaltyScore() const; + + + + /*---- Private helper functions ----*/ + + // Returns an ascending list of positions of alignment patterns for this version number. + // Each position is in the range [0,177), and are used on both the x and y axes. + // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. + private: std::vector getAlignmentPatternPositions() const; + + + // Returns the number of data bits that can be stored in a QR Code of the given version number, after + // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. + // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. + private: static int getNumRawDataModules(int ver); + + + // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any + // QR Code of the given version number and error correction level, with remainder bits discarded. + // This stateless pure function could be implemented as a (40*4)-cell lookup table. + private: static int getNumDataCodewords(int ver, Ecc ecl); + + + // Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be + // implemented as a lookup table over all possible parameter values, instead of as an algorithm. + private: static std::vector reedSolomonComputeDivisor(int degree); + + + // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. + private: static std::vector reedSolomonComputeRemainder(const std::vector &data, const std::vector &divisor); + + + // Returns the product of the two given field elements modulo GF(2^8/0x11D). + // All inputs are valid. This could be implemented as a 256*256 lookup table. + private: static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y); + + + // Can only be called immediately after a white run is added, and + // returns either 0, 1, or 2. A helper function for getPenaltyScore(). + private: int finderPenaltyCountPatterns(const std::array &runHistory) const; + + + // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). + private: int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array &runHistory) const; + + + // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). + private: void finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const; + + + // Returns true iff the i'th bit of x is set to 1. + private: static bool getBit(long x, int i); + + + /*---- Constants and tables ----*/ + + // The minimum version number supported in the QR Code Model 2 standard. + public: static constexpr int MIN_VERSION = 1; + + // The maximum version number supported in the QR Code Model 2 standard. + public: static constexpr int MAX_VERSION = 40; + + + // For use in getPenaltyScore(), when evaluating which mask is best. + private: static const int PENALTY_N1; + private: static const int PENALTY_N2; + private: static const int PENALTY_N3; + private: static const int PENALTY_N4; + + + private: static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; + private: static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; + +}; + + + +/*---- Public exception class ----*/ + +/* + * Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include: + * - Decrease the error correction level if it was greater than Ecc::LOW. + * - If the encodeSegments() function was called with a maxVersion argument, then increase + * it if it was less than QrCode::MAX_VERSION. (This advice does not apply to the other + * factory functions because they search all versions up to QrCode::MAX_VERSION.) + * - Split the text data into better or optimal segments in order to reduce the number of bits required. + * - Change the text or binary data to be shorter. + * - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric). + * - Propagate the error upward to the caller/user. + */ +class data_too_long : public std::length_error { + + public: explicit data_too_long(const std::string &msg); + +}; + + + +/* + * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. + */ +class BitBuffer final : public std::vector { + + /*---- Constructor ----*/ + + // Creates an empty bit buffer (length 0). + public: BitBuffer(); + + + + /*---- Method ----*/ + + // Appends the given number of low-order bits of the given value + // to this buffer. Requires 0 <= len <= 31 and val < 2^len. + public: void appendBits(std::uint32_t val, int len); + +}; + +} diff --git a/anpro/imagecropper.cpp b/anpro/imagecropper.cpp new file mode 100644 index 0000000..3d80f71 --- /dev/null +++ b/anpro/imagecropper.cpp @@ -0,0 +1,537 @@ +/***************************************************************************** +* ImageCropper Qt Widget for cropping images +* Copyright (C) 2013 Dimka Novikov, to@dimkanovikov.pro +* Copyright (C) 2020 Syping +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "imagecropper.h" +#include "AppEnv.h" + +#include +#include +#include + +namespace { + static const QSize WIDGET_MINIMUM_SIZE(470, 470); +} + +ImageCropper::ImageCropper(QWidget* parent) : + QWidget(parent), + pimpl(new ImageCropperPrivate) +{ + setMinimumSize(WIDGET_MINIMUM_SIZE); + setMouseTracking(true); +} + +ImageCropper::~ImageCropper() +{ + delete pimpl; +} + +void ImageCropper::setImage(const QPixmap& _image) +{ + pimpl->imageForCropping = _image; + update(); +} + +void ImageCropper::setBackgroundColor(const QColor& _backgroundColor) +{ + pimpl->backgroundColor = _backgroundColor; + update(); +} + +void ImageCropper::setCroppingRectBorderColor(const QColor& _borderColor) +{ + pimpl->croppingRectBorderColor = _borderColor; + update(); +} + +void ImageCropper::setProportion(const QSizeF& _proportion) +{ + // Пропорции хранятся в коэффициентах приращения сторон + // Таким образом, при изменении размера области выделения, + // размеры сторон изменяются на размер зависящий от + // коэффициентов приращения. + + // Сохраним пропорциональную зависимость области выделения в коэффициентах приращения сторон + if (pimpl->proportion != _proportion) { + pimpl->proportion = _proportion; + // ... расчитаем коэффициенты + float heightDelta = (float)_proportion.height() / _proportion.width(); + float widthDelta = (float)_proportion.width() / _proportion.height(); + // ... сохраним коэффициенты + pimpl->deltas.setHeight(heightDelta); + pimpl->deltas.setWidth(widthDelta); + } + + // Обновим пропорции области выделения + if ( pimpl->isProportionFixed ) { + float croppintRectSideRelation = + (float)pimpl->croppingRect.width() / pimpl->croppingRect.height(); + float proportionSideRelation = + (float)pimpl->proportion.width() / pimpl->proportion.height(); + // Если область выделения не соответствует необходимым пропорциям обновим её + if (croppintRectSideRelation != proportionSideRelation) { + bool widthShotrerThenHeight = + pimpl->croppingRect.width() < pimpl->croppingRect.height(); + // ... установим размер той стороны, что длиннее + if (widthShotrerThenHeight) { + pimpl->croppingRect.setHeight( + pimpl->croppingRect.width() * pimpl->deltas.height()); + } else { + pimpl->croppingRect.setWidth( + pimpl->croppingRect.height() * pimpl->deltas.width()); + } + // ... перерисуем виджет + update(); + } + } + +} + +void ImageCropper::setProportionFixed(const bool _isFixed) +{ + if (pimpl->isProportionFixed != _isFixed) { + pimpl->isProportionFixed = _isFixed; + setProportion(pimpl->proportion); + } +} + +const QPixmap ImageCropper::cropImage() +{ + // Получим размер отображаемого изображения + QSize scaledImageSize = + pimpl->imageForCropping.scaled( + size(), Qt::KeepAspectRatio, Qt::SmoothTransformation + ).size(); + // Определим расстояние от левого и верхнего краёв + float leftDelta = 0; + float topDelta = 0; + const float HALF_COUNT = 2; + if (size().height() == scaledImageSize.height()) { + leftDelta = (width() - scaledImageSize.width()) / HALF_COUNT; + } else { + topDelta = (height() - scaledImageSize.height()) / HALF_COUNT; + } + // Определим пропорцию области обрезки по отношению к исходному изображению + float xScale = (float)pimpl->imageForCropping.width() / scaledImageSize.width(); + float yScale = (float)pimpl->imageForCropping.height() / scaledImageSize.height(); + // Расчитаем область обрезки с учётом коррекции размеров исходного изображения + QRectF realSizeRect( + QPointF(pimpl->croppingRect.left() - leftDelta, pimpl->croppingRect.top() - topDelta), + pimpl->croppingRect.size()); + // ... корректируем левый и верхний края + realSizeRect.setLeft((pimpl->croppingRect.left() - leftDelta) * xScale); + realSizeRect.setTop ((pimpl->croppingRect.top() - topDelta) * yScale); + // ... корректируем размер + realSizeRect.setWidth(pimpl->croppingRect.width() * xScale); + realSizeRect.setHeight(pimpl->croppingRect.height() * yScale); + // Получаем обрезанное изображение + return pimpl->imageForCropping.copy(realSizeRect.toRect()); +} + +// ******** +// Protected section + +void ImageCropper::paintEvent(QPaintEvent* _event) +{ + QWidget::paintEvent(_event); + // + QPainter widgetPainter(this); + // Рисуем изображение по центру виджета + { +#if QT_VERSION >= 0x050600 + qreal screenRatioPR = AppEnv::screenRatioPR(); + // ... подгоним изображение для отображения по размеру виджета + QPixmap scaledImage = + pimpl->imageForCropping.scaled(qRound((double)width() * screenRatioPR), qRound((double)height() * screenRatioPR), Qt::KeepAspectRatio, Qt::SmoothTransformation); + scaledImage.setDevicePixelRatio(screenRatioPR); +#else + QPixmap scaledImage = + pimpl->imageForCropping.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); +#endif + // ... заливаем фон + widgetPainter.fillRect(rect(), pimpl->backgroundColor); + // ... рисуем изображение по центру виджета +#if QT_VERSION >= 0x050600 + if (qRound((double)height() * screenRatioPR) == scaledImage.height()) { + widgetPainter.drawPixmap( ( qRound((double)width() * screenRatioPR) - scaledImage.width() ) / 2, 0, scaledImage ); + } else { + widgetPainter.drawPixmap( 0, ( qRound((double)height() * screenRatioPR) - scaledImage.height() ) / 2, scaledImage ); + } +#else + if (height() == scaledImage.height()) { + widgetPainter.drawPixmap( ( width()- scaledImage.width() ) / 2, 0, scaledImage ); + } else { + widgetPainter.drawPixmap( 0, ( height() - scaledImage.height() ) / 2, scaledImage ); + } +#endif + } + // Рисуем область обрезки + { + // ... если это первое отображение после инициилизации, то центруем областо обрезки + if (pimpl->croppingRect.isNull()) { + const int cwidth = WIDGET_MINIMUM_SIZE.width()/2; + const int cheight = WIDGET_MINIMUM_SIZE.height()/2; + pimpl->croppingRect.setSize(QSize(cwidth, cheight)); + float x = (width() - pimpl->croppingRect.width())/2; + float y = (height() - pimpl->croppingRect.height())/2; + pimpl->croppingRect.moveTo(x, y); + } + + // ... рисуем затемненную область + QPainterPath p; + p.addRect(pimpl->croppingRect); + p.addRect(rect()); + widgetPainter.setBrush(QBrush(QColor(0,0,0,120))); + widgetPainter.setPen(Qt::transparent); + widgetPainter.drawPath(p); + // Рамка и контрольные точки + widgetPainter.setPen(pimpl->croppingRectBorderColor); + // ... рисуем прямоугольник области обрезки + { + widgetPainter.setBrush(QBrush(Qt::transparent)); + widgetPainter.drawRect(pimpl->croppingRect); + } + // ... рисуем контрольные точки + { + widgetPainter.setBrush(QBrush(pimpl->croppingRectBorderColor)); + // Вспомогательные X координаты + int leftXCoord = pimpl->croppingRect.left() - 2; + int centerXCoord = pimpl->croppingRect.center().x() - 3; + int rightXCoord = pimpl->croppingRect.right() - 2; + // Вспомогательные Y координаты + int topYCoord = pimpl->croppingRect.top() - 2; + int middleYCoord = pimpl->croppingRect.center().y() - 3; + int bottomYCoord = pimpl->croppingRect.bottom() - 2; + // + const QSize pointSize(6, 6); + // + QVector points; + points + // левая сторона + << QRect( QPoint(leftXCoord, topYCoord), pointSize ) + << QRect( QPoint(leftXCoord, middleYCoord), pointSize ) + << QRect( QPoint(leftXCoord, bottomYCoord), pointSize ) + // центр + << QRect( QPoint(centerXCoord, topYCoord), pointSize ) + << QRect( QPoint(centerXCoord, middleYCoord), pointSize ) + << QRect( QPoint(centerXCoord, bottomYCoord), pointSize ) + // правая сторона + << QRect( QPoint(rightXCoord, topYCoord), pointSize ) + << QRect( QPoint(rightXCoord, middleYCoord), pointSize ) + << QRect( QPoint(rightXCoord, bottomYCoord), pointSize ); + // + widgetPainter.drawRects( points ); + } + // ... рисуем пунктирные линии + { + QPen dashPen(pimpl->croppingRectBorderColor); + dashPen.setStyle(Qt::DashLine); + widgetPainter.setPen(dashPen); + // ... вертикальная + widgetPainter.drawLine( + QPoint(pimpl->croppingRect.center().x(), pimpl->croppingRect.top()), + QPoint(pimpl->croppingRect.center().x(), pimpl->croppingRect.bottom()) ); + // ... горизонтальная + widgetPainter.drawLine( + QPoint(pimpl->croppingRect.left(), pimpl->croppingRect.center().y()), + QPoint(pimpl->croppingRect.right(), pimpl->croppingRect.center().y()) ); + } + } + // + widgetPainter.end(); +} + +void ImageCropper::mousePressEvent(QMouseEvent* _event) +{ + if (_event->button() == Qt::LeftButton) { + pimpl->isMousePressed = true; + pimpl->startMousePos = _event->pos(); + pimpl->lastStaticCroppingRect = pimpl->croppingRect; + } + // + updateCursorIcon(_event->pos()); +} + +void ImageCropper::mouseMoveEvent(QMouseEvent* _event) +{ + QPointF mousePos = _event->pos(); // относительно себя (виджета) + // + if (!pimpl->isMousePressed) { + // Обработка обычного состояния, т.е. не изменяется размер + // области обрезки, и она не перемещается по виджету + pimpl->cursorPosition = cursorPosition(pimpl->croppingRect, mousePos); + updateCursorIcon(mousePos); + } else if (pimpl->cursorPosition != CursorPositionUndefined) { + // Обработка действий над областью обрезки + // ... определим смещение курсора мышки + QPointF mouseDelta; + mouseDelta.setX( mousePos.x() - pimpl->startMousePos.x() ); + mouseDelta.setY( mousePos.y() - pimpl->startMousePos.y() ); + // + if (pimpl->cursorPosition != CursorPositionMiddle) { + // ... изменяем размер области обрезки + QRectF newGeometry = + calculateGeometry( + pimpl->lastStaticCroppingRect, + pimpl->cursorPosition, + mouseDelta); + // ... пользователь пытается вывернуть область обрезки наизнанку + if (!newGeometry.isNull()) { + pimpl->croppingRect = newGeometry; + } + } else { + // ... перемещаем область обрезки + pimpl->croppingRect.moveTo( pimpl->lastStaticCroppingRect.topLeft() + mouseDelta ); + } + // Перерисуем виджет + update(); + } +} + +void ImageCropper::mouseReleaseEvent(QMouseEvent* _event) +{ + pimpl->isMousePressed = false; + updateCursorIcon(_event->pos()); +} + +// ******** +// Private section + +namespace { + // Находится ли точка рядом с координатой стороны + static bool isPointNearSide (const int _sideCoordinate, const int _pointCoordinate) + { + static const int indent = 10; + return (_sideCoordinate - indent) < _pointCoordinate && _pointCoordinate < (_sideCoordinate + indent); + } +} + +CursorPosition ImageCropper::cursorPosition(const QRectF& _cropRect, const QPointF& _mousePosition) +{ + CursorPosition cursorPosition = CursorPositionUndefined; + // + if ( _cropRect.contains( _mousePosition ) ) { + // Двухстороннее направление + if (isPointNearSide(_cropRect.top(), _mousePosition.y()) && + isPointNearSide(_cropRect.left(), _mousePosition.x())) { + cursorPosition = CursorPositionTopLeft; + } else if (isPointNearSide(_cropRect.bottom(), _mousePosition.y()) && + isPointNearSide(_cropRect.left(), _mousePosition.x())) { + cursorPosition = CursorPositionBottomLeft; + } else if (isPointNearSide(_cropRect.top(), _mousePosition.y()) && + isPointNearSide(_cropRect.right(), _mousePosition.x())) { + cursorPosition = CursorPositionTopRight; + } else if (isPointNearSide(_cropRect.bottom(), _mousePosition.y()) && + isPointNearSide(_cropRect.right(), _mousePosition.x())) { + cursorPosition = CursorPositionBottomRight; + // Одностороннее направление + } else if (isPointNearSide(_cropRect.left(), _mousePosition.x())) { + cursorPosition = CursorPositionLeft; + } else if (isPointNearSide(_cropRect.right(), _mousePosition.x())) { + cursorPosition = CursorPositionRight; + } else if (isPointNearSide(_cropRect.top(), _mousePosition.y())) { + cursorPosition = CursorPositionTop; + } else if (isPointNearSide(_cropRect.bottom(), _mousePosition.y())) { + cursorPosition = CursorPositionBottom; + // Без направления + } else { + cursorPosition = CursorPositionMiddle; + } + } + // + return cursorPosition; +} + +void ImageCropper::updateCursorIcon(const QPointF& _mousePosition) +{ + QCursor cursorIcon; + // + switch (cursorPosition(pimpl->croppingRect, _mousePosition)) + { + case CursorPositionTopRight: + case CursorPositionBottomLeft: + cursorIcon = QCursor(Qt::SizeBDiagCursor); + break; + case CursorPositionTopLeft: + case CursorPositionBottomRight: + cursorIcon = QCursor(Qt::SizeFDiagCursor); + break; + case CursorPositionTop: + case CursorPositionBottom: + cursorIcon = QCursor(Qt::SizeVerCursor); + break; + case CursorPositionLeft: + case CursorPositionRight: + cursorIcon = QCursor(Qt::SizeHorCursor); + break; + case CursorPositionMiddle: + cursorIcon = pimpl->isMousePressed ? + QCursor(Qt::ClosedHandCursor) : + QCursor(Qt::OpenHandCursor); + break; + case CursorPositionUndefined: + default: + cursorIcon = QCursor(Qt::ArrowCursor); + break; + } + // + this->setCursor(cursorIcon); +} + +const QRectF ImageCropper::calculateGeometry( + const QRectF& _sourceGeometry, + const CursorPosition _cursorPosition, + const QPointF& _mouseDelta + ) +{ + QRectF resultGeometry; + // + if ( pimpl->isProportionFixed ) { + resultGeometry = + calculateGeometryWithFixedProportions( + _sourceGeometry, _cursorPosition, _mouseDelta, pimpl->deltas); + } else { + resultGeometry = + calculateGeometryWithCustomProportions( + _sourceGeometry, _cursorPosition, _mouseDelta); + } + // Если пользователь пытается вывернуть область обрезки наизнанку, + // возвращаем null-прямоугольник + if ((resultGeometry.left() >= resultGeometry.right()) || + (resultGeometry.top() >= resultGeometry.bottom())) { + resultGeometry = QRect(); + } + // + return resultGeometry; +} + +const QRectF ImageCropper::calculateGeometryWithCustomProportions( + const QRectF& _sourceGeometry, + const CursorPosition _cursorPosition, + const QPointF& _mouseDelta + ) +{ + QRectF resultGeometry = _sourceGeometry; + // + switch ( _cursorPosition ) + { + case CursorPositionTopLeft: + resultGeometry.setLeft( _sourceGeometry.left() + _mouseDelta.x() ); + resultGeometry.setTop ( _sourceGeometry.top() + _mouseDelta.y() ); + break; + case CursorPositionTopRight: + resultGeometry.setTop ( _sourceGeometry.top() + _mouseDelta.y() ); + resultGeometry.setRight( _sourceGeometry.right() + _mouseDelta.x() ); + break; + case CursorPositionBottomLeft: + resultGeometry.setBottom( _sourceGeometry.bottom() + _mouseDelta.y() ); + resultGeometry.setLeft ( _sourceGeometry.left() + _mouseDelta.x() ); + break; + case CursorPositionBottomRight: + resultGeometry.setBottom( _sourceGeometry.bottom() + _mouseDelta.y() ); + resultGeometry.setRight ( _sourceGeometry.right() + _mouseDelta.x() ); + break; + case CursorPositionTop: + resultGeometry.setTop( _sourceGeometry.top() + _mouseDelta.y() ); + break; + case CursorPositionBottom: + resultGeometry.setBottom( _sourceGeometry.bottom() + _mouseDelta.y() ); + break; + case CursorPositionLeft: + resultGeometry.setLeft( _sourceGeometry.left() + _mouseDelta.x() ); + break; + case CursorPositionRight: + resultGeometry.setRight( _sourceGeometry.right() + _mouseDelta.x() ); + break; + default: + break; + } + // + return resultGeometry; +} + +const QRectF ImageCropper::calculateGeometryWithFixedProportions( + const QRectF& _sourceGeometry, + const CursorPosition _cursorPosition, + const QPointF& _mouseDelta, + const QSizeF& _deltas + ) +{ + QRectF resultGeometry = _sourceGeometry; + // + switch (_cursorPosition) + { + case CursorPositionLeft: + resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.x() * _deltas.height()); + resultGeometry.setLeft(_sourceGeometry.left() + _mouseDelta.x()); + break; + case CursorPositionRight: + resultGeometry.setTop(_sourceGeometry.top() - _mouseDelta.x() * _deltas.height()); + resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.x()); + break; + case CursorPositionTop: + resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.y()); + resultGeometry.setRight(_sourceGeometry.right() - _mouseDelta.y() * _deltas.width()); + break; + case CursorPositionBottom: + resultGeometry.setBottom(_sourceGeometry.bottom() + _mouseDelta.y()); + resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.y() * _deltas.width()); + break; + case CursorPositionTopLeft: + if ((_mouseDelta.x() * _deltas.height()) < (_mouseDelta.y())) { + resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.x() * _deltas.height()); + resultGeometry.setLeft(_sourceGeometry.left() + _mouseDelta.x()); + } else { + resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.y()); + resultGeometry.setLeft(_sourceGeometry.left() + _mouseDelta.y() * _deltas.width()); + } + break; + case CursorPositionTopRight: + if ((_mouseDelta.x() * _deltas.height() * -1) < (_mouseDelta.y())) { + resultGeometry.setTop(_sourceGeometry.top() - _mouseDelta.x() * _deltas.height()); + resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.x() ); + } else { + resultGeometry.setTop(_sourceGeometry.top() + _mouseDelta.y()); + resultGeometry.setRight(_sourceGeometry.right() - _mouseDelta.y() * _deltas.width()); + } + break; + case CursorPositionBottomLeft: + if ((_mouseDelta.x() * _deltas.height()) < (_mouseDelta.y() * -1)) { + resultGeometry.setBottom(_sourceGeometry.bottom() - _mouseDelta.x() * _deltas.height()); + resultGeometry.setLeft(_sourceGeometry.left() + _mouseDelta.x()); + } else { + resultGeometry.setBottom(_sourceGeometry.bottom() + _mouseDelta.y()); + resultGeometry.setLeft(_sourceGeometry.left() - _mouseDelta.y() * _deltas.width()); + } + break; + case CursorPositionBottomRight: + if ((_mouseDelta.x() * _deltas.height()) > (_mouseDelta.y())) { + resultGeometry.setBottom(_sourceGeometry.bottom() + _mouseDelta.x() * _deltas.height()); + resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.x()); + } else { + resultGeometry.setBottom(_sourceGeometry.bottom() + _mouseDelta.y()); + resultGeometry.setRight(_sourceGeometry.right() + _mouseDelta.y() * _deltas.width()); + } + break; + default: + break; + } + // + return resultGeometry; +} + diff --git a/anpro/imagecropper.h b/anpro/imagecropper.h new file mode 100644 index 0000000..a5a19a0 --- /dev/null +++ b/anpro/imagecropper.h @@ -0,0 +1,103 @@ +/***************************************************************************** +* ImageCropper Qt Widget for cropping images +* Copyright (C) 2013 Dimka Novikov, to@dimkanovikov.pro +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef IMAGECROPPER_H +#define IMAGECROPPER_H + +#include "imagecropper_p.h" +#include "imagecropper_e.h" + +#include + +class ImageCropper : public QWidget +{ + Q_OBJECT + +public: + ImageCropper(QWidget *parent = 0); + ~ImageCropper(); + +public slots: + // Установить изображение для обрезки + void setImage(const QPixmap& _image); + // Установить цвет фона виджета обрезки + void setBackgroundColor(const QColor& _backgroundColor); + // Установить цвет рамки области обрезки + void setCroppingRectBorderColor(const QColor& _borderColor); + // Установить пропорции области выделения + void setProportion(const QSizeF& _proportion); + // Использовать фиксированные пропорции области виделения + void setProportionFixed(const bool _isFixed); + +public: + // Обрезать изображение + const QPixmap cropImage(); + +protected: + virtual void paintEvent(QPaintEvent* _event); + virtual void mousePressEvent(QMouseEvent* _event); + virtual void mouseMoveEvent(QMouseEvent* _event); + virtual void mouseReleaseEvent(QMouseEvent* _event); + +private: + // Определение местоположения курсора над виджетом + CursorPosition cursorPosition(const QRectF& _cropRect, const QPointF& _mousePosition); + // Обновить иконку курсора соответствующую местоположению мыши + void updateCursorIcon(const QPointF& _mousePosition); + + // Получить размер виджета после его изменения мышью + // -------- + // Контракты: + // 1. Метод должен вызываться, только при зажатой кнопке мыши + // (т.е. при перемещении или изменении размера виджета) + // -------- + // В случае неудачи возвращает null-прямоугольник + const QRectF calculateGeometry( + const QRectF& _sourceGeometry, + const CursorPosition _cursorPosition, + const QPointF& _mouseDelta + ); + // Получить размер виджета после его изменения мышью + // Метод изменяет виджет не сохраняя начальных пропорций сторон + // ------ + // Контракты: + // 1. Метод должен вызываться, только при зажатой кнопке мыши + // (т.е. при перемещении или изменении размера виджета) + const QRectF calculateGeometryWithCustomProportions( + const QRectF& _sourceGeometry, + const CursorPosition _cursorPosition, + const QPointF& _mouseDelta + ); + // Получить размер виджета после его изменения мышью + // Метод изменяет виджет сохраняя начальные пропорции сторон + // ------ + // Контракты: + // 1. Метод должен вызываться, только при зажатой кнопке мыши + // (т.е. при перемещении или изменении размера виджета) + const QRectF calculateGeometryWithFixedProportions(const QRectF &_sourceGeometry, + const CursorPosition _cursorPosition, + const QPointF &_mouseDelta, + const QSizeF &_deltas + ); + +private: + // Private data implementation + ImageCropperPrivate* pimpl; +}; + +#endif // IMAGECROPPER_H diff --git a/anpro/imagecropper_e.h b/anpro/imagecropper_e.h new file mode 100644 index 0000000..a9ced6a --- /dev/null +++ b/anpro/imagecropper_e.h @@ -0,0 +1,36 @@ +/***************************************************************************** +* ImageCropper Qt Widget for cropping images +* Copyright (C) 2013 Dimka Novikov, to@dimkanovikov.pro +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef IMAGECROPPER_E_H +#define IMAGECROPPER_E_H + +enum CursorPosition +{ + CursorPositionUndefined, + CursorPositionMiddle, + CursorPositionTop, + CursorPositionBottom, + CursorPositionLeft, + CursorPositionRight, + CursorPositionTopLeft, + CursorPositionTopRight, + CursorPositionBottomLeft, + CursorPositionBottomRight +}; + +#endif // IMAGECROPPER_E_H diff --git a/anpro/imagecropper_p.h b/anpro/imagecropper_p.h new file mode 100644 index 0000000..bd09dbb --- /dev/null +++ b/anpro/imagecropper_p.h @@ -0,0 +1,76 @@ +/***************************************************************************** +* ImageCropper Qt Widget for cropping images +* Copyright (C) 2013 Dimka Novikov, to@dimkanovikov.pro +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef IMAGECROPPER_P_H +#define IMAGECROPPER_P_H + +#include "imagecropper_e.h" + +#include +#include +#include + +namespace { + const QRect INIT_CROPPING_RECT = QRect(); + const QSizeF INIT_PROPORTION = QSizeF(1.0, 1.0); +} + +class ImageCropperPrivate { +public: + ImageCropperPrivate() : + imageForCropping(QPixmap()), + croppingRect(INIT_CROPPING_RECT), + lastStaticCroppingRect(QRect()), + cursorPosition(CursorPositionUndefined), + isMousePressed(false), + isProportionFixed(false), + startMousePos(QPoint()), + proportion(INIT_PROPORTION), + deltas(INIT_PROPORTION), + backgroundColor(Qt::black), + croppingRectBorderColor(Qt::white) + {} + +public: + // Изображение для обрезки + QPixmap imageForCropping; + // Область обрезки + QRectF croppingRect; + // Последняя фиксированная область обрезки + QRectF lastStaticCroppingRect; + // Позиция курсора относительно области обрезки + CursorPosition cursorPosition; + // Зажата ли левая кнопка мыши + bool isMousePressed; + // Фиксировать пропорции области обрезки + bool isProportionFixed; + // Начальная позиция курсора при изменении размера области обрезки + QPointF startMousePos; + // Пропорции + QSizeF proportion; + // Приращения + // width - приращение по x + // height - приращение по y + QSizeF deltas; + // Цвет заливки фона под изображением + QColor backgroundColor; + // Цвет рамки области обрезки + QColor croppingRectBorderColor; +}; + +#endif // IMAGECROPPER_P_H diff --git a/config.h b/config.h old mode 100755 new mode 100644 index c855265..6f96056 --- a/config.h +++ b/config.h @@ -1,101 +1,124 @@ -/***************************************************************************** -* gta5view Grand Theft Auto V Profile Viewer -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef CONFIG_H -#define CONFIG_H -#include - -#ifndef GTA5SYNC_APPVENDOR -#define GTA5SYNC_APPVENDOR "Syping" -#endif - -#ifndef GTA5SYNC_APPVENDORLINK -#define GTA5SYNC_APPVENDORLINK "https://github.com/Syping/" -#endif - -#ifndef GTA5SYNC_DISABLED -#define GTA5SYNC_ENABLED -#endif - -#ifndef GTA5SYNC_APPSTR -#ifdef GTA5SYNC_ENABLED -#define GTA5SYNC_APPSTR "gta5sync" -#else -#define GTA5SYNC_APPSTR "gta5view" -#endif -#endif - -#ifndef GTA5SYNC_APPDES -#define GTA5SYNC_APPDES "INSERT YOUR APPLICATION DESCRIPTION HERE" -#endif - -#ifndef GTA5SYNC_COPYRIGHT -#define GTA5SYNC_COPYRIGHT "2016-2017" -#endif - -#ifndef GTA5SYNC_APPVER -#ifndef GTA5SYNC_DAILYB -#define GTA5SYNC_APPVER "1.4.0" -#else -#define GTA5SYNC_APPVER QString("%1").arg(GTA5SYNC_DAILYB) -#endif -#endif - -#ifndef GTA5SYNC_BUILDTYPE -#define GTA5SYNC_BUILDTYPE "Custom" -#endif - -#ifndef GTA5SYNC_SHARE -#define GTA5SYNC_SHARE "$RUNDIR" -#endif - -#ifndef GTA5SYNC_LANG -#define GTA5SYNC_LANG "$SHAREDIR$SEPARATORlang" -#endif - -#ifndef GTA5SYNC_PLUG -#define GTA5SYNC_PLUG "$RUNDIR$SEPARATORplugins" -#endif - -#ifdef GTA5SYNC_WINRT -#undef GTA5SYNC_WIN -#endif - -#ifndef GTA5SYNC_COMPILER -#ifdef __clang__ -#define GTA5SYNC_COMPILER QString("Clang %1.%2.%3").arg(QString::number(__clang_major__), QString::number(__clang_minor__), QString::number(__clang_patchlevel__)) -#elif defined(__GNUC__) -#define GTA5SYNC_COMPILER QString("GCC %1.%2.%3").arg(QString::number(__GNUC__), QString::number(__GNUC_MINOR__), QString::number(__GNUC_PATCHLEVEL__)) -#elif defined(__GNUG__) -#define GTA5SYNC_COMPILER QString("GCC %1.%2.%3").arg(QString::number(__GNUG__), QString::number(__GNUC_MINOR__), QString::number(__GNUC_PATCHLEVEL__)) -#elif defined(_MSC_VER) -#define GTA5SYNC_COMPILER QString("MSVC %1").arg(QString::number(_MSC_VER).insert(2, ".")) -#else -#define GTA5SYNC_COMPILER QString("Unknown Compiler") -#endif -#endif - -#ifndef GTA5SYNC_BUILDDATETIME -#define GTA5SYNC_BUILDDATETIME QString("%1, %2").arg(__DATE__, __TIME__); -#endif - -#ifndef GTA5SYNC_BUILDSTRING -#define GTA5SYNC_BUILDSTRING QString("%1, %2").arg(QT_VERSION_STR, GTA5SYNC_COMPILER); -#endif - -#endif // CONFIG_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef CONFIG_H +#define CONFIG_H + +#if __cplusplus +#include +#define REL_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Release") +#define RC_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Release Candidate") +#define BETA_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Beta") +#define ALPHA_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Alpha") +#define DEV_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Developer") +#define DAILY_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Daily Build") +#define CUSTOM_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Custom") +#endif + +#ifndef GTA5SYNC_APPVENDOR +#define GTA5SYNC_APPVENDOR "Syping" +#endif + +#ifndef GTA5SYNC_APPVENDORLINK +#define GTA5SYNC_APPVENDORLINK "g5e://about?U3lwaW5n:R2l0TGFiOiA8YSBocmVmPSJodHRwczovL2dpdGxhYi5jb20vU3lwaW5nIj5TeXBpbmc8L2E+PGJyLz5HaXRIdWI6IDxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9TeXBpbmciPlN5cGluZzwvYT48YnIvPlNvY2lhbCBDbHViOiA8YSBocmVmPSJodHRwczovL3NvY2lhbGNsdWIucm9ja3N0YXJnYW1lcy5jb20vbWVtYmVyL1N5cGluZy80NjMwMzA1NiI+U3lwaW5nPC9hPg" +#endif + +#ifndef GTA5SYNC_APPSTR +#define GTA5SYNC_APPSTR "gta5view" +#endif + +#ifndef GTA5SYNC_APPDES +#define GTA5SYNC_APPDES "INSERT YOUR APPLICATION DESCRIPTION HERE" +#endif + +#ifndef GTA5SYNC_COPYRIGHT +#define GTA5SYNC_COPYRIGHT "2016-2023" +#endif + +#ifndef GTA5SYNC_APPVER +#define GTA5SYNC_APPVER "1.10.2" +#endif + +#if __cplusplus +#ifndef GTA5SYNC_BUILDTYPE +#define GTA5SYNC_BUILDTYPE QT_TRANSLATE_NOOP("AboutDialog", "Custom") +#endif + +#ifndef GTA5SYNC_BUILDCODE +#define GTA5SYNC_BUILDCODE "Source" +#endif + +#ifdef GTA5SYNC_QCONF +#ifndef GTA5SYNC_SHARE +#ifdef Q_OS_WIN +#define GTA5SYNC_SHARE "RUNDIR:" +#else +#define GTA5SYNC_SHARE "RUNDIR:/../share" +#endif +#endif +#ifndef GTA5SYNC_LANG +#define GTA5SYNC_LANG "QCONFLANG:" +#endif +#ifndef GTA5SYNC_PLUG +#define GTA5SYNC_PLUG "QCONFPLUG:" +#endif +#ifdef GTA5SYNC_QCONF_IN +#ifndef GTA5SYNC_INLANG +#define GTA5SYNC_INLANG ":/tr" +#endif +#endif +#else +#ifndef GTA5SYNC_SHARE +#define GTA5SYNC_SHARE "RUNDIR:" +#endif +#ifndef GTA5SYNC_LANG +#define GTA5SYNC_LANG "SHAREDDIR:/lang" +#endif +#ifndef GTA5SYNC_PLUG +#define GTA5SYNC_PLUG "RUNDIR:/plugins" +#endif +#endif + +#ifndef GTA5SYNC_COMPILER +#ifdef __clang__ +#ifndef Q_OS_MAC +#define GTA5SYNC_COMPILER QString("Clang %1.%2.%3").arg(QString::number(__clang_major__), QString::number(__clang_minor__), QString::number(__clang_patchlevel__)) +#else +#define GTA5SYNC_COMPILER QString("Apple LLVM %1.%2.%3").arg(QString::number(__clang_major__), QString::number(__clang_minor__), QString::number(__clang_patchlevel__)) +#endif +#elif defined(__GNUC__) +#define GTA5SYNC_COMPILER QString("GCC %1.%2.%3").arg(QString::number(__GNUC__), QString::number(__GNUC_MINOR__), QString::number(__GNUC_PATCHLEVEL__)) +#elif defined(__GNUG__) +#define GTA5SYNC_COMPILER QString("GCC %1.%2.%3").arg(QString::number(__GNUG__), QString::number(__GNUC_MINOR__), QString::number(__GNUC_PATCHLEVEL__)) +#elif defined(_MSC_VER) +#define GTA5SYNC_COMPILER QString("MSVC %1").arg(QString::number(_MSC_VER).insert(2, ".")) +#else +#define GTA5SYNC_COMPILER QString("Unknown Compiler") +#endif +#endif + +#ifndef GTA5SYNC_BUILDDATETIME +#define GTA5SYNC_BUILDDATETIME QString("%1, %2").arg(__DATE__, __TIME__) +#endif + +#ifndef GTA5SYNC_BUILDSTRING +#define GTA5SYNC_BUILDSTRING QString("%1, %2").arg(QT_VERSION_STR, GTA5SYNC_COMPILER) +#endif +#endif + +#endif // CONFIG_H diff --git a/gta5view.pro b/gta5view.pro old mode 100755 new mode 100644 index 13e3a9e..9326210 --- a/gta5view.pro +++ b/gta5view.pro @@ -1,165 +1,285 @@ -#/***************************************************************************** -#* gta5view Grand Theft Auto V Profile Viewer -#* Copyright (C) 2015-2017 Syping -#* -#* This program is free software: you can redistribute it and/or modify -#* it under the terms of the GNU General Public License as published by -#* the Free Software Foundation, either version 3 of the License, or -#* (at your option) any later version. -#* -#* This program is distributed in the hope that it will be useful, -#* but WITHOUT ANY WARRANTY; without even the implied warranty of -#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#* GNU General Public License for more details. -#* -#* You should have received a copy of the GNU General Public License -#* along with this program. If not, see . -#*****************************************************************************/ - -QT += core gui network - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets -greaterThan(QT_MAJOR_VERSION, 4): greaterThan(QT_MINOR_VERSION, 1): win32: QT += winextras - -DEFINES += GTA5SYNC_DISABLED - -DEPLOYMENT.display_name = gta5view -TARGET = gta5view -TEMPLATE = app - -SOURCES += main.cpp \ - AboutDialog.cpp \ - AppEnv.cpp \ - CrewDatabase.cpp \ - DatabaseThread.cpp \ - ExportDialog.cpp \ - ExportThread.cpp \ - GlobalString.cpp \ - IconLoader.cpp \ - ImportDialog.cpp \ - OptionsDialog.cpp \ - PictureDialog.cpp \ - PictureExport.cpp \ - PictureWidget.cpp \ - ProfileDatabase.cpp \ - ProfileInterface.cpp \ - ProfileLoader.cpp \ - ProfileWidget.cpp \ - SavegameCopy.cpp \ - SavegameData.cpp \ - SavegameDialog.cpp \ - SavegameWidget.cpp \ - SidebarGenerator.cpp \ - SnapmaticEditor.cpp \ - SnapmaticPicture.cpp \ - SnapmaticWidget.cpp \ - StandardPaths.cpp \ - StringParser.cpp \ - UserInterface.cpp \ - uimod/UiModLabel.cpp \ - uimod/UiModWidget.cpp - -HEADERS += \ - AboutDialog.h \ - AppEnv.h \ - CrewDatabase.h \ - DatabaseThread.h \ - ExportDialog.h \ - ExportThread.h \ - GlobalString.h \ - IconLoader.h \ - ImportDialog.h \ - OptionsDialog.h \ - PictureDialog.h \ - PictureExport.h \ - PictureWidget.h \ - ProfileDatabase.h \ - ProfileInterface.h \ - ProfileLoader.h \ - ProfileWidget.h \ - SavegameCopy.h \ - SavegameData.h \ - SavegameDialog.h \ - SavegameWidget.h \ - SidebarGenerator.h \ - SnapmaticEditor.h \ - SnapmaticPicture.h \ - SnapmaticWidget.h \ - StandardPaths.h \ - StringParser.h \ - UserInterface.h \ - uimod/UiModLabel.h \ - uimod/UiModWidget.h - -PRECOMPILED_HEADER += config.h - -FORMS += \ - AboutDialog.ui \ - ExportDialog.ui \ - ImportDialog.ui \ - OptionsDialog.ui \ - PictureDialog.ui \ - ProfileInterface.ui \ - SavegameDialog.ui \ - SavegameWidget.ui \ - SnapmaticEditor.ui \ - SnapmaticWidget.ui \ - UserInterface.ui - -TRANSLATIONS += \ - res/gta5sync_de.ts \ - res/gta5sync_fr.ts \ - res/gta5sync_ru.ts - -RESOURCES += \ - res/app.qrc - -DISTFILES += res/app.rc \ - res/gta5sync.desktop \ - res/gta5sync_de.ts \ - res/gta5sync_fr.ts \ - res/gta5sync_ru.ts \ - res/gta5sync.exe.manifest \ - res/gta5sync.png \ - lang/README.txt - -INCLUDEPATH += ./uimod - -# WINDOWS ONLY - -win32: DEFINES += GTA5SYNC_WIN -win32: RC_FILE += res/app.rc -win32: LIBS += -luser32 -win32: CONFIG -= embed_manifest_exe - -# MAC OS X ONLY -macx: ICON = res/5sync.icns - -# QT4 ONLY STUFF - -isEqual(QT_MAJOR_VERSION, 4): INCLUDEPATH += ./qjson4 -isEqual(QT_MAJOR_VERSION, 4): HEADERS += qjson4/QJsonArray.h \ - qjson4/QJsonDocument.h \ - qjson4/QJsonObject.h \ - qjson4/QJsonParseError.h \ - qjson4/QJsonValue.h \ - qjson4/QJsonValueRef.h \ - qjson4/QJsonParser.h \ - qjson4/QJsonRoot.h - -isEqual(QT_MAJOR_VERSION, 4): SOURCES += qjson4/QJsonArray.cpp \ - qjson4/QJsonDocument.cpp \ - qjson4/QJsonObject.cpp \ - qjson4/QJsonParseError.cpp \ - qjson4/QJsonValue.cpp \ - qjson4/QJsonValueRef.cpp \ - qjson4/QJsonParser.cpp - -# UNIX SYSTEM STUFF - -unix: !macx: appfiles.path = $$(INSTALL_PATH)/share/applications -unix: !macx: appfiles.files = $$PWD/res/gta5view.desktop -unix: !macx: pixmaps.path = $$(INSTALL_PATH)/share/pixmaps -unix: !macx: pixmaps.files = $$PWD/res/gta5view.png -unix: !macx: target.path = $$(INSTALL_PATH)/bin -unix: !macx: INSTALLS += target pixmaps appfiles +#/***************************************************************************** +#* gta5view Grand Theft Auto V Profile Viewer +#* Copyright (C) 2015-2021 Syping +#* +#* This program is free software: you can redistribute it and/or modify +#* it under the terms of the GNU General Public License as published by +#* the Free Software Foundation, either version 3 of the License, or +#* (at your option) any later version. +#* +#* This program is distributed in the hope that it will be useful, +#* but WITHOUT ANY WARRANTY; without even the implied warranty of +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#* GNU General Public License for more details. +#* +#* You should have received a copy of the GNU General Public License +#* along with this program. If not, see . +#*****************************************************************************/ + +QT += core gui network svg + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +greaterThan(QT_MAJOR_VERSION, 4): win32: LIBS += -ldwmapi + +DEPLOYMENT.display_name = gta5view +TARGET = gta5view +TEMPLATE = app + +HEADERS += config.h \ + wrapper.h +PRECOMPILED_HEADER += config.h + +SOURCES += main.cpp \ + AboutDialog.cpp \ + AppEnv.cpp \ + CrewDatabase.cpp \ + DatabaseThread.cpp \ + ExportDialog.cpp \ + ExportThread.cpp \ + GlobalString.cpp \ + IconLoader.cpp \ + ImportDialog.cpp \ + JsonEditorDialog.cpp \ + MapLocationDialog.cpp \ + MessageThread.cpp \ + OptionsDialog.cpp \ + PictureDialog.cpp \ + PictureExport.cpp \ + PictureWidget.cpp \ + PlayerListDialog.cpp \ + ProfileDatabase.cpp \ + ProfileInterface.cpp \ + ProfileLoader.cpp \ + ProfileWidget.cpp \ + RagePhoto.cpp \ + SavegameCopy.cpp \ + SavegameData.cpp \ + SavegameDialog.cpp \ + SavegameWidget.cpp \ + SidebarGenerator.cpp \ + SnapmaticEditor.cpp \ + SnapmaticPicture.cpp \ + SnapmaticWidget.cpp \ + StandardPaths.cpp \ + StringParser.cpp \ + TelemetryClass.cpp \ + TranslationClass.cpp \ + UserInterface.cpp \ + anpro/imagecropper.cpp \ + pcg/pcg_basic.c \ + tmext/TelemetryClassAuthenticator.cpp \ + uimod/JSHighlighter.cpp \ + uimod/UiModLabel.cpp \ + uimod/UiModWidget.cpp + +HEADERS += \ + AboutDialog.h \ + AppEnv.h \ + CrewDatabase.h \ + DatabaseThread.h \ + ExportDialog.h \ + ExportThread.h \ + GlobalString.h \ + IconLoader.h \ + ImportDialog.h \ + JsonEditorDialog.h \ + MapLocationDialog.h \ + MessageThread.h \ + OptionsDialog.h \ + PictureDialog.h \ + PictureExport.h \ + PictureWidget.h \ + PlayerListDialog.h \ + ProfileDatabase.h \ + ProfileInterface.h \ + ProfileLoader.h \ + ProfileWidget.h \ + RagePhoto.h \ + SavegameCopy.h \ + SavegameData.h \ + SavegameDialog.h \ + SavegameWidget.h \ + SidebarGenerator.h \ + SnapmaticEditor.h \ + SnapmaticPicture.h \ + SnapmaticWidget.h \ + StandardPaths.h \ + StringParser.h \ + TelemetryClass.h \ + TranslationClass.h \ + UserInterface.h \ + anpro/imagecropper.h \ + anpro/imagecropper_e.h \ + anpro/imagecropper_p.h \ + pcg/pcg_basic.h \ + tmext/TelemetryClassAuthenticator.h \ + uimod/JSHighlighter.h \ + uimod/UiModLabel.h \ + uimod/UiModWidget.h + +FORMS += \ + AboutDialog.ui \ + ExportDialog.ui \ + ImportDialog.ui \ + JsonEditorDialog.ui \ + MapLocationDialog.ui \ + OptionsDialog.ui \ + PictureDialog.ui \ + PlayerListDialog.ui \ + ProfileInterface.ui \ + SavegameDialog.ui \ + SavegameWidget.ui \ + SnapmaticEditor.ui \ + SnapmaticWidget.ui \ + UserInterface.ui + +TRANSLATIONS += \ + res/gta5sync.ts \ + res/gta5sync_de.ts \ + res/gta5sync_en_US.ts \ + res/gta5sync_fr.ts \ + res/gta5sync_ko.ts \ + res/gta5sync_ru.ts \ + res/gta5sync_uk.ts \ + res/gta5sync_zh_TW.ts + +RESOURCES += \ + res/img.qrc \ + res/template.qrc \ + res/tr_g5p.qrc + +DISTFILES += \ + res/gta5view-16.png \ + res/gta5view-24.png \ + res/gta5view-32.png \ + res/gta5view-40.png \ + res/gta5view-48.png \ + res/gta5view-64.png \ + res/gta5view-96.png \ + res/gta5view-128.png \ + res/gta5view-256.png \ + res/gta5view-512.png \ + res/app.rc \ + res/de.syping.gta5view.desktop \ + res/de.syping.gta5view.png \ + res/gta5sync_de.ts \ + res/gta5sync_en_US.ts \ + res/gta5sync_fr.ts \ + res/gta5sync_ko.ts \ + res/gta5sync_ru.ts \ + res/gta5sync_uk.ts \ + res/gta5sync_zh_TW.ts \ + res/gta5view.exe.manifest \ + res/gta5view.png \ + lang/README.txt + +INCLUDEPATH += ./anpro ./pcg ./tmext ./uimod + +# GTA5SYNC/GTA5VIEW ONLY + +DEFINES += GTA5SYNC_QMAKE # We using qmake do we? +DEFINES += GTA5SYNC_PROJECT # Enable exclusive gta5sync/gta5view functions + +# WINDOWS ONLY + +win32: RC_FILE += res/app.rc +win32: CONFIG -= embed_manifest_exe +contains(DEFINES, GTA5SYNC_TELEMETRY): win32: LIBS += -ld3d9 # Required for getting information about GPU + +# MAC OS X ONLY +macx: ICON = res/gta5view.icns + +# QT4 ONLY STUFF + +isEqual(QT_MAJOR_VERSION, 4): INCLUDEPATH += ./qjson4 +isEqual(QT_MAJOR_VERSION, 4): HEADERS += qjson4/QJsonArray.h \ + qjson4/QJsonDocument.h \ + qjson4/QJsonObject.h \ + qjson4/QJsonParseError.h \ + qjson4/QJsonValue.h \ + qjson4/QJsonValueRef.h \ + qjson4/QJsonParser.h \ + qjson4/QJsonRoot.h + +isEqual(QT_MAJOR_VERSION, 4): SOURCES += qjson4/QJsonArray.cpp \ + qjson4/QJsonDocument.cpp \ + qjson4/QJsonObject.cpp \ + qjson4/QJsonParseError.cpp \ + qjson4/QJsonValue.cpp \ + qjson4/QJsonValueRef.cpp \ + qjson4/QJsonParser.cpp + +isEqual(QT_MAJOR_VERSION, 4): RESOURCES += res/qt4/tr_qt.qrc +isEqual(QT_MAJOR_VERSION, 4): GTA5SYNC_RCC = $$[QT_INSTALL_BINS]/rcc + +# QT5 ONLY STUFF + +isEqual(QT_MAJOR_VERSION, 5): RESOURCES += res/qt5/tr_qt.qrc + +# QT5+ ONLY STUFF + +greaterThan(QT_MAJOR_VERSION, 4): GTA5SYNC_RCC = $$[QT_HOST_BINS]/rcc + +# QT6 ONLY STUFF + +isEqual(QT_MAJOR_VERSION, 6): RESOURCES += res/qt6/tr_qt.qrc + +# RESOURCE COMPILATION + +system($$GTA5SYNC_RCC -threshold 0 -compress 9 $$PWD/res/global.qrc -o $$OUT_PWD/qrc_global.cpp) { + SOURCES += $$OUT_PWD/qrc_global.cpp +} else { + message("Failed to generate qrc_global.cpp") +} + +# PROJECT INSTALLATION + +isEmpty(GTA5SYNC_PREFIX): GTA5SYNC_PREFIX = /usr/local + +appfiles.path = $$GTA5SYNC_PREFIX/share/applications +appfiles.files = $$PWD/res/de.syping.gta5view.desktop +pixmaps.path = $$GTA5SYNC_PREFIX/share/pixmaps +pixmaps.files = $$PWD/res/de.syping.gta5view.png +target.path = $$GTA5SYNC_PREFIX/bin +INSTALLS += target pixmaps appfiles + +# QCONF BASED BUILD STUFF + +contains(DEFINES, GTA5SYNC_QCONF) { + isEqual(QT_MAJOR_VERSION, 4): RESOURCES -= res/qt4/tr_qt.qrc + isEqual(QT_MAJOR_VERSION, 5): RESOURCES -= res/qt5/tr_qt.qrc + isEqual(QT_MAJOR_VERSION, 6): RESOURCES -= res/qt6/tr_qt.qrc + !contains(DEFINES, GTA5SYNC_QCONF_IN) { + RESOURCES -= res/tr_g5p.qrc + langfiles.path = $$GTA5SYNC_PREFIX/share/gta5view/translations + langfiles.files = $$PWD/res/gta5sync_en_US.qm $$PWD/res/gta5sync_de.qm $$PWD/res/gta5sync_fr.qm $$PWD/res/gta5sync_ko.qm $$PWD/res/gta5sync_ru.qm $$PWD/res/gta5sync_uk.qm $$PWD/res/gta5sync_zh_TW.qm $$PWD/res/qtbase_en_GB.qm + INSTALLS += langfiles + } +} + +# TELEMETRY BASED STUFF + +!contains(DEFINES, GTA5SYNC_TELEMETRY) { + SOURCES -= TelemetryClass.cpp \ + tmext/TelemetryClassAuthenticator.cpp + HEADERS -= TelemetryClass.h \ + tmext/TelemetryClassAuthenticator.h +} + +!contains(DEFINES, GTA5SYNC_MOTD) { + SOURCES -= MessageThread.cpp + HEADERS -= MessageThread.h +} else { + lessThan(QT_MAJOR_VERSION, 5) { + SOURCES -= MessageThread.cpp + HEADERS -= MessageThread.h + DEFINES -= GTA5SYNC_MOTD + message("Messages require Qt5 or newer!") + } +} + +# CMAKE BASED STUFF + +greaterThan(QT_MAJOR_VERSION, 4) { + message("Building gta5view with QMake is deprecated, please use CMake instead!") +} diff --git a/lang/README.txt b/lang/README.txt old mode 100755 new mode 100644 index f95afe5..fd72055 --- a/lang/README.txt +++ b/lang/README.txt @@ -1,5 +1,5 @@ -Community translation files - -They get loaded in ApplicationPathExecFileFolder/lang - -You can help translate with using Qt Linguist, after you've translated you'll need to send me a pull request on https://github.com/Syping/gta5sync +Community translation files + +They get loaded in ApplicationPathExecFileFolder/lang + +You can help translate with using Qt Linguist, after you've translated you'll need to send me a pull request on https://github.com/SyDevTeam/gta5view diff --git a/main.cpp b/main.cpp old mode 100755 new mode 100644 index 0a6593b..350d430 --- a/main.cpp +++ b/main.cpp @@ -1,493 +1,310 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "SnapmaticPicture.h" -#include "ProfileDatabase.h" -#include "DatabaseThread.h" -#include "SavegameDialog.h" -#include "PictureDialog.h" -#include "UserInterface.h" -#include "CrewDatabase.h" -#include "SavegameData.h" -#include "IconLoader.h" -#include "AppEnv.h" -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef GTA5SYNC_WIN -#include "windows.h" -#include -#endif - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - a.setApplicationName(GTA5SYNC_APPSTR); - a.setApplicationVersion(GTA5SYNC_APPVER); - -#ifdef GTA5SYNC_WIN -#if QT_VERSION >= 0x050400 - if (QSysInfo::windowsVersion() >= 0x0080) - { - // Get Windows Font - NONCLIENTMETRICS ncm; - ncm.cbSize = sizeof(ncm); - SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0); - LOGFONTW uiFont = ncm.lfMessageFont; - QString uiFontStr(QString::fromStdWString(std::wstring(uiFont.lfFaceName))); - -#ifdef GTA5SYNC_DEBUG - QMessageBox::information(a.desktop(), QApplication::tr("Font"), QApplication::tr("Selected Font: %1").arg(uiFontStr)); -#endif - - // Set Application Font - QFont appFont(uiFontStr, 9); - a.setFont(appFont); - } -#endif -#endif - - QString pluginsDir = AppEnv::getPluginsFolder(); - if (QFileInfo(pluginsDir).exists()) - { - a.addLibraryPath(pluginsDir); - } - - // Loading translation settings - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - settings.beginGroup("Interface"); - QString language = settings.value("Language","System").toString(); - settings.endGroup(); - - // Start external translate loading - QString langpath = AppEnv::getLangFolder(); - bool trsf = false; - bool svlp = false; - QTranslator EappTranslator; - if (language == "System" || language.trimmed() == "") - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "gta5sync_" + langList.at(0) + ".qm")) - { - EappTranslator.load(langpath + QDir::separator() + "/gta5sync_" + langList.at(0) + ".qm"); - QLocale::setDefault(QLocale::system()); - } - } - } - else - { - QString languageName = language; - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "gta5sync_" + langList.at(0) + ".qm")) - { - if (!EappTranslator.load(langpath + QDir::separator() + "gta5sync_" + langList.at(0) + ".qm")) - { - if (langList.at(0) != "en") - { - trsf = true; - } - } - else - { - QLocale::setDefault(QLocale(langList.at(0))); - svlp = true; - } - } - else - { - if (langList.at(0) != "en") - { - trsf = true; - } - } - } - } - if (trsf) - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "gta5sync_" + langList.at(0) + ".qm")) - { - EappTranslator.load(langpath + QDir::separator() + "gta5sync_" + langList.at(0) + ".qm"); - QLocale::setDefault(QLocale(langList.at(0))); - } - } - } - a.installTranslator(&EappTranslator); -#if QT_VERSION >= 0x050000 - QTranslator EqtTranslator1; - if (language == "System" || language.trimmed() == "") - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "qtbase_" + langList.at(0) + ".qm")) - { - EqtTranslator1.load(langpath + QDir::separator() + "qtbase_" + langList.at(0) + ".qm"); - } - } - } - else - { - QString languageName = language; - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "qtbase_" + langList.at(0) + ".qm")) - { - EqtTranslator1.load(langpath + QDir::separator() + "qtbase_" + langList.at(0) + ".qm"); - } - } - } - if (trsf) - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "qtbase_" + langList.at(0) + ".qm")) - { - EqtTranslator1.load(langpath + QDir::separator() + "qtbase_" + langList.at(0) + ".qm"); - } - } - } - a.installTranslator(&EqtTranslator1); -#else - QTranslator EqtTranslator; - if (language == "System" || language.trimmed() == "") - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "qt_" + langList.at(0) + ".qm")) - { - EqtTranslator.load(langpath + QDir::separator() + "qt_" + langList.at(0) + ".qm"); - } - } - } - else - { - QString languageName = language; - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "qt_" + langList.at(0) + ".qm")) - { - EqtTranslator.load(langpath + QDir::separator() + "qt_" + langList.at(0) + ".qm"); - } - } - } - if (trsf) - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(langpath + QDir::separator() + "qt_" + langList.at(0) + ".qm")) - { - EqtTranslator.load(langpath + QDir::separator() + "qt_" + langList.at(0) + ".qm"); - } - } - } - a.installTranslator(&EqtTranslator); -#endif - // End external translate loading - // Start internal translate loading - QTranslator appTranslator; - trsf = false; - if (language == "System" || language.trimmed() == "") - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/gta5sync_" + langList.at(0) + ".qm")) - { - if (!appTranslator.load(":/tr/gta5sync_" + langList.at(0) + ".qm")) - { - if (langList.at(0) != "en") - { - if (svlp) { trsf = true; } - } - } - else - { - QLocale::setDefault(QLocale(langList.at(0))); - } - } - else - { - if (langList.at(0) != "en") - { - if (svlp) { trsf = true; } - } - } - } - } - else if (language == "en" || language == "English") - { - QLocale::setDefault(QLocale(QLocale::English, QLocale::AnyCountry)); - } - else - { - QString languageName = language; - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/gta5sync_" + langList.at(0) + ".qm")) - { - appTranslator.load(":/tr/gta5sync_" + langList.at(0) + ".qm"); - QLocale::setDefault(QLocale(langList.at(0))); - - } - } - } - if (trsf) - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/gta5sync_" + langList.at(0) + ".qm")) - { - appTranslator.load(":/tr/gta5sync_" + langList.at(0) + ".qm"); - QLocale::setDefault(QLocale(langList.at(0))); - } - } - } - a.installTranslator(&appTranslator); -#if QT_VERSION >= 0x050000 - QTranslator qtTranslator1; - if (language == "System" || language.trimmed() == "") - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/qtbase_" + langList.at(0) + ".qm")) - { - qtTranslator1.load(":/tr/qtbase_" + langList.at(0) + ".qm"); - } - } - } - else - { - QString languageName = language; - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/qtbase_" + langList.at(0) + ".qm")) - { - qtTranslator1.load(":/tr/qtbase_" + langList.at(0) + ".qm"); - } - } - } - if (trsf) - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/qtbase_" + langList.at(0) + ".qm")) - { - qtTranslator1.load(":/tr/qtbase_" + langList.at(0) + ".qm"); - } - } - } - a.installTranslator(&qtTranslator1); -#else - QTranslator qtTranslator1; - if (language == "System" || language.trimmed() == "") - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/qt_" + langList.at(0) + ".qm")) - { - qtTranslator1.load(":/tr/qt_" + langList.at(0) + ".qm"); - } - } - } - else - { - QString languageName = language; - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/qt_" + langList.at(0) + ".qm")) - { - qtTranslator1.load(":/tr/qt_" + langList.at(0) + ".qm"); - } - } - } - if (trsf) - { - QString languageName = QLocale::system().name(); - QStringList langList = languageName.split("_"); - if (langList.length() >= 1) - { - if (QFile::exists(":/tr/qt_" + langList.at(0) + ".qm")) - { - qtTranslator1.load(":/tr/qt_" + langList.at(0) + ".qm"); - } - } - } - a.installTranslator(&qtTranslator1); -#endif - // End internal translate loading - - QStringList applicationArgs = a.arguments(); - QString selectedAction; - QString arg1; - applicationArgs.removeAt(0); - - foreach(QString currentArg, applicationArgs) - { - QString reworkedArg; - if (currentArg.left(9) == "-showpic=" && selectedAction == "") - { - reworkedArg = currentArg.remove(0,9); - arg1 = reworkedArg; - selectedAction = "showpic"; - } - else if (currentArg.left(9) == "-showsgd=" && selectedAction == "") - { - reworkedArg = currentArg.remove(0,9); - arg1 = reworkedArg; - selectedAction = "showsgd"; - } - else if (selectedAction == "") - { - QFile argumentFile(currentArg); - QFileInfo argumentFileInfo(argumentFile); - if (argumentFile.exists()) - { - QString argumentFileName = argumentFileInfo.fileName(); - QString argumentFileType = argumentFileName.left(4); - QString argumentFileExt = argumentFileName.right(4); - - if (argumentFileType == "PGTA" || argumentFileExt == ".g5e") - { - arg1 = currentArg; - selectedAction = "showpic"; - } - else if (argumentFileType == "SGTA") - { - arg1 = currentArg; - selectedAction = "showsgd"; - } - else if (argumentFileType == "MISR") - { - arg1 = currentArg; - selectedAction = "showsgd"; - } - } - } - } - - if (selectedAction == "showpic") - { - CrewDatabase crewDB; - ProfileDatabase profileDB; - DatabaseThread threadDB(&crewDB); - PictureDialog picDialog(true, &profileDB, &crewDB); - SnapmaticPicture picture; - - bool readOk = picture.readingPictureFromFile(arg1); - picDialog.setWindowIcon(IconLoader::loadingAppIcon()); - picDialog.setSnapmaticPicture(&picture, readOk); - - int crewID = picture.getSnapmaticProperties().crewID; - if (crewID != 0) { crewDB.addCrew(crewID); } - if (!readOk) { return 1; } - - QEventLoop threadLoop; - QObject::connect(&threadDB, SIGNAL(playerNameFound(int, QString)), &profileDB, SLOT(setPlayerName(int, QString))); - QObject::connect(&threadDB, SIGNAL(playerNameUpdated()), &picDialog, SLOT(playerNameUpdated())); - QObject::connect(&threadDB, SIGNAL(finished()), &threadLoop, SLOT(quit())); - QObject::connect(&picDialog, SIGNAL(endDatabaseThread()), &threadDB, SLOT(doEndThread())); - threadDB.start(); - - picDialog.show(); - - threadLoop.exec(); - - return 0; - } - else if (selectedAction == "showsgd") - { - SavegameDialog savegameDialog; - SavegameData savegame; - - bool readOk = savegame.readingSavegameFromFile(arg1); - savegameDialog.setWindowIcon(IconLoader::loadingAppIcon()); - savegameDialog.setSavegameData(&savegame, arg1, readOk); - - if (!readOk) { return 1; } - - savegameDialog.show(); - - return a.exec(); - } - - CrewDatabase crewDB; - ProfileDatabase profileDB; - DatabaseThread threadDB(&crewDB); - - QEventLoop threadLoop; - QObject::connect(&threadDB, SIGNAL(playerNameFound(int, QString)), &profileDB, SLOT(setPlayerName(int, QString))); - QObject::connect(&threadDB, SIGNAL(finished()), &threadLoop, SLOT(quit())); - threadDB.start(); - - UserInterface uiWindow(&profileDB, &crewDB, &threadDB); - uiWindow.setWindowIcon(IconLoader::loadingAppIcon()); - uiWindow.setupDirEnv(); -#ifdef Q_OS_ANDROID - uiWindow.showMaximized(); -#else - uiWindow.show(); -#endif - - threadLoop.exec(); - - return 0; -} - +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "TranslationClass.h" +#include "SnapmaticPicture.h" +#include "ProfileDatabase.h" +#include "DatabaseThread.h" +#include "SavegameDialog.h" +#include "OptionsDialog.h" +#include "PictureDialog.h" +#include "UserInterface.h" +#include "CrewDatabase.h" +#include "SavegameData.h" +#include "UiModWidget.h" +#include "UiModLabel.h" +#include "IconLoader.h" +#include "AppEnv.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION < 0x060000 +#include +#endif + +#ifdef Q_OS_WIN +#include "windows.h" +#include +#endif + +#ifdef GTA5SYNC_MOTD +#include "MessageThread.h" +#endif + +#ifdef GTA5SYNC_TELEMETRY +#include "TelemetryClass.h" +#endif + +int main(int argc, char *argv[]) +{ +#if QT_VERSION >= 0x050600 +#if QT_VERSION < 0x060000 + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); +#endif +#endif + QApplication a(argc, argv); + a.setApplicationName(GTA5SYNC_APPSTR); + a.setApplicationVersion(GTA5SYNC_APPVER); + a.setQuitOnLastWindowClosed(false); + + QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + settings.beginGroup("Startup"); + +#ifdef GTA5SYNC_TELEMETRY + // Increase Start count at every startup + uint startCount = settings.value("StartCount", 0).toUInt(); + startCount++; + settings.setValue("StartCount", startCount); + settings.sync(); +#endif + + bool customStyle = settings.value("CustomStyle", false).toBool(); + if (customStyle) { + const QString appStyle = settings.value("AppStyle", "Default").toString(); + if (QStyleFactory::keys().contains(appStyle, Qt::CaseInsensitive)) { + a.setStyle(QStyleFactory::create(appStyle)); + } + } + +#ifdef Q_OS_WIN +#if QT_VERSION >= 0x060000 + a.setFont(QApplication::font("QMenu")); +#elif QT_VERSION >= 0x050400 + if (QSysInfo::windowsVersion() >= 0x0080) { + a.setFont(QApplication::font("QMenu")); + } +#endif +#endif + + bool customFont = settings.value("CustomFont", false).toBool(); + if (customFont) { + const QFont appFont = qvariant_cast(settings.value("AppFont", a.font())); + a.setFont(appFont); + } + + QStringList applicationArgs = a.arguments(); + QString selectedAction; + QString arg1; + applicationArgs.removeAt(0); + + Translator->initUserLanguage(); + Translator->loadTranslation(&a); + +#ifdef GTA5SYNC_TELEMETRY + Telemetry->init(); + Telemetry->work(); +#endif + +#ifdef GTA5SYNC_TELEMETRY + bool telemetryWindowLaunched = settings.value("PersonalUsageDataWindowLaunched", false).toBool(); + bool pushUsageData = settings.value("PushUsageData", false).toBool(); + if (!telemetryWindowLaunched && !pushUsageData) { + QDialog *telemetryDialog = new QDialog(); + telemetryDialog->setObjectName(QStringLiteral("TelemetryDialog")); + telemetryDialog->setWindowTitle(QString("%1 %2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER)); + telemetryDialog->setWindowFlags(telemetryDialog->windowFlags()^Qt::WindowContextHelpButtonHint^Qt::WindowCloseButtonHint); + telemetryDialog->setWindowIcon(IconLoader::loadingAppIcon()); + QVBoxLayout *telemetryLayout = new QVBoxLayout(telemetryDialog); + telemetryLayout->setObjectName(QStringLiteral("TelemetryLayout")); + telemetryDialog->setLayout(telemetryLayout); + UiModLabel *telemetryLabel = new UiModLabel(telemetryDialog); + telemetryLabel->setObjectName(QStringLiteral("TelemetryLabel")); + telemetryLabel->setText(QString("

%2

%1").arg( + QApplication::translate("TelemetryDialog", "You want help %1 to improve in the future by including personal usage data in your submission?").arg(GTA5SYNC_APPSTR), + QApplication::translate("TelemetryDialog", "%1 User Statistics").arg(GTA5SYNC_APPSTR))); + telemetryLayout->addWidget(telemetryLabel); + QCheckBox *telemetryCheckBox = new QCheckBox(telemetryDialog); + telemetryCheckBox->setObjectName(QStringLiteral("TelemetryCheckBox")); + telemetryCheckBox->setText(QApplication::translate("TelemetryDialog", "Yes, I want include personal usage data.")); + telemetryLayout->addWidget(telemetryCheckBox); + QHBoxLayout *telemetryButtonLayout = new QHBoxLayout(); + telemetryButtonLayout->setObjectName(QStringLiteral("TelemetryButtonLayout")); + telemetryLayout->addLayout(telemetryButtonLayout); + QSpacerItem *telemetryButtonSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); + telemetryButtonLayout->addSpacerItem(telemetryButtonSpacer); + QPushButton *telemetryButton = new QPushButton(telemetryDialog); + telemetryButton->setObjectName(QStringLiteral("TelemetryButton")); + telemetryButton->setText(QApplication::translate("TelemetryDialog", "&OK")); + telemetryButtonLayout->addWidget(telemetryButton); + QObject::connect(telemetryButton, SIGNAL(clicked(bool)), telemetryDialog, SLOT(close())); + telemetryDialog->setFixedSize(telemetryDialog->sizeHint()); + telemetryDialog->exec(); + QObject::disconnect(telemetryButton, SIGNAL(clicked(bool)), telemetryDialog, SLOT(close())); + if (telemetryCheckBox->isChecked()) { + QSettings telemetrySettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + telemetrySettings.beginGroup("Telemetry"); + telemetrySettings.setValue("PushUsageData", true); + telemetrySettings.setValue("PushAppConf", true); + telemetrySettings.endGroup(); + telemetrySettings.sync(); + Telemetry->init(); + Telemetry->work(); + } + settings.setValue("PersonalUsageDataWindowLaunched", true); + delete telemetryDialog; + } +#endif + settings.endGroup(); + + for (const QString ¤tArg : applicationArgs) { + QString reworkedArg; + if (currentArg.left(9) == "-showpic=" && selectedAction == "") { + reworkedArg = QString(currentArg).remove(0,9); + arg1 = reworkedArg; + selectedAction = "showpic"; + } + else if (currentArg.left(9) == "-showsgd=" && selectedAction == "") { + reworkedArg = QString(currentArg).remove(0,9); + arg1 = reworkedArg; + selectedAction = "showsgd"; + } + else if (selectedAction == "") { + QFile argumentFile(currentArg); + QFileInfo argumentFileInfo(argumentFile); + if (argumentFile.exists()) { + QString argumentFileName = argumentFileInfo.fileName(); + QString argumentFileType = argumentFileName.left(4); + QString argumentFileExt = argumentFileName.right(4); + + if (argumentFileType == "PGTA" || argumentFileExt == ".g5e") { + arg1 = currentArg; + selectedAction = "showpic"; + } + else if (argumentFileType == "SGTA") { + arg1 = currentArg; + selectedAction = "showsgd"; + } + else if (argumentFileType == "MISR") { + arg1 = currentArg; + selectedAction = "showsgd"; + } + } + } + } + + if (selectedAction == "showpic") { + CrewDatabase crewDB; + ProfileDatabase profileDB; + DatabaseThread threadDB(&crewDB); + PictureDialog picDialog(true, &profileDB, &crewDB); + SnapmaticPicture picture; + + bool readOk = picture.readingPictureFromFile(arg1); + picDialog.setWindowIcon(IconLoader::loadingAppIcon()); + picDialog.setSnapmaticPicture(&picture, readOk); + picDialog.setWindowFlags(picDialog.windowFlags()^Qt::Dialog^Qt::Window); + + int crewID = picture.getSnapmaticProperties().crewID; + if (crewID != 0) + crewDB.addCrew(crewID); + if (!readOk) + return 1; + + QObject::connect(&threadDB, SIGNAL(crewNameFound(int, QString)), &crewDB, SLOT(setCrewName(int, QString))); + QObject::connect(&threadDB, SIGNAL(crewNameUpdated()), &picDialog, SLOT(crewNameUpdated())); + QObject::connect(&threadDB, SIGNAL(playerNameFound(int, QString)), &profileDB, SLOT(setPlayerName(int, QString))); + QObject::connect(&threadDB, SIGNAL(playerNameUpdated()), &picDialog, SLOT(playerNameUpdated())); + QObject::connect(&threadDB, SIGNAL(finished()), &a, SLOT(quit())); + QObject::connect(&picDialog, SIGNAL(endDatabaseThread()), &threadDB, SLOT(terminateThread())); + threadDB.start(); + + picDialog.show(); + + return a.exec(); + } + else if (selectedAction == "showsgd") { + SavegameDialog savegameDialog; + SavegameData savegame; + + bool readOk = savegame.readingSavegameFromFile(arg1); + savegameDialog.setWindowIcon(IconLoader::loadingAppIcon()); + savegameDialog.setSavegameData(&savegame, arg1, readOk); + savegameDialog.setWindowFlags(savegameDialog.windowFlags()^Qt::Dialog^Qt::Window); + + if (!readOk) + return 1; + + a.setQuitOnLastWindowClosed(true); + savegameDialog.show(); + + return a.exec(); + } + + CrewDatabase crewDB; + ProfileDatabase profileDB; + DatabaseThread threadDB(&crewDB); + + QObject::connect(&threadDB, SIGNAL(crewNameFound(int,QString)), &crewDB, SLOT(setCrewName(int, QString))); + QObject::connect(&threadDB, SIGNAL(playerNameFound(int, QString)), &profileDB, SLOT(setPlayerName(int, QString))); + QObject::connect(&threadDB, SIGNAL(finished()), &a, SLOT(quit())); + threadDB.start(); + +#ifdef GTA5SYNC_MOTD + uint cacheId; + { + QSettings messageSettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); + messageSettings.beginGroup("Messages"); + cacheId = messageSettings.value("CacheId", 0).toUInt(); + messageSettings.endGroup(); + } + MessageThread threadMessage(cacheId); + QObject::connect(&threadMessage, SIGNAL(finished()), &threadDB, SLOT(terminateThread())); + threadMessage.start(); +#endif + +#ifdef GTA5SYNC_MOTD + UserInterface uiWindow(&profileDB, &crewDB, &threadDB, &threadMessage); + QObject::connect(&threadMessage, SIGNAL(messagesArrived(QJsonObject)), &uiWindow, SLOT(messagesArrived(QJsonObject))); + QObject::connect(&threadMessage, SIGNAL(updateCacheId(uint)), &uiWindow, SLOT(updateCacheId(uint))); +#else + UserInterface uiWindow(&profileDB, &crewDB, &threadDB); +#endif + uiWindow.setWindowIcon(IconLoader::loadingAppIcon()); +#ifdef GTA5SYNC_FLATPAK + uiWindow.setupDirEnv(false); +#else + uiWindow.setupDirEnv(); +#endif +#ifdef Q_OS_ANDROID + uiWindow.showMaximized(); +#else + uiWindow.show(); +#endif + + return a.exec(); +} diff --git a/pcg/LICENSE.txt b/pcg/LICENSE.txt new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/pcg/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/pcg/pcg_basic.c b/pcg/pcg_basic.c new file mode 100644 index 0000000..8c2fd0d --- /dev/null +++ b/pcg/pcg_basic.c @@ -0,0 +1,116 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014 Melissa O'Neill + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For additional information about the PCG random number generation scheme, + * including its license and other licensing options, visit + * + * http://www.pcg-random.org + */ + +/* + * This code is derived from the full C implementation, which is in turn + * derived from the canonical C++ PCG implementation. The C++ version + * has many additional features and is preferable if you can use C++ in + * your project. + */ + +#include "pcg_basic.h" + +// state for global RNGs + +static pcg32_random_t pcg32_global = PCG32_INITIALIZER; + +// pcg32_srandom(initstate, initseq) +// pcg32_srandom_r(rng, initstate, initseq): +// Seed the rng. Specified in two parts, state initializer and a +// sequence selection constant (a.k.a. stream id) + +void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq) +{ + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg32_random_r(rng); + rng->state += initstate; + pcg32_random_r(rng); +} + +void pcg32_srandom(uint64_t seed, uint64_t seq) +{ + pcg32_srandom_r(&pcg32_global, seed, seq); +} + +// pcg32_random() +// pcg32_random_r(rng) +// Generate a uniformly distributed 32-bit random number + +uint32_t pcg32_random_r(pcg32_random_t* rng) +{ + uint64_t oldstate = rng->state; + rng->state = oldstate * 6364136223846793005ULL + rng->inc; + uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u; + uint32_t rot = oldstate >> 59u; + return (xorshifted >> rot) | (xorshifted << ((-rot) & 31)); +} + +uint32_t pcg32_random() +{ + return pcg32_random_r(&pcg32_global); +} + + +// pcg32_boundedrand(bound): +// pcg32_boundedrand_r(rng, bound): +// Generate a uniformly distributed number, r, where 0 <= r < bound + +uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound) +{ + // To avoid bias, we need to make the range of the RNG a multiple of + // bound, which we do by dropping output less than a threshold. + // A naive scheme to calculate the threshold would be to do + // + // uint32_t threshold = 0x100000000ull % bound; + // + // but 64-bit div/mod is slower than 32-bit div/mod (especially on + // 32-bit platforms). In essence, we do + // + // uint32_t threshold = (0x100000000ull-bound) % bound; + // + // because this version will calculate the same modulus, but the LHS + // value is less than 2^32. + + uint32_t threshold = -bound % bound; + + // Uniformity guarantees that this loop will terminate. In practice, it + // should usually terminate quickly; on average (assuming all bounds are + // equally likely), 82.25% of the time, we can expect it to require just + // one iteration. In the worst case, someone passes a bound of 2^31 + 1 + // (i.e., 2147483649), which invalidates almost 50% of the range. In + // practice, bounds are typically small and only a tiny amount of the range + // is eliminated. + for (;;) { + uint32_t r = pcg32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + + +uint32_t pcg32_boundedrand(uint32_t bound) +{ + return pcg32_boundedrand_r(&pcg32_global, bound); +} + diff --git a/pcg/pcg_basic.h b/pcg/pcg_basic.h new file mode 100644 index 0000000..e2b526a --- /dev/null +++ b/pcg/pcg_basic.h @@ -0,0 +1,78 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014 Melissa O'Neill + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For additional information about the PCG random number generation scheme, + * including its license and other licensing options, visit + * + * http://www.pcg-random.org + */ + +/* + * This code is derived from the full C implementation, which is in turn + * derived from the canonical C++ PCG implementation. The C++ version + * has many additional features and is preferable if you can use C++ in + * your project. + */ + +#ifndef PCG_BASIC_H_INCLUDED +#define PCG_BASIC_H_INCLUDED 1 + +#include + +#if __cplusplus +extern "C" { +#endif + +struct pcg_state_setseq_64 { // Internals are *Private*. + uint64_t state; // RNG state. All values are possible. + uint64_t inc; // Controls which RNG sequence (stream) is + // selected. Must *always* be odd. +}; +typedef struct pcg_state_setseq_64 pcg32_random_t; + +// If you *must* statically initialize it, here's one. + +#define PCG32_INITIALIZER { 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL } + +// pcg32_srandom(initstate, initseq) +// pcg32_srandom_r(rng, initstate, initseq): +// Seed the rng. Specified in two parts, state initializer and a +// sequence selection constant (a.k.a. stream id) + +void pcg32_srandom(uint64_t initstate, uint64_t initseq); +void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, + uint64_t initseq); + +// pcg32_random() +// pcg32_random_r(rng) +// Generate a uniformly distributed 32-bit random number + +uint32_t pcg32_random(void); +uint32_t pcg32_random_r(pcg32_random_t* rng); + +// pcg32_boundedrand(bound): +// pcg32_boundedrand_r(rng, bound): +// Generate a uniformly distributed number, r, where 0 <= r < bound + +uint32_t pcg32_boundedrand(uint32_t bound); +uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound); + +#if __cplusplus +} +#endif + +#endif // PCG_BASIC_H_INCLUDED diff --git a/qjson4/QJsonArray b/qjson4/QJsonArray old mode 100755 new mode 100644 index 93afb31..89dbf4e --- a/qjson4/QJsonArray +++ b/qjson4/QJsonArray @@ -1 +1 @@ -#include "QJsonArray.h" +#include "QJsonArray.h" diff --git a/qjson4/QJsonArray.cpp b/qjson4/QJsonArray.cpp old mode 100755 new mode 100644 index f932825..ad8a82b --- a/qjson4/QJsonArray.cpp +++ b/qjson4/QJsonArray.cpp @@ -1,410 +1,410 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "QJsonArray.h" -#include "QJsonValueRef.h" -#include "QJsonValue.h" -#include - -#if QT_VERSION < 0x050000 - -//------------------------------------------------------------------------------ -// Name: QJsonArray -// Desc: default constructor -//------------------------------------------------------------------------------ -QJsonArray::QJsonArray() { - -} - -//------------------------------------------------------------------------------ -// Name: QJsonArray -// Desc: copy constructor -//------------------------------------------------------------------------------ -QJsonArray::QJsonArray(const QJsonArray &other) : values_(other.values_) { - -} - -#if __cplusplus >= 201103L -//------------------------------------------------------------------------------ -// Name: QJsonArray -// Desc: Creates an array initialized from args initialization list. -//------------------------------------------------------------------------------ -QJsonArray::QJsonArray(std::initializer_list args) { - for(const QJsonValue &arg : args) { - values_.append(arg); - } -} -#endif - -//------------------------------------------------------------------------------ -// Name: ~QJsonArray -// Desc: destructor -//------------------------------------------------------------------------------ -QJsonArray::~QJsonArray() { - -} - -//------------------------------------------------------------------------------ -// Name: operator= -// Desc: assignment operator -//------------------------------------------------------------------------------ -QJsonArray &QJsonArray::operator=(const QJsonArray &other) { - QJsonArray(other).swap(*this); - return *this; -} - -//------------------------------------------------------------------------------ -// Name: operator+= -// Desc: -//------------------------------------------------------------------------------ -QJsonArray &QJsonArray::operator+=(const QJsonValue &value) { - values_.append(value); - return *this; -} - -//------------------------------------------------------------------------------ -// Name: operator<< -// Desc: -//------------------------------------------------------------------------------ -QJsonArray &QJsonArray::operator<<(const QJsonValue &value) { - values_.append(value); - return *this; -} - -//------------------------------------------------------------------------------ -// Name: operator+ -// Desc: -//------------------------------------------------------------------------------ -QJsonArray QJsonArray::operator+(const QJsonValue &value) const { - QJsonArray arr(*this); - arr.append(value); - return arr; -} - -//------------------------------------------------------------------------------ -// Name: operator!= -// Desc: returns true if the compared array IS NOT equal to this -//------------------------------------------------------------------------------ -bool QJsonArray::operator!=(const QJsonArray &other) const { - return values_ != other.values_; -} - -//------------------------------------------------------------------------------ -// Name: operator== -// Desc: returns true if the compared array IS equal to this -//------------------------------------------------------------------------------ -bool QJsonArray::operator==(const QJsonArray &other) const { - return values_ == other.values_; -} - -//------------------------------------------------------------------------------ -// Name: begin -// Desc: returns an iterator to the first contained element -//------------------------------------------------------------------------------ -QJsonArray::const_iterator QJsonArray::begin() const { - return values_.begin(); -} - -//------------------------------------------------------------------------------ -// Name: end -// Desc: returns an iterator to one past the last contained element -//------------------------------------------------------------------------------ -QJsonArray::const_iterator QJsonArray::end() const { - return values_.end(); -} - -//------------------------------------------------------------------------------ -// Name: begin -// Desc: returns an iterator to the first contained element -//------------------------------------------------------------------------------ -QJsonArray::iterator QJsonArray::begin() { - return values_.begin(); -} - -//------------------------------------------------------------------------------ -// Name: end -// Desc: returns an iterator to one past the last contained element -//------------------------------------------------------------------------------ -QJsonArray::iterator QJsonArray::end() { - return values_.end(); -} - -//------------------------------------------------------------------------------ -// Name: constBegin -// Desc: returns an iterator to the first contained element -//------------------------------------------------------------------------------ -QJsonArray::const_iterator QJsonArray::constBegin() const { - return begin(); -} - -//------------------------------------------------------------------------------ -// Name: constEnd -// Desc: returns an iterator to one past the last contained element -//------------------------------------------------------------------------------ -QJsonArray::const_iterator QJsonArray::constEnd() const { - return end(); -} - -//------------------------------------------------------------------------------ -// Name: first -// Desc: returns the first element by value -//------------------------------------------------------------------------------ -QJsonValue QJsonArray::first() const { - Q_ASSERT(!empty()); - return values_.first(); -} - -//------------------------------------------------------------------------------ -// Name: last -// Desc: returns the last element by value -//------------------------------------------------------------------------------ -QJsonValue QJsonArray::last() const { - Q_ASSERT(!empty()); - return values_.last(); -} - -//------------------------------------------------------------------------------ -// Name: operator[] -//------------------------------------------------------------------------------ -QJsonValueRef QJsonArray::operator[](int i) { - return QJsonValueRef(this, i); -} - -//------------------------------------------------------------------------------ -// Name: operator[] -//------------------------------------------------------------------------------ -QJsonValue QJsonArray::operator[](int i) const { - return values_[i]; -} - -//------------------------------------------------------------------------------ -// Name: at -//------------------------------------------------------------------------------ -QJsonValue QJsonArray::at(int i) const { - return values_.at(i); -} - -//------------------------------------------------------------------------------ -// Name: size -//------------------------------------------------------------------------------ -int QJsonArray::size() const { - return values_.size(); -} - -//------------------------------------------------------------------------------ -// Name: count -//------------------------------------------------------------------------------ -int QJsonArray::count() const { - return size(); -} - -//------------------------------------------------------------------------------ -// Name: empty -//------------------------------------------------------------------------------ -bool QJsonArray::empty() const { - return values_.empty(); -} - -//------------------------------------------------------------------------------ -// Name: isEmpty -//------------------------------------------------------------------------------ -bool QJsonArray::isEmpty() const { - return empty(); -} - -//------------------------------------------------------------------------------ -// Name: pop_back -//------------------------------------------------------------------------------ -void QJsonArray::pop_back() { - values_.pop_back(); -} - -//------------------------------------------------------------------------------ -// Name: pop_front -//------------------------------------------------------------------------------ -void QJsonArray::pop_front() { - values_.pop_front(); -} - -//------------------------------------------------------------------------------ -// Name: push_back -//------------------------------------------------------------------------------ -void QJsonArray::push_back(const QJsonValue &value) { - values_.push_back(value); -} - -//------------------------------------------------------------------------------ -// Name: push_front -//------------------------------------------------------------------------------ -void QJsonArray::push_front(const QJsonValue &value) { - values_.push_front(value); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -void QJsonArray::append(const QJsonValue &value) { - values_.append(value); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -bool QJsonArray::contains(const QJsonValue &value) const { - return values_.contains(value); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonArray::iterator QJsonArray::erase(iterator it) { - return values_.erase(it); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -void QJsonArray::insert(int i, const QJsonValue &value) { - values_.insert(i, value); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonArray::iterator QJsonArray::insert(iterator before, const QJsonValue &value) { - return values_.insert(before, value); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -void QJsonArray::prepend(const QJsonValue &value) { - values_.prepend(value); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -void QJsonArray::removeAt(int i) { - values_.removeAt(i); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -void QJsonArray::removeFirst() { - values_.removeFirst(); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -void QJsonArray::removeLast() { - values_.removeLast(); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -void QJsonArray::replace(int i, const QJsonValue &value) { - values_.replace(i, value); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonValue QJsonArray::takeAt(int i) { - return values_.takeAt(i); -} - -//------------------------------------------------------------------------------ -// Name: toVariantList -//------------------------------------------------------------------------------ -QVariantList QJsonArray::toVariantList() const { - QVariantList a; - Q_FOREACH(const QJsonValue &v, *this) { - a.push_back(v.toVariant()); - } - return a; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonArray QJsonArray::fromStringList(const QStringList &list) { - QJsonArray a; - Q_FOREACH(const QString &s, list) { - a.push_back(QJsonValue(s)); - } - return a; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonArray QJsonArray::fromVariantList(const QVariantList &list) { - QJsonArray a; - Q_FOREACH(const QVariant &v, list) { - a.push_back(QJsonValue::fromVariant(v)); - } - return a; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonRoot *QJsonArray::clone() const { - return new QJsonArray(*this); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -const QJsonObject *QJsonArray::toObject() const { - return 0; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject *QJsonArray::toObject() { - return 0; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonArray *QJsonArray::toArray() { - return this; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -const QJsonArray *QJsonArray::toArray() const { - return this; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -void QJsonArray::swap(QJsonArray &other) { - qSwap(values_, other.values_); -} - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "QJsonArray.h" +#include "QJsonValueRef.h" +#include "QJsonValue.h" +#include + +#if QT_VERSION < 0x050000 + +//------------------------------------------------------------------------------ +// Name: QJsonArray +// Desc: default constructor +//------------------------------------------------------------------------------ +QJsonArray::QJsonArray() { + +} + +//------------------------------------------------------------------------------ +// Name: QJsonArray +// Desc: copy constructor +//------------------------------------------------------------------------------ +QJsonArray::QJsonArray(const QJsonArray &other) : values_(other.values_) { + +} + +#if __cplusplus >= 201103L +//------------------------------------------------------------------------------ +// Name: QJsonArray +// Desc: Creates an array initialized from args initialization list. +//------------------------------------------------------------------------------ +QJsonArray::QJsonArray(std::initializer_list args) { + for(const QJsonValue &arg : args) { + values_.append(arg); + } +} +#endif + +//------------------------------------------------------------------------------ +// Name: ~QJsonArray +// Desc: destructor +//------------------------------------------------------------------------------ +QJsonArray::~QJsonArray() { + +} + +//------------------------------------------------------------------------------ +// Name: operator= +// Desc: assignment operator +//------------------------------------------------------------------------------ +QJsonArray &QJsonArray::operator=(const QJsonArray &other) { + QJsonArray(other).swap(*this); + return *this; +} + +//------------------------------------------------------------------------------ +// Name: operator+= +// Desc: +//------------------------------------------------------------------------------ +QJsonArray &QJsonArray::operator+=(const QJsonValue &value) { + values_.append(value); + return *this; +} + +//------------------------------------------------------------------------------ +// Name: operator<< +// Desc: +//------------------------------------------------------------------------------ +QJsonArray &QJsonArray::operator<<(const QJsonValue &value) { + values_.append(value); + return *this; +} + +//------------------------------------------------------------------------------ +// Name: operator+ +// Desc: +//------------------------------------------------------------------------------ +QJsonArray QJsonArray::operator+(const QJsonValue &value) const { + QJsonArray arr(*this); + arr.append(value); + return arr; +} + +//------------------------------------------------------------------------------ +// Name: operator!= +// Desc: returns true if the compared array IS NOT equal to this +//------------------------------------------------------------------------------ +bool QJsonArray::operator!=(const QJsonArray &other) const { + return values_ != other.values_; +} + +//------------------------------------------------------------------------------ +// Name: operator== +// Desc: returns true if the compared array IS equal to this +//------------------------------------------------------------------------------ +bool QJsonArray::operator==(const QJsonArray &other) const { + return values_ == other.values_; +} + +//------------------------------------------------------------------------------ +// Name: begin +// Desc: returns an iterator to the first contained element +//------------------------------------------------------------------------------ +QJsonArray::const_iterator QJsonArray::begin() const { + return values_.begin(); +} + +//------------------------------------------------------------------------------ +// Name: end +// Desc: returns an iterator to one past the last contained element +//------------------------------------------------------------------------------ +QJsonArray::const_iterator QJsonArray::end() const { + return values_.end(); +} + +//------------------------------------------------------------------------------ +// Name: begin +// Desc: returns an iterator to the first contained element +//------------------------------------------------------------------------------ +QJsonArray::iterator QJsonArray::begin() { + return values_.begin(); +} + +//------------------------------------------------------------------------------ +// Name: end +// Desc: returns an iterator to one past the last contained element +//------------------------------------------------------------------------------ +QJsonArray::iterator QJsonArray::end() { + return values_.end(); +} + +//------------------------------------------------------------------------------ +// Name: constBegin +// Desc: returns an iterator to the first contained element +//------------------------------------------------------------------------------ +QJsonArray::const_iterator QJsonArray::constBegin() const { + return begin(); +} + +//------------------------------------------------------------------------------ +// Name: constEnd +// Desc: returns an iterator to one past the last contained element +//------------------------------------------------------------------------------ +QJsonArray::const_iterator QJsonArray::constEnd() const { + return end(); +} + +//------------------------------------------------------------------------------ +// Name: first +// Desc: returns the first element by value +//------------------------------------------------------------------------------ +QJsonValue QJsonArray::first() const { + Q_ASSERT(!empty()); + return values_.first(); +} + +//------------------------------------------------------------------------------ +// Name: last +// Desc: returns the last element by value +//------------------------------------------------------------------------------ +QJsonValue QJsonArray::last() const { + Q_ASSERT(!empty()); + return values_.last(); +} + +//------------------------------------------------------------------------------ +// Name: operator[] +//------------------------------------------------------------------------------ +QJsonValueRef QJsonArray::operator[](int i) { + return QJsonValueRef(this, i); +} + +//------------------------------------------------------------------------------ +// Name: operator[] +//------------------------------------------------------------------------------ +QJsonValue QJsonArray::operator[](int i) const { + return values_[i]; +} + +//------------------------------------------------------------------------------ +// Name: at +//------------------------------------------------------------------------------ +QJsonValue QJsonArray::at(int i) const { + return values_.at(i); +} + +//------------------------------------------------------------------------------ +// Name: size +//------------------------------------------------------------------------------ +int QJsonArray::size() const { + return values_.size(); +} + +//------------------------------------------------------------------------------ +// Name: count +//------------------------------------------------------------------------------ +int QJsonArray::count() const { + return size(); +} + +//------------------------------------------------------------------------------ +// Name: empty +//------------------------------------------------------------------------------ +bool QJsonArray::empty() const { + return values_.empty(); +} + +//------------------------------------------------------------------------------ +// Name: isEmpty +//------------------------------------------------------------------------------ +bool QJsonArray::isEmpty() const { + return empty(); +} + +//------------------------------------------------------------------------------ +// Name: pop_back +//------------------------------------------------------------------------------ +void QJsonArray::pop_back() { + values_.pop_back(); +} + +//------------------------------------------------------------------------------ +// Name: pop_front +//------------------------------------------------------------------------------ +void QJsonArray::pop_front() { + values_.pop_front(); +} + +//------------------------------------------------------------------------------ +// Name: push_back +//------------------------------------------------------------------------------ +void QJsonArray::push_back(const QJsonValue &value) { + values_.push_back(value); +} + +//------------------------------------------------------------------------------ +// Name: push_front +//------------------------------------------------------------------------------ +void QJsonArray::push_front(const QJsonValue &value) { + values_.push_front(value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::append(const QJsonValue &value) { + values_.append(value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +bool QJsonArray::contains(const QJsonValue &value) const { + return values_.contains(value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonArray::iterator QJsonArray::erase(iterator it) { + return values_.erase(it); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::insert(int i, const QJsonValue &value) { + values_.insert(i, value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonArray::iterator QJsonArray::insert(iterator before, const QJsonValue &value) { + return values_.insert(before, value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::prepend(const QJsonValue &value) { + values_.prepend(value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::removeAt(int i) { + values_.removeAt(i); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::removeFirst() { + values_.removeFirst(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::removeLast() { + values_.removeLast(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::replace(int i, const QJsonValue &value) { + values_.replace(i, value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonValue QJsonArray::takeAt(int i) { + return values_.takeAt(i); +} + +//------------------------------------------------------------------------------ +// Name: toVariantList +//------------------------------------------------------------------------------ +QVariantList QJsonArray::toVariantList() const { + QVariantList a; + Q_FOREACH(const QJsonValue &v, *this) { + a.push_back(v.toVariant()); + } + return a; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonArray QJsonArray::fromStringList(const QStringList &list) { + QJsonArray a; + Q_FOREACH(const QString &s, list) { + a.push_back(QJsonValue(s)); + } + return a; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonArray QJsonArray::fromVariantList(const QVariantList &list) { + QJsonArray a; + Q_FOREACH(const QVariant &v, list) { + a.push_back(QJsonValue::fromVariant(v)); + } + return a; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonRoot *QJsonArray::clone() const { + return new QJsonArray(*this); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +const QJsonObject *QJsonArray::toObject() const { + return 0; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject *QJsonArray::toObject() { + return 0; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonArray *QJsonArray::toArray() { + return this; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +const QJsonArray *QJsonArray::toArray() const { + return this; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonArray::swap(QJsonArray &other) { + qSwap(values_, other.values_); +} + +#endif diff --git a/qjson4/QJsonArray.h b/qjson4/QJsonArray.h old mode 100755 new mode 100644 index 2e443b4..dc4fc69 --- a/qjson4/QJsonArray.h +++ b/qjson4/QJsonArray.h @@ -1,139 +1,139 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef QJSON_ARRAY_H_ -#define QJSON_ARRAY_H_ - -#include - -#if QT_VERSION >= 0x050000 -#include -#else - -#include "QJsonRoot.h" -#include -#include - -class QJsonValue; -class QJsonValueRef; - -class QJsonArray : public QJsonRoot { - friend class QJsonDocument; - friend class QJsonValue; - friend class QJsonValueRef; - friend class QJsonParser; -public: - // TODO(eteran): manually implement the array, for now we use QList - // but the real thing has a custom implementation - // I guess for the purposes of less interdependancies? - // maybe so it's easier to forward declare the iterators? - - typedef QList::const_iterator const_iterator; - typedef QList::iterator iterator; - typedef const_iterator ConstIterator; - typedef iterator Iterator; - typedef QList::const_pointer const_pointer; - typedef QList::const_reference const_reference; - typedef QList::difference_type difference_type; - typedef QList::pointer pointer; - typedef QList::reference reference; - typedef QList::size_type size_type; - typedef QList::value_type value_type; - -public: - QJsonArray(); - QJsonArray(const QJsonArray &other); -#if __cplusplus >= 201103L - QJsonArray(std::initializer_list args); -#endif - ~QJsonArray(); - -public: - QJsonArray &operator=(const QJsonArray &other); - -public: - bool operator!=(const QJsonArray &other) const; - bool operator==(const QJsonArray &other) const; - QJsonArray operator+(const QJsonValue &value) const; - QJsonArray &operator+=(const QJsonValue &value); - QJsonArray &operator<<(const QJsonValue &value); - -public: - const_iterator begin() const; - const_iterator end() const; - iterator begin(); - iterator end(); - const_iterator constBegin() const; - const_iterator constEnd() const; - -public: - QJsonValueRef operator[](int i); - QJsonValue operator[](int i) const; - QJsonValue at(int i) const; - QJsonValue first() const; - QJsonValue last() const; - -public: - int size() const; - int count() const; - bool empty() const; - bool isEmpty() const; - -public: - void pop_back(); - void pop_front(); - void push_back(const QJsonValue &value); - void push_front(const QJsonValue &value); - -public: - void append(const QJsonValue &value); - bool contains(const QJsonValue &value) const; - iterator erase(iterator it); - void insert(int i, const QJsonValue &value); - iterator insert(iterator before, const QJsonValue &value); - void prepend(const QJsonValue &value); - void removeAt(int i); - void removeFirst(); - void removeLast(); - void replace(int i, const QJsonValue &value); - QJsonValue takeAt(int i); - -public: - QVariantList toVariantList() const; - -public: - static QJsonArray fromStringList(const QStringList &list); - static QJsonArray fromVariantList(const QVariantList &list); - -private: - virtual QJsonRoot *clone() const; - virtual QJsonArray *toArray(); - virtual QJsonObject *toObject(); - virtual const QJsonArray *toArray() const; - virtual const QJsonObject *toObject() const; - -private: - void swap(QJsonArray &other); - -private: - QList values_; -}; - -#endif - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef QJSON_ARRAY_H_ +#define QJSON_ARRAY_H_ + +#include + +#if QT_VERSION >= 0x050000 +#include +#else + +#include "QJsonRoot.h" +#include +#include + +class QJsonValue; +class QJsonValueRef; + +class QJsonArray : public QJsonRoot { + friend class QJsonDocument; + friend class QJsonValue; + friend class QJsonValueRef; + friend class QJsonParser; +public: + // TODO(eteran): manually implement the array, for now we use QList + // but the real thing has a custom implementation + // I guess for the purposes of less interdependancies? + // maybe so it's easier to forward declare the iterators? + + typedef QList::const_iterator const_iterator; + typedef QList::iterator iterator; + typedef const_iterator ConstIterator; + typedef iterator Iterator; + typedef QList::const_pointer const_pointer; + typedef QList::const_reference const_reference; + typedef QList::difference_type difference_type; + typedef QList::pointer pointer; + typedef QList::reference reference; + typedef QList::size_type size_type; + typedef QList::value_type value_type; + +public: + QJsonArray(); + QJsonArray(const QJsonArray &other); +#if __cplusplus >= 201103L + QJsonArray(std::initializer_list args); +#endif + ~QJsonArray(); + +public: + QJsonArray &operator=(const QJsonArray &other); + +public: + bool operator!=(const QJsonArray &other) const; + bool operator==(const QJsonArray &other) const; + QJsonArray operator+(const QJsonValue &value) const; + QJsonArray &operator+=(const QJsonValue &value); + QJsonArray &operator<<(const QJsonValue &value); + +public: + const_iterator begin() const; + const_iterator end() const; + iterator begin(); + iterator end(); + const_iterator constBegin() const; + const_iterator constEnd() const; + +public: + QJsonValueRef operator[](int i); + QJsonValue operator[](int i) const; + QJsonValue at(int i) const; + QJsonValue first() const; + QJsonValue last() const; + +public: + int size() const; + int count() const; + bool empty() const; + bool isEmpty() const; + +public: + void pop_back(); + void pop_front(); + void push_back(const QJsonValue &value); + void push_front(const QJsonValue &value); + +public: + void append(const QJsonValue &value); + bool contains(const QJsonValue &value) const; + iterator erase(iterator it); + void insert(int i, const QJsonValue &value); + iterator insert(iterator before, const QJsonValue &value); + void prepend(const QJsonValue &value); + void removeAt(int i); + void removeFirst(); + void removeLast(); + void replace(int i, const QJsonValue &value); + QJsonValue takeAt(int i); + +public: + QVariantList toVariantList() const; + +public: + static QJsonArray fromStringList(const QStringList &list); + static QJsonArray fromVariantList(const QVariantList &list); + +private: + virtual QJsonRoot *clone() const; + virtual QJsonArray *toArray(); + virtual QJsonObject *toObject(); + virtual const QJsonArray *toArray() const; + virtual const QJsonObject *toObject() const; + +private: + void swap(QJsonArray &other); + +private: + QList values_; +}; + +#endif + +#endif diff --git a/qjson4/QJsonDocument b/qjson4/QJsonDocument old mode 100755 new mode 100644 index dabae9b..f652bf4 --- a/qjson4/QJsonDocument +++ b/qjson4/QJsonDocument @@ -1 +1 @@ -#include "QJsonDocument.h" +#include "QJsonDocument.h" diff --git a/qjson4/QJsonDocument.cpp b/qjson4/QJsonDocument.cpp old mode 100755 new mode 100644 index 712a96d..7f8ad18 --- a/qjson4/QJsonDocument.cpp +++ b/qjson4/QJsonDocument.cpp @@ -1,417 +1,424 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "QJsonDocument.h" -#include "QJsonObject.h" -#include "QJsonArray.h" -#include "QJsonParser.h" - -#include -#include -#include -#include -#include - -#if QT_VERSION < 0x050000 - -//------------------------------------------------------------------------------ -// Name: QJsonDocument -//------------------------------------------------------------------------------ -QJsonDocument::QJsonDocument() : root_(0) { -} - -//------------------------------------------------------------------------------ -// Name: QJsonDocument -//------------------------------------------------------------------------------ -QJsonDocument::QJsonDocument(const QJsonObject &object) : root_(0) { - setObject(object); -} - -//------------------------------------------------------------------------------ -// Name: QJsonDocument -//------------------------------------------------------------------------------ -QJsonDocument::QJsonDocument(const QJsonArray &array) : root_(0) { - setArray(array); -} - -//------------------------------------------------------------------------------ -// Name: QJsonDocument -//------------------------------------------------------------------------------ -QJsonDocument::QJsonDocument(const QJsonDocument &other) : root_(0) { - if(other.root_) { - root_ = other.root_->clone(); - } -} - -//------------------------------------------------------------------------------ -// Name: ~QJsonDocument -//------------------------------------------------------------------------------ -QJsonDocument::~QJsonDocument() { - delete root_; -} - -//------------------------------------------------------------------------------ -// Name: operator= -//------------------------------------------------------------------------------ -QJsonDocument &QJsonDocument::operator=(const QJsonDocument &other) { - QJsonDocument(other).swap(*this); - return *this; -} - -//------------------------------------------------------------------------------ -// Name: operator!= -//------------------------------------------------------------------------------ -bool QJsonDocument::operator!=(const QJsonDocument &other) const { - return !(*this == other); -} - -//------------------------------------------------------------------------------ -// Name: operator== -//------------------------------------------------------------------------------ -bool QJsonDocument::operator==(const QJsonDocument &other) const { - - if(isArray() && other.isArray()) { - return array() == other.array(); - } - - if(isObject() && other.isObject()) { - return object() == other.object(); - } - - if(isEmpty() && other.isEmpty()) { - return true; - } - - if(isNull() && other.isNull()) { - return true; - } - - return false; -} - -//------------------------------------------------------------------------------ -// Name: isArray -//------------------------------------------------------------------------------ -bool QJsonDocument::isArray() const { - return root_ && root_->toArray(); -} - -//------------------------------------------------------------------------------ -// Name: isEmpty -//------------------------------------------------------------------------------ -bool QJsonDocument::isEmpty() const { - - // TODO(eteran): figure out the rules here that Qt5 uses - // it *looks* like they define empty as being NULL - // which is obviously different than this - - return !root_; -} - -//------------------------------------------------------------------------------ -// Name: isNull -//------------------------------------------------------------------------------ -bool QJsonDocument::isNull() const { - return !root_; -} - -//------------------------------------------------------------------------------ -// Name: isObject -//------------------------------------------------------------------------------ -bool QJsonDocument::isObject() const { - return root_ && root_->toObject(); -} - -//------------------------------------------------------------------------------ -// Name: setArray -//------------------------------------------------------------------------------ -void QJsonDocument::setArray(const QJsonArray &array) { - setRoot(array); -} - -//------------------------------------------------------------------------------ -// Name: setObject -//------------------------------------------------------------------------------ -void QJsonDocument::setObject(const QJsonObject &object) { - setRoot(object); -} - -//------------------------------------------------------------------------------ -// Name: setRoot -//------------------------------------------------------------------------------ -void QJsonDocument::setRoot(const QJsonRoot &root) { - delete root_; - root_ = root.clone(); -} - -//------------------------------------------------------------------------------ -// Name: toBinaryData -//------------------------------------------------------------------------------ -QByteArray QJsonDocument::toBinaryData() const { - QByteArray r; - // TODO(eteran): implement this - return r; -} - -//------------------------------------------------------------------------------ -// Name: escapeString -//------------------------------------------------------------------------------ -QString QJsonDocument::escapeString(const QString &s) const { - - QString r; - - Q_FOREACH(QChar ch, s) { - switch(ch.toLatin1()) { - case '\"': r.append("\\\""); break; - case '\\': r.append("\\\\"); break; - #if 0 - case '/': r.append("\\/"); break; - #endif - case '\b': r.append("\\b"); break; - case '\f': r.append("\\f"); break; - case '\n': r.append("\\n"); break; - case '\r': r.append("\\r"); break; - case '\t': r.append("\\t"); break; - default: - r += ch; - break; - } - } - - return r; -} - -//------------------------------------------------------------------------------ -// Name: toJson -//------------------------------------------------------------------------------ -QString QJsonDocument::toJson(const QJsonValue &v, JsonFormat format) const { - - QString b; - QTextStream ss(&b, QIODevice::WriteOnly | QIODevice::Text); - - switch(v.type()) { - case QJsonValue::Null: - ss << "null"; - break; - case QJsonValue::Bool: - ss << (v.toBool() ? "true" : "false"); - break; - case QJsonValue::Double: - { - double d = v.toDouble (); - if (qIsFinite(d)) { - // +2 to format to ensure the expected precision - ss << QByteArray::number(d, 'g', 15 + 2); // ::digits10 is 15 - } else { - ss << "null"; // +INF || -INF || NaN (see RFC4627#section2.4) - } - } - break; - case QJsonValue::String: - ss << '"' << escapeString(v.toString()) << '"'; - break; - case QJsonValue::Array: - { - const QJsonArray a = v.toArray(); - ss << "["; - if(!a.empty()) { - QJsonArray::const_iterator it = a.begin(); - QJsonArray::const_iterator e = a.end(); - - ss << toJson(*it++, format); - - for(;it != e; ++it) { - ss << ','; - ss << toJson(*it, format); - } - } - ss << "]"; - } - break; - case QJsonValue::Object: - { - const QJsonObject o = v.toObject(); - ss << "{"; - if(!o.empty()) { - QJsonObject::const_iterator it = o.begin(); - QJsonObject::const_iterator e = o.end(); - - ss << '"' << escapeString(it.key()) << "\": " << toJson(it.value(), format); - ++it; - for(;it != e; ++it) { - ss << ','; - ss << '"' << escapeString(it.key()) << "\": " << toJson(it.value(), format); - } - } - ss << "}"; - } - break; - case QJsonValue::Undefined: - Q_ASSERT(0); - break; - } - - return b; -} - -//------------------------------------------------------------------------------ -// Name: toJson -//------------------------------------------------------------------------------ -QByteArray QJsonDocument::toJson(JsonFormat format) const { - - Q_UNUSED(format); - - if(isArray()) { - QString s = toJson(array(), format); - return s.toUtf8(); - } - - if(isObject()) { - QString s = toJson(object(), format); - return s.toUtf8(); - } - - return QByteArray(); -} - -//------------------------------------------------------------------------------ -// Name: toVariant -//------------------------------------------------------------------------------ -QVariant QJsonDocument::toVariant() const { - - if(!isEmpty()) { - if(QJsonObject *const object = root_->toObject()) { - return object->toVariantMap(); - } - - if(QJsonArray *const array = root_->toArray()) { - return array->toVariantList(); - } - } - - return QVariant(); -} - -//------------------------------------------------------------------------------ -// Name: array -//------------------------------------------------------------------------------ -QJsonArray QJsonDocument::array() const { - - if(!isEmpty()) { - if(QJsonArray *const array = root_->toArray()) { - return *array; - } - } - - return QJsonArray(); -} - -//------------------------------------------------------------------------------ -// Name: object -//------------------------------------------------------------------------------ -QJsonObject QJsonDocument::object() const { - - if(!isEmpty()) { - if(QJsonObject *const object = root_->toObject()) { - return *object; - } - } - - return QJsonObject(); -} - -//------------------------------------------------------------------------------ -// Name: rawData -//------------------------------------------------------------------------------ -const char *QJsonDocument::rawData(int *size) const { - Q_UNUSED(size); - // TODO(eteran): implement this - return 0; -} - -//------------------------------------------------------------------------------ -// Name: fromBinaryData -//------------------------------------------------------------------------------ -QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidation validation) { - Q_UNUSED(data); - Q_UNUSED(validation); - - QJsonDocument doc; - // TODO(eteran): implement this - return doc; -} - -//------------------------------------------------------------------------------ -// Name: fromJson -//------------------------------------------------------------------------------ -QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error) { - QJsonDocument doc; - - const char *const begin = json.constData(); - const char *const end = begin + json.size(); - - QJsonParser parser(begin, end); - - doc.root_ = parser.parse(); - - if(error) { - *error = parser.state(); - } - - return doc; -} - -//------------------------------------------------------------------------------ -// Name: fromRawData -//------------------------------------------------------------------------------ -QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidation validation) { - - // data has to be aligned to a 4 byte boundary. - Q_ASSERT(!(reinterpret_cast(data) % 3)); - - return fromBinaryData(QByteArray::fromRawData(data, size), validation); -} - -//------------------------------------------------------------------------------ -// Name: fromVariant -//------------------------------------------------------------------------------ -QJsonDocument QJsonDocument::fromVariant(const QVariant &variant) { - - QJsonDocument doc; - - if (variant.type() == QVariant::Map) { - doc.setObject(QJsonObject::fromVariantMap(variant.toMap())); - } else if (variant.type() == QVariant::Hash) { - doc.setObject(QJsonObject::fromVariantHash(variant.toHash())); - } else if (variant.type() == QVariant::List) { - doc.setArray(QJsonArray::fromVariantList(variant.toList())); - } else if (variant.type() == QVariant::StringList) { - doc.setArray(QJsonArray::fromStringList(variant.toStringList())); - } - - return doc; -} - -//------------------------------------------------------------------------------ -// Name: swap -//------------------------------------------------------------------------------ -void QJsonDocument::swap(QJsonDocument &other) { - qSwap(root_, other.root_); -} - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "QJsonDocument.h" +#include "QJsonObject.h" +#include "QJsonArray.h" +#include "QJsonParser.h" + +#include +#include +#include +#include +#include + +#if QT_VERSION < 0x050000 + +//------------------------------------------------------------------------------ +// Name: QJsonDocument +//------------------------------------------------------------------------------ +QJsonDocument::QJsonDocument() : root_(0) { +} + +//------------------------------------------------------------------------------ +// Name: QJsonDocument +//------------------------------------------------------------------------------ +QJsonDocument::QJsonDocument(const QJsonObject &object) : root_(0) { + setObject(object); +} + +//------------------------------------------------------------------------------ +// Name: QJsonDocument +//------------------------------------------------------------------------------ +QJsonDocument::QJsonDocument(const QJsonArray &array) : root_(0) { + setArray(array); +} + +//------------------------------------------------------------------------------ +// Name: QJsonDocument +//------------------------------------------------------------------------------ +QJsonDocument::QJsonDocument(const QJsonDocument &other) : root_(0) { + if(other.root_) { + root_ = other.root_->clone(); + } +} + +//------------------------------------------------------------------------------ +// Name: ~QJsonDocument +//------------------------------------------------------------------------------ +QJsonDocument::~QJsonDocument() { + delete root_; +} + +//------------------------------------------------------------------------------ +// Name: operator= +//------------------------------------------------------------------------------ +QJsonDocument &QJsonDocument::operator=(const QJsonDocument &other) { + QJsonDocument(other).swap(*this); + return *this; +} + +//------------------------------------------------------------------------------ +// Name: operator!= +//------------------------------------------------------------------------------ +bool QJsonDocument::operator!=(const QJsonDocument &other) const { + return !(*this == other); +} + +//------------------------------------------------------------------------------ +// Name: operator== +//------------------------------------------------------------------------------ +bool QJsonDocument::operator==(const QJsonDocument &other) const { + + if(isArray() && other.isArray()) { + return array() == other.array(); + } + + if(isObject() && other.isObject()) { + return object() == other.object(); + } + + if(isEmpty() && other.isEmpty()) { + return true; + } + + if(isNull() && other.isNull()) { + return true; + } + + return false; +} + +//------------------------------------------------------------------------------ +// Name: isArray +//------------------------------------------------------------------------------ +bool QJsonDocument::isArray() const { + return root_ && root_->toArray(); +} + +//------------------------------------------------------------------------------ +// Name: isEmpty +//------------------------------------------------------------------------------ +bool QJsonDocument::isEmpty() const { + + // TODO(eteran): figure out the rules here that Qt5 uses + // it *looks* like they define empty as being NULL + // which is obviously different than this + + return !root_; +} + +//------------------------------------------------------------------------------ +// Name: isNull +//------------------------------------------------------------------------------ +bool QJsonDocument::isNull() const { + return !root_; +} + +//------------------------------------------------------------------------------ +// Name: isObject +//------------------------------------------------------------------------------ +bool QJsonDocument::isObject() const { + return root_ && root_->toObject(); +} + +//------------------------------------------------------------------------------ +// Name: setArray +//------------------------------------------------------------------------------ +void QJsonDocument::setArray(const QJsonArray &array) { + setRoot(array); +} + +//------------------------------------------------------------------------------ +// Name: setObject +//------------------------------------------------------------------------------ +void QJsonDocument::setObject(const QJsonObject &object) { + setRoot(object); +} + +//------------------------------------------------------------------------------ +// Name: setRoot +//------------------------------------------------------------------------------ +void QJsonDocument::setRoot(const QJsonRoot &root) { + delete root_; + root_ = root.clone(); +} + +//------------------------------------------------------------------------------ +// Name: toBinaryData +//------------------------------------------------------------------------------ +QByteArray QJsonDocument::toBinaryData() const { + QByteArray r; + // TODO(eteran): implement this + return r; +} + +//------------------------------------------------------------------------------ +// Name: escapeString +//------------------------------------------------------------------------------ +QString QJsonDocument::escapeString(const QString &s) const { + + QString r; + + Q_FOREACH(QChar ch, s) { + switch(ch.toLatin1()) { + case '\"': r.append("\\\""); break; + case '\\': r.append("\\\\"); break; +#if 0 + case '/': r.append("\\/"); break; +#endif + case '\b': r.append("\\b"); break; + case '\f': r.append("\\f"); break; + case '\n': r.append("\\n"); break; + case '\r': r.append("\\r"); break; + case '\t': r.append("\\t"); break; + default: + r += ch; + break; + } + } + + return r; +} + +//------------------------------------------------------------------------------ +// Name: toJson +//------------------------------------------------------------------------------ +QString QJsonDocument::toJson(const QJsonValue &v, JsonFormat format, int indent) const { + + QString b; + QTextStream ss(&b, QIODevice::WriteOnly | QIODevice::Text); + bool compact = (format == JsonFormat::Compact); + + switch(v.type()) { + case QJsonValue::Null: + ss << "null"; + break; + case QJsonValue::Bool: + ss << (v.toBool() ? "true" : "false"); + break; + case QJsonValue::Double: + { + double d = v.toDouble (); + if (qIsFinite(d)) { + // +2 to format to ensure the expected precision + ss << QByteArray::number(d, 'g', 15 + 2); // ::digits10 is 15 + } else { + ss << "null"; // +INF || -INF || NaN (see RFC4627#section2.4) + } + } + break; + case QJsonValue::String: + ss << '"' << escapeString(v.toString()) << '"'; + break; + case QJsonValue::Array: + { + const QJsonArray a = v.toArray(); + ss << (compact ? "[" : "[\n"); + if(!a.empty()) { + QJsonArray::const_iterator it = a.begin(); + QJsonArray::const_iterator e = a.end(); + + if (!compact) ss << QByteArray(4*indent, ' '); + ss << toJson(*it++, format, indent+1); + + for(;it != e; ++it) { + ss << (compact ? "," : ",\n"); + if (!compact) ss << QByteArray(4*indent, ' '); + ss << toJson(*it, format, indent+1); + } + } + indent--; + ss << (compact ? "]" : QString("\n%1]").arg(QString(4*indent, ' '))); + } + break; + case QJsonValue::Object: + { + const QJsonObject o = v.toObject(); + ss << (compact ? "{" : "{\n"); + if(!o.empty()) { + QJsonObject::const_iterator it = o.begin(); + QJsonObject::const_iterator e = o.end(); + + if (!compact) ss << QByteArray(4*indent, ' '); + ss << '"' << escapeString(it.key()) << (compact ? "\":" : "\": ") << toJson(it.value(), format, indent+1); + ++it; + for(;it != e; ++it) { + ss << (compact ? "," : ",\n"); + if (!compact) ss << QByteArray(4*indent, ' '); + ss << '"' << escapeString(it.key()) << (compact ? "\":" : "\": ") << toJson(it.value(), format, indent+1); + } + } + indent--; + ss << (compact ? "}" : QString("\n%1}").arg(QString(4*indent, ' '))); + } + break; + case QJsonValue::Undefined: + Q_ASSERT(0); + break; + } + + return b; +} + +//------------------------------------------------------------------------------ +// Name: toJson +//------------------------------------------------------------------------------ +QByteArray QJsonDocument::toJson(JsonFormat format) const { + + Q_UNUSED(format); + + if(isArray()) { + QString s = toJson(array(), format); + return s.toUtf8(); + } + + if(isObject()) { + QString s = toJson(object(), format); + return s.toUtf8(); + } + + return QByteArray(); +} + +//------------------------------------------------------------------------------ +// Name: toVariant +//------------------------------------------------------------------------------ +QVariant QJsonDocument::toVariant() const { + + if(!isEmpty()) { + if(QJsonObject *const object = root_->toObject()) { + return object->toVariantMap(); + } + + if(QJsonArray *const array = root_->toArray()) { + return array->toVariantList(); + } + } + + return QVariant(); +} + +//------------------------------------------------------------------------------ +// Name: array +//------------------------------------------------------------------------------ +QJsonArray QJsonDocument::array() const { + + if(!isEmpty()) { + if(QJsonArray *const array = root_->toArray()) { + return *array; + } + } + + return QJsonArray(); +} + +//------------------------------------------------------------------------------ +// Name: object +//------------------------------------------------------------------------------ +QJsonObject QJsonDocument::object() const { + + if(!isEmpty()) { + if(QJsonObject *const object = root_->toObject()) { + return *object; + } + } + + return QJsonObject(); +} + +//------------------------------------------------------------------------------ +// Name: rawData +//------------------------------------------------------------------------------ +const char *QJsonDocument::rawData(int *size) const { + Q_UNUSED(size); + // TODO(eteran): implement this + return 0; +} + +//------------------------------------------------------------------------------ +// Name: fromBinaryData +//------------------------------------------------------------------------------ +QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidation validation) { + Q_UNUSED(data); + Q_UNUSED(validation); + + QJsonDocument doc; + // TODO(eteran): implement this + return doc; +} + +//------------------------------------------------------------------------------ +// Name: fromJson +//------------------------------------------------------------------------------ +QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error) { + QJsonDocument doc; + + const char *const begin = json.constData(); + const char *const end = begin + json.size(); + + QJsonParser parser(begin, end); + + doc.root_ = parser.parse(); + + if(error) { + *error = parser.state(); + } + + return doc; +} + +//------------------------------------------------------------------------------ +// Name: fromRawData +//------------------------------------------------------------------------------ +QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidation validation) { + + // data has to be aligned to a 4 byte boundary. + Q_ASSERT(!(reinterpret_cast(data) % 3)); + + return fromBinaryData(QByteArray::fromRawData(data, size), validation); +} + +//------------------------------------------------------------------------------ +// Name: fromVariant +//------------------------------------------------------------------------------ +QJsonDocument QJsonDocument::fromVariant(const QVariant &variant) { + + QJsonDocument doc; + + if (variant.type() == QVariant::Map) { + doc.setObject(QJsonObject::fromVariantMap(variant.toMap())); + } else if (variant.type() == QVariant::Hash) { + doc.setObject(QJsonObject::fromVariantHash(variant.toHash())); + } else if (variant.type() == QVariant::List) { + doc.setArray(QJsonArray::fromVariantList(variant.toList())); + } else if (variant.type() == QVariant::StringList) { + doc.setArray(QJsonArray::fromStringList(variant.toStringList())); + } + + return doc; +} + +//------------------------------------------------------------------------------ +// Name: swap +//------------------------------------------------------------------------------ +void QJsonDocument::swap(QJsonDocument &other) { + qSwap(root_, other.root_); +} + +#endif diff --git a/qjson4/QJsonDocument.h b/qjson4/QJsonDocument.h old mode 100755 new mode 100644 index 32ae72f..3731f62 --- a/qjson4/QJsonDocument.h +++ b/qjson4/QJsonDocument.h @@ -1,103 +1,103 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef QJSON_DOCUMENT_H_ -#define QJSON_DOCUMENT_H_ - -#include - -#if QT_VERSION >= 0x050000 -#include -#else - -class QVariant; -class QByteArray; -class QTextStream; -class QJsonObject; -class QJsonValue; -class QJsonArray; -class QJsonParseError; -class QJsonRoot; - -class QJsonDocument { -public: - enum DataValidation { - Validate = 0, - BypassValidation = 1 - }; - - enum JsonFormat { - Indented, - Compact - }; - -public: - QJsonDocument(); - QJsonDocument(const QJsonObject &object); - QJsonDocument(const QJsonArray &array); - QJsonDocument(const QJsonDocument &other); - ~QJsonDocument(); - -public: - QJsonDocument &operator=(const QJsonDocument &other); - -public: - bool operator!=(const QJsonDocument &other) const; - bool operator==(const QJsonDocument &other) const; - -public: - bool isArray() const; - bool isEmpty() const; - bool isNull() const; - bool isObject() const; - -public: - QByteArray toBinaryData() const; - QByteArray toJson(JsonFormat format = Indented) const; - QVariant toVariant() const; - -public: - QJsonArray array() const; - QJsonObject object() const; - const char *rawData(int *size) const; - -public: - void setArray(const QJsonArray &array); - void setObject(const QJsonObject &object); - -public: - static QJsonDocument fromBinaryData(const QByteArray &data, DataValidation validation = Validate); - static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error = 0); - static QJsonDocument fromRawData(const char *data, int size, DataValidation validation = Validate); - static QJsonDocument fromVariant(const QVariant &variant); - -private: - void setRoot(const QJsonRoot &root); - QString toJson(const QJsonValue &v, JsonFormat format) const; - QString escapeString(const QString &s) const; - -private: - void swap(QJsonDocument &other); - -private: - QJsonRoot *root_; -}; - -#endif - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef QJSON_DOCUMENT_H_ +#define QJSON_DOCUMENT_H_ + +#include + +#if QT_VERSION >= 0x050000 +#include +#else + +class QVariant; +class QByteArray; +class QTextStream; +class QJsonObject; +class QJsonValue; +class QJsonArray; +class QJsonParseError; +class QJsonRoot; + +class QJsonDocument { +public: + enum DataValidation { + Validate = 0, + BypassValidation = 1 + }; + + enum JsonFormat { + Indented, + Compact + }; + +public: + QJsonDocument(); + QJsonDocument(const QJsonObject &object); + QJsonDocument(const QJsonArray &array); + QJsonDocument(const QJsonDocument &other); + ~QJsonDocument(); + +public: + QJsonDocument &operator=(const QJsonDocument &other); + +public: + bool operator!=(const QJsonDocument &other) const; + bool operator==(const QJsonDocument &other) const; + +public: + bool isArray() const; + bool isEmpty() const; + bool isNull() const; + bool isObject() const; + +public: + QByteArray toBinaryData() const; + QByteArray toJson(JsonFormat format = Indented) const; + QVariant toVariant() const; + +public: + QJsonArray array() const; + QJsonObject object() const; + const char *rawData(int *size) const; + +public: + void setArray(const QJsonArray &array); + void setObject(const QJsonObject &object); + +public: + static QJsonDocument fromBinaryData(const QByteArray &data, DataValidation validation = Validate); + static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error = 0); + static QJsonDocument fromRawData(const char *data, int size, DataValidation validation = Validate); + static QJsonDocument fromVariant(const QVariant &variant); + +private: + void setRoot(const QJsonRoot &root); + QString toJson(const QJsonValue &v, JsonFormat format, int indent = 1) const; + QString escapeString(const QString &s) const; + +private: + void swap(QJsonDocument &other); + +private: + QJsonRoot *root_; +}; + +#endif + +#endif diff --git a/qjson4/QJsonObject b/qjson4/QJsonObject old mode 100755 new mode 100644 index 2009be3..fb2126e --- a/qjson4/QJsonObject +++ b/qjson4/QJsonObject @@ -1 +1 @@ -#include "QJsonObject.h" +#include "QJsonObject.h" diff --git a/qjson4/QJsonObject.cpp b/qjson4/QJsonObject.cpp old mode 100755 new mode 100644 index 4a9e15a..ac36bb0 --- a/qjson4/QJsonObject.cpp +++ b/qjson4/QJsonObject.cpp @@ -1,322 +1,322 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "QJsonObject.h" - -#if QT_VERSION < 0x050000 - -//------------------------------------------------------------------------------ -// Name: QJsonObject -//------------------------------------------------------------------------------ -QJsonObject::QJsonObject() { -} - -//------------------------------------------------------------------------------ -// Name: QJsonObject -//------------------------------------------------------------------------------ -QJsonObject::QJsonObject(const QJsonObject &other) : values_(other.values_) { -} - -#if __cplusplus >= 201103L -//------------------------------------------------------------------------------ -// Name: QJsonObject -//------------------------------------------------------------------------------ -QJsonObject::QJsonObject(std::initializer_list > args) { - for(const QPair &arg : args) { - values_.insert(arg.first, arg.second); - - } -} -#endif - -//------------------------------------------------------------------------------ -// Name: ~QJsonObject -//------------------------------------------------------------------------------ -QJsonObject::~QJsonObject() { -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject &QJsonObject::operator=(const QJsonObject &other) { - QJsonObject(other).swap(*this); - return *this; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject::iterator QJsonObject::begin() { - return values_.begin(); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject::const_iterator QJsonObject::begin() const { - return values_.begin(); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject::iterator QJsonObject::end() { - return values_.end(); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject::const_iterator QJsonObject::end() const { - return values_.end(); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject::const_iterator QJsonObject::constBegin() const { - return begin(); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject::const_iterator QJsonObject::constEnd() const { - return end(); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -int QJsonObject::count() const { - return size(); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -int QJsonObject::length() const { - return size(); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -int QJsonObject::size() const { - return values_.size(); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -bool QJsonObject::empty() const { - return values_.empty(); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -bool QJsonObject::isEmpty() const { - return empty(); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject::const_iterator QJsonObject::constFind(const QString &key) const { - return values_.find(key); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -bool QJsonObject::contains(const QString &key) const { - return values_.contains(key); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject::iterator QJsonObject::find(const QString &key) { - return values_.find(key); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject::const_iterator QJsonObject::find(const QString &key) const { - return values_.find(key); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject::iterator QJsonObject::erase(iterator it) { - return values_.erase(it); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject::iterator QJsonObject::insert(const QString &key, const QJsonValue &value) { - return values_.insert(key, value); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QStringList QJsonObject::keys() const { - return values_.keys(); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -void QJsonObject::remove(const QString &key) { - values_.remove(key); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonValue QJsonObject::take(const QString &key) { - return values_.take(key); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonValue QJsonObject::value(const QString &key) const { - return values_.value(key); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -bool QJsonObject::operator!=(const QJsonObject &other) const { - return values_ != other.values_; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -bool QJsonObject::operator==(const QJsonObject &other) const { - return values_ != other.values_; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonValue QJsonObject::operator[](const QString &key) const { - return values_[key]; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonValueRef QJsonObject::operator[](const QString &key) { - return QJsonValueRef(this, key); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QVariantMap QJsonObject::toVariantMap() const { - QVariantMap a; - for(const_iterator it = begin(); it != end(); ++it) { - a.insert(it.key(), it.value().toVariant()); - } - return a; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QVariantHash QJsonObject::toVariantHash() const { - QVariantHash a; - for(const_iterator it = begin(); it != end(); ++it) { - a.insert(it.key(), it.value().toVariant()); - } - return a; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject QJsonObject::fromVariantMap(const QVariantMap &map) { - QJsonObject o; - for(QVariantMap::const_iterator it = map.begin(); it != map.end(); ++it) { - o.insert(it.key(), QJsonValue::fromVariant(it.value())); - } - return o; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject QJsonObject::fromVariantHash(const QVariantHash &hash) { - QJsonObject o; - for(QVariantHash::const_iterator it = hash.begin(); it != hash.end(); ++it) { - o.insert(it.key(), QJsonValue::fromVariant(it.value())); - } - return o; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonRoot *QJsonObject::clone() const { - return new QJsonObject(*this); -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -const QJsonObject *QJsonObject::toObject() const { - return this; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonObject *QJsonObject::toObject() { - return this; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -QJsonArray *QJsonObject::toArray() { - return 0; -} - -//------------------------------------------------------------------------------ -// Name: -//------------------------------------------------------------------------------ -const QJsonArray *QJsonObject::toArray() const { - return 0; -} - -//------------------------------------------------------------------------------ -// Name: swap -//------------------------------------------------------------------------------ -void QJsonObject::swap(QJsonObject &other) { - qSwap(values_, other.values_); -} - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "QJsonObject.h" + +#if QT_VERSION < 0x050000 + +//------------------------------------------------------------------------------ +// Name: QJsonObject +//------------------------------------------------------------------------------ +QJsonObject::QJsonObject() { +} + +//------------------------------------------------------------------------------ +// Name: QJsonObject +//------------------------------------------------------------------------------ +QJsonObject::QJsonObject(const QJsonObject &other) : values_(other.values_) { +} + +#if __cplusplus >= 201103L +//------------------------------------------------------------------------------ +// Name: QJsonObject +//------------------------------------------------------------------------------ +QJsonObject::QJsonObject(std::initializer_list > args) { + for(const QPair &arg : args) { + values_.insert(arg.first, arg.second); + + } +} +#endif + +//------------------------------------------------------------------------------ +// Name: ~QJsonObject +//------------------------------------------------------------------------------ +QJsonObject::~QJsonObject() { +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject &QJsonObject::operator=(const QJsonObject &other) { + QJsonObject(other).swap(*this); + return *this; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::iterator QJsonObject::begin() { + return values_.begin(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::const_iterator QJsonObject::begin() const { + return values_.begin(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::iterator QJsonObject::end() { + return values_.end(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::const_iterator QJsonObject::end() const { + return values_.end(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::const_iterator QJsonObject::constBegin() const { + return begin(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::const_iterator QJsonObject::constEnd() const { + return end(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +int QJsonObject::count() const { + return size(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +int QJsonObject::length() const { + return size(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +int QJsonObject::size() const { + return values_.size(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +bool QJsonObject::empty() const { + return values_.empty(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +bool QJsonObject::isEmpty() const { + return empty(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::const_iterator QJsonObject::constFind(const QString &key) const { + return values_.find(key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +bool QJsonObject::contains(const QString &key) const { + return values_.contains(key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::iterator QJsonObject::find(const QString &key) { + return values_.find(key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::const_iterator QJsonObject::find(const QString &key) const { + return values_.find(key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::iterator QJsonObject::erase(iterator it) { + return values_.erase(it); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject::iterator QJsonObject::insert(const QString &key, const QJsonValue &value) { + return values_.insert(key, value); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QStringList QJsonObject::keys() const { + return values_.keys(); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +void QJsonObject::remove(const QString &key) { + values_.remove(key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonValue QJsonObject::take(const QString &key) { + return values_.take(key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonValue QJsonObject::value(const QString &key) const { + return values_.value(key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +bool QJsonObject::operator!=(const QJsonObject &other) const { + return values_ != other.values_; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +bool QJsonObject::operator==(const QJsonObject &other) const { + return values_ != other.values_; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonValue QJsonObject::operator[](const QString &key) const { + return values_[key]; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonValueRef QJsonObject::operator[](const QString &key) { + return QJsonValueRef(this, key); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QVariantMap QJsonObject::toVariantMap() const { + QVariantMap a; + for(const_iterator it = begin(); it != end(); ++it) { + a.insert(it.key(), it.value().toVariant()); + } + return a; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QVariantHash QJsonObject::toVariantHash() const { + QVariantHash a; + for(const_iterator it = begin(); it != end(); ++it) { + a.insert(it.key(), it.value().toVariant()); + } + return a; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject QJsonObject::fromVariantMap(const QVariantMap &map) { + QJsonObject o; + for(QVariantMap::const_iterator it = map.begin(); it != map.end(); ++it) { + o.insert(it.key(), QJsonValue::fromVariant(it.value())); + } + return o; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject QJsonObject::fromVariantHash(const QVariantHash &hash) { + QJsonObject o; + for(QVariantHash::const_iterator it = hash.begin(); it != hash.end(); ++it) { + o.insert(it.key(), QJsonValue::fromVariant(it.value())); + } + return o; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonRoot *QJsonObject::clone() const { + return new QJsonObject(*this); +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +const QJsonObject *QJsonObject::toObject() const { + return this; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonObject *QJsonObject::toObject() { + return this; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +QJsonArray *QJsonObject::toArray() { + return 0; +} + +//------------------------------------------------------------------------------ +// Name: +//------------------------------------------------------------------------------ +const QJsonArray *QJsonObject::toArray() const { + return 0; +} + +//------------------------------------------------------------------------------ +// Name: swap +//------------------------------------------------------------------------------ +void QJsonObject::swap(QJsonObject &other) { + qSwap(values_, other.values_); +} + +#endif diff --git a/qjson4/QJsonObject.h b/qjson4/QJsonObject.h old mode 100755 new mode 100644 index 50a6ee5..6ee3a97 --- a/qjson4/QJsonObject.h +++ b/qjson4/QJsonObject.h @@ -1,121 +1,121 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef QJSON_OBJECT_H_ -#define QJSON_OBJECT_H_ - -#include - -#if QT_VERSION >= 0x050000 -#include -#else - -#include "QJsonRoot.h" -#include "QJsonValueRef.h" -#include "QJsonValue.h" -#include -#include -#include -#include - -class QJsonObject : public QJsonRoot { - friend class QJsonDocument; - friend class QJsonValue; - friend class QJsonValueRef; - friend class QJsonParser; -public: - // TODO(eteran): manually implement the map, for now we use QMap - // but the real thing has a custom implementation - // I guess for the purposes of less interdependancies? - // maybe so it's easier to forward declare the iterators? - - typedef QMap::const_iterator const_iterator; - typedef QMap::iterator iterator; - typedef const_iterator ConstIterator; - typedef iterator Iterator; - typedef QMap::key_type key_type; - typedef QMap::mapped_type mapped_type; - typedef QMap::size_type size_type; - -public: - QJsonObject(); -#if __cplusplus >= 201103L - QJsonObject(std::initializer_list > args); -#endif - QJsonObject(const QJsonObject &other); - ~QJsonObject(); - QJsonObject &operator=(const QJsonObject &other); - -public: - iterator begin(); - const_iterator begin() const; - iterator end(); - const_iterator end() const; - const_iterator constBegin() const; - const_iterator constEnd() const; - -public: - int count() const; - int length() const; - int size() const; - bool empty() const; - bool isEmpty() const; - -public: - const_iterator constFind(const QString &key) const; - bool contains(const QString &key) const; - iterator find(const QString &key); - const_iterator find(const QString &key) const; - -public: - iterator erase(iterator it); - iterator insert(const QString &key, const QJsonValue &value); - QStringList keys() const; - void remove(const QString &key); - QJsonValue take(const QString &key); - QJsonValue value(const QString &key) const; - bool operator!=(const QJsonObject &other) const; - bool operator==(const QJsonObject &other) const; - QJsonValue operator[](const QString &key) const; - QJsonValueRef operator[](const QString &key); - -public: - QVariantMap toVariantMap() const; - QVariantHash toVariantHash() const; - -public: - static QJsonObject fromVariantMap(const QVariantMap &map); - static QJsonObject fromVariantHash(const QVariantHash &hash); - -private: - virtual QJsonRoot *clone() const; - virtual QJsonArray *toArray(); - virtual QJsonObject *toObject(); - virtual const QJsonArray *toArray() const; - virtual const QJsonObject *toObject() const; - -private: - void swap(QJsonObject &other); - -private: - QMap values_; -}; - -#endif - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef QJSON_OBJECT_H_ +#define QJSON_OBJECT_H_ + +#include + +#if QT_VERSION >= 0x050000 +#include +#else + +#include "QJsonRoot.h" +#include "QJsonValueRef.h" +#include "QJsonValue.h" +#include +#include +#include +#include + +class QJsonObject : public QJsonRoot { + friend class QJsonDocument; + friend class QJsonValue; + friend class QJsonValueRef; + friend class QJsonParser; +public: + // TODO(eteran): manually implement the map, for now we use QMap + // but the real thing has a custom implementation + // I guess for the purposes of less interdependancies? + // maybe so it's easier to forward declare the iterators? + + typedef QMap::const_iterator const_iterator; + typedef QMap::iterator iterator; + typedef const_iterator ConstIterator; + typedef iterator Iterator; + typedef QMap::key_type key_type; + typedef QMap::mapped_type mapped_type; + typedef QMap::size_type size_type; + +public: + QJsonObject(); +#if __cplusplus >= 201103L + QJsonObject(std::initializer_list > args); +#endif + QJsonObject(const QJsonObject &other); + ~QJsonObject(); + QJsonObject &operator=(const QJsonObject &other); + +public: + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + const_iterator constBegin() const; + const_iterator constEnd() const; + +public: + int count() const; + int length() const; + int size() const; + bool empty() const; + bool isEmpty() const; + +public: + const_iterator constFind(const QString &key) const; + bool contains(const QString &key) const; + iterator find(const QString &key); + const_iterator find(const QString &key) const; + +public: + iterator erase(iterator it); + iterator insert(const QString &key, const QJsonValue &value); + QStringList keys() const; + void remove(const QString &key); + QJsonValue take(const QString &key); + QJsonValue value(const QString &key) const; + bool operator!=(const QJsonObject &other) const; + bool operator==(const QJsonObject &other) const; + QJsonValue operator[](const QString &key) const; + QJsonValueRef operator[](const QString &key); + +public: + QVariantMap toVariantMap() const; + QVariantHash toVariantHash() const; + +public: + static QJsonObject fromVariantMap(const QVariantMap &map); + static QJsonObject fromVariantHash(const QVariantHash &hash); + +private: + virtual QJsonRoot *clone() const; + virtual QJsonArray *toArray(); + virtual QJsonObject *toObject(); + virtual const QJsonArray *toArray() const; + virtual const QJsonObject *toObject() const; + +private: + void swap(QJsonObject &other); + +private: + QMap values_; +}; + +#endif + +#endif diff --git a/qjson4/QJsonParseError b/qjson4/QJsonParseError old mode 100755 new mode 100644 index de177e3..7d30db8 --- a/qjson4/QJsonParseError +++ b/qjson4/QJsonParseError @@ -1 +1 @@ -#include "QJsonParseError.h" +#include "QJsonParseError.h" diff --git a/qjson4/QJsonParseError.cpp b/qjson4/QJsonParseError.cpp old mode 100755 new mode 100644 index fa19f15..598c67c --- a/qjson4/QJsonParseError.cpp +++ b/qjson4/QJsonParseError.cpp @@ -1,64 +1,64 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "QJsonParseError.h" - -#if QT_VERSION < 0x050000 - -//------------------------------------------------------------------------------ -// Name: errorString -// Desc: The QJsonParseError class is used to report errors during JSON parsing. -//------------------------------------------------------------------------------ -QString QJsonParseError::errorString() const { - switch(error) { - case NoError: - return "No error occurred"; - case UnterminatedObject: - return "unterminated object"; - case MissingNameSeparator: - return "missing name separator"; - case UnterminatedArray: - return "unterminated array"; - case MissingValueSeparator: - return "missing value separator"; - case IllegalValue: - return "illegal value"; - case TerminationByNumber: - return "invalid termination by number"; - case IllegalNumber: - return "illegal number"; - case IllegalEscapeSequence: - return "illegal escape sequence"; - case IllegalUTF8String: - return "invalid UTF8 string"; - case UnterminatedString: - return "unterminated string"; - case MissingObject: - return "object is missing after a comma"; - case DeepNesting: - return "too deeply nested document"; - case DocumentTooLarge: - return "too large document"; - case GarbageAtEnd: - return "garbage at the end of the document"; - } - - return QString(); -} - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "QJsonParseError.h" + +#if QT_VERSION < 0x050000 + +//------------------------------------------------------------------------------ +// Name: errorString +// Desc: The QJsonParseError class is used to report errors during JSON parsing. +//------------------------------------------------------------------------------ +QString QJsonParseError::errorString() const { + switch(error) { + case NoError: + return "No error occurred"; + case UnterminatedObject: + return "unterminated object"; + case MissingNameSeparator: + return "missing name separator"; + case UnterminatedArray: + return "unterminated array"; + case MissingValueSeparator: + return "missing value separator"; + case IllegalValue: + return "illegal value"; + case TerminationByNumber: + return "invalid termination by number"; + case IllegalNumber: + return "illegal number"; + case IllegalEscapeSequence: + return "illegal escape sequence"; + case IllegalUTF8String: + return "invalid UTF8 string"; + case UnterminatedString: + return "unterminated string"; + case MissingObject: + return "object is missing after a comma"; + case DeepNesting: + return "too deeply nested document"; + case DocumentTooLarge: + return "too large document"; + case GarbageAtEnd: + return "garbage at the end of the document"; + } + + return QString(); +} + +#endif diff --git a/qjson4/QJsonParseError.h b/qjson4/QJsonParseError.h old mode 100755 new mode 100644 index d2eda8b..eddf04d --- a/qjson4/QJsonParseError.h +++ b/qjson4/QJsonParseError.h @@ -1,60 +1,60 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef QJSON_PARSE_ERROR_H_ -#define QJSON_PARSE_ERROR_H_ - -#include - -#if QT_VERSION >= 0x050000 -#include -#else - -#include - -class QJsonParseError { -public: - enum ParseError { - NoError = 0, - UnterminatedObject = 1, - MissingNameSeparator = 2, - UnterminatedArray = 3, - MissingValueSeparator = 4, - IllegalValue = 5, - TerminationByNumber = 6, - IllegalNumber = 7, - IllegalEscapeSequence = 8, - IllegalUTF8String = 9, - UnterminatedString = 10, - MissingObject = 11, - DeepNesting = 12, - DocumentTooLarge = 13, - GarbageAtEnd = 14 - }; - -public: - QString errorString() const; - -public: - ParseError error; - int offset; -}; - -#endif - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef QJSON_PARSE_ERROR_H_ +#define QJSON_PARSE_ERROR_H_ + +#include + +#if QT_VERSION >= 0x050000 +#include +#else + +#include + +class QJsonParseError { +public: + enum ParseError { + NoError = 0, + UnterminatedObject = 1, + MissingNameSeparator = 2, + UnterminatedArray = 3, + MissingValueSeparator = 4, + IllegalValue = 5, + TerminationByNumber = 6, + IllegalNumber = 7, + IllegalEscapeSequence = 8, + IllegalUTF8String = 9, + UnterminatedString = 10, + MissingObject = 11, + DeepNesting = 12, + DocumentTooLarge = 13, + GarbageAtEnd = 14 + }; + +public: + QString errorString() const; + +public: + ParseError error; + int offset; +}; + +#endif + +#endif diff --git a/qjson4/QJsonParser.cpp b/qjson4/QJsonParser.cpp old mode 100755 new mode 100644 index 08c3c62..052c9a8 --- a/qjson4/QJsonParser.cpp +++ b/qjson4/QJsonParser.cpp @@ -1,455 +1,455 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "QJsonParser.h" -#include "QJsonArray.h" -#include "QJsonObject.h" -#include "QJsonValue.h" - - -#if QT_VERSION < 0x050000 - -#include -#include -#include - -namespace { - -unsigned int to_hex(int ch) { - - static const int hexval[256] = { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 - }; - - if(static_cast(ch) < 256) { - return hexval[static_cast(ch)]; - } else { - return 0; - } -} - -} - -//------------------------------------------------------------------------------ -// Name: QJsonParser -//------------------------------------------------------------------------------ -QJsonParser::QJsonParser(const char *begin, const char *end) : begin_(begin), end_(end), p_(begin) { - state_.error = QJsonParseError::NoError; - state_.offset = 0; -} - -//------------------------------------------------------------------------------ -// Name: parse -//------------------------------------------------------------------------------ -QJsonRoot *QJsonParser::parse() { - if(begin_ == end_) { - return 0; - } - - QJsonRoot *ret = 0; - - try { - const char ch = peek(); - switch(ch) { - case ArrayBegin: - ret = getArray(); - break; - case ObjectBegin: - ret = getObject(); - break; - default: - state_.error = QJsonParseError::IllegalValue; - state_.offset = p_ - begin_; - break; - } - } catch(const QJsonParseError &e) { - state_ = e; - } - - if(ret) { - // eat up trailing white space... - while(p_ != end_ && std::isspace(*p_)) { - ++p_; - } - - //detect trailing garbage - if(p_ != end_) { - state_.error = QJsonParseError::GarbageAtEnd; - state_.offset = p_ - begin_; - } - } - - return ret; -} - -//------------------------------------------------------------------------------ -// Name: peek -//------------------------------------------------------------------------------ -char QJsonParser::peek() { - // first eat up some whitespace - while(p_ != end_ && std::isspace(*p_)) { - ++p_; - } - - return *p_; -} - -//------------------------------------------------------------------------------ -// Name: getValue -//------------------------------------------------------------------------------ -QJsonValue QJsonParser::getValue() { - - switch(peek()) { - case ObjectBegin: - { - QScopedPointer obj(getObject()); - return QJsonValue(*obj); - } - case ArrayBegin: - { - QScopedPointer arr(getArray()); - return QJsonValue(*arr); - } - case Quote: - return QJsonValue(getString()); - case 't': - return getTrue(); - case 'f': - return getFalse(); - case 'n': - return getNull(); - default: - return getNumber(); - } - - throwError(QJsonParseError::MissingObject); - return QJsonValue(); -} - -//------------------------------------------------------------------------------ -// Name: getObject -//------------------------------------------------------------------------------ -QJsonObject *QJsonParser::getObject() { - - QScopedPointer obj(new QJsonObject); - - char tok = peek(); - if(tok != ObjectBegin) { - throwError(QJsonParseError::IllegalValue); - } - - ++p_; - - // handle empty object - tok = peek(); - if(peek() == ObjectEnd) { - ++p_; - } else { - - do { - QPair p = getPair(); - obj->values_.insert(p.first, p.second); - - tok = peek(); - ++p_; - - } while(tok == ValueSeparator); - } - - if(tok != ObjectEnd) { - throwError(QJsonParseError::UnterminatedObject); - } - - return obj.take(); -} - -//------------------------------------------------------------------------------ -// Name: getArray -//------------------------------------------------------------------------------ -QJsonArray *QJsonParser::getArray() { - - QScopedPointer arr(new QJsonArray); - - char tok = peek(); - - if(tok != ArrayBegin) { - throwError(QJsonParseError::IllegalValue); - } - - ++p_; - - // handle empty object - tok = peek(); - if(tok == ArrayEnd) { - ++p_; - } else { - do { - arr->values_.push_back(getValue()); - - tok = peek(); - ++p_; - - } while(tok == ValueSeparator); - } - - if(tok != ArrayEnd) { - throwError(QJsonParseError::MissingValueSeparator); - } - - return arr.take(); -} - -//------------------------------------------------------------------------------ -// Name: getPair -//------------------------------------------------------------------------------ -QPair QJsonParser::getPair() { - - QString key = getString(); - - if(peek() != NameSeparator) { - throwError(QJsonParseError::MissingNameSeparator); - } - ++p_; - - return qMakePair(key, getValue()); -} - -//------------------------------------------------------------------------------ -// Name: getString -//------------------------------------------------------------------------------ -QString QJsonParser::getString() { - - if(peek() != Quote) { - throwError(QJsonParseError::IllegalUTF8String); - } - ++p_; - - QByteArray s; - - while(p_ != end_ && *p_ != Quote && *p_ != '\n') { - if(*p_ == '\\') { - ++p_; - if(p_ != end_) { - switch(*p_) { - case '"': s.append('"'); break; - case '\\': s.append('\\'); break; - case '/': s.append('/'); break; - case 'b': s.append('\b'); break; - case 'f': s.append('\f'); break; - case 'n': s.append('\n'); break; - case 'r': s.append('\r'); break; - case 't': s.append('\t'); break; - case 'u': - { - - QString hexChar; - - // convert \uXXXX escape sequences to UTF-8 - char hex[4]; - if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[0] = *++p_; - if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[1] = *++p_; - if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[2] = *++p_; - if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[3] = *++p_; - - if(!std::isxdigit(hex[0])) throwError(QJsonParseError::IllegalUTF8String); - if(!std::isxdigit(hex[1])) throwError(QJsonParseError::IllegalUTF8String); - if(!std::isxdigit(hex[2])) throwError(QJsonParseError::IllegalUTF8String); - if(!std::isxdigit(hex[3])) throwError(QJsonParseError::IllegalUTF8String); - - quint16 w1 = 0; - quint16 w2 = 0; - - w1 |= (to_hex(hex[0]) << 12); - w1 |= (to_hex(hex[1]) << 8); - w1 |= (to_hex(hex[2]) << 4); - w1 |= (to_hex(hex[3])); - - hexChar.append(QChar(w1)); - - if((w1 & 0xfc00) == 0xdc00) { - throwError(QJsonParseError::IllegalUTF8String); - } - - if((w1 & 0xfc00) == 0xd800) { - // part of a surrogate pair - if(p_ == end_ || *++p_ != '\\') { throwError(QJsonParseError::IllegalEscapeSequence); } - if(p_ == end_ || *++p_ != 'u') { throwError(QJsonParseError::IllegalEscapeSequence); } - - // convert \uXXXX escape sequences to UTF-8 - if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[0] = *++p_; - if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[1] = *++p_; - if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[2] = *++p_; - if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[3] = *++p_; - - if(!std::isxdigit(hex[0])) throwError(QJsonParseError::IllegalUTF8String); - if(!std::isxdigit(hex[1])) throwError(QJsonParseError::IllegalUTF8String); - if(!std::isxdigit(hex[2])) throwError(QJsonParseError::IllegalUTF8String); - if(!std::isxdigit(hex[3])) throwError(QJsonParseError::IllegalUTF8String); - - w2 |= (to_hex(hex[0]) << 12); - w2 |= (to_hex(hex[1]) << 8); - w2 |= (to_hex(hex[2]) << 4); - w2 |= (to_hex(hex[3])); - - hexChar.append(QChar(w2)); - } - - s.append(hexChar.toUtf8()); - } - break; - - default: - s.append('\\'); - break; - } - } - } else { - s.append(*p_); - } - ++p_; - } - - if(*p_ != Quote || p_ == end_) { - throwError(QJsonParseError::UnterminatedString); - } - - ++p_; - - return QString::fromUtf8(s, s.size()); -} - -//------------------------------------------------------------------------------ -// Name: getNull -//------------------------------------------------------------------------------ -QJsonValue QJsonParser::getNull() { - if(p_ == end_ || *p_++ != 'n') { throwError(QJsonParseError::IllegalValue); } - if(p_ == end_ || *p_++ != 'u') { throwError(QJsonParseError::IllegalValue); } - if(p_ == end_ || *p_++ != 'l') { throwError(QJsonParseError::IllegalValue); } - if(p_ == end_ || *p_++ != 'l') { throwError(QJsonParseError::IllegalValue); } - - return QJsonValue(); -} - -//------------------------------------------------------------------------------ -// Name: getTrue -//------------------------------------------------------------------------------ -QJsonValue QJsonParser::getTrue() { - if(p_ == end_ || *p_++ != 't') { throwError(QJsonParseError::IllegalValue); } - if(p_ == end_ || *p_++ != 'r') { throwError(QJsonParseError::IllegalValue); } - if(p_ == end_ || *p_++ != 'u') { throwError(QJsonParseError::IllegalValue); } - if(p_ == end_ || *p_++ != 'e') { throwError(QJsonParseError::IllegalValue); } - - return QJsonValue(true); -} - -//------------------------------------------------------------------------------ -// Name: getFalse -//------------------------------------------------------------------------------ -QJsonValue QJsonParser::getFalse() { - if(p_ == end_ || *p_++ != 'f') { throwError(QJsonParseError::IllegalValue); } - if(p_ == end_ || *p_++ != 'a') { throwError(QJsonParseError::IllegalValue); } - if(p_ == end_ || *p_++ != 'l') { throwError(QJsonParseError::IllegalValue); } - if(p_ == end_ || *p_++ != 's') { throwError(QJsonParseError::IllegalValue); } - if(p_ == end_ || *p_++ != 'e') { throwError(QJsonParseError::IllegalValue); } - - return QJsonValue(false); -} - -//------------------------------------------------------------------------------ -// Name: getNumber -//------------------------------------------------------------------------------ -QJsonValue QJsonParser::getNumber() { - // JSON numbers fit the regex: -?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)? - - const char *const first = p_; - - // -? - if(p_ != end_ && *p_ == '-') { - ++p_; - } - - // (0|[1-9][0-9]*) - if(p_ != end_) { - if(*p_ >= '1' && *p_ <= '9') { - while(p_ != end_ && std::isdigit(*p_)) { - ++p_; - } - } else if(*p_ == '0') { - ++p_; - } else { - throwError(QJsonParseError::IllegalNumber); - } - } - - // (\.[0-9]+)? - if(p_ != end_ && *p_ == '.') { - ++p_; - if(!std::isdigit(*p_)) { - throwError(QJsonParseError::IllegalNumber); - } - - while(p_ != end_ && std::isdigit(*p_)) { - ++p_; - } - } - - // ([eE][+-]?[0-9]+)? - if(p_ != end_ && (*p_ == 'e' || *p_ == 'E')) { - ++p_; - if(p_ != end_ && (*p_ == '+' || *p_ == '-')) { - ++p_; - } - if(!std::isdigit(*p_)) { - throwError(QJsonParseError::IllegalNumber); - } - while(p_ != end_ && std::isdigit(*p_)) { - ++p_; - } - } - - if(p_ == end_) { - throwError(QJsonParseError::TerminationByNumber); - } - - return QJsonValue(QByteArray::fromRawData(first, p_ - first).toDouble()); -} - -//------------------------------------------------------------------------------ -// Name: state -//------------------------------------------------------------------------------ -QJsonParseError QJsonParser::state() const { - return state_; -} - -//------------------------------------------------------------------------------ -// Name: throwError -//------------------------------------------------------------------------------ -void QJsonParser::throwError(QJsonParseError::ParseError e) { - QJsonParseError err; - err.error = e; - err.offset = p_ - begin_; - throw err; -} - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "QJsonParser.h" +#include "QJsonArray.h" +#include "QJsonObject.h" +#include "QJsonValue.h" + + +#if QT_VERSION < 0x050000 + +#include +#include +#include + +namespace { + +unsigned int to_hex(int ch) { + + static const int hexval[256] = { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 + }; + + if(static_cast(ch) < 256) { + return hexval[static_cast(ch)]; + } else { + return 0; + } +} + +} + +//------------------------------------------------------------------------------ +// Name: QJsonParser +//------------------------------------------------------------------------------ +QJsonParser::QJsonParser(const char *begin, const char *end) : begin_(begin), end_(end), p_(begin) { + state_.error = QJsonParseError::NoError; + state_.offset = 0; +} + +//------------------------------------------------------------------------------ +// Name: parse +//------------------------------------------------------------------------------ +QJsonRoot *QJsonParser::parse() { + if(begin_ == end_) { + return 0; + } + + QJsonRoot *ret = 0; + + try { + const char ch = peek(); + switch(ch) { + case ArrayBegin: + ret = getArray(); + break; + case ObjectBegin: + ret = getObject(); + break; + default: + state_.error = QJsonParseError::IllegalValue; + state_.offset = p_ - begin_; + break; + } + } catch(const QJsonParseError &e) { + state_ = e; + } + + if(ret) { + // eat up trailing white space... + while(p_ != end_ && std::isspace(*p_)) { + ++p_; + } + + //detect trailing garbage + if(p_ != end_) { + state_.error = QJsonParseError::GarbageAtEnd; + state_.offset = p_ - begin_; + } + } + + return ret; +} + +//------------------------------------------------------------------------------ +// Name: peek +//------------------------------------------------------------------------------ +char QJsonParser::peek() { + // first eat up some whitespace + while(p_ != end_ && std::isspace(*p_)) { + ++p_; + } + + return *p_; +} + +//------------------------------------------------------------------------------ +// Name: getValue +//------------------------------------------------------------------------------ +QJsonValue QJsonParser::getValue() { + + switch(peek()) { + case ObjectBegin: + { + QScopedPointer obj(getObject()); + return QJsonValue(*obj); + } + case ArrayBegin: + { + QScopedPointer arr(getArray()); + return QJsonValue(*arr); + } + case Quote: + return QJsonValue(getString()); + case 't': + return getTrue(); + case 'f': + return getFalse(); + case 'n': + return getNull(); + default: + return getNumber(); + } + + throwError(QJsonParseError::MissingObject); + return QJsonValue(); +} + +//------------------------------------------------------------------------------ +// Name: getObject +//------------------------------------------------------------------------------ +QJsonObject *QJsonParser::getObject() { + + QScopedPointer obj(new QJsonObject); + + char tok = peek(); + if(tok != ObjectBegin) { + throwError(QJsonParseError::IllegalValue); + } + + ++p_; + + // handle empty object + tok = peek(); + if(peek() == ObjectEnd) { + ++p_; + } else { + + do { + QPair p = getPair(); + obj->values_.insert(p.first, p.second); + + tok = peek(); + ++p_; + + } while(tok == ValueSeparator); + } + + if(tok != ObjectEnd) { + throwError(QJsonParseError::UnterminatedObject); + } + + return obj.take(); +} + +//------------------------------------------------------------------------------ +// Name: getArray +//------------------------------------------------------------------------------ +QJsonArray *QJsonParser::getArray() { + + QScopedPointer arr(new QJsonArray); + + char tok = peek(); + + if(tok != ArrayBegin) { + throwError(QJsonParseError::IllegalValue); + } + + ++p_; + + // handle empty object + tok = peek(); + if(tok == ArrayEnd) { + ++p_; + } else { + do { + arr->values_.push_back(getValue()); + + tok = peek(); + ++p_; + + } while(tok == ValueSeparator); + } + + if(tok != ArrayEnd) { + throwError(QJsonParseError::MissingValueSeparator); + } + + return arr.take(); +} + +//------------------------------------------------------------------------------ +// Name: getPair +//------------------------------------------------------------------------------ +QPair QJsonParser::getPair() { + + QString key = getString(); + + if(peek() != NameSeparator) { + throwError(QJsonParseError::MissingNameSeparator); + } + ++p_; + + return qMakePair(key, getValue()); +} + +//------------------------------------------------------------------------------ +// Name: getString +//------------------------------------------------------------------------------ +QString QJsonParser::getString() { + + if(peek() != Quote) { + throwError(QJsonParseError::IllegalUTF8String); + } + ++p_; + + QByteArray s; + + while(p_ != end_ && *p_ != Quote && *p_ != '\n') { + if(*p_ == '\\') { + ++p_; + if(p_ != end_) { + switch(*p_) { + case '"': s.append('"'); break; + case '\\': s.append('\\'); break; + case '/': s.append('/'); break; + case 'b': s.append('\b'); break; + case 'f': s.append('\f'); break; + case 'n': s.append('\n'); break; + case 'r': s.append('\r'); break; + case 't': s.append('\t'); break; + case 'u': + { + + QString hexChar; + + // convert \uXXXX escape sequences to UTF-8 + char hex[4]; + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[0] = *++p_; + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[1] = *++p_; + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[2] = *++p_; + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[3] = *++p_; + + if(!std::isxdigit(hex[0])) throwError(QJsonParseError::IllegalUTF8String); + if(!std::isxdigit(hex[1])) throwError(QJsonParseError::IllegalUTF8String); + if(!std::isxdigit(hex[2])) throwError(QJsonParseError::IllegalUTF8String); + if(!std::isxdigit(hex[3])) throwError(QJsonParseError::IllegalUTF8String); + + quint16 w1 = 0; + quint16 w2 = 0; + + w1 |= (to_hex(hex[0]) << 12); + w1 |= (to_hex(hex[1]) << 8); + w1 |= (to_hex(hex[2]) << 4); + w1 |= (to_hex(hex[3])); + + hexChar.append(QChar(w1)); + + if((w1 & 0xfc00) == 0xdc00) { + throwError(QJsonParseError::IllegalUTF8String); + } + + if((w1 & 0xfc00) == 0xd800) { + // part of a surrogate pair + if(p_ == end_ || *++p_ != '\\') { throwError(QJsonParseError::IllegalEscapeSequence); } + if(p_ == end_ || *++p_ != 'u') { throwError(QJsonParseError::IllegalEscapeSequence); } + + // convert \uXXXX escape sequences to UTF-8 + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[0] = *++p_; + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[1] = *++p_; + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[2] = *++p_; + if(p_ == end_) { throwError(QJsonParseError::IllegalEscapeSequence); } hex[3] = *++p_; + + if(!std::isxdigit(hex[0])) throwError(QJsonParseError::IllegalUTF8String); + if(!std::isxdigit(hex[1])) throwError(QJsonParseError::IllegalUTF8String); + if(!std::isxdigit(hex[2])) throwError(QJsonParseError::IllegalUTF8String); + if(!std::isxdigit(hex[3])) throwError(QJsonParseError::IllegalUTF8String); + + w2 |= (to_hex(hex[0]) << 12); + w2 |= (to_hex(hex[1]) << 8); + w2 |= (to_hex(hex[2]) << 4); + w2 |= (to_hex(hex[3])); + + hexChar.append(QChar(w2)); + } + + s.append(hexChar.toUtf8()); + } + break; + + default: + s.append('\\'); + break; + } + } + } else { + s.append(*p_); + } + ++p_; + } + + if(*p_ != Quote || p_ == end_) { + throwError(QJsonParseError::UnterminatedString); + } + + ++p_; + + return QString::fromUtf8(s, s.size()); +} + +//------------------------------------------------------------------------------ +// Name: getNull +//------------------------------------------------------------------------------ +QJsonValue QJsonParser::getNull() { + if(p_ == end_ || *p_++ != 'n') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'u') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'l') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'l') { throwError(QJsonParseError::IllegalValue); } + + return QJsonValue(); +} + +//------------------------------------------------------------------------------ +// Name: getTrue +//------------------------------------------------------------------------------ +QJsonValue QJsonParser::getTrue() { + if(p_ == end_ || *p_++ != 't') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'r') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'u') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'e') { throwError(QJsonParseError::IllegalValue); } + + return QJsonValue(true); +} + +//------------------------------------------------------------------------------ +// Name: getFalse +//------------------------------------------------------------------------------ +QJsonValue QJsonParser::getFalse() { + if(p_ == end_ || *p_++ != 'f') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'a') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'l') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 's') { throwError(QJsonParseError::IllegalValue); } + if(p_ == end_ || *p_++ != 'e') { throwError(QJsonParseError::IllegalValue); } + + return QJsonValue(false); +} + +//------------------------------------------------------------------------------ +// Name: getNumber +//------------------------------------------------------------------------------ +QJsonValue QJsonParser::getNumber() { + // JSON numbers fit the regex: -?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)? + + const char *const first = p_; + + // -? + if(p_ != end_ && *p_ == '-') { + ++p_; + } + + // (0|[1-9][0-9]*) + if(p_ != end_) { + if(*p_ >= '1' && *p_ <= '9') { + while(p_ != end_ && std::isdigit(*p_)) { + ++p_; + } + } else if(*p_ == '0') { + ++p_; + } else { + throwError(QJsonParseError::IllegalNumber); + } + } + + // (\.[0-9]+)? + if(p_ != end_ && *p_ == '.') { + ++p_; + if(!std::isdigit(*p_)) { + throwError(QJsonParseError::IllegalNumber); + } + + while(p_ != end_ && std::isdigit(*p_)) { + ++p_; + } + } + + // ([eE][+-]?[0-9]+)? + if(p_ != end_ && (*p_ == 'e' || *p_ == 'E')) { + ++p_; + if(p_ != end_ && (*p_ == '+' || *p_ == '-')) { + ++p_; + } + if(!std::isdigit(*p_)) { + throwError(QJsonParseError::IllegalNumber); + } + while(p_ != end_ && std::isdigit(*p_)) { + ++p_; + } + } + + if(p_ == end_) { + throwError(QJsonParseError::TerminationByNumber); + } + + return QJsonValue(QByteArray::fromRawData(first, p_ - first).toDouble()); +} + +//------------------------------------------------------------------------------ +// Name: state +//------------------------------------------------------------------------------ +QJsonParseError QJsonParser::state() const { + return state_; +} + +//------------------------------------------------------------------------------ +// Name: throwError +//------------------------------------------------------------------------------ +void QJsonParser::throwError(QJsonParseError::ParseError e) { + QJsonParseError err; + err.error = e; + err.offset = p_ - begin_; + throw err; +} + +#endif diff --git a/qjson4/QJsonParser.h b/qjson4/QJsonParser.h old mode 100755 new mode 100644 index 0686838..f11f5a0 --- a/qjson4/QJsonParser.h +++ b/qjson4/QJsonParser.h @@ -1,81 +1,81 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -// NOTE: this is not part of the "public" Qt API, so using this class directly -// is not recomended - -#ifndef QJSON_PARSER_H_ -#define QJSON_PARSER_H_ - -#include - -#if QT_VERSION < 0x050000 - -#include "QJsonParseError.h" -#include -class QJsonRoot; -class QJsonObject; -class QJsonArray; -class QJsonValue; - -class QJsonParser { - friend class QJsonDocument; - -public: - QJsonParser(const char *begin, const char *end); - -public: - QJsonRoot *parse(); - -public: - QJsonParseError state() const; - -private: - static const char ArrayBegin = '['; - static const char ArrayEnd = ']'; - static const char NameSeparator = ':'; - static const char ValueSeparator = ','; - static const char ObjectBegin = '{'; - static const char ObjectEnd = '}'; - static const char Quote = '"'; - -private: - char peek(); - QJsonObject *getObject(); - QJsonArray *getArray(); - QJsonValue getValue(); - QString getString(); - QJsonValue getTrue(); - QJsonValue getFalse(); - QJsonValue getNull(); - QJsonValue getNumber(); - QPair getPair(); - -private: - void throwError(QJsonParseError::ParseError e); - -private: - QJsonParseError state_; - const char *const begin_; - const char *const end_; - const char * p_; -}; - -#endif - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +// NOTE: this is not part of the "public" Qt API, so using this class directly +// is not recomended + +#ifndef QJSON_PARSER_H_ +#define QJSON_PARSER_H_ + +#include + +#if QT_VERSION < 0x050000 + +#include "QJsonParseError.h" +#include +class QJsonRoot; +class QJsonObject; +class QJsonArray; +class QJsonValue; + +class QJsonParser { + friend class QJsonDocument; + +public: + QJsonParser(const char *begin, const char *end); + +public: + QJsonRoot *parse(); + +public: + QJsonParseError state() const; + +private: + static const char ArrayBegin = '['; + static const char ArrayEnd = ']'; + static const char NameSeparator = ':'; + static const char ValueSeparator = ','; + static const char ObjectBegin = '{'; + static const char ObjectEnd = '}'; + static const char Quote = '"'; + +private: + char peek(); + QJsonObject *getObject(); + QJsonArray *getArray(); + QJsonValue getValue(); + QString getString(); + QJsonValue getTrue(); + QJsonValue getFalse(); + QJsonValue getNull(); + QJsonValue getNumber(); + QPair getPair(); + +private: + void throwError(QJsonParseError::ParseError e); + +private: + QJsonParseError state_; + const char *const begin_; + const char *const end_; + const char * p_; +}; + +#endif + +#endif diff --git a/qjson4/QJsonRoot b/qjson4/QJsonRoot old mode 100755 new mode 100644 index 32bf3f0..fbcaca1 --- a/qjson4/QJsonRoot +++ b/qjson4/QJsonRoot @@ -1 +1 @@ -#include "QJsonRoot.h" +#include "QJsonRoot.h" diff --git a/qjson4/QJsonRoot.h b/qjson4/QJsonRoot.h old mode 100755 new mode 100644 index a7e0729..d249465 --- a/qjson4/QJsonRoot.h +++ b/qjson4/QJsonRoot.h @@ -1,45 +1,45 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef QJSON_ROOT_H_ -#define QJSON_ROOT_H_ - -#include - -#if QT_VERSION < 0x050000 - -class QJsonObject; -class QJsonArray; - -class QJsonRoot { -public: - virtual ~QJsonRoot() {}; - -public: - virtual QJsonRoot *clone() const = 0; - -public: - virtual QJsonArray *toArray() = 0; - virtual QJsonObject *toObject() = 0; - virtual const QJsonArray *toArray() const = 0; - virtual const QJsonObject *toObject() const = 0; -}; - -#endif - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef QJSON_ROOT_H_ +#define QJSON_ROOT_H_ + +#include + +#if QT_VERSION < 0x050000 + +class QJsonObject; +class QJsonArray; + +class QJsonRoot { +public: + virtual ~QJsonRoot() {}; + +public: + virtual QJsonRoot *clone() const = 0; + +public: + virtual QJsonArray *toArray() = 0; + virtual QJsonObject *toObject() = 0; + virtual const QJsonArray *toArray() const = 0; + virtual const QJsonObject *toObject() const = 0; +}; + +#endif + +#endif diff --git a/qjson4/QJsonValue b/qjson4/QJsonValue old mode 100755 new mode 100644 index d4ca2b1..eb1b6fe --- a/qjson4/QJsonValue +++ b/qjson4/QJsonValue @@ -1 +1 @@ -#include "QJsonValue.h" +#include "QJsonValue.h" diff --git a/qjson4/QJsonValue.cpp b/qjson4/QJsonValue.cpp old mode 100755 new mode 100644 index 8365571..8ac4770 --- a/qjson4/QJsonValue.cpp +++ b/qjson4/QJsonValue.cpp @@ -1,391 +1,391 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "QJsonValue.h" -#include "QJsonArray.h" -#include "QJsonObject.h" - -#if QT_VERSION < 0x050000 -#include -#include - -//------------------------------------------------------------------------------ -// Name: QJsonValue -//------------------------------------------------------------------------------ -QJsonValue::QJsonValue(Type type) : type_(type) { -} - -//------------------------------------------------------------------------------ -// Name: QJsonValue -//------------------------------------------------------------------------------ -QJsonValue::QJsonValue(bool b) : type_(Bool) { - value_.b = b; -} - -//------------------------------------------------------------------------------ -// Name: QJsonValue -//------------------------------------------------------------------------------ -QJsonValue::QJsonValue(double n) : type_(Double) { - value_.n = n; -} - -//------------------------------------------------------------------------------ -// Name: QJsonValue -//------------------------------------------------------------------------------ -QJsonValue::QJsonValue(const QString &s) : type_(String) { - value_.s = new QString(s); -} - -//------------------------------------------------------------------------------ -// Name: QJsonValue -//------------------------------------------------------------------------------ -QJsonValue::QJsonValue(QLatin1String s) : type_(String) { - value_.s = new QString(s); -} - -#ifndef QT_NO_CAST_FROM_ASCII -//------------------------------------------------------------------------------ -// Name: QJsonValue -//------------------------------------------------------------------------------ -QJsonValue::QJsonValue(const char *s) : type_(String) { - value_.s = new QString(QString::fromUtf8(s)); -} -#endif - -//------------------------------------------------------------------------------ -// Name: QJsonValue -//------------------------------------------------------------------------------ -QJsonValue::QJsonValue(const QJsonArray &a) : type_(Array) { - value_.r = a.clone(); -} - -//------------------------------------------------------------------------------ -// Name: QJsonValue -//------------------------------------------------------------------------------ -QJsonValue::QJsonValue(const QJsonObject &o) : type_(Object) { - value_.r = o.clone(); -} - -//------------------------------------------------------------------------------ -// Name: QJsonValue -//------------------------------------------------------------------------------ -QJsonValue::QJsonValue(int n) : type_(Double) { - value_.n = n; -} - -//------------------------------------------------------------------------------ -// Name: QJsonValue -//------------------------------------------------------------------------------ -QJsonValue::QJsonValue(qint64 n) : type_(Double) { - value_.n = n; -} - -//------------------------------------------------------------------------------ -// Name: QJsonValue -//------------------------------------------------------------------------------ -QJsonValue::QJsonValue(const QJsonValue &other) : type_(other.type_) { - - switch(other.type_) { - case Bool: - value_.b = other.value_.b; - break; - case Double: - value_.n = other.value_.n; - break; - case String: - value_.s = new QString(*other.value_.s); - break; - case Array: - case Object: - value_.r = other.value_.r->clone(); - break; - case Undefined: - case Null: - value_ = other.value_; - break; - } -} - -//------------------------------------------------------------------------------ -// Name: ~QJsonValue -//------------------------------------------------------------------------------ -QJsonValue::~QJsonValue() { - switch(type_) { - case Null: - case Bool: - case Double: - case Undefined: - break; - case String: - delete value_.s; - break; - case Object: - case Array: - delete value_.r; - break; - } -} - -//------------------------------------------------------------------------------ -// Name: operator= -//------------------------------------------------------------------------------ -QJsonValue &QJsonValue::operator=(const QJsonValue &other) { - QJsonValue(other).swap(*this); - return *this; -} - -//------------------------------------------------------------------------------ -// Name: operator!= -//------------------------------------------------------------------------------ -bool QJsonValue::operator!=(const QJsonValue &other) const { - return !(*this == other); -} - -//------------------------------------------------------------------------------ -// Name: operator== -//------------------------------------------------------------------------------ -bool QJsonValue::operator==(const QJsonValue &other) const { - if(type_ == other.type_) { - switch(type_) { - case Null: - return true; - case Bool: - return value_.b == other.value_.b; - case Double: - return value_.n == other.value_.n; - case Undefined: - return true; - case String: - return *value_.s == *other.value_.s; - case Array: - return *(value_.r->toArray()) == *(other.value_.r->toArray()); - case Object: - return *(value_.r->toObject()) == *(other.value_.r->toObject()); - } - } - return false; -} - -//------------------------------------------------------------------------------ -// Name: isArray -//------------------------------------------------------------------------------ -bool QJsonValue::isArray() const { - return type_ == Array; -} - -//------------------------------------------------------------------------------ -// Name: isBool -//------------------------------------------------------------------------------ -bool QJsonValue::isBool() const { - return type_ == Bool; -} - -//------------------------------------------------------------------------------ -// Name: isDouble -//------------------------------------------------------------------------------ -bool QJsonValue::isDouble() const { - return type_ == Double; -} - -//------------------------------------------------------------------------------ -// Name: isNull -//------------------------------------------------------------------------------ -bool QJsonValue::isNull() const { - return type_ == Null; -} - -//------------------------------------------------------------------------------ -// Name: isObject -//------------------------------------------------------------------------------ -bool QJsonValue::isObject() const { - return type_ == Object; -} - -//------------------------------------------------------------------------------ -// Name: isString -//------------------------------------------------------------------------------ -bool QJsonValue::isString() const { - return type_ == String; -} - -//------------------------------------------------------------------------------ -// Name: isUndefined -//------------------------------------------------------------------------------ -bool QJsonValue::isUndefined() const { - return type_ == Undefined; -} - -//------------------------------------------------------------------------------ -// Name: type -//------------------------------------------------------------------------------ -QJsonValue::Type QJsonValue::type() const { - return type_; -} - -//------------------------------------------------------------------------------ -// Name: toArray -//------------------------------------------------------------------------------ -QJsonArray QJsonValue::toArray(const QJsonArray &defaultValue) const { - if(isArray()) { - return *(value_.r->toArray()); - } - - return defaultValue; -} - -//------------------------------------------------------------------------------ -// Name: toArray -//------------------------------------------------------------------------------ -QJsonArray QJsonValue::toArray() const { - return toArray(QJsonArray()); -} - -//------------------------------------------------------------------------------ -// Name: toBool -//------------------------------------------------------------------------------ -bool QJsonValue::toBool(bool defaultValue) const { - if(isBool()) { - return value_.b; - } - - return defaultValue; -} - -//------------------------------------------------------------------------------ -// Name: toDouble -//------------------------------------------------------------------------------ -double QJsonValue::toDouble(double defaultValue) const { - if(isDouble()) { - return value_.n; - } - - return defaultValue; -} - -//------------------------------------------------------------------------------ -// Name: toInt -//------------------------------------------------------------------------------ -int QJsonValue::toInt(int defaultValue) const { - if(isDouble() && qFloor(value_.n) == value_.n) { - return value_.n; - } - - return defaultValue; -} - -//------------------------------------------------------------------------------ -// Name: toObject -//------------------------------------------------------------------------------ -QJsonObject QJsonValue::toObject(const QJsonObject &defaultValue) const { - if(isObject()) { - return *(value_.r->toObject()); - } - - return defaultValue; -} - -//------------------------------------------------------------------------------ -// Name: toObject -//------------------------------------------------------------------------------ -QJsonObject QJsonValue::toObject() const { - return toObject(QJsonObject()); -} - -//------------------------------------------------------------------------------ -// Name: toString -//------------------------------------------------------------------------------ -QString QJsonValue::toString(const QString &defaultValue) const { - - if(isString()) { - return *value_.s; - } - - return defaultValue; -} - -//------------------------------------------------------------------------------ -// Name: toVariant -//------------------------------------------------------------------------------ -QVariant QJsonValue::toVariant() const { - switch(type_) { - case Null: - return QVariant(); - case Bool: - return QVariant::fromValue(value_.b); - case Double: - return QVariant::fromValue(value_.n); - case String: - return QVariant::fromValue(*value_.s); - case Array: - return value_.r->toArray()->toVariantList(); - case Object: - return value_.r->toObject()->toVariantMap(); - case Undefined: - return QVariant(); - } - - return QVariant(); -} - -//------------------------------------------------------------------------------ -// Name: fromVariant -//------------------------------------------------------------------------------ -QJsonValue QJsonValue::fromVariant(const QVariant &variant) { - if(variant.isNull()) { - return QJsonValue(Null); - } - - switch(variant.type()) { - case QVariant::Bool: - return QJsonValue(variant.toBool()); - case QVariant::Int: - return QJsonValue(variant.toInt()); - case QVariant::Double: - case QVariant::LongLong: - case QVariant::ULongLong: - case QVariant::UInt: - return QJsonValue(variant.toDouble()); - case QVariant::String: - return QJsonValue(variant.toString()); - case QVariant::List: - return QJsonArray::fromVariantList(variant.toList()); - case QVariant::StringList: - return QJsonArray::fromStringList(variant.toStringList()); - case QVariant::Map: - return QJsonObject::fromVariantMap(variant.toMap()); - default: - const QString s = variant.toString(); - if(!s.isEmpty()) { - return QJsonValue(s); - } - break; - } - - return QJsonValue(Null); - -} - -//------------------------------------------------------------------------------ -// Name: swap -//------------------------------------------------------------------------------ -void QJsonValue::swap(QJsonValue &other) { - qSwap(type_, other.type_); - qSwap(value_, other.value_); -} - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "QJsonValue.h" +#include "QJsonArray.h" +#include "QJsonObject.h" + +#if QT_VERSION < 0x050000 +#include +#include + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(Type type) : type_(type) { +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(bool b) : type_(Bool) { + value_.b = b; +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(double n) : type_(Double) { + value_.n = n; +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(const QString &s) : type_(String) { + value_.s = new QString(s); +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(QLatin1String s) : type_(String) { + value_.s = new QString(s); +} + +#ifndef QT_NO_CAST_FROM_ASCII +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(const char *s) : type_(String) { + value_.s = new QString(QString::fromUtf8(s)); +} +#endif + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(const QJsonArray &a) : type_(Array) { + value_.r = a.clone(); +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(const QJsonObject &o) : type_(Object) { + value_.r = o.clone(); +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(int n) : type_(Double) { + value_.n = n; +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(qint64 n) : type_(Double) { + value_.n = n; +} + +//------------------------------------------------------------------------------ +// Name: QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::QJsonValue(const QJsonValue &other) : type_(other.type_) { + + switch(other.type_) { + case Bool: + value_.b = other.value_.b; + break; + case Double: + value_.n = other.value_.n; + break; + case String: + value_.s = new QString(*other.value_.s); + break; + case Array: + case Object: + value_.r = other.value_.r->clone(); + break; + case Undefined: + case Null: + value_ = other.value_; + break; + } +} + +//------------------------------------------------------------------------------ +// Name: ~QJsonValue +//------------------------------------------------------------------------------ +QJsonValue::~QJsonValue() { + switch(type_) { + case Null: + case Bool: + case Double: + case Undefined: + break; + case String: + delete value_.s; + break; + case Object: + case Array: + delete value_.r; + break; + } +} + +//------------------------------------------------------------------------------ +// Name: operator= +//------------------------------------------------------------------------------ +QJsonValue &QJsonValue::operator=(const QJsonValue &other) { + QJsonValue(other).swap(*this); + return *this; +} + +//------------------------------------------------------------------------------ +// Name: operator!= +//------------------------------------------------------------------------------ +bool QJsonValue::operator!=(const QJsonValue &other) const { + return !(*this == other); +} + +//------------------------------------------------------------------------------ +// Name: operator== +//------------------------------------------------------------------------------ +bool QJsonValue::operator==(const QJsonValue &other) const { + if(type_ == other.type_) { + switch(type_) { + case Null: + return true; + case Bool: + return value_.b == other.value_.b; + case Double: + return value_.n == other.value_.n; + case Undefined: + return true; + case String: + return *value_.s == *other.value_.s; + case Array: + return *(value_.r->toArray()) == *(other.value_.r->toArray()); + case Object: + return *(value_.r->toObject()) == *(other.value_.r->toObject()); + } + } + return false; +} + +//------------------------------------------------------------------------------ +// Name: isArray +//------------------------------------------------------------------------------ +bool QJsonValue::isArray() const { + return type_ == Array; +} + +//------------------------------------------------------------------------------ +// Name: isBool +//------------------------------------------------------------------------------ +bool QJsonValue::isBool() const { + return type_ == Bool; +} + +//------------------------------------------------------------------------------ +// Name: isDouble +//------------------------------------------------------------------------------ +bool QJsonValue::isDouble() const { + return type_ == Double; +} + +//------------------------------------------------------------------------------ +// Name: isNull +//------------------------------------------------------------------------------ +bool QJsonValue::isNull() const { + return type_ == Null; +} + +//------------------------------------------------------------------------------ +// Name: isObject +//------------------------------------------------------------------------------ +bool QJsonValue::isObject() const { + return type_ == Object; +} + +//------------------------------------------------------------------------------ +// Name: isString +//------------------------------------------------------------------------------ +bool QJsonValue::isString() const { + return type_ == String; +} + +//------------------------------------------------------------------------------ +// Name: isUndefined +//------------------------------------------------------------------------------ +bool QJsonValue::isUndefined() const { + return type_ == Undefined; +} + +//------------------------------------------------------------------------------ +// Name: type +//------------------------------------------------------------------------------ +QJsonValue::Type QJsonValue::type() const { + return type_; +} + +//------------------------------------------------------------------------------ +// Name: toArray +//------------------------------------------------------------------------------ +QJsonArray QJsonValue::toArray(const QJsonArray &defaultValue) const { + if(isArray()) { + return *(value_.r->toArray()); + } + + return defaultValue; +} + +//------------------------------------------------------------------------------ +// Name: toArray +//------------------------------------------------------------------------------ +QJsonArray QJsonValue::toArray() const { + return toArray(QJsonArray()); +} + +//------------------------------------------------------------------------------ +// Name: toBool +//------------------------------------------------------------------------------ +bool QJsonValue::toBool(bool defaultValue) const { + if(isBool()) { + return value_.b; + } + + return defaultValue; +} + +//------------------------------------------------------------------------------ +// Name: toDouble +//------------------------------------------------------------------------------ +double QJsonValue::toDouble(double defaultValue) const { + if(isDouble()) { + return value_.n; + } + + return defaultValue; +} + +//------------------------------------------------------------------------------ +// Name: toInt +//------------------------------------------------------------------------------ +int QJsonValue::toInt(int defaultValue) const { + if(isDouble() && qFloor(value_.n) == value_.n) { + return value_.n; + } + + return defaultValue; +} + +//------------------------------------------------------------------------------ +// Name: toObject +//------------------------------------------------------------------------------ +QJsonObject QJsonValue::toObject(const QJsonObject &defaultValue) const { + if(isObject()) { + return *(value_.r->toObject()); + } + + return defaultValue; +} + +//------------------------------------------------------------------------------ +// Name: toObject +//------------------------------------------------------------------------------ +QJsonObject QJsonValue::toObject() const { + return toObject(QJsonObject()); +} + +//------------------------------------------------------------------------------ +// Name: toString +//------------------------------------------------------------------------------ +QString QJsonValue::toString(const QString &defaultValue) const { + + if(isString()) { + return *value_.s; + } + + return defaultValue; +} + +//------------------------------------------------------------------------------ +// Name: toVariant +//------------------------------------------------------------------------------ +QVariant QJsonValue::toVariant() const { + switch(type_) { + case Null: + return QVariant(); + case Bool: + return QVariant::fromValue(value_.b); + case Double: + return QVariant::fromValue(value_.n); + case String: + return QVariant::fromValue(*value_.s); + case Array: + return value_.r->toArray()->toVariantList(); + case Object: + return value_.r->toObject()->toVariantMap(); + case Undefined: + return QVariant(); + } + + return QVariant(); +} + +//------------------------------------------------------------------------------ +// Name: fromVariant +//------------------------------------------------------------------------------ +QJsonValue QJsonValue::fromVariant(const QVariant &variant) { + if(variant.isNull()) { + return QJsonValue(Null); + } + + switch(variant.type()) { + case QVariant::Bool: + return QJsonValue(variant.toBool()); + case QVariant::Int: + return QJsonValue(variant.toInt()); + case QVariant::Double: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::UInt: + return QJsonValue(variant.toDouble()); + case QVariant::String: + return QJsonValue(variant.toString()); + case QVariant::List: + return QJsonArray::fromVariantList(variant.toList()); + case QVariant::StringList: + return QJsonArray::fromStringList(variant.toStringList()); + case QVariant::Map: + return QJsonObject::fromVariantMap(variant.toMap()); + default: + const QString s = variant.toString(); + if(!s.isEmpty()) { + return QJsonValue(s); + } + break; + } + + return QJsonValue(Null); + +} + +//------------------------------------------------------------------------------ +// Name: swap +//------------------------------------------------------------------------------ +void QJsonValue::swap(QJsonValue &other) { + qSwap(type_, other.type_); + qSwap(value_, other.value_); +} + +#endif diff --git a/qjson4/QJsonValue.h b/qjson4/QJsonValue.h old mode 100755 new mode 100644 index dec14aa..d902352 --- a/qjson4/QJsonValue.h +++ b/qjson4/QJsonValue.h @@ -1,120 +1,120 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef QJSON_VALUE_H_ -#define QJSON_VALUE_H_ - -#include - -#if QT_VERSION >= 0x050000 -#include -#else - -class QString; - -#include - -class QJsonRoot; -class QJsonArray; -class QJsonObject; - -class QJsonValue { -public: - enum Type { - Null = 0x0, - Bool = 0x1, - Double = 0x2, - String = 0x3, - Array = 0x4, - Object = 0x5, - Undefined = 0x80 - }; - -public: - QJsonValue(Type type = Null); - QJsonValue(bool b); - QJsonValue(double n); - QJsonValue(int n); - QJsonValue(qint64 n); - QJsonValue(const QString &s); - QJsonValue(QLatin1String s); -#ifndef QT_NO_CAST_FROM_ASCII - QJsonValue(const char *s); -#endif - QJsonValue(const QJsonArray &a); - QJsonValue(const QJsonObject &o); - QJsonValue(const QJsonValue &other); - - ~QJsonValue(); - -private: - // to protect against incorrect usage due to passing a const char * - QJsonValue(const void *); - -public: - QJsonValue &operator=(const QJsonValue &other); - -public: - bool operator!=(const QJsonValue &other) const; - bool operator==(const QJsonValue &other) const; - -public: - bool isArray() const; - bool isBool() const; - bool isDouble() const; - bool isNull() const; - bool isObject() const; - bool isString() const; - bool isUndefined() const; - -public: - QJsonArray toArray(const QJsonArray &defaultValue) const; - QJsonArray toArray() const; - bool toBool(bool defaultValue = false) const; - double toDouble(double defaultValue = 0) const; - int toInt(int defaultValue = 0) const; - QJsonObject toObject(const QJsonObject &defaultValue) const; - QJsonObject toObject() const; - QString toString(const QString &defaultValue = QString()) const; - QVariant toVariant() const; - -public: - Type type() const; - -public: - static QJsonValue fromVariant(const QVariant &variant); - -private: - void swap(QJsonValue &other); - -private: - Type type_; - - union ValueType { - bool b; - double n; - QString *s; - QJsonRoot *r; // OJsonObject or QJsonArray - }; - - ValueType value_; -}; - -#endif - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef QJSON_VALUE_H_ +#define QJSON_VALUE_H_ + +#include + +#if QT_VERSION >= 0x050000 +#include +#else + +class QString; + +#include + +class QJsonRoot; +class QJsonArray; +class QJsonObject; + +class QJsonValue { +public: + enum Type { + Null = 0x0, + Bool = 0x1, + Double = 0x2, + String = 0x3, + Array = 0x4, + Object = 0x5, + Undefined = 0x80 + }; + +public: + QJsonValue(Type type = Null); + QJsonValue(bool b); + QJsonValue(double n); + QJsonValue(int n); + QJsonValue(qint64 n); + QJsonValue(const QString &s); + QJsonValue(QLatin1String s); +#ifndef QT_NO_CAST_FROM_ASCII + QJsonValue(const char *s); +#endif + QJsonValue(const QJsonArray &a); + QJsonValue(const QJsonObject &o); + QJsonValue(const QJsonValue &other); + + ~QJsonValue(); + +private: + // to protect against incorrect usage due to passing a const char * + QJsonValue(const void *); + +public: + QJsonValue &operator=(const QJsonValue &other); + +public: + bool operator!=(const QJsonValue &other) const; + bool operator==(const QJsonValue &other) const; + +public: + bool isArray() const; + bool isBool() const; + bool isDouble() const; + bool isNull() const; + bool isObject() const; + bool isString() const; + bool isUndefined() const; + +public: + QJsonArray toArray(const QJsonArray &defaultValue) const; + QJsonArray toArray() const; + bool toBool(bool defaultValue = false) const; + double toDouble(double defaultValue = 0) const; + int toInt(int defaultValue = 0) const; + QJsonObject toObject(const QJsonObject &defaultValue) const; + QJsonObject toObject() const; + QString toString(const QString &defaultValue = QString()) const; + QVariant toVariant() const; + +public: + Type type() const; + +public: + static QJsonValue fromVariant(const QVariant &variant); + +private: + void swap(QJsonValue &other); + +private: + Type type_; + + union ValueType { + bool b; + double n; + QString *s; + QJsonRoot *r; // OJsonObject or QJsonArray + }; + + ValueType value_; +}; + +#endif + +#endif diff --git a/qjson4/QJsonValueRef b/qjson4/QJsonValueRef old mode 100755 new mode 100644 index f106170..f3b6811 --- a/qjson4/QJsonValueRef +++ b/qjson4/QJsonValueRef @@ -1 +1 @@ -#include "QJsonValueRef.h" +#include "QJsonValueRef.h" diff --git a/qjson4/QJsonValueRef.cpp b/qjson4/QJsonValueRef.cpp old mode 100755 new mode 100644 index 386d056..dade257 --- a/qjson4/QJsonValueRef.cpp +++ b/qjson4/QJsonValueRef.cpp @@ -1,228 +1,228 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "QJsonValueRef.h" - -#if QT_VERSION < 0x050000 - -#include "QJsonArray.h" -#include "QJsonObject.h" - -//------------------------------------------------------------------------------ -// Name: -// Desc: -//------------------------------------------------------------------------------ -QJsonValueRef::QJsonValueRef(QJsonArray *array, int idx) : p_(array), index_(idx) { -} - -//------------------------------------------------------------------------------ -// Name: -// Desc: -//------------------------------------------------------------------------------ -QJsonValueRef::QJsonValueRef(QJsonObject *object, const QString &key) : p_(object), index_(0), key_(key) { -} - -//------------------------------------------------------------------------------ -// Name: -// Desc: -//------------------------------------------------------------------------------ -QJsonValueRef::operator QJsonValue() const { - return toValue(); -} - -//------------------------------------------------------------------------------ -// Name: -// Desc: -//------------------------------------------------------------------------------ -QJsonValueRef &QJsonValueRef::operator=(const QJsonValue &val) { - - if(QJsonObject *const o = p_->toObject()) { - o->values_[key_] = val; - } else if(QJsonArray *const a = p_->toArray()) { - a->values_[index_] = val; - } - return *this; -} - -//------------------------------------------------------------------------------ -// Name: -// Desc: -//------------------------------------------------------------------------------ -QJsonValueRef &QJsonValueRef::operator=(const QJsonValueRef &ref) { - - if(QJsonObject *const o = p_->toObject()) { - o->values_[key_] = ref; - } else if(QJsonArray *const a = p_->toArray()) { - a->values_[index_] = ref; - } - return *this; -} - -//------------------------------------------------------------------------------ -// Name: type -// Desc: -//------------------------------------------------------------------------------ -QJsonValue::Type QJsonValueRef::type() const { - return toValue().type(); -} - -//------------------------------------------------------------------------------ -// Name: isNull -// Desc: -//------------------------------------------------------------------------------ -bool QJsonValueRef::isNull() const { - return toValue().isNull(); -} - -//------------------------------------------------------------------------------ -// Name: isBool -// Desc: -//------------------------------------------------------------------------------ -bool QJsonValueRef::isBool() const { - return toValue().isBool(); -} - -//------------------------------------------------------------------------------ -// Name: isDouble -// Desc: -//------------------------------------------------------------------------------ -bool QJsonValueRef::isDouble() const { - return toValue().isDouble(); -} - -//------------------------------------------------------------------------------ -// Name: isString -// Desc: -//------------------------------------------------------------------------------ -bool QJsonValueRef::isString() const { - return toValue().isString(); -} - -//------------------------------------------------------------------------------ -// Name: isArray -// Desc: -//------------------------------------------------------------------------------ -bool QJsonValueRef::isArray() const { - return toValue().isArray(); -} - -//------------------------------------------------------------------------------ -// Name: isObject -// Desc: -//------------------------------------------------------------------------------ -bool QJsonValueRef::isObject() const { - return toValue().isObject(); -} - -//------------------------------------------------------------------------------ -// Name: isUndefined -// Desc: -//------------------------------------------------------------------------------ -bool QJsonValueRef::isUndefined() const { - return toValue().isUndefined(); -} - -//------------------------------------------------------------------------------ -// Name: toBool -// Desc: -//------------------------------------------------------------------------------ -bool QJsonValueRef::toBool() const { - return toValue().toBool(); -} - -//------------------------------------------------------------------------------ -// Name: toDouble -// Desc: -//------------------------------------------------------------------------------ -double QJsonValueRef::toDouble() const { - return toValue().toDouble(); -} - -//------------------------------------------------------------------------------ -// Name: toInt -// Desc: -//------------------------------------------------------------------------------ -int QJsonValueRef::toInt(int defaultValue) const { - return toValue().toInt(defaultValue); -} - -//------------------------------------------------------------------------------ -// Name: toString -// Desc: -//------------------------------------------------------------------------------ -QString QJsonValueRef::toString() const { - return toValue().toString(); -} - -//------------------------------------------------------------------------------ -// Name: toArray -// Desc: -//------------------------------------------------------------------------------ -QJsonArray QJsonValueRef::toArray() const { - return toValue().toArray(); -} - -//------------------------------------------------------------------------------ -// Name: toObject -// Desc: -//------------------------------------------------------------------------------ -QJsonObject QJsonValueRef::toObject() const { - return toValue().toObject(); -} - -//------------------------------------------------------------------------------ -// Name: operator== -// Desc: -//------------------------------------------------------------------------------ -bool QJsonValueRef::operator==(const QJsonValue &other) const { - return toValue() == other; -} - -//------------------------------------------------------------------------------ -// Name: operator!= -// Desc: -//------------------------------------------------------------------------------ -bool QJsonValueRef::operator!=(const QJsonValue &other) const { - return toValue() != other; -} - -//------------------------------------------------------------------------------ -// Name: toValue -// Desc: -//------------------------------------------------------------------------------ -QJsonValue QJsonValueRef::toValue() const { - if(QJsonObject *const o = p_->toObject()) { - return o->values_[key_]; - } else if(QJsonArray *const a = p_->toArray()) { - return a->values_[index_]; - } - - return QJsonValue(); -} - -//------------------------------------------------------------------------------ -// Name: swap -// Desc: -//------------------------------------------------------------------------------ -void QJsonValueRef::swap(QJsonValueRef &other) { - qSwap(p_, other.p_); - qSwap(key_, other.key_); - qSwap(index_, other.index_); -} - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "QJsonValueRef.h" + +#if QT_VERSION < 0x050000 + +#include "QJsonArray.h" +#include "QJsonObject.h" + +//------------------------------------------------------------------------------ +// Name: +// Desc: +//------------------------------------------------------------------------------ +QJsonValueRef::QJsonValueRef(QJsonArray *array, int idx) : p_(array), index_(idx) { +} + +//------------------------------------------------------------------------------ +// Name: +// Desc: +//------------------------------------------------------------------------------ +QJsonValueRef::QJsonValueRef(QJsonObject *object, const QString &key) : p_(object), index_(0), key_(key) { +} + +//------------------------------------------------------------------------------ +// Name: +// Desc: +//------------------------------------------------------------------------------ +QJsonValueRef::operator QJsonValue() const { + return toValue(); +} + +//------------------------------------------------------------------------------ +// Name: +// Desc: +//------------------------------------------------------------------------------ +QJsonValueRef &QJsonValueRef::operator=(const QJsonValue &val) { + + if(QJsonObject *const o = p_->toObject()) { + o->values_[key_] = val; + } else if(QJsonArray *const a = p_->toArray()) { + a->values_[index_] = val; + } + return *this; +} + +//------------------------------------------------------------------------------ +// Name: +// Desc: +//------------------------------------------------------------------------------ +QJsonValueRef &QJsonValueRef::operator=(const QJsonValueRef &ref) { + + if(QJsonObject *const o = p_->toObject()) { + o->values_[key_] = ref; + } else if(QJsonArray *const a = p_->toArray()) { + a->values_[index_] = ref; + } + return *this; +} + +//------------------------------------------------------------------------------ +// Name: type +// Desc: +//------------------------------------------------------------------------------ +QJsonValue::Type QJsonValueRef::type() const { + return toValue().type(); +} + +//------------------------------------------------------------------------------ +// Name: isNull +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::isNull() const { + return toValue().isNull(); +} + +//------------------------------------------------------------------------------ +// Name: isBool +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::isBool() const { + return toValue().isBool(); +} + +//------------------------------------------------------------------------------ +// Name: isDouble +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::isDouble() const { + return toValue().isDouble(); +} + +//------------------------------------------------------------------------------ +// Name: isString +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::isString() const { + return toValue().isString(); +} + +//------------------------------------------------------------------------------ +// Name: isArray +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::isArray() const { + return toValue().isArray(); +} + +//------------------------------------------------------------------------------ +// Name: isObject +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::isObject() const { + return toValue().isObject(); +} + +//------------------------------------------------------------------------------ +// Name: isUndefined +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::isUndefined() const { + return toValue().isUndefined(); +} + +//------------------------------------------------------------------------------ +// Name: toBool +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::toBool() const { + return toValue().toBool(); +} + +//------------------------------------------------------------------------------ +// Name: toDouble +// Desc: +//------------------------------------------------------------------------------ +double QJsonValueRef::toDouble() const { + return toValue().toDouble(); +} + +//------------------------------------------------------------------------------ +// Name: toInt +// Desc: +//------------------------------------------------------------------------------ +int QJsonValueRef::toInt(int defaultValue) const { + return toValue().toInt(defaultValue); +} + +//------------------------------------------------------------------------------ +// Name: toString +// Desc: +//------------------------------------------------------------------------------ +QString QJsonValueRef::toString() const { + return toValue().toString(); +} + +//------------------------------------------------------------------------------ +// Name: toArray +// Desc: +//------------------------------------------------------------------------------ +QJsonArray QJsonValueRef::toArray() const { + return toValue().toArray(); +} + +//------------------------------------------------------------------------------ +// Name: toObject +// Desc: +//------------------------------------------------------------------------------ +QJsonObject QJsonValueRef::toObject() const { + return toValue().toObject(); +} + +//------------------------------------------------------------------------------ +// Name: operator== +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::operator==(const QJsonValue &other) const { + return toValue() == other; +} + +//------------------------------------------------------------------------------ +// Name: operator!= +// Desc: +//------------------------------------------------------------------------------ +bool QJsonValueRef::operator!=(const QJsonValue &other) const { + return toValue() != other; +} + +//------------------------------------------------------------------------------ +// Name: toValue +// Desc: +//------------------------------------------------------------------------------ +QJsonValue QJsonValueRef::toValue() const { + if(QJsonObject *const o = p_->toObject()) { + return o->values_[key_]; + } else if(QJsonArray *const a = p_->toArray()) { + return a->values_[index_]; + } + + return QJsonValue(); +} + +//------------------------------------------------------------------------------ +// Name: swap +// Desc: +//------------------------------------------------------------------------------ +void QJsonValueRef::swap(QJsonValueRef &other) { + qSwap(p_, other.p_); + qSwap(key_, other.key_); + qSwap(index_, other.index_); +} + +#endif diff --git a/qjson4/QJsonValueRef.h b/qjson4/QJsonValueRef.h old mode 100755 new mode 100644 index 292f37b..478b657 --- a/qjson4/QJsonValueRef.h +++ b/qjson4/QJsonValueRef.h @@ -1,79 +1,79 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef QJSON_VALUEREF_H_ -#define QJSON_VALUEREF_H_ - -#include - -#if QT_VERSION >= 0x050000 -#include -#else - -#include "QJsonValue.h" -class QJsonRoot; - -class QJsonValueRef { -public: - QJsonValueRef(QJsonArray *array, int idx); - - // slight variant from official APIs implementation - QJsonValueRef(QJsonObject *object, const QString &key); - -public: - operator QJsonValue() const; - -public: - QJsonValueRef &operator=(const QJsonValue &val); - QJsonValueRef &operator=(const QJsonValueRef &val); - -public: - QJsonValue::Type type() const; - bool isNull() const; - bool isBool() const; - bool isDouble() const; - bool isString() const; - bool isArray() const; - bool isObject() const; - bool isUndefined() const; - -public: - bool toBool() const; - double toDouble() const; - QString toString() const; - QJsonArray toArray() const; - QJsonObject toObject() const; - int toInt(int defaultValue = 0) const; - -public: - bool operator==(const QJsonValue &other) const; - bool operator!=(const QJsonValue &other) const; - -private: - QJsonValue toValue() const; - void swap(QJsonValueRef &other); - -private: - QJsonRoot *p_; - int index_; - QString key_; -}; - -#endif - -#endif +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef QJSON_VALUEREF_H_ +#define QJSON_VALUEREF_H_ + +#include + +#if QT_VERSION >= 0x050000 +#include +#else + +#include "QJsonValue.h" +class QJsonRoot; + +class QJsonValueRef { +public: + QJsonValueRef(QJsonArray *array, int idx); + + // slight variant from official APIs implementation + QJsonValueRef(QJsonObject *object, const QString &key); + +public: + operator QJsonValue() const; + +public: + QJsonValueRef &operator=(const QJsonValue &val); + QJsonValueRef &operator=(const QJsonValueRef &val); + +public: + QJsonValue::Type type() const; + bool isNull() const; + bool isBool() const; + bool isDouble() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + bool isUndefined() const; + +public: + bool toBool() const; + double toDouble() const; + QString toString() const; + QJsonArray toArray() const; + QJsonObject toObject() const; + int toInt(int defaultValue = 0) const; + +public: + bool operator==(const QJsonValue &other) const; + bool operator!=(const QJsonValue &other) const; + +private: + QJsonValue toValue() const; + void swap(QJsonValueRef &other); + +private: + QJsonRoot *p_; + int index_; + QString key_; +}; + +#endif + +#endif diff --git a/res/5sync-128.png b/res/5sync-128.png deleted file mode 100644 index 4b4bf5a..0000000 Binary files a/res/5sync-128.png and /dev/null differ diff --git a/res/5sync-16.png b/res/5sync-16.png deleted file mode 100644 index 9db49fd..0000000 Binary files a/res/5sync-16.png and /dev/null differ diff --git a/res/5sync-24.png b/res/5sync-24.png deleted file mode 100644 index 01c2fcf..0000000 Binary files a/res/5sync-24.png and /dev/null differ diff --git a/res/5sync-256.png b/res/5sync-256.png deleted file mode 100644 index bf41d47..0000000 Binary files a/res/5sync-256.png and /dev/null differ diff --git a/res/5sync-32.png b/res/5sync-32.png deleted file mode 100644 index 6a768fd..0000000 Binary files a/res/5sync-32.png and /dev/null differ diff --git a/res/5sync-40.png b/res/5sync-40.png deleted file mode 100644 index 0852394..0000000 Binary files a/res/5sync-40.png and /dev/null differ diff --git a/res/5sync-48.png b/res/5sync-48.png deleted file mode 100755 index ff8390c..0000000 Binary files a/res/5sync-48.png and /dev/null differ diff --git a/res/5sync-64.png b/res/5sync-64.png deleted file mode 100644 index 123db7e..0000000 Binary files a/res/5sync-64.png and /dev/null differ diff --git a/res/5sync-96.png b/res/5sync-96.png deleted file mode 100644 index 49f1db7..0000000 Binary files a/res/5sync-96.png and /dev/null differ diff --git a/res/5sync.ico b/res/5sync.ico old mode 100755 new mode 100644 index 105c5cc..af3d0fc Binary files a/res/5sync.ico and b/res/5sync.ico differ diff --git a/res/960x536.png b/res/960x536.png deleted file mode 100644 index 0feac2d..0000000 Binary files a/res/960x536.png and /dev/null differ diff --git a/res/add.svgz b/res/add.svgz new file mode 100644 index 0000000..ca716e5 Binary files /dev/null and b/res/add.svgz differ diff --git a/res/app.qrc b/res/app.qrc deleted file mode 100755 index 8ae92ac..0000000 --- a/res/app.qrc +++ /dev/null @@ -1,43 +0,0 @@ - - - gta5sync_de.qm - qt_de.qm - qtbase_de.qm - gta5sync_fr.qm - qt_fr.qm - qtbase_fr.qm - gta5sync_ru.qm - qt_ru.qm - qtbase_ru.qm - - - savegame.png - 5sync-48.png - 5sync-16.png - 5sync-24.png - 5sync-32.png - 5sync-40.png - 5sync-64.png - 5sync-96.png - 5sync-128.png - 5sync-256.png - back.png - next.png - 960x536.png - empty1x16.png - avatararea.png - avatarareaimport.png - - - global.de.ini - global.en.ini - global.es.ini - global.fr.ini - global.ja.ini - global.ru.ini - global.zh.ini - - - template.g5e - - diff --git a/res/app.rc b/res/app.rc old mode 100755 new mode 100644 index e662ebc..56b1561 --- a/res/app.rc +++ b/res/app.rc @@ -1,36 +1,33 @@ -IDI_ICON1 ICON DISCARDABLE "5sync.ico" - -#define RT_MANIFEST 24 -#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 -CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "gta5view.exe.manifest" - -#include - -VS_VERSION_INFO VERSIONINFO -FILEVERSION 1, 4, 0, 6 -PRODUCTVERSION 1, 4, 0, 6 -FILEFLAGSMASK 0x3fL -FILEFLAGS 0 -FILEOS VOS_NT_WINDOWS32 -FILETYPE VFT_APP -FILESUBTYPE VFT2_UNKNOWN -BEGIN - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0409, 1200 - END - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "CompanyName", "Syping" - VALUE "FileDescription", "gta5view\0" - VALUE "FileVersion", "1.4.0-rc2\0" - VALUE "InternalName", "gta5view\0" - VALUE "LegalCopyright", "Copyright 2016-2017 Syping\0" - VALUE "OriginalFilename", "gta5view.exe\0" - VALUE "ProductName", "gta5view\0" - VALUE "ProductVersion", "1.4.0-rc2\0" - END - END -END +IDI_ICON1 ICON DISCARDABLE "5sync.ico" +#define RT_MANIFEST 24 +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "gta5view.exe.manifest" +#include +VS_VERSION_INFO VERSIONINFO +FILEVERSION 1, 10, 2, 0 +PRODUCTVERSION 1, 10, 2, 0 +FILEFLAGSMASK 0x3fL +FILEFLAGS 0 +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0809, 1200 + END + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Syping" + VALUE "FileDescription", "gta5view" + VALUE "FileVersion", "1.10.2" + VALUE "InternalName", "gta5view" + VALUE "LegalCopyright", "Copyright 2016-2023 Syping" + VALUE "OriginalFilename", "gta5view.exe" + VALUE "ProductName", "gta5view" + VALUE "ProductVersion", "1.10.2" + END + END +END diff --git a/res/avatararea.png b/res/avatararea.png index 6305664..463d846 100644 Binary files a/res/avatararea.png and b/res/avatararea.png differ diff --git a/res/avatarareaimport.png b/res/avatarareaimport.png index 97c53cf..cddc1d8 100644 Binary files a/res/avatarareaimport.png and b/res/avatarareaimport.png differ diff --git a/res/back.png b/res/back.png deleted file mode 100644 index 49beb0a..0000000 Binary files a/res/back.png and /dev/null differ diff --git a/res/back.svgz b/res/back.svgz new file mode 100644 index 0000000..f2f90a5 Binary files /dev/null and b/res/back.svgz differ diff --git a/res/btc.str b/res/btc.str new file mode 100644 index 0000000..33e40a5 --- /dev/null +++ b/res/btc.str @@ -0,0 +1 @@ +Bitcoin \ No newline at end of file diff --git a/res/btc.svgz b/res/btc.svgz new file mode 100644 index 0000000..e10b3a5 Binary files /dev/null and b/res/btc.svgz differ diff --git a/res/de.syping.gta5view.desktop b/res/de.syping.gta5view.desktop new file mode 100644 index 0000000..ed9c7d1 --- /dev/null +++ b/res/de.syping.gta5view.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Type=Application +Name=gta5view +Comment=Open and edit GTA V profiles +Comment[de]=GTA V Profile öffnen und bearbeiten +Comment[ko]=GTA V 프로필을 열고 편집 +Comment[ru]=Просмотрщик и редактор профилей GTA V +Comment[zh_TW]=打開與編輯 GTA V 個人檔案 +Categories=Qt;Utility;FileTools; +TryExec=gta5view +Exec=gta5view %f +Icon=de.syping.gta5view +MimeType=application/x-gta5view-export; diff --git a/res/de.syping.gta5view.metainfo.xml b/res/de.syping.gta5view.metainfo.xml new file mode 100644 index 0000000..c5ab767 --- /dev/null +++ b/res/de.syping.gta5view.metainfo.xml @@ -0,0 +1,72 @@ + + + + de.syping.gta5view + de.syping.gta5view.desktop + CC0-1.0 + GPL-3.0+ + + gta5view + + Open and edit GTA V profiles + GTA V Profile öffnen und bearbeiten + GTA V 프로필을 열고 편집 + Просмотрщик и редактор профилей GTA V + 打開與編輯 GTA V 個人檔案 + + https://img.syping.de/gta5view/gta5view.png + + +

Open Source Snapmatic and Savegame viewer/editor for GTA V

+

Features

+
    +
  • View Snapmatics with the ability to disable them in-game
  • +
  • Edit Snapmatic pictures and properties in multiple ways
  • +
  • Import/Export Snapmatics, Savegames and pictures
  • +
  • Choose between multiple Social Club accounts as GTA V profiles IDs
  • +
+
+ + + Utility + + + Syping + + + + + + + + + + + + + + User Interface + https://img.syping.de/gta5view/mainui_kde.png + + + Snapmatic Viewer + https://img.syping.de/gta5view/picture_kde.png + + + Picture Importer + https://img.syping.de/gta5view/import_kde.png + + + Snapmatic Player Editor + https://img.syping.de/gta5view/players_kde.png + + + Snapmatic Property Editor + https://img.syping.de/gta5view/prop_kde.png + + + + + + https://gta5view.syping.de/ +
diff --git a/res/de.syping.gta5view.png b/res/de.syping.gta5view.png new file mode 100644 index 0000000..3e955a6 Binary files /dev/null and b/res/de.syping.gta5view.png differ diff --git a/res/de.syping.gta5view.xml b/res/de.syping.gta5view.xml new file mode 100644 index 0000000..820ed88 --- /dev/null +++ b/res/de.syping.gta5view.xml @@ -0,0 +1,8 @@ + + + + gta5view Export + + + + diff --git a/res/donate.qrc b/res/donate.qrc new file mode 100644 index 0000000..e4a0005 --- /dev/null +++ b/res/donate.qrc @@ -0,0 +1,15 @@ + + + btc.str + btc.svgz + eth.str + eth.svgz + ltc.str + ltc.svgz + xmr.str + xmr.svgz + + + donate.svgz + + diff --git a/res/donate.svgz b/res/donate.svgz new file mode 100644 index 0000000..c9b7f63 Binary files /dev/null and b/res/donate.svgz differ diff --git a/res/empty1x16.png b/res/empty1x16.png deleted file mode 100644 index 6ab4481..0000000 Binary files a/res/empty1x16.png and /dev/null differ diff --git a/res/eth.str b/res/eth.str new file mode 100644 index 0000000..24b4676 --- /dev/null +++ b/res/eth.str @@ -0,0 +1 @@ +Ethereum \ No newline at end of file diff --git a/res/eth.svgz b/res/eth.svgz new file mode 100644 index 0000000..a2fbcc5 Binary files /dev/null and b/res/eth.svgz differ diff --git a/res/flag-de.png b/res/flag-de.png new file mode 100644 index 0000000..2aeaf35 Binary files /dev/null and b/res/flag-de.png differ diff --git a/res/flag-fr.png b/res/flag-fr.png new file mode 100644 index 0000000..d54b858 Binary files /dev/null and b/res/flag-fr.png differ diff --git a/res/flag-gb.png b/res/flag-gb.png new file mode 100644 index 0000000..a425dcc Binary files /dev/null and b/res/flag-gb.png differ diff --git a/res/flag-kr.png b/res/flag-kr.png new file mode 100644 index 0000000..c0058ea Binary files /dev/null and b/res/flag-kr.png differ diff --git a/res/flag-ru.png b/res/flag-ru.png new file mode 100644 index 0000000..c0111e2 Binary files /dev/null and b/res/flag-ru.png differ diff --git a/res/flag-tw.png b/res/flag-tw.png new file mode 100644 index 0000000..4a8faac Binary files /dev/null and b/res/flag-tw.png differ diff --git a/res/flag-ua.png b/res/flag-ua.png new file mode 100644 index 0000000..0ee802d Binary files /dev/null and b/res/flag-ua.png differ diff --git a/res/flag-us.png b/res/flag-us.png new file mode 100644 index 0000000..293337e Binary files /dev/null and b/res/flag-us.png differ diff --git a/res/global.de.ini b/res/global.de.ini old mode 100755 new mode 100644 index 5b2d94f..0916785 --- a/res/global.de.ini +++ b/res/global.de.ini @@ -1,102 +1,99 @@ -[Global] -AIRP="Internationaler Flughafen von LS" -ALAMO="Alamosee" -ALTA="Alta" -ARMYB="Fort Zancudo" -BANNING="Banning" -BAYTRE="Baytree Canyon" -BEACH="Vespucci Beach" -BHAMCA="Banham Canyon" -BRADP="Braddock-Pass" -BRADT="Braddock-Tunnel" -BURTON="Burton" -CALAFB="Calafia-Brücke" -CANNY="Raton Canyon" -CCREAK="Cassidy Creek" -CHAMH="Chamberlain Hills" -CHIL="Vinewood Hills" -CHU="Chumash" -CMSW="Chiliad-Mountain-Naturschutzgebiet" -COSI="Vorstädte" -CYPRE="Cypress Flats" -DAVIS="Davis" -DELBE="Del Perro Beach" -DELPE="Del Perro" -DELSOL="La Puerta" -DESRT="Grand-Senora-Wüste" -DOWNT="Innenstadt" -DTVINE="Vinewood Mitte" -EAST_V="East Vinewood" -EBURO="El Burro Heights" -ECLIPS="Eclipse" -ELGORL="Leuchtturm El Gordo" -ELSANT="East Los Santos" -ELYSIAN="Elysian Island" -GALFISH="Galilee" -GALLI="Galileo-Park" -GOLF="GWC und Golfclub" -GRAPES="Grapeseed" -GREATC="Great Chaparral" -HARMO="Harmony" -HAWICK="Hawick" -HEART="Heart Attacks Beach" -HORS="Vinewood-Rennbahn" -HUD_MG_TRI_ALA="Alamosee" -HUD_MG_TRI_VES="Vespucci" -HUMLAB="Humane Labs and Research" -JAIL="Bolingbroke-Strafanstalt" -KOREAT="Little Seoul" -LACT="Land-Act-Stausee" -LAGO="Lago Zancudo" -LDAM="Land-Act-Staudamm" -LMESA="La Mesa" -LOSPUER="La Puerta" -LOSSF="Los Santos Freeway" -MGCR_1="South Los Santos" -MGSR_3="Raton Canyon" -MIRR="Mirror Park" -MORN="Morningwood" -MOVIE="Richards Majestic" -MTCHIL="Mount Chiliad" -MTGORDO="Mount Gordo" -MTJOSE="Mount Josiah" -MURRI="Murrieta Heights" -NCHU="North Chumash" -OBSERV="Galileo-Observatorium" -OCEANA="Pazifik" -PALCOV="Paleto Cove" -PALETO="Paleto Bay" -PALFOR="Paleto Forest" -PALHIGH="Palomino-Hochland" -PALMPOW="Palmer-Taylor-Elektrizitätswerk" -PBLUFF="Pacific Bluffs" -PBOX="Pillbox Hill" -PROCOB="Procopio Beach" -PROL="North Yankton" -RANCHO="Rancho" -RGLEN="Richman Glen" -RICHM="Richman" -ROCKF="Rockford Hills" -RTRAK="Redwood-Lights-Rennstrecke" -SANAND="San Andreas" -SANCHIA="San-Chianski-Bergkette" -SANDY="Sandy Shores" -SKID="Mission Row" -SLAB="Stab City" -SLSANT="South Los Santos" -STAD="Maze Bank Arena" -STRAW="Strawberry" -TATAMO="Tataviam-Bergkette" -TERMINA="Terminal" -TEXTI="Textilbezirk" -TONGVAH="Tongva Hills" -TONGVAV="Tongva Valley" -UTOPIAG="Utopia Gardens" -VCANA="Vespucci-Kanäle" -VESP="Vespucci" -VINE="Vinewood" -WINDF="Ron-Alternates-Windpark" -WMIRROR="West Mirror Drive" -WVINE="Vinewood West" -ZANCUDO="Zancudo River" -ZENORA="Senora Freeway" +[Global] +AIRP="Internationaler Flughafen von LS" +ALAMO="Alamosee" +ALTA="Alta" +ARMYB="Fort Zancudo" +BANNING="Banning" +BAYTRE="Baytree Canyon" +BEACH="Vespucci Beach" +BHAMCA="Banham Canyon" +BRADP="Braddock-Pass" +BRADT="Braddock-Tunnel" +BURTON="Burton" +CALAFB="Calafia-Brücke" +CANNY="Raton Canyon" +CCREAK="Cassidy Creek" +CHAMH="Chamberlain Hills" +CHIL="Vinewood Hills" +CHU="Chumash" +CMSW="Chiliad-Mountain-Naturschutzgebiet" +COSI="Vorstädte" +CYPRE="Cypress Flats" +DAVIS="Davis" +DELBE="Del Perro Beach" +DELPE="Del Perro" +DELSOL="La Puerta" +DESRT="Grand-Senora-Wüste" +DOWNT="Innenstadt" +DTVINE="Vinewood Mitte" +EAST_V="East Vinewood" +EBURO="El Burro Heights" +ECLIPS="Eclipse" +ELGORL="Leuchtturm El Gordo" +ELSANT="East Los Santos" +ELYSIAN="Elysian Island" +GALFISH="Galilee" +GALLI="Galileo-Park" +GOLF="GWC und Golfclub" +GRAPES="Grapeseed" +GREATC="Great Chaparral" +HARMO="Harmony" +HAWICK="Hawick" +HEART="Heart Attacks Beach" +HORS="Vinewood-Rennbahn" +HUMLAB="Humane Labs and Research" +ISHEIST="Cayo Perico" +JAIL="Bolingbroke-Strafanstalt" +KOREAT="Little Seoul" +LACT="Land-Act-Stausee" +LAGO="Lago Zancudo" +LDAM="Land-Act-Staudamm" +LMESA="La Mesa" +LOSPUER="La Puerta" +LOSSF="Los Santos Freeway" +MIRR="Mirror Park" +MORN="Morningwood" +MOVIE="Richards Majestic" +MTCHIL="Mount Chiliad" +MTGORDO="Mount Gordo" +MTJOSE="Mount Josiah" +MURRI="Murrieta Heights" +NCHU="North Chumash" +OBSERV="Galileo-Observatorium" +OCEANA="Pazifik" +PALCOV="Paleto Cove" +PALETO="Paleto Bay" +PALFOR="Paleto Forest" +PALHIGH="Palomino-Hochland" +PALMPOW="Palmer-Taylor-Elektrizitätswerk" +PBLUFF="Pacific Bluffs" +PBOX="Pillbox Hill" +PROCOB="Procopio Beach" +PROL="North Yankton" +RANCHO="Rancho" +RGLEN="Richman Glen" +RICHM="Richman" +ROCKF="Rockford Hills" +RTRAK="Redwood-Lights-Rennstrecke" +SANAND="San Andreas" +SANCHIA="San-Chianski-Bergkette" +SANDY="Sandy Shores" +SKID="Mission Row" +SLAB="Stab City" +SLSANT="South Los Santos" +STAD="Maze Bank Arena" +STRAW="Strawberry" +TATAMO="Tataviam-Bergkette" +TERMINA="Terminal" +TEXTI="Textilbezirk" +TONGVAH="Tongva Hills" +TONGVAV="Tongva Valley" +UTOPIAG="Utopia Gardens" +VCANA="Vespucci-Kanäle" +VESP="Vespucci" +VINE="Vinewood" +WINDF="Ron-Alternates-Windpark" +WMIRROR="West Mirror Drive" +WVINE="Vinewood West" +ZANCUDO="Zancudo River" +ZENORA="Senora Freeway" diff --git a/res/global.en.ini b/res/global.en.ini old mode 100755 new mode 100644 index 375c91c..97c84dd --- a/res/global.en.ini +++ b/res/global.en.ini @@ -1,103 +1,99 @@ -[Global] -AIRP="Los Santos International Airport" -ALAMO="Alamo Sea" -ALTA="Alta" -ARMYB="Fort Zancudo" -BANNING="Banning" -BAYTRE="Baytree Canyon" -BEACH="Vespucci Beach" -BHAMCA="Banham Canyon" -BRADP="Braddock Pass" -BRADT="Braddock Tunnel" -BURTON="Burton" -CALAFB="Calafia Bridge" -CANNY="Raton Canyon" -CCREAK="Cassidy Creek" -CHAMH="Chamberlain Hills" -CHIL="Vinewood Hills" -CHU="Chumash" -CMSW="Chiliad Mountain State Wilderness" -COSI="Countryside" -CYPRE="Cypress Flats" -DAVIS="Davis" -DELBE="Del Perro Beach" -DELPE="Del Perro" -DELSOL="La Puerta" -DESRT="Grand Senora Desert" -DOWNT="Downtown" -DTVINE="Downtown Vinewood" -EAST_V="East Vinewood" -EBURO="El Burro Heights" -ECLIPS="Eclipse" -ELGORL="El Gordo Lighthouse" -ELSANT="East Los Santos" -ELYSIAN="Elysian Island" -GALFISH="Galilee" -GALLI="Galileo Park" -GOLF="GWC and Golfing Society" -GRAPES="Grapeseed" -GREATC="Great Chaparral" -HARMO="Harmony" -HAWICK="Hawick" -HEART="Heart Attacks Beach" -HORS="Vinewood Racetrack" -HUD_MG_TRI_ALA="Alamo Sea" -HUD_MG_TRI_VES="Vespucci" -HUMLAB="Humane Labs and Research" -JAIL="Bolingbroke Penitentiary" -KOREAT="Little Seoul" -LACT="Land Act Reservoir" -LAGO="Lago Zancudo" -LDAM="Land Act Dam" -LMESA="La Mesa" -LOSPUER="La Puerta" -LOSSF="Los Santos Freeway" -MGCR_1="South Los Santos" -MGCR_6="Vespucci Canals" -MGSR_3="Raton Canyon" -MIRR="Mirror Park" -MORN="Morningwood" -MOVIE="Richards Majestic" -MTCHIL="Mount Chiliad" -MTGORDO="Mount Gordo" -MTJOSE="Mount Josiah" -MURRI="Murrieta Heights" -NCHU="North Chumash" -OBSERV="Galileo Observatory" -OCEANA="Pacific Ocean" -PALCOV="Paleto Cove" -PALETO="Paleto Bay" -PALFOR="Paleto Forest" -PALHIGH="Palomino Highlands" -PALMPOW="Palmer-Taylor Power Station" -PBLUFF="Pacific Bluffs" -PBOX="Pillbox Hill" -PROCOB="Procopio Beach" -PROL="North Yankton" -RANCHO="Rancho" -RGLEN="Richman Glen" -RICHM="Richman" -ROCKF="Rockford Hills" -RTRAK="Redwood Lights Track" -SANAND="San Andreas" -SANCHIA="San Chianski Mountain Range" -SANDY="Sandy Shores" -SKID="Mission Row" -SLAB="Stab City" -SLSANT="South Los Santos" -STAD="Maze Bank Arena" -STRAW="Strawberry" -TATAMO="Tataviam Mountains" -TERMINA="Terminal" -TEXTI="Textile City" -TONGVAH="Tongva Hills" -TONGVAV="Tongva Valley" -UTOPIAG="Utopia Gardens" -VCANA="Vespucci Canals" -VESP="Vespucci" -VINE="Vinewood" -WINDF="Ron Alternates Wind Farm" -WMIRROR="West Mirror Drive" -WVINE="West Vinewood" -ZANCUDO="Zancudo River" -ZENORA="Senora Freeway" +[Global] +AIRP="Los Santos International Airport" +ALAMO="Alamo Sea" +ALTA="Alta" +ARMYB="Fort Zancudo" +BANNING="Banning" +BAYTRE="Baytree Canyon" +BEACH="Vespucci Beach" +BHAMCA="Banham Canyon" +BRADP="Braddock Pass" +BRADT="Braddock Tunnel" +BURTON="Burton" +CALAFB="Calafia Bridge" +CANNY="Raton Canyon" +CCREAK="Cassidy Creek" +CHAMH="Chamberlain Hills" +CHIL="Vinewood Hills" +CHU="Chumash" +CMSW="Chiliad Mountain State Wilderness" +COSI="Countryside" +CYPRE="Cypress Flats" +DAVIS="Davis" +DELBE="Del Perro Beach" +DELPE="Del Perro" +DELSOL="La Puerta" +DESRT="Grand Senora Desert" +DOWNT="Downtown" +DTVINE="Downtown Vinewood" +EAST_V="East Vinewood" +EBURO="El Burro Heights" +ECLIPS="Eclipse" +ELGORL="El Gordo Lighthouse" +ELSANT="East Los Santos" +ELYSIAN="Elysian Island" +GALFISH="Galilee" +GALLI="Galileo Park" +GOLF="GWC and Golfing Society" +GRAPES="Grapeseed" +GREATC="Great Chaparral" +HARMO="Harmony" +HAWICK="Hawick" +HEART="Heart Attacks Beach" +HORS="Vinewood Racetrack" +HUMLAB="Humane Labs and Research" +ISHEIST="Cayo Perico" +JAIL="Bolingbroke Penitentiary" +KOREAT="Little Seoul" +LACT="Land Act Reservoir" +LAGO="Lago Zancudo" +LDAM="Land Act Dam" +LMESA="La Mesa" +LOSPUER="La Puerta" +LOSSF="Los Santos Freeway" +MIRR="Mirror Park" +MORN="Morningwood" +MOVIE="Richards Majestic" +MTCHIL="Mount Chiliad" +MTGORDO="Mount Gordo" +MTJOSE="Mount Josiah" +MURRI="Murrieta Heights" +NCHU="North Chumash" +OBSERV="Galileo Observatory" +OCEANA="Pacific Ocean" +PALCOV="Paleto Cove" +PALETO="Paleto Bay" +PALFOR="Paleto Forest" +PALHIGH="Palomino Highlands" +PALMPOW="Palmer-Taylor Power Station" +PBLUFF="Pacific Bluffs" +PBOX="Pillbox Hill" +PROCOB="Procopio Beach" +PROL="North Yankton" +RANCHO="Rancho" +RGLEN="Richman Glen" +RICHM="Richman" +ROCKF="Rockford Hills" +RTRAK="Redwood Lights Track" +SANAND="San Andreas" +SANCHIA="San Chianski Mountain Range" +SANDY="Sandy Shores" +SKID="Mission Row" +SLAB="Stab City" +SLSANT="South Los Santos" +STAD="Maze Bank Arena" +STRAW="Strawberry" +TATAMO="Tataviam Mountains" +TERMINA="Terminal" +TEXTI="Textile City" +TONGVAH="Tongva Hills" +TONGVAV="Tongva Valley" +UTOPIAG="Utopia Gardens" +VCANA="Vespucci Canals" +VESP="Vespucci" +VINE="Vinewood" +WINDF="Ron Alternates Wind Farm" +WMIRROR="West Mirror Drive" +WVINE="West Vinewood" +ZANCUDO="Zancudo River" +ZENORA="Senora Freeway" diff --git a/res/global.es.ini b/res/global.es.ini index b2d3cb6..b364c50 100644 --- a/res/global.es.ini +++ b/res/global.es.ini @@ -1,107 +1,101 @@ -[Global] -AIRP="Aeropuerto Intl. de Los Santos" -ALAMO="Alamo Sea" -ALTA="Alta" -ARMYB="Fort Zancudo" -BANNING="Banning" -BAYTRE="Baytree Canyon" -BEACH="Vespucci Beach" -BHAMCA="Banham Canyon" -BRADP="Braddock Pass" -BRADT="Túnel de Braddock" -BURTON="Burton" -CALAFB="Puente de Calafia" -CANNY="Raton Canyon" -CCREAK="Cassidy Creek" -CHAMH="Chamberlain Hills" -CHIL="Vinewood Hills" -CHU="Chumash" -CMSW="Parque natural del monte Chiliad" -COSI="Zona rural" -CYPRE="Cypress Flats" -DAVIS="Davis" -DELBE="Del Perro Beach" -DELPE="Del Perro" -DELSOL="La Puerta" -DESRT="Desierto de Grand Señora" -DOWNT="Centro" -DTVINE="Centro de Vinewood" -EAST_V="Vinewood Este" -EBURO="El Burro Heights" -ECLIPS="Eclipse" -ELGORL="Faro de El Gordo" -ELSANT="Los Santos Este" -ELYSIAN="Elysian Island" -GALFISH="Galilee" -GALLI="Galileo Park" -GOLF="Club de campo y de golf GW" -GRAPES="Grapeseed" -GREATC="Great Chaparral" -HARMO="Harmony" -HAWICK="Hawick" -HEART="Heart Attacks Beach" -HORS="Circuito de Vinewood" -HUD_MG_TRI_ALA="Alamo Sea" -HUD_MG_TRI_VES="Vespucci" -HUMLAB="Laboratorios Humane" -JAIL="Penitenciaría de Bolingbroke" -KOREAT="Little Seoul" -LACT="Embalse de Land Act" -LAGO="Lago Zancudo" -LDAM="Presa de Land Act" -LMESA="La Mesa" -LOSPFY="Autopista de La Puerta" -LOSPUER="La Puerta" -LOSSF="Autopista de Los Santos" -MGCR_1="Los Santos Sur" -MGCR_6="Canales de Vespucci" -MGSR_3="Raton Canyon" -MIRR="Mirror Park" -MORN="Morningwood" -MOVIE="Richards Majestic" -MO_CS_HIGH="Alta" -MO_HIGH="Alta" -MTCHIL="Monte Chiliad" -MTGORDO="Monte Gordo" -MTJOSE="Monte Josiah" -MURRI="Murrieta Heights" -NCHU="Chumash Norte" -OBSERV="Observatorio Galileo" -OCEANA="Océano Pacífico" -PALCOV="Paleto Cove" -PALETO="Paleto Bay" -PALFOR="Bosque de Paleto" -PALHIGH="Palomino Highlands" -PALMPOW="Central eléctrica Palmer-Taylor" -PBLUFF="Pacific Bluffs" -PBOX="Pillbox Hill" -PROCOB="Procopio Beach" -PROL="North Yankton" -RANCHO="Rancho" -RGLEN="Richman Glen" -RICHM="Richman" -ROCKF="Rockford Hills" -RTRAK="Circuito Redwood Lights" -SANAND="San Andreas" -SANCHIA="Cordillera San Chianski" -SANDY="Sandy Shores" -SENORA="Autopista de Señora" -SKID="Mission Row" -SLAB="Stab City" -SLSANT="Los Santos Sur" -STAD="Maze Bank Arena" -STRAW="Strawberry" -TATAMO="Montañas Tataviam" -TERMINA="Terminal" -TEXTI="Textile City" -TONGVAH="Colinas de Tongva" -TONGVAV="Valle de Tongva" -UTOPIAG="Utopia Gardens" -VCANA="Canales de Vespucci" -VESP="Vespucci" -VINE="Vinewood" -WINDF="Granja eólica de Ron Alternates" -WMIRROR="West Mirror Drive" -WVINE="Vinewood Oeste" -ZANCUDO="Río Zancudo" -ZENORA="Autopista de Señora" +[Global] +AIRP="Aeropuerto Intl. de Los Santos" +ALAMO="Alamo Sea" +ALTA="Alta" +ARMYB="Fort Zancudo" +BANNING="Banning" +BAYTRE="Baytree Canyon" +BEACH="Vespucci Beach" +BHAMCA="Banham Canyon" +BRADP="Braddock Pass" +BRADT="Túnel de Braddock" +BURTON="Burton" +CALAFB="Puente de Calafia" +CANNY="Raton Canyon" +CCREAK="Cassidy Creek" +CHAMH="Chamberlain Hills" +CHIL="Vinewood Hills" +CHU="Chumash" +CMSW="Parque natural del monte Chiliad" +COSI="Zona rural" +CYPRE="Cypress Flats" +DAVIS="Davis" +DELBE="Del Perro Beach" +DELPE="Del Perro" +DELSOL="La Puerta" +DESRT="Desierto de Grand Señora" +DOWNT="Centro" +DTVINE="Centro de Vinewood" +EAST_V="Vinewood Este" +EBURO="El Burro Heights" +ECLIPS="Eclipse" +ELGORL="Faro de El Gordo" +ELSANT="Los Santos Este" +ELYSIAN="Elysian Island" +GALFISH="Galilee" +GALLI="Galileo Park" +GOLF="Club de campo y de golf GW" +GRAPES="Grapeseed" +GREATC="Great Chaparral" +HARMO="Harmony" +HAWICK="Hawick" +HEART="Heart Attacks Beach" +HORS="Circuito de Vinewood" +HUMLAB="Laboratorios Humane" +ISHEIST="Cayo Perico" +JAIL="Penitenciaría de Bolingbroke" +KOREAT="Little Seoul" +LACT="Embalse de Land Act" +LAGO="Lago Zancudo" +LDAM="Presa de Land Act" +LMESA="La Mesa" +LOSPFY="Autopista de La Puerta" +LOSPUER="La Puerta" +LOSSF="Autopista de Los Santos" +MIRR="Mirror Park" +MORN="Morningwood" +MOVIE="Richards Majestic" +MTCHIL="Monte Chiliad" +MTGORDO="Monte Gordo" +MTJOSE="Monte Josiah" +MURRI="Murrieta Heights" +NCHU="Chumash Norte" +OBSERV="Observatorio Galileo" +OCEANA="Océano Pacífico" +PALCOV="Paleto Cove" +PALETO="Paleto Bay" +PALFOR="Bosque de Paleto" +PALHIGH="Palomino Highlands" +PALMPOW="Central eléctrica Palmer-Taylor" +PBLUFF="Pacific Bluffs" +PBOX="Pillbox Hill" +PROCOB="Procopio Beach" +PROL="North Yankton" +RANCHO="Rancho" +RGLEN="Richman Glen" +RICHM="Richman" +ROCKF="Rockford Hills" +RTRAK="Circuito Redwood Lights" +SANAND="San Andreas" +SANCHIA="Cordillera San Chianski" +SANDY="Sandy Shores" +SENORA="Autopista de Señora" +SKID="Mission Row" +SLAB="Stab City" +SLSANT="Los Santos Sur" +STAD="Maze Bank Arena" +STRAW="Strawberry" +TATAMO="Montañas Tataviam" +TERMINA="Terminal" +TEXTI="Textile City" +TONGVAH="Colinas de Tongva" +TONGVAV="Valle de Tongva" +UTOPIAG="Utopia Gardens" +VCANA="Canales de Vespucci" +VESP="Vespucci" +VINE="Vinewood" +WINDF="Granja eólica de Ron Alternates" +WMIRROR="West Mirror Drive" +WVINE="Vinewood Oeste" +ZANCUDO="Río Zancudo" +ZENORA="Autopista de Señora" diff --git a/res/global.es_MX.ini b/res/global.es_MX.ini new file mode 100644 index 0000000..620ef14 --- /dev/null +++ b/res/global.es_MX.ini @@ -0,0 +1,101 @@ +[Global] +AIRP="Aeropuerto Internacional de Los Santos" +ALAMO="Alamo Sea" +ALTA="Alta" +ARMYB="Fort Zancudo" +BANNING="Banning" +BAYTRE="Baytree Canyon" +BEACH="Vespucci Beach" +BHAMCA="Banham Canyon" +BRADP="Braddock Pass" +BRADT="Braddock Tunnel" +BURTON="Burton" +CALAFB="Calafia Bridge" +CANNY="Raton Canyon" +CCREAK="Cassidy Creek" +CHAMH="Chamberlain Hills" +CHIL="Vinewood Hills" +CHU="Chumash" +CMSW="Reserva Natural Mount Chiliad" +COSI="Zona Rural" +CYPRE="Cypress Flats" +DAVIS="Davis" +DELBE="Del Perro Beach" +DELPE="Del Perro" +DELSOL="La Puerta" +DESRT="Grand Senora Desert" +DOWNT="Centro" +DTVINE="Centro de Vinewood" +EAST_V="Vinewood Este" +EBURO="El Burro Heights" +ECLIPS="Eclipse" +ELGORL="Faro de El Gordo" +ELSANT="Los Santos Este" +ELYSIAN="Elysian Island" +GALFISH="Galilee" +GALLI="Galileo Park" +GOLF="Club de golf GW" +GRAPES="Grapeseed" +GREATC="Great Chaparral" +HARMO="Harmony" +HAWICK="Hawick" +HEART="Heart Attacks Beach" +HORS="Circuito de Vinewood" +HUMLAB="Humane Labs and Research" +ISHEIST="Cayo Perico" +JAIL="Penitenciaría de Bolingbroke" +KOREAT="Little Seoul" +LACT="Presa de Land Act" +LAGO="Lago Zancudo" +LDAM="Presa de Land Act" +LMESA="La Mesa" +LOSPFY="La Puerta Freeway" +LOSPUER="La Puerta" +LOSSF="Los Santos Freeway" +MIRR="Mirror Park" +MORN="Morningwood" +MOVIE="Richards Majestic" +MTCHIL="Mount Chiliad" +MTGORDO="Mount Gordo" +MTJOSE="Mount Josiah" +MURRI="Murrieta Heights" +NCHU="Chumash Norte" +OBSERV="Observatorio Galileo" +OCEANA="Océano Pacífico" +PALCOV="Paleto Cove" +PALETO="Paleto Bay" +PALFOR="Paleto Forest" +PALHIGH="Palomino Highlands" +PALMPOW="Central Palmer-Taylor" +PBLUFF="Pacific Bluffs" +PBOX="Pillbox Hill" +PROCOB="Procopio Beach" +PROL="North Yankton" +RANCHO="Rancho" +RGLEN="Richman Glen" +RICHM="Richman" +ROCKF="Rockford Hills" +RTRAK="Circuito Redwood Lights" +SANAND="San Andreas" +SANCHIA="San Chianski Mountain Range" +SANDY="Sandy Shores" +SENORA="Senora Freeway" +SKID="Mission Row" +SLAB="Stab City" +SLSANT="Los Santos Sur" +STAD="Maze Bank Arena" +STRAW="Strawberry" +TATAMO="Tataviam Mountains" +TERMINA="Terminal" +TEXTI="Textile City" +TONGVAH="Tongva Hills" +TONGVAV="Tongva Valley" +UTOPIAG="Utopia Gardens" +VCANA="Vespucci Canals" +VESP="Vespucci" +VINE="Vinewood" +WINDF="Granja eólica Ron Alternates" +WMIRROR="West Mirror Drive" +WVINE="Vinewood Oeste" +ZANCUDO="Río Zancudo" +ZENORA="Senora Freeway" diff --git a/res/global.fr.ini b/res/global.fr.ini index e2669d1..807c5ec 100644 --- a/res/global.fr.ini +++ b/res/global.fr.ini @@ -1,103 +1,99 @@ -[Global] -AIRP="Aéroport international de LS" -ALAMO="Alamo Sea" -ALTA="Alta" -ARMYB="Fort Zancudo" -BANNING="Banning" -BAYTRE="Baytree Canyon" -BEACH="Vespucci Beach" -BHAMCA="Banham Canyon" -BRADP="Braddock Pass" -BRADT="Braddock Tunnel" -BURTON="Burton" -CALAFB="Calafia Bridge" -CANNY="Raton Canyon" -CCREAK="Cassidy Creek" -CHAMH="Chamberlain Hills" -CHIL="Vinewood Hills" -CHU="Chumash" -CMSW="Parc national du Mont Chiliad" -CYPRE="Cypress Flats" -DAVIS="Davis" -DELBE="Del Perro Beach" -DELPE="Del Perro" -DELSOL="La Puerta" -DESRT="Grand Señora Desert" -DOWNT="Centre-ville" -DTVINE="Centre de Vinewood" -EAST_V="Vinewood East" -EBURO="El Burro Heights" -ECLIPS="Eclipse" -ELGORL="Phare d'El Gordo" -ELSANT="East Los Santos" -ELYSIAN="Elysian Island" -GALFISH="Galilee" -GALLI="Galileo Park" -GOLF="Club de golf et de détente du Grand Ouest" -GRAPES="Grapeseed" -GREATC="Great Chaparral" -HARMO="Harmony" -HAWICK="Hawick" -HEART="Heart Attacks Beach" -HORS="Hippodrome de Vinewood" -HUD_MG_TRI_ALA="Alamo Sea" -HUD_MG_TRI_VES="Vespucci" -HUMLAB="Laboratoires Humane" -JAIL="Pénitencier de Bolingbroke" -KOREAT="Little Seoul" -LACT="Land Act Reservoir" -LAGO="Lago Zancudo" -LDAM="Land Act Dam" -LMESA="La Mesa" -LOSPFY="La Puerta Freeway" -LOSPUER="La Puerta" -LOSSF="Los Santos Freeway" -MGCR_1="South Los Santos" -MGCR_6="Canaux de Vespucci" -MGSR_3="Raton Canyon" -MIRR="Mirror Park" -MORN="Morningwood" -MOVIE="Richards Majestic" -MTCHIL="Mont Chiliad" -MTGORDO="Mont Gordo" -MTJOSE="Mont Josiah" -MURRI="Murrieta Heights" -NCHU="North Chumash" -OBSERV="Observatoire Galileo" -OCEANA="Océan pacifique" -PALCOV="Paleto Cove" -PALETO="Paleto Bay" -PALFOR="Paleto Forest" -PALHIGH="Palomino Highlands" -PALMPOW="Centrale Palmer-Taylor" -PBLUFF="Pacific Bluffs" -PBOX="Pillbox Hill" -PROCOB="Procopio Beach" -PROL="North Yankton" -RANCHO="Rancho" -RGLEN="Richman Glen" -RICHM="Richman" -ROCKF="Rockford Hills" -RTRAK="Circuit Redwood Lights" -SANAND="San Andreas" -SANCHIA="Monts de San Chianski" -SANDY="Sandy Shores" -SKID="Mission Row" -SLAB="Stab City" -SLSANT="South Los Santos" -STAD="Maze Bank Arena" -STRAW="Strawberry" -TATAMO="Monts Tataviam" -TERMINA="Terminal" -TEXTI="Textile City" -TONGVAH="Tongva Hills" -TONGVAV="Tongva Valley" -UTOPIAG="Utopia Gardens" -VCANA="Canaux de Vespucci" -VESP="Vespucci" -VINE="Vinewood" -WINDF="Parc d'éoliennes Ron Alternates" -WMIRROR="Mirror Drive West" -WVINE="Vinewood West" -ZANCUDO="Zancudo River" -ZENORA="Señora Freeway" +[Global] +AIRP="Aéroport international de LS" +ALAMO="Alamo Sea" +ALTA="Alta" +ARMYB="Fort Zancudo" +BANNING="Banning" +BAYTRE="Baytree Canyon" +BEACH="Vespucci Beach" +BHAMCA="Banham Canyon" +BRADP="Braddock Pass" +BRADT="Braddock Tunnel" +BURTON="Burton" +CALAFB="Calafia Bridge" +CANNY="Raton Canyon" +CCREAK="Cassidy Creek" +CHAMH="Chamberlain Hills" +CHIL="Vinewood Hills" +CHU="Chumash" +CMSW="Parc national du Mont Chiliad" +CYPRE="Cypress Flats" +DAVIS="Davis" +DELBE="Del Perro Beach" +DELPE="Del Perro" +DELSOL="La Puerta" +DESRT="Grand Señora Desert" +DOWNT="Centre-ville" +DTVINE="Centre de Vinewood" +EAST_V="Vinewood East" +EBURO="El Burro Heights" +ECLIPS="Eclipse" +ELGORL="Phare d'El Gordo" +ELSANT="East Los Santos" +ELYSIAN="Elysian Island" +GALFISH="Galilee" +GALLI="Galileo Park" +GOLF="Club de golf et de détente du Grand Ouest" +GRAPES="Grapeseed" +GREATC="Great Chaparral" +HARMO="Harmony" +HAWICK="Hawick" +HEART="Heart Attacks Beach" +HORS="Hippodrome de Vinewood" +HUMLAB="Laboratoires Humane" +ISHEIST="Cayo Perico" +JAIL="Pénitencier de Bolingbroke" +KOREAT="Little Seoul" +LACT="Land Act Reservoir" +LAGO="Lago Zancudo" +LDAM="Land Act Dam" +LMESA="La Mesa" +LOSPFY="La Puerta Freeway" +LOSPUER="La Puerta" +LOSSF="Los Santos Freeway" +MIRR="Mirror Park" +MORN="Morningwood" +MOVIE="Richards Majestic" +MTCHIL="Mont Chiliad" +MTGORDO="Mont Gordo" +MTJOSE="Mont Josiah" +MURRI="Murrieta Heights" +NCHU="North Chumash" +OBSERV="Observatoire Galileo" +OCEANA="Océan pacifique" +PALCOV="Paleto Cove" +PALETO="Paleto Bay" +PALFOR="Paleto Forest" +PALHIGH="Palomino Highlands" +PALMPOW="Centrale Palmer-Taylor" +PBLUFF="Pacific Bluffs" +PBOX="Pillbox Hill" +PROCOB="Procopio Beach" +PROL="North Yankton" +RANCHO="Rancho" +RGLEN="Richman Glen" +RICHM="Richman" +ROCKF="Rockford Hills" +RTRAK="Circuit Redwood Lights" +SANAND="San Andreas" +SANCHIA="Monts de San Chianski" +SANDY="Sandy Shores" +SKID="Mission Row" +SLAB="Stab City" +SLSANT="South Los Santos" +STAD="Maze Bank Arena" +STRAW="Strawberry" +TATAMO="Monts Tataviam" +TERMINA="Terminal" +TEXTI="Textile City" +TONGVAH="Tongva Hills" +TONGVAV="Tongva Valley" +UTOPIAG="Utopia Gardens" +VCANA="Canaux de Vespucci" +VESP="Vespucci" +VINE="Vinewood" +WINDF="Parc d'éoliennes Ron Alternates" +WMIRROR="Mirror Drive West" +WVINE="Vinewood West" +ZANCUDO="Zancudo River" +ZENORA="Señora Freeway" diff --git a/res/global.it.ini b/res/global.it.ini new file mode 100644 index 0000000..4c89a36 --- /dev/null +++ b/res/global.it.ini @@ -0,0 +1,100 @@ +[Global] +AIRP="Los Santos International Airport" +ALAMO="Alamo Sea" +ALTA="Alta" +ARMYB="Fort Zancudo" +BANNING="Banning" +BAYTRE="Baytree Canyon" +BEACH="Vespucci Beach" +BHAMCA="Banham Canyon" +BRADP="Braddock Pass" +BRADT="Braddock Tunnel" +BURTON="Burton" +CALAFB="Calafia Bridge" +CANNY="Raton Canyon" +CCREAK="Cassidy Creek" +CHAMH="Chamberlain Hills" +CHIL="Vinewood Hills" +CHU="Chumash" +CMSW="Chiliad Mountain State Wilderness" +COSI="Campagna" +CYPRE="Cypress Flats" +DAVIS="Davis" +DELBE="Del Perro Beach" +DELPE="Del Perro" +DELSOL="La Puerta" +DESRT="Grand Senora Desert" +DOWNT="Centro" +DTVINE="Downtown Vinewood" +EAST_V="East Vinewood" +EBURO="El Burro Heights" +ECLIPS="Eclipse" +ELGORL="Faro di El Gordo" +ELSANT="East Los Santos" +ELYSIAN="Elysian Island" +GALFISH="Galilee" +GALLI="Galileo Park" +GOLF="GWC and Golfing Society" +GRAPES="Grapeseed" +GREATC="Grande boscaglia" +HARMO="Harmony" +HAWICK="Hawick" +HEART="Heart Attacks Beach" +HORS="Vinewood Racetrack" +HUMLAB="Laboratori di ricerca Humane" +ISHEIST="Cayo Perico" +JAIL="Bolingbroke Penitentiary" +KOREAT="Little Seoul" +LACT="Land Act Reservoir" +LAGO="Lago Zancudo" +LDAM="Land Act Dam" +LMESA="La Mesa" +LOSPFY="La Puerta Freeway" +LOSPUER="La Puerta" +LOSSF="Los Santos Freeway" +MIRR="Mirror Park" +MORN="Morningwood" +MOVIE="Richards Majestic" +MTCHIL="Mount Chiliad" +MTGORDO="Mount Gordo" +MTJOSE="Mount Josiah" +NCHU="North Chumash" +OBSERV="Galileo Observatory" +OCEANA="Oceano Pacifico" +PALCOV="Paleto Cove" +PALETO="Paleto Bay" +PALFOR="Paleto Forest" +PALHIGH="Palomino Highlands" +PALMPOW="Centrale elettrica Palmer-Taylor" +PBLUFF="Pacific Bluffs" +PBOX="Pillbox Hill" +PROCOB="Procopio Beach" +PROL="North Yankton" +RANCHO="Rancho" +RGLEN="Richman Glen" +RICHM="Richman" +ROCKF="Rockford Hills" +RTRAK="Redwood Lights Track" +SANAND="San Andreas" +SANCHIA="San Chianski Mountain Range" +SANDY="Sandy Shores" +SENORA="Senora Freeway" +SKID="Mission Row" +SLAB="Stab City" +SLSANT="South Los Santos" +STAD="Maze Bank Arena" +STRAW="Strawberry" +TATAMO="Tataviam Mountains" +TERMINA="Terminal" +TEXTI="Textile City" +TONGVAH="Tongva Hills" +TONGVAV="Tongva Valley" +UTOPIAG="Utopia Gardens" +VCANA="Vespucci Canals" +VESP="Vespucci" +VINE="Vinewood" +WINDF="Parco eolico Ron Alternates" +WMIRROR="West Mirror Drive" +WVINE="West Vinewood" +ZANCUDO="Zancudo River" +ZENORA="Senora Freeway" diff --git a/res/global.ja.ini b/res/global.ja.ini old mode 100755 new mode 100644 index 24ad7d2..f38223d --- a/res/global.ja.ini +++ b/res/global.ja.ini @@ -1,106 +1,101 @@ -[Global] -AIRP="ロスサントス国際空港" -ALAMO="アラモ海" -ALTA="アルタ" -ARMYB="フォート・ザンクード" -BANNING="バニング" -BAYTRE="ベイツリー・キャニオン" -BEACH="ベスプッチ・ビーチ" -BHAMCA="バンナムキャニオン" -BRADP="ブラドック・パス" -BRADT="ブラドック・トンネル" -BSS_BSTR_131="リチャーズ・マジェスティック" -BURTON="バートン" -CALAFB="カラフィア橋" -CANNY="ラトン・キャニオン" -CCREAK="キャシディ・クリーク" -CHAMH="チェンバーレイン・ヒルズ" -CHIL="バインウッド・ヒルズ" -CHU="チュマシュ" -CMSW="チリアド山自然保護区" -COSI="農園地帯" -CYPRE="サイプレス・フラット" -DAVIS="デイビス" -DELBE="デル・ペロ・ビーチ" -DELPE="デル・ペロ" -DELSOL="ラ・プエルタ" -DESRT="グランド・セノーラ砂漠" -DOWNT="ダウンタウン" -DTVINE="ダウンタウン・バインウッド" -EAST_V="イースト・バインウッド" -EBURO="エル・ブロ・ハイツ" -ECLIPS="イクリプス" -ELGORL="エル・ゴルド灯台" -ELSANT="イースト・ロスサントス" -ELYSIAN="エリシアン島" -GALFISH="ガリラヤ" -GALLI="ガリレオ・パーク" -GOLF="GWC&ゴルフ協会" -GRAPES="グレイプシード" -GREATC="グレート・チャパレル" -HARMO="ハーモニー" -HAWICK="ハウィック" -HEART="ハートアタック・ビーチ" -HORS="バインウッド・レーストラック" -HUD_MG_TRI_ALA="アラモ海" -HUD_MG_TRI_VES="ベスプッチ" -HUMLAB="ヒューメイン研究所" -JAIL="ボーリングブローク刑務所" -KOREAT="リトル・ソウル" -LACT="ランド・アクト貯水池" -LAGO="ラゴ・ザンクード" -LDAM="ランド・アクト・ダム" -LMESA="ラ・メサ" -LOSPFY="ラ・プエルタ高速道路" -LOSPUER="ラ・プエルタ" -LOSSF="ロスサントス高速道路" -MGCR_1="サウス・ロスサントス" -MGCR_6="ベスプッチ運河" -MGSR_3="ラトン・キャニオン" -MIRR="ミラー・パーク" -MORN="モーニングウッド" -MOVIE="リチャーズ・マジェスティック" -MTCHIL="チリアド山" -MTGORDO="ゴルド山" -MTJOSE="ジョサイア山" -MURRI="ムリエタ・ハイツ" -NCHU="北チュマシュ" -OBSERV="ガリレオ観測所" -OCEANA="太平洋" -PALCOV="パレト・コーブ" -PALETO="パレト・ベイ" -PALFOR="パレト・フォレスト" -PALHIGH="パロミノ高地" -PALMPOW="パーマー・テイラー発電所" -PBLUFF="パシフィック・ブラフス" -PBOX="ピルボックス・ヒル" -PROCOB="プロコピオ・ビーチ" -PROL="ノース・ヤンクトン" -RANCHO="ランチョ" -RGLEN="リッチマン・グレン" -RICHM="リッチマン" -ROCKF="ロックフォード・ヒルズ" -RTRAK="レッドウッド・ライト・トラック" -SANAND="サンアンドレアス" -SANCHIA="サン・チアンスキー山脈" -SANDY="サンディ海岸" -SENORA="セノーラ高速道路" -SKID="ミッション・ロウ" -SLAB="スタブシティ" -SLSANT="サウス・ロスサントス" -STAD="メイズバンク・アリーナ" -STRAW="ストロベリー" -TATAMO="タタヴィアム山地" -TERMINA="ターミナル" -TEXTI="テキスタイルシティ" -TONGVAH="トングバ・ヒルズ" -TONGVAV="トングバ・バレー" -UTOPIAG="ユートピア・ガーデンズ" -VCANA="ベスプッチ運河" -VESP="ベスプッチ" -VINE="バインウッド" -WINDF="ロン・オルタネット・ウィンドファーム" -WMIRROR="ウエスト・ミラー・ドライブ" -WVINE="ウエスト・バインウッド" -ZANCUDO="ザンクード川" -ZENORA="セノーラ高速道路" +[Global] +AIRP="ロスサントス国際空港" +ALAMO="アラモ海" +ALTA="アルタ" +ARMYB="フォート・ザンクード" +BANNING="バニング" +BAYTRE="ベイツリー・キャニオン" +BEACH="ベスプッチ・ビーチ" +BHAMCA="バンナムキャニオン" +BRADP="ブラドック・パス" +BRADT="ブラドック・トンネル" +BURTON="バートン" +CALAFB="カラフィア橋" +CANNY="ラトン・キャニオン" +CCREAK="キャシディ・クリーク" +CHAMH="チェンバーレイン・ヒルズ" +CHIL="バインウッド・ヒルズ" +CHU="チュマシュ" +CMSW="チリアド山自然保護区" +COSI="農園地帯" +CYPRE="サイプレス・フラット" +DAVIS="デイビス" +DELBE="デル・ペロ・ビーチ" +DELPE="デル・ペロ" +DELSOL="ラ・プエルタ" +DESRT="グランド・セノーラ砂漠" +DOWNT="ダウンタウン" +DTVINE="ダウンタウン・バインウッド" +EAST_V="イースト・バインウッド" +EBURO="エル・ブロ・ハイツ" +ECLIPS="イクリプス" +ELGORL="エル・ゴルド灯台" +ELSANT="イースト・ロスサントス" +ELYSIAN="エリシアン島" +GALFISH="ガリラヤ" +GALLI="ガリレオ・パーク" +GOLF="GWC&ゴルフ協会" +GRAPES="グレイプシード" +GREATC="グレート・チャパレル" +HARMO="ハーモニー" +HAWICK="ハウィック" +HEART="ハートアタック・ビーチ" +HORS="バインウッド・レーストラック" +HUMLAB="ヒューメイン研究所" +ISHEIST="カヨ・ペリコ" +JAIL="ボーリングブローク刑務所" +KOREAT="リトル・ソウル" +LACT="ランド・アクト貯水池" +LAGO="ラゴ・ザンクード" +LDAM="ランド・アクト・ダム" +LMESA="ラ・メサ" +LOSPFY="ラ・プエルタ高速道路" +LOSPUER="ラ・プエルタ" +LOSSF="ロスサントス高速道路" +MIRR="ミラー・パーク" +MORN="モーニングウッド" +MOVIE="リチャーズ・マジェスティック" +MTCHIL="チリアド山" +MTGORDO="ゴルド山" +MTJOSE="ジョサイア山" +MURRI="ムリエタ・ハイツ" +NCHU="北チュマシュ" +OBSERV="ガリレオ観測所" +OCEANA="太平洋" +PALCOV="パレト・コーブ" +PALETO="パレト・ベイ" +PALFOR="パレト・フォレスト" +PALHIGH="パロミノ高地" +PALMPOW="パーマー・テイラー発電所" +PBLUFF="パシフィック・ブラフス" +PBOX="ピルボックス・ヒル" +PROCOB="プロコピオ・ビーチ" +PROL="ノース・ヤンクトン" +RANCHO="ランチョ" +RGLEN="リッチマン・グレン" +RICHM="リッチマン" +ROCKF="ロックフォード・ヒルズ" +RTRAK="レッドウッド・ライト・トラック" +SANAND="サンアンドレアス" +SANCHIA="サン・チアンスキー山脈" +SANDY="サンディ海岸" +SENORA="セノーラ高速道路" +SKID="ミッション・ロウ" +SLAB="スタブシティ" +SLSANT="サウス・ロスサントス" +STAD="メイズバンク・アリーナ" +STRAW="ストロベリー" +TATAMO="タタヴィアム山地" +TERMINA="ターミナル" +TEXTI="テキスタイルシティ" +TONGVAH="トングバ・ヒルズ" +TONGVAV="トングバ・バレー" +UTOPIAG="ユートピア・ガーデンズ" +VCANA="ベスプッチ運河" +VESP="ベスプッチ" +VINE="バインウッド" +WINDF="ロン・オルタネット・ウィンドファーム" +WMIRROR="ウエスト・ミラー・ドライブ" +WVINE="ウエスト・バインウッド" +ZANCUDO="ザンクード川" +ZENORA="セノーラ高速道路" diff --git a/res/global.ko.ini b/res/global.ko.ini new file mode 100644 index 0000000..7296638 --- /dev/null +++ b/res/global.ko.ini @@ -0,0 +1,101 @@ +[Global] +AIRP="로스 산토스 국제공항" +ALAMO="알라모 해" +ALTA="알타" +ARMYB="포트 잔쿠도" +BANNING="배닝" +BAYTRE="베이트리 협곡" +BEACH="베스푸치 해변" +BHAMCA="밴험 협곡" +BRADP="브래독 패스" +BRADT="브래독 터널" +BURTON="버튼" +CALAFB="칼라피아 브릿지" +CANNY="레이튼 협곡" +CCREAK="캐시디 크리크" +CHAMH="쳄벌레인 힐즈" +CHIL="바인우드 힐즈" +CHU="추마쉬" +CMSW="칠리아드 마운틴 주 황무지" +COSI="시골 지역" +CYPRE="사이프레스 플랫" +DAVIS="데이비스" +DELBE="델 페로 해변" +DELPE="델 페로" +DELSOL="라 푸에르타" +DESRT="그랜드 세뇨라 사막" +DOWNT="다운타운" +DTVINE="다운타운 바인우드" +EAST_V="동부 바인우드" +EBURO="엘 부로 하이츠" +ECLIPS="이클립스" +ELGORL="엘 고르도 라이트하우스" +ELSANT="동부 로스 산토스" +ELYSIAN="엘리시안 섬" +GALFISH="갈릴리" +GALLI="갈릴레오 파크" +GOLF="GWC & 골프 클럽" +GRAPES="그레이프시드" +GREATC="그레이트 섀퍼럴" +HARMO="하모니" +HAWICK="호익" +HEART="하트 어택 해변" +HORS="바인우드 레이스트랙" +HUMLAB="휴메인 실험 연구소" +ISHEIST="카요 페리코" +JAIL="볼링브로크 교도소" +KOREAT="리틀 서울" +LACT="랜드 액트 저수지" +LAGO="라고 잔쿠도" +LDAM="랜드 액트 댐" +LMESA="라 메사" +LOSPFY="라 푸에르타 고속도로" +LOSPUER="라 푸에르타" +LOSSF="로스 산토스 고속도로" +MIRR="미러 파크" +MORN="모닝우드" +MOVIE="리차드 마제스틱" +MTCHIL="마운트 칠리아드" +MTGORDO="고르도 산" +MTJOSE="마운트 조시아" +MURRI="무리에타 하이츠" +NCHU="북부 추마쉬" +OBSERV="갈릴레오 관측소" +OCEANA="퍼시픽 오션" +PALCOV="팔레토 코브" +PALETO="팔레토 항구" +PALFOR="팔레토 숲" +PALHIGH="팔로미노 고원" +PALMPOW="파머 테일러 발전소" +PBLUFF="퍼시픽 블러프" +PBOX="필박스 힐" +PROCOB="프로코피오 해변" +PROL="북부 양크턴" +RANCHO="란초" +RGLEN="리치맨 글렌" +RICHM="리치맨" +ROCKF="락포드 힐즈" +RTRAK="레드우드 라이트 트랙" +SANAND="산 안드레아스" +SANCHIA="샌 치안스키 산맥" +SANDY="샌디 해안" +SENORA="세뇨라 고속도로" +SKID="미션 로우" +SLAB="스탭 시티" +SLSANT="남부 로스 산토스" +STAD="메이즈 은행 경기장" +STRAW="스트로베리" +TATAMO="타타비암 산" +TERMINA="터미널" +TEXTI="텍스타일 시티" +TONGVAH="통바 힐즈" +TONGVAV="통바 계곡" +UTOPIAG="유토피아 가든" +VCANA="베스푸치 운하" +VESP="베스푸치" +VINE="바인우드" +WINDF="론 얼터네이트 윈드 팜" +WMIRROR="서 미러 드라이브" +WVINE="서부 바인우드" +ZANCUDO="잔쿠도 강" +ZENORA="세뇨라 고속도로" diff --git a/res/global.pl.ini b/res/global.pl.ini new file mode 100644 index 0000000..e318284 --- /dev/null +++ b/res/global.pl.ini @@ -0,0 +1,96 @@ +[Global] +AIRP="Lotnisko międzynarodowe Los Santos" +ALAMO="Alamo Sea" +ALTA="Alta" +ARMYB="Fort Zancudo" +BANNING="Banning" +BAYTRE="Kanion Baytree" +BEACH="Vespucci Beach" +BHAMCA="Kanion Banham" +BRADP="Przełęcz Braddock" +BRADT="Tunel Braddock" +BURTON="Burton" +CALAFB="Most Calafia" +CANNY="Kanion Raton" +CCREAK="Cassidy Creek" +CHAMH="Chamberlain Hills" +CHIL="Vinewood Hills" +CHU="Chumash" +CMSW="Park krajobrazowy Góry Chiliad" +COSI="Tereny pozamiejskie" +CYPRE="Cypress Flats" +DAVIS="Davis" +DELBE="Plaża Del Perro" +DELPE="Del Perro" +DELSOL="La Puerta" +DESRT="Pustynia Grand Senora" +DOWNT="Centrum" +DTVINE="Centrum Vinewood" +EAST_V="Wschodnie Vinewood" +EBURO="El Burro Heights" +ECLIPS="Eclipse" +ELGORL="Latarnia El Gordo" +ELSANT="Wschodnie Los Santos" +ELYSIAN="Wyspa Elizejska" +GALFISH="Galilee" +GALLI="Park Galileusza" +GOLF="Stowarzyszenie GWC & Golfing" +GRAPES="Grapeseed" +GREATC="Great Chaparral" +HARMO="Harmony" +HAWICK="Hawick" +HEART="Plaża Zawałowców" +HORS="Tor wyścigowy Vinewood" +HUMLAB="Humane Labs and Research" +ISHEIST="Cayo Perico" +JAIL="Zakład karny Bolingbroke" +KOREAT="Mały Seul" +LACT="Jezioro zaporowe" +LAGO="Lago Zancudo" +LMESA="La Mesa" +LOSPFY="La Puerta Freeway" +LOSPUER="La Puerta" +LOSSF="Los Santos Freeway" +MIRR="Park Mirror" +MORN="Morningwood" +MOVIE="Richards Majestic" +MTCHIL="Góra Chiliad" +MTGORDO="Góra Gordo" +MURRI="Murrieta Heights" +NCHU="Północne Chumash" +OBSERV="Obserwatorium Galileusza" +PALCOV="Zatoka Paleto" +PALETO="Paleto Bay" +PALFOR="Las Paleto" +PALHIGH="Wyżyna Palomino" +PALMPOW="Elektrownia Palmer-Taylor" +PBLUFF="Pacific Bluffs" +PBOX="Pillbox Hill" +PROCOB="Plaża Procopio" +PROL="North Yankton" +RANCHO="Ranczo" +RGLEN="Richman Glen" +RICHM="Richman" +ROCKF="Rockford Hills" +RTRAK="Tor Redwood Lights" +SANAND="San Andreas" +SANCHIA="Masyw górski San Chianski" +SANDY="Sandy Shores" +SKID="Mission Row" +SLAB="Stab City" +SLSANT="Południowe Los Santos" +STAD="Stadion Maze Bank" +STRAW="Strawberry" +TATAMO="Góry Tataviam" +TERMINA="Terminal" +TEXTI="Textile City" +TONGVAH="Tongva Hills" +TONGVAV="Dolina Tongva" +UTOPIAG="Utopia Gardens" +VCANA="Vespucci Canals" +VESP="Vespucci" +VINE="Vinewood" +WINDF="Farma wiatrowa Ron Alternates" +WVINE="Zachodnie Vinewood" +ZANCUDO="Rzeka Zancudo" +ZENORA="Senora Freeway" diff --git a/res/global.pt.ini b/res/global.pt.ini new file mode 100644 index 0000000..79d46ab --- /dev/null +++ b/res/global.pt.ini @@ -0,0 +1,100 @@ +[Global] +AIRP="Aeroporto Internacional de Los Santos" +ALAMO="Mar Alamo" +ALTA="Alta" +ARMYB="Fort Zancudo" +BANNING="Banning" +BAYTRE="Baytree Canyon" +BEACH="Vespucci Beach" +BHAMCA="Banham Canyon" +BRADP="Braddock Pass" +BRADT="Braddock Tunnel" +BURTON="Burton" +CALAFB="Calafia Bridge" +CANNY="Raton Canyon" +CCREAK="Cassidy Creek" +CHAMH="Chamberlain Hills" +CHIL="Vinewood Hills" +CHU="Chumash" +CMSW="Reserva Natural Monte Chiliad" +COSI="Zona Rural" +CYPRE="Cypress Flats" +DAVIS="Davis" +DELBE="Del Perro Beach" +DELPE="Del Perro" +DELSOL="La Puerta" +DESRT="Deserto Grand Senora" +DOWNT="Centro" +EAST_V="East Vinewood" +EBURO="El Burro Heights" +ECLIPS="Eclipse" +ELGORL="Farol El Gordo" +ELSANT="East Los Santos" +ELYSIAN="Elysian Island" +GALFISH="Galilee" +GALLI="Galileo Park" +GOLF="Clube de campo e golfe GW" +GRAPES="Grapeseed" +GREATC="Great Chaparral" +HARMO="Harmony" +HAWICK="Hawick" +HEART="Heart Attacks Beach" +HORS="Hipódromo de Vinewood" +HUMLAB="Laboratórios e Pesquisas Humane" +ISHEIST="Cayo Perico" +JAIL="Penitenciária Bolingbroke" +KOREAT="Little Seoul" +LACT="Reservatório Land Act" +LAGO="Lago Zancudo" +LDAM="Represa Land Act" +LMESA="La Mesa" +LOSPFY="La Puerta Freeway" +LOSPUER="La Puerta" +LOSSF="Los Santos Freeway" +MIRR="Mirror Park" +MORN="Morningwood" +MOVIE="Richards Majestic" +MTCHIL="Monte Chiliad" +MTGORDO="Monte Gordo" +MTJOSE="Monte Josiah" +MURRI="Murrieta Heights" +NCHU="North Chumash" +OBSERV="Observatório Galileo" +OCEANA="Oceano Pacífico" +PALCOV="Paleto Cove" +PALETO="Paleto Bay" +PALFOR="Paleto Forest" +PALHIGH="Palomino Highlands" +PALMPOW="Usina Elétrica Palmer-Taylor" +PBLUFF="Pacific Bluffs" +PBOX="Pillbox Hill" +PROCOB="Procopio Beach" +PROL="North Yankton" +RANCHO="Rancho" +RGLEN="Richman Glen" +RICHM="Richman" +ROCKF="Rockford Hills" +RTRAK="Pista Redwood Lights" +SANAND="San Andreas" +SANCHIA="Cordilheira San Chianski" +SANDY="Sandy Shores" +SENORA="Senora Freeway" +SKID="Mission Row" +SLAB="Stab City" +SLSANT="South Los Santos" +STAD="Arena Maze Bank" +STRAW="Strawberry" +TATAMO="Montanhas Tataviam" +TERMINA="Terminal" +TEXTI="Textile City" +TONGVAH="Tongva Hills" +TONGVAV="Tongva Valley" +UTOPIAG="Utopia Gardens" +VCANA="Vespucci Canals" +VESP="Vespucci" +VINE="Vinewood" +WINDF="Parque Eólico Ron Alternates" +WMIRROR="West Mirror Drive" +WVINE="West Vinewood" +ZANCUDO="Rio Zancudo" +ZENORA="Senora Freeway" diff --git a/res/global.qrc b/res/global.qrc new file mode 100644 index 0000000..424d0ab --- /dev/null +++ b/res/global.qrc @@ -0,0 +1,17 @@ + + + global.de.ini + global.en.ini + global.es.ini + global.es_MX.ini + global.fr.ini + global.it.ini + global.ja.ini + global.ko.ini + global.pl.ini + global.pt.ini + global.ru.ini + global.zh.ini + global.zh.loc + + diff --git a/res/global.rcc b/res/global.rcc new file mode 100644 index 0000000..60cc688 Binary files /dev/null and b/res/global.rcc differ diff --git a/res/global.ru.ini b/res/global.ru.ini index e53f2c9..996f807 100644 --- a/res/global.ru.ini +++ b/res/global.ru.ini @@ -41,9 +41,8 @@ HARMO="Хармони" HAWICK="Хавик" HEART="Харт-Аттакс-Бич" HORS="Гоночная трасса Вайнвуда" -HUD_MG_TRI_ALA="Аламо-Си" -HUD_MG_TRI_VES="Веспуччи" HUMLAB="Лаборатория Humane Labs and Research" +ISHEIST="Кайо-Перико" JAIL="Тюрьма Болингброук" KOREAT="Маленький Сеул" LACT="Лэнд-экт-резервуар" @@ -53,9 +52,6 @@ LMESA="Ла-Меса" LOSPFY="Шоссе Ла-Пуэрта" LOSPUER="Ла-Пуэрта" LOSSF="Шоссе Лос-Сантоса" -MGCR_1="Южный Лос-Сантос" -MGCR_6="Каналы Веспуччи" -MGSR_3="Каньон Ратон" MIRR="Миррор-Парк" MORN="Морнингвуд" MOVIE="Richards Majestic" @@ -103,4 +99,3 @@ WMIRROR="Вест-Миррор-драйв" WVINE="Западный Вайнвуд" ZANCUDO="Река Занкудо" ZENORA="Шоссе Сенора" -ZP_ORT="Порт Южного Лос-Сантоса" diff --git a/res/global.zh.ini b/res/global.zh.ini index 3990a2f..49650cc 100644 --- a/res/global.zh.ini +++ b/res/global.zh.ini @@ -1,104 +1,100 @@ -[Global] -AIRP="洛聖都國際機場" -ALAMO="阿拉莫海" -ALTA="艾爾塔" -ARMYB="桑庫多堡壘" -BANNING="班寧" -BAYTRE="貝特里峽谷" -BEACH="威斯普奇海灘" -BHAMCA="班漢峽谷" -BRADP="布萊杜要道" -BRADT="布萊杜隧道" -BURTON="巴頓" -CALAFB="卡拉非橋" -CANNY="雷通峽谷" -CCREAK="加斯迪小溪" -CHAMH="張伯倫山" -CHIL="好麥塢山" -CHU="丘瑪墟" -CMSW="奇力耶德山國家生態保護區" -CYPRE="扁柏平地" -DAVIS="戴維斯" -DELBE="佩羅海灘" -DELPE="佩羅" -DELSOL="洛波塔" -DESRT="塞諾拉大沙漠" -DOWNT="市中心" -DTVINE="好麥塢市中心" -EAST_V="東好麥塢" -EBURO="布羅高地" -ECLIPS="日蝕" -ELGORL="戈多燈塔" -ELSANT="東洛聖都" -ELYSIAN="安樂島" -GALFISH="加利利" -GALLI="伽利略公園" -GOLF="西部鄉村高爾夫俱樂部" -GRAPES="葡萄籽" -GREATC="大叢林" -HARMO="和美尼" -HAWICK="霍伊克" -HEART="驚心海灘" -HORS="好麥塢賽馬場" -HUD_MG_TRI_ALA="阿拉莫海" -HUD_MG_TRI_VES="威斯普奇" -HUMLAB="人道研究實驗室" -JAIL="博林布魯克監獄" -KOREAT="小首爾" -LACT="蘭艾水庫" -LAGO="桑庫多沼地" -LDAM="蘭艾水壩" -LMESA="梅薩" -LOSPFY="洛波塔高速公路" -LOSPUER="洛波塔" -LOSSF="洛聖都高速公路" -MGCR_1="南洛聖都" -MGCR_6="威斯普奇運河" -MGSR_3="雷通峽谷" -MIRR="米羅公園" -MORN="摩寧塢" -MOVIE="李察尊爵" -MTCHIL="奇力耶德山" -MTGORDO="戈多山" -MTJOSE="尤夏山" -MURRI="穆瑞塔高地" -NCHU="北丘瑪墟" -OBSERV="伽利略天文台" -OCEANA="太平洋" -PALCOV="佩立托小海灣" -PALETO="佩立托灣" -PALFOR="佩立托森林" -PALHIGH="巴洛米諾高地" -PALMPOW="帕莫泰勒發電站" -PBLUFF="太平崖" -PBOX="圓帽山" -PROCOB="普羅科皮奧海灘" -PROL="北揚克頓" -RANCHO="藍丘" -RGLEN="利金漫幽谷" -RICHM="利金漫" -ROCKF="羅克福德山" -RTRAK="紅木賽道" -SANAND="聖安地列斯" -SANCHIA="聖強斯基山脈" -SANDY="沙灘海岸" -SENORA="塞諾拉高速公路" -SKID="密申羅" -SLAB="背刺城" -SLSANT="南洛聖都" -STAD="花園銀行體育場" -STRAW="斯卓貝利" -TATAMO="塔塔維昂山" -TERMINA="碼頭" -TEXTI="紡織城" -TONGVAH="通瓦山" -TONGVAV="通瓦谷地" -UTOPIAG="烏托邦花園" -VCANA="威斯普奇運河" -VESP="威斯普奇" -VINE="好麥塢" -WINDF="朗恩.艾特梅茲風車農場" -WMIRROR="米羅車道西段" -WVINE="西好麥塢" -ZANCUDO="桑庫多河" -ZENORA="塞諾拉高速公路" +[Global] +AIRP="洛聖都國際機場" +ALAMO="阿拉莫海" +ALTA="艾爾塔" +ARMYB="桑庫多堡壘" +BANNING="班寧" +BAYTRE="貝特里峽谷" +BEACH="威斯普奇海灘" +BHAMCA="班漢峽谷" +BRADP="布萊杜要道" +BRADT="布萊杜隧道" +BURTON="巴頓" +CALAFB="卡拉非橋" +CANNY="雷通峽谷" +CCREAK="加斯迪小溪" +CHAMH="張伯倫山" +CHIL="好麥塢山" +CHU="丘瑪墟" +CMSW="奇力耶德山國家生態保護區" +CYPRE="扁柏平地" +DAVIS="戴維斯" +DELBE="佩羅海灘" +DELPE="佩羅" +DELSOL="洛波塔" +DESRT="塞諾拉大沙漠" +DOWNT="市中心" +DTVINE="好麥塢市中心" +EAST_V="東好麥塢" +EBURO="布羅高地" +ECLIPS="日蝕" +ELGORL="戈多燈塔" +ELSANT="東洛聖都" +ELYSIAN="安樂島" +GALFISH="加利利" +GALLI="伽利略公園" +GOLF="西部鄉村高爾夫俱樂部" +GRAPES="葡萄籽" +GREATC="大叢林" +HARMO="和美尼" +HAWICK="霍伊克" +HEART="驚心海灘" +HORS="好麥塢賽馬場" +HUMLAB="人道研究實驗室" +ISHEIST="佩里克島" +JAIL="博林布魯克監獄" +KOREAT="小首爾" +LACT="蘭艾水庫" +LAGO="桑庫多沼地" +LDAM="蘭艾水壩" +LMESA="梅薩" +LOSPFY="洛波塔高速公路" +LOSPUER="洛波塔" +LOSSF="洛聖都高速公路" +MIRR="米羅公園" +MORN="摩寧塢" +MOVIE="李察尊爵" +MTCHIL="奇力耶德山" +MTGORDO="戈多山" +MTJOSE="尤夏山" +MURRI="穆瑞塔高地" +NCHU="北丘瑪墟" +OBSERV="伽利略天文台" +OCEANA="太平洋" +PALCOV="佩立托小海灣" +PALETO="佩立托灣" +PALFOR="佩立托森林" +PALHIGH="巴洛米諾高地" +PALMPOW="帕莫泰勒發電站" +PBLUFF="太平崖" +PBOX="圓帽山" +PROCOB="普羅科皮奧海灘" +PROL="北揚克頓" +RANCHO="藍丘" +RGLEN="利金漫幽谷" +RICHM="利金漫" +ROCKF="羅克福德山" +RTRAK="紅木賽道" +SANAND="聖安地列斯" +SANCHIA="聖強斯基山脈" +SANDY="沙灘海岸" +SENORA="塞諾拉高速公路" +SKID="密申羅" +SLAB="背刺城" +SLSANT="南洛聖都" +STAD="花園銀行體育場" +STRAW="斯卓貝利" +TATAMO="塔塔維昂山" +TERMINA="碼頭" +TEXTI="紡織城" +TONGVAH="通瓦山" +TONGVAV="通瓦谷地" +UTOPIAG="烏托邦花園" +VCANA="威斯普奇運河" +VESP="威斯普奇" +VINE="好麥塢" +WINDF="朗恩.艾特梅茲風車農場" +WMIRROR="米羅車道西段" +WVINE="西好麥塢" +ZANCUDO="桑庫多河" +ZENORA="塞諾拉高速公路" diff --git a/res/global.zh.loc b/res/global.zh.loc new file mode 100644 index 0000000..dad8857 --- /dev/null +++ b/res/global.zh.loc @@ -0,0 +1 @@ +zh_TW diff --git a/res/gta5sync.ts b/res/gta5sync.ts new file mode 100644 index 0000000..88ab419 --- /dev/null +++ b/res/gta5sync.ts @@ -0,0 +1,2505 @@ + + + + + AboutDialog + + + About %1 + + + + + <span style="font-weight:600">%1</span><br/> +<br/> +%2<br/> +<br/> +Version %3<br/> +Created on %4<br/> +Built with Qt %5<br/> +Running with Qt %6<br/> +<br/> +%7 + + + + + &Close + + + + + Translated by %1 + Translated by translator, example Translated by Syping + + + + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + + + + + A project for viewing Grand Theft Auto V Snapmatic<br/> +Pictures and Savegames + + + + + Copyright &copy; <a href="%1">%2</a> %3 + + + + + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + + + + + Release + + + + + Release Candidate + + + + + Daily Build + + + + + Developer + + + + + Beta + + + + + Alpha + + + + + + Custom + + + + + CrewDatabase + + + + No Crew + + + + + ExportDialog + + + Dialog + + + + + Export Format + + + + + &JPEG/PNG format + + + + + GTA &Snapmatic format + + + + + Export Size + + + + + Default &Size + + + + + &Desktop Size + + + + + &Custom Size + + + + + Custom Size: + + + + + x + + + + + &Export + + + + + &Close + + + + + ImageEditorDialog + + + Overwrite Image... + + + + + Apply changes + + + + + &Overwrite + + + + + Discard changes + + + + + &Close + + + + + + + + Snapmatic Image Editor + + + + + + Patching of Snapmatic Image failed because of I/O Error + + + + + + Patching of Snapmatic Image failed because of Image Error + + + + + ImportDialog + + + Import... + + + + + Picture + + + + + Avatar + + + + + + Ignore Aspect Ratio + + + + + Watermark + + + + + Crop to Aspect Ratio + + + + + Background + + + + + + + + Background Colour: <span style="color: %1">%1</span> + + + + + Select background colour + + + + + + + + Background Image: + + + + + Select background image + + + + + Remove background image + + + + + Force Colour in Avatar Zone + + + + + Advanced + + + + + Resolution: + + + + + Snapmatic resolution + + + + + Avoid compression and expand buffer instead, improves picture quality, but may break Snapmatic + + + + + Unlimited Buffer + + + + + Import as-is, don't change the picture at all, guaranteed to break Snapmatic unless you know what you doing + + + + + Import as-is + + + + + Import options + + + + + &Options + + + + + Import picture + + + + + &OK + + + + + Discard picture + + + + + &Cancel + + + + + &Import new Picture... + + + + + &Crop Picture... + + + + + &Load Settings... + + + + + &Save Settings... + + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + + + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + + + + + Storage + Background Image: Storage + + + + + Crop Picture... + + + + + &Crop + + + + + Crop Picture + + + + + + Please import a new picture first + + + + + + Default + Default as Default Profile + + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + + + + + + Load Settings... + + + + + + Save Settings... + + + + + + Snapmatic Avatar Zone + + + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + + + + + Select Colour... + + + + + + Background Image: %1 + + + + + + Please select your settings profile + + + + + File + Background Image: File + + + + + JsonEditorDialog + + + Snapmatic JSON Editor + + + + + Apply changes + + + + + &Save + + + + + Discard changes + + + + + &Close + + + + + JSON Error + + + + + MapLocationDialog + + + Snapmatic Map Viewer + + + + + Close viewer + + + + + &Close + + + + + Apply new position + + + + + &Apply + + + + + Revert old position + + + + + &Revert + + + + + Select new position + + + + + &Select + + + + + Quit select position + + + + + &Done + + + + + X: %1 +Y: %2 + X and Y position + + + + + OptionsDialog + + + %1 - Settings + + + + + Profiles + + + + + Content Open/Select Mode + + + + + Open with Doubleclick + + + + + Default Profile + + + + + Custom GTA V Folder + + + + + Force using Custom Folder + + + + + ... + + + + + Pictures + + + + + Export Size + + + + + Default: %1x%2 + + + + + Screen Resolution: %1x%2 + + + + + + Custom Size: + + + + + x + + + + + Ignore Aspect Ratio + + + + + Export Quality + + + + + Enable Custom Quality + + + + + Quality: + + + + + %1% + + + + + Picture Viewer + + + + + Enable Navigation Bar + + + + + Players + + + + + ID + + + + + Name + + + + + Game + + + + + Social Club Version + + + + + + + + + + + + Found: %1 + + + + + + + + + + + + + + Language: %1 + + + + + Steam Version + + + + + Feedback + + + + + Participation + + + + + + Participate in %1 User Statistics + + + + + Categories + + + + + Hardware, Application and OS Specification + + + + + System Language Configuration + + + + + Application Configuration + + + + + Personal Usage Data + + + + + Other + + + + + + + Participation ID: %1 + + + + + &Copy + + + + + Interface + + + + + Language for Interface + + + + + + + + Current: %1 + + + + + Language for Areas + + + + + Style + + + + + Use Default Style (Restart) + + + + + Style: + + + + + Font + + + + + Use Default Font (Restart) + + + + + Font: + + + + + Apply changes + + + + + &OK + OK, Cancel, Apply + + + + + Discard changes + + + + + &Cancel + OK, Cancel, Apply + + + + + System + System in context of System default + + + + + %1 (Game language) + Next closest language compared to the Game settings + + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + + + + + + + Auto + Automatic language choice. + + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + + + + + %1 + %1 + + + + + The new Custom Folder will initialise after you restart %1. + + + + + No Profile + No Profile, as default + + + + + + + Profile: %1 + + + + + View %1 User Statistics Online + + + + + Not registered + + + + + + + + Yes + + + + + + No + + + + + + OS defined + + + + + + Steam defined + + + + + PictureDialog + + + Snapmatic Picture Viewer - %1 + + + + + <span style="font-weight:600">Title: </span>%6<br/> +<span style="font-weight:600">Location: </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">Players: </span>%4 (Crew %5)<br/> +<span style="font-weight:600">Created: </span>%8 + + + + + Manage picture + + + + + &Manage + + + + + Close viewer + + + + + &Close + + + + + + Export as &Picture... + + + + + + Export as &Snapmatic... + + + + + + &Edit Properties... + + + + + + &Overwrite Image... + + + + + + Open &Map Viewer... + + + + + + Open &JSON Editor... + + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + + + + + Snapmatic Picture Viewer + + + + + Failed at %1 + + + + + + + No Players + + + + + + No Crew + + + + + Unknown Location + + + + + Avatar Preview Mode +Press 1 for Default View + + + + + Export as Picture... + + + + + + Export + + + + + JPEG Graphics (*.jpg *.jpeg) + + + + + Portable Network Graphics (*.png) + + + + + + + + + + Export as Picture + + + + + + Overwrite %1 with current Snapmatic picture? + + + + + Failed to export the picture because the system occurred a write failure + + + + + Failed to export the picture because the format detection failures + + + + + Failed to export the picture because the file can't be written + + + + + Failed to export the picture because of an unknown reason + + + + + + No valid file is selected + + + + + Export as Snapmatic... + + + + + GTA V Export (*.g5e) + + + + + GTA V Raw Export (*.auto) + + + + + Snapmatic pictures (PGTA*) + + + + + + + + + Export as Snapmatic + + + + + + Failed to export current Snapmatic picture + + + + + Exported Snapmatic to "%1" because of using the .auto extension. + + + + + PlayerListDialog + + + Edit Players... + + + + + Available Players: + + + + + Selected Players: + + + + + &Apply + + + + + &Cancel + + + + + Add Players... + + + + + Failed to add more Players because the limit of Players are %1! + + + + + + Add Player... + + + + + Enter Social Club Player ID + + + + + Failed to add Player %1 because Player %1 is already added! + + + + + ProfileInterface + + + Profile Interface + + + + + Loading file %1 of %2 files + + + + + %1 %2 + + + + + Import file + + + + + &Import... + + + + + Close profile + + + + + &Close + + + + + + + Export file %1 of %2 files + + + + + + + + + + + + + + + + + + + + + + + Import... + + + + + + + + + + Import + + + + + + + All image files (%1) + + + + + + + + All files (**) + + + + + + + Can't import %1 because file can't be open + + + + + + + Can't import %1 because file can't be parsed properly + + + + + Enabled pictures: %1 of %2 + + + + + Loading... + + + + + Snapmatic Loader + + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + + + + + Importable files (%1) + + + + + + GTA V Export (*.g5e) + + + + + + Savegames files (SGTA*) + + + + + + Snapmatic pictures (PGTA*) + + + + + + + No valid file is selected + + + + + + Import file %1 of %2 files + + + + + Import failed with... + +%1 + + + + + + Failed to read Snapmatic picture + + + + + + Failed to read Savegame file + + + + + Can't import %1 because file format can't be detected + + + + + Prepare Content for Import... + + + + + Failed to import the Snapmatic picture, file not begin with PGTA or end with .g5e + + + + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + + + + + Failed to import the Snapmatic picture, can't copy the file into profile + + + + + Failed to import the Savegame, can't copy the file into profile + + + + + Failed to import the Savegame, no Savegame slot is left + + + + + + + + + Export selected... + + + + + + JPG pictures and GTA Snapmatic + + + + + + JPG pictures only + + + + + + GTA Snapmatic only + + + + + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: + + + + + Initialising export... + + + + + Export failed with... + +%1 + + + + + + No Snapmatic pictures or Savegames files are selected + + + + + + + Remove selected + + + + + You really want remove the selected Snapmatic picutres and Savegame files? + + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + + + + + + + + + + No Snapmatic pictures are selected + + + + + + + + + + %1 failed with... + +%2 + Action failed with... + + + + + + Qualify as Avatar + + + + + + + + Patch selected... + + + + + + + + + + + + Patch file %1 of %2 files + + + + + Qualify + %1 failed with... + + + + + + Change Players... + + + + + Change Players + %1 failed with... + + + + + + + Change Crew... + + + + + Failed to enter a valid Snapmatic Crew ID + + + + + Change Crew + %1 failed with... + + + + + + + Change Title... + + + + + Failed to enter a valid Snapmatic title + + + + + Change Title + %1 failed with... + + + + + All profile files (*.g5e SGTA* PGTA*) + + + + + SavegameDialog + + + + Savegame Viewer + + + + + <span style="font-weight:600">Savegame</span><br><br>%1 + + + + + &Export + + + + + &Close + + + + + Failed at %1 + + + + + SavegameWidget + + + Savegame Widget + + + + + SAVE %3 - %1<br>%2 + + + + + View savegame + + + + + View + + + + + Copy savegame + + + + + + Export + + + + + Delete savegame + + + + + Delete + + + + + &View + + + + + + + &Export + + + + + + + &Remove + + + + + + &Select + + + + + + &Deselect + + + + + + Select &All + + + + + + &Deselect All + + + + + Savegame files (SGTA*) + + + + + All files (**) + + + + + + + + Export Savegame + + + + + Overwrite %1 with current Savegame? + + + + + Failed to overwrite %1 with current Savegame + + + + + Failed to export current Savegame + + + + + No valid file is selected + + + + + Export Savegame... + + + + + + AUTOSAVE - %1 +%2 + + + + + + SAVE %3 - %1 +%2 + + + + + + WRONG FORMAT + + + + + UNKNOWN + + + + + + Delete Savegame + + + + + Are you sure to delete %1 from your savegames? + + + + + Failed at deleting %1 from your savegames + + + + + SnapmaticEditor + + + + + + + + + + + Snapmatic Properties + + + + + Snapmatic Type + + + + + Editor + + + + + Selfie + + + + + Regular + + + + + Mugshot + + + + + Meme + + + + + Director + + + + + Snapmatic Values + + + + + Extras + + + + + Qualify as Avatar automatically at apply + + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + + + + + Apply changes + + + + + &Apply + + + + + Discard changes + + + + + &Cancel + + + + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + + + + + Patching of Snapmatic Properties failed because of %1 + + + + + + + + Patching of Snapmatic Properties failed because of I/O Error + + + + + Patching of Snapmatic Properties failed because of JSON Error + + + + + + Snapmatic Crew + + + + + + New Snapmatic crew: + + + + + + Snapmatic Title + + + + + + New Snapmatic title: + + + + + + + Edit + + + + + Players: %1 (%2) + Multiple Player are inserted here + + + + + Player: %1 (%2) + One Player is inserted here + + + + + Title: %1 (%2) + + + + + + Appropriate: %1 + + + + + Yes + Yes, should work fine + + + + + No + No, could lead to issues + + + + + Crew: %1 (%2) + + + + + SnapmaticPicture + + + + JSON is incomplete and malformed + + + + + + JSON is incomplete + + + + + + JSON is malformed + + + + + PHOTO - %1 + + + + + open file %1 + + + + + header not exists + + + + + header is malformed + + + + + picture not exists (%1) + + + + + JSON not exists (%1) + + + + + title not exists (%1) + + + + + description not exists (%1) + + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + + + + + SnapmaticWidget + + + Snapmatic Widget + + + + + PHOTO - 00/00/00 00:00:00 + + + + + View picture + + + + + View + + + + + Copy picture + + + + + Copy + + + + + Export picture + + + + + Export + + + + + + + Delete picture + + + + + Delete + + + + + + + Edi&t + + + + + + + Show &In-game + + + + + + + Hide &In-game + + + + + &Export + + + + + &View + + + + + &Remove + + + + + + &Select + + + + + + &Deselect + + + + + + Select &All + + + + + + &Deselect All + + + + + Are you sure to delete %1 from your Snapmatic pictures? + + + + + Failed at deleting %1 from your Snapmatic pictures + + + + + Failed to hide %1 In-game from your Snapmatic pictures + + + + + Failed to show %1 In-game from your Snapmatic pictures + + + + + TelemetryDialog + + + You want help %1 to improve in the future by including personal usage data in your submission? + + + + + %1 User Statistics + + + + + Yes, I want include personal usage data. + + + + + &OK + + + + + UserInterface + + + + %2 - %1 + + + + + Select profile + + + + + %1 %2 + + + + + Reload profile overview + + + + + &Reload + + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + + + + + + + + &Close + + + + + &File + + + + + &Help + + + + + &Edit + + + + + &Profile + + + + + &Selection visibility + + + + + Selection &mass tools + + + + + + + &About %1 + + + + + &Exit + + + + + Exit + + + + + Close &Profile + + + + + &Settings + + + + + Select &All + + + + + &Deselect All + + + + + &Export selected... + + + + + &Remove selected + + + + + &Import files... + + + + + &Open File... + + + + + + Select &GTA V Folder... + + + + + + + + Select GTA V Folder... + + + + + Show In-gam&e + + + + + Hi&de In-game + + + + + + + Change &Title... + + + + + + + Change &Crew... + + + + + + + &Qualify as Avatar + + + + + + + Change &Players... + + + + + + + Show In-game + + + + + + + Hide In-game + + + + + + + Select Profile + + + + + + &Donate + + + + + Donate + + + + + Donation methods + + + + + &Copy + + + + + Open File... + + + + + + + + Open File + + + + + Can't open %1 because of not valid file format + + + + + %1 - Messages + + + + diff --git a/res/gta5sync_de.qm b/res/gta5sync_de.qm old mode 100755 new mode 100644 index 0890c94..f25770f Binary files a/res/gta5sync_de.qm and b/res/gta5sync_de.qm differ diff --git a/res/gta5sync_de.ts b/res/gta5sync_de.ts old mode 100755 new mode 100644 index ec70f48..054fac6 --- a/res/gta5sync_de.ts +++ b/res/gta5sync_de.ts @@ -3,56 +3,6 @@ AboutDialog - - About gta5sync - Über gta5sync - - - <span style=" font-weight:600;">gta5sync</span><br/> -<br/> -A project for viewing and sync Grand Theft Auto V Snapmatic<br/> -Pictures and Savegames<br/> -<br/> -Project version: %1<br/> -Project build: %4, %5<br/> -Compiled with Qt %2<br/> -Running with Qt %3<br/> -<br/> -Copyright &copy; <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5sync is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - <span style=" font-weight:600;">gta5sync</span><br/> -<br/> -Ein Projekt zum ansehen und synchronisieren von<br/> -Grand Theft Auto V Snapmatic Bilder und Spielständen<br/> -<br/> -Projektversion: %1<br/> -Projektbau: %4, %5<br/> -Gebaut mit Qt %2<br/> -Läuft auf Qt %3<br/> -<br/> -Copyright &copy; <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5sync ist lizenziert unter <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - - - <span style=" font-weight:600;">gta5sync</span><br/> -<br/> -A project for viewing and sync Grand Theft Auto 5 Snapmatic Pictures and Savegames<br/> -<br/> -Project version: %1<br/> -Project build: %4, %5<br/> -Compiled with Qt %2<br/> -Running with Qt %3<br/> -<br/> -Copyright &copy; <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5sync is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - <span style=" font-weight:600;">gta5sync</span><br/> -<br/> -Ein Projekt zum ansehen und synchronisieren von Grand Theft Auto 5 Snapmatic Bilder und Spielständen<br/> -<br/> -Projektversion: %1<br/> -Projektbau: %4, %5<br/> -Gebaut mit Qt %2<br/> -Läuft auf Qt %3<br/> -<br/> -Copyright &copy; <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5sync is lizenziert unter <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - About %1 @@ -60,7 +10,7 @@ Copyright &copy; <a href="https://github.com/Syping/">Syping - <span style=" font-weight:600;">%1</span><br/> + <span style="font-weight:600">%1</span><br/> <br/> %2<br/> <br/> @@ -70,7 +20,7 @@ Built with Qt %5<br/> Running with Qt %6<br/> <br/> %7 - <span style=" font-weight:600;">%1</span><br/> + <span style="font-weight:600">%1</span><br/> <br/> %2<br/> <br/> @@ -80,50 +30,6 @@ Gebaut mit Qt %5<br/> Läuft auf Qt %6<br/> <br/> %7 - - - <span style=" font-weight:600;">%1</span><br/> -<br/> -%2<br/> -<br/> -Version %3<br/> -Created on %4<br/> -Built with Qt %5<br/> -Running with Qt %6<br/> -%8<br/> -%7 - <span style=" font-weight:600;">%1</span><br/> -<br/> -%2<br/> -<br/> -Version %3<br/> -Erstellt am %4<br/> -Gebaut mit Qt %5<br/> -Läuft auf Qt %6<br/> -%8<br/> -%7 - - - <span style=" font-weight:600;">%8</span><br/> -<br/> -%9<br/> -<br/> -Project version: %1<br/> -Project build: %4<br/> -Compiled with Qt %2<br/> -Running with Qt %3<br/> -<br/> -Copyright &copy; <a href="%5">%6</a> %7<br/>%8 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - <span style=" font-weight:600;">%8</span><br/> -<br/> -%9<br/> -<br/> -Projektversion: %1<br/> -Projektbau: %4<br/> -Gebaut mit Qt %2<br/> -Läuft auf Qt %3<br/> -<br/> -Copyright &copy; <a href="%5">%6</a> %7<br/>%8 ist lizenziert unter <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> @@ -131,106 +37,86 @@ Copyright &copy; <a href="%5">%6</a> %7<br/>%8 i S&chließen - generated by class - Generiert von der Klasse - - - <span style=" font-weight:600;">gta5sync</span><br/><br/>A project for viewing and sync Grand Theft Auto 5 Snapmatic Pictures and Savegames<br/><br/>Project version: %1<br/>Compiled with Qt %2<br/>Running with Qt %3<br/><br/>Copyright © <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5sync is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - <span style=" font-weight:600;">gta5sync</span><br/><br/>Ein Projekt zum ansehen und synchronisieren von Grand Theft Auto 5 Snapmatic Bilder und Spielständen<br/><br/>Projektversion: %1<br/>Gebaut mit Qt %2<br/>Läuft auf Qt %3<br/><br/>Copyright © <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5sync is lizenziert unter <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - - - Close - Schließen - - - <span style=" font-weight:600;">gta5sync</span><br/><br/>A project for viewing and sync Grand Theft Auto 5 Snapmatic Pictures and Savegames<br/><br/>Project version: %1<br/>Compiled with Qt %2<br/>Running with Qt %3<br/><br/>Copyright &copy; <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5sync is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - <span style=" font-weight:600;">gta5sync</span><br/><br/>Ein Projekt zum ansehen und synchronisieren von Grand Theft Auto 5 Snapmatic Bilder und Spielständen<br/><br/>Projektversion: %1<br/>Gebaut mit Qt %2<br/>Läuft auf Qt %3<br/><br/>Copyright &copy; <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5sync is lizenziert unter <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - - - Using %1 %2 - Verwendet %1 %2 - - + Translated by %1 - Übersetzt von %1 - - - - Using %1 %2 - Exp. Using libmyfuck - Verwendet %1 %2 - - - - Translated by %1 - Exp. Translated by Syping + Translated by translator, example Translated by Syping Übersetzt von %1 - - NAME_OF_TRANSLATOR - Your Name (The person behind your screen looking at this text!) - Syping + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + Syping,g5e://about?U3lwaW5n:R2l0TGFiOiA8YSBocmVmPSJodHRwczovL2dpdGxhYi5jb20vU3lwaW5nIj5TeXBpbmc8L2E+PGJyLz5HaXRIdWI6IDxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9TeXBpbmciPlN5cGluZzwvYT48YnIvPlNvY2lhbCBDbHViOiA8YSBocmVmPSJodHRwczovL3NvY2lhbGNsdWIucm9ja3N0YXJnYW1lcy5jb20vbWVtYmVyL1N5cGluZy80NjMwMzA1NiI+U3lwaW5nPC9hPg - - TRANSLATOR_PROFILE - mailto: http:// https:// Exp. https://github.com/Syping/ - https://github.com/Syping/ - - - + A project for viewing Grand Theft Auto V Snapmatic<br/> Pictures and Savegames Ein Projekt zum ansehen von Grand Theft Auto V<br/> Snapmatic Bilder und Spielständen - + Copyright &copy; <a href="%1">%2</a> %3 Copyright &copy; <a href="%1">%2</a> %3 - + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> %1 ist lizenziert unter <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - Copyright &copy; <a href="%1">%2</a> %3<br/>%4 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - Copyright &copy; <a href="%1">%2</a> %3<br/>%4 ist lizenziert unter <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + + Release + Release - - A project for viewing and sync Grand Theft Auto V Snapmatic<br/> -Pictures and Savegames - Ein Projekt zum ansehen und synchronisieren von<br/> -Grand Theft Auto V Snapmatic Bilder und Spielständen + + Release Candidate + Release Candidate + + + + Daily Build + Daily Build + + + + Developer + Entwickler + + + + Beta + Beta + + + + Alpha + Alpha + + + + + Custom + Eigener CrewDatabase - + + No Crew Keine Crew - - Free - Crew Free (means no crew) - Crew Keine (bedeutet keine Crew) - Keine - ExportDialog Dialog - - - - Format - Format + Dialog @@ -242,10 +128,6 @@ Grand Theft Auto V Snapmatic Bilder und Spielständen GTA &Snapmatic format GTA &Snapmatic Format - - Resolution - Auflösung - Export Format @@ -292,81 +174,467 @@ Grand Theft Auto V Snapmatic Bilder und Spielständen S&chließen + + ImageEditorDialog + + + + + + Snapmatic Image Editor + Snapmatic Bild Editor + + + + Overwrite Image... + Bild überschreiben... + + + + Apply changes + Änderungen übernehmen + + + + &Overwrite + &Überschreiben + + + + Discard changes + Änderungen verwerfen + + + + &Close + S&chließen + + + + + Patching of Snapmatic Image failed because of I/O Error + Patchen von Snapmatic Bild fehlgeschlagen wegen I/O Fehler + + + + + Patching of Snapmatic Image failed because of Image Error + Patchen von Snapmatic Bild fehlgeschlagen wegen Bild Fehler + + ImportDialog - + Import... Importieren... - - Settings - Einstellungen + + + Ignore Aspect Ratio + Seitenverhältnis ignorieren - - &Keep Aspect Ratio - Seitenverhältnis &behalten + + Avatar + Avatar - - &Ignore Aspect Ratio - Seitenverhältnis &ignorieren + + Picture + Bild - - &Avatar - &Avatar + + Watermark + Wasserzeichen - + Force Borderless + Erzwinge keine Ränder + + + + Background + Hintergrund + + + + + + + Background Colour: <span style="color: %1">%1</span> + Hintergrundfarbe: <span style="color: %1">%1</span> + + + + Select background colour + Hintergrundfarbe auswählen + + + ... + ... + + + + Select background image + Hintergrundbild auswählen + + + + Remove background image + Hintergrundbild entfernen + + + + Import as-is, don't change the picture at all, guaranteed to break Snapmatic unless you know what you doing + Importiere das Bild ohne Veränderungen, Snapmatic wird garantiert beschädigt wenn du nicht weißt was du tust + + + + + Background Image: %1 + Hintergrundbild: %1 + + + X + X + + + + Crop to Aspect Ratio + Seitenverhältnis zuschneiden + + + + Force Colour in Avatar Zone + Erzwinge Farbe in Avatar Zone + + + + Advanced + Erweitert + + + + Resolution: + Auflösung: + + + + Snapmatic resolution + Snapmatic Auflösung + + + + Avoid compression and expand buffer instead, improves picture quality, but may break Snapmatic + Vermeide Kom­pri­mie­rung und vergrößere Buffer stattdessen, verbessert Bild Qualität, aber könnte Snapmatic beschädigen + + + + Unlimited Buffer + Un­li­mi­tierter Buffer + + + + Import as-is + Importiere unverändert + + + + Import options + Import Optionen + + + + &Options + &Optionen + + + + Import picture + Bild importieren + + + &OK &OK - + + Discard picture + Bild verwerfen + + + &Cancel Abbre&chen + + + + + + Background Image: + Hintergrundbild: + + + + &Import new Picture... + Neues Bild &importieren... + + + + &Crop Picture... + Bild zu&schneiden... + + + + &Load Settings... + Einstellungen &laden... + + + + &Save Settings... + Einstellungen &speichern... + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + Eigener Avatar + + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + Eigenes Bild + + + + Storage + Background Image: Storage + Speicher + + + + Crop Picture... + Bild zuschneiden... + + + + &Crop + Zu&schneiden + + + + Crop Picture + Bild zuschneiden + + + + + Please import a new picture first + Bitte importiere ein neues Bild zuerst + + + + + Default + Default as Default Profile + Standard + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + Profil %1 + + + + + Load Settings... + Einstellungen laden... + + + + + Please select your settings profile + Bitte wähle dein Einstellungsprofil aus + + + + + Save Settings... + Einstellungen speichern... + + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + Bist du sicher ein Quadrat Bild außerhalb der Avatar Zone zu verwenden? +Wenn du es als Avatar verwenden möchtest wird es abgetrennt! + + + + + Snapmatic Avatar Zone + Snapmatic Avatar Zone + + + + Select Colour... + Farbe auswählen... + + + + File + Background Image: File + Datei + + + + JsonEditorDialog + + + Snapmatic JSON Editor + Snapmatic JSON Editor + + + + Apply changes + Änderungen übernehmen + + + + &Save + &Speichern + + + + Discard changes + Änderungen verwerfen + + + + &Close + S&chließen + + + + JSON Error + JSON Fehler + + + + MapLocationDialog + + + Snapmatic Map Viewer + Snapmatic Kartenansicht + + + + Close viewer + Ansicht schließen + + + + &Close + S&chließen + + + + Apply new position + Neue Position festlegen + + + + &Apply + &Übernehmen + + + + Revert old position + Alte Position wiederherstellen + + + + &Revert + &Zurücksetzen + + + + Select new position + Neue Position auswählen + + + + &Select + Au&swählen + + + + Quit select position + Position auswählen verlassen + + + + &Done + &Fertig + + + + X: %1 +Y: %2 + X and Y position + X: %1 +Y: %2 + OptionsDialog - - Options - Optionen - - - gta5sync - Options - gta5sync - Optionen - - - gta5sync - Settings - gta5sync - Einstellungen - - - Profile - Profil - Content Open/Select Mode Inhalte Öffnen/Auswählen Modus - Open with Singleclick - Ein Klick zum öffnen + Ein Klick zum öffnen - + Open with Doubleclick Doppelklick zum öffnen - Select with Singleclick - Ein Klick zum auswählen + Ein Klick zum auswählen @@ -383,23 +651,11 @@ Grand Theft Auto V Snapmatic Bilder und Spielständen Export Size Export Größe - - Default Size: %1x%2 - Standard Größe: %1x%2 - - - Desktop Size: %1x%2 - Desktop Größe: %1x%2 - Screen Resolution: %1x%2 Bildschirmauflösung: %1x%2 - - Custom Size - Eigene Größe - %1 - Settings @@ -493,80 +749,272 @@ Grand Theft Auto V Snapmatic Bilder und Spielständen + Game + Spiel + + - Language - Sprache + Social Club Version + Social Club Version - - Sync - Sync + + + + + + + + + Found: %1 + Gefunden: %1 - - Sync is not implemented at current time - Sync wurde bisher nicht implementiert + + + + + + + + + + + Language: %1 + Sprache: %1 - + + Steam Version + Steam Version + + + + Feedback + Feedback + + + + Participation + Teilnahme + + + + + Participate in %1 User Statistics + An %1 Benutzerstatistik teilnehmen + + + + Categories + Kategorien + + + + Hardware, Application and OS Specification + Hardware, Anwendung und OS Spezifikation + + + + System Language Configuration + Spracheinstellungen des System + + + + Application Configuration + Anwendungseinstellungen + + + + Other + Sonstiges + + + + + + Participation ID: %1 + Teilnahme ID: %1 + + + + &Copy + &Kopieren + + + + Language for Areas + Sprache für Standorte + + + + Style + Stil + + + + Use Default Style (Restart) + Benutze Standard Stil (Neustart) + + + + Style: + Stil: + + + + Font + Schriftart + + + Always use Message Font (Windows 2003 and earlier) + Immer Nachrichtenschrift nutzen (Windows 2003 und früher) + + + + Interface + Oberfläche + + + + Personal Usage Data + Persönliche Nutzungsdaten + + + + Language for Interface + Sprache für Oberfläche + + + + + + + Current: %1 + Aktuell: %1 + + + + Use Default Font (Restart) + Benutze Standard Schriftart (Neustart) + + + + Font: + Schriftart: + + + + Apply changes + Änderungen übernehmen + + + &OK OK, Cancel, Apply &OK - + + Discard changes + Änderungen verwerfen + + + &Cancel OK, Cancel, Apply Abbre&chen - %1 (%2 if available) [sys] - System like PC System = %1, System Language like Deutsch = %2 - %1 (%2 wenn verfügbar) [sys] - - - - System - System like PC System - System - - - - %1 (%2 if available) - System like PC System = %1, System Language like Deutsch = %2 - %1 (%2 wenn verfügbar) - - - - + %1 %1 %1 - - The new Custom Folder will initialize after you restart %1. + + System + System in context of System default + System + + + + %1 (Game language) + Next closest language compared to the Game settings + %1 (Spielsprache) + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + %1 (Näheste zur Oberfläche) + + + + + + Auto + Automatic language choice. + Automatisch + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + %1 (Sprachenpriorität) + + + + The new Custom Folder will initialise after you restart %1. Der eigene Ordner wird initialisiert sobald du %1 neugestartet hast. - The new Custom Folder initialize after you restart %1. - Der eigene Ordner initialisiert sobald du %1 neugestartet hast. + + View %1 User Statistics Online + %1 Benutzerstatistik Online ansehen - - The language change will take effect after you restart %1. - Die Änderung der Sprache nimmt Effekt sobald du %1 neugestartet hast. + + Not registered + Nicht registriert - + + + + + Yes + Ja + + + + + No + Nein + + + + + OS defined + OS-defined + + + + + Steam defined + Steam-definiert + + + No Profile No Profile, as default Kein Profil - - - + + + Profile: %1 Profil: %1 @@ -575,105 +1023,78 @@ Grand Theft Auto V Snapmatic Bilder und Spielständen PictureDialog - %1 - Snapmatic Picture Viewer - %1 - Snapmatic Bildansicht + Snapmatic Picture Viewer - %1 + Snapmatic Bildansicht - %1 - <html><head/><body><p><span style=" font-weight:600;">Title: </span>%6<br/><span style=" font-weight:600;">Location: </span>%7 (%1, %2, %3)<br/><span style=" font-weight:600;">Players: </span>%4<br/><span style=" font-weight:600;">Crew ID: </span>%5</p></body></html> - <html><head/><body><p><span style=" font-weight:600;">Titel: </span>%6<br/><span style=" font-weight:600;">Standort: </span>%7 (%1, %2, %3)<br/><span style=" font-weight:600;">Spieler: </span>%4<br/><span style=" font-weight:600;">Crew ID: </span>%5</p></body></html> + + <span style="font-weight:600">Title: </span>%6<br/> +<span style="font-weight:600">Location: </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">Players: </span>%4 (Crew %5)<br/> +<span style="font-weight:600">Created: </span>%8 + <span style="font-weight:600">Titel: </span>%6<br/> +<span style="font-weight:600">Standort: </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">Spieler: </span>%4 (Crew %5)<br/> +<span style="font-weight:600">Erstellt: </span>%8 - <span style=" font-weight:600;">Title: </span>%6<br/> -<span style=" font-weight:600;">Location: </span>%7 (%1, %2, %3)<br/> -<span style=" font-weight:600;">Players: </span>%4<br/> -<span style=" font-weight:600;">Crew ID: </span>%5 - <span style=" font-weight:600;">Titel: </span>%6<br/> -<span style=" font-weight:600;">Standort: </span>%7 (%1, %2, %3)<br/> -<span style=" font-weight:600;">Spieler: </span>%4<br/> -<span style=" font-weight:600;">Crew ID: </span>%5 + + Manage picture + Bild verwalten - <span style=" font-weight:600;">Title: </span>%6<br/> -<span style=" font-weight:600;">Location: </span>%7 (%1, %2, %3)<br/> -<span style=" font-weight:600;">Players: </span>%4 (Crew %5)<br/> -<span style=" font-weight:600;">Created: </span>%8<br/> - <span style=" font-weight:600;">Titel: </span>%6<br/> -<span style=" font-weight:600;">Standort: </span>%7 (%1, %2, %3)<br/> -<span style=" font-weight:600;">Spieler: </span>%4 (Crew %5)<br/> -<span style=" font-weight:600;">Erstellt: </span>%8 + + &Manage + &Verwalten - - <span style=" font-weight:600;">Title: </span>%6<br/> -<span style=" font-weight:600;">Location: </span>%7 (%1, %2, %3)<br/> -<span style=" font-weight:600;">Players: </span>%4 (Crew %5)<br/> -<span style=" font-weight:600;">Created: </span>%8 - <span style=" font-weight:600;">Titel: </span>%6<br/> -<span style=" font-weight:600;">Standort: </span>%7 (%1, %2, %3)<br/> -<span style=" font-weight:600;">Spieler: </span>%4 (Crew %5)<br/> -<span style=" font-weight:600;">Erstellt: </span>%8 + + Close viewer + Ansicht schließen - - &Export - &Exportieren - - - + &Close - &Schließen + S&chließen - <span style=" font-weight:600;">Location: </span>%1, %2, %3 <br><span style=" font-weight:600;">Players: </span>%4<br><span style=" font-weight:600;">Crew ID: </span>%5 - <span style=" font-weight:600;">Standort: </span>%1, %2, %3 <br><span style=" font-weight:600;">Spieler: </span>%4<br><span style=" font-weight:600;">Crew ID: </span>%5 - - - <span style=" font-weight:600;">Picture Title: </span>%6<br> -<span style=" font-weight:600;">Location: </span>%1, %2, %3 <br> -<span style=" font-weight:600;">Players: </span>%4<br> -<span style=" font-weight:600;">Crew ID: </span>%5 - <span style=" font-weight:600;">Bildtitel: </span>%6<br> -<span style=" font-weight:600;">Standort: </span>%1, %2, %3 <br> -<span style=" font-weight:600;">Spieler: </span>%4<br> -<span style=" font-weight:600;">Crew ID: </span>%5 - - - <span style=" font-weight:600;">Title: </span>%6<br> -<span style=" font-weight:600;">Location: </span>%1, %2, %3 <br> -<span style=" font-weight:600;">Players: </span>%4<br> -<span style=" font-weight:600;">Crew ID: </span>%5 - <span style=" font-weight:600;">Titel: </span>%6<br> -<span style=" font-weight:600;">Standort: </span>%1, %2, %3 <br> -<span style=" font-weight:600;">Spieler: </span>%4<br> -<span style=" font-weight:600;">Crew ID: </span>%5 - - - - + + Export Exportieren - Copy - Kopieren + + + Export as &Picture... + Als &Bild exportieren... - - Close - Schließen + + + Export as &Snapmatic... + Als &Snapmatic exportieren... - - Export as &JPG picture... - Exportiere als &JPG Bild... + + + &Edit Properties... + Eigenschaften bearb&eiten... - - Export as &GTA Snapmatic... - Exportiere als &GTA Snapmatic... + + + &Overwrite Image... + Bild &überschreiben... - + + + Open &Map Viewer... + &Kartenansicht öffnen... + + + Key 1 - Avatar Preview Mode Key 2 - Toggle Overlay Arrow Keys - Navigate @@ -682,199 +1103,197 @@ Taste 2 - Overlay umschalten Pfeiltasten - Navigieren - - + Snapmatic Picture Viewer Snapmatic Bildansicht - - + Failed at %1 - Fehlgeschlagen bei %1 + Fehlgeschlagen beim %1 - + + + No Crew + Keine Crew + + + + + + No Players + Keine Spieler + + + Avatar Preview Mode Press 1 for Default View Avatar Vorschaumodus Drücke 1 für Standardmodus - 1 - Avatar Preview Mode -2 - Toggle Overlay -Arrow Keys - Navigate - // L is for Left and R is for Right, Left Arrow Right Arrow - 1 - Avatar Vorschaumodus -2 - Overlay umschalten -Pfeiltasten - Navigieren - - - 1 - Avatar Preview Mode -2 - Toggle Overlay -L Arrow - Back -R Arrow - Next - L is for Left and R is for Right, Left Arrow Right Arrow - 1 - Avatar Vorschaumodus -2 - Overlay umschalten -L Pfeil - Zurück -R Pfeil - Weiter - - - Avatar Preview Mode -Press A for Default View - Avatar Vorschaumodus -Drücke A für Standardansicht - - - Avatar Preview Mode<br>Press A for Default View - Avatar Vorschaumodus<br>Drücke A für Standardansicht - - - - - No player - Keine Spieler - - - - - No crew - Keine Crew - - - + Unknown Location Unbekannter Standort - Export picture... - Exportiere Bild... - - - - Export as JPG picture... - Exportiere als JPG Bild... - - - - JPEG picture (*.jpg) - JPEG Bild (*.jpg) - - - + Portable Network Graphics (*.png) Portable Network Graphics (*.png) - - - - - Export as JPG picture - Exportiere als JPG Bild - - - - + + Overwrite %1 with current Snapmatic picture? Überschreibe %1 mit aktuellen Snapmatic Bild? - - - - - - - Export as GTA Snapmatic - Exportiere als GTA Snapmatic + + Export as Picture... + Als Bild exportieren... - - - Failed to overwrite %1 with current Snapmatic picture - Fehlgeschlagen beim Überschreiben von %1 mit aktuellen Snapmatic Bild + + JPEG Graphics (*.jpg *.jpeg) + JPEG Graphics (*.jpg *.jpeg) - - + + + + + + + Export as Picture + Als Bild exportieren + + + + Failed to export the picture because the system occurred a write failure + Fehlgeschlagen beim Exportieren weil das System ein Schreibfehler ausgelöst hat + + + + Failed to export the picture because the format detection failures + Fehlgeschlagen beim Exportieren weil die Formaterkennung fehlschlägt + + + + Failed to export the picture because the file can't be written + Fehlgeschlagen beim Exportieren weil die Datei nicht beschrieben werden kann + + + + Failed to export the picture because of an unknown reason + Fehlgeschlagen beim Exportieren wegen einen unbekannten Grund + + + Failed to export current Snapmatic picture Fehlgeschlagen beim Exportieren vom aktuellen Snapmatic Bild - + + Export as Snapmatic... + Als Snapmatic exportieren... + + + + + + + + Export as Snapmatic + Als Snapmatic exportieren + + + Exported Snapmatic to "%1" because of using the .auto extension. Snapmatic wurde wegen Benutzung der .auto Erweiterung zu "%1" exportiert. - Exported Snapmatic to "%1" because of using the .auto extension - Snapmatic wurde wegen Benutzung der .auto Erweiterung zu "%1" exportiert - - - Copy picture - Bild kopieren - - - Export picture for Import... - Exporti - - - - Export as GTA Snapmatic... - Exportiere als GTA Snapmatic... - - - + GTA V Export (*.g5e) GTA V Export (*.g5e) - + GTA V Raw Export (*.auto) GTA V Roher Export (*.auto) - + Snapmatic pictures (PGTA*) Snapmatic Bilder (PGTA*) - All files (**) - Alle Dateien (**) - - - Failed to copy current Snapmatic picture - Fehlgeschlagen beim Kopieren vom Snapmatic Bild - - - Failed to save current picture - Fehlgeschlagen beim Speichern vom aktuellen Bild - - - JPEG picture (*.jpg);;Portable Network Graphics (*.png) - JPEG Bild (*.jpg);;Portable Network Graphics (*.png) - - - - Export picture - Bild exportieren - - - Snapmatic Picture Exporter - Snapmatic Bild Exporter - - - Failed to save the picture - Beim Speichern des Bildes ist ein Fehler aufgetreten - - - - + + No valid file is selected Keine gültige Datei wurde ausgewählt + + + + Open &JSON Editor... + &JSON Editor öffnen... + + + + PlayerListDialog + + + Edit Players... + Spieler bearbeiten... + + + + Available Players: + Verfügbare Spieler: + + + + Selected Players: + Ausgewählte Spieler: + + + + &Apply + &Übernehmen + + + + &Cancel + Abbre&chen + + + + Add Players... + Spieler hinzufügen... + + + + Failed to add more Players because the limit of Players are %1! + Fehlgeschlagen beim Hinzufügen von mehr Spielern weil der Limit von Spielern %1 ist! + + + + + Add Player... + Spieler hinzufügen... + + + + Enter Social Club Player ID + Social Club Spieler ID eingeben + + + + Failed to add Player %1 because Player %1 is already added! + Fehlgeschlagen beim Hinzufügen vom Spieler %1 weil Spieler %1 bereits hinzugefügt wurde! + ProfileInterface @@ -890,113 +1309,118 @@ Drücke A für Standardansicht Lade Datei %1 von %2 Dateien - + %1 %2 %1 %2 - - Import exported file - Importiere exportierte Datei + + Import file + Importiere Datei - + &Import... &Importieren... - + Close profile Profil schließen - + &Close S&chließen - Import copy - Kopie importieren - - - Content of Profile %1 - Inhalt vom Profil %1 - - - View - Ansehen - - - Close Profile - Profil schließen - - - + Loading... Lade... - - + + Snapmatic Loader + Snapmatic Lader + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + <h4>Folgende Snapmatic Bilder wurden repariert</h4>%1 + + + + + + + + + + + + + + + + + + + + + Import... Importieren... - - - - - - - - - - - - + + + + + + Import Importieren - All profile files (SGTA* PGTA*) - Alle Profildateien (SGTA* PGTA*) - - - - Importable files (*.g5e *.jpg *.png SGTA* PGTA*) - Importfähige Dateien (*.g5e *.jpg *.png SGTA* PGTA*) - - - - + + Savegames files (SGTA*) Spielstanddateien (SGTA*) - - + + Snapmatic pictures (PGTA*) Snapmatic Bilder (PGTA*) - - All image files (*.jpg *.png) - Alle Bilddateien (*.jpg *.png) + + Importable files (%1) + Importfähige Dateien (%1) - - + + + + All image files (%1) + Alle Bilddateien (%1) + + + + + + All files (**) Alle Dateien (**) - - + + Import file %1 of %2 files Importiere Datei %1 von %2 Dateien - + Import failed with... %1 @@ -1005,215 +1429,265 @@ Drücke A für Standardansicht %1 - - + + Failed to read Snapmatic picture Fehler beim Lesen vom Snapmatic Bild - - + + Failed to read Savegame file Fehler beim Lesen von Spielstanddatei - - Can't import %1 because of not valid file format - Kann %1 nicht importieren weil das Dateiformat nicht gültig ist + + + + Can't import %1 because file can't be open + Kann %1 nicht importieren weil die Datei nicht geöffnet werden kann - + + + + Can't import %1 because file can't be parsed properly + Kann %1 nicht importieren weil die Datei nicht richtig gelesen werden kann + + + + Can't import %1 because file format can't be detected + Kann %1 nicht importieren weil das Dateiformat nicht erkannt werden kann + + + + Initialising export... + Initialisiere Export... + + + Failed to import the Snapmatic picture, file not begin with PGTA or end with .g5e Fehlgeschlagen beim Importieren vom Snapmatic Bild, Datei beginnt nicht mit PGTA oder endet mit .g5e - - Failed to import the Snapmatic picture, the picture is already in the game - Fehlgeschlagen beim Importieren vom Snapmatic Bild, dieses Bild ist bereits im Spiel - - - + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: %1Exportiere Snapmatic Bilder%2<br><br>JPG Bilder machen es möglich sie mit ein Bildansicht Programm zu öffnen<br>Das GTA Snapmatic Format macht es möglich sie wieder ins Game zu importieren<br><br>Exportieren als: - - - + + + No valid file is selected Keine gültige Datei wurde ausgewählt - + Enabled pictures: %1 of %2 Aktivierte Bilder: %1 von %2 - Failed to import the Snapmatic picture, file not begin with PGTA - Fehlgeschlagen beim Importieren vom Snapmatic Bild, Datei beginnt nicht mit PGTA + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + Ein Snapmatic Bild mit der Uid %1 existiert bereits, möchtest du dein Import eine neue Uid und Zeitstempel zuweisen? - + Failed to import the Snapmatic picture, can't copy the file into profile - Fehlgeschlagen beim Importieren vom Snapmatic Bild, kann Snapmatic Bild nicht ins Profil kopieren + Fehlgeschlagen beim Importieren vom Snapmatic Bild, kann Snapmatic Bild nicht ins Profil kopieren - + Failed to import the Savegame, can't copy the file into profile Fehlgeschlagen beim Importieren vom Spielstand, kann Spielstanddatei nicht ins Profil kopieren - + Failed to import the Savegame, no Savegame slot is left Fehlgeschlagen beim Importieren vom Spielstand, kein Spielstandslot mehr frei - - + + JPG pictures and GTA Snapmatic JPG Bilder und GTA Snapmatic - - + + JPG pictures only Nur JPG Bilder - - + + GTA Snapmatic only Nur GTA Snapmatic - Export Snapmatic pictures - -JPG pictures make it possible to open the picture with a Image Viewer -GTA Snapmatic make it possible to import the picture into the game - -Export as: - Exportiere Snapmatic Bilder - -JPG Bilder machen es möglich sie mit ein Bildansicht Programm zu öffnen -Das GTA Snapmatic Format macht es möglich sie wieder ins Game zu importieren - -Exportieren als: + + + + + Patch selected... + Auswahl patchen... - - + + + + + + + + + Patch file %1 of %2 files + Patche Datei %1 von %2 Dateien + + + + + Qualify as Avatar + Als Avatar qualifizieren + + + + + + + + + No Snapmatic pictures are selected + Keine Snapmatic Bilder sind ausgewählt + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + Fehlgeschlagen beim Entfernen von allen augewählten Snapmatic Bildern und/oder Spielstanddateien + + + + + + + + + %1 failed with... + +%2 + Action failed with... + %1 fehlgeschlagen mit... + +%2 + + + + Prepare Content for Import... + Bereite Inhalt für Import vor... + + + + Qualify + %1 failed with... + Qualifizieren + + + + + Change Players... + Spieler ändern... + + + + Change Players + %1 failed with... + Spieler ändern + + + + + + Change Crew... + Crew ändern... + + + + Failed to enter a valid Snapmatic Crew ID + Fehlgeschlagen beim Eingeben von einer gültigen Crew ID + + + + Change Crew + %1 failed with... + Crew ändern + + + + + + Change Title... + Titel ändern... + + + + Failed to enter a valid Snapmatic title + Fehlgeschlagen beim Eingeben eines gültigen Snapmatic Titel + + + + Change Title + %1 failed with... + Titel ändern + + + + No Snapmatic pictures or Savegames files are selected - Keine Snapmatic Bilder oder Spielstände ausgewählt + Keine Snapmatic Bilder oder Spielstände sind ausgewählt - - - + + + Remove selected Auswahl löschen - + You really want remove the selected Snapmatic picutres and Savegame files? Möchtest du wirklich die ausgewählten Snapmatic Bilder und Spielstanddateien löschen? - - Failed at remove the complete selected Snapmatic pictures and/or Savegame files - Fehlgeschlagen beim kompletten entfernen der ausgewählten Snapmatic Bilder und/oder der Spielstanddateien - - - Failed to import copy of Snapmatic picture because the file not begin with PGTA - Fehlgeschlagenen beim Import vom Snapmatic Bild weil die Datei nicht mit PGTA begint - - - Failed to import copy of Snapmatic picture because the copy failed - Fehlgeschlagenen beim Import vom Snapmatic Bild weil kopieren fehlgeschlagen ist - - - Failed to import copy of Savegame file because the copy failed - Fehlgeschlagenen beim Import vom Spielstand weil kopieren fehlgeschlagen ist - - - Failed to import copy of Savegame file because no free Savegame slot left - Fehlgeschlagenen beim Import vom Spielstand weil kein Spielstandslot mehr übrig ist - - - - - - - Export selected - Auswahl exportieren - - - Export and Copy pictures - Bilder exportieren und kopieren - - - Export pictures - Bilder exportieren - - - Copy pictures - Bilder kopieren - - - How should we deal with the Snapmatic pictures? - Wie sollen wir mit den Snapmatic Bilder umgehen? - - - + + + + + Export selected... Auswahl exportieren... - - Initializing export... - Initialisiere Export... - - - Initializing... - Initialisierung... - - - + Export failed with... %1 Exportieren fehlgeschlagen bei...\n%1 - Delete selected - Auswahl löschen - - - You really want delete the selected content? - Möchtest du wirklich den ausgewählten Inhalt löschen? - - - Failed at delete all selected content - Fehlgeschlagen beim Löschen von alle ausgewählte Inhalte - - - Current export job: %1 - Aktueller Exportiervorgang: %1 - - - - - + + + Export file %1 of %2 files Exportiere Datei %1 von %2 Dateien - + All profile files (*.g5e SGTA* PGTA*) Alle Profildateien (*.g5e SGTA* PGTA*) - - + + GTA V Export (*.g5e) GTA V Export (*.g5e) @@ -1221,53 +1695,35 @@ Exportieren als: QApplication - - Font - Schrift - - - - Selected Font: %1 - Ausgewähle Schrift: %1 + <h4>Welcome to %1!</h4>You want to configure %1 before you start using it? + <h4>Willkommen zu %1!</h4>Möchtest du %1 einstellen bevor du es nutzt? SavegameDialog - + Savegame Viewer Spielstandanzeiger - - <span style=" font-weight:600;">Savegame</span><br><br>%1 - <span style=" font-weight:600;">Spielstand</span><br><br>%1 + + <span style="font-weight:600">Savegame</span><br><br>%1 + <span style="font-weight:600">Spielstand</span><br><br>%1 - + &Export &Exportieren - Cop&y - &Kopieren - - - + &Close S&chließen - Copy - Kopieren - - - Close - Schließen - - - + Failed at %1 Fehlgeschlagen bei %1 @@ -1280,82 +1736,60 @@ Exportieren als: Spielstand Widget - The Third Way (100%) - 00/00/00 00:00:00 - The Third Way (100%) - 00/00/00 00:00:00 - - - Savegame %1 -%2 - Spielstand %1 -%2 - - - SAVE - %1<br>%2 - SPIELSTAND - %1<br>%2 - - - + View Ansehen - - + + Export Exportieren - Copy - Kopieren - - - + Delete Löschen - - - + Delete savegame Savegame löschen - + Export Savegame... Spielstand exportieren... - + SAVE %3 - %1<br>%2 SPIELSTAND %3 - %1<br>%2 - - + + WRONG FORMAT FALSCHES FORMAT - AUTO - AUTO - - - + + AUTOSAVE - %1 %2 AUTOSAVE - %1 %2 - + + SAVE %3 - %1 %2 SPIELSTAND %3 - %1 %2 - + UNKNOWN UNKNOWN @@ -1365,154 +1799,120 @@ Exportieren als: Bist du sicher %1 von deinen Spielständen zu löschen? - + + + Delete Savegame + Savegame löschen + + + Failed at deleting %1 from your savegames Fehlgeschlagen beim Löschen %1 von deinen Spielständen - + &View A&nsehen - + + + &Remove Entfe&rnen - - + + &Select Au&swählen - + + &Deselect A&bwählen - - + + Select &All &Alles auswählen - + + &Deselect All Alles a&bwählen - Select - Auswählen - - - Deselect - Abwählen - - - Select all - Alles auswählen - - - Ctrl+S - Strg+S - - - Deselect all - Alles abwählen - - - Shift+S - Umschalt+S - - - + View savegame Spielstand ansehen - + Copy savegame Spielstand kopieren - + + + &Export &Exportieren - + Savegame files (SGTA*) Spielstanddateien (SGTA*) - + All files (**) Alle Dateien (**) - - - + + + Export Savegame Spielstand exportieren - + Overwrite %1 with current Savegame? Überschreibe %1 mit aktuellen Spielstand? - + Failed to overwrite %1 with current Savegame Fehlgeschlagen beim Überschrieben von %1 mit aktuellen Spielstand - + Failed to export current Savegame Fehlgeschlagen beim Exportieren vom aktuellen Spielstand - Failed to copy current Savegame - Fehlgeschlagen beim Kopieren vom aktuellen Spielstand - - - Overwrite %1 with current savegame? - Überschreibe %1 mit aktuellen Spielstand? - - - Failed to overwrite %1 with current savegame - Fehlgeschlagen beim Überschreiben von %1 mit aktuellen Spielstand - - - Failed to copy current savegame - Fehlgeschlagen beim Kopieren vom Spielstand - - - Failed to copy the savegame - Beim Kopieren vom Spielstand ist ein Fehler aufgetreten - - - + No valid file is selected Keine gültige Datei wurde ausgewählt - - Are you sure to delete %1 from your Savegames? - Bist du sicher %1 von deinen Spielständen zu löschen? - - - Failed at deleting %1 from your Savegames - Fehlgeschlagen beim Löschen %1 von deinen Spielständen - SnapmaticEditor - + + + + + + + Snapmatic Properties Snapmatic Eigenschaften @@ -1539,11 +1939,7 @@ Exportieren als: Mugshot - Fahndungsfoto - - - Custom - Eigenes + Polizeifoto @@ -1556,7 +1952,8 @@ Exportieren als: Meme - + + Snapmatic Title Snapmatic Titel @@ -1566,92 +1963,125 @@ Exportieren als: Snapmatic Werte - - + Crew: %1 (%2) Crew: %1 (%2) - - + Title: %1 (%2) Titel: %1 (%2) - - - + + Players: %1 (%2) + Multiple Player are inserted here + Spieler: %1 (%2) + + + + Player: %1 (%2) + One Player is inserted here + Spieler: %1 (%2) + + + + Appropriate: %1 Angemessen: %1 - + Extras Extras - + Qualify as Avatar automatically at apply - Beim Übernehmen als Avatar qualifizieren + Beim Übernehmen als Avatar qualifizieren - + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture Das Qualifizieren als Avatar erlaubt dir dieses Snapmatic als Social Club Profilbild zu nutzen - + + Apply changes + Änderungen übernehmen + + + &Apply &Übernehmen - + + Discard changes + Änderungen verwerfen + + + &Cancel Abbre&chen - Apply - Übernehmen - - - Cancel - Cancel - - - - + + + Edit Bearbeiten - + Yes Yes, should work fine Ja - + No No, could lead to issues Nein - + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + <h4>Ungespeicherte Änderungen erkannt</h4>Möchtest du den JSON Inhalt speichern bevor du verlässt? + + + + Patching of Snapmatic Properties failed because of %1 + Patchen von Snapmatic Eigenschaften fehlgeschlagen wegen %1 + + + + Patching of Snapmatic Properties failed because of JSON Error + Patchen von Snapmatic Eigenschaften fehlgeschlagen wegen JSON Fehler + + + + + + Patching of Snapmatic Properties failed because of I/O Error Patchen von Snapmatic Eigenschaften fehlgeschlagen wegen I/O Fehler - + + New Snapmatic title: Neuer Snapmatic Titel: - + + Snapmatic Crew Snapmatic Crew - + + New Snapmatic crew: Neue Snapmatic Crew: @@ -1659,10 +2089,69 @@ Exportieren als: SnapmaticPicture - + PHOTO - %1 FOTO - %1 + + + open file %1 + Datei öffnen %1 + + + + header not exists + Header nicht existiert + + + + header is malformed + Header fehlerhaft ist + + + + picture not exists (%1) + Bild nicht existiert (%1) + + + + JSON not exists (%1) + JSON nicht existiert (%1) + + + + title not exists (%1) + Titel nicht existiert (%1) + + + + description not exists (%1) + Beschreibung nicht existiert (%1) + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + Datei lesen von %1 weil %2 + + + + + JSON is incomplete and malformed + JSON ist unvollständig und Fehlerhaft + + + + + JSON is incomplete + JSON ist unvollständig + + + + + JSON is malformed + JSON ist Fehlerhaft + SnapmaticWidget @@ -1672,293 +2161,173 @@ Exportieren als: Snapmatic Widget - + PHOTO - 00/00/00 00:00:00 FOTO - 00/00/00 00:00:00 - + View Ansehen - + Copy Kopieren - + Export Exportieren - + Delete Löschen - - - + + + Delete picture Bild löschen - + Are you sure to delete %1 from your Snapmatic pictures? Bist du sicher %1 von deine Snapmatic Bilder zu löschen? - + + Failed to hide %1 In-game from your Snapmatic pictures + Fehlgeschlagen beim Ausblenden von %1 im Spiel von deinen Snapmatic Bildern + + + + Failed to show %1 In-game from your Snapmatic pictures + Fehlgeschlagen beim Anzeigen von %1 im Spiel von deinen Snapmatic Bildern + + + + + Edi&t Bearbei&ten - Enable &In-game - &Im Spiel aktivieren - - - Disable &In-game - &Im Spiel deaktivieren - - - + &Export &Exportieren - Export as &JPG picture - Exportiere als &JPG Bild - - - Export as &GTA Snapmatic - Exportiere als &GTA Snapmatic - - - + + + Show &In-game &Im Spiel anzeigen - + + + Hide &In-game &Im Spiel ausblenden - &Edit Properties - &Eigenschaften bearbeiten - - - PHOTO - %1 - FOTO - %1 - - - - &Edit Properties... - &Eigenschaften bearbeiten... - - - - Export as &JPG picture... - Exportiere als &JPG Bild... - - - - Export as &GTA Snapmatic... - Exportiere als &GTA Snapmatic... - - - + &View A&nsehen - + &Remove Entfe&rnen - - + + &Select Au&swählen - + + &Deselect A&bwählen - - + + Select &All Alles &auswählen - + + &Deselect All Alles a&bwählen - Select - Auswählen - - - Deselect - Abwählen - - - Select all - Alles auswählen - - - Ctrl+S - Strg+S - - - Deselect all - Alles abwählen - - - Shift+S - Umschalt+S - - - + View picture Bild ansehen - + Copy picture Bild kopieren - + Export picture Bild exportieren - You're sure to delete %1 from your Snapmatic pictures? - Bist du sicher %1 von deinen Snapmatic Bilder zu löschen? - - - + Failed at deleting %1 from your Snapmatic pictures Fehlgeschlagen beim Löschen von %1 von deinen Snapmatic Bildern + + TelemetryDialog + + + %1 User Statistics + %1 Benutzerstatistik + + + + You want help %1 to improve in the future by including personal usage data in your submission? + Sollen bei Einreichungen Persönliche Nutzungsdaten einbezogen werden um %1 in der Zukunft zu unterstützen? + + + + Yes, I want include personal usage data. + Ja, ich möchte Persönliche Nutzungsdaten einbeziehen. + + + + &OK + &OK + + UserInterface - Grand Theft Auto V Sync - Grand Theft Auto V Sync - - - GTA V Sync - GTA V Sync - - - gta5sync - %1 - gta5sync - %1 - - - File - Datei - - - Help - Hilfe - - - Edit - Bearbeiten - - - Profile - Profil - - - About gta5sync - Über gta5sync - - - - Ctrl+A - Strg+A - - - Close - Schließen - - - Options - Optionen - - - - Ctrl+O - Strg+O - - - Select all - Alles auswählen - - - - Ctrl+S - Strg+S - - - Deselect all - Alles abwählen - - - Shift+S - Umschalt+S - - - Export selected - Auswahl exportieren - - - - Ctrl+E - Strg+E - - - Delete selected - Auswahl löschen - - - - Ctrl+D - Strg+D - - - + Exit Beenden - + &Selection visibility Auswahl &Sichtbarkeit - &About Product - &Über Produkt - - - + &Exit B&eenden - - - Ctrl+Q - Strg+Q - Select profile @@ -1970,237 +2339,231 @@ Exportieren als: %1 %2 - + + Reload profile overview + Übersicht der Profile neuladen + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + Schließe %1 + + + &File &Datei - + &Help &Hilfe - + &Edit Bearb&eiten - + &Profile &Profil - Selection visibility - Auswahl Sichtbarkeit + + Selection &mass tools + Auswahl &Massenwerkzeuge - &About gta5sync - &Über gta5sync + + + + Change &Title... + &Titel ändern... - + + + + &Qualify as Avatar + Als Avatar &qualifizieren + + + + + + Change &Players... + S&pieler ändern... + + + + + + Change &Crew... + &Crew ändern... + + + Close &Profile &Profil schließen - - Ctrl+End - Strg+Ende - - - - Ctrl+Del - Strg+Entf - - - + &Open File... Datei &öffnen... - - + + Select &GTA V Folder... Wähle &GTA V Ordner... - - Ctrl+G - Strg+G - - - + Show In-gam&e Im Spiel anzeig&en - + Hi&de In-game Im Spiel ausblen&den - Hid&e In-game - Im Spiel ausblen&den - - - &Enable In-game - Im Spiel aktivier&en - - - - Shift+E - Umschalt+E - - - &Disable In-game - Im Spiel &deaktivieren - - - - Shift+D - Umschalt+D - - - + + + + &Close S&chließen - Select &Profile - &Profil auswählen - - - - Ctrl+P - Strg+P - - - &Options - &Optionen - - - + &Settings Ein&stellungen - + Select &All &Alles auswählen - + &Deselect All Alles a&bwählen - + &Export selected... Auswahl &exportieren... - + &Remove selected Auswahl entfe&rnen - Ctrl+R - Strg+R - - - + &Import files... Dateien &importieren... - - Ctrl+I - Strg+I - - - GTA V Folder not found! - GTA V Ordner nicht gefunden! - - - - + + + Select Profile Profil auswählen - - - - + + + + Select GTA V Folder... Wähle GTA V Ordner... - Select GTA V &Folder... - Wähle GTA V &Ordner... - - - + Open File... Datei öffnen... - - Import - Importieren - - + %2 - %1 %2 - %1 - - + + + &About %1 &Über %1 - - - - + + + &Donate + Spen&den + + + + Donate + Spenden + + + + Donation methods + Spendenmethoden + + + + &Copy + &Kopieren + + + View + Ansehen + + + Copy + Kopieren + + + + + + Open File Datei öffnen - + Can't open %1 because of not valid file format Kann nicht %1 öffnen weil Dateiformat nicht gültig ist - Change GTA V &Folder - GTA V &Ordner ändern + + %1 - Messages + %1 - Nachrichten - gta5sync - gta5sync - - - Grand Theft Auto V Folder not found! - Grand Theft Auto V Ordner wurde nicht gefunden! - - - + &Reload &Neuladen - Reload - Neuladen + + + + Show In-game + Im Spiel anzeigen - Reload profiles - Profile neuladen - - - Not able to reload profiles - Nicht fähig Profile neuzuladen + + + + Hide In-game + Im Spiel ausblenden diff --git a/res/gta5sync_en_US.qm b/res/gta5sync_en_US.qm new file mode 100644 index 0000000..502289e Binary files /dev/null and b/res/gta5sync_en_US.qm differ diff --git a/res/gta5sync_en_US.ts b/res/gta5sync_en_US.ts new file mode 100644 index 0000000..1b4c531 --- /dev/null +++ b/res/gta5sync_en_US.ts @@ -0,0 +1,2505 @@ + + + + + AboutDialog + + + About %1 + + + + + <span style="font-weight:600">%1</span><br/> +<br/> +%2<br/> +<br/> +Version %3<br/> +Created on %4<br/> +Built with Qt %5<br/> +Running with Qt %6<br/> +<br/> +%7 + + + + + &Close + + + + + Translated by %1 + Translated by translator, example Translated by Syping + + + + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + Syping,g5e://about?U3lwaW5n:R2l0TGFiOiA8YSBocmVmPSJodHRwczovL2dpdGxhYi5jb20vU3lwaW5nIj5TeXBpbmc8L2E+PGJyLz5HaXRIdWI6IDxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9TeXBpbmciPlN5cGluZzwvYT48YnIvPlNvY2lhbCBDbHViOiA8YSBocmVmPSJodHRwczovL3NvY2lhbGNsdWIucm9ja3N0YXJnYW1lcy5jb20vbWVtYmVyL1N5cGluZy80NjMwMzA1NiI+U3lwaW5nPC9hPg== + + + + A project for viewing Grand Theft Auto V Snapmatic<br/> +Pictures and Savegames + + + + + Copyright &copy; <a href="%1">%2</a> %3 + + + + + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + + + + + Release + + + + + Release Candidate + + + + + Daily Build + + + + + Developer + + + + + Beta + + + + + Alpha + + + + + + Custom + + + + + CrewDatabase + + + + No Crew + + + + + ExportDialog + + + Dialog + + + + + Export Format + + + + + &JPEG/PNG format + + + + + GTA &Snapmatic format + + + + + Export Size + + + + + Default &Size + + + + + &Desktop Size + + + + + &Custom Size + + + + + Custom Size: + + + + + x + + + + + &Export + + + + + &Close + + + + + ImageEditorDialog + + + + + + Snapmatic Image Editor + + + + + Overwrite Image... + + + + + Apply changes + + + + + &Overwrite + + + + + Discard changes + + + + + &Close + + + + + + Patching of Snapmatic Image failed because of I/O Error + + + + + + Patching of Snapmatic Image failed because of Image Error + + + + + ImportDialog + + + Import... + + + + + Crop to Aspect Ratio + + + + + + + + Background Colour: <span style="color: %1">%1</span> + Background Color: <span style="color: %1">%1</span> + + + + Select background colour + Select background color + + + + Avatar + + + + + Picture + + + + + + Ignore Aspect Ratio + + + + + Watermark + + + + + Background + + + + + + Background Image: %1 + + + + + Select background image + + + + + Remove background image + + + + + Force Colour in Avatar Zone + Force Color in Avatar Zone + + + + Advanced + + + + + Resolution: + + + + + Snapmatic resolution + + + + + Avoid compression and expand buffer instead, improves picture quality, but may break Snapmatic + + + + + Unlimited Buffer + + + + + Import as-is, don't change the picture at all, guaranteed to break Snapmatic unless you know what you doing + + + + + Import as-is + + + + + Import options + + + + + &Options + + + + + Import picture + + + + + &OK + + + + + Discard picture + + + + + &Cancel + + + + + + + + Background Image: + + + + + &Import new Picture... + + + + + &Crop Picture... + + + + + &Load Settings... + + + + + &Save Settings... + + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + + + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + + + + + Storage + Background Image: Storage + + + + + Crop Picture... + + + + + &Crop + + + + + Crop Picture + + + + + + Please import a new picture first + + + + + + Default + Default as Default Profile + + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + + + + + + Load Settings... + + + + + + Please select your settings profile + + + + + + Save Settings... + + + + + + Snapmatic Avatar Zone + + + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + + + + + Select Colour... + Select Color... + + + + File + Background Image: File + + + + + JsonEditorDialog + + + Snapmatic JSON Editor + + + + + Apply changes + + + + + &Save + + + + + Discard changes + + + + + &Close + + + + + JSON Error + + + + + MapLocationDialog + + + Snapmatic Map Viewer + + + + + Close viewer + + + + + &Close + + + + + Apply new position + + + + + &Apply + + + + + Revert old position + + + + + &Revert + + + + + Select new position + + + + + &Select + + + + + Quit select position + + + + + &Done + + + + + X: %1 +Y: %2 + X and Y position + + + + + OptionsDialog + + + %1 - Settings + + + + + Profiles + + + + + Content Open/Select Mode + + + + + Open with Doubleclick + + + + + Default Profile + + + + + Custom GTA V Folder + + + + + Force using Custom Folder + + + + + ... + + + + + Pictures + + + + + Export Size + + + + + Default: %1x%2 + + + + + Screen Resolution: %1x%2 + + + + + + Custom Size: + + + + + x + + + + + Ignore Aspect Ratio + + + + + Export Quality + + + + + Enable Custom Quality + + + + + Quality: + + + + + %1% + + + + + Picture Viewer + + + + + Enable Navigation Bar + + + + + Players + + + + + ID + + + + + Name + + + + + Game + + + + + Social Club Version + + + + + + + + + + + + Found: %1 + + + + + + + + + + + + + + Language: %1 + + + + + Steam Version + + + + + Feedback + + + + + + Participate in %1 User Statistics + + + + + Hardware, Application and OS Specification + + + + + Application Configuration + + + + + Other + + + + + + + Participation ID: %1 + + + + + &Copy + + + + + Language for Areas + + + + + Style + + + + + Style: + + + + + Font + + + + + Interface + + + + + Participation + + + + + Categories + + + + + System Language Configuration + + + + + Personal Usage Data + + + + + Language for Interface + + + + + + + + Current: %1 + + + + + Use Default Style (Restart) + + + + + Use Default Font (Restart) + + + + + Font: + + + + + Apply changes + + + + + &OK + OK, Cancel, Apply + + + + + Discard changes + + + + + &Cancel + OK, Cancel, Apply + + + + + System + System in context of System default + + + + + %1 (Game language) + Next closest language compared to the Game settings + + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + + + + + + + Auto + Automatic language choice. + + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + + + + + %1 + %1 + + + + + The new Custom Folder will initialise after you restart %1. + The new Custom Folder will initialize after you restart %1. + + + + No Profile + No Profile, as default + + + + + + + Profile: %1 + + + + + View %1 User Statistics Online + + + + + Not registered + + + + + + + + Yes + + + + + + No + + + + + + OS defined + + + + + + Steam defined + + + + + PictureDialog + + + Snapmatic Picture Viewer - %1 + + + + + <span style="font-weight:600">Title: </span>%6<br/> +<span style="font-weight:600">Location: </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">Players: </span>%4 (Crew %5)<br/> +<span style="font-weight:600">Created: </span>%8 + + + + + Manage picture + + + + + &Manage + + + + + Close viewer + + + + + &Close + + + + + + Export as &Picture... + + + + + + Export as &Snapmatic... + + + + + + &Overwrite Image... + + + + + + &Edit Properties... + + + + + + Open &Map Viewer... + + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + + + + + Snapmatic Picture Viewer + + + + + Failed at %1 + + + + + + + No Players + + + + + + No Crew + + + + + Unknown Location + + + + + Avatar Preview Mode +Press 1 for Default View + + + + + Export as Picture... + + + + + + Export + + + + + JPEG Graphics (*.jpg *.jpeg) + + + + + Portable Network Graphics (*.png) + + + + + + + + + + Export as Picture + + + + + + Overwrite %1 with current Snapmatic picture? + + + + + + Failed to export current Snapmatic picture + + + + + + No valid file is selected + + + + + Failed to export the picture because the system occurred a write failure + + + + + Failed to export the picture because the format detection failures + + + + + Failed to export the picture because the file can't be written + + + + + Failed to export the picture because of an unknown reason + + + + + Export as Snapmatic... + + + + + GTA V Export (*.g5e) + + + + + GTA V Raw Export (*.auto) + + + + + Snapmatic pictures (PGTA*) + + + + + + + + + Export as Snapmatic + + + + + Exported Snapmatic to "%1" because of using the .auto extension. + + + + + + Open &JSON Editor... + + + + + PlayerListDialog + + + Edit Players... + + + + + Available Players: + + + + + Selected Players: + + + + + &Apply + + + + + &Cancel + + + + + Add Players... + + + + + Failed to add more Players because the limit of Players are %1! + + + + + + Add Player... + + + + + Enter Social Club Player ID + + + + + Failed to add Player %1 because Player %1 is already added! + + + + + ProfileInterface + + + Profile Interface + + + + + Loading file %1 of %2 files + + + + + %1 %2 + + + + + Import file + + + + + &Import... + + + + + Close profile + + + + + &Close + + + + + + + Export file %1 of %2 files + + + + + Enabled pictures: %1 of %2 + + + + + Loading... + + + + + Snapmatic Loader + + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + + + + + + + + + + + + + + + + + + + + + + + Import... + + + + + + + + + + Import + + + + + Importable files (%1) + + + + + + GTA V Export (*.g5e) + + + + + + Savegames files (SGTA*) + + + + + + Snapmatic pictures (PGTA*) + + + + + + + All image files (%1) + + + + + + + + All files (**) + + + + + + + No valid file is selected + + + + + + Import file %1 of %2 files + + + + + Import failed with... + +%1 + + + + + + Failed to read Snapmatic picture + + + + + + Failed to read Savegame file + + + + + + + Can't import %1 because file can't be open + + + + + + + Can't import %1 because file can't be parsed properly + + + + + Can't import %1 because file format can't be detected + + + + + Failed to import the Snapmatic picture, file not begin with PGTA or end with .g5e + + + + + Failed to import the Snapmatic picture, can't copy the file into profile + + + + + Failed to import the Savegame, can't copy the file into profile + + + + + Failed to import the Savegame, no Savegame slot is left + + + + + + JPG pictures and GTA Snapmatic + + + + + + JPG pictures only + + + + + + GTA Snapmatic only + + + + + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: + + + + + + + + + Export selected... + + + + + Initialising export... + Initializing export... + + + + Export failed with... + +%1 + + + + + + No Snapmatic pictures or Savegames files are selected + + + + + + + Remove selected + + + + + You really want remove the selected Snapmatic picutres and Savegame files? + + + + + + Qualify as Avatar + + + + + + + + + + No Snapmatic pictures are selected + + + + + + + + Patch selected... + + + + + + + + + + + + Patch file %1 of %2 files + + + + + + + + + + %1 failed with... + +%2 + Action failed with... + + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + + + + + Prepare Content for Import... + + + + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + + + + + Qualify + %1 failed with... + + + + + + Change Players... + + + + + Change Players + %1 failed with... + + + + + + + Change Crew... + + + + + Failed to enter a valid Snapmatic Crew ID + + + + + Change Crew + %1 failed with... + + + + + + + Change Title... + + + + + Failed to enter a valid Snapmatic title + + + + + Change Title + %1 failed with... + + + + + All profile files (*.g5e SGTA* PGTA*) + + + + + SavegameDialog + + + + Savegame Viewer + + + + + <span style="font-weight:600">Savegame</span><br><br>%1 + + + + + &Export + + + + + &Close + + + + + Failed at %1 + + + + + SavegameWidget + + + Savegame Widget + + + + + SAVE %3 - %1<br>%2 + + + + + View savegame + + + + + View + + + + + Copy savegame + + + + + + Export + + + + + Delete savegame + + + + + Delete + + + + + &View + + + + + + + &Export + + + + + + + &Remove + + + + + + &Select + + + + + + &Deselect + + + + + + Select &All + + + + + + &Deselect All + + + + + Savegame files (SGTA*) + + + + + All files (**) + + + + + + + + Export Savegame + + + + + Overwrite %1 with current Savegame? + + + + + Failed to overwrite %1 with current Savegame + + + + + Failed to export current Savegame + + + + + No valid file is selected + + + + + Export Savegame... + + + + + + AUTOSAVE - %1 +%2 + + + + + + SAVE %3 - %1 +%2 + + + + + + WRONG FORMAT + + + + + UNKNOWN + + + + + Are you sure to delete %1 from your savegames? + + + + + + Delete Savegame + + + + + Failed at deleting %1 from your savegames + + + + + SnapmaticEditor + + + + + + + + + + + Snapmatic Properties + + + + + Snapmatic Type + + + + + Editor + + + + + Selfie + + + + + Regular + + + + + Mugshot + + + + + Meme + + + + + Director + + + + + Snapmatic Values + + + + + Crew: %1 (%2) + + + + + Title: %1 (%2) + + + + + Players: %1 (%2) + Multiple Player are inserted here + + + + + Player: %1 (%2) + One Player is inserted here + + + + + + Appropriate: %1 + + + + + Extras + + + + + Qualify as Avatar automatically at apply + + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + + + + + Apply changes + + + + + &Apply + + + + + Discard changes + + + + + &Cancel + + + + + + + Edit + + + + + Yes + Yes, should work fine + + + + + No + No, could lead to issues + + + + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + + + + + Patching of Snapmatic Properties failed because of %1 + + + + + Patching of Snapmatic Properties failed because of JSON Error + + + + + + + + Patching of Snapmatic Properties failed because of I/O Error + + + + + + Snapmatic Title + + + + + + New Snapmatic title: + + + + + + Snapmatic Crew + + + + + + New Snapmatic crew: + + + + + SnapmaticPicture + + + PHOTO - %1 + + + + + open file %1 + + + + + header not exists + + + + + header is malformed + + + + + picture not exists (%1) + + + + + JSON not exists (%1) + + + + + title not exists (%1) + + + + + description not exists (%1) + + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + + + + + + JSON is incomplete and malformed + + + + + + JSON is incomplete + + + + + + JSON is malformed + + + + + SnapmaticWidget + + + Snapmatic Widget + + + + + PHOTO - 00/00/00 00:00:00 + + + + + View picture + + + + + View + + + + + Copy picture + + + + + Copy + + + + + Export picture + + + + + Export + + + + + + + Delete picture + + + + + Delete + + + + + + + Edi&t + + + + + + + Show &In-game + + + + + + + Hide &In-game + + + + + &Export + + + + + &View + + + + + &Remove + + + + + + &Select + + + + + + &Deselect + + + + + + Select &All + + + + + + &Deselect All + + + + + Are you sure to delete %1 from your Snapmatic pictures? + + + + + Failed at deleting %1 from your Snapmatic pictures + + + + + Failed to hide %1 In-game from your Snapmatic pictures + + + + + Failed to show %1 In-game from your Snapmatic pictures + + + + + TelemetryDialog + + + You want help %1 to improve in the future by including personal usage data in your submission? + + + + + %1 User Statistics + + + + + Yes, I want include personal usage data. + + + + + &OK + + + + + UserInterface + + + + %2 - %1 + + + + + Select profile + + + + + %1 %2 + + + + + Reload profile overview + + + + + &Reload + + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + + + + + + + + &Close + + + + + &File + + + + + &Help + + + + + &Edit + + + + + &Profile + + + + + &Selection visibility + + + + + + + &About %1 + + + + + &Exit + + + + + Exit + + + + + Close &Profile + + + + + &Settings + + + + + Select &All + + + + + &Deselect All + + + + + &Export selected... + + + + + &Remove selected + + + + + &Import files... + + + + + &Open File... + + + + + + Select &GTA V Folder... + + + + + + + + Select GTA V Folder... + + + + + Show In-gam&e + + + + + Hi&de In-game + + + + + + + Change &Players... + + + + + Selection &mass tools + + + + + + + Change &Title... + + + + + + + Change &Crew... + + + + + + + &Qualify as Avatar + + + + + + + Select Profile + + + + + + &Donate + + + + + Donate + + + + + Donation methods + + + + + &Copy + + + + + Open File... + + + + + + + + Open File + + + + + Can't open %1 because of not valid file format + + + + + %1 - Messages + + + + + + + Show In-game + + + + + + + Hide In-game + + + + diff --git a/res/gta5sync_fr.qm b/res/gta5sync_fr.qm index 2b5dd18..5b24a03 100644 Binary files a/res/gta5sync_fr.qm and b/res/gta5sync_fr.qm differ diff --git a/res/gta5sync_fr.ts b/res/gta5sync_fr.ts index 42e78d2..7e90ba6 100644 --- a/res/gta5sync_fr.ts +++ b/res/gta5sync_fr.ts @@ -3,35 +3,6 @@ AboutDialog - - About gta5sync - À propos de gta5sync - - - <span style=" font-weight:600;">gta5sync</span><br/> -<br/> -A project for viewing and sync Grand Theft Auto V Snapmatic<br/> -Pictures and Savegames<br/> -<br/> -Project version: %1<br/> -Project build: %4, %5<br/> -Compiled with Qt %2<br/> -Running with Qt %3<br/> -<br/> -Copyright &copy; <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5sync is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - <span style=" font-weight:600;">gta5sync</span><br/> -<br/> -Un outil pour gérer et synchroniser les photos Snapmatic<br/> -et les fichiers de sauvegarde de Grand Theft Auto V<br/> -<br/> -gta5sync v%1<br/> -Build %4, %5<br/> -Compilé avec Qt %2<br/> -Fonctionne avec Qt %3<br/> -<br/> -Copyright &copy; <a href="https://github.com/Syping/">Syping</a> 2016<br/> -gta5sync est distribué sous license <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - About %1 @@ -39,7 +10,7 @@ gta5sync est distribué sous license <a href="https://www.gnu.org/licens - <span style=" font-weight:600;">%1</span><br/> + <span style="font-weight:600">%1</span><br/> <br/> %2<br/> <br/> @@ -49,7 +20,7 @@ Built with Qt %5<br/> Running with Qt %6<br/> <br/> %7 - <span style=" font-weight:600;">%1</span><br/> + <span style="font-weight:600">%1</span><br/> <br/> %2<br/> <br/> @@ -59,51 +30,6 @@ Compilé avec Qt %5<br/> Fonctionne avec Qt %6<br/> <br/> %7 - - - <span style=" font-weight:600;">%1</span><br/> -<br/> -%2<br/> -<br/> -Version %3<br/> -Created on %4<br/> -Built with Qt %5<br/> -Running with Qt %6<br/> -%8<br/> -%7 - <span style=" font-weight:600;">%1</span><br/> -<br/> -%2<br/> -<br/> -Version %3<br/> -Publié le %4<br/> -Compilé avec Qt %5<br/> -Fonctionne avec Qt %6<br/> -%8<br/> -%7 - - - <span style=" font-weight:600;">%8</span><br/> -<br/> -%9<br/> -<br/> -Project version: %1<br/> -Project build: %4<br/> -Compiled with Qt %2<br/> -Running with Qt %3<br/> -<br/> -Copyright &copy; <a href="%5">%6</a> %7<br/>%8 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - <span style=" font-weight:600;">%8</span><br/> -<br/> -%9<br/> -<br/> -Version %1<br/> -Build %4<br/> -Compilé avec Qt %2<br/> -Fonctionne avec Qt %3<br/> -<br/> -Copyright &copy; <a href="%5">%6</a> %7<br/> -%8 est distribué sous license <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> @@ -111,62 +37,77 @@ Copyright &copy; <a href="%5">%6</a> %7<br/> &Fermer - - Using %1 %2 - Exp. Using libmyfuck - Utilise %1 %2 - - - + Translated by %1 - Exp. Translated by Syping + Translated by translator, example Translated by Syping Traduit par %1 - - NAME_OF_TRANSLATOR - Your Name (The person behind your screen looking at this text!) - Ganjalo + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + Ganjalo,https://github.com/Ganjalo/ +XeriosG,g5e://about?WGVyaW9zRw:RGlzY29yZDogWGVyaW9zRyM1MzIxPGJyLz5TdGVhbTogPGEgaHJlZj0iaHR0cHM6Ly9zdGVhbWNvbW11bml0eS5jb20vcHJvZmlsZXMvNzY1NjExOTg0MjU2NjU3MjQvIj5YZXJpb3NHPC9hPg - - TRANSLATOR_PROFILE - mailto: http:// https:// Exp. https://github.com/Syping/ - https://github.com/Ganjalo/ - - - + A project for viewing Grand Theft Auto V Snapmatic<br/> Pictures and Savegames Un outil pour gérer les photos Snapmatic<br/> et les fichiers de sauvegarde de Grand Theft Auto V - + Copyright &copy; <a href="%1">%2</a> %3 Copyright &copy; <a href="%1">%2</a> %3 - + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> %1 est distribué sous license <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - Copyright &copy; <a href="%1">%2</a> %3<br/>%4 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - Copyright &copy; <a href="%1">%2</a> %3<br/>%4 est distribué sous license <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + + Release + Release - - A project for viewing and sync Grand Theft Auto V Snapmatic<br/> -Pictures and Savegames - Un outil pour gérer et synchroniser les photos Snapmatic<br/> -et les fichiers de sauvegarde de Grand Theft Auto V + + Release Candidate + Release Candidate + + + + Daily Build + Daily Build + + + + Developer + Developer + + + + Beta + Beta + + + + Alpha + Alpha + + + + + Custom + Personnalisé CrewDatabase - + + No Crew Aucun crew @@ -234,42 +175,442 @@ et les fichiers de sauvegarde de Grand Theft Auto V &Fermer + + ImageEditorDialog + + + + + + Snapmatic Image Editor + Éditeur d'images Snapmatic + + + + Overwrite Image... + Remplacer l'image... + + + + Apply changes + Appliquer les modifications + + + + &Overwrite + &Remplacer + + + + Discard changes + Annuler les modifications + + + + &Close + &Fermer + + + + + Patching of Snapmatic Image failed because of I/O Error + Échec du patch Snapmatic : I/O Error + + + + + Patching of Snapmatic Image failed because of Image Error + Échec du patch Snapmatic : Image Error + + ImportDialog - + Import... Importer... - - Settings - Paramètres + + + Ignore Aspect Ratio + Déverrouiller le ratio d'aspect - - &Keep Aspect Ratio - &Conserver le rapport d'aspect + + Avatar + Avatar - - &Ignore Aspect Ratio - &Ignorer le rapport d'aspect + + Picture + Image - - &Avatar - &Avatar + + Watermark + Filigrane - + + Background + Fond + + + + + + + Background Colour: <span style="color: %1">%1</span> + Couleur de fond : <span style="color: %1">%1</span> + + + + Select background colour + Choisir la couleur de fond + + + ... + ... + + + + Select background image + Choisir l'image de fond + + + + Remove background image + Supprimer l'image de fond + + + + + Background Image: %1 + Image de fond : %1 + + + X + X + + + + Crop to Aspect Ratio + Recadrage au ratio d'aspect + + + + Force Colour in Avatar Zone + Forcer la couleur dans la zone d'avatar + + + + Advanced + Avancé + + + + Resolution: + Résolution : + + + + Snapmatic resolution + Résolution Snapmatic + + + + Avoid compression and expand buffer instead, improves picture quality, but may break Snapmatic + Éviter la compression et étendre la mémoire tampon à la place, améliore la qualité de l'image mais peut casser Snapmatic + + + + Unlimited Buffer + Mémoire tampon illimitée + + + + Import as-is, don't change the picture at all, guaranteed to break Snapmatic unless you know what you doing + Importer tel quel,ne changez pas du tout l'image, garantie de casser Snapmatic à moins que vous ne sachiez ce que vous faites + + + + Import as-is + Importer tel quel + + + + Import options + Options d'importation + + + + &Options + &Options + + + + Import picture + Importer l'image + + + &OK &OK - + + Discard picture + Supprimer l'image + + + &Cancel - A&nnuler + &Annuler + + + + + + + Background Image: + Image de fond : + + + + &Import new Picture... + &Importer une nouvelle image... + + + + &Crop Picture... + &Rogner l'image... + + + + &Load Settings... + &Charger les paramètres... + + + + &Save Settings... + &Sauvegarder les paramètres... + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + Avatar personnalisé + + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + Image personnalisé + + + + Storage + Background Image: Storage + Stockage + + + + Crop Picture... + Rogner l'image... + + + + &Crop + &Rogner + + + + Crop Picture + Rogner l'image + + + + + Please import a new picture first + Veuillez d'abord importer une nouvelle image + + + + + Default + Default as Default Profile + Défaut + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + Profil %1 + + + + + Load Settings... + Charger les paramètres... + + + + + Please select your settings profile + Veuillez choisir votre profil de paramètres + + + + + Save Settings... + Sauvegarder les paramètres... + + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + Êtes-vous sûr d'utiliser une image carrée en dehors de la Zone d'Avatar ? +Si vous l'utilisez comme Avatar, l'image sera détachée ! + + + + + Snapmatic Avatar Zone + Zone d'avatar Snapmatic + + + + Select Colour... + Choisir une couleur... + + + + File + Background Image: File + Fichier + + + + JsonEditorDialog + + + Snapmatic JSON Editor + Éditeur Snapmatic JSON + + + + Apply changes + Appliquer les modifications + + + + &Save + &Sauvegarder + + + + Discard changes + Annuler les modifications + + + + &Close + &Fermer + + + + JSON Error + Erreur JSON + + + + MapLocationDialog + + + Snapmatic Map Viewer + Visionneuse de carte Snapmatic + + + + Close viewer + Fermer la visionneuse + + + + &Close + &Fermer + + + + Apply new position + Appliquer la nouvelle position + + + + &Apply + &Appliquer + + + + Revert old position + Revenir à l'ancienne position + + + + &Revert + &Revenir + + + + Select new position + Sélectionner la nouvelle position + + + + &Select + &Sélectionner + + + + Quit select position + Quitter la sélection de position + + + + &Done + &Terminer + + + + X: %1 +Y: %2 + X and Y position + X : %1 +Y : %2 @@ -290,19 +631,17 @@ et les fichiers de sauvegarde de Grand Theft Auto V Ouverture/Sélection du contenu - Open with Singleclick - Ouvrir avec un clic + Ouvrir avec un clic - + Open with Doubleclick Ouvrir avec un double-clic - Select with Singleclick - Sélectionner avec un clic + Sélectionner avec un clic @@ -407,71 +746,272 @@ et les fichiers de sauvegarde de Grand Theft Auto V + Game + Jeu + + - Language - Langue + Social Club Version + Version du Social Club - - Sync - Synchronisation + + + + + + + + + Found: %1 + Trouvé : %1 - - Sync is not implemented at current time - La synchronisation n'est pas encore implémentée + + + + + + + + + + + Language: %1 + Langue : %1 - + + Steam Version + Version de Steam + + + + Feedback + Feedback + + + + Participation + Participation + + + + + Participate in %1 User Statistics + Participer aux statistiques d'usage %1 + + + + Categories + Catégories + + + + Hardware, Application and OS Specification + Matériel, applications et OS + + + + System Language Configuration + Langage système + + + + Application Configuration + Configuration de l'application + + + + Other + Autres + + + + + + Participation ID: %1 + ID de participation : %1 + + + + &Copy + &Copier + + + + Language for Areas + Langage des Zones + + + + Style + Style + + + + Style: + Style : + + + + Font + Police + + + Always use Message Font (Windows 2003 and earlier) + Toujours utiliser la police Message (Windows 2003 et précédent) + + + + Interface + Interface + + + + Personal Usage Data + Données d'utilisation + + + + Language for Interface + Langage de l'interface + + + + + + + Current: %1 + Actuel : %1 + + + + Use Default Style (Restart) + Utiliser le style par défaut (redémarrage requis) + + + + Use Default Font (Restart) + Utiliser la police par défaut (redémarrage requis) + + + + Font: + Police : + + + + Apply changes + Appliquer les changements + + + &OK OK, Cancel, Apply &OK - + + Discard changes + Annuler les changements + + + &Cancel OK, Cancel, Apply &Annuler - - %1 (%2 if available) - System like PC System = %1, System Language like Deutsch = %2 - %1 (%2 si disponible) - - - + System - System like PC System + System in context of System default Système - - + + %1 (Game language) + Next closest language compared to the Game settings + %1 (Langue du jeu) + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + %1 (Langage proche de l'interface) + + + + + + Auto + Automatic language choice. + Automatique + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + %1 (Priorité de la langue) + + + %1 %1 %1 - - The new Custom Folder will initialize after you restart %1. - Le répertoire personnalisé sera actif au prochain lancement de %1. + + The new Custom Folder will initialise after you restart %1. + Le nouveau Dossier personnalisé sera initialisé au redémarrage de %1. - - The language change will take effect after you restart %1. - Le changement de langue sera actif au prochain lancement de %1. + + View %1 User Statistics Online + Voir les statistiques d'usage %1 en ligne - + + Not registered + Pas enregistré + + + + + + + Yes + Oui + + + + + No + Non + + + + + OS defined + Défini par le système d'exploitation + + + + + Steam defined + Défini par Steam + + + No Profile No Profile, as default Aucun profil - - - + + + Profile: %1 Profil : %1 @@ -480,113 +1020,158 @@ et les fichiers de sauvegarde de Grand Theft Auto V PictureDialog - %1 - Snapmatic Picture Viewer - %1 - Visionneuse de photo Snapmatic + Snapmatic Picture Viewer - %1 + Visionneuse de photo Snapmatic - %1 - - <span style=" font-weight:600;">Title: </span>%6<br/> -<span style=" font-weight:600;">Location: </span>%7 (%1, %2, %3)<br/> -<span style=" font-weight:600;">Players: </span>%4 (Crew %5)<br/> -<span style=" font-weight:600;">Created: </span>%8 - <span style=" font-weight:600;">Titre : </span>%6<br/> -<span style=" font-weight:600;">Emplacement : </span>%7 (%1, %2, %3)<br/> -<span style=" font-weight:600;">Joueurs : </span>%4 (Crew %5)<br/> -<span style=" font-weight:600;">Créé le : </span>%8 + + <span style="font-weight:600">Title: </span>%6<br/> +<span style="font-weight:600">Location: </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">Players: </span>%4 (Crew %5)<br/> +<span style="font-weight:600">Created: </span>%8 + <span style="font-weight:600">Titre : </span>%6<br/> +<span style="font-weight:600">Emplacement : </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">Joueurs : </span>%4 (Crew %5)<br/> +<span style="font-weight:600">Créé le : </span>%8 + + + + Manage picture + Gestion de l'image - Export picture - Exporter la photo + &Manage + &Gestion - - &Export - &Exporter - - - - Close + + Close viewer Fermer la visionneuse - + &Close &Fermer - - Export as GTA Snapmatic... + + Failed to export the picture because the system occurred a write failure + Échec de l'export de l'image : erreur d'écriture + + + + Failed to export the picture because the format detection failures + Échec de l'export de l'image : erreur de détection du format + + + + Failed to export the picture because the file can't be written + Échec de l'export de l'image : impossible d'écrire dans le fichier + + + + Failed to export the picture because of an unknown reason + Échec de l'export de l'image : erreur inconnue + + + + Export as Snapmatic... Exporter comme Snapmatic... - + GTA V Export (*.g5e) GTA V Export (*.g5e) - + GTA V Raw Export (*.auto) GTA V Export Brut (*.g5e) - + Snapmatic pictures (PGTA*) Fichiers GTA Snapmatic (PGTA*) - + + + + + + Export as Snapmatic + Exporter comme Snapmatic + + + Exported Snapmatic to "%1" because of using the .auto extension. Exporté comme "%1" avec l'utilisation de l'extension .auto. - All files (**) - Tous les fichiers (**) - - - - - - - - - Export as GTA Snapmatic - Exporter comme GTA Snapmatic - - - - + + Overwrite %1 with current Snapmatic picture? %1 existe déjà. Vous-vous le remplacer ? - - - Failed to overwrite %1 with current Snapmatic picture - Echec du remplacement de %1 + + Export as Picture... + Exporter comme image... - Failed to copy current Snapmatic picture - Echec de la copie + + JPEG Graphics (*.jpg *.jpeg) + JPEG Graphics (*.jpg *.jpeg) - - + + + + + + + Export as Picture + Exporter comme image + + + + No valid file is selected Fichier invalide - - Export as &JPG picture... - Exporter comme image &JPG... + + + Export as &Picture... + Exporter comme &image... - - Export as &GTA Snapmatic... - Exporter comme &GTA Snapmatic... + + + Export as &Snapmatic... + Exporter comme &Snapmatic... - + + + &Overwrite Image... + &Remplacer l'image... + + + + + &Edit Properties... + Modifier les &propriétés... + + + + + Open &Map Viewer... + Ouvrir la &Visionneuse de Carte... + + + Key 1 - Avatar Preview Mode Key 2 - Toggle Overlay Arrow Keys - Navigate @@ -595,81 +1180,117 @@ Touche 2 - Activer/désactiver l'overlay Touches fléchées - Naviguer - - + Snapmatic Picture Viewer Visionneuse de photo Snapmatic - - + Failed at %1 Echec de %1 - + + + No Crew + Aucun crew + + + + + + No Players + Aucun joueurs + + + Avatar Preview Mode Press 1 for Default View Mode Aperçu Avatar Appuyer sur 1 pour le mode par défaut - Avatar Preview Mode<br>Press A for Default View - Aperçu avatar<br>Appuyer sur A pour la vue par défaut - - - - - No player - Aucun joueur - - - - - No crew - Aucun crew - - - + Unknown Location Emplacement inconnu - - Export as JPG picture... - Exporter comme image JPG... - - - - + + Export Exporter - - JPEG picture (*.jpg) - Image JPEG (*.jpg) - - - + Portable Network Graphics (*.png) Portable Network Graphics (*.png) - - - - - Export as JPG picture - Exporter comme image JPG - - - - + Failed to export current Snapmatic picture Échec de l'export de la photo Snapmatic + + + + Open &JSON Editor... + Ouvrir l'éditeur &JSON... + + + + PlayerListDialog + + + Edit Players... + Modifier les joueurs... + + + + Available Players: + Joueurs disponibles : + + + + Selected Players: + Joueurs sélectionnés : + + + + &Apply + A&ppliquer + + + + &Cancel + A&nnuler + + + + Add Players... + Ajouter des joueurs... + + + + Failed to add more Players because the limit of Players are %1! + Échec de l'ajout de joueurs : la limite de %1 est atteinte ! + + + + + Add Player... + Ajouter un joueur... + + + + Enter Social Club Player ID + Entrer l'ID Social Club du joueur + + + + Failed to add Player %1 because Player %1 is already added! + Échec de l'ajout du joueur %1 car le joueur %1 est déjà ajouté ! + ProfileInterface @@ -684,99 +1305,125 @@ Appuyer sur 1 pour le mode par défaut Chargement du fichier %1 sur %2 - + %1 %2 %1 %2 - - Import exported file - Importer un profil + + Import file + Importer un fichier - + &Import... &Importer... - + Close profile Fermer - + &Close &Fermer - - - + + + Export file %1 of %2 files Copie du fichier %1 sur %2 - + Enabled pictures: %1 of %2 Photos activées : %1 sur %2 - + Loading... Chargement... - - + + Snapmatic Loader + Snapmatic Loader + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + <h4>Les Snapmatic suivants ont été répaés</h4>%1 + + + + + + + + + + + + + + + + + + + + + Import... Importer... - - - - - - - - - - - - + + + + + + Import Importer - All profile files (SGTA* PGTA*) - Fichiers de profil GTA (SGTA* PGTA*) - - - - + + Savegames files (SGTA*) Fichiers de sauvegarde GTA (SGTA*) - - + + Snapmatic pictures (PGTA*) Photos Snapmatic (PGTA*) - - + + + + All image files (%1) + Toutes les images (%1) + + + + + + All files (**) Tous les fichiers (**) - - + + Import file %1 of %2 files Importation du fichier %1 sur %2 - + Import failed with... %1 @@ -785,111 +1432,227 @@ Appuyer sur 1 pour le mode par défaut %1 - - - + + + No valid file is selected Fichier invalide - - + + Importable files (%1) + Fichiers importables (%1) + + + + Failed to read Snapmatic picture Impossible d'ouvrir la photo Snapmatic - - + + Failed to read Savegame file Impossible de lire le fichier de sauvegarde - - Can't import %1 because of not valid file format - Impossible d'importer %1, format invalide + + + + Can't import %1 because file can't be open + Impossible d'importer %1, le fichier ne peut pas être ouvert - Failed to import the Snapmatic picture, file not begin with PGTA - Impossible d'importer la photo Snapmatic,nom de fichier incorrect (PGTA*) + + + + Can't import %1 because file can't be parsed properly + Impossible d'importer %1, le fichier ne peut pas être parsé correctement - - Importable files (*.g5e *.jpg *.png SGTA* PGTA*) - Fichiers importables (*.g5e *.jpg *.png SGTA* PGTA*) + + Can't import %1 because file format can't be detected + Impossible d'importer %1, le format du fichier n'est pas détecté - - All image files (*.jpg *.png) - Tous les fichiers image (*.jpg *.png) - - - + Failed to import the Snapmatic picture, file not begin with PGTA or end with .g5e Impossible d'importer la photo Snapmatic,nom de fichier incorrect (PGTA*, *.g5e) - - Failed to import the Snapmatic picture, the picture is already in the game - Impossible d'importer la photo Snapmatic, un fichier du même nom existe déjà - - - + Failed to import the Snapmatic picture, can't copy the file into profile Impossible d'importer la photo Snapmatic, impossible de copier le fichier dans le profil - + Failed to import the Savegame, can't copy the file into profile Impossible d'importer la sauvegarde, impossible de copier le fichier dans le profil - + Failed to import the Savegame, no Savegame slot is left Impossible d'importer la sauvegarde, aucun emplacement libre - - - - - Export selected - Exporter la sélection - - - - + + JPG pictures and GTA Snapmatic Images JPG et GTA Snapmatic - - + + JPG pictures only Images JPG seulement - - + + GTA Snapmatic only GTA Snapmatic seulement - + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: %1Exporter les photos Snapmatic%2<br><br>Les fichiers JPG permettent d'ouvrir les photos avec une visionneuse d'images<br>Les GTA Snapmatic permettent d'importer les photos dans le jeu<br><br>Exporter comme : - + + + + + Export selected... Exporter la sélection... - - Initializing export... + + Initialising export... Initialisation de l'export... - + + + Qualify as Avatar + Qualifier comme Avatar + + + + + + + + + No Snapmatic pictures are selected + Aucun Snapmatic sélectionné + + + + + + + Patch selected... + Patcher la sélection... + + + + + + + + + + + Patch file %1 of %2 files + Patch du fichier %1 sur %2 + + + + + + + + + %1 failed with... + +%2 + Action failed with... + %1 a échoué avec... + +%2 + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + Échec de la supression des Snapmatic et/ou des fichiers de sauvegarde sélectionnés + + + + Prepare Content for Import... + Préparation du contenu pour l'import... + + + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + Un Snapmatic existe déjà avec le uid %1, voulez-vous assigner à votre import un nouvel uid et timestamp ? + + + + Qualify + %1 failed with... + Qualifier + + + + + Change Players... + Modifier les joueurs... + + + + Change Players + %1 failed with... + Modifier les joueurs + + + + + + Change Crew... + Modifier le Crew... + + + + Failed to enter a valid Snapmatic Crew ID + Snapmatic Crew ID invalide + + + + Change Crew + %1 failed with... + Changer le Crew + + + + + + Change Title... + Changer le titre... + + + + Failed to enter a valid Snapmatic title + Titre Snapmatic invalide + + + + Change Title + %1 failed with... + Changer le titre + + + Export failed with... %1 @@ -898,36 +1661,31 @@ Appuyer sur 1 pour le mode par défaut %1 - - + + No Snapmatic pictures or Savegames files are selected Aucun fichier de sauvegarde ou photo Snapmatic sélectionné - - - + + + Remove selected Supprimer la sélection - + You really want remove the selected Snapmatic picutres and Savegame files? Supprimer la sélection ? - - Failed at remove the complete selected Snapmatic pictures and/or Savegame files - Impossible de supprimer la sélection - - - + All profile files (*.g5e SGTA* PGTA*) Tous les fichiers de profil (*.g5e SGTA* PGTA*) - - + + GTA V Export (*.g5e) GTA V Export (*.g5e) @@ -935,41 +1693,43 @@ Appuyer sur 1 pour le mode par défaut QApplication - Font - Police + Police - Selected Font: %1 - Police sélectionnée : %1 + Police sélectionnée : %1 + + + <h4>Welcome to %1!</h4>You want to configure %1 before you start using it? + <h4>Bienvenue sur %1!</h4>Voulez-vous configurer %1 avant de l'utiliser t? SavegameDialog - + Savegame Viewer Gestionnaire de sauvegardes - - <span style=" font-weight:600;">Savegame</span><br><br>%1 - <span style=" font-weight:600;">Sauvegarde</span><br><br>%1 + + <span style="font-weight:600">Savegame</span><br><br>%1 + <span style="font-weight:600">Sauvegarde</span><br><br>%1 - + &Export &Exporter - + &Close &Fermer - + Failed at %1 Échec de %1 @@ -982,113 +1742,115 @@ Appuyer sur 1 pour le mode par défaut Sauvegarde - + SAVE %3 - %1<br>%2 SAUVEGARDE %3 - %1<br>%2 - + View savegame Voir la sauvegarde - + View Voir - + Copy savegame Copier la sauvegarde - - + + Export Exporter - - - + Delete savegame Supprimer la sauvegarde - + Delete Supprimer - + + + &Export &Exporter - + Savegame files (SGTA*) Fichiers de sauvegarde GTA (SGTA*) - + All files (**) Tous les fichiers (**) - - - + + + Export Savegame Exporter la sauvegarde - + Overwrite %1 with current Savegame? Remplacer %1 ? - + Failed to overwrite %1 with current Savegame Impossible de remplacer %1 - + Failed to export current Savegame Impossible d'exporter la sauvegarde - + No valid file is selected Fichier invalide - + Export Savegame... Exporter la sauvegarde... - + + AUTOSAVE - %1 %2 SAUVEGARDE AUTO - %1 %2 - + + SAVE %3 - %1 %2 SAUVEGARDE %3 - %1 %2 - - + + WRONG FORMAT Format invalide - + UNKNOWN Inconnu @@ -1098,39 +1860,49 @@ Appuyer sur 1 pour le mode par défaut Supprimer %1 ? - + + + Delete Savegame + Supprimer la sauvegarde + + + Failed at deleting %1 from your savegames Impossible de supprimer %1 - + &View &Voir - + + + &Remove &Supprimer - - + + &Select &Sélectionner - + + &Deselect &Déselectionner - - + + Select &All Sélectionner to&ut - + + &Deselect All &Déselectionner tout @@ -1140,7 +1912,13 @@ Appuyer sur 1 pour le mode par défaut - + + + + + + + Snapmatic Properties Propriétés Snapmatic @@ -1169,10 +1947,6 @@ Appuyer sur 1 pour le mode par défaut Mugshot Photo d'identité - - Custom - Personnalisé - Director @@ -1184,7 +1958,8 @@ Appuyer sur 1 pour le mode par défaut Meme - + + Snapmatic Title Titre Snapmatic @@ -1194,86 +1969,127 @@ Appuyer sur 1 pour le mode par défaut Valeurs Snapmatic - - + Crew: %1 (%2) Crew : %1 (%2) - - + Title: %1 (%2) Titre : %1 (%2) - - - + + Players: %1 (%2) + Multiple Player are inserted here + Joueurs : %1 (%2) + + + + Player: %1 (%2) + One Player is inserted here + Joueur : %1 (%2) + + + + Appropriate: %1 Valide : %1 - + Extras Extras - + Qualify as Avatar automatically at apply Qualifier comme Avatar - + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture Qualifier comme Avatar permet d'utiliser cette image en tant que photo de profil sur le Social Club - - &Apply - A&ppliquer + + Apply changes + Appliquer les modifications - + + &Apply + &Appliquer + + + + Discard changes + Annuler les modifications + + + &Cancel A&nnuler - - + + + Edit Éditer - + Yes Yes, should work fine Oui, devrait fonctionner Oui - + No No, could lead to issues Non, pourrait causer des erreurs Non - + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + <h4>Modifications détectées</h4>Voulez-vous sauvegarder le contenu JSON avant de quitter ? + + + + Patching of Snapmatic Properties failed because of %1 + Patch des propriétés Snapmatic échoué : %1 + + + + Patching of Snapmatic Properties failed because of JSON Error + Patch des propriétés Snapmatic échoué : erreur JSON + + + + + + Patching of Snapmatic Properties failed because of I/O Error La modification des propriétés Snapmatic a échoué : erreur d'entrée/sortie - + + New Snapmatic title: Nouveau titre Snapmatic : - + + Snapmatic Crew Crew Snapmatic - + + New Snapmatic crew: Nouveau crew Snapmatic : @@ -1281,10 +2097,69 @@ Appuyer sur 1 pour le mode par défaut SnapmaticPicture - + PHOTO - %1 PHOTO - %1 + + + open file %1 + ouverture du fichier %1 + + + + header not exists + les headers n'existent pas + + + + header is malformed + les headers sont incorrects + + + + picture not exists (%1) + l'image n'existe pas (%1) + + + + JSON not exists (%1) + le JSON n'existe pas (%1) + + + + title not exists (%1) + le titre n'existe pas (%1) + + + + description not exists (%1) + la description n'existe pas (%1) + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + lecture du fichier %1 : %2 + + + + + JSON is incomplete and malformed + JSON incomplet ou incorrect + + + + + JSON is incomplete + JSON incomplet + + + + + JSON is malformed + JSON incorrect + SnapmaticWidget @@ -1294,136 +2169,158 @@ Appuyer sur 1 pour le mode par défaut Snapmatic - + PHOTO - 00/00/00 00:00:00 Photo - 00/00/00 00:00:00 - + View picture Voir la photo - + View Voir - + Copy picture Copier la photo - + Copy Copier - + Export picture Exporter la photo - + Export Exporter - - - + + + Delete picture Supprimer la photo - + Delete Supprimer - + Are you sure to delete %1 from your Snapmatic pictures? Supprimer %1 ? - + Failed at deleting %1 from your Snapmatic pictures Impossible de supprimer %1 - + + Failed to hide %1 In-game from your Snapmatic pictures + %1 n'a pas pu être rendu invisible en jeu + + + + Failed to show %1 In-game from your Snapmatic pictures + %1 n'a pas pu être rendu visible en jeu + + + + + Edi&t Édi&ter - + + + Show &In-game &Visible en jeu - + + + Hide &In-game &Invisible en jeu - - &Edit Properties... - Modifier les &propriétés... - - - + &Export &Exporter - - Export as &JPG picture... - Exporter comme image &JPG... - - - - Export as &GTA Snapmatic... - Exporter comme &GTA Snapmatic... - - - + &View &Voir - + &Remove S&upprimer - - + + &Select &Sélectionner - + + &Deselect &Déselectionner - - + + Select &All Sélectionner &tout - + + &Deselect All &Déselectionner tout - UserInterface + TelemetryDialog - gta5sync - %1 - gta5sync - %1 + + You want help %1 to improve in the future by including personal usage data in your submission? + Voulez-vous aider au développement de %1 en transmettant vos données d'utilisation ? + + + %1 User Statistics + Statistiques utilisateurs %1 + + + + Yes, I want include personal usage data. + Oui, je veux partager mes données d'utilisation. + + + + &OK + &OK + + + + UserInterface Select profile @@ -1436,217 +2333,245 @@ Appuyer sur 1 pour le mode par défaut + Reload profile overview + Recharger la vue du profil + + + &Reload &Rafraîchir - + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + Fermer %1 + + + + + + &Close Fer&mer - + &File &Fichier - + &Help Ai&de - + &Edit &Éditer - + &Profile &Profil - &About gta5sync - &À propos de gta5sync - - - - Ctrl+P - Ctrl+P - - - + &Exit &Quitter - + Exit Quitter - - Ctrl+Q - Ctrl+Q - - - + Close &Profile Fermer le &profil - - Ctrl+End - Ctrl + Fin - - - + &Settings Paramètre&s - - Ctrl+S - Ctrl+S - - - + Select &All Sélectionner &tout - - Ctrl+A - Ctrl+A - - - + &Deselect All &Désélectionner tout - - Ctrl+D - Ctrl+D - - - + &Export selected... &Exporter la sélection... - - Ctrl+E - Ctrl+E - - - + &Remove selected &Supprimer la sélection - - Ctrl+Del - Ctrl+Del - - - + &Import files... &Importer... - - Ctrl+I - Ctrl+I - - - + &Open File... &Ouvrir... - - Ctrl+O - Ctrl+O - - - - + + Select &GTA V Folder... Modifier l'emplacement de &GTA V... - - - - + + + + Select GTA V Folder... Modifier l'emplacement de GTA V... - - Ctrl+G - Ctrl+G - - - + Show In-gam&e Rendre visible &en jeu - - Shift+E - Shift+E + + + + Change &Players... + Modifier les &joueurs... - + + Selection &mass tools + Outils de sélectionne en &masse + + + + + + Change &Title... + Changer le &titre... + + + + + + Change &Crew... + Changer le &Crew... + + + + + + &Qualify as Avatar + &Qualifier comme Avatar + + + &Selection visibility &Visibilité de la sélection - + Hi&de In-game Ren&dre invisible en jeu - - - Shift+D - Shift+D - - + %2 - %1 %2 - %1 - - + + + &About %1 &À propos de %1 - - + + + Select Profile Sélectionner un profil - + + + &Donate + &Don + + + + Donate + Don + + + + Donation methods + Méthodes de don + + + View + Voir + + + Copy + Copier + + + + &Copy + &Copier + + + Open File... Ouvrir... - - - - + + + + Open File Ouvrir - + Can't open %1 because of not valid file format Impossible d'ouvrir %1, format invalide + + + %1 - Messages + %1 - Nouvelles + + + + + + Show In-game + Visible en jeu + + + + + + Hide In-game + Invisible en jeu + diff --git a/res/gta5sync_ko.qm b/res/gta5sync_ko.qm new file mode 100644 index 0000000..b100265 Binary files /dev/null and b/res/gta5sync_ko.qm differ diff --git a/res/gta5sync_ko.ts b/res/gta5sync_ko.ts new file mode 100644 index 0000000..d2ebaa3 --- /dev/null +++ b/res/gta5sync_ko.ts @@ -0,0 +1,2603 @@ + + + + + AboutDialog + + + About %1 + %1 정보 + + + + <span style="font-weight:600">%1</span><br/> +<br/> +%2<br/> +<br/> +Version %3<br/> +Created on %4<br/> +Built with Qt %5<br/> +Running with Qt %6<br/> +<br/> +%7 + <span style="font-weight:600">%1</span><br/> +<br/> +%2<br/> +<br/> +버전 %3<br/> +생성됨 %4<br/> +Qt로 제작 %5<br/> +Qt로 실행 %6<br/> +<br/> +%7 + + + + &Close + 닫기(&C) + + + + Translated by %1 + Translated by translator, example Translated by Syping + 번역 %1 + + + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + 앙시모사우루스,https://steamcommunity.com/profiles/76561198166105984/ + + + + A project for viewing Grand Theft Auto V Snapmatic<br/> +Pictures and Savegames + 이 프로그램은 GTA 5 스냅매틱을 수정하고 보기 위한 프로젝트입니다.<br/> +이미지 뷰어 및 세이브 파일 관리 지원 + + + + Copyright &copy; <a href="%1">%2</a> %3 + 저작권 &copy; <a href="%1">%2</a> %3 + + + + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + %1는 <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> 에 따라 라이센스가 부여됩니다 + + + + Release + 릴리즈 + + + + Release Candidate + 릴리즈 후보 + + + + Daily Build + 일일 빌드 + + + + Developer + 개발자 + + + + Beta + 베타 + + + + Alpha + 알파 + + + + + Custom + 사용자 지정 + + + + CrewDatabase + + + + No Crew + 조직 없음 + + + + ExportDialog + + + Dialog + 다이얼로그 + + + + Export Format + 내보낼 형식 + + + + &JPEG/PNG format + JPEG/PNG 형식(&J) + + + + GTA &Snapmatic format + GTA 스냅매틱 형식(&S) + + + + Export Size + 내보낼 크기 + + + + Default &Size + 기본 크기(&S) + + + + &Desktop Size + 바탕화면 크기(&D) + + + + &Custom Size + 사용자 정의 크기(&C) + + + + Custom Size: + 사용자 정의 크기: + + + + x + x + + + + &Export + 내보내기(&E) + + + + &Close + 닫기(&C) + + + + ImageEditorDialog + + + Overwrite Image... + 이미지 덮어쓰기 + + + + Apply changes + 변경 사항 적용 + + + + &Overwrite + 덮어쓰기(&O) + + + + Discard changes + 변경 사항 무시 + + + + &Close + 닫기(&C) + + + + + + + Snapmatic Image Editor + 스냅매틱 이미지 편집기 + + + + + Patching of Snapmatic Image failed because of I/O Error + I/O 오류로 인해 스냅매틱 이미지를 패치하지 못했습니다. + + + + + Patching of Snapmatic Image failed because of Image Error + 이미지 오류로 인해 스냅매틱 이미지를 패치하지 못했습니다. + + + + ImportDialog + + + Import... + 가져오기 + + + + Picture + 이미지 + + + + Avatar + 아바타 + + + + + Ignore Aspect Ratio + 화면 비율 무시 + + + + Watermark + 워터마크 + + + Force Borderless + 강제 테두리 없는 창 + + + + Background + 배경 + + + + + + + Background Colour: <span style="color: %1">%1</span> + 배경 색상: <span style="color: %1">%1</span> + + + + Select background colour + 배경 색상 선택 + + + ... + ... + + + + + + + Background Image: + 배경 이미지: + + + + Select background image + 배경 이미지 선택 + + + + Remove background image + 배경 이미지 제거 + + + X + X + + + + Crop to Aspect Ratio + 원본 비율로 자르기 + + + + Force Colour in Avatar Zone + 아바타 구역에 색상을 적용합니다 + + + + Advanced + 고급 + + + + Resolution: + 해상도: + + + + Snapmatic resolution + 스냅매틱 해상도 + + + + Avoid compression and expand buffer instead, improves picture quality, but may break Snapmatic + 압축하지 않고 버퍼를 확장하여 화질을 향상시키지만 스냅매틱이 손상될 수 있습니다. + + + + Unlimited Buffer + 버퍼 제한 없음 + + + + Import as-is, don't change the picture at all, guaranteed to break Snapmatic unless you know what you doing + 원본 그대로 가져오기 기능은 이미지를 건들지 않지만 이 기능으로 인해 당신의 스냅매틱이 손상될 수 있습니다. + + + + Import as-is + 원본 그대로 가져오기 + + + + Import options + 가져오기 옵션 + + + + &Options + 옵션(&O) + + + + Import picture + 사진 가져오기 + + + + &OK + 확인(&O) + + + + Discard picture + 사진 삭제 + + + + &Cancel + 취소(&C) + + + + &Import new Picture... + 새로운 사진 가져오기(&I) + + + + &Crop Picture... + 사진 자르기(&C) + + + + &Load Settings... + 설정 불러오기(&L) + + + + &Save Settings... + 설정 저장(&S) + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + 소셜클럽의 사용자 지정 아바타 설명입니다. 특수 문자를 사용하지 마십시오! + 사용자 지정 아바타 + + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + 소셜클럽의 사용자 지정 그림 설명입니다. 특수 문자를 사용하지 마십시오! + 사용자 지정 사진 + + + + + Background Image: %1 + 배경 이미지: %1 + + + + Storage + Background Image: Storage + 배경 이미지: 저장됨 + 저장됨 + + + + Crop Picture... + 사진 자르기 + + + + &Crop + 자르기(&C) + + + + Crop Picture + 사진 자르기 + + + + + Load Settings... + 설정 불러오기 + + + + + Please import a new picture first + 먼저 새 이미지를 가져오세요 + + + + + Default + Default as Default Profile + 기본 프로필로 기본 설정 + 기본 + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + %1을 프로필 1로 지정합니다. + 프로필 %1 + + + + + Please select your settings profile + 설정 프로필을 선택하세요 + + + + + Save Settings... + 설정 저장 + + + + + Snapmatic Avatar Zone + 스냅매틱 아바타 영역 + + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + 아바타 구역 밖에서 네모난 이미지를 정말 사용합니까? +아바타로 사용하려는 경우 이미지가 분리됩니다! + + + + Select Colour... + 색상 선택 + + + + File + Background Image: File + 배경 이미지: 파일 + 파일 + + + + JsonEditorDialog + + + Snapmatic JSON Editor + 스냅매틱 JSON 편집기 + + + + Apply changes + 변경 사항 적용 + + + + &Save + 저장(&S) + + + + Discard changes + 변경 사항 무시 + + + + &Close + 닫기(&C) + + + + JSON Error + JSON 오류 + + + + MapLocationDialog + + + Snapmatic Map Viewer + 스냅매틱 지도 뷰어 + + + + Close viewer + 뷰어 닫기 + + + + &Close + 닫기(&C) + + + + Apply new position + 새 위치 적용 + + + + &Apply + 적용(&A) + + + + Revert old position + 이전 위치로 되돌리기 + + + + &Revert + 되돌리기(&R) + + + + Select new position + 새 위치 선택 + + + + &Select + 선택(&S) + + + + Quit select position + 선택 위치 종료 + + + + &Done + 완료(&D) + + + + X: %1 +Y: %2 + X and Y position + X 및 Y 위치 + X: %1 +Y: %2 + + + + OptionsDialog + + + %1 - Settings + %1 - 설정 + + + + Profiles + 프로필 + + + + Content Open/Select Mode + 컨텐츠 열기/선택 모드 + + + Open with Singleclick + 한 번 클릭으로 열기 + + + + Open with Doubleclick + 두 번 클릭으로 열기 + + + Select with Singleclick + 한 번 클릭으로 선택 + + + + Default Profile + 기본 프로필 + + + + Custom GTA V Folder + 사용자 지정 GTA 5 폴더 + + + + Force using Custom Folder + 사용자 지정 폴더를 강제로 사용합니다. + + + + ... + ... + + + + Pictures + 이미지 + + + + Export Size + 내보낼 크기 + + + + Default: %1x%2 + 기본: %1x%2 + + + + Screen Resolution: %1x%2 + 화면 해상도: %1x%2 + + + + + Custom Size: + 사용자 지정 크기: + + + + x + x + + + + Ignore Aspect Ratio + 화면 비율 무시 + + + + Export Quality + 내보낼 품질 + + + + Enable Custom Quality + 사용자 지정 품질 사용 + + + + Quality: + 품질: + + + + %1% + %1% + + + + Picture Viewer + 이미지 뷰어 + + + + Enable Navigation Bar + 탐색바 사용 + + + + Players + 플레이어 + + + + ID + 아이디 + + + + Name + 이름 + + + + Game + 게임 + + + + Social Club Version + 소셜 클럽 버전 + + + + + + + + + + + Found: %1 + 찾음: %1 + + + + + + + + + + + + + Language: %1 + 언어: %1 + + + + Steam Version + 스팀 버전 + + + + Feedback + 피드백 + + + + Participation + 참가 + + + + + Participate in %1 User Statistics + 사용자 통계 참가 %1 + + + + Categories + 카테고리 + + + + Hardware, Application and OS Specification + 하드웨어, 응용 프로그램 및 OS 사양 + + + + System Language Configuration + 시스템 언어 설정 + + + + Application Configuration + 응용 프로그램 설정 + + + + Personal Usage Data + 개인 사용 데이터 + + + + Other + 그 외 + + + + + + Participation ID: %1 + 참여 아이디: %1 + + + + &Copy + 복사(&C) + + + + Interface + 인터페이스 + + + + Language for Interface + 인터페이스 언어 + + + + + + + Current: %1 + 현재: %1 + + + + Language for Areas + 지역 언어 + + + + Style + 스타일 + + + + Use Default Style (Restart) + 기본 스타일을 사용합니다 (재시작 필요) + + + + Style: + 스타일: + + + + Font + 폰트 + + + + Use Default Font (Restart) + 기본 폰트 사용 (재시작 필요) + + + + Font: + 폰트: + + + Always use Message Font (Windows 2003 and earlier) + 항상 메시지 글꼴을 사용합니다.(Windows 2003 및 이전 버전) + + + + Apply changes + 변경 사항 적용 + + + + &OK + OK, Cancel, Apply + 확인, 취소, 적용 + 확인(&O) + + + + Discard changes + 변경 사항 취소 + + + + &Cancel + OK, Cancel, Apply + 확인, 취소, 적용 + 취소(&C) + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + %1 (우선 순위) + + + + System + System in context of System default + 시스템 + + + + %1 (Game language) + Next closest language compared to the Game settings + 게임 설정과 가장 가까운 언어 + %1 (게임 언어) + + + + + + Auto + Automatic language choice. + 언어 자동 선택 + 자동 + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + 인터페이스와 가장 가까운 언어 + %1 (인터페이스와 가까운 언어) + + + + %1 + %1 + %1 + %1 + + + + The new Custom Folder will initialise after you restart %1. + 다시 시작한 후 새 사용자 지정 폴더가 초기화됩니다. %1. + + + + No Profile + No Profile, as default + 프로필 없음 (기본값) + 프로필 없음 + + + + + + Profile: %1 + 프로필: %1 + + + + View %1 User Statistics Online + 온라인 %1 사용자 통계 보기 + + + + Not registered + 등록되지 않았습니다. + + + + + + + Yes + + + + + + No + 아니요 + + + + + OS defined + OS 정의 + + + + + Steam defined + 스팀 정의 + + + + PictureDialog + + + Snapmatic Picture Viewer - %1 + 스냅매틱 이미지 뷰어 - %1 + + + + <span style="font-weight:600">Title: </span>%6<br/> +<span style="font-weight:600">Location: </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">Players: </span>%4 (Crew %5)<br/> +<span style="font-weight:600">Created: </span>%8 + <span style="font-weight:600">제목: </span>%6<br/> +<span style="font-weight:600">위치: </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">플레이어: </span>%4 (Crew %5)<br/> +<span style="font-weight:600">생성 날짜: </span>%8 + + + + Manage picture + 이미지 관리 + + + + &Manage + 관리(&M) + + + + Close viewer + 뷰어 닫기 + + + + &Close + 닫기(&C) + + + + + Export as &Picture... + 내 PC에 이미지로 내보내기(&P) + + + + + Export as &Snapmatic... + 내 PC에 스냅매틱으로 내보내기(&S) + + + + + &Edit Properties... + 속성 편집(&E) + + + + + &Overwrite Image... + 이미지 덮어쓰기(&O) + + + + + Open &Map Viewer... + 지도 뷰어 열기(&M) + + + + + Open &JSON Editor... + JSON 편집기 열기(&J) + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + 숫자 1 - 아바타 미리보기 모드 +숫자 2 - 오버레이 전환 +화살표키 - 이동 + + + + Snapmatic Picture Viewer + 스냅매틱 이미지 뷰어 + + + + Failed at %1 + %1에서 실패했습니다. + + + + + + No Players + 플레이어 없음 + + + + + No Crew + 조직 없음 + + + + Unknown Location + 알 수 없는 위치 + + + + Avatar Preview Mode +Press 1 for Default View + 아바타 미리 보기 모드입니다. +돌아가려면 숫자 1을 누릅니다. + + + + Export as Picture... + 내 PC에 이미지로 내보내기 + + + + + Export + 내보내기 + + + + JPEG Graphics (*.jpg *.jpeg) + JPEG Graphics (*.jpg *.jpeg) + + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + + + + + + + Export as Picture + 내 PC에 이미지로 내보내기 + + + + + Overwrite %1 with current Snapmatic picture? + %1을 현재 스냅매틱 이미지로 덮어쓰시겠습니까? + + + + Failed to export the picture because the system occurred a write failure + 시스템에서 쓰기 오류가 발생하여 이미지를 내보내지 못했습니다. + + + + Failed to export the picture because the format detection failures + 확장자 감지에 실패하여 이미지를 내보내지 못했습니다. + + + + Failed to export the picture because the file can't be written + 파일을 쓸 수 없으므로 이미지를 내보내지 못했습니다. + + + + Failed to export the picture because of an unknown reason + 알 수 없는 이유로 이미지를 내보내지 못했습니다. + + + + + No valid file is selected + 올바른 파일이 선택되지 않았습니다. + + + + Export as Snapmatic... + 내 PC에 스냅매틱으로 내보내기 + + + + GTA V Export (*.g5e) + GTA V Export (*.g5e) + + + + GTA V Raw Export (*.auto) + GTA V Raw Export (*.auto) + + + + Snapmatic pictures (PGTA*) + Snapmatic pictures (PGTA*) + + + + + + + + Export as Snapmatic + 내 PC에 스냅매틱으로 내보내기 + + + + + Failed to export current Snapmatic picture + 현재 스냅매틱 이미지를 내보내지 못했습니다. + + + + Exported Snapmatic to "%1" because of using the .auto extension. + .auto 확장자를 사용하기 때문에 스냅매틱을 "%1"로 내보냈습니다. + + + + PlayerListDialog + + + Edit Players... + 플레이어 편집 + + + + Available Players: + 사용 가능한 플레이어: + + + + Selected Players: + 선택된 플레이어: + + + + &Apply + 적용(&A) + + + + &Cancel + 취소(&C) + + + + Add Players... + 플레이어 추가 + + + + Failed to add more Players because the limit of Players are %1! + 플레이어의 제한이 %1이므로 플레이어를 추가하지 못했습니다! + + + + + Add Player... + 플레이어 추가 + + + + Enter Social Club Player ID + 소셜 클럽 플레이어 아이디 입력 + + + + Failed to add Player %1 because Player %1 is already added! + %1 플레이어가 이미 추가되어 %1 플레이어를 추가하지 못했습니다! + + + + ProfileInterface + + + Profile Interface + 프로필 인터페이스 + + + + Loading file %1 of %2 files + %2 파일의 %1 파일을 불러오는 중입니다. + + + + %1 %2 + %1 %2 + + + + Import file + 파일 가져오기 + + + + &Import... + 가져오기(&I) + + + + Close profile + 프로필 닫기 + + + + &Close + 닫기(&C) + + + + + + Export file %1 of %2 files + %2 파일 중 %1 파일을 내보냅니다. + + + + + + + + + + + + + + + + + + + + + + Import... + 가져오기 + + + + + + + + + Import + 가져오기 + + + + + + All image files (%1) + 모든 이미지 파일 (%1) + + + + + + + All files (**) + 모든 파일 (**) + + + + + + Can't import %1 because file can't be open + 파일을 열 수 없으므로 %1을 가져올 수 없습니다. + + + + + + Can't import %1 because file can't be parsed properly + 파일을 구문 분석할 수 없으므로 %1을 가져올 수 없습니다. + + + + Enabled pictures: %1 of %2 + 활성화된 이미지: %2의 %1 + + + + Loading... + 불러오는 중... + + + + Snapmatic Loader + 스냅매틱 불러오기 + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + <h4>다음 스냅매틱 이미지를 복구했습니다. </h4>%1 + + + + Importable files (%1) + 가져올 수 있는 파일 (%1) + + + + + GTA V Export (*.g5e) + GTA V로 내보내기 (*.g5e) + + + + + Savegames files (SGTA*) + 세이브 파일 (SGTA*) + + + + + Snapmatic pictures (PGTA*) + 스냅매틱 이미지 (PGTA*) + + + + + + No valid file is selected + 올바른 파일이 선택되지 않았습니다. + + + + + Import file %1 of %2 files + %2 파일 중 %1 파일을 가져옵니다. + + + + Import failed with... + +%1 + 가져오기에 실패했습니다... + +%1 + + + + + Failed to read Snapmatic picture + 스냅매틱 이미지를 읽지 못했습니다. + + + + + Failed to read Savegame file + 세이브 파일을 읽지 못했습니다. + + + + Can't import %1 because file format can't be detected + 파일 형식을 검색할 수 없으므로 %1을 가져올 수 없습니다. + + + + Prepare Content for Import... + 가져올 컨텐츠를 준비합니다. + + + + Failed to import the Snapmatic picture, file not begin with PGTA or end with .g5e + 스냅매틱 이미지를 가져오지 못했습니다. 파일이 PGTA로 시작되거나 .g5e로 끝나지 않습니다. + + + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + uid %1이(가) 있는 스냅매틱 이미지가 이미 있습니다. 가져오기를 새 uid 및 타임스탬프를 할당하시겠습니까? + + + + Failed to import the Snapmatic picture, can't copy the file into profile + 스냅매틱 이미지를 가져오지 못했습니다. 파일을 프로필에 복사할 수 없습니다. + + + + Failed to import the Savegame, can't copy the file into profile + 게임 저장 파일을 가져오지 못했습니다. 파일을 프로필에 복사할 수 없습니다. + + + + Failed to import the Savegame, no Savegame slot is left + 게임 저장 파일을 가져오지 못했습니다. 게임 저장 슬롯이 남아 있지 않습니다. + + + + + + + + Export selected... + 내보내기를 선택했습니다. + + + + + JPG pictures and GTA Snapmatic + JPG 이미지 및 GTA 스냅매틱 + + + + + JPG pictures only + JPG 이미지만 + + + + + GTA Snapmatic only + GTA 스냅매틱만 + + + + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: + %1 스냅매틱 이미지 내보내기를 시작합니다.%2 <br><br>JPG 이미지를 사용하면 이미지 뷰어로 파일을 열 수 있습니다.<br>GTA 스냅매틱을 사용하면 다음과 같이 이미지를 게임으로 가져올 수 있습니다. + + + + Initialising export... + 내보내기를 초기화하는 중... + + + + Export failed with... + +%1 + 내보내지 못했습니다... + +%1 + + + + + No Snapmatic pictures or Savegames files are selected + 스냅매틱 이미지 또는 세이브 파일이 선택되지 않았습니다. + + + + + + Remove selected + 선택한 항목 삭제 + + + + You really want remove the selected Snapmatic picutres and Savegame files? + 선택한 스냅매틱 이미지 및 세이브 파일을 삭제하시겠습니까? + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + 선택한 모든 스냅매틱 이미지 및 세이브 파일을 삭제하지 못했습니다. + + + + + + + + + No Snapmatic pictures are selected + 스냅매틱 이미지가 선택되지 않았습니다. + + + + + + + + + %1 failed with... + +%2 + Action failed with... + 작업 실패... + %1이(가) 실패했습니다. + +%2 + + + + + Qualify as Avatar + 아바타 자격 부여 + + + + + + + Patch selected... + 패치가 선택됨... + + + + + + + + + + + Patch file %1 of %2 files + %2 파일의 %1 패치 파일입니다. + + + + Qualify + %1 failed with... + %1이(가) 실패한 경우... + 자격 부여 + + + + + Change Players... + 플레이어 변경 + + + + Change Players + %1 failed with... + %1이(가) 실패한 경우... + 플레이어 변경 + + + + + + Change Crew... + 조직 변경 + + + + Failed to enter a valid Snapmatic Crew ID + 올바른 스냅매틱 조직 아이디를 입력하지 못했습니다. + + + + Change Crew + %1 failed with... + %1이(가) 실패한 경우... + 조직 변경 + + + + + + Change Title... + 제목 변경 + + + + Failed to enter a valid Snapmatic title + 올바른 스냅매틱 제목을 입력하지 않았습니다. + + + + Change Title + %1 failed with... + %1이(가) 실패한 경우... + 제목 변경 + + + + All profile files (*.g5e SGTA* PGTA*) + 모든 프로필 파일 (*.g5e SGTA* PGTA*) + + + + QApplication + + Font + 폰트 + + + Selected Font: %1 + 선택된 폰트: %1 + + + <h4>Welcome to %1!</h4>You want to configure %1 before you start using it? + <h4>%1에 오신 것을 환영합니다!</h4>%1을 사용하기 전에 설정 창을 여시겠습니까? + + + + SavegameDialog + + + + Savegame Viewer + 세이브 파일 보기 + + + + <span style="font-weight:600">Savegame</span><br><br>%1 + <span style="font-weight:600">세이브 파일</span><br><br>%1 + + + + &Export + 내보내기(&E) + + + + &Close + 닫기(&C) + + + + Failed at %1 + 실패 %1 + + + + SavegameWidget + + + Savegame Widget + 세이브 파일 위젯 + + + + SAVE %3 - %1<br>%2 + 저장 %3 - %1<br>%2 + + + + View savegame + 세이브 파일 보기 + + + + View + 보기 + + + + Copy savegame + 세이브 파일 복사 + + + + + Export + 내보내기 + + + + Delete savegame + 세이브 파일 삭제 + + + + Delete + 삭제 + + + + &View + 보기(&V) + + + + + + &Export + 내보내기(&E) + + + + + + &Remove + 삭제(&R) + + + + + &Select + 선택(&S) + + + + + &Deselect + 선택 해제(&D) + + + + + Select &All + 모두 선택(&A) + + + + + &Deselect All + 모두 선택 해제(&D) + + + + Savegame files (SGTA*) + 세이브 파일 (SGTA*) + + + + All files (**) + 모든 파일 (**) + + + + + + + Export Savegame + 세이브 파일 내보내기 + + + + Overwrite %1 with current Savegame? + %1을 현재 세이브 파일로 덮어쓰시겠습니까? + + + + Failed to overwrite %1 with current Savegame + %1을 현재 세이브 파일로 덮어쓰지 못했습니다. + + + + Failed to export current Savegame + 현재 세이브 파일을 내보내지 못했습니다. + + + + No valid file is selected + 올바른 파일이 선택되지 않았습니다. + + + + Export Savegame... + 세이브 파일 내보내기 + + + + + AUTOSAVE - %1 +%2 + 자동 저장 - %1 +%2 + + + + + SAVE %3 - %1 +%2 + 저장 %3 - %1 +%2 + + + + + WRONG FORMAT + 잘못된 형식 + + + + UNKNOWN + 알 수 없음 + + + + + Delete Savegame + 세이브 파일 삭제 + + + + Are you sure to delete %1 from your savegames? + %1을(를) 세이브 파일에서 삭제하시겠습니까? + + + + Failed at deleting %1 from your savegames + %1을(를) 세이브 파일에서 삭제하지 못했습니다. + + + + SnapmaticEditor + + + + + + + + + + + Snapmatic Properties + 스냅매틱 속성 + + + + Snapmatic Type + 스냅매틱 형식 + + + + Editor + 편집기 + + + + Selfie + 셀피 + + + + Regular + 일반 + + + + Mugshot + 머그샷 + + + + Meme + + + + + Director + 감독 + + + + Snapmatic Values + 스냅매틱 값 + + + + Extras + 기타 + + + + Qualify as Avatar automatically at apply + 적용 시 자동으로 아바타 자격을 부여합니다. + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + 이 스냅매틱을 소셜 클럽 프로필 이미지로 사용할 수 있습니다. + + + + Apply changes + 변경 사항 적용 + + + + &Apply + 적용(&A) + + + + Discard changes + 변경 사항 취소 + + + + &Cancel + 취소(&C) + + + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + < h4>저장되지 않은 변경 내용이 감지되었습니다. </h4>그만두기 전에 JSON 콘텐츠를 저장하겠습니까? + + + + Patching of Snapmatic Properties failed because of %1 + %1로 인해 스냅매틱 속성을 패치하지 못했습니다. + + + + + + + Patching of Snapmatic Properties failed because of I/O Error + I/O 오류로 인해 스냅매틱 속성을 패치하지 못했습니다. + + + + Patching of Snapmatic Properties failed because of JSON Error + JSON 오류로 인해 스냅매틱 속성을 패치하지 못했습니다. + + + + + Snapmatic Crew + 조직 스냅매틱 + + + + + New Snapmatic crew: + 새로운 조직 스냅매틱: + + + + + Snapmatic Title + 스냅매틱 제목 + + + + + New Snapmatic title: + 새로운 스냅매틱 제목: + + + + + + Edit + 편집 + + + + Players: %1 (%2) + Multiple Player are inserted here + 여기에 여러 플레이어가 추가됩니다. + 플레이어: %1 (%2) + + + + Player: %1 (%2) + One Player is inserted here + 여기에 플레이어 하나가 추가됩니다. + 플레이어: %1 (%2) + + + + Title: %1 (%2) + 제목: %1 (%2) + + + + + Appropriate: %1 + 변경: %1 + + + + Yes + Yes, should work fine + 네, 잘 될 거예요. + + + + + No + No, could lead to issues + 아니요, 문제가 발생할 수 있습니다. + 아니요 + + + + Crew: %1 (%2) + 조직: %1 (%2) + + + + SnapmaticPicture + + + + JSON is incomplete and malformed + JSON 파일이 불안정하거나 형식이 잘못되었습니다. + + + + + JSON is incomplete + JSON 파일이 불안정합니다. + + + + + JSON is malformed + 잘못된 JSON 형식 + + + + PHOTO - %1 + 사진 - %1 + + + + open file %1 + 파일 열기 %1 + + + + header not exists + 헤더가 존재하지 않습니다. + + + + header is malformed + 헤더의 형식이 잘못되었습니다. + + + + picture not exists (%1) + 이미지가 존재하지 않습니다. (%1) + + + + JSON not exists (%1) + JSON 파일이 존재하지 않습니다. (%1) + + + + title not exists (%1) + 제목이 존재하지 않습니다. (%1) + + + + description not exists (%1) + 설명이 존재하지 않습니다. (%1) + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + %2의 예: JSON이 잘못된 형식입니다 + %2 때문에 %1 파일을 읽습니다. + + + + SnapmaticWidget + + + Snapmatic Widget + 스냅매틱 위젯 + + + + PHOTO - 00/00/00 00:00:00 + 이미지 - 00/00/00 00:00:00 + + + + View picture + 이미지 보기 + + + + View + 보기 + + + + Copy picture + 이미지 복사 + + + + Copy + 복사 + + + + Export picture + 이미지 내보내기 + + + + Export + 내보내기 + + + + + + Delete picture + 이미지 삭제 + + + + Delete + 삭제 + + + + + + Edi&t + 편집(&T) + + + + + + Show &In-game + 인게임에서 보이기(&I) + + + + + + Hide &In-game + 인게임에서 숨기기(&I) + + + + &Export + 내보내기(&E) + + + + &View + 보기(&V) + + + + &Remove + 삭제(&R) + + + + + &Select + 선택(&S) + + + + + &Deselect + 선택 해제(&D) + + + + + Select &All + 모두 선택(&A) + + + + + &Deselect All + 모두 선택 해제(&D) + + + + Are you sure to delete %1 from your Snapmatic pictures? + 스냅매틱 이미지에서 %1을 삭제하시겠습니까? + + + + Failed at deleting %1 from your Snapmatic pictures + 스냅매틱 이미지에서 %1을 삭제하지 못했습니다. + + + + Failed to hide %1 In-game from your Snapmatic pictures + 인게임 스냅매틱 이미지에서 %1 을 숨기지 못했습니다. + + + + Failed to show %1 In-game from your Snapmatic pictures + 인게임 스냅매틱 이미지에서 %1 을 표시하지 못했습니다. + + + + TelemetryDialog + + + You want help %1 to improve in the future by including personal usage data in your submission? + 개인 사용 데이터를 제출에 포함시켜 %1이(가) 개선되기를 원합니까? + + + + %1 User Statistics + %1 사용자 통계 + + + + Yes, I want include personal usage data. + 예, 개인 사용 데이터를 포함시키고 싶습니다. + + + + &OK + 확인(&O) + + + + UserInterface + + + + %2 - %1 + %2 - %1 + + + + Select profile + 프로필 선택 + + + + %1 %2 + %1 %2 + + + + Reload profile overview + 프로필 다시 불러오기 + + + + &Reload + 새로고침(&R) + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + 닫기 %1 <- (gta5view/gta5sync) - %1 자동으로 교체됩니다. + 닫기 %1 + + + + + + + &Close + 닫기(&C) + + + + &File + 파일(&F) + + + + &Help + 도움말(&H) + + + + &Edit + 편집(&E) + + + + &Profile + 프로필(&P) + + + + &Selection visibility + 인게임 표시(&S) + + + + Selection &mass tools + 선택 작업(&M) + + + + + + &About %1 + %1 정보(&A) + + + + &Exit + 종료(&E) + + + + Exit + 종료 + + + + Close &Profile + 프로필 닫기(&P) + + + + &Settings + 설정(&S) + + + + Select &All + 모두 선택(&A) + + + + &Deselect All + 모두 선택 해제(&D) + + + + &Export selected... + 선택 내보내기(&E) + + + + &Remove selected + 선택 삭제(&R) + + + + &Import files... + 파일 불러오기(&I) + + + + &Open File... + 파일 열기(&O) + + + + + Select &GTA V Folder... + GTA V 폴더 선택(&G) + + + + + + + Select GTA V Folder... + GTA V 폴더 선택 + + + + Show In-gam&e + 인게임 보이기(&E) + + + + Hi&de In-game + 인게임 숨기기(&D) + + + + + + Change &Title... + 제목 변경(&T) + + + + + + Change &Crew... + &조직 상징 변경(&C) + + + + + + &Qualify as Avatar + 아바타 자격 부여(&Q) + + + + + + Change &Players... + 플레이어 변경(&P) + + + + + + Show In-game + 인게임 보이기 + + + + + + Hide In-game + 인게임 숨기기 + + + + + + Select Profile + 프로필 선택 + + + + + &Donate + 기부하기(&D) + + + + Donate + 기부하기 + + + + Donation methods + 기부 방법 + + + View + 보기 + + + Copy + 복사 + + + + &Copy + 복사(&C) + + + + Open File... + 파일 열기... + + + + + + + Open File + 파일 열기 + + + + Can't open %1 because of not valid file format + 올바른 파일 형식이 아니므로 %1을 열 수 없습니다. + + + + %1 - Messages + %1 - 뉴스 + + + diff --git a/res/gta5sync_ru.qm b/res/gta5sync_ru.qm index 6a271c0..214adf1 100644 Binary files a/res/gta5sync_ru.qm and b/res/gta5sync_ru.qm differ diff --git a/res/gta5sync_ru.ts b/res/gta5sync_ru.ts index 3a3e584..a05570c 100644 --- a/res/gta5sync_ru.ts +++ b/res/gta5sync_ru.ts @@ -3,10 +3,6 @@ AboutDialog - - About gta5sync - О программе gta5sync - About %1 @@ -14,7 +10,7 @@ - <span style=" font-weight:600;">%1</span><br/> + <span style="font-weight:600">%1</span><br/> <br/> %2<br/> <br/> @@ -24,7 +20,7 @@ Built with Qt %5<br/> Running with Qt %6<br/> <br/> %7 - <span style=" font-weight:600;">%1</span><br/> + <span style="font-weight:600">%1</span><br/> <br/> %2<br/> <br/> @@ -33,28 +29,6 @@ Running with Qt %6<br/> Сделано с Qt %5<br/> Выполняется на Qt %6<br/> <br/> -%7 - - - <span style=" font-weight:600;">%1</span><br/> -<br/> -%2<br/> -<br/> -Version %3<br/> -Created on %4<br/> -Built with Qt %5<br/> -Running with Qt %6<br/> -%8<br/> -%7 - <span style=" font-weight:600;">%1</span><br/> -<br/> -%2<br/> -<br/> -Версия %3<br/> -Сделано %4<br/> -Сделано с Qt %5<br/> -Выполняется на Qt %6<br/> -%8<br/> %7 @@ -63,70 +37,78 @@ Running with Qt %6<br/> &Закрыть - <span style=" font-weight:600;">gta5sync</span><br/><br/>A project for viewing and sync Grand Theft Auto 5 Snapmatic Pictures and Savegames<br/><br/>Project version: %1<br/>Compiled with Qt %2<br/>Running with Qt %3<br/><br/>Copyright © <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5sync is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - <span style=" font-weight:600;">gta5sync</span><br/><br/>Проект для просмотра и синхронизации фотографий Snapmatic и сохранений от Grand Theft Auto 5<br/><br/>Версия проекта: %1<br/>Скомпилировано с Qt %2<br/>Работает на Qt %3<br/><br/>Copyright © <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5sync лицензирован по <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - - - Close - Закрыть - - - - Using %1 %2 - Exp. Using libmyfuck - Использует %1 %2 - - - + Translated by %1 - Exp. Translated by Syping + Translated by translator, example Translated by Syping Перевёл %1 - - NAME_OF_TRANSLATOR - Your Name (The person behind your screen looking at this text!) - VADemon + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + VADemon,https://github.com/VADemon/ - - TRANSLATOR_PROFILE - mailto: http:// https:// Exp. https://github.com/Syping/ - https://github.com/VADemon/ - - - + A project for viewing Grand Theft Auto V Snapmatic<br/> Pictures and Savegames Проект для просмотра Grand Theft Auto V Snapmatic<br/> картинок и сохранений - + Copyright &copy; <a href="%1">%2</a> %3 Copyright &copy; <a href="%1">%2</a> %3 - + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> %1 под лицензией <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - Copyright &copy; <a href="%1">%2</a> %3<br/>%4 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - Copyright &copy; <a href="%1">%2</a> %3<br/>%4 под лицензией <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + + Release + Релиз - - A project for viewing and sync Grand Theft Auto V Snapmatic<br/> -Pictures and Savegames - Проект для просмотра и синхронизирования <br/> -Grand Theft Auto V Snapmatic картинок и сохранений + + Release Candidate + Предварительный выпуск + + + + Daily Build + Дневная сборка + + + + Developer + Разработчик + + + + Beta + Бета + + + + Alpha + Альфа + + + + + Custom + Не известен контекст + + Своя CrewDatabase - + + No Crew Вне банды @@ -136,7 +118,8 @@ Grand Theft Auto V Snapmatic картинок и сохранений Dialog - Возможно не это имелось ввиду, немецкого перевода нету + Возможно не это имелось ввиду, немецкого перевода нету + Диалоговое окно @@ -177,12 +160,12 @@ Grand Theft Auto V Snapmatic картинок и сохранений Custom Size: - Другой размер: + Размер: x - x + на @@ -195,44 +178,450 @@ Grand Theft Auto V Snapmatic картинок и сохранений&Закрыть + + ImageEditorDialog + + + + + + Snapmatic Image Editor + Редактор картинок Snapmatic + + + + Overwrite Image... + Перезаписать картинку... + + + + Apply changes + Применить изменения + + + + &Overwrite + &Перезаписать + + + + Discard changes + Отменить изменения + + + + &Close + &Закрыть + + + + + Patching of Snapmatic Image failed because of I/O Error + Не удалось изменить картинку Snapmatic из-за ошибки ввода-вывода + + + + + Patching of Snapmatic Image failed because of Image Error + Не удалось изменить картинку Snapmatic из-за ошибки Image Error + + ImportDialog - + Import... Импортировать... - - Settings - Настройки + + + Ignore Aspect Ratio + Игнорировать соотн. сторон - - &Keep Aspect Ratio - О&ставить соотношение сторон + + Avatar + Аватар - - &Ignore Aspect Ratio - &Игнорировать соотношение сторон + + Picture + Картинка - - &Avatar - &Аватар + + Watermark + Водяной знак - + Force Borderless + Обрезать рамки + + + + Background + Фон + + + + + + + Background Colour: <span style="color: %1">%1</span> + Цвет фона: <span style="color: %1">%1</span> + + + + Select background colour + Выберите цвет фона + + + ... + ... + + + + Select background image + Выбрать фоновое изображение + + + + Remove background image + Убрать фоновую картинку + + + + Import as-is, don't change the picture at all, guaranteed to break Snapmatic unless you know what you doing + Импортировать как есть, не меняя картинку. Обязательно поломает Snapmatic, если не знаешь, что делаешь + + + + + Background Image: %1 + Фоновая картинка: %1 + + + X + latin X + + X + + + + Crop to Aspect Ratio + Обрезать до соотн. сторон + + + + Force Colour in Avatar Zone + Задать цвет в зоне аватарки + + + + Advanced + Расширенное + + + + Resolution: + Разрешение: + + + + Snapmatic resolution + Разрешение Snapmatic + + + + Avoid compression and expand buffer instead, improves picture quality, but may break Snapmatic + Не сжимать, а увеличить буфер. Улучшит качество картинки, но может поломать Snapmatic + + + + Unlimited Buffer + Неограниченный буфер + + + + Import as-is + Импортировать как есть + + + + Import options + Опции импорта + + + + &Options + &Опции + + + + Import picture + Импортировать картинку + + + &OK &ОК - + + Discard picture + Отклонить картинку + + + &Cancel - Я не уверен насчет горячих клавиш... + Я не уверен насчет горячих клавиш... + От&мена + + + + + + Background Image: + Фоновая картинка: + + + + &Import new Picture... + &Импортировать картинку... + + + + &Crop Picture... + Об&резать картинку... + + + + &Load Settings... + &Загрузить настройки... + + + + &Save Settings... + &Сохранить настройки... + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + Свой Аватар + + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + Своя Картинка + + + + Storage + Background Image: Storage + Хранилище + + + + Crop Picture... + Обрезать картинку... + + + + &Crop + Об&резать + + + + Crop Picture + Обрезать картинку + + + + + Please import a new picture first + Импортируй сначала новую картинку + + + + + Default + Default as Default Profile + По умолчанию + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + Профиль %1 + + + + + Load Settings... + Загрузить настройки... + + + + + Please select your settings profile + Пожалуйста, выбери профиль для настроек + + + + + Save Settings... + Сохранить настройки... + + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + Ты точно хочешь использовать квадратное изображение вне зоны аватарки? Если это аватар, то изображение будет обрезано! + + + + + Snapmatic Avatar Zone + Зона Snapmatic Аватарки + + + + Select Colour... + Выбрать цвет... + + + + File + Background Image: File + Файл + + + + JsonEditorDialog + + + Snapmatic JSON Editor + Редактор JSON для Snapmatic + + + + Apply changes + Применить изменения + + + + &Save + &Сохранить + + + + Discard changes + Отменить изменения + + + + &Close + &Закрыть + + + + JSON Error + Ошибка JSON + + + + MapLocationDialog + + + Snapmatic Map Viewer + Просмотрщик карты Snapmatic + + + + Close viewer + Закрыть просмотрщик + + + + &Close + &Закрыть + + + + Apply new position + Применить новую позицию + + + + &Apply + &Применить + + + + Revert old position + Вернуть старую позицию + + + + &Revert + &Откатить + + + + Select new position + Выбрать новую позицию + + + + &Select + &Выбрать + + + + Quit select position + Покинуть выбор позиции + + + + &Done + &Готово + + + + X: %1 +Y: %2 + X and Y position + X: %1 +Y: %2 + OptionsDialog @@ -242,19 +631,17 @@ Grand Theft Auto V Snapmatic картинок и сохраненийОткрывать/выбирать содержимое - Open with Singleclick - Открывать одним щелчком + Открывать одним щелчком - + Open with Doubleclick Открывать двойным щелчком - Select with Singleclick - Выбирать одним щелчком + Выбирать одним щелчком @@ -274,7 +661,7 @@ Grand Theft Auto V Snapmatic картинок и сохранений Screen Resolution: %1x%2 - Разрешение экрана: %1x%2 + Как разрешение экрана: %1x%2 @@ -299,7 +686,7 @@ Grand Theft Auto V Snapmatic картинок и сохранений Force using Custom Folder - Использовать эту папку GTA V + Использовать эту папку @@ -330,7 +717,7 @@ Grand Theft Auto V Snapmatic картинок и сохранений Enable Custom Quality - Использовать другое качество + Выставить другое качество @@ -369,72 +756,276 @@ Grand Theft Auto V Snapmatic картинок и сохранений + Game + Игра + + - Language - Язык + Social Club Version + Версия Social Club - - Sync - Sync + + + + + + + + + Found: %1 + Найдено: %1 - - Sync is not implemented at current time - Синхронизация пока ещё не реализована + + + + + + + + + + + Language: %1 + Язык: %1 - + + Steam Version + Версия Steam + + + + Feedback + Обратная связь + + + + Participation + Участие + + + + + Participate in %1 User Statistics + Участвовать в пользовательской статистике %1 + + + + Categories + Категории + + + + Hardware, Application and OS Specification + Application = gta5view + + Железо, выпуск программы, тип ОС + + + + System Language Configuration + Языковые настройки системы + + + + Application Configuration + Настройки программы + + + + Other + Другое + + + + + + Participation ID: %1 + Номер участника: %1 + + + + &Copy + &Копировать + + + + Language for Areas + Язык для местоположений? + + Язык перевода местоположений + + + + Style + Внешний вид + + + + Style: + Стиль: + + + + Font + Шрифт + + + Always use Message Font (Windows 2003 and earlier) + Всегда использовать шрифт сообщений (Windows 2003 и ранние) + + + + Interface + Интерфейс + + + + Personal Usage Data + Пользование программой + + + + Language for Interface + Язык интерфейса + + + + + + + Current: %1 + Сейчас: %1 + + + + Use Default Style (Restart) + Использовать стандартный стиль (Перезапуск) + + + + Use Default Font (Restart) + Использовать стандартный шрифт (Перезапуск) + + + + Font: + Шрифт: + + + + Apply changes + Применить изменения + + + &OK OK, Cancel, Apply &ОК - + + Discard changes + Отвергнуть изменения + + + &Cancel OK, Cancel, Apply От&мена - + System - System like PC System - может быть надо прилагательное + System in context of System default Система - - %1 (%2 if available) - System like PC System = %1, System Language like Deutsch = %2 - %1 (%2 если имеется) + + %1 (Game language) + Next closest language compared to the Game settings + %1 (Язык игры) - - + + + %1 (Closest to Interface) + Next closest language compared to the Interface + %1 (Как язык интерфейса) + + + + + + Auto + Automatic language choice. + Автоматически + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + %1 (Приоритетный язык) + + + %1 %1 %1 - - The new Custom Folder will initialize after you restart %1. + + The new Custom Folder will initialise after you restart %1. Другая папка будет загружена после перезапуска %1. - - The language change will take effect after you restart %1. - Язык изменится после перезапуска %1. + + View %1 User Statistics Online + Посмотреть статистику %1 онлайн - + + Not registered + Не зарегистрирован + + + + + + + Yes + Да + + + + + No + Нет + + + + + OS defined + Настройка от ОС + + + + + Steam defined + Настройка от Steam + + + No Profile No Profile, as default - Нет профиля + Не выбран - - - + + + Profile: %1 Профиль: %1 @@ -442,73 +1033,79 @@ Grand Theft Auto V Snapmatic картинок и сохранений PictureDialog - - %1 - Snapmatic Picture Viewer - %1 - Просмотрщик фотографий Snapmatic - - - <span style=" font-weight:600;">Title: </span>%6<br> -<span style=" font-weight:600;">Location: </span>%1, %2, %3 <br> -<span style=" font-weight:600;">Players: </span>%4<br> -<span style=" font-weight:600;">Crew ID: </span>%5 - <span style=" font-weight:600;">Заголовок: </span>%6<br> -<span style=" font-weight:600;">Позиция: </span>%1, %2, %3 <br> -<span style=" font-weight:600;">Игроки: </span>%4<br> -<span style=" font-weight:600;">ID группы: </span>%5 - - - - <span style=" font-weight:600;">Title: </span>%6<br/> -<span style=" font-weight:600;">Location: </span>%7 (%1, %2, %3)<br/> -<span style=" font-weight:600;">Players: </span>%4 (Crew %5)<br/> -<span style=" font-weight:600;">Created: </span>%8 - <span style=" font-weight:600;">Заголовок: </span>%6<br/> -<span style=" font-weight:600;">Место: </span>%7 (%1, %2, %3)<br/> -<span style=" font-weight:600;">Игроки: </span>%4 (Банда %5)<br/> -<span style=" font-weight:600;">Сделано: </span>%8 + + <span style="font-weight:600">Title: </span>%6<br/> +<span style="font-weight:600">Location: </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">Players: </span>%4 (Crew %5)<br/> +<span style="font-weight:600">Created: </span>%8 + <span style="font-weight:600">Заголовок: </span>%6<br/> +<span style="font-weight:600">Место: </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">Игроки: </span>%4 (Банда %5)<br/> +<span style="font-weight:600">Сделано: </span>%8 - Export picture - Экспорт картинки + &Manage + &Управление - - &Export - &Экспорт + + Manage picture + Настройки картинки - + + Snapmatic Picture Viewer - %1 + Просмотрщик фотографий Snapmatic - %1 + + + + Close viewer + Закрыть просмотрщик + + + &Close &Закрыть - - + + Export Экспортировать - Copy - Копировать + + + Export as &Picture... + Экспортировать как &картинку... - - Close - Закрыть + + + Export as &Snapmatic... + Экспортировать как &Snapmatic... - - Export as &JPG picture... - Эксп&ортировать как картинку JPG... + + + &Overwrite Image... + &Перезаписать картинку... - - Export as &GTA Snapmatic... - Экс&портировать как GTA Snapmatic... + + + &Edit Properties... + &Изменить свойства... - + + + Open &Map Viewer... + Открыть &карту... + + + Key 1 - Avatar Preview Mode Key 2 - Toggle Overlay Arrow Keys - Navigate @@ -517,135 +1114,196 @@ Arrow Keys - Navigate Стрелки - Навигация - - + Snapmatic Picture Viewer Просмотрщик фотографий Snapmatic - - + Failed at %1 Ошибка при %1 - + + + No Crew + Вне банды + + + + + + No Players + Игроков нет + + + Avatar Preview Mode Press 1 for Default View Режим просмотра аватарок Нажмите 1 для стандартного просмотра - - - No player - Игроков нет - - - - - No crew - Без группы - - - + Unknown Location Неизвестное место - - Export as JPG picture... - Экспортировать картинкой JPG... - - - - JPEG picture (*.jpg) - Картинка JPEG (*.jpg) - - - + Portable Network Graphics (*.png) Картинка Portable Network Graphics (*.png) - - - - - Export as JPG picture - Экспортировать как картинку JPG - - - - + + Overwrite %1 with current Snapmatic picture? Перезаписать %1 текущей картинкой Snapmatic? - - - - - - - Export as GTA Snapmatic - Экспортировать как GTA Snapmatic + + Export as Picture... + Экспорт как картинку... - - - Failed to overwrite %1 with current Snapmatic picture - Не удалось перезаписать %1 картинкой Snapmatic + + JPEG Graphics (*.jpg *.jpeg) + Картинка JPEG (*.jpg *.jpeg) - - + + + + + + + Export as Picture + Экспорт как картинку + + + + Failed to export the picture because the system occurred a write failure + Не удалось экспортировать картинку из-за ошибки системы при записи + + + + Failed to export the picture because the format detection failures + Не удалось экспортировать картинку, потому что произошла ошибка при распозновании формата + + + + Failed to export the picture because the file can't be written + Не удалось экспортировать картинку, так как файл не может быть записан + + + + Failed to export the picture because of an unknown reason + Не удалось экспортировать картинку по неизвестной причине + + + Failed to export current Snapmatic picture Не удалось экспортировать текущую картинку Snapmatic - + + Export as Snapmatic... + Экспортировать как Snapmatic... + + + + + + + + Export as Snapmatic + Экспортировать как Snapmatic + + + Exported Snapmatic to "%1" because of using the .auto extension. Snapmatic был экспортирован как "%1" из-за расширеня файла. - - + + No valid file is selected Выбранный файл неверен - Copy picture - Скопировать картинку - - - - Export as GTA Snapmatic... - Экспортировать как GTA Snapmatic... - - - + GTA V Export (*.g5e) GTA V Export (*.g5e) - + GTA V Raw Export (*.auto) GTA V Экспорт Исходника (*.auto) - + Snapmatic pictures (PGTA*) Картинки Snapmatic (PGTA*) - All files (**) - Все файлы (**) + + + Open &JSON Editor... + Открыть &редактор JSON... + + + + PlayerListDialog + + + Edit Players... + Изменить игроков... - Failed to copy current Snapmatic picture - Не удалось скопировать текущую картинку Snapmatic + + Available Players: + Доступные игроки: + + + + Selected Players: + Выбранные игроки: + + + + &Apply + &Применить + + + + &Cancel + &Отмена + + + + Add Players... + Добавить игроков... + + + + Failed to add more Players because the limit of Players are %1! + Невозможно добавить больше игроков из-за ограничения в %1! + + + + + Add Player... + Добавить игрока... + + + + Enter Social Club Player ID + Введите идентификатор игрока из Social Club + + + + Failed to add Player %1 because Player %1 is already added! + Нельзя повторно добавить игрока %1, %1 уже добавлен! @@ -661,95 +1319,108 @@ Press 1 for Default View Загружается файл %1 из %2 - + %1 %2 %1 %2 - - Import exported file - Импортировать экспортированный файл + + Import file + Импортировать файл - + &Import... &Импортировать... - + Close profile Закрыть профиль - + &Close &Закрыть - Import copy - Импортировать копию - - - Close Profile - Закрыть профиль - - - + Loading... Загрузка... - - + + Snapmatic Loader + Загрузчик Snapmatic + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + Change wording if the %1 is not a multiline beginning at new line + + <h4>Нижеследующие картинки Snapmatic были восстановлены</h4>%1 + + + + + + + + + + + + + + + + + + + + + Import... Импортировать... - - - - - - - - - - - - + + + + + + Import Импортировать - All profile files (SGTA* PGTA*) - Все файлы профиля (SGTA* PGTA*) - - - - + + Savegames files (SGTA*) Файлы сохранения (SGTA*) - - + + Snapmatic pictures (PGTA*) Картинка Snapmatic (PGTA*) - - + + + + All files (**) Все файлы (**) - - + + Import file %1 of %2 files Импортируются файлы %1 из %2 - + Import failed with... %1 @@ -758,151 +1429,258 @@ Press 1 for Default View %1 - - + + Failed to read Snapmatic picture Не удалось загрузить картинку Snapmatic - - + + Failed to read Savegame file Не удалось загрузить файл сохранения - - Can't import %1 because of not valid file format - Не получилось импортировать %1 из-за неправильного формата файла - - - - - + + + No valid file is selected Выбранный файл неверен - + Enabled pictures: %1 of %2 Включенные картинки: %1 из %2 - - Importable files (*.g5e *.jpg *.png SGTA* PGTA*) - Подходящие для импорта файлы (*.g5e *.jpg *.png SGTA* PGTA*) + + Importable files (%1) + Файлы для импорта (%1) - - All image files (*.jpg *.png) - Все изображения (*.jpg *.png) + + + + All image files (%1) + Все файлы изображений (%1) - + + + + Can't import %1 because file can't be open + Не удалось открыть %1, файл не может быть открыт + + + + + + Can't import %1 because file can't be parsed properly + Не получилось импортировать %1, файл не может быть правильно обработан + + + + Can't import %1 because file format can't be detected + Не получилось импортировать %1, не удалось определить формат файла + + + Failed to import the Snapmatic picture, file not begin with PGTA or end with .g5e Не удалось импортировать картинку Snapmatic, название не начинается с PGTA или не заканчивается с .g5e - - Failed to import the Snapmatic picture, the picture is already in the game - Не удалось импортировать картинку Snapmatic, картинка уже в игре - - - + Failed to import the Snapmatic picture, can't copy the file into profile Не удалось импортировать картинку Snapmatic, не получилось скопировать файл в профиль - + Failed to import the Savegame, can't copy the file into profile Не удалось импортировать сохранение, не получилось скопировать файл в профиль - + Failed to import the Savegame, no Savegame slot is left Не удалось импортировать сохранение, нет пустых ячеек под сохранения - - + + JPG pictures and GTA Snapmatic Картинки JPG и GTA Snapmatic - - + + JPG pictures only Только картинки JPG - - + + GTA Snapmatic only Только GTA Snapmatic - - - No Snapmatic pictures or Savegames files are selected - Не выделены ни один Snapmatic или сохранение + + Initialising export... + Подготовка к экспорту... - - - + + + No Snapmatic pictures or Savegames files are selected + Не выделен ни один Snapmatic или сохранение + + + + + Remove selected Снять выделение - + You really want remove the selected Snapmatic picutres and Savegame files? Точно ли хочешь удалить выбранные картинки Snapmatic и файлы сохранений? - - Failed at remove the complete selected Snapmatic pictures and/or Savegame files - Не удалось удалить полностью выбранные картинки Snapmatic и/или файлы сохранений + + Prepare Content for Import... + Подготовка данных к импорту... - Failed to import copy of Snapmatic picture because the file not begin with PGTA - Не удалось имортировать копию картинки Snapmatic, т.к. файл не начинается с PGTA + + + Qualify as Avatar + Пометить как Аватар - Failed to import copy of Snapmatic picture because the copy failed - Не получилось имортировать копию картинки Snapmatic, потому что не удалось его скопировать + + + + + + + No Snapmatic pictures are selected + Не выделена ни одна картинка Snapmatic - Failed to import copy of Savegame file because the copy failed - Не получилось имортировать копию сохранения, потому что не удалось его скопировать + + + + + Patch selected... + Пропатчить выделенные... - Failed to import copy of Savegame file because no free Savegame slot left - Не получилось имортировать копию сохранения, потому что не осталось свободных под них слотов + + + + + + + + + Patch file %1 of %2 files + Изменяется файл %1 из %2 - - - - - Export selected - Экспортировать выделенное + + + + + + + %1 failed with... + +%2 + Action failed with... + %1 завершился с ошибкой... + +%2 - + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + Можно использовать слово "приписать" + + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + Не удалось удалить все выделенные картинки Snapmatic и/или сохранения + + + + Qualify + %1 failed with... + Помечание + + + + + Change Players... + Изменить игроков... + + + + Change Players + %1 failed with... + Измение игроков + + + + + + Change Crew... + Изменить банду... + + + + Failed to enter a valid Snapmatic Crew ID + Введённый идентификатор банды не верен + + + + Change Crew + %1 failed with... + Изменение банды + + + + + + Change Title... + Изменить заголовок... + + + + Failed to enter a valid Snapmatic title + Введённый заголовок не верен + + + + Change Title + %1 failed with... + Изменение заголовка + + + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: %1Эскпортировать картинки Snapmatic%2<br><br>Картинки JPG можно открыть любым просмотрщиком<br>Картинки формата GTA Snapmatic можно снова импортировать в игру<br><br>Экспортировать как: - + + + + + Export selected... Экпортировать выделенное... - - Initializing export... - Подготавливаю эскпорт... - - - + Export failed with... %1 @@ -911,20 +1689,20 @@ Press 1 for Default View %1 - - - + + + Export file %1 of %2 files Экспортируется файл %1 из %2 - + All profile files (*.g5e SGTA* PGTA*) Все файлы профиля (*.g5e SGTA* PGTA*) - - + + GTA V Export (*.g5e) GTA V Export (*.g5e) @@ -932,49 +1710,35 @@ Press 1 for Default View QApplication - - Font - Шрифт - - - - Selected Font: %1 - Выбранный шрифт: %1 + <h4>Welcome to %1!</h4>You want to configure %1 before you start using it? + <h4>Добро пожаловать в %1!</h4>Хочешь изменить настройки %1 перед использованием? SavegameDialog - + Savegame Viewer Просмотрщик сохранений - - <span style=" font-weight:600;">Savegame</span><br><br>%1 - <span style=" font-weight:600;">Сохранение</span><br><br>%1 + + <span style="font-weight:600">Savegame</span><br><br>%1 + <span style="font-weight:600">Сохранение</span><br><br>%1 - + &Export &Экспорт - + &Close &Закрыть - Copy - Копировать - - - Close - Закрыть - - - + Failed at %1 Ошибка при %1 @@ -987,73 +1751,65 @@ Press 1 for Default View Виджет сохранений - The Third Way (100%) - 00/00/00 00:00:00 - Третий путь (100%) - 00/00/00 00:00:00 - - - + View savegame Просмотреть сохранение - + View Просмотр - - + + Export Экспорт - Copy - Копировать - - - + Delete Удалить - - - + Delete savegame Удалить сохранение - + Export Savegame... Экспортировать сохранение... - + SAVE %3 - %1<br>%2 СОХРАНЕНИЕ %3 - %1<br>%2 - - + + WRONG FORMAT НЕВЕРНЫЙ ФОРМАТ - + + AUTOSAVE - %1 %2 АВТОСОХРАНЕНИЕ - %1 %2 - + + SAVE %3 - %1 %2 СОХРАНЕНИЕ %3 - %1 %2 - + UNKNOWN НЕИЗВЕСТНО @@ -1063,99 +1819,99 @@ Press 1 for Default View Вы уверены, что хотите удалить сохранение %1? - + + + Delete Savegame + Удалить сохранение + + + Failed at deleting %1 from your savegames Не удалось удалить сохранение %1 - + &View &Просмотр - + + + &Remove &Удалить - - + + &Select &Выбрать - + + &Deselect Сн&ять выбор - - + + Select &All В&ыбрать все - + + &Deselect All Снять выбо&р со всех - + Copy savegame Копировать сохранение - + + + &Export &Экспортировать - + Savegame files (SGTA*) Файлы сохранений (SGTA*) - + All files (**) Все файлы (**) - - - + + + Export Savegame Экспортировать сохранение - + Overwrite %1 with current Savegame? Перезаписать %1 текущим сохранением? - + Failed to overwrite %1 with current Savegame Не удалось переписать %1 текущим сохранением - + Failed to export current Savegame Не удалось экспортировать текущее сохранение - Overwrite %1 with current savegame? - Перезаписать %1 текущим сохранением? - - - Failed to overwrite %1 with current savegame - Не удалось перезаписать %1 текущим сохранением - - - Failed to copy current savegame - Не удалось скопировать текущее сохранение - - - + No valid file is selected Выбранный файл неверен @@ -1165,7 +1921,13 @@ Press 1 for Default View - + + + + + + + Snapmatic Properties Свойства Snapmatic @@ -1205,8 +1967,7 @@ Press 1 for Default View Значения в Snapmatic - - + Crew: %1 (%2) Банда: %1 (%2) @@ -1216,83 +1977,126 @@ Press 1 for Default View Meme - + + Snapmatic Title Заголовок Snapmatic - - + Title: %1 (%2) Заголовок: %1 (%2) - - - - Appropriate: %1 - Разумные: %1 + + Players: %1 (%2) + Multiple Player are inserted here + Игроки: %1 (%2) - + + Player: %1 (%2) + One Player is inserted here + Игрок: %1 (%2) + + + + + Appropriate: %1 + Подходит: %1 + + + Extras Дополнительно - + Qualify as Avatar automatically at apply - При применении автоматически классифицировать как аватар + Пометить как аватарку - + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture - C классификацией аватара можно использовать этот Snapmatic в профиле на Social Club + C меткой аватара можно загрузить эту картинку Snapmatic в профиль на Social Club - + + Apply changes + Применить изменения + + + &Apply &Применить - + + Discard changes + + + + &Cancel &Отмена - - + + + Edit Правка - + Yes Yes, should work fine Да - + No No, could lead to issues Нет - + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + <h4>Несохранённые изменения</h4>Сохранить изменения в JSON перед выходом? + + + + Patching of Snapmatic Properties failed because of %1 + Не удалось изменить свойства Snapmatic из-за %1 + + + + Patching of Snapmatic Properties failed because of JSON Error + Не удалось измененить свойства Snapmatic из-за ошибки JSON + + + + + + Patching of Snapmatic Properties failed because of I/O Error Не удалось измененить свойства Snapmatic из-за проблемы ввода/вывода - + + New Snapmatic title: Новый заголовок Snapmatic: - + + Snapmatic Crew Банда на Snapmatic - + + New Snapmatic crew: Новая банда на Snapmatic: @@ -1300,10 +2104,69 @@ Press 1 for Default View SnapmaticPicture - + PHOTO - %1 ФОТО - %1 + + + open file %1 + Открыть файл %1 + + + + header not exists + Отсутствует шапка (header) + + + + header is malformed + Шапка (header) повреждена + + + + picture not exists (%1) + Картинки не существует (%1) + + + + JSON not exists (%1) + JSON не существует (%1) + + + + title not exists (%1) + Заголовок отсутствует (%1) + + + + description not exists (%1) + Описание отсутствует (%1) + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + Чтение из файла %1 из-за %2 + + + + + JSON is incomplete and malformed + JSON не полный и повреждён + + + + + JSON is incomplete + JSON частично отсутствует + + + + + JSON is malformed + JSON повреждён + SnapmaticWidget @@ -1313,136 +2176,158 @@ Press 1 for Default View Виджет Snapmatic - + PHOTO - 00/00/00 00:00:00 ФОТО - 00/00/00 00:00:00 - + View picture Просмотр картинки - + View Просмотр - + Copy Копировать - + Export Экспорт - + Delete Удалить - - - + + + Delete picture Удалить картинку - + Are you sure to delete %1 from your Snapmatic pictures? Уверены, что хотите удалить %1 из коллекции картинок Snapmatic? - + Failed at deleting %1 from your Snapmatic pictures - Не удалось удалить %1 из колелкции картинок Snapmatic + Не удалось удалить %1 из колелкции картинок Snapmatic - + + Failed to hide %1 In-game from your Snapmatic pictures + Не удалось скрыть %1 из списка картинок Snapmatic в игре + + + + Failed to show %1 In-game from your Snapmatic pictures + Не удалось показать %1 в списке картинок Snapmatic в игре + + + + + Edi&t &Правка - + + + Show &In-game Показывать в &игре - + + + Hide &In-game Ск&рыть в игре - - &Edit Properties... - &Изменить свойства... - - - + &Export &Экспорт - - Export as &JPG picture... - Эксп&ортировать как картинку JPG... - - - - Export as &GTA Snapmatic... - Экс&портировать как GTA Snapmatic... - - - + &View По&казать - + &Remove У&далить - - + + &Select &Выделить - + + &Deselect Сн&ять выделение - - + + Select &All В&ыбрать все - + + &Deselect All Снять выбо&р со всех - + Copy picture Скопировать картинку - + Export picture Экспорт картинки - UserInterface + TelemetryDialog - gta5sync - %1 - gta5sync - %1 + + You want help %1 to improve in the future by including personal usage data in your submission? + Разрешишь нам собирать статистику о пользовании тобой %1? Это поможет нам в разработке. + + + %1 User Statistics + %1 Пользовательская статистика + + + + Yes, I want include personal usage data. + Да, передавать данные о пользовании программой. + + + + &OK + &ОК + + + + UserInterface Select profile @@ -1454,238 +2339,246 @@ Press 1 for Default View %1 %2 - + + Reload profile overview + Перезагрузить обзор профилей + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + Закрыть %1 + + + &File &Файл - + &Help &Справка - + &Edit &Правка - + &Profile П&рофиль - + &Exit В&ыход - + Exit Выход - + Close &Profile Закрыть п&рофиль - - Ctrl+End - Ctrl+End - - - + &Settings &Настройки - - Ctrl+Del - Ctrl+Del - - - + &Import files... &Импортировать файлы... - - Ctrl+I - Ctrl+I - - - - + + Select &GTA V Folder... Выбрать &папку GTA V... - - Ctrl+G - Ctrl+G - - - + Show In-gam&e Показывать в и&гре - - Shift+E - Shift+E - - - + Hi&de In-game Скры&ть в игре - - Shift+D - Shift+D + + + + Change &Players... + &Изменить игрока... - + + + + Change &Title... + Изменить &Заголовок... + + + + + + Change &Crew... + Изменить &банду... + + + + + + &Qualify as Avatar + &Пометить как Аватар + + + + + + &Close &Закрыть - + &Selection visibility - В&идимость выделение + В&идимость выделенного - + + Selection &mass tools + Инструменты &массовой выборки + + + Select &All В&ыбрать все - + &Deselect All Снять выбо&р со всех - + &Export selected... &Экпортировать выделенное... - + &Remove selected &Удалить выделенное - File - Файл - - - Help - Помощь - - - About gta5sync - О программе gta5sync - - - - Ctrl+A - Ctrl+A - - - Close - Закрыть - - - - Ctrl+Q - Ctrl+Q - - - - Ctrl+P - Ctrl+P - - - + &Open File... &Открыть файл... - - Ctrl+O - Ctrl+O - - - - Ctrl+S - Ctrl+S - - - - Ctrl+E - Ctrl+E - - - - Ctrl+D - Ctrl+D - - - - + + + Select Profile Выбор профиля - - - - + + + + Select GTA V Folder... Выбрать папку GTA V... - + %2 - %1 %2 - %1 - - + + + &About %1 - &О %1 + &О программе %1 - + + + &Donate + По&жертвовать + + + + Donate + Пожертвовать + + + + Donation methods + Способы для взноса + + + + &Copy + &Копировать + + + View + Просмотр + + + Copy + Копировать + + + Open File... Открыть файл... - - - - + + + + Open File Открыть файл - + Can't open %1 because of not valid file format Не удалось открыть %1 из-за неверного формата файла - gta5sync - gta5sync + + %1 - Messages + %1 - Новости - + &Reload Пере&загрузить - GTA V Folder not found! - Папка GTA V не была найдена! + + + + Show In-game + Показывать в игре + + + + + + Hide In-game + Скрыть в игре diff --git a/res/gta5sync_uk.qm b/res/gta5sync_uk.qm new file mode 100644 index 0000000..07fed50 Binary files /dev/null and b/res/gta5sync_uk.qm differ diff --git a/res/gta5sync_uk.ts b/res/gta5sync_uk.ts new file mode 100644 index 0000000..c49c9ce --- /dev/null +++ b/res/gta5sync_uk.ts @@ -0,0 +1,2583 @@ + + + + + AboutDialog + + + About %1 + Про %1 + + + + <span style="font-weight:600">%1</span><br/> +<br/> +%2<br/> +<br/> +Version %3<br/> +Created on %4<br/> +Built with Qt %5<br/> +Running with Qt %6<br/> +<br/> +%7 + <span style="font-weight:600">%1</span><br/> +<br/> +%2<br/> +<br/> +Версія %3<br/> +Створено %4<br/> +Побудовано з Qt %5<br/> +Виконується на Qt %6<br/> +<br/> +%7 + + + + &Close + &Закрити + + + + Translated by %1 + Translated by translator, example Translated by Syping + Переклад %1 + + + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + PROFessoR 'AppleSOft',https://steamcommunity.com/id/AppleSOft +VenJam1n,https://socialclub.rockstargames.com/member/--VenJam1n-- +twitter,https://twitter.com/_VenJam1n + VenJam1n,g5e://about?VmVuSmFtMW4:U3RlYW06IDxhIGhyZWY9Imh0dHBzOi8vc3RlYW1jb21tdW5pdHkuY29tL3Byb2ZpbGVzLzc2NTYxMTk3OTg0NjM1ODE2LyI+UFJPRmVzc29SICdBcHBsZVNPZnQnPC9hPjxici8+U29jaWFsIENsdWI6IDxhIGhyZWY9Imh0dHBzOi8vc29jaWFsY2x1Yi5yb2Nrc3RhcmdhbWVzLmNvbS9tZW1iZXIvLS1WZW5KYW0xbi0tLzU2Mzc1NjkiPlZlbkphbTFuPC9hPjxici8+VHdpdHRlcjogPGEgaHJlZj0iaHR0cHM6Ly90d2l0dGVyLmNvbS9fVmVuSmFtMW4iPlZlbkphbTFuPC9hPjxici8+PGJyLz7Qn9C+0LbQtdGA0YLQstGD0LLQsNC90L3Rjzxici8+PGEgaHJlZj0iaHR0cHM6Ly9zdGVhbWNvbW11bml0eS5jb20vdHJhZGVvZmZlci9uZXc/cGFydG5lcj0yNDM3MDA4OCZ0b2tlbj1HSW16XzhRSyI+U3RlYW0gVHJhZGU8L2E + + + + A project for viewing Grand Theft Auto V Snapmatic<br/> +Pictures and Savegames + Проект для перегляду Grand Theft Auto V Snapmatic<br/> +зображень та сейвів + + + + Copyright &copy; <a href="%1">%2</a> %3 + Авторське право &copy; <a href="%1">%2</a> %3 + + + + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + %1 ліцензовано під <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + + + + Release + Реліз + + + + Release Candidate + Реліз-Кандидат + + + + Daily Build + Щоденна Збірка + + + + Developer + Розробник + + + + Beta + Бета + + + + Alpha + Альфа + + + + + Custom + Custom + + + + CrewDatabase + + + + No Crew + Без банди + + + + ExportDialog + + + Dialog + Діалог + + + + Export Format + Формат експорту + + + + &JPEG/PNG format + &JPEG/PNG формат + + + + GTA &Snapmatic format + GTA &Snapmatic формат + + + + Export Size + Експортувати розміром + + + + Default &Size + Стандартний &розмір + + + + &Desktop Size + &Розмір робочого столу + + + + &Custom Size + &Користувацький розмір + + + + Custom Size: + Користувацький розмір: + + + + x + x + + + + &Export + &Експорт + + + + &Close + &Закрити + + + + ImageEditorDialog + + + Overwrite Image... + Перезаписати зображення... + + + + Apply changes + Застосувати зміни + + + + &Overwrite + &Перезаписати + + + + Discard changes + Скасувати зміни + + + + &Close + &Закрити + + + + + + + Snapmatic Image Editor + Редактор Snapmatic зображень + + + + + Patching of Snapmatic Image failed because of I/O Error + Виправлення Snapmatic зображення не вдалося через I/O Error + + + + + Patching of Snapmatic Image failed because of Image Error + Виправлення Snapmatic зображення не вдалося через помилку картинки + + + + ImportDialog + + + Import... + Імпорт... + + + + Picture + Зображення + + + + Avatar + Аватар + + + + + Ignore Aspect Ratio + Ігнорувати співвідношення сторін + + + + Watermark + Водяний знак + + + Force Borderless + Примусово без рамок + + + + Background + Фон + + + + + + + Background Colour: <span style="color: %1">%1</span> + Фоновий колір: <span style="color: %1">%1</span> + + + + Select background colour + Виберіть колір фону + + + ... + ... + + + + + + + Background Image: + Фонове зображення: + + + + Select background image + Виберіть фонове зображення + + + + Remove background image + Видалити фонове зображення + + + X + Х + + + + Crop to Aspect Ratio + Обрізати під співвідношення сторін + + + + Force Colour in Avatar Zone + Примусовий колір в зоні Аватару + + + + Advanced + Додатково + + + + Resolution: + Розширення: + + + + Snapmatic resolution + Розширення Snapmatic + + + + Avoid compression and expand buffer instead, improves picture quality, but may break Snapmatic + Не стискати, а збільшити буфер. Поліпшить якість картинки, але може поламати Snapmatic + + + + Unlimited Buffer + Необмежений буфер + + + + Import as-is, don't change the picture at all, guaranteed to break Snapmatic unless you know what you doing + Імпортуати як є, взагалі не змінюється зображення, гарантовано зламається Snapmatic, тільки якщо не знаєте, що робите + + + + Import as-is + Імпортувати як є + + + + Import options + Параметри імпорту + + + + &Options + &Параметри + + + + Import picture + Імпортувати зображення + + + + &OK + &OK + + + + Discard picture + Відхилити зображення + + + + &Cancel + &Скасувати + + + + &Import new Picture... + &Імпортувати нове зображення... + + + + &Crop Picture... + &Обрізати зображення... + + + + &Load Settings... + &Завантажити параметри... + + + + &Save Settings... + &Зберегти параметри... + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + Користувацький Аватар + + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + Користувацьке Зображення + + + + Storage + Background Image: Storage + Зберігання + + + + Crop Picture... + Обрізати зображення... + + + + &Crop + &Обрізати + + + + Crop Picture + Обрізати зображення + + + + + Please import a new picture first + Спершу імпортуйте нове зображення + + + + + Default + Default as Default Profile + Стандартний + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + Профіль %1 + + + + + Load Settings... + Завантажити параметри... + + + + + Save Settings... + Зберегти параметри... + + + + + Snapmatic Avatar Zone + Зона Snapmatic Аватару + + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + Ви впевнені, що будете використовувати квадратне зображення поза зоною аватара? +Якщо ви хочете використовувати його як Аватар, зображення буде відокремлено! + + + + Select Colour... + Вибір кольору... + + + + + Background Image: %1 + Фонове зображення: %1 + + + + + Please select your settings profile + Будь ласка, виберіть свій профіль налаштувань + + + + File + Background Image: File + Файл + + + + JsonEditorDialog + + + Snapmatic JSON Editor + JSON редактор Snapmatic + + + + Apply changes + Застосувати зміни + + + + &Save + &Зберегти + + + + Discard changes + Скасувати зміни + + + + &Close + &Закрити + + + + JSON Error + JSON помилка + + + + MapLocationDialog + + + Snapmatic Map Viewer + Перегляд карти Snapmatic + + + + Close viewer + Закрити переглядач + + + + &Close + &Закрити + + + + Apply new position + Застосувати нову позицію + + + + &Apply + &Застосувати + + + + Revert old position + Повернути стару позицію + + + + &Revert + &Повернути + + + + Select new position + Виберіть нову позицію + + + + &Select + &Виділення + + + + Quit select position + Вийти з вибору позиції + + + + &Done + &Готово + + + + X: %1 +Y: %2 + X and Y position + X: %1 +Y: %2 + + + + OptionsDialog + + + %1 - Settings + %1 - Налаштування + + + + Profiles + Профілі + + + + Content Open/Select Mode + Відкривати/обирати вміст + + + Open with Singleclick + Відкривати одиночним кліком + + + + Open with Doubleclick + Відкривати подвійним кліком + + + Select with Singleclick + Обирати одиночним кліком + + + + Default Profile + Типовий профіль + + + + Custom GTA V Folder + Користувацька GTA V тека + + + + Force using Custom Folder + Використовувати цю теку GTA V + + + + ... + ... + + + + Pictures + Зображення + + + + Export Size + Розмір при експорті + + + + Default: %1x%2 + Стандартно: %1x%2 + + + + Screen Resolution: %1x%2 + Розширення дисплея: %1x%2 + + + + + Custom Size: + Користувацький розмір: + + + + x + x + + + + Ignore Aspect Ratio + Ігнорувати співвідношення сторін + + + + Export Quality + Якість при експорті + + + + Enable Custom Quality + Увімкнути користувацьку якість + + + + Quality: + Якість: + + + + %1% + %1% + + + + Picture Viewer + Переглядач зображень + + + + Enable Navigation Bar + Увімкнути навігаціїйну панель + + + + Players + Гравці + + + + ID + ID + + + + Name + Ім'я + + + + Game + Гра + + + + Social Club Version + Social Club версія + + + + + + + + + + + Found: %1 + Знайдено:%1 + + + + + + + + + + + + + Language: %1 + Мова: %1 + + + + Steam Version + Steam версія + Steam Version + + + + Feedback + Опитування + + + + Participation + Участь + + + + + Participate in %1 User Statistics + Опитування %1 про устаткування ПК + + + + Categories + Категорії + + + + Hardware, Application and OS Specification + Обладнання, випуск програми, специфікації ОС + + + + System Language Configuration + Мовні налаштування системи + + + + Application Configuration + Налаштування програми + + + + Personal Usage Data + Особисті дані використання + + + + Other + Інше + + + + + + Participation ID: %1 + ID учасника : %1 + + + + &Copy + &Копіювати + + + + Interface + Інтерфейс + + + + Language for Interface + Мова інтерфейсу + + + + + + + Current: %1 + Зараз: %1 + + + + Language for Areas + Мова перекладу розташування + + + + Style + Стиль + + + + Use Default Style (Restart) + Використовувати стандартний стиль (Перезапуск) + + + + Style: + Стиль: + + + + Font + Шрифт + + + + Use Default Font (Restart) + Використовувати стандартний шрифт (Перезапуск) + + + + Font: + Шрифт: + + + Always use Message Font (Windows 2003 and earlier) + Завжди використовуйте шрифт повідомлень (Windows 2003 і раніше) + + + + Apply changes + Застосувати зміни + + + + &OK + OK, Cancel, Apply + &OK + + + + Discard changes + Скасувати зміни + + + + &Cancel + OK, Cancel, Apply + &Скасувати + + + + System + System in context of System default + Як у системи + + + + %1 (Game language) + Next closest language compared to the Game settings + %1 (Мова гри) + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + %1 (Співпадає з інтерфейсом) + + + + + + Auto + Automatic language choice. + Автоматично + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + %1 (пріоритет мови) + + + + %1 + %1 + %1 + + + + The new Custom Folder will initialise after you restart %1. + Нова користувацька папка буде ініціалізована після перезапуску %1. + + + + No Profile + No Profile, as default + Жодного + + + + + + Profile: %1 + Профіль: %1 + + + + View %1 User Statistics Online + Переглянути користувацьку статистику %1 онлайн + + + + Not registered + Не зареєстрований + + + + + + + Yes + Так + + + + + No + Ні + + + + + OS defined + Визначається ОС + + + + + Steam defined + Визначається Steam + + + + PictureDialog + + + Snapmatic Picture Viewer - %1 + Переглядач зображень Snapmatic - %1 + + + + <span style="font-weight:600">Title: </span>%6<br/> +<span style="font-weight:600">Location: </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">Players: </span>%4 (Crew %5)<br/> +<span style="font-weight:600">Created: </span>%8 + <span style="font-weight:600">Назва: </span>%6<br/> +<span style="font-weight:600">Розташування: </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">Гравці: </span>%4 (Банда %5)<br/> +<span style="font-weight:600">Створено: </span>%8 + + + + Manage picture + Керування зображенням + + + + &Manage + &Керувати + + + + Close viewer + Закрити переглядач + + + + &Close + &Закрити + + + + + Export as &Picture... + Експортувати як &зображення... + + + + + Export as &Snapmatic... + Експортувати як &Snapmatic... + + + + + &Edit Properties... + &Змінити властивості... + + + + + &Overwrite Image... + &Перезаписати зображення... + + + + + Open &Map Viewer... + Відкрити &карту... + + + + + Open &JSON Editor... + Відкрити редактор &JSON... + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + Клавіша 1 - Режим показу аватарки +Клавіша 2 - Вкл./Викл. Оверлей +Стрілки - Навігація + + + + Snapmatic Picture Viewer + Переглядач фотографій Snapmatic + + + + Failed at %1 + Помилка на%1 + + + + + + No Players + Гравців немає + + + + + No Crew + Банди немає + + + + Unknown Location + Невідома локація + + + + Avatar Preview Mode +Press 1 for Default View + Режим для аватарок +Натисніть 1 для стандартного перегляду + + + + Export as Picture... + Експортувати як зображення... + + + + + Export + Експорт + + + + JPEG Graphics (*.jpg *.jpeg) + JPEG Graphics (*.jpg *.jpeg) + + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + + + + + + + Export as Picture + Експортувати як зображення + + + + + Overwrite %1 with current Snapmatic picture? + Перезаписати %1 поточним Snapmatic зображенням? + + + + Failed to export the picture because the system occurred a write failure + Не вдалося експортувати зображення, оскільки в системі виникла помилка запису + + + + Failed to export the picture because the format detection failures + Не вдалося експортувати зображення через помилки виявлення формату + + + + Failed to export the picture because the file can't be written + Не вдалося експортувати зображення, оскільки файл не може бути записаний + + + + Failed to export the picture because of an unknown reason + Не вдалося експортувати зображення через невідому причину + + + + + No valid file is selected + Вибрано невірний файл + + + + Export as Snapmatic... + Експортувати як Snapmatic... + + + + GTA V Export (*.g5e) + GTA V Export (*.g5e) + + + + GTA V Raw Export (*.auto) + GTA V RAW-експорт (*.auto) + + + + Snapmatic pictures (PGTA*) + Snapmatic картинки (PGTA*) + + + + + + + + Export as Snapmatic + Експортувати як Snapmatic + + + + + Failed to export current Snapmatic picture + Не вдалося експортувати поточну фотографію Snapmatic + + + + Exported Snapmatic to "%1" because of using the .auto extension. + Експортується Snapmatic до "%1" через використання .auto розширення. + + + + PlayerListDialog + + + Edit Players... + Редагувати гравців... + + + + Available Players: + Доступні гравці: + + + + Selected Players: + Вибрані гравці: + + + + &Apply + &Застосувати + + + + &Cancel + &Скасувати + + + + Add Players... + Додати гравців... + + + + Failed to add more Players because the limit of Players are %1! + Не вдалося додати більше гравців, бо ліміт %1! + + + + + Add Player... + Додати гравця... + + + + Enter Social Club Player ID + Введіть ID гравця Social Club + + + + Failed to add Player %1 because Player %1 is already added! + Не вдалося додати гравця %1, оскільки %1 вже доданий! + + + + ProfileInterface + + + Profile Interface + Інтерфейс профілю + + + + Loading file %1 of %2 files + Завантаження файлу %1 з %2 файлів + + + + %1 %2 + %1 %2 + + + + Import file + Імпортувати файл + + + + &Import... + &Імпортувати... + + + + Close profile + Закрити профіль + + + + &Close + &Закрити + + + + + + Export file %1 of %2 files + Експортується файл %1 з %2 файлів + + + + + + + + + + + + + + + + + + + + + + Import... + Імпортування... + + + + + + + + + Import + Імпорт + + + + + + All image files (%1) + Файли зображень (%1) + + + + + + + All files (**) + Усі файли (**) + + + + + + Can't import %1 because file can't be open + Неможливо імпортувати %1, оскільки файл не може бути відкритий + + + + + + Can't import %1 because file can't be parsed properly + Неможливо імпортувати %1, оскільки файл неможливо розібрати правильно + + + + Enabled pictures: %1 of %2 + Увімкнено фотографії:%1 з%2 + + + + Loading... + Завантаження... + + + + Snapmatic Loader + Snapmatic Loader + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + <h4>Наступні Snapmatic зображення були відновлені</h4>%1 + + + + Importable files (%1) + Імпортуються файли (%1) + + + + + GTA V Export (*.g5e) + GTA V Export (*.g5e) + + + + + Savegames files (SGTA*) + Файли збереження гри (SGTA*) + + + + + Snapmatic pictures (PGTA*) + Snapmatic зображення (PGTA*) + + + + + + No valid file is selected + Вибрані недійсні файли + + + + + Import file %1 of %2 files + Імпортується файл %1 з %2 файлів + + + + Import failed with... + +%1 + Не вдалося імпортувати тому що... + +%1 + + + + + Failed to read Snapmatic picture + Не вдалося прочитати Snapmatic картинку + + + + + Failed to read Savegame file + Не вдалося прочитати файл збереження гри + + + + Can't import %1 because file format can't be detected + Неможливо імпортувати%1, оскільки формат файлу не може бути виявлений + + + + Failed to import the Snapmatic picture, file not begin with PGTA or end with .g5e + Не вдалося імпортувати зображення Snapmatic, файл не починається з PGTA або закінчується .g5e + + + + Failed to import the Snapmatic picture, can't copy the file into profile + Не вдалося імпортувати зображення Snapmatic, не можна скопіювати файл у профіль + + + + Failed to import the Savegame, can't copy the file into profile + Не вдалося імпортувати Сейв, не можна скопіювати файл у профіль + + + + Failed to import the Savegame, no Savegame slot is left + Не вдалося імпортувати Сейв, немає вільного слота + + + + + + + + Export selected... + Експорт обраних... + + + + + JPG pictures and GTA Snapmatic + JPG картинки і GTA Snapmatic + + + + + JPG pictures only + Тільки JPG картинки + + + + + GTA Snapmatic only + Тільки GTA Snapmatic + + + + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: + %1 Експортувати Snapmatic фотографії %2 <br><br> Фотографії JPG дозволяють відкривати зображення за допомогою засобу перегляду зображень<br>GTA Snapmatic дає змогу імпортувати зображення в гру<br><br>Експортувати як: + + + + Initialising export... + Ініціалізація експорту... + + + + Export failed with... + +%1 + Експортувати не вдалося тому що... + +%1 + + + + + No Snapmatic pictures or Savegames files are selected + Не вибрано жодного Snapmatic зображення або файлу збереження + + + + + + Remove selected + Видалити вибрані + + + + You really want remove the selected Snapmatic picutres and Savegame files? + Ви дійсно хочете видалити вибрані Snapmatic фотографії та файли збереження гри? + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + Не вдалося видалити всі обрані Snapmatic фотографії та/або Сейви + + + + + + + + + No Snapmatic pictures are selected + Не вибрано жодного Snapmatic зображення + + + + + + + + + %1 failed with... + +%2 + Action failed with... + %1 не вдалося з... + +%2 + + + + Prepare Content for Import... + Підготувати контент для імпорту ... + + + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + Snapmatic зображення з uid %1 вже існує, ви хочете призначити для імпорту новий uid та мітку часу? + + + + + Qualify as Avatar + Позначити як Аватар + + + + + + + Patch selected... + Вибір патчу... + + + + + + + + + + + Patch file %1 of %2 files + Патч файлу %1 з %2 файлів + + + + Qualify + %1 failed with... + Якість + + + + + Change Players... + Зміна гравців... + + + + Change Players + %1 failed with... + Змінити гравців + + + + + + Change Crew... + Зміна банди... + + + + Failed to enter a valid Snapmatic Crew ID + Не вдалося ввести дійсний ID Банди Snapmatic + + + + Change Crew + %1 failed with... + Змінити банду + + + + + + Change Title... + Зміна назви... + + + + Failed to enter a valid Snapmatic title + Не вдалося ввести дійсний заголовок Snapmatic + + + + Change Title + %1 failed with... + Змінити назву + + + + All profile files (*.g5e SGTA* PGTA*) + Усі файли зображень (*.g5e SGTA* PGTA*) + + + + QApplication + + Font + Шрифт + + + Selected Font: %1 + Вибраний шрифт:%1 + + + <h4>Welcome to %1!</h4>You want to configure %1 before you start using it? + <h4>Ласкаво просимо до %1!</h4>Ви хочете налаштувати %1 перед використанням? + + + + SavegameDialog + + + + Savegame Viewer + Перегляд файлів збереження гри + + + + <span style="font-weight:600">Savegame</span><br><br>%1 + <span style="font-weight:600">Ігрове збереження</span><br><br>%1 + + + + &Export + &Експорт + + + + &Close + &Закрити + + + + Failed at %1 + Помилка на %1 + + + + SavegameWidget + + + Savegame Widget + Віджет збереження гри + + + + SAVE %3 - %1<br>%2 + ЗБЕРЕЖЕННЯ %3 - %1<br>%2 + + + + View savegame + Переглянути ігрове збереження + + + + View + Перегляд + + + + Copy savegame + Скопіювати файл збереження + + + + + Export + Експорт + + + + Delete savegame + Видалити сейв + + + + Delete + Видалити + + + + &View + &Перегляд + + + + + + &Export + &Експорт + + + + + + &Remove + &Видалення + + + + + &Select + &Виділення + + + + + &Deselect + &Зняти виділення + + + + + Select &All + Вибрати &усі + + + + + &Deselect All + &Зняти виділення усіх + + + + Savegame files (SGTA*) + Файли збереження гри (SGTA*) + + + + All files (**) + Усі файли (**) + + + + + + + Export Savegame + Експорт файлу збереження + + + + Overwrite %1 with current Savegame? + Перезаписати %1 поточним ігровим збереженням? + + + + Failed to overwrite %1 with current Savegame + Не вдалося перезаписати %1 поточним збереженням гри + + + + Failed to export current Savegame + Не вдалося експортувати поточне ігрове збереження + + + + No valid file is selected + Вибрано невірний файл + + + + Export Savegame... + Експортування файлу збереження... + + + + + AUTOSAVE - %1 +%2 + АВТОМАТИЧНЕ ЗБЕРЕЖЕННЯ - %1 +%2 + + + + + SAVE %3 - %1 +%2 + ЗБЕРЕЖЕННЯ %3 - %1 +%2 + + + + + WRONG FORMAT + НЕПРАВИЛЬНИЙ ФОРМАТ + + + + UNKNOWN + НЕВІДОМИЙ + + + + + Delete Savegame + Видалити файл збереження + + + + Are you sure to delete %1 from your savegames? + Ви впевнені, що хочете видалити %1 зі своїх сейвів? + + + + Failed at deleting %1 from your savegames + Не вдалося видалити %1 із ваших збережених ігор + + + + SnapmaticEditor + + + + + + + + + + + Snapmatic Properties + Властивості Snapmatic + + + + Snapmatic Type + Тип Snapmatic + + + + Editor + Редактор + + + + Selfie + Автопортрет + + + + Regular + Звичайний + + + + Mugshot + Кухоль постріл + + + + Meme + Мем + + + + Director + Режисер + + + + Snapmatic Values + Значення в Snapmatic + + + + Extras + Додатково + + + + Qualify as Avatar automatically at apply + При застосуванні налаштувань помітити як Аватар + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + З міткою як Аватар можливо завантажити це зображення Snapmatic в профіль Social Club + + + + Apply changes + Застосувати зміни + + + + &Apply + &Застосувати + + + + Discard changes + Скасувати зміни + + + + &Cancel + &Скасувати + + + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + <h4> Виявлені незбережені зміни </h4> Ви хочете зберегти вміст JSON перед тим, як вийти? + + + + Patching of Snapmatic Properties failed because of %1 + Змінити властивості Snapmatic не вдалося тому що%1 + + + + + + + Patching of Snapmatic Properties failed because of I/O Error + Змінити властивості Snapmatic не вдалося через I/O Помилку + + + + Patching of Snapmatic Properties failed because of JSON Error + Змінити властивості Snapmatic не вдалося через JSON Помилку + + + + + Snapmatic Crew + Snapmatic банда + + + + + New Snapmatic crew: + Нова Snapmatic банда: + + + + + Snapmatic Title + Snapmatic назва + + + + + New Snapmatic title: + Новий Snapmatic заголовок: + + + + + + Edit + Правка + + + + Players: %1 (%2) + Multiple Player are inserted here + Гравці: %1 (%2) + + + + Player: %1 (%2) + One Player is inserted here + Гравець: %1 (%2) + + + + Title: %1 (%2) + Назва: %1 (%2) + + + + + Appropriate: %1 + Підходить: %1 + + + + Yes + Yes, should work fine + Так + + + + No + No, could lead to issues + Ні + + + + Crew: %1 (%2) + Банда: %1 (%2) + + + + SnapmaticPicture + + + + JSON is incomplete and malformed + JSON неповний та неправильний + + + + + JSON is incomplete + JSON неповний + + + + + JSON is malformed + JSON неправильний + + + + PHOTO - %1 + ФОТО - %1 + + + + open file %1 + відкрити файл%1 + + + + header not exists + заголовок не існує + + + + header is malformed + заголовок неправильний + + + + picture not exists (%1) + зображення не існує (%1) + + + + JSON not exists (%1) + JSON не існує (%1) + + + + title not exists (%1) + заголовок не існує (%1) + + + + description not exists (%1) + опис не існує (%1) + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + читання файлу %1 тому що %2 + + + + SnapmaticWidget + + + Snapmatic Widget + Віджет Snapmatic + + + + PHOTO - 00/00/00 00:00:00 + ФОТО - 00/00/00 00:00:00 + + + + View picture + Переглянути зображення + + + + View + Перегляд + + + + Copy picture + Копіювати фото + + + + Copy + Копіювати + + + + Export picture + Експорт фото + + + + Export + Експорт + + + + + + Delete picture + Видалити фото + + + + Delete + Видалити + + + + + + Edi&t + Редагува&ти + + + + + + Show &In-game + Показати &у грі + + + + + + Hide &In-game + Сховати &у грі + + + + &Export + &Експортувати + + + + &View + &Переглянути + + + + &Remove + &Видалити + + + + + &Select + &Виділення + + + + + &Deselect + &Зняти виділення + + + + + Select &All + Вибрати &усі + + + + + &Deselect All + &Зняти виділення усіх + + + + Are you sure to delete %1 from your Snapmatic pictures? + Ви дійсно бажаєте видалити %1 з ваших Snapmatic фотографій? + + + + Failed at deleting %1 from your Snapmatic pictures + Не вдалося видалити%1 з ваших Snapmatic фотографій + + + + Failed to hide %1 In-game from your Snapmatic pictures + Не вдалося сховати %1 Snapmatic у грі + + + + Failed to show %1 In-game from your Snapmatic pictures + Не вдалося показати %1 Snapmatic у грі + + + + TelemetryDialog + + + You want help %1 to improve in the future by including personal usage data in your submission? + Ви хочете допомогти %1 покращитись у майбутньому, включивши дані особистого користування? + + + + %1 User Statistics + %1 Статистика користувачів + + + + Yes, I want include personal usage data. + Так, я хочу включити дані особистого користування. + + + + &OK + &OK + &OK + + + + UserInterface + + + + %2 - %1 + %2 - %1 + + + + Select profile + Виберіть профіль + + + + %1 %2 + %1 %2 + + + + Reload profile overview + Перезавантажити огляд профілю + + + + &Reload + &Перезавантажити + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + Закрити %1 + + + + + + + &Close + &Закрити + + + + &File + &Файл + + + + &Help + &Справка + + + + &Edit + &Правка + + + + &Profile + &Профіль + + + + &Selection visibility + &Вибір видимості + + + + Selection &mass tools + Інструменти &масової вибірки + + + + + + &About %1 + &Про %1 + + + + &Exit + &Вихід + + + + Exit + Вихід + + + + Close &Profile + Закрити &профіль + + + + &Settings + &Налаштування + + + + Select &All + Вибрати &все + + + + &Deselect All + &Скасувати вибір усіх + + + + &Export selected... + &Експорт вибраного... + + + + &Remove selected + &Видалити вибране + + + + &Import files... + &Імпорт файлів... + + + + &Open File... + &Відкрити файл... + + + + + Select &GTA V Folder... + Вибрати &GTA V теку... + + + + + + + Select GTA V Folder... + Вибрати GTA V теку... + + + + Show In-gam&e + &Показати у грі + + + + Hi&de In-game + &Приховати у грі + + + + + + Change &Title... + Змінити &заголовок... + + + + + + Change &Crew... + Змінити &банду... + + + + + + &Qualify as Avatar + Позначити як &аватар + + + + + + Change &Players... + Змінити &гравців... + + + + + + Show In-game + Показати у грі + + + + + + Hide In-game + Сховати у грі + + + + + + Select Profile + Вибрати профіль + + + + + &Donate + &Пожертвування + + + + Donate + Пожертвування + + + + Donation methods + Метод пожертвування + + + View + Перегляд + + + Copy + Копіювати + + + + &Copy + &Копіювати + + + + Open File... + Відкрити файл... + + + + + + + Open File + Відкрити файл + + + + Can't open %1 because of not valid file format + Неможливо відкрити %1 через невідомий формат файлу + + + + %1 - Messages + %1 - Новини + + + diff --git a/res/gta5sync_zh_TW.qm b/res/gta5sync_zh_TW.qm new file mode 100644 index 0000000..d4eb70d Binary files /dev/null and b/res/gta5sync_zh_TW.qm differ diff --git a/res/gta5sync_zh_TW.ts b/res/gta5sync_zh_TW.ts new file mode 100644 index 0000000..fb4d4bc --- /dev/null +++ b/res/gta5sync_zh_TW.ts @@ -0,0 +1,2564 @@ + + + + + AboutDialog + + + About %1 + 關於 %1 + + + + <span style="font-weight:600">%1</span><br/> +<br/> +%2<br/> +<br/> +Version %3<br/> +Created on %4<br/> +Built with Qt %5<br/> +Running with Qt %6<br/> +<br/> +%7 + <span style="font-weight:600">%1</span><br/> +<br/> +%2<br/> +<br/> +版本:%3<br/> +建置於 %4<br/> +使用 Qt %5 建置<br/> +使用 Qt %6 執行<br/> +<br/> +%7 + + + + &Close + 關閉(&C) + + + + Translated by %1 + Translated by translator, example Translated by Syping + 繁體中文化: %1 + + + + TRANSLATOR + Insert your name here and profile here in following scheme, First Translator,First Profile\nSecond Translator\nThird Translator,Second Profile + Ray,https://steamcommunity.com/profiles/76561198282701714/ + + + + A project for viewing Grand Theft Auto V Snapmatic<br/> +Pictures and Savegames + 一個 Grand Theft Auto V Snapmatic 圖片、遊戲存檔檢視專案 + + + + Copyright &copy; <a href="%1">%2</a> %3 + 版權 &copy; <a href="%1">%2</a> %3 + + + + %1 is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> + %1 使用 <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> 授權條款發布 + + + + Release + 正式版本 + + + + Release Candidate + 最終測試版本 + + + + Daily Build + 每日建置版本 + + + + Developer + 開發版本 + + + + Beta + Beta 版本 + + + + Alpha + Alpha 版本 + + + + + Custom + 自訂 + + + + CrewDatabase + + + + No Crew + + + + + ExportDialog + + + Dialog + 對話 + + + + Export Format + 匯出格式 + + + + &JPEG/PNG format + JPEG/PNG 格式(&J) + + + + GTA &Snapmatic format + GTA Snapmatic 格式(&S) + + + + Export Size + 匯出尺寸 + + + + Default &Size + 預設(&S) + + + + &Desktop Size + 桌面尺寸(&D) + + + + &Custom Size + 自訂尺寸(&C) + + + + Custom Size: + 自訂尺寸: + + + + x + x + + + + &Export + 匯出(&E) + + + + &Close + 關閉(&C) + + + + ImageEditorDialog + + + Overwrite Image... + 修改圖片... + + + + Apply changes + 套用變更 + + + + &Overwrite + 修改(&O) + + + + Discard changes + 捨棄變更 + + + + &Close + 關閉(&C) + + + + + + + Snapmatic Image Editor + Snapmatic 圖片編輯器 + + + + + Patching of Snapmatic Image failed because of I/O Error + I/O 錯誤,Snapmatic 圖片更新失敗 + + + + + Patching of Snapmatic Image failed because of Image Error + 圖片錯誤,Snapmatic 圖片更新失敗 + + + + ImportDialog + + + Import... + 匯入... + + + + Picture + 圖片 + + + + Avatar + 大頭貼 + + + + + Ignore Aspect Ratio + 忽略長寬比 + + + + Watermark + 浮水印 + + + Force Borderless + 強制無邊框 + + + + Background + 背景 + + + + + + + Background Colour: <span style="color: %1">%1</span> + 背景顏色: <span style="color: %1">%1</span> + + + + Select background colour + 選擇背景顏色 + + + ... + ... + + + + + + + Background Image: + 背景圖片: + + + + Select background image + 選擇背景圖片 + + + + Remove background image + 移除背景圖片 + + + X + X + + + + Crop to Aspect Ratio + 裁剪長寬比 + + + + Force Colour in Avatar Zone + 強制在大頭貼區域使用顏色 + + + + Advanced + 進階 + + + + Resolution: + 解析度: + + + + Snapmatic resolution + Snapmatic 解析度 + + + + Avoid compression and expand buffer instead, improves picture quality, but may break Snapmatic + 避免壓縮來提高圖片品質,但可能會破壞 Snapmatic 運作 + + + + Unlimited Buffer + 無限緩衝 + + + + Import as-is, don't change the picture at all, guaranteed to break Snapmatic unless you know what you doing + 除非你知道自己在幹什麼,否則修改圖片將使 Snapmatic 故障 + + + + Import as-is + 照原樣匯入 + + + + Import options + 匯入選項 + + + + &Options + 選項(&O) + + + + Import picture + 匯入圖片 + + + + &OK + 確定(&O) + + + + Discard picture + 捨棄圖片 + + + + &Cancel + 取消(&C) + + + + &Import new Picture... + 匯入新圖片(&I)... + + + + &Crop Picture... + 裁剪圖片(&C)... + + + + &Load Settings... + 載入設定(&L)... + + + + &Save Settings... + 儲存設定(&S)... + + + + + Custom Avatar + Custom Avatar Description in SC, don't use Special Character! + 自訂大頭貼 + + + + + + Custom Picture + Custom Picture Description in SC, don't use Special Character! + 自訂圖片 + + + + Storage + Background Image: Storage + 儲存 + + + + Crop Picture... + 裁剪圖片... + + + + &Crop + 裁剪(&C) + + + + Crop Picture + 裁剪圖片 + + + + + Please import a new picture first + 請先匯入新圖片 + + + + + Default + Default as Default Profile + 預設 + + + + + + + + + + + + + + + + + + + + + + + Profile %1 + Profile %1 as Profile 1 + 設定檔 %1 + + + + + Load Settings... + 載入設定... + + + + + Save Settings... + 儲存設定... + + + + + Snapmatic Avatar Zone + Snapmatic 大頭貼區域 + + + + + Are you sure to use a square image outside of the Avatar Zone? +When you want to use it as Avatar the image will be detached! + 你確定要在大頭貼區域以外的地方使用方形圖片嗎? 作為大頭貼的圖片將被分離! + + + + Select Colour... + 選擇顏色... + + + + + Background Image: %1 + 背景圖片: %1 + + + + + Please select your settings profile + 請選擇設定檔 + + + + File + Background Image: File + 文件 + + + + JsonEditorDialog + + + Snapmatic JSON Editor + Snapmatic JSON 編輯器 + + + + Apply changes + 套用變更 + + + + &Save + 保存(&S) + + + + Discard changes + 捨棄變更 + + + + &Close + 關閉(&C) + + + + JSON Error + JSON 錯誤 + + + + MapLocationDialog + + + Snapmatic Map Viewer + Snapmatic 地圖檢視器 + + + + Close viewer + 關閉檢視器 + + + + &Close + 關閉(&C) + + + + Apply new position + 套用新位置 + + + + &Apply + 套用(&A) + + + + Revert old position + 還原舊位置 + + + + &Revert + 還原(&R) + + + + Select new position + 選擇新位置 + + + + &Select + 選擇(&S) + + + + Quit select position + 離開位置選擇 + + + + &Done + 完成(&D) + + + + X: %1 +Y: %2 + X and Y position + X: %1 +Y: %2 + + + + OptionsDialog + + + %1 - Settings + %1 - 設定 + + + + Profiles + 設定檔 + + + + Content Open/Select Mode + 開啟/選擇模式 + + + Open with Singleclick + 點一次開啟 + + + + Open with Doubleclick + 點兩次開啟 + + + Select with Singleclick + 點一次選取 + + + + Default Profile + 預設設定檔 + + + + Custom GTA V Folder + 自訂 GTA V 資料夾 + + + + Force using Custom Folder + 強制使用自訂資料夾 + + + + ... + ... + + + + Pictures + 圖片 + + + + Export Size + 匯出尺寸 + + + + Default: %1x%2 + 預設: %1x%2 + + + + Screen Resolution: %1x%2 + 螢幕解析度: %1x%2 + + + + + Custom Size: + 自訂尺寸: + + + + x + x + + + + Ignore Aspect Ratio + 忽略長寬比 + + + + Export Quality + 匯出品質 + + + + Enable Custom Quality + 啟用自訂品質 + + + + Quality: + 品質: + + + + %1% + %1% + + + + Picture Viewer + 圖片檢視器 + + + + Enable Navigation Bar + 啟用導航欄 + + + + Players + 玩家 + + + + ID + ID + + + + Name + 名稱 + + + + Game + 遊戲 + + + + Social Club Version + Social Club 版 + + + + + + + + + + + Found: %1 + 找到: %1 + + + + + + + + + + + + + Language: %1 + 語言: %1 + + + + Steam Version + Steam 版 + + + + Feedback + 反饋 + + + + Participation + 參與 + + + + + Participate in %1 User Statistics + 參與 %1 使用者統計 + + + + Categories + 分類 + + + + Hardware, Application and OS Specification + 硬體、軟體和 OS 規格 + + + + System Language Configuration + 系統語言設定 + + + + Application Configuration + 應用程式設定 + + + + Personal Usage Data + 個人使用數據 + + + + Other + 其他 + + + + + + Participation ID: %1 + 參與 ID: %1 + + + + &Copy + 複製(&C) + + + + Interface + 介面 + + + + Language for Interface + 介面語言 + + + + + + + Current: %1 + 目前: %1 + + + + Language for Areas + 區域語言 + + + + Style + 樣式 + + + + Use Default Style (Restart) + 使用預設樣式 (需重新啟動) + + + + Style: + 樣式: + + + + Font + 字體 + + + + Use Default Font (Restart) + 使用預設字體 (需重新啟動) + + + + Font: + 字體: + + + Always use Message Font (Windows 2003 and earlier) + 總是使用訊息字體 (Windows 2003 和更早版本) + + + + Apply changes + 套用變更 + + + + &OK + OK, Cancel, Apply + 確定(&O) + + + + Discard changes + 捨棄變更 + + + + &Cancel + OK, Cancel, Apply + 取消(&C) + + + + System + System in context of System default + 系統 + + + + + %1 (Closest to Interface) + Next closest language compared to the Interface + %1 (與介面接近的語言) + + + + + + Auto + Automatic language choice. + 自動 + + + + %1 (Language priority) + First language a person can talk with a different person/application. "Native" or "Not Native". + %1 (語言優先) + + + + %1 (Game language) + Next closest language compared to the Game settings + %1 (遊戲語言) + + + + %1 + %1 + %1 + + + + The new Custom Folder will initialise after you restart %1. + 自訂資料夾將在 %1 重新啟動後初始化. + + + + No Profile + No Profile, as default + + + + + + + Profile: %1 + 設定檔: %1 + + + + View %1 User Statistics Online + 檢視 %1 使用者統計資訊 + + + + Not registered + 未註冊參與 + + + + + + + Yes + + + + + + No + + + + + + OS defined + 系統定義 + + + + + Steam defined + Steam 定義 + + + + PictureDialog + + + Snapmatic Picture Viewer - %1 + Snapmatic 圖片檢視器 - %1 + + + + <span style="font-weight:600">Title: </span>%6<br/> +<span style="font-weight:600">Location: </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">Players: </span>%4 (Crew %5)<br/> +<span style="font-weight:600">Created: </span>%8 + <span style="font-weight:600">標題: </span>%6<br/> +<span style="font-weight:600">地點: </span>%7 (%1, %2, %3)<br/> +<span style="font-weight:600">玩家: </span>%4 (Crew %5)<br/> +<span style="font-weight:600">建立於: </span>%8 + + + + Manage picture + 管理圖片 + + + + &Manage + 管理(&M) + + + + Close viewer + 關閉檢視器 + + + + &Close + 關閉(&C) + + + + + Export as &Picture... + 匯出成圖片(&P)... + + + + + Export as &Snapmatic... + 匯出成 Snapmatic(&S)... + + + + + &Edit Properties... + 編輯屬性(&E) ... + + + + + &Overwrite Image... + 修改圖片(&O)... + + + + + Open &Map Viewer... + 開啟地圖檢視器(&M)... + + + + + Open &JSON Editor... + 開啟 JSON 編輯器(&J)... + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + 數字鍵 1 - 大頭接預覽模式 +數字鍵 2 - 開關重疊層 +方向鍵 - 導覽 + + + + Snapmatic Picture Viewer + Snapmatic 圖片檢視器 + + + + Failed at %1 + 失敗: %1 + + + + + + No Players + + + + + + No Crew + + + + + Unknown Location + 未知地點 + + + + Avatar Preview Mode +Press 1 for Default View + 大頭貼預覽模式 +按 1 切換格預設檢視 + + + + Export as Picture... + 匯出成圖片... + + + + + Export + 匯出 + + + + JPEG Graphics (*.jpg *.jpeg) + JPEG 圖形格式 (*.jpg *.jpeg) + + + + Portable Network Graphics (*.png) + 可攜式網路圖形 (*.png) + + + + + + + + + Export as Picture + 匯出成圖片 + + + + + Overwrite %1 with current Snapmatic picture? + 確定修改目前的 Snapmatic 圖片 %1 ? + + + + Failed to export the picture because the system occurred a write failure + 系統寫入失敗,無法匯出圖片 + + + + Failed to export the picture because the format detection failures + 格式檢測失敗,無法匯出圖片 + + + + Failed to export the picture because the file can't be written + 文件無法寫入,匯出圖片失敗 + + + + Failed to export the picture because of an unknown reason + 未知的錯誤,無法匯出圖片 + + + + + No valid file is selected + 未選擇有效的檔案 + + + + Export as Snapmatic... + 匯出成 Snapmatic... + + + + GTA V Export (*.g5e) + GTA V Export (*.g5e) + + + + GTA V Raw Export (*.auto) + GTA V Raw Export (*.auto) + + + + Snapmatic pictures (PGTA*) + Snapmatic 圖片 (PGTA*) + + + + + + + + Export as Snapmatic + 匯出成 Snapmatic + + + + + Failed to export current Snapmatic picture + 匯出目前的 Snapmatic 圖片失敗 + + + + Exported Snapmatic to "%1" because of using the .auto extension. + 因為使用 .auto 格式,將 Snapmatic 匯出到 "%1". + + + + PlayerListDialog + + + Edit Players... + 編輯玩家... + + + + Available Players: + 可用的玩家: + + + + Selected Players: + 已選擇玩家: + + + + &Apply + 套用(&A) + + + + &Cancel + 取消(&C) + + + + Add Players... + 新增玩家... + + + + Failed to add more Players because the limit of Players are %1! + 因為數量限制 %1,無法新增更多玩家! + + + + + Add Player... + 新增玩家... + + + + Enter Social Club Player ID + 輸入玩家的 Social Club ID + + + + Failed to add Player %1 because Player %1 is already added! + 新增 %1 失敗,因為 %1 已被新增! + + + + ProfileInterface + + + Profile Interface + 設定檔界面 + + + + Loading file %1 of %2 files + 載入檔案中 %1 共 %2 個檔案 + + + + %1 %2 + %1 %2 + + + + Import file + 匯入檔案 + + + + &Import... + 匯入(&I)... + + + + Close profile + 關閉設定檔 + + + + &Close + 關閉(&C) + + + + + + Export file %1 of %2 files + 匯出檔案中 %1 共 %2 個檔案 + + + + + + + + + + + + + + + + + + + + + + Import... + 匯入... + + + + + + + + + Import + 匯入 + + + + + + All image files (%1) + 所有圖片 (%1) + + + + + + + All files (**) + 所有檔案 (**) + + + + + + Can't import %1 because file can't be open + 無法匯入 %1,因為檔案無法開啟 + + + + + + Can't import %1 because file can't be parsed properly + 無法匯入 %1,因為檔案無法正確解析 + + + + Enabled pictures: %1 of %2 + 開啟圖片 %1 共 %2 + + + + Loading... + 載入中... + + + + Snapmatic Loader + Snapmatic 載入器 + + + + <h4>Following Snapmatic Pictures got repaired</h4>%1 + <h4>下列的 Snapmatic 圖片已被更新</h4>%1 + + + + Importable files (%1) + 可匯入的檔案 (%1) + + + + + GTA V Export (*.g5e) + GTA V Export (*.g5e) + + + + + Savegames files (SGTA*) + 遊戲存檔 (SGTA*) + + + + + Snapmatic pictures (PGTA*) + Snapmatic 圖片 (PGTA*) + + + + + + No valid file is selected + 沒有選擇有效的檔案 + + + + + Import file %1 of %2 files + 匯入檔案 %1 共 %2 個 + + + + Import failed with... + +%1 + %1 匯入失敗 + + + + + Failed to read Snapmatic picture + 無法讀取 Snapmatic 圖片 + + + + + Failed to read Savegame file + 無法讀取遊戲存檔 + + + + Can't import %1 because file format can't be detected + 無法匯入 %1,因為無法檢測該檔案格式 + + + + Failed to import the Snapmatic picture, file not begin with PGTA or end with .g5e + 匯入 Snapmatic 圖片失敗,檔案不是 PGTA 開頭或附檔名不是 .g5e + + + + Failed to import the Snapmatic picture, can't copy the file into profile + 匯入 Snapmatic 圖片失敗,無法將該檔案複製到設定檔中 + + + + Failed to import the Savegame, can't copy the file into profile + 匯入遊戲存檔失敗,無法將該檔案複製到設定檔中 + + + + Failed to import the Savegame, no Savegame slot is left + 匯入遊戲存檔失敗,沒有遊戲存檔欄位 + + + + + + + + Export selected... + 匯出所選... + + + + + JPG pictures and GTA Snapmatic + JPG 圖片和 GTA Snapmatic + + + + + JPG pictures only + 只有 JPG 圖片 + + + + + GTA Snapmatic only + 只有 GTA Snapmatic + + + + %1Export Snapmatic pictures%2<br><br>JPG pictures make it possible to open the picture with a Image Viewer<br>GTA Snapmatic make it possible to import the picture into the game<br><br>Export as: + %1 匯出 Snapmatic 圖片 %2<br><br>JPG 圖片可使用圖片檢視器開啟<br>GTA Snapmatic 可以匯入到遊戲中<br><br>匯出成: + + + + Initialising export... + 初始化... + + + + Export failed with... + +%1 + %1 匯出失敗 + + + + + No Snapmatic pictures or Savegames files are selected + 未選擇 Snapmatic 圖片或遊戲存檔 + + + + + + Remove selected + 移除所選 + + + + You really want remove the selected Snapmatic picutres and Savegame files? + 你想移除所選的 Snapmatic 圖片/存檔嗎? + + + + Failed to remove all selected Snapmatic pictures and/or Savegame files + 無法移除所選擇的 Snapmatic 圖片/遊戲存檔 + + + + + + + + + No Snapmatic pictures are selected + 未選擇 Snapmatic 圖片 + + + + + + + + + %1 failed with... + +%2 + Action failed with... + %1 失敗... + +%2 + + + + Prepare Content for Import... + 準備匯入內容... + + + + A Snapmatic picture already exists with the uid %1, you want assign your import a new uid and timestamp? + 已有與 uid %1 相同的 Snapmatic 圖片,你想要匯入新的 uid 和時間戳嗎? + + + + + Qualify as Avatar + 合格大頭貼 + + + + + + + Patch selected... + 修改所選... + + + + + + + + + + + Patch file %1 of %2 files + 修改檔案 %1 共 %2 個檔案 + + + + Qualify + %1 failed with... + 合格 + + + + + Change Players... + 更改玩家... + + + + Change Players + %1 failed with... + 更改玩家 + + + + + + Change Crew... + 更改幫會... + + + + Failed to enter a valid Snapmatic Crew ID + 輸入了無效的幫會 ID + + + + Change Crew + %1 failed with... + 更改幫會 + + + + + + Change Title... + 更改標題... + + + + Failed to enter a valid Snapmatic title + 輸入了無效的標題 + + + + Change Title + %1 failed with... + 更改標題 + + + + All profile files (*.g5e SGTA* PGTA*) + 所有設定檔檔案 (*.g5e SGTA* PGTA*) + + + + QApplication + + <h4>Welcome to %1!</h4>You want to configure %1 before you start using it? + <h4>歡迎使用 %1!</h4> 你想在開始前先設定 %1 嗎? + + + + SavegameDialog + + + + Savegame Viewer + 遊戲存檔檢視器 + + + + <span style="font-weight:600">Savegame</span><br><br>%1 + <span style="font-weight:600">遊戲存檔</span><br><br>%1 + + + + &Export + 匯出(&E) + + + + &Close + 關閉(&C) + + + + Failed at %1 + 失敗 %1 + + + + SavegameWidget + + + Savegame Widget + 遊戲存檔小工具 + + + + SAVE %3 - %1<br>%2 + 存檔 %3 - %1<br>%2 + + + + View savegame + 檢視存檔 + + + + View + 檢視 + + + + Copy savegame + 複製存檔 + + + + + Export + 匯出 + + + + Delete savegame + 刪除存檔 + + + + Delete + 刪除 + + + + &View + 檢視(&V) + + + + + + &Export + 匯出(&E) + + + + + + &Remove + 移除(&R) + + + + + &Select + 選擇(&S) + + + + + &Deselect + 取消選擇(&D) + + + + + Select &All + 選擇全部(&A) + + + + + &Deselect All + 取消選擇全部(&D) + + + + Savegame files (SGTA*) + 遊戲存檔 (SGTA*) + + + + All files (**) + 所有檔案 (**) + + + + + + + Export Savegame + 匯出存檔 + + + + Overwrite %1 with current Savegame? + 是否修改目前的存檔 %1? + + + + Failed to overwrite %1 with current Savegame + 遊戲存檔 %1 修改失敗 + + + + Failed to export current Savegame + 匯出目前的存檔失敗 + + + + No valid file is selected + 沒有選擇有效的檔案 + + + + Export Savegame... + 匯出遊戲存檔... + + + + + AUTOSAVE - %1 +%2 + 自動存檔 - %1 +%2 + + + + + SAVE %3 - %1 +%2 + 存檔 %3 - %1 +%2 + + + + + WRONG FORMAT + 格式錯誤 + + + + UNKNOWN + 未知 + + + + + Delete Savegame + 刪除存檔 + + + + Are you sure to delete %1 from your savegames? + 你確定要刪除存檔 %1? + + + + Failed at deleting %1 from your savegames + 刪除存檔 %1 失敗 + + + + SnapmaticEditor + + + + + + + + + + + Snapmatic Properties + Snapmatic 屬性 + + + + Snapmatic Type + Snapmatic 類型 + + + + Editor + 編輯 + + + + Selfie + 自拍 + + + + Regular + 正常 + + + + Mugshot + 犯罪照片 + + + + Meme + Meme + + + + Director + 導演 + + + + Snapmatic Values + Snapmatic 信息 + + + + Extras + 附加功能 + + + + Qualify as Avatar automatically at apply + 自動設定成符合資格的圖片 + + + + Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture + 符合資格的圖片將可以設定為 Social Club 大頭貼 + + + + Apply changes + 套用變更 + + + + &Apply + 套用(&A) + + + + Discard changes + 捨棄變更 + + + + &Cancel + 取消(&C) + + + + <h4>Unsaved changes detected</h4>You want to save the JSON content before you quit? + <h4>目前的變更未儲存</h4> 你想要在退出之前儲存 JSON 嗎? + + + + Patching of Snapmatic Properties failed because of %1 + 更新 Snapmatic 屬性失敗,因為 %1 + + + + + + + Patching of Snapmatic Properties failed because of I/O Error + 讀寫錯誤,未能更新 Snapmatic 屬性 + + + + Patching of Snapmatic Properties failed because of JSON Error + JSON 錯誤,未能更新 Snapmatic 屬性 + + + + + Snapmatic Crew + 幫會 + + + + + New Snapmatic crew: + 輸入新的幫會: + + + + + Snapmatic Title + 標題 + + + + + New Snapmatic title: + 輸入新的標題: + + + + + + Edit + 編輯 + + + + Players: %1 (%2) + Multiple Player are inserted here + 玩家: %1 (%2) + + + + Player: %1 (%2) + One Player is inserted here + 玩家: %1 (%2) + + + + Title: %1 (%2) + 標題: %1 (%2) + + + + + Appropriate: %1 + 可使用: %1 + + + + Yes + Yes, should work fine + + + + + No + No, could lead to issues + + + + + Crew: %1 (%2) + 幫會: %1 (%2) + + + + SnapmaticPicture + + + + JSON is incomplete and malformed + JSON 不完整和異常 + + + + + JSON is incomplete + JSON 不完整 + + + + + JSON is malformed + JSON 異常 + + + + PHOTO - %1 + 照片 - %1 + + + + open file %1 + 開啟檔案 - %1 + + + + header not exists + 標頭不存在 + + + + header is malformed + 標頭異常 + + + + picture not exists (%1) + 圖片不存在 (%1) + + + + JSON not exists (%1) + JSON 不存在 (%1) + + + + title not exists (%1) + 標題不存在 (%1) + + + + description not exists (%1) + 描述不存在 (%1) + + + + reading file %1 because of %2 + Example for %2: JSON is malformed error + 讀取檔案 %1 失敗,因為 %2 + + + + SnapmaticWidget + + + Snapmatic Widget + Snapmatic 小工具 + + + + PHOTO - 00/00/00 00:00:00 + 照片 - 00/00/00 00:00:00 + + + + View picture + 檢視圖片 + + + + View + 檢視 + + + + Copy picture + 複製圖片 + + + + Copy + 複製 + + + + Export picture + 匯出圖片 + + + + Export + 匯出 + + + + + + Delete picture + 刪除圖片 + + + + Delete + 刪除 + + + + + + Edi&t + 編輯(&E) + + + + + + Show &In-game + 在遊戲中顯示(&I) + + + + + + Hide &In-game + 在遊戲中隱藏(&I) + + + + &Export + 匯出(&E) + + + + &View + 檢視(&V) + + + + &Remove + 移除(&R) + + + + + &Select + 選擇(&S) + + + + + &Deselect + 取消選擇(&D) + + + + + Select &All + 選擇全部(&A) + + + + + &Deselect All + 取消選擇全部(&D) + + + + Are you sure to delete %1 from your Snapmatic pictures? + 你確定要刪除Snapmatic 圖片 %1 嗎? + + + + Failed at deleting %1 from your Snapmatic pictures + 刪除 Snapmatic 圖片 %1 失敗 + + + + Failed to hide %1 In-game from your Snapmatic pictures + 在遊戲中隱藏圖片 %1 失敗 + + + + Failed to show %1 In-game from your Snapmatic pictures + 在遊戲中顯示圖片 %1 失敗 + + + + TelemetryDialog + + + You want help %1 to improve in the future by including personal usage data in your submission? + 你希望通過收集資料來幫助改善 %1 嗎? + + + + %1 User Statistics + %1 使用者統計 + + + + Yes, I want include personal usage data. + 是的,我想幫忙. + + + + &OK + 確定(&O) + + + + UserInterface + + + + %2 - %1 + %2 - %1 + + + + Select profile + 選擇設定檔 + + + + %1 %2 + %1 %2 + + + + Reload profile overview + 重新載入設定檔概述 + + + + &Reload + 重新載入(&R) + + + + Close %1 + Close %1 <- (gta5view/gta5sync) - %1 will be replaced automatically + 關閉 %1 + + + + + + + &Close + 關閉(&C) + + + + &File + 檔案(&F) + + + + &Help + 幫助(&H) + + + + &Edit + 編輯(&E) + + + + &Profile + 設定檔(&P) + + + + &Selection visibility + 選擇可見度(&S) + + + + Selection &mass tools + 工具(&M) + + + + + + &About %1 + 關於 %1(&A) + + + + &Exit + 離開(&E) + + + + Exit + 離開 + + + + Close &Profile + 關閉設定檔(&P) + + + + &Settings + 設定(&S) + + + + Select &All + 選擇全部(&A) + + + + &Deselect All + 取消選擇全部(&D) + + + + &Export selected... + 匯出所選(&E)... + + + + &Remove selected + 移除所選(&R) + + + + &Import files... + 匯入檔案(&I)... + + + + &Open File... + 開啟檔案(&O)... + + + + + Select &GTA V Folder... + 選擇 GTA V 資料夾(&G)... + + + + + + + Select GTA V Folder... + 選擇 GTA V 資料夾... + + + + Show In-gam&e + 在遊戲中顯示(&E) + + + + Hi&de In-game + 在遊戲中隱藏(&D) + + + + + + Change &Title... + 更改標題(&T)... + + + + + + Change &Crew... + 更改幫會(&C)... + + + + + + &Qualify as Avatar + 符合大頭貼資格(&Q) + + + + + + Change &Players... + 更改玩家(&P)... + + + + + + Show In-game + 在遊戲中顯示 + + + + + + Hide In-game + 在遊戲中隱藏 + + + + + + Select Profile + 選擇設定檔 + + + + + &Donate + 贊助(&D) + + + + Donate + 贊助 + + + + Donation methods + 贊助方式 + + + View + 檢視 + + + Copy + 複製 + + + + &Copy + 複製(&C) + + + + Open File... + 開啟檔案... + + + + + + + Open File + 開啟檔案 + + + + Can't open %1 because of not valid file format + 格式無效,無法開啟 %1 + + + + %1 - Messages + %1 - 新聞 + + + diff --git a/res/gta5view-128.png b/res/gta5view-128.png new file mode 100644 index 0000000..fc3d97a Binary files /dev/null and b/res/gta5view-128.png differ diff --git a/res/gta5view-16.png b/res/gta5view-16.png new file mode 100644 index 0000000..662b09d Binary files /dev/null and b/res/gta5view-16.png differ diff --git a/res/gta5view-24.png b/res/gta5view-24.png new file mode 100644 index 0000000..3b2797b Binary files /dev/null and b/res/gta5view-24.png differ diff --git a/res/gta5view-256.png b/res/gta5view-256.png new file mode 100644 index 0000000..e728fb4 Binary files /dev/null and b/res/gta5view-256.png differ diff --git a/res/gta5view-32.png b/res/gta5view-32.png new file mode 100644 index 0000000..b0f64cd Binary files /dev/null and b/res/gta5view-32.png differ diff --git a/res/gta5view-40.png b/res/gta5view-40.png new file mode 100644 index 0000000..27a6988 Binary files /dev/null and b/res/gta5view-40.png differ diff --git a/res/gta5view-48.png b/res/gta5view-48.png new file mode 100644 index 0000000..3f32f7e Binary files /dev/null and b/res/gta5view-48.png differ diff --git a/res/gta5view-512.png b/res/gta5view-512.png new file mode 100644 index 0000000..cb29d15 Binary files /dev/null and b/res/gta5view-512.png differ diff --git a/res/gta5view-64.png b/res/gta5view-64.png new file mode 100644 index 0000000..e48fb58 Binary files /dev/null and b/res/gta5view-64.png differ diff --git a/res/gta5view-96.png b/res/gta5view-96.png new file mode 100644 index 0000000..975157d Binary files /dev/null and b/res/gta5view-96.png differ diff --git a/res/gta5view.desktop b/res/gta5view.desktop deleted file mode 100644 index 28c99d7..0000000 --- a/res/gta5view.desktop +++ /dev/null @@ -1,11 +0,0 @@ -[Desktop Entry] -Version=1.0 -Encoding=UTF-8 -Type=Application -Name=gta5view -Comment=gta5view -Categories=Qt;Application;Utility; -Exec=gta5view -Icon=gta5view -Terminal=false -StartupNotify=false diff --git a/res/gta5view.exe.manifest b/res/gta5view.exe.manifest index c9bde0a..3f57eaf 100644 --- a/res/gta5view.exe.manifest +++ b/res/gta5view.exe.manifest @@ -1,15 +1,15 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/res/5sync.icns b/res/gta5view.icns similarity index 100% rename from res/5sync.icns rename to res/gta5view.icns diff --git a/res/gta5view.png b/res/gta5view.png deleted file mode 100644 index bf41d47..0000000 Binary files a/res/gta5view.png and /dev/null differ diff --git a/res/gta5view.xpm b/res/gta5view.xpm deleted file mode 100644 index f1ee97b..0000000 --- a/res/gta5view.xpm +++ /dev/null @@ -1,135 +0,0 @@ -/* XPM */ -static char * C:\Users\Rafael\Documents\Projects\gta5view\res\gta5view_xpm[] = { -"128 128 4 1", -" c None", -". c #000000", -"+ c #FE0000", -"@ c #FF0000", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ...................................................... ", -" ....................................................... ", -" ....................................................... ", -" ....................................................... ", -" ....................................................... ", -" ....................................................... ", -" ....................................................... ", -" ....................................................... ", -" ....................................................... ", -" ....................................................... ", -" ....................................................... ", -" ....................................................... ", -" ............. ", -" ............. ", -" ............. ", -" .............. ", -" ............. ", -" ............. ", -" ............. ", -" .............. ", -" .............. ", -" ............. ", -" ............. ", -" ............. ", -" .............. ", -" ............. ", -" ............. ", -" ............. ", -" ............. ", -" .............. ", -" ............. ", -" ............. ", -" ............. ", -" ............. ", -" .............. ", -" ............. ", -" ............. ", -" ............. ", -" .............. ", -" .............. ", -" ............. .. ", -" ................................ ", -" .................................... ", -" ........................................ ", -" .......................................... ", -" ............................................ ", -" ............................................. ", -" .............................................. ", -" ................................................. ", -" ................................................. ", -" .................................................. ", -" ..... ............................. ", -" ......................... ", -" ...................... ", -" ..................... ", -" ................... ", -" ................... ", -" .................. ", -" ................. ", -" ................ ", -" ............... ", -" ................ ", -" ................ ", -" ............... ", -" ............... ", -" ............... ", -" ............... ", -" .............. ", -" ++ .............. ", -" +@@@+ .............. ", -" @@@@@ .............. ", -" +@@@@@ .............. ", -" @@@@+ ............... ", -" @@@+ .............. ", -" .............. ", -" ", -" ", -" ", -" ", -" @@@@ @@@@ @@@ +++@@++ ++++ ++++ @@@@@ ", -" @@@@+ +@@@@ @@@@+ ++@@@@@@@@@+ @@@@+ +@@@@+ +@@@@+ ", -" @@@@+ @@@@+ @@@@+ +@@@@@@@@@@@@@ @@@@+ @@@@@@ @@@@@ ", -" +@@@+ +@@@@ +@@@@ +@@@@@@@@@@@@@@+ @@@@+ +@@@@@@ @@@@+ ", -" +@@@@ +@@@@+ +@@@@ @@@@@++ +@@@@@ @@@@+ @@@@@@@ +@@@@ ", -" +@@@@ +@@@+ +@@@+ +@@@@+ +@@@@+ @@@@+ +@@@+@@@ @@@@+ ", -" +@@@@ +@@@@+ @@@@+ +@@@@+ @@@@+ @@@@+ @@@++@@@ +@@@@ ", -" +@@@@ +@@@+ @@@@+ +@@@@+ +@@@+ @@@@+ @@@@ +@@@ @@@@+ ", -" @@@@ +@@@@+ +@@@@ +@@@@ @@@@+ @@@@@ @@@+ +@@@ +@@@@ ", -" @@@@+ @@@@+ +@@@@ +@@@@+ +@@@@+ +@@@+ +@@@ +@@@ +@@@+ ", -" @@@@+ +@@@@ +@@@+ +@@@@ @@@@+ @@@@+ @@@+ +@@@ +@@@@+ ", -" @@@@+ @@@@+ @@@@+ @@@@+ ++@@@@+ +@@@+ +@@@+ +@@@+ @@@@@ ", -" +@@@+ +@@@@ @@@@ +@@@@ ++@@@@@+ +@@@+ +@@@ +@@@+ @@@@+ ", -" +@@@+ @@@@+ +@@@@ +@@@@+++++++@@@@@@@+ +@@@+ +@@@+ +@@@+ +@@@@ ", -" +@@@@ +@@@@ +@@@+ @@@@@@@@@@@@@@@@@++ +@@@+ +@@@ +@@@+ @@@@+ ", -" +@@@@ @@@@+ @@@@+ @@@@@@@@@@@@@@@++ +@@@+ @@@+ @@@+ +@@@@ ", -" @@@@ +@@@+ @@@@+ +@@@@@@@@@@@+++ +@@@+ +@@@ @@@+ @@@@+ ", -" @@@@ +@@@@@ +@@@@ +@@@@++ +@@@+ @@@+ @@@+ +@@@@ ", -" @@@@ +@@@+ +@@@@ +@@@@ +@@@@ +@@@ @@@+ +@@@+ ", -" @@@@+ +@@@@+ +@@@+ +@@@@ +@@@@ @@@+ @@@+ @@@@ ", -" +@@@+ +@@@+ @@@@+ +@@@@ @@@@+ +@@@ @@@+ @@@@+ ", -" +@@@+ @@@@@ @@@@+ +@@@@ @@@+ @@@+ @@@+ @@@@ ", -" +@@@+ +@@@+ +@@@@ @@@@+ @@@+ +@@@+ @@@+ +@@@+ ", -" +@@@+ @@@@ +@@@@ @@@@+ @@@+ +@@+ @@@+ +@@@+ ", -" +@@@+ +@@@+ +@@@+ +@@@@+ @@@+ @@@+ @@@+ @@@+ ", -" @@@+ @@@@ @@@@+ +@@@@@ ++ @@@++@@+ +@@++@@@+ ", -" @@@++@@@+ @@@@+ +@@@@@+ ++@@@ @@@+@@@+ +@@++@@@ ", -" @@@@@@@@ +@@@@ @@@@@@@@@@@@@@@@@ @@@@@@@ +@@@@@@+ ", -" @@@@@@@+ +@@@@ +@@@@@@@@@@@@@@@ @@@@@@+ +@@@@@@ ", -" +@@@@@+ +@@@+ +@@@@@@@@@@@++ @@@@@@ +@@@@@+ ", -" @@@@@ @@@@ ++++@++++ @@@@@ @@@@@ ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" "}; diff --git a/res/img.cmake b/res/img.cmake new file mode 100644 index 0000000..7a51a90 --- /dev/null +++ b/res/img.cmake @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.7) + +set(GTA5VIEW_IMGFILES + res/add.svgz + res/avatararea.png + res/avatarareaimport.png + res/back.svgz + res/flag-de.png + res/flag-fr.png + res/flag-gb.png + res/flag-kr.png + res/flag-ru.png + res/flag-tw.png + res/flag-ua.png + res/flag-us.png + res/mapcayoperico.jpg + res/mappreview.jpg + res/next.svgz + res/pointmaker-8.png + res/pointmaker-16.png + res/pointmaker-24.png + res/pointmaker-32.png + res/savegame.svgz + res/watermark_1b.png + res/watermark_2b.png + res/watermark_2r.png +) diff --git a/res/img.qrc b/res/img.qrc new file mode 100644 index 0000000..4fe80f2 --- /dev/null +++ b/res/img.qrc @@ -0,0 +1,36 @@ + + + add.svgz + avatararea.png + avatarareaimport.png + back.svgz + flag-de.png + flag-fr.png + flag-gb.png + flag-kr.png + flag-ru.png + flag-tw.png + flag-ua.png + flag-us.png + gta5view-16.png + gta5view-24.png + gta5view-32.png + gta5view-40.png + gta5view-48.png + gta5view-64.png + gta5view-96.png + gta5view-128.png + gta5view-256.png + mapcayoperico.jpg + mappreview.jpg + next.svgz + pointmaker-8.png + pointmaker-16.png + pointmaker-24.png + pointmaker-32.png + savegame.svgz + watermark_1b.png + watermark_2b.png + watermark_2r.png + + diff --git a/res/ltc.str b/res/ltc.str new file mode 100644 index 0000000..865862c --- /dev/null +++ b/res/ltc.str @@ -0,0 +1 @@ +Litecoin \ No newline at end of file diff --git a/res/ltc.svgz b/res/ltc.svgz new file mode 100644 index 0000000..ae954e1 Binary files /dev/null and b/res/ltc.svgz differ diff --git a/res/mapcayoperico.jpg b/res/mapcayoperico.jpg new file mode 100644 index 0000000..a9cdab1 Binary files /dev/null and b/res/mapcayoperico.jpg differ diff --git a/res/mappreview.jpg b/res/mappreview.jpg new file mode 100644 index 0000000..0049692 Binary files /dev/null and b/res/mappreview.jpg differ diff --git a/res/next.png b/res/next.png deleted file mode 100644 index 4a36bc2..0000000 Binary files a/res/next.png and /dev/null differ diff --git a/res/next.svgz b/res/next.svgz new file mode 100644 index 0000000..bdf7ee9 Binary files /dev/null and b/res/next.svgz differ diff --git a/res/pointmaker-16.png b/res/pointmaker-16.png new file mode 100644 index 0000000..f038879 Binary files /dev/null and b/res/pointmaker-16.png differ diff --git a/res/pointmaker-24.png b/res/pointmaker-24.png new file mode 100644 index 0000000..87e9669 Binary files /dev/null and b/res/pointmaker-24.png differ diff --git a/res/pointmaker-32.png b/res/pointmaker-32.png new file mode 100644 index 0000000..de591b1 Binary files /dev/null and b/res/pointmaker-32.png differ diff --git a/res/pointmaker-8.png b/res/pointmaker-8.png new file mode 100644 index 0000000..5c0e653 Binary files /dev/null and b/res/pointmaker-8.png differ diff --git a/res/qt_de.qm b/res/qt4/qt_de.qm old mode 100755 new mode 100644 similarity index 100% rename from res/qt_de.qm rename to res/qt4/qt_de.qm diff --git a/res/qt_fr.qm b/res/qt4/qt_fr.qm similarity index 100% rename from res/qt_fr.qm rename to res/qt4/qt_fr.qm diff --git a/res/qt4/qt_ko.qm b/res/qt4/qt_ko.qm new file mode 100644 index 0000000..e3d1231 Binary files /dev/null and b/res/qt4/qt_ko.qm differ diff --git a/res/qt_ru.qm b/res/qt4/qt_ru.qm similarity index 100% rename from res/qt_ru.qm rename to res/qt4/qt_ru.qm diff --git a/res/qt4/qt_uk.qm b/res/qt4/qt_uk.qm new file mode 100644 index 0000000..112ca5c Binary files /dev/null and b/res/qt4/qt_uk.qm differ diff --git a/res/qt4/qt_zh_TW.qm b/res/qt4/qt_zh_TW.qm new file mode 100644 index 0000000..a9a25b2 Binary files /dev/null and b/res/qt4/qt_zh_TW.qm differ diff --git a/res/qt4/tr_qt.qrc b/res/qt4/tr_qt.qrc new file mode 100644 index 0000000..0b79a15 --- /dev/null +++ b/res/qt4/tr_qt.qrc @@ -0,0 +1,10 @@ + + + qt_de.qm + qt_fr.qm + qt_ko.qm + qt_ru.qm + qt_uk.qm + qt_zh_TW.qm + + diff --git a/res/qt5/qtbase_de.qm b/res/qt5/qtbase_de.qm new file mode 100644 index 0000000..4a4c988 Binary files /dev/null and b/res/qt5/qtbase_de.qm differ diff --git a/res/qt5/qtbase_en_GB.qm b/res/qt5/qtbase_en_GB.qm new file mode 100644 index 0000000..8a7e376 Binary files /dev/null and b/res/qt5/qtbase_en_GB.qm differ diff --git a/res/qt5/qtbase_en_GB.ts b/res/qt5/qtbase_en_GB.ts new file mode 100644 index 0000000..0fbfffd --- /dev/null +++ b/res/qt5/qtbase_en_GB.ts @@ -0,0 +1,65 @@ + + + + + QColorDialog + + Hu&e: + + + + &Sat: + + + + &Val: + + + + &Red: + + + + &Green: + + + + Bl&ue: + + + + A&lpha channel: + + + + &HTML: + + + + Cursor at %1, %2 +Press ESC to cancel + Cursour at %1, %2 +Press ESC to cancel + + + Select Color + Select Colour + + + &Basic colors + &Basic colours + + + &Custom colors + &Custom colours + + + &Add to Custom Colors + &Add to Custom Colours + + + &Pick Screen Color + &Pick Screen Colour + + + diff --git a/res/qtbase_fr.qm b/res/qt5/qtbase_fr.qm similarity index 99% rename from res/qtbase_fr.qm rename to res/qt5/qtbase_fr.qm index 8353f0a..009fb5a 100644 Binary files a/res/qtbase_fr.qm and b/res/qt5/qtbase_fr.qm differ diff --git a/res/qt5/qtbase_ko.qm b/res/qt5/qtbase_ko.qm new file mode 100644 index 0000000..20e4661 Binary files /dev/null and b/res/qt5/qtbase_ko.qm differ diff --git a/res/qt5/qtbase_ru.qm b/res/qt5/qtbase_ru.qm new file mode 100644 index 0000000..c1a2286 Binary files /dev/null and b/res/qt5/qtbase_ru.qm differ diff --git a/res/qt5/qtbase_uk.qm b/res/qt5/qtbase_uk.qm new file mode 100644 index 0000000..21a3038 Binary files /dev/null and b/res/qt5/qtbase_uk.qm differ diff --git a/res/qt5/qtbase_zh_TW.qm b/res/qt5/qtbase_zh_TW.qm new file mode 100644 index 0000000..6205298 Binary files /dev/null and b/res/qt5/qtbase_zh_TW.qm differ diff --git a/res/qt5/tr_qt.qrc b/res/qt5/tr_qt.qrc new file mode 100644 index 0000000..7bfe7cc --- /dev/null +++ b/res/qt5/tr_qt.qrc @@ -0,0 +1,11 @@ + + + qtbase_en_GB.qm + qtbase_de.qm + qtbase_fr.qm + qtbase_ko.qm + qtbase_ru.qm + qtbase_uk.qm + qtbase_zh_TW.qm + + diff --git a/res/qt6/qtbase_de.qm b/res/qt6/qtbase_de.qm new file mode 100644 index 0000000..a0d2672 Binary files /dev/null and b/res/qt6/qtbase_de.qm differ diff --git a/res/qt6/qtbase_en_GB.qm b/res/qt6/qtbase_en_GB.qm new file mode 100644 index 0000000..8a7e376 Binary files /dev/null and b/res/qt6/qtbase_en_GB.qm differ diff --git a/res/qt6/qtbase_en_GB.ts b/res/qt6/qtbase_en_GB.ts new file mode 100644 index 0000000..0fbfffd --- /dev/null +++ b/res/qt6/qtbase_en_GB.ts @@ -0,0 +1,65 @@ + + + + + QColorDialog + + Hu&e: + + + + &Sat: + + + + &Val: + + + + &Red: + + + + &Green: + + + + Bl&ue: + + + + A&lpha channel: + + + + &HTML: + + + + Cursor at %1, %2 +Press ESC to cancel + Cursour at %1, %2 +Press ESC to cancel + + + Select Color + Select Colour + + + &Basic colors + &Basic colours + + + &Custom colors + &Custom colours + + + &Add to Custom Colors + &Add to Custom Colours + + + &Pick Screen Color + &Pick Screen Colour + + + diff --git a/res/qt6/qtbase_fr.qm b/res/qt6/qtbase_fr.qm new file mode 100644 index 0000000..009fb5a Binary files /dev/null and b/res/qt6/qtbase_fr.qm differ diff --git a/res/qt6/qtbase_ko.qm b/res/qt6/qtbase_ko.qm new file mode 100644 index 0000000..20e4661 Binary files /dev/null and b/res/qt6/qtbase_ko.qm differ diff --git a/res/qt6/qtbase_ru.qm b/res/qt6/qtbase_ru.qm new file mode 100644 index 0000000..c1a2286 Binary files /dev/null and b/res/qt6/qtbase_ru.qm differ diff --git a/res/qt6/qtbase_uk.qm b/res/qt6/qtbase_uk.qm new file mode 100644 index 0000000..21a3038 Binary files /dev/null and b/res/qt6/qtbase_uk.qm differ diff --git a/res/qt6/qtbase_zh_TW.qm b/res/qt6/qtbase_zh_TW.qm new file mode 100644 index 0000000..f32a72f Binary files /dev/null and b/res/qt6/qtbase_zh_TW.qm differ diff --git a/res/qt6/tr_qt.qrc b/res/qt6/tr_qt.qrc new file mode 100644 index 0000000..7bfe7cc --- /dev/null +++ b/res/qt6/tr_qt.qrc @@ -0,0 +1,11 @@ + + + qtbase_en_GB.qm + qtbase_de.qm + qtbase_fr.qm + qtbase_ko.qm + qtbase_ru.qm + qtbase_uk.qm + qtbase_zh_TW.qm + + diff --git a/res/qtbase_de.qm b/res/qtbase_de.qm deleted file mode 100755 index ed026f9..0000000 Binary files a/res/qtbase_de.qm and /dev/null differ diff --git a/res/qtbase_ru.qm b/res/qtbase_ru.qm deleted file mode 100644 index 8dba5ce..0000000 Binary files a/res/qtbase_ru.qm and /dev/null differ diff --git a/res/savegame.png b/res/savegame.png deleted file mode 100755 index a2c61b0..0000000 Binary files a/res/savegame.png and /dev/null differ diff --git a/res/savegame.svgz b/res/savegame.svgz new file mode 100644 index 0000000..a1c8247 Binary files /dev/null and b/res/savegame.svgz differ diff --git a/res/src/AvatarAreaProject.xcf b/res/src/AvatarAreaProject.xcf index d09d1e4..98c204e 100644 Binary files a/res/src/AvatarAreaProject.xcf and b/res/src/AvatarAreaProject.xcf differ diff --git a/res/src/mainui.png b/res/src/mainui.png new file mode 100644 index 0000000..211e3cc Binary files /dev/null and b/res/src/mainui.png differ diff --git a/res/src/picture.png b/res/src/picture.png new file mode 100644 index 0000000..94d46a5 Binary files /dev/null and b/res/src/picture.png differ diff --git a/res/src/prop.png b/res/src/prop.png new file mode 100644 index 0000000..7acd21c Binary files /dev/null and b/res/src/prop.png differ diff --git a/res/template.g5e b/res/template.g5e index 2700af1..c74ba7f 100644 Binary files a/res/template.g5e and b/res/template.g5e differ diff --git a/res/template.qrc b/res/template.qrc new file mode 100644 index 0000000..06dcf5a --- /dev/null +++ b/res/template.qrc @@ -0,0 +1,5 @@ + + + template.g5e + + diff --git a/res/tr_g5p.qrc b/res/tr_g5p.qrc new file mode 100644 index 0000000..b51e8fd --- /dev/null +++ b/res/tr_g5p.qrc @@ -0,0 +1,11 @@ + + + gta5sync_en_US.qm + gta5sync_de.qm + gta5sync_fr.qm + gta5sync_ko.qm + gta5sync_ru.qm + gta5sync_uk.qm + gta5sync_zh_TW.qm + + diff --git a/res/watermark_1b.png b/res/watermark_1b.png new file mode 100644 index 0000000..c0a8adc Binary files /dev/null and b/res/watermark_1b.png differ diff --git a/res/watermark_2b.png b/res/watermark_2b.png new file mode 100644 index 0000000..0cf74b9 Binary files /dev/null and b/res/watermark_2b.png differ diff --git a/res/watermark_2r.png b/res/watermark_2r.png new file mode 100644 index 0000000..51aa4b2 Binary files /dev/null and b/res/watermark_2r.png differ diff --git a/res/xmr.str b/res/xmr.str new file mode 100644 index 0000000..131342e --- /dev/null +++ b/res/xmr.str @@ -0,0 +1 @@ +Monero \ No newline at end of file diff --git a/res/xmr.svgz b/res/xmr.svgz new file mode 100644 index 0000000..55c269a Binary files /dev/null and b/res/xmr.svgz differ diff --git a/tmext/TelemetryClassAuthenticator.cpp b/tmext/TelemetryClassAuthenticator.cpp new file mode 100644 index 0000000..fc523f7 --- /dev/null +++ b/tmext/TelemetryClassAuthenticator.cpp @@ -0,0 +1,99 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2018 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "TelemetryClassAuthenticator.h" +#include +#include + +#ifndef GTA5SYNC_TELEMETRY_PUSHURL +#define GTA5SYNC_TELEMETRY_PUSHURL "" +#endif + +#ifndef GTA5SYNC_TELEMETRY_REGURL +#define GTA5SYNC_TELEMETRY_REGURL "" +#endif + +#ifndef GTA5SYNC_TELEMETRY_AUTHID +#define GTA5SYNC_TELEMETRY_AUTHID "" +#endif + +#ifndef GTA5SYNC_TELEMETRY_AUTHPW +#define GTA5SYNC_TELEMETRY_AUTHPW "" +#endif + +const QUrl TelemetryClassAuthenticator::getTrackingPushURL() +{ + if (haveAccessData()) + { + QUrl pushUrl(GTA5SYNC_TELEMETRY_PUSHURL); + QUrlQuery pushQuery(pushUrl); + if (!getTrackingAuthID().isEmpty()) { pushQuery.addQueryItem("tid", getTrackingAuthID()); } + if (!getTrackingAuthPW().isEmpty()) { pushQuery.addQueryItem("tpw", getTrackingAuthPW()); } + pushUrl.setQuery(pushQuery.query(QUrl::FullyEncoded)); + return pushUrl; + } + else + { + QUrl pushUrl(GTA5SYNC_TELEMETRY_PUSHURL); + return pushUrl; + } +} + +const QUrl TelemetryClassAuthenticator::getTrackingRegURL() +{ + if (haveAccessData()) + { + QUrl regUrl(GTA5SYNC_TELEMETRY_REGURL); + QUrlQuery regQuery(regUrl); + if (!getTrackingAuthID().isEmpty()) { regQuery.addQueryItem("tid", getTrackingAuthID()); } + if (!getTrackingAuthPW().isEmpty()) { regQuery.addQueryItem("tpw", getTrackingAuthPW()); } + regUrl.setQuery(regQuery.query(QUrl::FullyEncoded)); + return regUrl; + } + else + { + QUrl regUrl(GTA5SYNC_TELEMETRY_REGURL); + return regUrl; + } +} + +const QString TelemetryClassAuthenticator::getTrackingAuthID() +{ + return QString(GTA5SYNC_TELEMETRY_AUTHID); +} + +const QString TelemetryClassAuthenticator::getTrackingAuthPW() +{ + return QString(GTA5SYNC_TELEMETRY_AUTHPW); +} + +bool TelemetryClassAuthenticator::havePushURL() +{ + return !getTrackingPushURL().isEmpty(); +} + +bool TelemetryClassAuthenticator::haveRegURL() +{ + return !getTrackingRegURL().isEmpty(); +} + +bool TelemetryClassAuthenticator::haveAccessData() +{ + if (getTrackingAuthID().isEmpty() && getTrackingAuthPW().isEmpty()) { return false; } + return true; +} diff --git a/tmext/TelemetryClassAuthenticator.h b/tmext/TelemetryClassAuthenticator.h new file mode 100644 index 0000000..4180029 --- /dev/null +++ b/tmext/TelemetryClassAuthenticator.h @@ -0,0 +1,41 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2018 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef TELEMETRYCLASSAUTHENTICATOR_H +#define TELEMETRYCLASSAUTHENTICATOR_H + +#include +#include +#include +#include + +class TelemetryClassAuthenticator : public QObject +{ + Q_OBJECT +public: + static const QUrl getTrackingPushURL(); + static const QUrl getTrackingRegURL(); + static const QString getTrackingAuthID(); + static const QString getTrackingAuthPW(); + static bool havePushURL(); + static bool haveRegURL(); + static bool haveAccessData(); +}; + + +#endif // TELEMETRYCLASSAUTHENTICATOR_H diff --git a/uimod/JSHighlighter.cpp b/uimod/JSHighlighter.cpp new file mode 100644 index 0000000..42a988c --- /dev/null +++ b/uimod/JSHighlighter.cpp @@ -0,0 +1,99 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2017-2020 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "JSHighlighter.h" + +JSHighlighter::JSHighlighter(QTextDocument *parent) : + QSyntaxHighlighter(parent) +{ + HighlightingRule rule; + + QBrush keywordBrush(QColor::fromRgb(66, 137, 244)); + keywordFormat.setForeground(keywordBrush); + keywordFormat.setFontItalic(true); + QStringList keywordPatterns; + keywordPatterns << "\\btrue\\b" << "\\bfalse\\b"; + for (QString pattern : keywordPatterns) + { +#if QT_VERSION >= 0x050000 + rule.pattern = QRegularExpression(pattern); +#else + rule.pattern = QRegExp(pattern); +#endif + rule.format = keywordFormat; + highlightingRules.append(rule); + } + + QBrush doubleBrush(QColor::fromRgb(66, 137, 244)); + doubleFormat.setForeground(doubleBrush); +#if QT_VERSION >= 0x050000 + rule.pattern = QRegularExpression("[+-]?\\d*\\.?\\d+"); +#else + rule.pattern = QRegExp("[+-]?\\d*\\.?\\d+"); +#endif + rule.format = doubleFormat; + highlightingRules.append(rule); + + QBrush quotationBrush(QColor::fromRgb(66, 244, 104)); + quotationFormat.setForeground(quotationBrush); +#if QT_VERSION >= 0x050000 + rule.pattern = QRegularExpression("\"[^\"]*\""); +#else + rule.pattern = QRegExp("\"[^\"]*\""); +#endif + rule.format = quotationFormat; + highlightingRules.append(rule); + + QBrush objectBrush(QColor::fromRgb(255, 80, 80)); + objectFormat.setForeground(objectBrush); +#if QT_VERSION >= 0x050000 + rule.pattern = QRegularExpression("\"[^\"]*\"(?=:)"); +#else + rule.pattern = QRegExp("\"[^\"]*\"(?=:)"); +#endif + rule.format = objectFormat; + highlightingRules.append(rule); +} + +void JSHighlighter::highlightBlock(const QString &text) +{ +#if QT_VERSION >= 0x050000 + for (const HighlightingRule &rule : qAsConst(highlightingRules)) +#else + for (const HighlightingRule &rule : highlightingRules) +#endif + { +#if QT_VERSION >= 0x050000 + QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text); + while (matchIterator.hasNext()) { + QRegularExpressionMatch match = matchIterator.next(); + setFormat(match.capturedStart(), match.capturedLength(), rule.format); + } +#else + QRegExp expression(rule.pattern); + int index = expression.indexIn(text); + while (index >= 0) + { + int length = expression.matchedLength(); + setFormat(index, length, rule.format); + index = expression.indexIn(text, index + length); + } +#endif + } + setCurrentBlockState(0); +} diff --git a/uimod/JSHighlighter.h b/uimod/JSHighlighter.h new file mode 100644 index 0000000..d93bfa9 --- /dev/null +++ b/uimod/JSHighlighter.h @@ -0,0 +1,65 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2017-2020 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef JSHIGHLIGHTER_H +#define JSHIGHLIGHTER_H + +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x050000 +#include +#else +#include +#endif + +class QTextDocument; + +class JSHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT + +public: + struct HighlightingRule + { +#if QT_VERSION >= 0x050000 + QRegularExpression pattern; +#else + QRegExp pattern; +#endif + QTextCharFormat format; + }; + QVector highlightingRules; + + QTextCharFormat keywordFormat; + QTextCharFormat doubleFormat; + QTextCharFormat quotationFormat; + QTextCharFormat objectFormat; + + JSHighlighter(QTextDocument *parent = 0); + +protected: + void highlightBlock(const QString &text) override; +}; + +#endif // JSHIGHLIGHTER_H diff --git a/uimod/UiModLabel.cpp b/uimod/UiModLabel.cpp old mode 100755 new mode 100644 index 0051a7a..4a2c2d4 --- a/uimod/UiModLabel.cpp +++ b/uimod/UiModLabel.cpp @@ -1,75 +1,75 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "UiModLabel.h" -#include -#include - -UiModLabel::UiModLabel(const QString &text, QWidget *parent) : QLabel(parent) -{ - setText(text); -} - -UiModLabel::UiModLabel(QWidget *parent, const QString &text) : QLabel(parent) -{ - setText(text); -} - -UiModLabel::UiModLabel(QWidget *parent) : QLabel(parent) -{ -} - -UiModLabel::~UiModLabel() -{ -} - -void UiModLabel::paintEvent(QPaintEvent *ev) -{ - QLabel::paintEvent(ev); - emit labelPainted(); -} - -void UiModLabel::mouseMoveEvent(QMouseEvent *ev) -{ - QLabel::mouseMoveEvent(ev); - emit mouseMoved(); -} - -void UiModLabel::mousePressEvent(QMouseEvent *ev) -{ - QLabel::mousePressEvent(ev); - emit mousePressed(ev->button()); -} - -void UiModLabel::mouseReleaseEvent(QMouseEvent *ev) -{ - QLabel::mouseReleaseEvent(ev); - emit mouseReleased(ev->button()); -} - -void UiModLabel::mouseDoubleClickEvent(QMouseEvent *ev) -{ - QLabel::mouseDoubleClickEvent(ev); - emit mouseDoubleClicked(ev->button()); -} - -void UiModLabel::resizeEvent(QResizeEvent *ev) -{ - QLabel::resizeEvent(ev); - emit resized(ev->size()); -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "UiModLabel.h" +#include +#include + +UiModLabel::UiModLabel(const QString &text, QWidget *parent) : QLabel(parent) +{ + setText(text); +} + +UiModLabel::UiModLabel(QWidget *parent, const QString &text) : QLabel(parent) +{ + setText(text); +} + +UiModLabel::UiModLabel(QWidget *parent) : QLabel(parent) +{ +} + +UiModLabel::~UiModLabel() +{ +} + +void UiModLabel::paintEvent(QPaintEvent *ev) +{ + QLabel::paintEvent(ev); + emit labelPainted(); +} + +void UiModLabel::mouseMoveEvent(QMouseEvent *ev) +{ + QLabel::mouseMoveEvent(ev); + emit mouseMoved(); +} + +void UiModLabel::mousePressEvent(QMouseEvent *ev) +{ + QLabel::mousePressEvent(ev); + emit mousePressed(ev->button()); +} + +void UiModLabel::mouseReleaseEvent(QMouseEvent *ev) +{ + QLabel::mouseReleaseEvent(ev); + emit mouseReleased(ev->button()); +} + +void UiModLabel::mouseDoubleClickEvent(QMouseEvent *ev) +{ + QLabel::mouseDoubleClickEvent(ev); + emit mouseDoubleClicked(ev->button()); +} + +void UiModLabel::resizeEvent(QResizeEvent *ev) +{ + QLabel::resizeEvent(ev); + emit resized(ev->size()); +} diff --git a/uimod/UiModLabel.h b/uimod/UiModLabel.h old mode 100755 new mode 100644 index 545ee27..0988a4e --- a/uimod/UiModLabel.h +++ b/uimod/UiModLabel.h @@ -1,53 +1,53 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016-2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef UIMODLABEL_H -#define UIMODLABEL_H - -#include -#include -#include -#include - -class UiModLabel : public QLabel -{ - Q_OBJECT -public: - UiModLabel(const QString &text, QWidget *parent = 0); - UiModLabel(QWidget *parent, const QString &text); - UiModLabel(QWidget *parent = 0); - ~UiModLabel(); - -protected: - void mouseMoveEvent(QMouseEvent *ev); - void mousePressEvent(QMouseEvent *ev); - void mouseReleaseEvent(QMouseEvent *ev); - void mouseDoubleClickEvent(QMouseEvent *ev); - void paintEvent(QPaintEvent *ev); - void resizeEvent(QResizeEvent *ev); - -signals: - void mouseMoved(); - void mousePressed(Qt::MouseButton button); - void mouseReleased(Qt::MouseButton button); - void mouseDoubleClicked(Qt::MouseButton button); - void labelPainted(); - void resized(QSize newSize); -}; - -#endif // UIMODLABEL_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2016-2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef UIMODLABEL_H +#define UIMODLABEL_H + +#include +#include +#include +#include + +class UiModLabel : public QLabel +{ + Q_OBJECT +public: + UiModLabel(const QString &text, QWidget *parent = 0); + UiModLabel(QWidget *parent, const QString &text); + UiModLabel(QWidget *parent = 0); + ~UiModLabel(); + +protected: + void mouseMoveEvent(QMouseEvent *ev); + void mousePressEvent(QMouseEvent *ev); + void mouseReleaseEvent(QMouseEvent *ev); + void mouseDoubleClickEvent(QMouseEvent *ev); + void paintEvent(QPaintEvent *ev); + void resizeEvent(QResizeEvent *ev); + +signals: + void mouseMoved(); + void mousePressed(Qt::MouseButton button); + void mouseReleased(Qt::MouseButton button); + void mouseDoubleClicked(Qt::MouseButton button); + void labelPainted(); + void resized(QSize newSize); +}; + +#endif // UIMODLABEL_H diff --git a/uimod/UiModWidget.cpp b/uimod/UiModWidget.cpp index f9dd61f..990ce7f 100644 --- a/uimod/UiModWidget.cpp +++ b/uimod/UiModWidget.cpp @@ -1,76 +1,81 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#include "UiModWidget.h" -#include -#include -#include -#include -#include -#include - -UiModWidget::UiModWidget(QWidget *parent) : QWidget(parent) -{ - filesMode = false; -} - -UiModWidget::~UiModWidget() -{ -} - -void UiModWidget::setFilesMode(bool filesModeEnabled) -{ - filesMode = filesModeEnabled; -} - -void UiModWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) -{ - if (filesMode && dragEnterEvent->mimeData()->hasUrls()) - { - QStringList pathList; - QList urlList = dragEnterEvent->mimeData()->urls(); - - foreach(const QUrl ¤tUrl, urlList) - { - if (currentUrl.isLocalFile()) - { - pathList.append(currentUrl.toLocalFile()); - } - } - - if (!pathList.isEmpty()) - { - dragEnterEvent->acceptProposedAction(); - } - } -} - -void UiModWidget::dropEvent(QDropEvent *dropEvent) -{ - dropEvent->acceptProposedAction(); - emit dropped(dropEvent->mimeData()); -} - -void UiModWidget::paintEvent(QPaintEvent *paintEvent) -{ - Q_UNUSED(paintEvent) - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "UiModWidget.h" +#include +#include +#include +#include +#include +#include + +UiModWidget::UiModWidget(QWidget *parent) : QWidget(parent) +{ + filesDropEnabled = false; + imageDropEnabled = false; +} + +UiModWidget::~UiModWidget() +{ +} + +void UiModWidget::setFilesDropEnabled(bool enabled) +{ + filesDropEnabled = enabled; +} + +void UiModWidget::setImageDropEnabled(bool enabled) +{ + imageDropEnabled = enabled; +} + +void UiModWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) +{ + if (filesDropEnabled && dragEnterEvent->mimeData()->hasUrls()) { + QVector pathList; + const QList urlList = dragEnterEvent->mimeData()->urls(); + + for (const QUrl ¤tUrl : urlList) { + if (currentUrl.isLocalFile()) { + pathList.append(currentUrl.toLocalFile()); + } + } + + if (!pathList.isEmpty()) { + dragEnterEvent->acceptProposedAction(); + } + } + else if (imageDropEnabled && dragEnterEvent->mimeData()->hasImage()) { + dragEnterEvent->acceptProposedAction(); + } +} + +void UiModWidget::dropEvent(QDropEvent *dropEvent) +{ + dropEvent->acceptProposedAction(); + emit dropped(dropEvent->mimeData()); +} + +void UiModWidget::paintEvent(QPaintEvent *paintEvent) +{ + Q_UNUSED(paintEvent) + QStyleOption opt; + opt.initFrom(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} diff --git a/uimod/UiModWidget.h b/uimod/UiModWidget.h index 3858425..469c000 100644 --- a/uimod/UiModWidget.h +++ b/uimod/UiModWidget.h @@ -1,47 +1,49 @@ -/***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2017 Syping -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*****************************************************************************/ - -#ifndef UIMODWIDGET_H -#define UIMODWIDGET_H - -#include -#include -#include -#include - -class UiModWidget : public QWidget -{ - Q_OBJECT -public: - UiModWidget(QWidget *parent = 0); - void setFilesMode(bool enabled); - ~UiModWidget(); - -protected: - void dragEnterEvent(QDragEnterEvent *dragEnterEvent); - void dropEvent(QDropEvent *dropEvent); - void paintEvent(QPaintEvent *paintEvent); - -private: - bool filesMode; - -signals: - void dropped(const QMimeData *mimeData); -}; - -#endif // UIMODWIDGET_H +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2017 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef UIMODWIDGET_H +#define UIMODWIDGET_H + +#include +#include +#include +#include + +class UiModWidget : public QWidget +{ + Q_OBJECT +public: + UiModWidget(QWidget *parent = 0); + void setFilesDropEnabled(bool enabled); + void setImageDropEnabled(bool enabled); + ~UiModWidget(); + +protected: + void dragEnterEvent(QDragEnterEvent *dragEnterEvent); + void dropEvent(QDropEvent *dropEvent); + void paintEvent(QPaintEvent *paintEvent); + +private: + bool filesDropEnabled; + bool imageDropEnabled; + +signals: + void dropped(const QMimeData *mimeData); +}; + +#endif // UIMODWIDGET_H diff --git a/wrapper.h b/wrapper.h new file mode 100644 index 0000000..9ea0443 --- /dev/null +++ b/wrapper.h @@ -0,0 +1,30 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2021 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef WRAPPER_H +#define WRAPPER_H + +#if QT_VERSION < 0x050700 +#if __cplusplus > 201703L +#define qAsConst(x) std::as_const(x) +#else +#define qAsConst +#endif +#endif + +#endif // WRAPPER_H