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 @@ +Po§θQƒŠNΧ<μ1x£%™{ ¬Θw|RtZvφ[kΞηςιAZγε2Α›ψŸΞ«`ο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 51e8cc0..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,31 +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) @@ -64,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(); @@ -84,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; } @@ -119,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(); @@ -138,16 +209,20 @@ void ProfileInterface::savegameLoaded(SavegameData *savegame, QString savegamePa SavegameWidget *sgdWidget = new SavegameWidget(this); sgdWidget->setSavegameData(savegame, savegamePath); sgdWidget->setContentMode(contentMode); + sgdWidget->setMouseTracking(true); + sgdWidget->installEventFilter(this); widgets[sgdWidget] = "SGD" % QFileInfo(savegamePath).fileName(); savegames += savegame; - if (selectedWidgts != 0 || contentMode == 2) { sgdWidget->setSelectionMode(true); } + 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) @@ -155,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); + picWidget->setMouseTracking(true); + picWidget->installEventFilter(this); widgets[picWidget] = "PIC" % picture->getPictureSortStr(); pictures += picture; - if (selectedWidgts != 0 || contentMode == 2) { picWidget->setSelectionMode(true); } + 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())); @@ -171,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) @@ -181,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()); } } } @@ -295,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); } } @@ -313,7 +458,7 @@ void ProfileInterface::sortingProfileInterface() ui->vlSavegame->setEnabled(true); ui->vlSnapmatic->setEnabled(true); - qApp->processEvents(); + QApplication::processEvents(); } void ProfileInterface::profileLoaded_p() @@ -324,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; @@ -354,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; @@ -386,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? @@ -393,17 +551,27 @@ 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); @@ -413,21 +581,18 @@ fileDialogPreOpen: //Work? 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)) 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? } } @@ -438,18 +603,18 @@ fileDialogPreOpen: //Work? settings.endGroup(); } -void ProfileInterface::importFilesProgress(QStringList selectedFiles) +bool ProfileInterface::importFilesProgress(QStringList selectedFiles) { int maximumId = selectedFiles.length(); - int overallId = 1; + 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); @@ -457,79 +622,115 @@ 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 (!importFile(selectedFile, false)) - { - failedFiles << QFileInfo(selectedFile).fileName(); + importDateTime = QDateTime::currentDateTime(); + if (!importFile(selectedFile, importDateTime, false)) { + failed << QFileInfo(selectedFile).fileName(); } - overallId++; } + pbDialog.close(); - foreach (const QString &curErrorStr, failedFiles) - { + 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) +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)) - { + if (!snapmaticFile.open(QFile::ReadOnly)) { delete picture; return false; } @@ -537,8 +738,7 @@ bool ProfileInterface::importFile(QString selectedFile, bool notMultiple) QImageReader snapmaticImageReader; snapmaticImageReader.setDecideFormatFromContent(true); snapmaticImageReader.setDevice(&snapmaticFile); - if (!snapmaticImageReader.read(&snapmaticImage)) - { + if (!snapmaticImageReader.read(&snapmaticImage)) { delete picture; return false; } @@ -546,207 +746,494 @@ bool ProfileInterface::importFile(QString selectedFile, bool notMultiple) QPixmap snapmaticPixmap(960, 536); snapmaticPixmap.fill(Qt::black); QPainter snapmaticPainter(&snapmaticPixmap); - 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; } - QString currentTime = QTime::currentTime().toString("HHmmss"); SnapmaticProperties spJson = picture->getSnapmaticProperties(); - spJson.uid = QString(currentTime + - QString::number(QDate::currentDate().dayOfYear())).toInt(); - bool fExists = QFile::exists(profileFolder % QDir::separator() % "PGTA5" % QString::number(spJson.uid)); - bool fExistsHidden = QFile::exists(profileFolder % QDir::separator() % "PGTA5" % QString::number(spJson.uid) % ".hidden"); + 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 || fExistsHidden) && cEnough < 5000) - { - currentTime = QString::number(currentTime.toInt() - 1); - spJson.uid = QString(currentTime + - QString::number(QDate::currentDate().dayOfYear())).toInt(); - fExists = QFile::exists(profileFolder % QDir::separator() % "PGTA5" % QString::number(spJson.uid)); - fExistsHidden = QFile::exists(profileFolder % QDir::separator() % "PGTA5" % QString::number(spJson.uid) % ".hidden"); + 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 = QDateTime::currentDateTime(); + 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; QFile snapmaticFile(selectedFile); - if (!snapmaticFile.open(QFile::ReadOnly)) - { + 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; } - QImage snapmaticImage; + QImage *snapmaticImage = new QImage(); QImageReader snapmaticImageReader; snapmaticImageReader.setDecideFormatFromContent(true); snapmaticImageReader.setDevice(&snapmaticFile); - if (!snapmaticImageReader.read(&snapmaticImage)) - { + 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(this); - importDialog->setWindowFlags(importDialog->windowFlags()^Qt::WindowContextHelpButtonHint); + ImportDialog *importDialog = new ImportDialog(profileName, this); importDialog->setImage(snapmaticImage); importDialog->setModal(true); importDialog->show(); importDialog->exec(); - if (importDialog->isDoImport()) - { - if (picture->setImage(importDialog->image())) - { - QString currentTime = QTime::currentTime().toString("HHmmss"); + if (importDialog->isImportAgreed()) { + if (picture->setImage(importDialog->image(), importDialog->isUnlimitedBuffer())) { SnapmaticProperties spJson = picture->getSnapmaticProperties(); - spJson.uid = QString(currentTime + - QString::number(QDate::currentDate().dayOfYear())).toInt(); - bool fExists = QFile::exists(profileFolder % QDir::separator() % "PGTA5" % QString::number(spJson.uid)); - bool fExistsHidden = QFile::exists(profileFolder % QDir::separator() % "PGTA5" % QString::number(spJson.uid) % ".hidden"); + 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 || fExistsHidden) && cEnough < 25) - { - currentTime = QString::number(currentTime.toInt() - 1); - spJson.uid = QString(currentTime + - QString::number(QDate::currentDate().dayOfYear())).toInt(); - fExists = QFile::exists(profileFolder % QDir::separator() % "PGTA5" % QString::number(spJson.uid)); - fExistsHidden = QFile::exists(profileFolder % QDir::separator() % "PGTA5" % QString::number(spJson.uid) % ".hidden"); + 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 = QDateTime::currentDateTime(); + 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; } } @@ -757,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; - 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++; @@ -808,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); } } @@ -825,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; @@ -851,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"); @@ -881,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(); @@ -949,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(); @@ -960,22 +1438,18 @@ void ProfileInterface::exportSelected() errorList << getFailedCopyPictures; errorList << getFailedSavegames; - foreach (const QString &curErrorStr, errorList) - { + 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(); @@ -985,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() @@ -1038,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() @@ -1109,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 += 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); - } - 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 9d769e8..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); - 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 25c313c..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.4" -#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 58e2987..9326210 --- a/gta5view.pro +++ b/gta5view.pro @@ -1,172 +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/tr_g5p.qrc \ - res/app.qrc - -DISTFILES += res/app.rc \ - res/gta5sync.desktop \ - res/gta5sync_de.ts \ - res/gta5sync_fr.ts \ - res/gta5sync_ru.ts \ - res/gta5view.exe.manifest \ - res/gta5view.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 - -isEqual(QT_MAJOR_VERSION, 4): RESOURCES += res/tr_qt4.qrc - -# QT5 ONLY STUFF - -isEqual(QT_MAJOR_VERSION, 5): RESOURCES += res/tr_qt5.qrc - -# 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 c41d920..0000000 --- a/res/app.qrc +++ /dev/null @@ -1,32 +0,0 @@ - - - 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 4fbbc6e..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, 4, 0 -PRODUCTVERSION 1, 4, 4, 0 -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.4\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.4\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/tr_qt4.qrc b/res/qt4/tr_qt.qrc similarity index 61% rename from res/tr_qt4.qrc rename to res/qt4/tr_qt.qrc index 098d37c..0b79a15 100644 --- a/res/tr_qt4.qrc +++ b/res/qt4/tr_qt.qrc @@ -2,6 +2,9 @@ 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/tr_qt5.qrc b/res/qt5/tr_qt.qrc similarity index 53% rename from res/tr_qt5.qrc rename to res/qt5/tr_qt.qrc index f34728d..7bfe7cc 100644 --- a/res/tr_qt5.qrc +++ b/res/qt5/tr_qt.qrc @@ -1,7 +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 29e518e..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 a11b7c7..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 index 393f585..b51e8fd 100644 --- a/res/tr_g5p.qrc +++ b/res/tr_g5p.qrc @@ -1,7 +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