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 245b0b6..fcd678b 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,8 @@ *.app # Qt project user file -*.pro.user \ No newline at end of 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 new file mode 100644 index 0000000..f6c3397 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,50 @@ +dist: bionic +sudo: required + +language: cpp + +services: + - docker + +env: + 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: + - ".travis/source.sh" + +script: + - ".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= + label: ${RELEASE_LABEL} + file_glob: true + file: assets/* + skip_cleanup: true + on: + tags: true diff --git a/.travis/TelemetryClassAuthenticator.cpp.enc b/.travis/TelemetryClassAuthenticator.cpp.enc new file mode 100644 index 0000000..498ad86 Binary files /dev/null and b/.travis/TelemetryClassAuthenticator.cpp.enc differ diff --git a/.travis/dropbox_uploader.enc b/.travis/dropbox_uploader.enc new file mode 100644 index 0000000..60a77f9 --- /dev/null +++ b/.travis/dropbox_uploader.enc @@ -0,0 +1 @@ +PoQN<1x%{w|RtZv[kAZ2Ϋ`J,4vϥ@eʥ~U$+P|y<&H \ No newline at end of file diff --git a/.travis/source.sh b/.travis/source.sh new file mode 100755 index 0000000..833e125 --- /dev/null +++ b/.travis/source.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +rm -rf tmext/TelemetryClassAuthenticator.cpp && \ +openssl aes-256-cbc -K $encrypted_db000a5d87d6_key -iv $encrypted_db000a5d87d6_iv -in .travis/TelemetryClassAuthenticator.cpp.enc -out tmext/TelemetryClassAuthenticator.cpp -d && \ +openssl aes-256-cbc -K $encrypted_d57e7d2f8877_key -iv $encrypted_d57e7d2f8877_iv -in .travis/dropbox_uploader.enc -out ~/.dropbox_uploader -d diff --git a/.travis/travis.sh b/.travis/travis.sh new file mode 100755 index 0000000..94154d5 --- /dev/null +++ b/.travis/travis.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Install lua +if [ "${TRAVIS_OS_NAME}" == "osx" ]; then + brew install lua +else + sudo apt-get update -qq && \ + sudo apt-get install -qq lua5.2 +fi + +# Check if build is not tagged +if [ "${TRAVIS_TAG}" == "" ]; then + export EXECUTABLE_TAG=-$(git rev-parse --short HEAD) +else + export EXECUTABLE_TAG= +fi + +# Check if package code is not set +if [ "${PACKAGE_CODE}" == "" ]; then + export PACKAGE_CODE=GitHub +fi + +# Init Application Commit Hash +export APPLICATION_COMMIT=$(git rev-parse --short HEAD) + +# Start CI script +.ci/ci.sh diff --git a/.travis/ubuntu_travis.sh b/.travis/ubuntu_travis.sh new file mode 100755 index 0000000..d93bcbd --- /dev/null +++ b/.travis/ubuntu_travis.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Install packages +sudo .ci/debian_install.sh && \ + +# Build gta5view +sudo .ci/debian_build.sh && \ +cd ${PROJECT_DIR} diff --git a/AboutDialog.cpp b/AboutDialog.cpp old mode 100755 new mode 100644 index f492236..12852cf --- a/AboutDialog.cpp +++ b/AboutDialog.cpp @@ -1,40 +1,126 @@ -/***************************************************************************** -* 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 -#include "AboutDialog.h" -#include "ui_AboutDialog.h" -#include "config.h" - -AboutDialog::AboutDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::AboutDialog) -{ - ui->setupUi(this); - aboutStr = ui->labAbout->text(); - QString appVersion = qApp->applicationVersion(); - QString buildType = GTA5SYNC_BUILDTYPE; - buildType.replace("_", " "); - QString buildStr = QString("%1, %2").arg(QT_VERSION_STR, GTA5SYNC_COMPILER); - ui->labAbout->setText(aboutStr.arg(appVersion % " (" % buildType % ")", buildStr, qVersion(), __DATE__, __TIME__)); -} - -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 1ff5484..a13dcc0 --- a/AboutDialog.h +++ b/AboutDialog.h @@ -1,43 +1,44 @@ -/***************************************************************************** -* 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 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; -}; - -#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 883ed7f..3672e20 --- a/AboutDialog.ui +++ b/AboutDialog.ui @@ -1,106 +1,102 @@ - - - AboutDialog - - - - 0 - 0 - 375 - 260 - - - - About gta5view - - - true - - - - - - - 0 - 0 - - - - <span style=" font-weight:600;">gta5view</span><br/> -<br/> -A project for viewing 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/>gta5view is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - - - 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 11b67d7..9d112a5 --- a/AppEnv.cpp +++ b/AppEnv.cpp @@ -1,132 +1,457 @@ -/***************************************************************************** -* 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 "config.h" -#include "AppEnv.h" -#include "StringParser.h" -#include "StandardPaths.h" -#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 SyncSettings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - SyncSettings.beginGroup("dir"); - bool forceDir = SyncSettings.value("force", false).toBool(); - GTAV_returnFolder = SyncSettings.value("dir", GTAV_defaultFolder).toString(); - SyncSettings.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::getPlayerFetchingUrl(QString crewID, QString pageNumber) -{ - return QUrl(QString("https://socialclub.rockstargames.com/crewsapi/GetMembersList?crewId=%1&pageNumber=%2").arg(crewID, pageNumber)); -} +/***************************************************************************** +* 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 ed24295..1e182a2 --- a/AppEnv.h +++ b/AppEnv.h @@ -1,42 +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 . -*****************************************************************************/ - -#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); -}; - -#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 9a34a50..0fa96c6 --- a/CrewDatabase.cpp +++ b/CrewDatabase.cpp @@ -1,81 +1,176 @@ -/***************************************************************************** -* 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 "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) -{ - return crewDB->value(QString::number(crewID), crewID).toString(); -} - -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 6204fe9..692ea79 --- a/CrewDatabase.h +++ b/CrewDatabase.h @@ -1,43 +1,54 @@ -/***************************************************************************** -* 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 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 8ca7ac0..9173eac --- a/DatabaseThread.cpp +++ b/DatabaseThread.cpp @@ -1,223 +1,223 @@ -/***************************************************************************** -* 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 "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; - - // 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())); - waitingLoop->exec(); - delete waitingLoop; - - while (threadRunning) - { - crewList = crewDB->getCrews(); - - // Long time scan - scanCrewReference(crewList, 10000); - scanCrewMembersList(crewList, crewMaxPages, 10000); - emit playerNameUpdated(); - - QTimer::singleShot(300000, &threadLoop, SLOT(quit())); - threadLoop.exec(); - } -} - -void DatabaseThread::scanCrewReference(QStringList crewList, int requestDelay) -{ - foreach (const QString &crewID, crewList) - { - if (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())); - 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())); - waitingLoop->exec(); - delete waitingLoop; - - netReply->deleteLater(); - delete netReply; - netManager->deleteLater(); - delete netManager; - } - } -} - -void DatabaseThread::scanCrewMembersList(QStringList crewList, int maxPages, int requestDelay) -{ - foreach (const QString &crewID, crewList) - { - if (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))); - 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())); - 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())); - waitingLoop->exec(); - delete waitingLoop; - - currentPage++; - } - - netReply->deleteLater(); - delete netReply; - netManager->deleteLater(); - delete netManager; - } - } - } -} +/***************************************************************************** +* 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 068b55c..37c6f76 --- a/DatabaseThread.h +++ b/DatabaseThread.h @@ -1,49 +1,56 @@ -/***************************************************************************** -* 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 DATABASETHREAD_H -#define DATABASETHREAD_H - -#include "CrewDatabase.h" -#include -#include - -class DatabaseThread : public QThread -{ - Q_OBJECT -public: - explicit DatabaseThread(CrewDatabase *crewDB, QObject *parent = 0); - -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(); - -}; - -#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 37cb338..43d8d97 --- a/ExportDialog.cpp +++ b/ExportDialog.cpp @@ -1,48 +1,48 @@ -/***************************************************************************** -* 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 "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 3202812..5da4b91 --- a/ExportDialog.h +++ b/ExportDialog.h @@ -1,46 +1,46 @@ -/***************************************************************************** -* 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 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 5f92f26..d00b208 --- a/ExportDialog.ui +++ b/ExportDialog.ui @@ -1,226 +1,226 @@ - - - ExportDialog - - - - 0 - 0 - 400 - 300 - - - - Dialog - - - - 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 1187985..63aef4f --- a/ExportThread.cpp +++ b/ExportThread.cpp @@ -1,181 +1,199 @@ -/***************************************************************************** -* 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 "SnapmaticPicture.h" -#include "PictureExport.h" -#include "ProfileWidget.h" -#include "ExportThread.h" -#include "SavegameData.h" -#include "config.h" -#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); - - 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->getPicture(); - 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 originalFileName = picWidget->getPicturePath(); - QString adjustedFileName = originalFileName; - if (adjustedFileName.right(7) == ".hidden") // for the hidden file system - { - adjustedFileName.remove(adjustedFileName.length() - 7, 7); - } - QFileInfo adjustedFileInfo(adjustedFileName); - QString exportFileName = adjustedFileInfo.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)) - { - 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 e6d380e..99ad28b --- a/ExportThread.h +++ b/ExportThread.h @@ -1,56 +1,56 @@ -/***************************************************************************** -* 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 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 8fe6eb6..0a04a59 --- a/GlobalString.cpp +++ b/GlobalString.cpp @@ -1,89 +1,84 @@ -/***************************************************************************** -* 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 -#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 65abad2..711afa9 --- a/GlobalString.h +++ b/GlobalString.h @@ -1,35 +1,35 @@ -/***************************************************************************** -* 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 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 f093fb7..71ccee1 --- a/IconLoader.cpp +++ b/IconLoader.cpp @@ -1,40 +1,61 @@ -/***************************************************************************** -* 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 "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 3fd8f07..8456688 --- a/IconLoader.h +++ b/IconLoader.h @@ -1,31 +1,32 @@ -/***************************************************************************** -* 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 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 new file mode 100644 index 0000000..594cbf8 --- /dev/null +++ b/ImportDialog.cpp @@ -0,0 +1,974 @@ +/***************************************************************************** +* 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 new file mode 100644 index 0000000..1cd93ac --- /dev/null +++ b/ImportDialog.h @@ -0,0 +1,93 @@ +/***************************************************************************** +* 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 new file mode 100644 index 0000000..5780456 --- /dev/null +++ b/ImportDialog.ui @@ -0,0 +1,420 @@ + + + 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 3ebc8c9..f288702 100644 --- a/LICENSE +++ b/LICENSE @@ -1 +1,674 @@ -The license for GRAND THEFT AUTO V SYNC is GNU GPLv3 + GNU 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. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +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.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 2125f2d..d096383 --- a/OptionsDialog.cpp +++ b/OptionsDialog.cpp @@ -1,436 +1,745 @@ -/***************************************************************************** -* 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 "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) -{ - 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()))); - - setupTreeWidget(); - setupLanguageBox(); - setupRadioButtons(); - setupDefaultProfile(); - setupPictureSettings(); - setupCustomGTAFolder(); - setupSnapmaticPictureViewer(); - - 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 7630dc2..f53854e --- a/OptionsDialog.h +++ b/OptionsDialog.h @@ -1,79 +1,89 @@ -/****************************************************************************** -* 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 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 c83a658..6c5e80b --- a/OptionsDialog.ui +++ b/OptionsDialog.ui @@ -1,489 +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 - - - - - - - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - &OK - - - - - - - - - - - 0 - 0 - - - - &Cancel - - - - - - - - - - - - - - cmdClose - 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/PictureCopy.cpp b/PictureCopy.cpp deleted file mode 100755 index de9a8c2..0000000 --- a/PictureCopy.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/***************************************************************************** -* 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 "PictureCopy.h" -#include "PictureDialog.h" -#include "StandardPaths.h" -#include "SidebarGenerator.h" -#include -#include -#include - -PictureCopy::PictureCopy() -{ - -} - -void PictureCopy::copyPicture(QWidget *parent, QString picPath) -{ - QSettings settings("Syping", "gta5sync"); - settings.beginGroup("FileDialogs"); - settings.beginGroup("PictureCopy"); - - QString adjustedPicPath = picPath; - if (adjustedPicPath.right(7) == ".hidden") // for the hidden file system - { - adjustedPicPath.remove(adjustedPicPath.length() - 7, 7); - } - -fileDialogPreSave: - 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(""); - 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("Snapmatic pictures (PGTA*)"); - filters << PictureDialog::tr("All files (**)"); - fileDialog.setNameFilters(filters); - - QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); - - fileDialog.setSidebarUrls(sidebarUrls); - fileDialog.setDirectory(settings.value("Directory", StandardPaths::documentsLocation()).toString()); - fileDialog.selectFile(sgdFileInfo.fileName()); - 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; - } - } - else - { - goto fileDialogPreSave; - } - } - - bool isCopied = QFile::copy(picPath, selectedFile); - if (!isCopied) - { - QMessageBox::warning(parent, PictureDialog::tr("Export as GTA Snapmatic"), PictureDialog::tr("Failed to copy current Snapmatic picture")); - goto fileDialogPreSave; - } - } - else - { - QMessageBox::warning(parent, PictureDialog::tr("Export as GTA Snapmatic"), PictureDialog::tr("No valid file is selected")); - goto fileDialogPreSave; - } - } - - settings.setValue(parent->objectName() + "+Geometry", fileDialog.saveGeometry()); - settings.setValue("Directory", fileDialog.directory().absolutePath()); - settings.endGroup(); -} diff --git a/PictureDialog.cpp b/PictureDialog.cpp old mode 100755 new mode 100644 index 929b01b..99850f8 --- a/PictureDialog.cpp +++ b/PictureDialog.cpp @@ -1,448 +1,928 @@ -/***************************************************************************** -* 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 "PictureDialog.h" -#include "PictureWidget.h" -#include "ProfileDatabase.h" -#include "ui_PictureDialog.h" -#include "SidebarGenerator.h" -#include "StandardPaths.h" -#include "PictureExport.h" -#include "GlobalString.h" -#include "PictureCopy.h" -#include "UiModLabel.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 - -PictureDialog::PictureDialog(ProfileDatabase *profileDB, CrewDatabase *crewDB, QWidget *parent) : - QDialog(parent), profileDB(profileDB), crewDB(crewDB), - ui(new Ui::PictureDialog) -{ - ui->setupUi(this); - windowTitleStr = this->windowTitle(); - jsonDrawString = ui->labJSON->text(); - ui->cmdExport->setEnabled(0); - plyrsList = QStringList(); - fullscreenWidget = 0; - rqfullscreen = 0; - navienabled = 0; - indexed = 0; - picArea = ""; - picTitl = ""; - picPath = ""; - created = ""; - crewID = ""; - locX = ""; - locY = ""; - locZ = ""; - smpic = 0; - - // 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))); - - installEventFilter(this); - installEventFilter(ui->labPicture); - ui->labPicture->setFocusPolicy(Qt::StrongFocus); -} - -PictureDialog::~PictureDialog() -{ - delete jpegExportAction; - delete pgtaExportAction; - delete exportMenu; - delete ui; -} - -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(); - setMinimumSize(width(), newDialogHeight); - setMaximumSize(width(), newDialogHeight); - resize(width(), newDialogHeight); - ui->labPicture->updateGeometry(); - ui->jsonFrame->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; -#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; - } - } - } - 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::setSnapmaticPicture(SnapmaticPicture *picture, QString picturePath, bool readOk, bool _indexed, int _index) -{ - snapmaticPicture = QImage(); - indexed = _indexed; - index = _index; - picPath = picturePath; - smpic = picture; - if (!readOk) - { - QMessageBox::warning(this, tr("Snapmatic Picture Viewer"), tr("Failed at %1").arg(picture->getLastStep())); - return; - } - if (picture->isPicOk()) - { - snapmaticPicture = picture->getPicture(); - ui->labPicture->setPixmap(QPixmap::fromImage(snapmaticPicture, Qt::AutoColor)); - 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); - crewID = crewDB->getCrewName(picture->getSnapmaticProperties().crewID); - created = picture->getSnapmaticProperties().createdDateTime.toString(Qt::DefaultLocaleShortDate); - plyrsList = picture->getSnapmaticProperties().playersList; - picTitl = picture->getPictureTitl(); - picArea = picture->getSnapmaticProperties().area; - if (globalMap.contains(picArea)) - { - picAreaStr = globalMap[picArea]; - } - else - { - picAreaStr = picArea; - } - - QString plyrsStr; - if (plyrsList.length() >= 1) - { - foreach (const QString &player, plyrsList) - { - QString playerName = profileDB->getPlayerName(player.toInt()); - 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, QString picPath, bool readOk) -{ - setSnapmaticPicture(picture, picPath, readOk, false, 0); -} - -void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, QString picPath) -{ - setSnapmaticPicture(picture, picPath, true); -} - -void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk, int index) -{ - setSnapmaticPicture(picture, picture->getPictureFileName(), readOk, true, index); -} - -void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, bool readOk) -{ - setSnapmaticPicture(picture, picture->getPictureFileName(), readOk); -} - -void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture, int index) -{ - setSnapmaticPicture(picture, true, index); -} - -void PictureDialog::setSnapmaticPicture(SnapmaticPicture *picture) -{ - setSnapmaticPicture(picture, true); -} - -void PictureDialog::playerNameUpdated() -{ - if (plyrsList.count() >= 1) - { - QString plyrsStr; - foreach (const QString &player, plyrsList) - { - QString playerName = profileDB->getPlayerName(player.toInt()); - 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::exportPicture(fullscreenWidget, smpic); - } - else - { - PictureExport::exportPicture(this, smpic); - } -} - -void PictureDialog::copySnapmaticPicture() -{ - if (rqfullscreen && fullscreenWidget) - { - PictureCopy::copyPicture(fullscreenWidget, picPath); - } - else - { - PictureCopy::copyPicture(this, picPath); - } -} - -void PictureDialog::on_labPicture_mouseDoubleClicked(Qt::MouseButton button) -{ - if (button == Qt::LeftButton) - { - QRect desktopRect = QApplication::desktop()->screenGeometry(this); - PictureWidget *pictureWidget = new PictureWidget(this); - pictureWidget->setObjectName("PictureWidget"); -#if QT_VERSION >= 0x050600 - pictureWidget->setWindowFlags(pictureWidget->windowFlags()^Qt::FramelessWindowHint^Qt::MaximizeUsingFullscreenGeometryHint); -#else - pictureWidget->setWindowFlags(pictureWidget->windowFlags()^Qt::FramelessWindowHint); -#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; - delete pictureWidget; - } -} - -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 a42883c..a35ebb4 --- a/PictureDialog.h +++ b/PictureDialog.h @@ -1,108 +1,136 @@ -/***************************************************************************** -* 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 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); - void setSnapmaticPicture(SnapmaticPicture *picture, QString picPath, bool readOk, bool indexed, int index); - void setSnapmaticPicture(SnapmaticPicture *picture, QString picPath, bool readOk); - void setSnapmaticPicture(SnapmaticPicture *picture, QString picPath); - 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(); - -signals: - void nextPictureRequested(); - void previousPictureRequested(); - void newPictureCommited(QImage picture); - -protected: - bool eventFilter(QObject *obj, QEvent *ev); - void mousePressEvent(QMouseEvent *ev); - bool event(QEvent *event); - -private: - ProfileDatabase *profileDB; - CrewDatabase *crewDB; - Ui::PictureDialog *ui; - QMap globalMap; - SnapmaticPicture *smpic; - QWidget *fullscreenWidget; - QAction *jpegExportAction; - QAction *pgtaExportAction; - QImage snapmaticPicture; - 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 rqfullscreen; - bool navienabled; - bool indexed; - int index; - 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 ecf1214..f324d84 --- a/PictureDialog.ui +++ b/PictureDialog.ui @@ -1,244 +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 - - - - - - - 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 2007c28..8101ddd --- a/PictureExport.cpp +++ b/PictureExport.cpp @@ -1,200 +1,308 @@ -/***************************************************************************** -* 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 "config.h" -#include "PictureExport.h" -#include "PictureDialog.h" -#include "StandardPaths.h" -#include "SidebarGenerator.h" -#include -#include -#include -#include -#include -#include - -PictureExport::PictureExport() -{ - -} - -void PictureExport::exportPicture(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(); - settings.endGroup(); - // End Picture Settings - - settings.beginGroup("FileDialogs"); - settings.beginGroup("ExportPicture"); - -fileDialogPreSave: - 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); - 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; - } - } - else - { - goto fileDialogPreSave; - } - } - - // Scale Picture - QImage exportPicture = picture->getPicture(); - 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; - } - } - else - { - QMessageBox::warning(parent, PictureDialog::tr("Export as JPG picture"), PictureDialog::tr("No valid file is selected")); - goto fileDialogPreSave; - } - } - - settings.setValue(parent->objectName() + "+Geometry", fileDialog.saveGeometry()); - settings.setValue("Directory", fileDialog.directory().absolutePath()); - settings.endGroup(); - 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 76e224e..623f093 --- a/PictureExport.h +++ b/PictureExport.h @@ -1,34 +1,35 @@ -/***************************************************************************** -* 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 PICTUREEXPORT_H -#define PICTUREEXPORT_H - -#include "SnapmaticPicture.h" -#include -#include - -class PictureExport -{ -public: - PictureExport(); - static void exportPicture(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 5811f0c..49bdb8b 100644 --- a/PictureWidget.cpp +++ b/PictureWidget.cpp @@ -1,6 +1,6 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 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 f9066d9..d2e64f3 100644 --- a/PictureWidget.h +++ b/PictureWidget.h @@ -1,6 +1,6 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 Syping +* 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 @@ -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 a809c82..09ff581 --- a/ProfileDatabase.cpp +++ b/ProfileDatabase.cpp @@ -1,61 +1,85 @@ -/***************************************************************************** -* 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 "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 e32b1c4..99bfc80 --- a/ProfileDatabase.h +++ b/ProfileDatabase.h @@ -1,43 +1,46 @@ -/***************************************************************************** -* 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 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 188dd87..8eca40a --- a/ProfileInterface.cpp +++ b/ProfileInterface.cpp @@ -1,6 +1,6 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC -* Copyright (C) 2016 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" @@ -27,26 +28,53 @@ #include "StandardPaths.h" #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) @@ -58,38 +86,94 @@ 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()))); + 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); + + // 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(); +#ifndef Q_OS_MAC + ui->hlButtons->setSpacing(6 * screenRatio); + ui->hlButtons->setContentsMargins(9 * screenRatio, 9 * screenRatio, 9 * screenRatio, 9 * screenRatio); +#else +#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); - widget->deleteLater(); - 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); - savegame->deleteLater(); + widgets.clear(); + + for (SavegameData *savegame : qAsConst(savegames)) { delete savegame; } - foreach(SnapmaticPicture *picture, pictures) - { - pictures.removeAll(picture); - picture->deleteLater(); + savegames.clear(); + + for (SnapmaticPicture *picture : qAsConst(pictures)) { delete picture; } - profileLoader->deleteLater(); - delete profileLoader; + pictures.clear(); + delete profileLoader; delete ui; } @@ -101,57 +185,78 @@ void ProfileInterface::setProfileFolder(QString folder, QString profile) void ProfileInterface::setupProfileInterface() { + fixedPictures.clear(); ui->labProfileLoading->setText(tr("Loading...")); profileLoader = new ProfileLoader(profileFolder, crewDB); - QObject::connect(profileLoader, SIGNAL(savegameLoaded(SavegameData*, QString)), this, SLOT(savegameLoaded(SavegameData*, QString))); - QObject::connect(profileLoader, SIGNAL(pictureLoaded(SnapmaticPicture*, QString)), this, SLOT(pictureLoaded(SnapmaticPicture*, QString))); +#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(); } -void ProfileInterface::savegameLoaded(SavegameData *savegame, QString savegamePath) +void ProfileInterface::savegameLoaded_event(SavegameData *savegame, QString savegamePath) { - savegameLoaded_f(savegame, savegamePath, false); + savegameLoaded(savegame, savegamePath, false); } -void ProfileInterface::savegameLoaded_f(SavegameData *savegame, QString savegamePath, bool inserted) +void ProfileInterface::savegameLoaded(SavegameData *savegame, QString savegamePath, bool inserted) { SavegameWidget *sgdWidget = new SavegameWidget(this); sgdWidget->setSavegameData(savegame, savegamePath); sgdWidget->setContentMode(contentMode); - widgets[sgdWidget] = "SGD" + QFileInfo(savegamePath).fileName(); - savegames.append(savegame); - if (selectedWidgts != 0 || contentMode == 2) { sgdWidget->setSelectionMode(true); } - QObject::connect(sgdWidget, SIGNAL(savegameDeleted()), this, SLOT(savegameDeleted())); + sgdWidget->setMouseTracking(true); + sgdWidget->installEventFilter(this); + widgets[sgdWidget] = "SGD" % QFileInfo(savegamePath).fileName(); + savegames += savegame; + if (selectedWidgts != 0 || contentMode == 2) + sgdWidget->setSelectionMode(true); + QObject::connect(sgdWidget, SIGNAL(savegameDeleted()), this, SLOT(savegameDeleted_event())); QObject::connect(sgdWidget, SIGNAL(widgetSelected()), this, SLOT(profileWidgetSelected())); QObject::connect(sgdWidget, SIGNAL(widgetDeselected()), this, SLOT(profileWidgetDeselected())); QObject::connect(sgdWidget, SIGNAL(allWidgetsSelected()), this, SLOT(selectAllWidgets())); QObject::connect(sgdWidget, SIGNAL(allWidgetsDeselected()), this, SLOT(deselectAllWidgets())); - if (inserted) { insertSavegameIPI(sgdWidget); } + QObject::connect(sgdWidget, SIGNAL(contextMenuTriggered(QContextMenuEvent*)), this, SLOT(contextMenuTriggeredSGD(QContextMenuEvent*))); + if (inserted) + insertSavegameIPI(sgdWidget); } -void ProfileInterface::pictureLoaded(SnapmaticPicture *picture, QString picturePath) +void ProfileInterface::pictureLoaded_event(SnapmaticPicture *picture) { - pictureLoaded_f(picture, picturePath, false); + pictureLoaded(picture, false); } -void ProfileInterface::pictureLoaded_f(SnapmaticPicture *picture, QString picturePath, bool inserted) +void ProfileInterface::pictureFixed_event(SnapmaticPicture *picture) { - SnapmaticWidget *picWidget = new SnapmaticWidget(profileDB, crewDB, threadDB, this); - picWidget->setSnapmaticPicture(picture, picturePath); + QString fixedPicture = picture->getPictureStr() % " (" % picture->getPictureTitl() % ")"; + fixedPictures << fixedPicture; +} + +void ProfileInterface::pictureLoaded(SnapmaticPicture *picture, bool inserted) +{ + SnapmaticWidget *picWidget = new SnapmaticWidget(profileDB, crewDB, threadDB, profileName, this); + picWidget->setSnapmaticPicture(picture); picWidget->setContentMode(contentMode); - widgets[picWidget] = "PIC" + picture->getPictureSortStr(); - pictures.append(picture); - if (selectedWidgts != 0 || contentMode == 2) { picWidget->setSelectionMode(true); } - QObject::connect(picWidget, SIGNAL(pictureDeleted()), this, SLOT(pictureDeleted())); + picWidget->setMouseTracking(true); + picWidget->installEventFilter(this); + widgets[picWidget] = "PIC" % picture->getPictureSortStr(); + pictures += picture; + if (selectedWidgts != 0 || contentMode == 2) + picWidget->setSelectionMode(true); + QObject::connect(picWidget, SIGNAL(pictureDeleted()), this, SLOT(pictureDeleted_event())); QObject::connect(picWidget, SIGNAL(widgetSelected()), this, SLOT(profileWidgetSelected())); QObject::connect(picWidget, SIGNAL(widgetDeselected()), this, SLOT(profileWidgetDeselected())); QObject::connect(picWidget, SIGNAL(allWidgetsSelected()), this, SLOT(selectAllWidgets())); QObject::connect(picWidget, SIGNAL(allWidgetsDeselected()), this, SLOT(deselectAllWidgets())); QObject::connect(picWidget, SIGNAL(nextPictureRequested(QWidget*)), this, SLOT(dialogNextPictureRequested(QWidget*))); QObject::connect(picWidget, SIGNAL(previousPictureRequested(QWidget*)), this, SLOT(dialogPreviousPictureRequested(QWidget*))); - if (inserted) { insertSnapmaticIPI(picWidget); } + QObject::connect(picWidget, SIGNAL(contextMenuTriggered(QContextMenuEvent*)), this, SLOT(contextMenuTriggeredPIC(QContextMenuEvent*))); + if (inserted) + insertSnapmaticIPI(picWidget); } void ProfileInterface::loadingProgress(int value, int maximum) @@ -161,109 +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->setMaximumSize(picDialog->width(), 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->adaptNewDialogSize(); } } } 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); + const QString newWidgetKey = pictureKeyList.at(picIndex); + SnapmaticWidget *picWidget = static_cast(widgets.key(newWidgetKey)); picDialog->setSnapmaticPicture(picWidget->getPicture(), picIndex); - //picDialog->adaptNewDialogSize(); } } } @@ -274,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); } } @@ -292,7 +458,7 @@ void ProfileInterface::sortingProfileInterface() ui->vlSavegame->setEnabled(true); ui->vlSnapmatic->setEnabled(true); - qApp->processEvents(); + QApplication::processEvents(); } void ProfileInterface::profileLoaded_p() @@ -303,39 +469,67 @@ 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() +void ProfileInterface::savegameDeleted_event() { - savegameDeleted_f((SavegameWidget*)sender()); + savegameDeleted(qobject_cast(sender()), true); } -void ProfileInterface::savegameDeleted_f(QWidget *sgdWidget_) +void ProfileInterface::savegameDeleted(SavegameWidget *sgdWidget, bool isRemoteEmited) { - SavegameWidget *sgdWidget = (SavegameWidget*)sgdWidget_; SavegameData *savegame = sgdWidget->getSavegame(); - if (sgdWidget->isSelected()) { sgdWidget->setSelected(false); } + if (sgdWidget->isSelected()) + sgdWidget->setSelected(false); widgets.remove(sgdWidget); - sgdWidget->close(); - sgdWidget->deleteLater(); + + sgdWidget->disconnect(); + sgdWidget->removeEventFilter(this); + if (sgdWidget == previousWidget) { + previousWidget = nullptr; + } + + // Deleting when the widget did send a event cause a crash + isRemoteEmited ? sgdWidget->deleteLater() : delete sgdWidget; + savegames.removeAll(savegame); delete savegame; } -void ProfileInterface::pictureDeleted() +void ProfileInterface::pictureDeleted_event() { - pictureDeleted_f((SnapmaticWidget*)sender()); + pictureDeleted(qobject_cast(sender()), true); } -void ProfileInterface::pictureDeleted_f(QWidget *picWidget_) +void ProfileInterface::pictureDeleted(SnapmaticWidget *picWidget, bool isRemoteEmited) { - SnapmaticWidget *picWidget = (SnapmaticWidget*)picWidget_; SnapmaticPicture *picture = picWidget->getPicture(); - if (picWidget->isSelected()) { picWidget->setSelected(false); } + if (picWidget->isSelected()) + picWidget->setSelected(false); widgets.remove(picWidget); - picWidget->close(); - picWidget->deleteLater(); + + picWidget->disconnect(); + picWidget->removeEventFilter(this); + if (picWidget == previousWidget) { + previousWidget = nullptr; + } + + // Deleting when the widget did send a event cause a crash + isRemoteEmited ? picWidget->deleteLater() : delete picWidget; + pictures.removeAll(picture); delete picture; } @@ -349,177 +543,697 @@ 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: +fileDialogPreOpen: //Work? QFileDialog fileDialog(this); 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("All profile files (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 (%1)").arg(imageFormatsStr.trimmed()); filters << tr("All files (**)"); fileDialog.setNameFilters(filters); QList sidebarUrls = SidebarGenerator::generateSidebarUrls(fileDialog.sidebarUrls()); fileDialog.setSidebarUrls(sidebarUrls); - fileDialog.setDirectory(settings.value(profileName + "+Directory", StandardPaths::documentsLocation()).toString()); - fileDialog.restoreGeometry(settings.value(profileName + "+Geometry", "").toByteArray()); + fileDialog.setDirectory(settings.value(profileName % "+Directory", StandardPaths::documentsLocation()).toString()); + fileDialog.restoreGeometry(settings.value(profileName % "+Geometry", "").toByteArray()); - if (fileDialog.exec()) - { + if (fileDialog.exec()) { QStringList selectedFiles = fileDialog.selectedFiles(); - if (selectedFiles.length() == 1) - { + if (selectedFiles.length() == 1) { QString selectedFile = selectedFiles.at(0); - if (!importFile(selectedFile, true)) goto fileDialogPreOpen; + QDateTime importDateTime = QDateTime::currentDateTime(); + if (!importFile(selectedFile, importDateTime, true)) goto fileDialogPreOpen; //Work? } - else if (selectedFiles.length() > 1) - { - QString errorStr; - QStringList failedFiles; - foreach(const QString &selectedFile, selectedFiles) - { - if (!importFile(selectedFile, false)) - { - failedFiles << QFileInfo(selectedFile).fileName(); - } - } - foreach (const QString &curErrorStr, failedFiles) - { - errorStr.append(", " + curErrorStr); - } - if (errorStr != "") - { - errorStr.remove(0, 2); - QMessageBox::warning(this, tr("Import"), tr("Import failed with...\n\n%1").arg(errorStr)); - } + else if (selectedFiles.length() > 1) { + importFilesProgress(selectedFiles); } - else - { - QMessageBox::warning(this, tr("Import"), tr("No valid file is selected")); - goto fileDialogPreOpen; + else { + QMessageBox::warning(this, tr("Import..."), tr("No valid file is selected")); + goto fileDialogPreOpen; //Work? } } - settings.setValue(profileName + "+Geometry", fileDialog.saveGeometry()); - settings.setValue(profileName + "+Directory", fileDialog.directory().absolutePath()); + settings.setValue(profileName % "+Geometry", fileDialog.saveGeometry()); + settings.setValue(profileName % "+Directory", fileDialog.directory().absolutePath()); settings.endGroup(); settings.endGroup(); } -bool ProfileInterface::importFile(QString selectedFile, bool warn) +bool ProfileInterface::importFilesProgress(QStringList selectedFiles) +{ + int maximumId = selectedFiles.length(); + int overallId = 0; + QString errorStr; + 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(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(); + + // 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))); + importDateTime = QDateTime::currentDateTime(); + if (!importFile(selectedFile, importDateTime, false)) { + failed << QFileInfo(selectedFile).fileName(); + } + } + + pbDialog.close(); + for (const QString &curErrorStr : qAsConst(failed)) { + errorStr += ", " % curErrorStr; + } + if (errorStr != "") { + errorStr.remove(0, 2); + QMessageBox::warning(this, tr("Import..."), tr("Import failed with...\n\n%1").arg(errorStr)); + return false; + } + return true; +} + +bool ProfileInterface::importFile(QString selectedFile, QDateTime importDateTime, bool notMultiple) { QString selectedFileName = QFileInfo(selectedFile).fileName(); - if (QFile::exists(selectedFile)) - { - if (selectedFileName.left(4) == "PGTA") - { + if (QFile::exists(selectedFile)) { + if ((selectedFileName.left(4) == "PGTA" && !selectedFileName.contains('.')) || selectedFileName.right(4) == ".g5e") { SnapmaticPicture *picture = new SnapmaticPicture(selectedFile); - if (picture->readingPicture()) - { - bool success = importSnapmaticPicture(picture, selectedFile, warn); - if (!success) delete picture; + if (picture->readingPicture(true)) { + bool success = importSnapmaticPicture(picture, notMultiple); + 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 (warn) QMessageBox::warning(this, tr("Import"), tr("Failed to read Snapmatic picture")); - picture->deleteLater(); + 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()) - { - bool success = importSavegameData(savegame, selectedFile, warn); - if (!success) delete savegame; + if (savegame->readingSavegame()) { + bool success = importSavegameData(savegame, selectedFile, notMultiple); + 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 (warn) QMessageBox::warning(this, tr("Import"), tr("Failed to read Savegame file")); - savegame->deleteLater(); + else { + if (notMultiple) QMessageBox::warning(this, tr("Import..."), tr("Failed to read Savegame file")); delete savegame; return false; } } - else - { + else if (isSupportedImageFile(selectedFileName)) { + SnapmaticPicture *picture = new SnapmaticPicture(":/template/template.g5e"); + if (picture->readingPicture(false)) { + if (!notMultiple) { + QFile snapmaticFile(selectedFile); + if (!snapmaticFile.open(QFile::ReadOnly)) { + delete picture; + return false; + } + QImage snapmaticImage; + QImageReader snapmaticImageReader; + snapmaticImageReader.setDecideFormatFromContent(true); + snapmaticImageReader.setDevice(&snapmaticFile); + if (!snapmaticImageReader.read(&snapmaticImage)) { + delete picture; + return false; + } + QString customImageTitle; + QPixmap snapmaticPixmap(960, 536); + snapmaticPixmap.fill(Qt::black); + QPainter snapmaticPainter(&snapmaticPixmap); + if (snapmaticImage.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()) { + diffHeight = 470 - snapmaticImage.height(); + diffHeight = diffHeight / 2; + } + else if (snapmaticImage.width() < snapmaticImage.height()) { + diffWidth = 470 - snapmaticImage.width(); + diffWidth = diffWidth / 2; + } + snapmaticPainter.drawImage(145 + diffWidth, 66 + diffHeight, snapmaticImage); + customImageTitle = ImportDialog::tr("Custom Avatar", "Custom Avatar Description in SC, don't use Special Character!"); + } + else { + // Picture mode + int diffWidth = 0; + int diffHeight = 0; + snapmaticImage = snapmaticImage.scaled(960, 536, Qt::KeepAspectRatio, Qt::SmoothTransformation); + if (snapmaticImage.width() != 960) { + diffWidth = 960 - snapmaticImage.width(); + diffWidth = diffWidth / 2; + } + else if (snapmaticImage.height() != 536) { + diffHeight = 536 - snapmaticImage.height(); + diffHeight = diffHeight / 2; + } + snapmaticPainter.drawImage(0 + diffWidth, 0 + diffHeight, snapmaticImage); + customImageTitle = ImportDialog::tr("Custom Picture", "Custom Picture Description in SC, don't use Special Character!"); + } + snapmaticPainter.end(); + if (!picture->setImage(snapmaticPixmap.toImage())) { + delete picture; + return false; + } + 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(customImageTitle); + picture->updateStrings(); + bool success = importSnapmaticPicture(picture, notMultiple); + if (!success) + delete picture; + return success; + } + else { + bool success = false; + QFile snapmaticFile(selectedFile); + if (!snapmaticFile.open(QFile::ReadOnly)) { + QMessageBox::warning(this, tr("Import..."), tr("Can't import %1 because file can't be open").arg("\""+selectedFileName+"\"")); + delete picture; + return false; + } + QImage *snapmaticImage = new QImage(); + QImageReader snapmaticImageReader; + snapmaticImageReader.setDecideFormatFromContent(true); + snapmaticImageReader.setDevice(&snapmaticFile); + if (!snapmaticImageReader.read(snapmaticImage)) { + QMessageBox::warning(this, tr("Import..."), tr("Can't import %1 because file can't be parsed properly").arg("\""+selectedFileName+"\"")); + delete snapmaticImage; + delete picture; + return false; + } + ImportDialog *importDialog = new ImportDialog(profileName, this); + importDialog->setImage(snapmaticImage); + importDialog->setModal(true); + importDialog->show(); + importDialog->exec(); + if (importDialog->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, 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 { + delete picture; + success = true; + } + delete importDialog; + if (!success) + delete picture; + return success; + } + } + else { + delete picture; + return false; + } + } + else { SnapmaticPicture *picture = new SnapmaticPicture(selectedFile); SavegameData *savegame = new SavegameData(selectedFile); - if (picture->readingPicture()) - { - bool success = importSnapmaticPicture(picture, selectedFile, warn); + 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()) - { - bool success = importSavegameData(savegame, selectedFile, warn); + 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 - { - savegame->deleteLater(); - picture->deleteLater(); - delete savegame; + else { +#ifdef GTA5SYNC_DEBUG + qDebug() << "ImportError SnapmaticPicture" << picture->getLastStep(); + qDebug() << "ImportError SavegameData" << savegame->getLastStep(); +#endif delete picture; - if (warn) 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 (warn) 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::importSnapmaticPicture(SnapmaticPicture *picture, QString picPath, bool warn) +bool ProfileInterface::importUrls(const QMimeData *mimeData) { - QFileInfo picFileInfo(picPath); - QString picFileName = picFileInfo.fileName(); - QString adjustedFileName = picFileName; - if (adjustedFileName.right(7) == ".hidden") // for the hidden file system - { - adjustedFileName.remove(adjustedFileName.length() - 7, 7); + QStringList pathList; + + for (const QUrl ¤tUrl : mimeData->urls()) { + if (currentUrl.isLocalFile()) + pathList += currentUrl.toLocalFile(); } - if (adjustedFileName.right(4) == ".bak") // for the backup file system - { - adjustedFileName.remove(adjustedFileName.length() - 4, 4); + + if (pathList.length() == 1) { + QString selectedFile = pathList.at(0); + return importFile(selectedFile, QDateTime::currentDateTime(), true); } - if (picFileName.left(4) != "PGTA") - { - if (warn) QMessageBox::warning(this, tr("Import"), tr("Failed to import the Snapmatic picture, file not begin with PGTA")); + 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; } - 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")); +} + +bool ProfileInterface::importSnapmaticPicture(SnapmaticPicture *picture, bool warn) +{ + QString picFileName = picture->getPictureFileName(); + 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::copy(picPath, profileFolder + QDir::separator() + adjustedFileName)) - { - picture->setPicFileName(profileFolder + QDir::separator() + adjustedFileName); - pictureLoaded_f(picture, profileFolder + QDir::separator() + adjustedFileName, true); + 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(); + } + } + 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; } } @@ -530,50 +1244,49 @@ bool ProfileInterface::importSavegameData(SavegameData *savegame, QString sgdPat bool foundFree = 0; int currentSgd = 0; - while (currentSgd < 15 && !foundFree) - { + while (currentSgd < 15 && !foundFree) { QString sgdNumber = QString::number(currentSgd); - if (sgdNumber.length() == 1) - { + if (sgdNumber.length() == 1) { sgdNumber.insert(0, "0"); } - sgdFileName = "SGTA500" + sgdNumber; + sgdFileName = "SGTA500" % sgdNumber; - if (!QFile::exists(profileFolder + QDir::separator() + sgdFileName)) - { + if (!QFile::exists(profileFolder % "/" % sgdFileName)) { foundFree = true; } currentSgd++; } - if (foundFree) - { - if (QFile::copy(sgdPath, profileFolder + QDir::separator() + sgdFileName)) - { - savegame->setSavegameFileName(profileFolder + QDir::separator() + sgdFileName); - savegameLoaded_f(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++; @@ -581,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); } } @@ -598,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; @@ -624,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"); @@ -654,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(); @@ -722,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(); @@ -733,80 +1438,95 @@ void ProfileInterface::exportSelected() errorList << getFailedCopyPictures; errorList << getFailedSavegames; - foreach (const QString &curErrorStr, errorList) - { - errorStr.append(", " + curErrorStr); + for (const QString &curErrorStr : qAsConst(errorList)) { + errorStr += ", " % curErrorStr; } - if (errorStr != "") - { + if (errorStr != "") { errorStr.remove(0, 2); - QMessageBox::warning(this, tr("Export selected"), tr("Export failed with...\n\n%1").arg(errorStr)); + QMessageBox::warning(this, tr("Export selected..."), tr("Export failed with...\n\n%1").arg(errorStr)); } - if (exportThread->isFinished()) - { - exportThread->deleteLater(); + if (exportThread->isFinished()) { delete exportThread; } - else - { + else { QEventLoop threadFinishLoop; QObject::connect(exportThread, SIGNAL(finished()), &threadFinishLoop, SLOT(quit())); threadFinishLoop.exec(); - exportThread->deleteLater(); delete exportThread; } } 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; - QString fileName = picWidget->getPicturePath(); - if (!QFile::exists(fileName) || QFile::remove(fileName)) - { - pictureDeleted_f(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_f(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() @@ -814,71 +1534,865 @@ 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() { return selectedWidgts; } + +void ProfileInterface::contextMenuTriggeredPIC(QContextMenuEvent *ev) +{ + 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); + 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 { + 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())); + } +} + +void ProfileInterface::contextMenuTriggeredSGD(QContextMenuEvent *ev) +{ + 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); + 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())); + } + 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())); + } +} + +void ProfileInterface::on_saProfileContent_dropped(const QMimeData *mimeData) +{ + if (!mimeData) + return; + if (mimeData->hasImage()) { + QImage *snapmaticImage = new QImage(qvariant_cast(mimeData->imageData())); + importImage(snapmaticImage, QDateTime::currentDateTime()); + } + 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 692d888..a3297ae --- a/ProfileInterface.h +++ b/ProfileInterface.h @@ -1,110 +1,153 @@ -/***************************************************************************** -* 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 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 selectAllWidgets(); - void deselectAllWidgets(); - void exportSelected(); - void deleteSelected(); - void importFiles(); - -private slots: - void on_cmdCloseProfile_clicked(); - void on_cmdImport_clicked(); - void pictureLoaded(SnapmaticPicture *picture, QString picturePath); - void savegameLoaded(SavegameData *savegame, QString savegamePath); - void loadingProgress(int value, int maximum); - void pictureDeleted(); - void savegameDeleted(); - void profileLoaded_p(); - void profileWidgetSelected(); - void profileWidgetDeselected(); - void dialogNextPictureRequested(QWidget *dialog); - void dialogPreviousPictureRequested(QWidget *dialog); - -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 warn); - bool importSnapmaticPicture(SnapmaticPicture *picture, QString picPath, bool warn = true); - bool importSavegameData(SavegameData *savegame, QString sgdPath, bool warn = true); - void pictureLoaded_f(SnapmaticPicture *picture, QString picturePath, bool inserted); - void savegameLoaded_f(SavegameData *savegame, QString savegamePath, bool inserted); - void savegameDeleted_f(QWidget *sgdWidget); - void pictureDeleted_f(QWidget *picWidget); - 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 65f7ce9..066e636 --- a/ProfileInterface.ui +++ b/ProfileInterface.ui @@ -1,233 +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 - 98 - 28 - - - - - 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 - - - - - - - - - - + + + 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 d8e4f15..a4e4318 --- a/ProfileLoader.cpp +++ b/ProfileLoader.cpp @@ -1,93 +1,133 @@ -/***************************************************************************** -* 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 "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()) - { - emit pictureLoaded(picture, picturePath); - int crewNumber = picture->getSnapmaticProperties().crewID; - if (!crewList.contains(crewNumber)) - { - crewList.append(crewNumber); - } - } - curFile++; - } - - // adding found crews - foreach(int crewID, crewList) - { - crewDB->addCrew(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 "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 c0ed9b4..6bde0ed --- a/ProfileLoader.h +++ b/ProfileLoader.h @@ -1,48 +1,54 @@ -/***************************************************************************** -* 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 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; - -signals: - void pictureLoaded(SnapmaticPicture *picture, QString picturePath); - 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 1b208e2..a325d92 --- a/ProfileWidget.cpp +++ b/ProfileWidget.cpp @@ -1,61 +1,66 @@ -/***************************************************************************** -* 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 "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 cde3218..b7d2f77 --- a/ProfileWidget.h +++ b/ProfileWidget.h @@ -1,45 +1,46 @@ -/***************************************************************************** -* 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 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 397d79a..8831699 100644 --- a/README.md +++ b/README.md @@ -1,30 +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 .rpm file for Red Hat/openSuSE/Fedora. + 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 c08a604..9ebbe66 --- a/SavegameCopy.cpp +++ b/SavegameCopy.cpp @@ -1,100 +1,108 @@ -/***************************************************************************** -* 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 "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: - 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; - } - } - else - { - goto fileDialogPreSave; - } - } - - bool isCopied = QFile::copy(sgdPath, selectedFile); - if (!isCopied) - { - QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("Failed to export current Savegame")); - goto fileDialogPreSave; - } - } - else - { - QMessageBox::warning(parent, SavegameWidget::tr("Export Savegame"), SavegameWidget::tr("No valid file is selected")); - goto fileDialogPreSave; - } - } - - 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 c60edfd..f5550ba --- a/SavegameCopy.h +++ b/SavegameCopy.h @@ -1,32 +1,32 @@ -/***************************************************************************** -* 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 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 ac3feb8..42a9ae3 --- a/SavegameData.cpp +++ b/SavegameData.cpp @@ -1,119 +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 . -*****************************************************************************/ - -#include "StringParser.h" -#include "SavegameData.h" -#include -#include -#include - -SavegameData::SavegameData(const QString &fileName, QObject *parent) : QObject(parent), savegameFileName(fileName) -{ - // PARSE INT INIT - DO NOT CHANGE THIS VALUES - savegameHeaderLength = 260; - verificationValue = QByteArray::fromHex("00000001"); - - // 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 46c64fd..bedb57c --- a/SavegameData.h +++ b/SavegameData.h @@ -1,49 +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 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; - - // PARSE INT - QByteArray verificationValue; - int savegameHeaderLength; -}; - -#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 c4a74aa..27b0229 --- a/SavegameDialog.cpp +++ b/SavegameDialog.cpp @@ -1,39 +1,104 @@ -#include "SavegameDialog.h" -#include "ui_SavegameDialog.h" -#include "SavegameCopy.h" -#include - -SavegameDialog::SavegameDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::SavegameDialog) -{ - ui->setupUi(this); - savegameLabStr = ui->labSavegameText->text(); -} - -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 035b5f9..ac9481e --- a/SavegameDialog.ui +++ b/SavegameDialog.ui @@ -1,86 +1,93 @@ - - - SavegameDialog - - - - 0 - 0 - 400 - 104 - - - - Savegame Viewer - - - - - - - 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 ef58f21..cede630 --- a/SavegameWidget.cpp +++ b/SavegameWidget.cpp @@ -1,275 +1,314 @@ -/***************************************************************************** -* 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 "SavegameWidget.h" -#include "ui_SavegameWidget.h" -#include "SidebarGenerator.h" -#include "ProfileInterface.h" -#include "SavegameDialog.h" -#include "StandardPaths.h" -#include "SavegameData.h" -#include "SavegameCopy.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); - - 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->setWindowFlags(savegameDialog->windowFlags()^Qt::WindowContextHelpButtonHint); - savegameDialog->setSavegameData(sgdata, sgdPath, true); - savegameDialog->setModal(true); - savegameDialog->show(); - savegameDialog->exec(); - savegameDialog->deleteLater(); - 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) -{ - QMenu contextMenu(this); - contextMenu.addAction(tr("&View"), this, SLOT(on_cmdView_clicked())); - contextMenu.addAction(tr("&Export"), this, SLOT(on_cmdCopy_clicked())); - contextMenu.addAction(tr("&Remove"), this, SLOT(on_cmdDelete_clicked())); - if (ui->cbSelected->isVisible()) - { - contextMenu.addSeparator(); - if (!ui->cbSelected->isChecked()) { contextMenu.addAction(tr("&Select"), this, SLOT(savegameSelected())); } - if (ui->cbSelected->isChecked()) { contextMenu.addAction(tr("&Deselect"), this, SLOT(savegameSelected())); } - contextMenu.addAction(tr("Select &All"), this, SLOT(selectAllWidgets()), QKeySequence::fromString("Ctrl+A")); - ProfileInterface *profileInterface = (ProfileInterface*)snwgt; - if (profileInterface->selectedWidgets() != 0) - { - contextMenu.addAction(tr("&Deselect All"), this, SLOT(deselectAllWidgets()), QKeySequence::fromString("Ctrl+D")); - } - } - else - { - contextMenu.addSeparator(); - contextMenu.addAction(tr("&Select"), this, SLOT(savegameSelected())); - contextMenu.addAction(tr("Select &All"), this, SLOT(selectAllWidgets()), QKeySequence::fromString("Ctrl+A")); - } - //ui->SavegameFrame->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()))); - contextMenu.exec(ev->globalPos()); - //ui->SavegameFrame->setStyleSheet(""); -} - -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 85e8fb2..103fa55 --- a/SavegameWidget.h +++ b/SavegameWidget.h @@ -1,83 +1,80 @@ -/***************************************************************************** -* 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 SAVEGAMEWIDGET_H -#define SAVEGAMEWIDGET_H - -#include "ProfileInterface.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(); -}; - -#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 d148f66..ea5e3c6 --- a/SavegameWidget.ui +++ b/SavegameWidget.ui @@ -1,137 +1,135 @@ - - - SavegameWidget - - - - 0 - 0 - 405 - 46 - - - - Savegame Widget - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - - - - - - - - - - - - - :/img/savegame.png - - - - - - - - 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 c2eecdc..e93474c --- a/SidebarGenerator.cpp +++ b/SidebarGenerator.cpp @@ -1,61 +1,61 @@ -/***************************************************************************** -* 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 "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 d01bba0..446f73c --- a/SidebarGenerator.h +++ b/SidebarGenerator.h @@ -1,32 +1,32 @@ -/***************************************************************************** -* 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 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 a959ccb..f07346e 100644 --- a/SnapmaticEditor.cpp +++ b/SnapmaticEditor.cpp @@ -1,243 +1,427 @@ -/***************************************************************************** -* 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 "SnapmaticEditor.h" -#include "ui_SnapmaticEditor.h" -#include "SnapmaticPicture.h" -#include -#include -#include - -SnapmaticEditor::SnapmaticEditor(QWidget *parent) : - QDialog(parent), - ui(new Ui::SnapmaticEditor) -{ - ui->setupUi(this); - ui->cbSelfie->setVisible(false); - ui->cbMugshot->setVisible(false); - ui->cbEditor->setVisible(false); - ui->cmdApply->setDefault(true); - smpic = 0; -} - -SnapmaticEditor::~SnapmaticEditor() -{ - delete ui; -} - -void SnapmaticEditor::on_cbSelfie_toggled(bool checked) -{ - if (checked) - { - ui->cbMugshot->setEnabled(false); - ui->cbEditor->setEnabled(false); - } - else if (!ui->cbDirector->isChecked()) - { - ui->cbMugshot->setEnabled(true); - ui->cbEditor->setEnabled(true); - } -} - - -void SnapmaticEditor::on_cbMugshot_toggled(bool checked) -{ - if (checked) - { - ui->cbSelfie->setEnabled(false); - ui->cbEditor->setEnabled(false); - ui->cbDirector->setEnabled(false); - } - else - { - ui->cbSelfie->setEnabled(true); - ui->cbEditor->setEnabled(true); - ui->cbDirector->setEnabled(true); - } -} - -void SnapmaticEditor::on_cbDirector_toggled(bool checked) -{ - if (checked) - { - ui->cbMugshot->setEnabled(false); - ui->cbEditor->setEnabled(false); - } - else if (!ui->cbSelfie->isChecked()) - { - ui->cbMugshot->setEnabled(true); - ui->cbEditor->setEnabled(true); - } -} - -void SnapmaticEditor::on_cbEditor_toggled(bool checked) -{ - if (checked) - { - ui->cbSelfie->setEnabled(false); - ui->cbMugshot->setEnabled(false); - ui->cbDirector->setEnabled(false); - } - else - { - ui->cbSelfie->setEnabled(true); - ui->cbMugshot->setEnabled(true); - ui->cbDirector->setEnabled(true); - } -} - -void SnapmaticEditor::on_rbSelfie_toggled(bool checked) -{ - if (checked) - { - ui->cbMugshot->setChecked(false); - ui->cbEditor->setChecked(false); - ui->cbSelfie->setChecked(true); - } -} - -void SnapmaticEditor::on_rbMugshot_toggled(bool checked) -{ - if (checked) - { - ui->cbSelfie->setChecked(false); - ui->cbEditor->setChecked(false); - ui->cbDirector->setChecked(false); - ui->cbMugshot->setChecked(true); - } -} - -void SnapmaticEditor::on_rbEditor_toggled(bool checked) -{ - if (checked) - { - ui->cbSelfie->setChecked(false); - ui->cbMugshot->setChecked(false); - ui->cbDirector->setChecked(false); - ui->cbEditor->setChecked(true); - } -} - -void SnapmaticEditor::on_rbCustom_toggled(bool checked) -{ - if (checked) - { - ui->cbSelfie->setChecked(false); - ui->cbMugshot->setChecked(false); - ui->cbEditor->setChecked(false); - } -} - -void SnapmaticEditor::setSnapmaticPicture(SnapmaticPicture *picture) -{ - smpic = picture; - localSpJson = smpic->getSnapmaticProperties(); - ui->rbCustom->setChecked(true); - ui->cbSelfie->setChecked(localSpJson.isSelfie); - ui->cbMugshot->setChecked(localSpJson.isMug); - ui->cbEditor->setChecked(localSpJson.isFromRSEditor); - ui->cbDirector->setChecked(localSpJson.isFromDirector); - ui->cbMeme->setChecked(localSpJson.isMeme); - if (ui->cbSelfie->isChecked()) - { - ui->rbSelfie->setChecked(true); - } - else if (ui->cbMugshot->isChecked()) - { - ui->rbMugshot->setChecked(true); - } - else if (ui->cbEditor->isChecked()) - { - ui->rbEditor->setChecked(true); - } - else - { - ui->rbCustom->setChecked(true); - } -} - -void SnapmaticEditor::on_cmdCancel_clicked() -{ - close(); -} - -void SnapmaticEditor::on_cmdApply_clicked() -{ - if (ui->cbQualify->isChecked()) - { - qualifyAvatar(); - } - localSpJson.isSelfie = ui->cbSelfie->isChecked(); - localSpJson.isMug = ui->cbMugshot->isChecked(); - localSpJson.isFromRSEditor = ui->cbEditor->isChecked(); - localSpJson.isFromDirector = ui->cbDirector->isChecked(); - localSpJson.isMeme = ui->cbMeme->isChecked(); - if (smpic) - { - QString originalFileName = smpic->getPictureFileName(); - 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); - } - smpic->setSnapmaticProperties(localSpJson); - if (!smpic->exportPicture(originalFileName)) - { - QMessageBox::warning(this, tr("Snapmatic Properties"), tr("Patching of Snapmatic Properties failed because of I/O Error")); - } - } - 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); - } - } -} +/***************************************************************************** +* 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 e22960c..98ed2d6 100644 --- a/SnapmaticEditor.h +++ b/SnapmaticEditor.h @@ -1,59 +1,77 @@ -/***************************************************************************** -* 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 SNAPMATICEDITOR_H -#define SNAPMATICEDITOR_H - -#include -#include "SnapmaticPicture.h" - -namespace Ui { -class SnapmaticEditor; -} - -class SnapmaticEditor : public QDialog -{ - Q_OBJECT - -public: - explicit SnapmaticEditor(QWidget *parent = 0); - void setSnapmaticPicture(SnapmaticPicture *picture); - ~SnapmaticEditor(); - -private slots: - void on_cbSelfie_toggled(bool checked); - void on_cbMugshot_toggled(bool checked); - void on_cbDirector_toggled(bool checked); - void on_cbEditor_toggled(bool checked); - 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); - -private: - Ui::SnapmaticEditor *ui; - SnapmaticProperties localSpJson; - SnapmaticPicture *smpic; - 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 fda63ed..fc9ede9 100644 --- a/SnapmaticEditor.ui +++ b/SnapmaticEditor.ui @@ -1,178 +1,276 @@ - - - SnapmaticEditor - - - - 0 - 0 - 375 - 305 - - - - Snapmatic Properties - - - - - - Snapmatic Type - - - - - - Editor - - - - - - - Selfie - - - - - - - Regular - - - - - - - Mugshot - - - - - - - - - - Snapmatic Properties - - - - - - Editor - - - - - - - Meme - - - - - - - Director - - - - - - - Selfie - - - - - - - Mugshot - - - - - - - - - - Extras - - - - - - Qualify as Avatar automatically at apply - - - - - - - Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture - - - true - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - &Apply - - - - - - - - - - &Cancel - - - - - - - - - - - - - + + + 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 2109d48..c3664fd --- a/SnapmaticPicture.cpp +++ b/SnapmaticPicture.cpp @@ -1,647 +1,836 @@ -/***************************************************************************** -* 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 "SnapmaticPicture.h" -#include "StringParser.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -SnapmaticPicture::SnapmaticPicture(const QString &fileName, QObject *parent) : QObject(parent), picFileName(fileName) -{ - // PARSE INT INIT - DO NOT CHANGE THIS VALUES - snapmaticHeaderLength = 278; - snapmaticUsefulLength = 260; - snapmaticFileMaxSize = 528192; - jpegHeaderLineDifStr = 2; - jpegPreHeaderLength = 14; - jpegPicStreamLength = 524288; - jsonStreamLength = 3076; - tideStreamLength = 260; - - // PARSE EDITOR INIT - jpegStreamEditorBegin = 292; - jsonStreamEditorBegin = 524588; - jsonStreamEditorLength = 3072; - rawPicContent = ""; - - // INIT PIC - cachePicture = QImage(0, 0, QImage::Format_RGB32); - picExportFileName = ""; - pictureStr = ""; - lastStep = ""; - sortStr = ""; - titlStr = ""; - descStr = ""; - picOk = 0; - - // INIT JSON - jsonOk = 0; - jsonStr = ""; -} - -SnapmaticPicture::~SnapmaticPicture() -{ -} - -bool SnapmaticPicture::readingPicture(bool writeEnabled_, bool cacheEnabled_) -{ - // Start opening file - // lastStep is like currentStep - - // Set boolean values - writeEnabled = writeEnabled_; - cacheEnabled = cacheEnabled_; - - QFile *picFile = new QFile(picFileName); - QIODevice *picStream; - - if (!picFile->open(QFile::ReadOnly)) - { - lastStep = "1;/1,OpenFile," + StringParser::convertDrawStringForLog(picFileName); - picFile->deleteLater(); - delete picFile; - return false; - } - rawPicContent = picFile->read(snapmaticFileMaxSize); - picFile->close(); - delete picFile; - - picStream = new QBuffer(&rawPicContent); - picStream->open(QIODevice::ReadWrite); - - // Reading Snapmatic Header - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," + StringParser::convertDrawStringForLog(picFileName) + ",1,NOHEADER"; - picStream->close(); - picStream->deleteLater(); - delete picStream; - return false; - } - QByteArray snapmaticHeaderLine = picStream->read(snapmaticHeaderLength); - pictureStr = getSnapmaticPictureString(snapmaticHeaderLine); - - // Reading JPEG Header Line - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," + StringParser::convertDrawStringForLog(picFileName) + ",2,NOHEADER"; - picStream->close(); - picStream->deleteLater(); - delete picStream; - return false; - } - QByteArray jpegHeaderLine = picStream->read(jpegPreHeaderLength); - - // Checking for JPEG - jpegHeaderLine.remove(0, jpegHeaderLineDifStr); - if (jpegHeaderLine.left(4) != "JPEG") - { - lastStep = "2;/3,ReadingFile," + StringParser::convertDrawStringForLog(picFileName) + ",2,NOJPEG"; - picStream->close(); - picStream->deleteLater(); - delete picStream; - return false; - } - - // Read JPEG Stream - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," + StringParser::convertDrawStringForLog(picFileName) + ",2,NOPIC"; - picStream->close(); - picStream->deleteLater(); - delete picStream; - return false; - } - QByteArray jpegRawContent = picStream->read(jpegPicStreamLength); - if (cacheEnabled) picOk = cachePicture.loadFromData(jpegRawContent, "JPEG"); - if (!cacheEnabled) - { - QImage tempPicture; - picOk = tempPicture.loadFromData(jpegRawContent, "JPEG"); - } - - // Read JSON Stream - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," + StringParser::convertDrawStringForLog(picFileName) + ",3,NOJSON"; - picStream->close(); - picStream->deleteLater(); - delete picStream; - return picOk; - } - else if (picStream->read(4) != "JSON") - { - lastStep = "2;/3,ReadingFile," + StringParser::convertDrawStringForLog(picFileName) + ",3,CTJSON"; - picStream->close(); - picStream->deleteLater(); - delete picStream; - return picOk; - } - QByteArray jsonRawContent = picStream->read(jsonStreamLength); - jsonStr = getSnapmaticJSONString(jsonRawContent); - parseJsonContent(); // JSON parsing is own function - - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," + StringParser::convertDrawStringForLog(picFileName) + ",4,NOTITL"; - picStream->close(); - picStream->deleteLater(); - delete picStream; - return picOk; - } - else if (picStream->read(4) != "TITL") - { - lastStep = "2;/3,ReadingFile," + StringParser::convertDrawStringForLog(picFileName) + ",4,CTTITL"; - picStream->close(); - picStream->deleteLater(); - delete picStream; - return picOk; - } - QByteArray titlRawContent = picStream->read(tideStreamLength); - titlStr = getSnapmaticTIDEString(titlRawContent); - - if (!picStream->isReadable()) - { - lastStep = "2;/3,ReadingFile," + StringParser::convertDrawStringForLog(picFileName) + ",5,NODESC"; - picStream->close(); - picStream->deleteLater(); - delete picStream; - return picOk; - } - else if (picStream->read(4) != "DESC") - { - lastStep = "2;/3,ReadingFile," + StringParser::convertDrawStringForLog(picFileName) + ",5,CTDESC"; - picStream->close(); - picStream->deleteLater(); - delete picStream; - return picOk; - } - QByteArray descRawContent = picStream->read(tideStreamLength); - descStr = getSnapmaticTIDEString(descRawContent); - - parseSnapmaticExportAndSortString(); - - picStream->close(); - picStream->deleteLater(); - delete picStream; - if (!writeEnabled) { rawPicContent.clear(); } - return picOk; -} - -QString SnapmaticPicture::getSnapmaticPictureString(const QByteArray &snapmaticHeader) -{ - QByteArray snapmaticBytes = snapmaticHeader.left(snapmaticUsefulLength); - QList snapmaticBytesList = snapmaticBytes.split(char(0x01)); - snapmaticBytes = snapmaticBytesList.at(1); - snapmaticBytesList.clear(); - return StringParser::parseTitleString(snapmaticBytes, snapmaticBytes.length()); -} - -QString SnapmaticPicture::getSnapmaticJSONString(const QByteArray &jsonBytes) -{ - QByteArray jsonUsefulBytes = jsonBytes; - jsonUsefulBytes.replace((char)0x00, ""); - jsonUsefulBytes.replace((char)0x0c, ""); - return QString::fromUtf8(jsonUsefulBytes).trimmed(); -} - -QString SnapmaticPicture::getSnapmaticTIDEString(const QByteArray &tideBytes) -{ - QByteArray tideUsefulBytes = tideBytes; - tideUsefulBytes.remove(0,4); - QList tideUsefulBytesList = tideUsefulBytes.split(char(0x00)); - return QString::fromUtf8(tideUsefulBytesList.at(0)).trimmed(); -} - -void SnapmaticPicture::parseSnapmaticExportAndSortString() -{ - QStringList pictureStrList = pictureStr.split(" - "); - if (pictureStrList.length() <= 2) - { - QString dtStr = pictureStrList.at(1); - QStringList dtStrList = dtStr.split(" "); - if (dtStrList.length() <= 2) - { - QString dayStr; - QString yearStr; - QString monthStr; - QString dateStr = dtStrList.at(0); - QString timeStr = dtStrList.at(1); - timeStr.replace(":",""); - QStringList dateStrList = dateStr.split("/"); - if (dateStrList.length() <= 3) - { - dayStr = dateStrList.at(1); - yearStr = dateStrList.at(2); - monthStr = dateStrList.at(0); - } - QString cmpPicTitl = titlStr; - cmpPicTitl.replace("\"", "''"); - cmpPicTitl.replace(" ", "_"); - cmpPicTitl.replace(":", "-"); - cmpPicTitl.replace("\\", ""); - cmpPicTitl.replace("/", ""); - cmpPicTitl.replace("<", ""); - cmpPicTitl.replace(">", ""); - cmpPicTitl.replace("*", ""); - cmpPicTitl.replace("?", ""); - cmpPicTitl.replace(".", ""); - sortStr = yearStr + monthStr + dayStr + timeStr; - picExportFileName = sortStr + "_" + cmpPicTitl + ".jpg"; - } - } -} - -bool SnapmaticPicture::readingPictureFromFile(const QString &fileName, bool writeEnabled_, bool cacheEnabled_) -{ - if (fileName != "") - { - picFileName = fileName; - return readingPicture(writeEnabled_, cacheEnabled_); - } - else - { - return false; - } -} - -bool SnapmaticPicture::setPicture(const QImage &picture) -{ - if (writeEnabled) - { - QByteArray picByteArray; - QBuffer snapmaticStream(&rawPicContent); - snapmaticStream.open(QIODevice::ReadWrite); - if (snapmaticStream.seek(jpegStreamEditorBegin)) - { - bool saveSuccess; - Q_UNUSED(saveSuccess) - QByteArray picByteArray1; - QBuffer picStream1(&picByteArray1); - picStream1.open(QIODevice::WriteOnly); - saveSuccess = picture.save(&picStream1, "JPEG", 95); - picStream1.close(); - - if (picByteArray1.length() > jpegPicStreamLength) - { - QByteArray picByteArray2; - QBuffer picStream2(&picByteArray2); - picStream2.open(QIODevice::WriteOnly); - saveSuccess = picture.save(&picStream2, "JPEG", 80); - picStream2.close(); - if (picByteArray2.length() > jpegPicStreamLength) - { - snapmaticStream.close(); - return false; - } - picByteArray = picByteArray2; - } - else - { - picByteArray = picByteArray1; - } - } - while (picByteArray.length() != jpegPicStreamLength) - { - picByteArray.append((char)0x00); - } - int result = snapmaticStream.write(picByteArray); - if (result != 0) - { - if (cacheEnabled) - { - cachePicture = picture; - } - return true; - } - return false; - } - return false; -} - -bool SnapmaticPicture::exportPicture(const QString &fileName) -{ - QFile *picFile = new QFile(fileName); - if (picFile->open(QIODevice::WriteOnly)) - { - picFile->write(rawPicContent); - picFile->close(); - picFile->deleteLater(); - return true; - } - else - { - return false; - } -} - -QString SnapmaticPicture::getExportPictureFileName() -{ - return picExportFileName; -} - -QString SnapmaticPicture::getPictureFileName() -{ - return picFileName; -} - -QString SnapmaticPicture::getPictureSortStr() -{ - return sortStr; -} - -QString SnapmaticPicture::getPictureDesc() -{ - return descStr; -} - -QString SnapmaticPicture::getPictureTitl() -{ - return titlStr; -} - -QString SnapmaticPicture::getPictureStr() -{ - return pictureStr; -} - -QString SnapmaticPicture::getLastStep() -{ - return lastStep; -} - -QImage SnapmaticPicture::getPicture() -{ - if (cacheEnabled) - { - return cachePicture; - } - else if (writeEnabled) - { - bool returnOk = 0; - QImage returnPicture; - - QBuffer snapmaticStream(&rawPicContent); - snapmaticStream.open(QIODevice::ReadOnly); - if (snapmaticStream.seek(jpegStreamEditorBegin)) - { - QByteArray jpegRawContent = snapmaticStream.read(jpegPicStreamLength); - returnOk = returnPicture.loadFromData(jpegRawContent, "JPEG"); - } - snapmaticStream.close(); - - if (returnOk) - { - return returnPicture; - } - } - else - { - bool returnOk = 0; - QImage returnPicture; - QIODevice *picStream; - - QFile *picFile = new QFile(picFileName); - if (!picFile->open(QFile::ReadOnly)) - { - lastStep = "1;/1,OpenFile," + StringParser::convertDrawStringForLog(picFileName); - picFile->deleteLater(); - delete picFile; - return QImage(0, 0, QImage::Format_RGB32); - } - 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_RGB32); -} - -bool SnapmaticPicture::isPicOk() -{ - return picOk; -} - -void SnapmaticPicture::setPicFileName(QString picFileName_) -{ - picFileName = picFileName_; -} - -void SnapmaticPicture::clearCache() -{ - cacheEnabled = false; - cachePicture = QImage(0, 0, QImage::Format_RGB32); -} - -// JSON part - -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("area")) - { - localSpJson.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::isJsonOk() -{ - return jsonOk; -} - -QString SnapmaticPicture::getJsonStr() -{ - return jsonStr; -} - -SnapmaticProperties SnapmaticPicture::getSnapmaticProperties() -{ - return localSpJson; -} - -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["area"] = newSpJson.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.append((char)0x00); - } - 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; - return true; - } - } - else - { - return false; - } - } - else - { - return false; - } - - return true; -} - -// VISIBILITY - -bool SnapmaticPicture::isHidden() -{ - if (picFileName.right(7) == ".hidden") - { - return true; - } - return false; -} - -bool SnapmaticPicture::setPictureHidden() -{ - if (!isHidden()) - { - QString newPicFileName = QString(picFileName + ".hidden"); - if (QFile::rename(picFileName, newPicFileName)) - { - picFileName = newPicFileName; - return true; - } - return false; - } - return true; -} - -bool SnapmaticPicture::setPictureVisible() -{ - if (isHidden()) - { - QString newPicFileName = QString(picFileName).remove(picFileName.length() - 7, 7); - if (QFile::rename(picFileName, newPicFileName)) - { - picFileName = newPicFileName; - return true; - } - return false; - } - return true; -} +/***************************************************************************** +* 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 cc1b215..8113cdc --- a/SnapmaticPicture.h +++ b/SnapmaticPicture.h @@ -1,126 +1,172 @@ -/***************************************************************************** -* 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 SNAPMATICPICTURE_H -#define SNAPMATICPICTURE_H - -#include -#include -#include -#include -#include -#include - -struct SnapmaticProperties { - struct SnapmaticLocation { - double x; - double y; - double z; - }; - int crewID; - QString area; - 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(); - bool readingPictureFromFile(const QString &fileName, bool writeEnabled = true, bool cacheEnabled = true); - bool readingPicture(bool writeEnabled = true, bool cacheEnabled = true); - bool isPicOk(); - void clearCache(); - QImage getPicture(); - QString getLastStep(); - QString getPictureStr(); - QString getPictureTitl(); - QString getPictureDesc(); - QString getPictureSortStr(); - QString getPictureFileName(); - QString getExportPictureFileName(); - QDateTime getCreatedDateTime(); - bool setPicture(const QImage &picture); - bool exportPicture(const QString &fileName); - void setPicFileName(QString picFileName_); - - // JSON - bool isJsonOk(); - QString getJsonStr(); - SnapmaticProperties getSnapmaticProperties(); - bool setSnapmaticProperties(SnapmaticProperties newSpJson); - - // VISIBILITY - bool isHidden(); - bool setPictureHidden(); - bool setPictureVisible(); - -private: - QString getSnapmaticPictureString(const QByteArray &snapmaticHeader); - QString getSnapmaticJSONString(const QByteArray &jsonBytes); - QString getSnapmaticTIDEString(const QByteArray &tideBytes); - void parseSnapmaticExportAndSortString(); - QImage cachePicture; - QString picExportFileName; - QString picFileName; - QString pictureStr; - QString lastStep; - QString sortStr; - QString titlStr; - QString descStr; - bool picOk; - bool writeEnabled; - bool cacheEnabled; - - // PARSE INT - int snapmaticHeaderLength; - int snapmaticUsefulLength; - int snapmaticFileMaxSize; - int jpegHeaderLineDifStr; - int jpegPreHeaderLength; - int jpegPicStreamLength; - int jsonStreamLength; - int tideStreamLength; - - // PARSE EDITOR - int jpegStreamEditorBegin; - int jsonStreamEditorBegin; - int jsonStreamEditorLength; - QByteArray rawPicContent; - - // JSON - void parseJsonContent(); - bool jsonOk; - QString jsonStr; - SnapmaticProperties localSpJson; - -signals: - -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 560881a..db718a7 --- a/SnapmaticWidget.cpp +++ b/SnapmaticWidget.cpp @@ -1,369 +1,495 @@ -/***************************************************************************** -* 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 "SnapmaticWidget.h" -#include "ui_SnapmaticWidget.h" -#include "SnapmaticPicture.h" -#include "SnapmaticEditor.h" -#include "DatabaseThread.h" -#include "PictureDialog.h" -#include "PictureExport.h" -#include "PictureCopy.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(); - - snwgt = parent; - 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, QString picturePath) -{ - smpic = picture; - picPath = picturePath; - picStr = picture->getPictureStr(); - picTitl = picture->getPictureTitl(); - - QPixmap SnapmaticPixmap = QPixmap::fromImage(picture->getPicture().scaled(ui->labPicture->width(), ui->labPicture->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::AutoColor); - ui->labPicStr->setText(picStr + "\n" + picTitl + ""); - ui->labPicture->setPixmap(SnapmaticPixmap); - - picture->clearCache(); - - adjustTextColor(); -} - -void SnapmaticWidget::setSnapmaticPicture(SnapmaticPicture *picture) -{ - setSnapmaticPicture(picture, picture->getPictureFileName()); -} - -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->setWindowFlags(picDialog->windowFlags()^Qt::WindowContextHelpButtonHint); - picDialog->setSnapmaticPicture(smpic, picPath, 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 - picDialog->show(); - if (navigationBar) picDialog->stylizeDialog(); - //picDialog->adaptNewDialogSize(); - picDialog->setMinimumSize(picDialog->size()); - picDialog->setMaximumSize(picDialog->size()); - picDialog->exec(); - delete picDialog; -} - -void SnapmaticWidget::on_cmdCopy_clicked() -{ - PictureCopy::copyPicture(this, picPath); -} - -void SnapmaticWidget::on_cmdExport_clicked() -{ - PictureExport::exportPicture(this, smpic); -} - -void SnapmaticWidget::on_cmdDelete_clicked() -{ - 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 (!QFile::exists(picPath)) - { - emit pictureDeleted(); - } - else if(QFile::remove(picPath)) - { - emit pictureDeleted(); - } - else - { - QMessageBox::warning(this, tr("Delete picture"), tr("Failed at deleting %1 from your Snapmatic pictures").arg("\""+picStr+"\"")); - } - } -} - -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) -{ - QMenu contextMenu(this); - QMenu editMenu(tr("Edi&t"), this); - if (isHidden()) - { - editMenu.addAction(tr("Show &In-game"), this, SLOT(makePictureVisibleSlot())); - } - else - { - editMenu.addAction(tr("Hide &In-game"), this, SLOT(makePictureHiddenSlot())); - } - editMenu.addAction(tr("&Edit Properties..."), this, SLOT(editSnapmaticProperties())); - QMenu exportMenu(tr("&Export"), this); - exportMenu.addAction(tr("Export as &JPG picture..."), this, SLOT(on_cmdExport_clicked())); - exportMenu.addAction(tr("Export as >A Snapmatic..."), this, SLOT(on_cmdCopy_clicked())); - contextMenu.addAction(tr("&View"), this, SLOT(on_cmdView_clicked())); - contextMenu.addMenu(&editMenu); - contextMenu.addMenu(&exportMenu); - contextMenu.addAction(tr("&Remove"), this, SLOT(on_cmdDelete_clicked())); - if (ui->cbSelected->isVisible()) - { - contextMenu.addSeparator(); - if (!ui->cbSelected->isChecked()) { contextMenu.addAction(tr("&Select"), this, SLOT(pictureSelected())); } - if (ui->cbSelected->isChecked()) { contextMenu.addAction(tr("&Deselect"), this, SLOT(pictureSelected())); } - contextMenu.addAction(tr("Select &All"), this, SLOT(selectAllWidgets()), QKeySequence::fromString("Ctrl+A")); - ProfileInterface *profileInterface = (ProfileInterface*)snwgt; - if (profileInterface->selectedWidgets() != 0) - { - contextMenu.addAction(tr("&Deselect All"), this, SLOT(deselectAllWidgets()), QKeySequence::fromString("Ctrl+D")); - } - } - else - { - contextMenu.addSeparator(); - contextMenu.addAction(tr("&Select"), this, SLOT(pictureSelected())); - contextMenu.addAction(tr("Select &All"), this, SLOT(selectAllWidgets()), QKeySequence::fromString("Ctrl+A")); - } - contextMenu.exec(ev->globalPos()); -} - -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() -{ - SnapmaticPicture *picture = (SnapmaticPicture*)smpic; - if (picture->setPictureHidden()) - { - picPath = picture->getPictureFileName(); - adjustTextColor(); - return true; - } - return false; -} - -bool SnapmaticWidget::makePictureVisible() -{ - SnapmaticPicture *picture = (SnapmaticPicture*)smpic; - if (picture->setPictureVisible()) - { - picPath = picture->getPictureFileName(); - adjustTextColor(); - return true; - } - return false; -} - -void SnapmaticWidget::makePictureHiddenSlot() -{ - makePictureHidden(); -} - -void SnapmaticWidget::makePictureVisibleSlot() -{ - makePictureVisible(); -} - -void SnapmaticWidget::editSnapmaticProperties() -{ - SnapmaticEditor *snapmaticEditor = new SnapmaticEditor(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 d4b8eaf..8c28f12 --- a/SnapmaticWidget.h +++ b/SnapmaticWidget.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 SNAPMATICWIDGET_H -#define SNAPMATICWIDGET_H - -#include "SnapmaticPicture.h" -#include "ProfileInterface.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, QString picturePath); - void setSnapmaticPicture(SnapmaticPicture *picture); - void setSelectionMode(bool selectionMode); - void setSelected(bool isSelected); - 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(); - -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); -}; - -#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 1a6f42f..a3e7e89 --- a/SnapmaticWidget.ui +++ b/SnapmaticWidget.ui @@ -1,166 +1,169 @@ - - - SnapmaticWidget - - - - 0 - 0 - 490 - 45 - - - - Snapmatic Widget - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - - - - - - - - - - - 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 3e9f38a..c2f8111 --- a/StandardPaths.cpp +++ b/StandardPaths.cpp @@ -1,128 +1,125 @@ -/***************************************************************************** -* 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 "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 d78f25c..590e0dc --- a/StandardPaths.h +++ b/StandardPaths.h @@ -1,41 +1,40 @@ -/***************************************************************************** -* 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 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 aebe01a..4ac3fba --- a/StringParser.cpp +++ b/StringParser.cpp @@ -1,62 +1,54 @@ -/***************************************************************************** -* 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 "StringParser.h" -#include "config.h" -#include -#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((char)0x00)); - 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;","&"); -} - -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; -} +/***************************************************************************** +* 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 44346b6..7dac436 --- a/StringParser.h +++ b/StringParser.h @@ -1,35 +1,32 @@ -/***************************************************************************** -* 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 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); - static QString convertBuildedString(const QString &buildedStr); -}; - -#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 45f4a41..a990b51 --- a/UserInterface.cpp +++ b/UserInterface.cpp @@ -1,472 +1,920 @@ -/***************************************************************************** -* 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 "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 - -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()); - 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)); -} - -void UserInterface::setupDirEnv() -{ - 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); - } - } - - // profiles init - QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR); - 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() -{ - if (GTAV_Profiles.length() == 0) - { - QPushButton *changeDirBtn = new QPushButton(tr("Select >A V Folder..."), ui->swSelection); - changeDirBtn->setObjectName("cmdChangeDir"); - changeDirBtn->setMinimumSize(0, 40); - 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); - 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); - profileUI->deleteLater(); - delete profileUI; - } - this->setWindowTitle(defaultWindowTitle.arg(tr("Select Profile"))); -} - -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->setWindowFlags(aboutDialog->windowFlags()^Qt::WindowContextHelpButtonHint); - aboutDialog->setModal(true); - aboutDialog->show(); - aboutDialog->exec(); - aboutDialog->deleteLater(); - 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->setWindowFlags(optionsDialog->windowFlags()^Qt::WindowContextHelpButtonHint); - optionsDialog->commitProfiles(GTAV_Profiles); - QObject::connect(optionsDialog, SIGNAL(settingsApplied(int,QString)), this, SLOT(settingsApplied(int,QString))); - - optionsDialog->setModal(true); - optionsDialog->show(); - 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 (SGTA* PGTA*)"); - 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") - { - 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 = new PictureDialog(profileDB, crewDB, this); - picDialog->setWindowFlags(picDialog->windowFlags()^Qt::WindowContextHelpButtonHint); - 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())); - - picDialog->show(); - picDialog->setMinimumSize(picDialog->size()); - picDialog->setMaximumSize(picDialog->size()); - - picDialog->exec(); - delete picDialog; -} - -void UserInterface::openSavegameFile(SavegameData *savegame) -{ - SavegameDialog *sgdDialog = new SavegameDialog(this); - sgdDialog->setWindowFlags(sgdDialog->windowFlags()^Qt::WindowContextHelpButtonHint); - sgdDialog->setSavegameData(savegame, savegame->getSavegameFileName(), true); - sgdDialog->setModal(true); - - sgdDialog->show(); - sgdDialog->exec(); - delete sgdDialog; -} - -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 9f9a74a..12abc90 --- a/UserInterface.h +++ b/UserInterface.h @@ -1,89 +1,122 @@ -/***************************************************************************** -* 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 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 - -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); - -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 b2f835f..7927dcc --- a/UserInterface.ui +++ b/UserInterface.ui @@ -1,359 +1,396 @@ - - - UserInterface - - - - 0 - 0 - 625 - 500 - - - - - 625 - 500 - - - - gta5view - %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 gta5view - - - 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 a9f638f..6f96056 --- a/config.h +++ b/config.h @@ -1,73 +1,124 @@ -/***************************************************************************** -* 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 CONFIG_H -#define CONFIG_H -#include - -#ifndef GTA5SYNC_APPVENDOR -#define GTA5SYNC_APPVENDOR "Syping" -#endif - -#ifndef GTA5SYNC_APPSTR -#define GTA5SYNC_APPSTR "gta5view" -#endif - -#ifndef GTA5SYNC_APPVER -#ifndef GTA5SYNC_DAILYB -#define GTA5SYNC_APPVER "1.1.1" -#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 - -#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 f5a0fb7..9326210 --- a/gta5view.pro +++ b/gta5view.pro @@ -1,155 +1,285 @@ -#/***************************************************************************** -#* gta5view Grand Theft Auto V Profile Viewer -#* Copyright (C) 2015-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 . -#*****************************************************************************/ - -QT += core gui network - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets -greaterThan(QT_MAJOR_VERSION, 4): greaterThan(QT_MINOR_VERSION, 1): win32: QT += winextras - -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 \ - OptionsDialog.cpp \ - PictureCopy.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 - -HEADERS += \ - AboutDialog.h \ - AppEnv.h \ - config.h \ - CrewDatabase.h \ - DatabaseThread.h \ - ExportDialog.h \ - ExportThread.h \ - GlobalString.h \ - IconLoader.h \ - OptionsDialog.h \ - PictureCopy.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 - -FORMS += \ - AboutDialog.ui \ - ExportDialog.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 \ - lang/gta5sync_ru.ts - -RESOURCES += \ - res/app.qrc - -DISTFILES += res/app.rc \ - res/gta5sync.desktop \ - res/gta5sync_de.ts \ - res/gta5sync_fr.ts \ - lang/qtbase_ru.qm \ - lang/gta5sync_ru.ts \ - lang/qt_ru.qm \ - lang/README.txt - -INCLUDEPATH += ./uimod - -# WINDOWS ONLY - -win32: DEFINES += GTA5SYNC_WIN -win32: RC_FILE += res/app.rc -win32: LIBS += -luser32 - -# QT4 ONLY STUFF - -isEqual(QT_MAJOR_VERSION, 4): INCLUDEPATH += ./qjson4 -isEqual(QT_MAJOR_VERSION, 4): HEADERS += qjson4/QJsonArray.h \ - qjson4/QJsonDocument.h \ - qjson4/QJsonObject.h \ - qjson4/QJsonParseError.h \ - qjson4/QJsonValue.h \ - qjson4/QJsonValueRef.h \ - qjson4/QJsonParser.h \ - qjson4/QJsonRoot.h - -isEqual(QT_MAJOR_VERSION, 4): SOURCES += qjson4/QJsonArray.cpp \ - qjson4/QJsonDocument.cpp \ - qjson4/QJsonObject.cpp \ - qjson4/QJsonParseError.cpp \ - qjson4/QJsonValue.cpp \ - qjson4/QJsonValueRef.cpp \ - qjson4/QJsonParser.cpp - -# UNIX SYSTEM STUFF - -unix: !macx: appfiles.path = $$(INSTALL_PATH)/share/applications -unix: !macx: appfiles.files = $$PWD/res/gta5view.desktop -unix: !macx: pixmaps.path = $$(INSTALL_PATH)/share/pixmaps -unix: !macx: pixmaps.files = $$PWD/res/gta5view.xpm -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 c21786f..fd72055 --- a/lang/README.txt +++ b/lang/README.txt @@ -2,4 +2,4 @@ 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 +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/lang/gta5sync_ru.qm b/lang/gta5sync_ru.qm deleted file mode 100755 index ef6b45b..0000000 Binary files a/lang/gta5sync_ru.qm and /dev/null differ diff --git a/lang/gta5sync_ru.ts b/lang/gta5sync_ru.ts deleted file mode 100755 index 3c855fb..0000000 --- a/lang/gta5sync_ru.ts +++ /dev/null @@ -1,1418 +0,0 @@ - - - - - AboutDialog - - About gta5sync - О программе gta5sync - - - - About gta5view - - - - - <span style=" font-weight:600;">gta5view</span><br/> -<br/> -A project for viewing 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/>gta5view is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - - - - - &Close - - - - <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 - Закрыть - - - - ExportDialog - - - Dialog - - - - - &JPEG/PNG format - - - - - GTA &Snapmatic format - - - - - Export Format - - - - - Export Size - - - - - Default &Size - - - - - &Desktop Size - - - - - &Custom Size - - - - - Custom Size: - - - - - x - - - - - &Export - - - - - &Close - - - - - OptionsDialog - - - Content Open/Select Mode - - - - - Open with Singleclick - - - - - Open with Doubleclick - - - - - Select with Singleclick - - - - - Default Profile - - - - - Pictures - - - - - Export Size - - - - - Screen Resolution: %1x%2 - - - - - Default: %1x%2 - - - - - %1 - Settings - - - - - Profiles - - - - - Custom GTA V Folder - - - - - Force using Custom Folder - - - - - ... - - - - - - Custom Size: - - - - - x - - - - - Ignore Aspect Ratio - - - - - Export Quality - - - - - Enable Custom Quality - - - - - Quality: - - - - - %1% - - - - - Picture Viewer - - - - - Enable Navigation Bar - - - - - Players - - - - - ID - - - - - Name - - - - - - Language - - - - - &OK - OK, Cancel, Apply - - - - - &Cancel - OK, Cancel, Apply - - - - - System - System like PC System - - - - - %1 (%2 if available) - System like PC System = %1, System Language like Deutsch = %2 - - - - - - %1 - %1 - - - - - The new Custom Folder will initialize after you restart %1. - - - - - The language change will take effect after you restart %1. - - - - - No Profile - No Profile, as default - - - - - - - Profile: %1 - - - - - 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 - - - - - Export picture - Экспорт картинки - - - - - &Export - - - - - &Close - - - - - Export - Экспорт - - - Copy - Копировать - - - - Close - Закрыть - - - - Export as &JPG picture... - - - - - Export as &GTA Snapmatic... - - - - - - Snapmatic Picture Viewer - Просмотрщик фотографий Snapmatic - - - - - Failed at %1 - Ошибка при %1 - - - - - No player - Игроков нет - - - - - No crew - Без группы - - - - Unknown Location - - - - - Export as JPG picture... - - - - - JPEG picture (*.jpg) - Картинка JPEG (*.jpg) - - - - Portable Network Graphics (*.png) - Картинка Portable Network Graphics (*.png) - - - - - - - Export as JPG picture - - - - - - Overwrite %1 with current Snapmatic picture? - Перезаписать %1 текущей картинкой Snapmatic? - - - - - - - Export as GTA Snapmatic - - - - - - Failed to overwrite %1 with current Snapmatic picture - Не удалось перезаписать %1 картинкой Snapmatic - - - - Failed to export current Snapmatic picture - Не удалось экспортировать текущую картинку Snapmatic - - - - - No valid file is selected - Выбранный файл неверен - - - Copy picture - Скопировать картинку - - - - Export as GTA Snapmatic... - - - - - Snapmatic pictures (PGTA*) - Картинки Snapmatic (PGTA*) - - - - All files (**) - Все файлы (**) - - - - Failed to copy current Snapmatic picture - Не удалось скопировать текущую картинку Snapmatic - - - - ProfileInterface - - - Profile Interface - Интерфейс профиля - - - - Loading file %1 of %2 files - Загружается файл %1 из %2 - - - - %1 %2 - - - - - Import exported file - - - - - &Import... - - - - - Close profile - - - - - &Close - - - - Import copy - Импортировать копию - - - Close Profile - Закрыть профиль - - - - Loading... - Загрузка... - - - - Import... - - - - - - - - - - - - - - - - Import - - - - - - All profile files (SGTA* PGTA*) - Все файлы профиля (SGTA* PGTA*) - - - - - Savegames files (SGTA*) - Файлы сохранения (SGTA*) - - - - - Snapmatic pictures (PGTA*) - Картинка Snapmatic (PGTA*) - - - - - All files (**) - Все файлы (**) - - - - Import failed with... - -%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 - - - - - Failed to import the Snapmatic picture, file not begin with PGTA - - - - - Failed to import the Snapmatic picture, the picture is already in the game - - - - - 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 - - - - - - No Snapmatic pictures or Savegames files are selected - - - - - - - Remove selected - - - - - You really want remove the selected Snapmatic picutres and Savegame files? - - - - - Failed at remove the complete selected Snapmatic pictures and/or Savegame files - - - - Failed to import copy of Snapmatic picture because the file not begin with PGTA - Не удалось имортировать копию картинки Snapmatic, т.к. файл не начинается с PGTA - - - Failed to import copy of Snapmatic picture because the copy failed - Не получилось имортировать копию картинки Snapmatic, потому что не удалось его скопировать - - - Failed to import copy of Savegame file because the copy failed - Не получилось имортировать копию сохранения, потому что не удалось его скопировать - - - Failed to import copy of Savegame file because no free Savegame slot left - Не получилось имортировать копию сохранения, потому что не осталось свободных под них слотов - - - - - - - Export selected - - - - - %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... - - - - - Initializing export... - - - - - Export failed with... - -%1 - - - - - - - Export file %1 of %2 files - - - - - QApplication - - - Font - - - - - Selected Font: %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 - - - - Copy - Копировать - - - Close - Закрыть - - - - Failed at %1 - Ошибка при %1 - - - - SavegameWidget - - - Savegame Widget - Виджет сохранений - - - 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 - - - - - - WRONG FORMAT - - - - - AUTOSAVE - %1 -%2 - - - - - SAVE %3 - %1 -%2 - - - - - UNKNOWN - - - - - Are you sure to delete %1 from your savegames? - Вы уверены, что хотите удалить сохранение %1? - - - - 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? - - - - - Failed to overwrite %1 with current Savegame - - - - - 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 - Выбранный файл неверен - - - - SnapmaticEditor - - - - - Snapmatic Properties - - - - - Snapmatic Type - - - - - - Editor - - - - - - Selfie - - - - - Regular - - - - - - Mugshot - - - - - Director - - - - - Meme - - - - - Extras - - - - - Qualify as Avatar automatically at apply - - - - - Qualify as Avatar allows you to use this Snapmatic as a Social Club profile picture - - - - - &Apply - - - - - &Cancel - - - - - Patching of Snapmatic Properties failed because of I/O Error - - - - - SnapmaticWidget - - - Snapmatic Widget - Виджет 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 - - - - Edi&t - - - - - Show &In-game - - - - - Hide &In-game - - - - - &Edit Properties... - - - - - &Export - - - - - Export as &JPG picture... - - - - - Export as &GTA Snapmatic... - - - - - &View - - - - - &Remove - - - - - - &Select - - - - - &Deselect - - - - - - Select &All - - - - - &Deselect All - - - - - Copy picture - Скопировать картинку - - - - Export picture - Экспорт картинки - - - - UserInterface - - gta5sync - %1 - gta5sync - %1 - - - - Select profile - Выбор профиля - - - - %1 %2 - - - - - &File - - - - - &Help - - - - - &Edit - - - - - &Profile - - - - - &Exit - - - - - Exit - - - - - Close &Profile - - - - - Ctrl+End - - - - - &Settings - - - - - Ctrl+Del - - - - - &Import files... - - - - - Ctrl+I - - - - - - Select &GTA V Folder... - - - - - Ctrl+G - - - - - Show In-gam&e - - - - - Shift+E - - - - - Hi&de In-game - - - - - Shift+D - - - - - &Close - - - - - gta5view - %1 - - - - - &Selection visibility - - - - - &About gta5view - - - - - 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+S - - - - - Ctrl+E - - - - - Ctrl+D - - - - - - Select Profile - - - - - - - - Select GTA V Folder... - - - - - %2 - %1 - - - - - Open File... - - - - - - - - Open File - - - - - Can't open %1 because of not valid file format - - - - gta5sync - gta5sync - - - - &Reload - - - - GTA V Folder not found! - Папка GTA V не была найдена! - - - diff --git a/lang/qtbase_ru.qm b/lang/qtbase_ru.qm deleted file mode 100755 index 8dba5ce..0000000 Binary files a/lang/qtbase_ru.qm and /dev/null differ diff --git a/main.cpp b/main.cpp old mode 100755 new mode 100644 index e58c35b..350d430 --- a/main.cpp +++ b/main.cpp @@ -1,483 +1,310 @@ -/***************************************************************************** -* 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 "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); - - if (argumentFileType == "PGTA") - { - 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 = new CrewDatabase(); - ProfileDatabase *profileDB = new ProfileDatabase(); - DatabaseThread *threadDB = new DatabaseThread(crewDB); - PictureDialog *picDialog = new PictureDialog(profileDB, crewDB); - SnapmaticPicture picture; - - bool readOk = picture.readingPictureFromFile(arg1); - picDialog->setWindowFlags(picDialog->windowFlags()^Qt::WindowContextHelpButtonHint); - picDialog->setWindowIcon(IconLoader::loadingAppIcon()); - picDialog->setSnapmaticPicture(&picture, readOk); - - int crewID = picture.getSnapmaticProperties().crewID; - if (crewID != 0) { crewDB->addCrew(crewID); } - if (!readOk) { return 1; } - - QObject::connect(threadDB, SIGNAL(playerNameFound(int, QString)), profileDB, SLOT(setPlayerName(int, QString))); - QObject::connect(threadDB, SIGNAL(playerNameUpdated()), picDialog, SLOT(playerNameUpdated())); - threadDB->start(); - - picDialog->show(); - picDialog->setMinimumSize(picDialog->size()); - picDialog->setMaximumSize(picDialog->size()); - - return a.exec(); - } - else if (selectedAction == "showsgd") - { - SavegameDialog *savegameDialog = new SavegameDialog(); - SavegameData savegame; - - bool readOk = savegame.readingSavegameFromFile(arg1); - savegameDialog->setWindowFlags(savegameDialog->windowFlags()^Qt::WindowContextHelpButtonHint); - savegameDialog->setWindowIcon(IconLoader::loadingAppIcon()); - savegameDialog->setSavegameData(&savegame, arg1, readOk); - - if (!readOk) { return 1; } - - savegameDialog->show(); - - return a.exec(); - } - - CrewDatabase *crewDB = new CrewDatabase(); - ProfileDatabase *profileDB = new ProfileDatabase(); - DatabaseThread *threadDB = new DatabaseThread(crewDB); - - QObject::connect(threadDB, SIGNAL(playerNameFound(int, QString)), profileDB, SLOT(setPlayerName(int, QString))); - threadDB->start(); - - UserInterface *uiWindow = new UserInterface(profileDB, crewDB, threadDB); - uiWindow->setWindowIcon(IconLoader::loadingAppIcon()); - uiWindow->setupDirEnv(); - uiWindow->show(); - - return a.exec(); -} - +/***************************************************************************** +* 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 diff --git a/qjson4/QJsonArray.cpp b/qjson4/QJsonArray.cpp old mode 100755 new mode 100644 index 531941f..ad8a82b --- a/qjson4/QJsonArray.cpp +++ b/qjson4/QJsonArray.cpp @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify diff --git a/qjson4/QJsonArray.h b/qjson4/QJsonArray.h old mode 100755 new mode 100644 index 94aab1b..dc4fc69 --- a/qjson4/QJsonArray.h +++ b/qjson4/QJsonArray.h @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify diff --git a/qjson4/QJsonDocument b/qjson4/QJsonDocument old mode 100755 new mode 100644 diff --git a/qjson4/QJsonDocument.cpp b/qjson4/QJsonDocument.cpp old mode 100755 new mode 100644 index 59adf32..7f8ad18 --- a/qjson4/QJsonDocument.cpp +++ b/qjson4/QJsonDocument.cpp @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify @@ -39,45 +39,45 @@ QJsonDocument::QJsonDocument() : root_(0) { // Name: QJsonDocument //------------------------------------------------------------------------------ QJsonDocument::QJsonDocument(const QJsonObject &object) : root_(0) { - setObject(object); + setObject(object); } //------------------------------------------------------------------------------ // Name: QJsonDocument //------------------------------------------------------------------------------ QJsonDocument::QJsonDocument(const QJsonArray &array) : root_(0) { - setArray(array); + setArray(array); } //------------------------------------------------------------------------------ // Name: QJsonDocument //------------------------------------------------------------------------------ QJsonDocument::QJsonDocument(const QJsonDocument &other) : root_(0) { - if(other.root_) { - root_ = other.root_->clone(); - } + if(other.root_) { + root_ = other.root_->clone(); + } } //------------------------------------------------------------------------------ // Name: ~QJsonDocument //------------------------------------------------------------------------------ QJsonDocument::~QJsonDocument() { - delete root_; + delete root_; } //------------------------------------------------------------------------------ // Name: operator= //------------------------------------------------------------------------------ QJsonDocument &QJsonDocument::operator=(const QJsonDocument &other) { - QJsonDocument(other).swap(*this); - return *this; + QJsonDocument(other).swap(*this); + return *this; } //------------------------------------------------------------------------------ // Name: operator!= //------------------------------------------------------------------------------ bool QJsonDocument::operator!=(const QJsonDocument &other) const { - return !(*this == other); + return !(*this == other); } //------------------------------------------------------------------------------ @@ -85,30 +85,30 @@ bool QJsonDocument::operator!=(const QJsonDocument &other) const { //------------------------------------------------------------------------------ bool QJsonDocument::operator==(const QJsonDocument &other) const { - if(isArray() && other.isArray()) { - return array() == other.array(); - } + if(isArray() && other.isArray()) { + return array() == other.array(); + } - if(isObject() && other.isObject()) { - return object() == other.object(); - } + if(isObject() && other.isObject()) { + return object() == other.object(); + } - if(isEmpty() && other.isEmpty()) { - return true; - } + if(isEmpty() && other.isEmpty()) { + return true; + } - if(isNull() && other.isNull()) { - return true; - } + if(isNull() && other.isNull()) { + return true; + } - return false; + return false; } //------------------------------------------------------------------------------ // Name: isArray //------------------------------------------------------------------------------ bool QJsonDocument::isArray() const { - return root_ && root_->toArray(); + return root_ && root_->toArray(); } //------------------------------------------------------------------------------ @@ -116,56 +116,56 @@ bool QJsonDocument::isArray() const { //------------------------------------------------------------------------------ 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 + // 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_; + return !root_; } //------------------------------------------------------------------------------ // Name: isNull //------------------------------------------------------------------------------ bool QJsonDocument::isNull() const { - return !root_; + return !root_; } //------------------------------------------------------------------------------ // Name: isObject //------------------------------------------------------------------------------ bool QJsonDocument::isObject() const { - return root_ && root_->toObject(); + return root_ && root_->toObject(); } //------------------------------------------------------------------------------ // Name: setArray //------------------------------------------------------------------------------ void QJsonDocument::setArray(const QJsonArray &array) { - setRoot(array); + setRoot(array); } //------------------------------------------------------------------------------ // Name: setObject //------------------------------------------------------------------------------ void QJsonDocument::setObject(const QJsonObject &object) { - setRoot(object); + setRoot(object); } //------------------------------------------------------------------------------ // Name: setRoot //------------------------------------------------------------------------------ void QJsonDocument::setRoot(const QJsonRoot &root) { - delete root_; - root_ = root.clone(); + delete root_; + root_ = root.clone(); } //------------------------------------------------------------------------------ // Name: toBinaryData //------------------------------------------------------------------------------ QByteArray QJsonDocument::toBinaryData() const { - QByteArray r; - // TODO(eteran): implement this - return r; + QByteArray r; + // TODO(eteran): implement this + return r; } //------------------------------------------------------------------------------ @@ -173,100 +173,107 @@ QByteArray QJsonDocument::toBinaryData() const { //------------------------------------------------------------------------------ QString QJsonDocument::escapeString(const QString &s) const { - QString r; + 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; - } - } + 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; + return r; } //------------------------------------------------------------------------------ // Name: toJson //------------------------------------------------------------------------------ -QString QJsonDocument::toJson(const QJsonValue &v, JsonFormat format) const { +QString QJsonDocument::toJson(const QJsonValue &v, JsonFormat format, int indent) const { - QString b; - QTextStream ss(&b, QIODevice::WriteOnly | QIODevice::Text); + 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 << "["; - if(!a.empty()) { - QJsonArray::const_iterator it = a.begin(); - QJsonArray::const_iterator e = a.end(); + 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(); - ss << toJson(*it++, format); + if (!compact) ss << QByteArray(4*indent, ' '); + ss << toJson(*it++, format, indent+1); - 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(); + 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(); - 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; - } + 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; + return b; } //------------------------------------------------------------------------------ @@ -274,19 +281,19 @@ QString QJsonDocument::toJson(const QJsonValue &v, JsonFormat format) const { //------------------------------------------------------------------------------ QByteArray QJsonDocument::toJson(JsonFormat format) const { - Q_UNUSED(format); + Q_UNUSED(format); - if(isArray()) { - QString s = toJson(array(), format); - return s.toUtf8(); - } + if(isArray()) { + QString s = toJson(array(), format); + return s.toUtf8(); + } - if(isObject()) { - QString s = toJson(object(), format); - return s.toUtf8(); - } + if(isObject()) { + QString s = toJson(object(), format); + return s.toUtf8(); + } - return QByteArray(); + return QByteArray(); } //------------------------------------------------------------------------------ @@ -294,17 +301,17 @@ QByteArray QJsonDocument::toJson(JsonFormat format) const { //------------------------------------------------------------------------------ QVariant QJsonDocument::toVariant() const { - if(!isEmpty()) { - if(QJsonObject *const object = root_->toObject()) { - return object->toVariantMap(); - } + if(!isEmpty()) { + if(QJsonObject *const object = root_->toObject()) { + return object->toVariantMap(); + } - if(QJsonArray *const array = root_->toArray()) { - return array->toVariantList(); - } - } + if(QJsonArray *const array = root_->toArray()) { + return array->toVariantList(); + } + } - return QVariant(); + return QVariant(); } //------------------------------------------------------------------------------ @@ -312,13 +319,13 @@ QVariant QJsonDocument::toVariant() const { //------------------------------------------------------------------------------ QJsonArray QJsonDocument::array() const { - if(!isEmpty()) { - if(QJsonArray *const array = root_->toArray()) { - return *array; - } - } + if(!isEmpty()) { + if(QJsonArray *const array = root_->toArray()) { + return *array; + } + } - return QJsonArray(); + return QJsonArray(); } //------------------------------------------------------------------------------ @@ -326,54 +333,54 @@ QJsonArray QJsonDocument::array() const { //------------------------------------------------------------------------------ QJsonObject QJsonDocument::object() const { - if(!isEmpty()) { - if(QJsonObject *const object = root_->toObject()) { - return *object; - } - } + if(!isEmpty()) { + if(QJsonObject *const object = root_->toObject()) { + return *object; + } + } - return QJsonObject(); + return QJsonObject(); } //------------------------------------------------------------------------------ // Name: rawData //------------------------------------------------------------------------------ const char *QJsonDocument::rawData(int *size) const { - Q_UNUSED(size); - // TODO(eteran): implement this - return 0; + 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); + Q_UNUSED(data); + Q_UNUSED(validation); - QJsonDocument doc; - // TODO(eteran): implement this - return doc; + QJsonDocument doc; + // TODO(eteran): implement this + return doc; } //------------------------------------------------------------------------------ // Name: fromJson //------------------------------------------------------------------------------ QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error) { - QJsonDocument doc; + QJsonDocument doc; - const char *const begin = json.constData(); - const char *const end = begin + json.size(); + const char *const begin = json.constData(); + const char *const end = begin + json.size(); - QJsonParser parser(begin, end); + QJsonParser parser(begin, end); - doc.root_ = parser.parse(); + doc.root_ = parser.parse(); - if(error) { - *error = parser.state(); - } + if(error) { + *error = parser.state(); + } - return doc; + return doc; } //------------------------------------------------------------------------------ @@ -381,10 +388,10 @@ QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *e //------------------------------------------------------------------------------ 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)); + // data has to be aligned to a 4 byte boundary. + Q_ASSERT(!(reinterpret_cast(data) % 3)); - return fromBinaryData(QByteArray::fromRawData(data, size), validation); + return fromBinaryData(QByteArray::fromRawData(data, size), validation); } //------------------------------------------------------------------------------ @@ -392,26 +399,26 @@ QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidat //------------------------------------------------------------------------------ QJsonDocument QJsonDocument::fromVariant(const QVariant &variant) { - QJsonDocument doc; + 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())); - } + 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; + return doc; } //------------------------------------------------------------------------------ // Name: swap //------------------------------------------------------------------------------ void QJsonDocument::swap(QJsonDocument &other) { - qSwap(root_, other.root_); + qSwap(root_, other.root_); } #endif diff --git a/qjson4/QJsonDocument.h b/qjson4/QJsonDocument.h old mode 100755 new mode 100644 index 12e8fc7..3731f62 --- a/qjson4/QJsonDocument.h +++ b/qjson4/QJsonDocument.h @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify @@ -36,66 +36,66 @@ class QJsonRoot; class QJsonDocument { public: - enum DataValidation { - Validate = 0, - BypassValidation = 1 - }; + enum DataValidation { + Validate = 0, + BypassValidation = 1 + }; - enum JsonFormat { - Indented, - Compact - }; + enum JsonFormat { + Indented, + Compact + }; public: - QJsonDocument(); - QJsonDocument(const QJsonObject &object); - QJsonDocument(const QJsonArray &array); - QJsonDocument(const QJsonDocument &other); - ~QJsonDocument(); + QJsonDocument(); + QJsonDocument(const QJsonObject &object); + QJsonDocument(const QJsonArray &array); + QJsonDocument(const QJsonDocument &other); + ~QJsonDocument(); public: - QJsonDocument &operator=(const QJsonDocument &other); + QJsonDocument &operator=(const QJsonDocument &other); public: - bool operator!=(const QJsonDocument &other) const; - bool operator==(const QJsonDocument &other) const; + 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; + 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; + 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; + QJsonArray array() const; + QJsonObject object() const; + const char *rawData(int *size) const; public: - void setArray(const QJsonArray &array); - void setObject(const QJsonObject &object); + 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); + 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; + 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); + void swap(QJsonDocument &other); private: - QJsonRoot *root_; + QJsonRoot *root_; }; #endif diff --git a/qjson4/QJsonObject b/qjson4/QJsonObject old mode 100755 new mode 100644 diff --git a/qjson4/QJsonObject.cpp b/qjson4/QJsonObject.cpp old mode 100755 new mode 100644 index 55f8cf1..ac36bb0 --- a/qjson4/QJsonObject.cpp +++ b/qjson4/QJsonObject.cpp @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify diff --git a/qjson4/QJsonObject.h b/qjson4/QJsonObject.h old mode 100755 new mode 100644 index ad657bc..6ee3a97 --- a/qjson4/QJsonObject.h +++ b/qjson4/QJsonObject.h @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify diff --git a/qjson4/QJsonParseError b/qjson4/QJsonParseError old mode 100755 new mode 100644 diff --git a/qjson4/QJsonParseError.cpp b/qjson4/QJsonParseError.cpp old mode 100755 new mode 100644 index 6bcfd98..598c67c --- a/qjson4/QJsonParseError.cpp +++ b/qjson4/QJsonParseError.cpp @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify diff --git a/qjson4/QJsonParseError.h b/qjson4/QJsonParseError.h old mode 100755 new mode 100644 index b87d7aa..eddf04d --- a/qjson4/QJsonParseError.h +++ b/qjson4/QJsonParseError.h @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify diff --git a/qjson4/QJsonParser.cpp b/qjson4/QJsonParser.cpp old mode 100755 new mode 100644 index 9b084f7..052c9a8 --- a/qjson4/QJsonParser.cpp +++ b/qjson4/QJsonParser.cpp @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify diff --git a/qjson4/QJsonParser.h b/qjson4/QJsonParser.h old mode 100755 new mode 100644 index d54a0d9..f11f5a0 --- a/qjson4/QJsonParser.h +++ b/qjson4/QJsonParser.h @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify diff --git a/qjson4/QJsonRoot b/qjson4/QJsonRoot old mode 100755 new mode 100644 diff --git a/qjson4/QJsonRoot.h b/qjson4/QJsonRoot.h old mode 100755 new mode 100644 index 77b9751..d249465 --- a/qjson4/QJsonRoot.h +++ b/qjson4/QJsonRoot.h @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify diff --git a/qjson4/QJsonValue b/qjson4/QJsonValue old mode 100755 new mode 100644 diff --git a/qjson4/QJsonValue.cpp b/qjson4/QJsonValue.cpp old mode 100755 new mode 100644 index 68bf87f..8ac4770 --- a/qjson4/QJsonValue.cpp +++ b/qjson4/QJsonValue.cpp @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify diff --git a/qjson4/QJsonValue.h b/qjson4/QJsonValue.h old mode 100755 new mode 100644 index bf32898..d902352 --- a/qjson4/QJsonValue.h +++ b/qjson4/QJsonValue.h @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify diff --git a/qjson4/QJsonValueRef b/qjson4/QJsonValueRef old mode 100755 new mode 100644 diff --git a/qjson4/QJsonValueRef.cpp b/qjson4/QJsonValueRef.cpp old mode 100755 new mode 100644 index 7d67ef4..dade257 --- a/qjson4/QJsonValueRef.cpp +++ b/qjson4/QJsonValueRef.cpp @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify diff --git a/qjson4/QJsonValueRef.h b/qjson4/QJsonValueRef.h old mode 100755 new mode 100644 index 567c68a..478b657 --- a/qjson4/QJsonValueRef.h +++ b/qjson4/QJsonValueRef.h @@ -1,5 +1,5 @@ /***************************************************************************** -* gta5sync GRAND THEFT AUTO V SYNC +* gta5view Grand Theft Auto V Profile Viewer * Copyright (C) 2016 Syping * * This program is free software: you can redistribute it and/or modify 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 2abd805..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 b380c75..0000000 --- a/res/app.qrc +++ /dev/null @@ -1,34 +0,0 @@ - - - gta5sync_de.qm - qt_de.qm - qtbase_de.qm - gta5sync_fr.qm - qt_fr.qm - qtbase_fr.qm - - - savegame.png - 5sync-48.png - 5sync-16.png - 5sync-24.png - 5sync-32.png - 5sync-40.png - 5sync-64.png - 5sync-96.png - 5sync-128.png - 5sync-256.png - back.png - next.png - 960x536.png - empty1x16.png - - - global.de.ini - global.en.ini - global.ja.ini - global.fr.ini - global.zh.ini - global.es.ini - - diff --git a/res/app.rc b/res/app.rc old mode 100755 new mode 100644 index 2d8ac5b..56b1561 --- a/res/app.rc +++ b/res/app.rc @@ -1,32 +1,33 @@ -IDI_ICON1 ICON DISCARDABLE "5sync.ico" - -#include - -VS_VERSION_INFO VERSIONINFO -FILEVERSION 1, 1, 1, 0 -PRODUCTVERSION 1, 1, 1, 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.1.1\0" - VALUE "InternalName", "gta5view\0" - VALUE "LegalCopyright", "Copyright 2016 Syping\0" - VALUE "OriginalFilename", "gta5view.exe\0" - VALUE "ProductName", "gta5view\0" - VALUE "ProductVersion", "1.1.1\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 new file mode 100644 index 0000000..463d846 Binary files /dev/null and b/res/avatararea.png differ diff --git a/res/avatarareaimport.png b/res/avatarareaimport.png new file mode 100644 index 0000000..cddc1d8 Binary files /dev/null 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 fea6010..0916785 --- a/res/global.de.ini +++ b/res/global.de.ini @@ -41,9 +41,8 @@ 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" +ISHEIST="Cayo Perico" JAIL="Bolingbroke-Strafanstalt" KOREAT="Little Seoul" LACT="Land-Act-Stausee" @@ -52,8 +51,6 @@ 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" diff --git a/res/global.en.ini b/res/global.en.ini old mode 100755 new mode 100644 index 4c31ad3..97c84dd --- a/res/global.en.ini +++ b/res/global.en.ini @@ -41,9 +41,8 @@ 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" +ISHEIST="Cayo Perico" JAIL="Bolingbroke Penitentiary" KOREAT="Little Seoul" LACT="Land Act Reservoir" @@ -52,9 +51,6 @@ 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" diff --git a/res/global.es.ini b/res/global.es.ini index dda46a7..b364c50 100644 --- a/res/global.es.ini +++ b/res/global.es.ini @@ -41,9 +41,8 @@ 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" +ISHEIST="Cayo Perico" JAIL="Penitenciaría de Bolingbroke" KOREAT="Little Seoul" LACT="Embalse de Land Act" @@ -53,14 +52,9 @@ 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" 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 130709f..807c5ec 100644 --- a/res/global.fr.ini +++ b/res/global.fr.ini @@ -40,9 +40,8 @@ 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" +ISHEIST="Cayo Perico" JAIL="Pénitencier de Bolingbroke" KOREAT="Little Seoul" LACT="Land Act Reservoir" @@ -52,9 +51,6 @@ 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" 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 366e294..f38223d --- a/res/global.ja.ini +++ b/res/global.ja.ini @@ -9,7 +9,6 @@ BEACH="ベスプッチ・ビーチ" BHAMCA="バンナムキャニオン" BRADP="ブラドック・パス" BRADT="ブラドック・トンネル" -BSS_BSTR_131="リチャーズ・マジェスティック" BURTON="バートン" CALAFB="カラフィア橋" CANNY="ラトン・キャニオン" @@ -42,9 +41,8 @@ HARMO="ハーモニー" HAWICK="ハウィック" HEART="ハートアタック・ビーチ" HORS="バインウッド・レーストラック" -HUD_MG_TRI_ALA="アラモ海" -HUD_MG_TRI_VES="ベスプッチ" HUMLAB="ヒューメイン研究所" +ISHEIST="カヨ・ペリコ" JAIL="ボーリングブローク刑務所" KOREAT="リトル・ソウル" LACT="ランド・アクト貯水池" @@ -54,9 +52,6 @@ LMESA="ラ・メサ" LOSPFY="ラ・プエルタ高速道路" LOSPUER="ラ・プエルタ" LOSSF="ロスサントス高速道路" -MGCR_1="サウス・ロスサントス" -MGCR_6="ベスプッチ運河" -MGSR_3="ラトン・キャニオン" MIRR="ミラー・パーク" MORN="モーニングウッド" MOVIE="リチャーズ・マジェスティック" 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 new file mode 100644 index 0000000..996f807 --- /dev/null +++ b/res/global.ru.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="Гольф-клуб" +GRAPES="Грейпсид" +GREATC="Грейт-Чапаррал" +HARMO="Хармони" +HAWICK="Хавик" +HEART="Харт-Аттакс-Бич" +HORS="Гоночная трасса Вайнвуда" +HUMLAB="Лаборатория Humane Labs and Research" +ISHEIST="Кайо-Перико" +JAIL="Тюрьма Болингброук" +KOREAT="Маленький Сеул" +LACT="Лэнд-экт-резервуар" +LAGO="Лаго-Занкудо" +LDAM="Лэнд-экт-дэм" +LMESA="Ла-Меса" +LOSPFY="Шоссе Ла-Пуэрта" +LOSPUER="Ла-Пуэрта" +LOSSF="Шоссе Лос-Сантоса" +MIRR="Миррор-Парк" +MORN="Морнингвуд" +MOVIE="Richards Majestic" +MTCHIL="Гора Чилиад" +MTGORDO="Гора Гордо" +MTJOSE="Гора Джосайя" +MURRI="Мурьета-Хайтс" +NCHU="Северный Чумаш" +OBSERV="Обсерватория Галилео" +OCEANA="Тихий океан" +PALCOV="Бухта Палето" +PALETO="Палето-Бэй" +PALFOR="Лес Палето" +PALHIGH="Нагорья Паломино" +PALMPOW="Электростанция Палмер-Тэйлор" +PBLUFF="Пасифик-Блаффс" +PBOX="Пиллбокс-Хилл" +PROCOB="Прокопио-Бич" +PROL="Северный Янктон" +RANCHO="Ранчо" +RGLEN="Ричман-Глен" +RICHM="Ричман" +ROCKF="Рокфорд-Хиллз" +RTRAK="Трасса Redwood Lights" +SANAND="Сан-Андреас" +SANCHIA="Сан-Шаньский горный хребет" +SANDY="Сэнди-Шорс" +SENORA="Шоссе Сенора" +SKID="Мишн-Роу" +SLAB="Стэб-Сити" +SLSANT="Южный Лос-Сантос" +STAD="Арена Maze Bank" +STRAW="Строберри" +TATAMO="Татавиамские горы" +TERMINA="Терминал" +TEXTI="Текстайл-Сити" +TONGVAH="Тонгва-Хиллз" +TONGVAV="Долина Тонгва" +UTOPIAG="Утопия-Гарденс" +VCANA="Каналы Веспуччи" +VESP="Веспуччи" +VINE="Вайнвуд" +WINDF="Ветряная ферма Ron Alternates" +WMIRROR="Вест-Миррор-драйв" +WVINE="Западный Вайнвуд" +ZANCUDO="Река Занкудо" +ZENORA="Шоссе Сенора" diff --git a/res/global.zh.ini b/res/global.zh.ini index 35564bd..49650cc 100644 --- a/res/global.zh.ini +++ b/res/global.zh.ini @@ -40,9 +40,8 @@ HARMO="和美尼" HAWICK="霍伊克" HEART="驚心海灘" HORS="好麥塢賽馬場" -HUD_MG_TRI_ALA="阿拉莫海" -HUD_MG_TRI_VES="威斯普奇" HUMLAB="人道研究實驗室" +ISHEIST="佩里克島" JAIL="博林布魯克監獄" KOREAT="小首爾" LACT="蘭艾水庫" @@ -52,9 +51,6 @@ LMESA="梅薩" LOSPFY="洛波塔高速公路" LOSPUER="洛波塔" LOSSF="洛聖都高速公路" -MGCR_1="南洛聖都" -MGCR_6="威斯普奇運河" -MGSR_3="雷通峽谷" MIRR="米羅公園" MORN="摩寧塢" MOVIE="李察尊爵" 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 ada88af..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 bf75dad..054fac6 --- a/res/gta5sync_de.ts +++ b/res/gta5sync_de.ts @@ -1,108 +1,114 @@ - + 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 is 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 gta5view - Über gta5view + About %1 + Über %1 - <span style=" font-weight:600;">gta5view</span><br/> + <span style="font-weight:600">%1</span><br/> <br/> -A project for viewing Grand Theft Auto V Snapmatic<br/> -Pictures and Savegames<br/> +%2<br/> <br/> -Project version: %1<br/> -Project build: %4, %5<br/> -Compiled with Qt %2<br/> -Running with Qt %3<br/> +Version %3<br/> +Created on %4<br/> +Built with Qt %5<br/> +Running with Qt %6<br/> <br/> -Copyright &copy; <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5view is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - <span style=" font-weight:600;">gta5view</span><br/> +%7 + <span style="font-weight:600">%1</span><br/> <br/> -Ein Projekt zum ansehen von Grand Theft Auto V<br/> -Snapmatic Bilder und Spielständen<br/> +%2<br/> <br/> -Projektversion: %1<br/> -Projektbau: %4, %5<br/> -Gebaut mit Qt %2<br/> -Läuft auf Qt %3<br/> +Version %3<br/> +Erstellt am %4<br/> +Gebaut mit Qt %5<br/> +Läuft auf Qt %6<br/> <br/> -Copyright &copy; <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5view ist lizenziert unter <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> +%7 - + &Close S&chließen - generated by class - Generiert von der Klasse + + Translated by %1 + Translated by translator, example Translated by Syping + Übersetzt von %1 - <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> + + 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 - Close - Schließen + + 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 - <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> + + 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> + + + + Release + Release + + + + Release Candidate + Release Candidate + + + + Daily Build + Daily Build + + + + Developer + Entwickler + + + + Beta + Beta + + + + Alpha + Alpha + + + + + Custom + Eigener + + + + CrewDatabase + + + + No Crew + Keine Crew @@ -110,109 +116,525 @@ Copyright &copy; <a href="https://github.com/Syping/">Syping Dialog - + Dialog - Format - Format - - - + &JPEG/PNG format &JPEG/PNG Format - + GTA &Snapmatic format GTA &Snapmatic Format - Resolution - Auflösung - - - + Export Format Export Format - + Export Size Export Größe - + Default &Size &Standard Größe - + &Desktop Size &Desktop Größe - + &Custom Size &Eigene Größe - + Custom Size: Eigene Größe: - + x x - + &Export &Exportieren - + &Close 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... + + + + + Ignore Aspect Ratio + Seitenverhältnis ignorieren + + + + Avatar + Avatar + + + + Picture + Bild + + + + 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 @@ -229,23 +651,11 @@ Copyright &copy; <a href="https://github.com/Syping/">Syping 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 @@ -339,78 +749,272 @@ Copyright &copy; <a href="https://github.com/Syping/">Syping + 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 @@ -419,239 +1023,277 @@ Copyright &copy; <a href="https://github.com/Syping/">Syping 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 + Taste 1 - Avatar Vorschaumodus +Taste 2 - Overlay umschalten +Pfeiltasten - Navigieren + + + Snapmatic Picture Viewer Snapmatic Bildansicht - - + Failed at %1 - Fehlgeschlagen bei %1 + Fehlgeschlagen beim %1 - - - No player - Keine Spieler - - - - - No crew + + + No Crew Keine Crew - + + + + No Players + Keine Spieler + + + + Avatar Preview Mode +Press 1 for Default View + Avatar Vorschaumodus +Drücke 1 für Standardmodus + + + 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 - Copy picture - Bild kopieren + + Export as Snapmatic... + Als Snapmatic exportieren... - Export picture for Import... - Exporti + + + + + + Export as Snapmatic + Als Snapmatic exportieren - - Export as GTA Snapmatic... - Exportiere als GTA Snapmatic... + + Exported Snapmatic to "%1" because of using the .auto extension. + Snapmatic wurde wegen Benutzung der .auto Erweiterung zu "%1" exportiert. - + + 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 @@ -667,98 +1309,118 @@ Copyright &copy; <a href="https://github.com/Syping/">Syping 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*) - - - - + + Savegames files (SGTA*) Spielstanddateien (SGTA*) - - + + Snapmatic pictures (PGTA*) Snapmatic Bilder (PGTA*) - - + + 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 @@ -767,254 +1429,301 @@ Copyright &copy; <a href="https://github.com/Syping/">Syping %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 - - 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 + + + + 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 + + + %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) + 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 @@ -1026,20 +1735,6 @@ Exportieren als: Savegame Widget 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 @@ -1047,13 +1742,10 @@ Exportieren als: + Export Exportieren - - Copy - Kopieren - Delete @@ -1061,13 +1753,11 @@ Exportieren als: - - Delete savegame Savegame löschen - + Export Savegame... Spielstand exportieren... @@ -1077,100 +1767,84 @@ Exportieren als: 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 - + Are you sure to delete %1 from your savegames? 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 @@ -1182,159 +1856,302 @@ Exportieren als: 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 - + Snapmatic Type Snapmatic Typ - - + Editor Editor - - + Selfie Selbstporträt - + Regular Typisch - - + Mugshot - Fahndungsfoto + Polizeifoto - Custom - Eigenes - - - + Director Director - + Meme Meme - + + + Snapmatic Title + Snapmatic Titel + + + + Snapmatic Values + 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 - + + + + 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: + + + + 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 @@ -1344,530 +2161,409 @@ 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 deinen Snapmatic Bilder zu löschen? + 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 - - - - &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 - - Failed at deleting %1 from your Snapmatic pictures - Fehlgeschlagen beim Löschen %1 von deinen Snapmatic Bildern + + 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 gta5view - &Über gta5view - - - + &Exit B&eenden - - - Ctrl+Q - Strg+Q - Select profile Profil auswählen - - - gta5view - %1 - gta5view - %1 - %1 %2 %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 42bcebb..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 d0a8cb7..7e90ba6 100644 --- a/res/gta5sync_fr.ts +++ b/res/gta5sync_fr.ts @@ -3,71 +3,114 @@ 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 gta5view - À propos de gta5view + About %1 + À propos de %1 - <span style=" font-weight:600;">gta5view</span><br/> + <span style="font-weight:600">%1</span><br/> <br/> -A project for viewing Grand Theft Auto V Snapmatic<br/> -Pictures and Savegames<br/> +%2<br/> <br/> -Project version: %1<br/> -Project build: %4, %5<br/> -Compiled with Qt %2<br/> -Running with Qt %3<br/> +Version %3<br/> +Created on %4<br/> +Built with Qt %5<br/> +Running with Qt %6<br/> <br/> -Copyright &copy; <a href="https://github.com/Syping/">Syping</a> 2016<br/>gta5view is licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> - <span style=" font-weight:600;">gta5view</span><br/> +%7 + <span style="font-weight:600">%1</span><br/> <br/> -Un outil pour gérer les photos Snapmatic<br/> -et les fichiers de sauvegarde de Grand Theft Auto V<br/> +%2<br/> <br/> -gta5view v%1<br/> -Build %4, %5<br/> -Compilé avec Qt %2<br/> -Fonctionne avec Qt %3<br/> +Version %3<br/> +Publié le %4<br/> +Compilé avec Qt %5<br/> +Fonctionne avec Qt %6<br/> <br/> -Copyright &copy; <a href="https://github.com/Syping/">Syping</a> 2016<br/> -gta5view est distribué sous license <a href="https://www.gnu.org/licenses/gpl-3.0.html#content">GNU GPLv3</a> +%7 - + &Close &Fermer + + + Translated by %1 + Translated by translator, example Translated by Syping + Traduit par %1 + + + + 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 + + + + 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> + + + + Release + Release + + + + Release Candidate + Release Candidate + + + + Daily Build + Daily Build + + + + Developer + Developer + + + + Beta + Beta + + + + Alpha + Alpha + + + + + Custom + Personnalisé + + + + CrewDatabase + + + + No Crew + Aucun crew + ExportDialog @@ -77,61 +120,499 @@ gta5view est distribué sous license <a href="https://www.gnu.org/licens Exporter - + Export Format Format - + &JPEG/PNG format &JPEG/PNG - + GTA &Snapmatic format GTA &Snapmatic - + Export Size Dimensions - + Default &Size Par &défaut - + &Desktop Size Dimensions de l'&écran - + &Custom Size &Personnalisé - + Custom Size: Dimensions: - + x x - + &Export &Exporter - + &Close &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... + + + + + Ignore Aspect Ratio + Déverrouiller le ratio d'aspect + + + + Avatar + Avatar + + + + Picture + Image + + + + 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 + &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 + + OptionsDialog @@ -150,19 +631,17 @@ gta5view est distribué sous license <a href="https://www.gnu.org/licens 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 @@ -267,69 +746,272 @@ gta5view est distribué sous license <a href="https://www.gnu.org/licens + 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 @@ -338,159 +1020,277 @@ gta5view est distribué sous license <a href="https://www.gnu.org/licens 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*) - - All files (**) - Tous les fichiers (**) + + + + + + Export as Snapmatic + Exporter comme Snapmatic - - - - - Export as GTA Snapmatic - Exporter comme GTA Snapmatic + + Exported Snapmatic to "%1" because of using the .auto extension. + Exporté comme "%1" avec l'utilisation de l'extension .auto. - - + + 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 + Touche 1 - Mode Aperçu Avatar +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 player - Aucun joueur - - - - - No crew + + + 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 + + + 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 @@ -505,94 +1305,125 @@ gta5view est distribué sous license <a href="https://www.gnu.org/licens 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 @@ -601,97 +1432,227 @@ gta5view est distribué sous license <a href="https://www.gnu.org/licens %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 - - 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à + + Can't import %1 because file format can't be detected + Impossible d'importer %1, le format du fichier n'est pas détecté - + + 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, 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 @@ -700,67 +1661,75 @@ gta5view est distribué sous license <a href="https://www.gnu.org/licens %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) 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 @@ -794,13 +1763,12 @@ gta5view est distribué sous license <a href="https://www.gnu.org/licens + Export Exporter - - Delete savegame Supprimer la sauvegarde @@ -810,118 +1778,131 @@ gta5view est distribué sous license <a href="https://www.gnu.org/licens 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 - + Are you sure to delete %1 from your savegames? 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 @@ -930,83 +1911,255 @@ gta5view est distribué sous license <a href="https://www.gnu.org/licens SnapmaticEditor - - + + + + + + + + Snapmatic Properties Propriétés Snapmatic - + Snapmatic Type - Type Snapmatic + Type - - + Editor Éditeur - - + Selfie Selfie - + Regular Normal - - + Mugshot - Mugshot + Photo d'identité - Custom - Personnalisé - - - + Director Director - + Meme Meme - + + + Snapmatic Title + Titre Snapmatic + + + + Snapmatic Values + 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 : + + + + 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 @@ -1016,136 +2169,158 @@ gta5view est distribué sous license <a href="https://www.gnu.org/licens 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 @@ -1158,220 +2333,245 @@ gta5view est distribué sous license <a href="https://www.gnu.org/licens + 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 - - gta5view - %1 - gta5view - %1 - - - - &About gta5view - &À propos de gta5view - - - + 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 new file mode 100644 index 0000000..214adf1 Binary files /dev/null and b/res/gta5sync_ru.qm differ diff --git a/res/gta5sync_ru.ts b/res/gta5sync_ru.ts new file mode 100644 index 0000000..a05570c --- /dev/null +++ b/res/gta5sync_ru.ts @@ -0,0 +1,2584 @@ + + + + + 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 + VADemon,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> + + + + Release + Релиз + + + + Release Candidate + Предварительный выпуск + + + + Daily Build + Дневная сборка + + + + Developer + Разработчик + + + + Beta + Бета + + + + Alpha + Альфа + + + + + Custom + Не известен контекст + + Своя + + + + CrewDatabase + + + + No Crew + Вне банды + + + + ExportDialog + + + Dialog + Возможно не это имелось ввиду, немецкого перевода нету + + Диалоговое окно + + + + &JPEG/PNG format + Формат &JPEG/PNG + + + + GTA &Snapmatic format + Формат GTA &Snapmatic + + + + Export Format + Формат экспорта + + + + Export Size + Размер экспорта + + + + Default &Size + По &умолчанию + + + + &Desktop Size + Размер рабо&чего стола + + + + &Custom Size + &Другой размер + + + + Custom Size: + Размер: + + + + x + на + + + + &Export + &Экспортировать + + + + &Close + &Закрыть + + + + 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... + Импортировать... + + + + + Ignore Aspect Ratio + Игнорировать соотн. сторон + + + + Avatar + Аватар + + + + Picture + Картинка + + + + 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 + + + Content Open/Select Mode + Открывать/выбирать содержимое + + + Open with Singleclick + Открывать одним щелчком + + + + Open with Doubleclick + Открывать двойным щелчком + + + Select with Singleclick + Выбирать одним щелчком + + + + Default Profile + Профиль по умолчанию + + + + Pictures + Картинки + + + + Export Size + Размер экспорта + + + + Screen Resolution: %1x%2 + Как разрешение экрана: %1x%2 + + + + Default: %1x%2 + По умолчанию: %1x%2 + + + + %1 - Settings + %1 - Настройки + + + + Profiles + Профили + + + + Custom GTA V Folder + Другая папка GTA V + + + + Force using Custom Folder + Использовать эту папку + + + + ... + ... + + + + + 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 + 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 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. + + + + View %1 User Statistics Online + Посмотреть статистику %1 онлайн + + + + Not registered + Не зарегистрирован + + + + + + + Yes + Да + + + + + No + Нет + + + + + OS defined + Настройка от ОС + + + + + Steam defined + Настройка от Steam + + + + No Profile + No Profile, as default + Не выбран + + + + + + Profile: %1 + Профиль: %1 + + + + PictureDialog + + + <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 + &Управление + + + + Manage picture + Настройки картинки + + + + Snapmatic Picture Viewer - %1 + Просмотрщик фотографий Snapmatic - %1 + + + + Close viewer + Закрыть просмотрщик + + + + &Close + &Закрыть + + + + + Export + Экспортировать + + + + + Export as &Picture... + Экспортировать как &картинку... + + + + + Export as &Snapmatic... + Экспортировать как &Snapmatic... + + + + + &Overwrite Image... + &Перезаписать картинку... + + + + + &Edit Properties... + &Изменить свойства... + + + + + Open &Map Viewer... + Открыть &карту... + + + + Key 1 - Avatar Preview Mode +Key 2 - Toggle Overlay +Arrow Keys - Navigate + Клавиша 1 - Режим показа аватарки +Клавиша 2 - Вкл./выкл. оверлей +Стрелки - Навигация + + + + Snapmatic Picture Viewer + Просмотрщик фотографий Snapmatic + + + + Failed at %1 + Ошибка при %1 + + + + + No Crew + Вне банды + + + + + + No Players + Игроков нет + + + + Avatar Preview Mode +Press 1 for Default View + Режим просмотра аватарок +Нажмите 1 для стандартного просмотра + + + + Unknown Location + Неизвестное место + + + + Portable Network Graphics (*.png) + Картинка Portable Network Graphics (*.png) + + + + + Overwrite %1 with current Snapmatic picture? + Перезаписать %1 текущей картинкой Snapmatic? + + + + Export as Picture... + Экспорт как картинку... + + + + 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 + Выбранный файл неверен + + + + GTA V Export (*.g5e) + GTA V Export (*.g5e) + + + + GTA V Raw Export (*.auto) + GTA V Экспорт Исходника (*.auto) + + + + Snapmatic pictures (PGTA*) + Картинки Snapmatic (PGTA*) + + + + + Open &JSON Editor... + Открыть &редактор JSON... + + + + 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 + Введите идентификатор игрока из 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 + &Закрыть + + + + 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 + Импортировать + + + + + Savegames files (SGTA*) + Файлы сохранения (SGTA*) + + + + + Snapmatic pictures (PGTA*) + Картинка Snapmatic (PGTA*) + + + + + + + All files (**) + Все файлы (**) + + + + + Import file %1 of %2 files + Импортируются файлы %1 из %2 + + + + Import failed with... + +%1 + Ошибка при импорте... + +%1 + + + + + Failed to read Snapmatic picture + Не удалось загрузить картинку Snapmatic + + + + + Failed to read Savegame file + Не удалось загрузить файл сохранения + + + + + + No valid file is selected + Выбранный файл неверен + + + + Enabled pictures: %1 of %2 + Включенные картинки: %1 из %2 + + + + Importable files (%1) + Файлы для импорта (%1) + + + + + + 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, 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 + + + + 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 и файлы сохранений? + + + + Prepare Content for Import... + Подготовка данных к импорту... + + + + + Qualify as Avatar + Пометить как Аватар + + + + + + + + + No Snapmatic pictures are selected + Не выделена ни одна картинка Snapmatic + + + + + + + Patch selected... + Пропатчить выделенные... + + + + + + + + + + + Patch file %1 of %2 files + Изменяется файл %1 из %2 + + + + + + + + + %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... + Экпортировать выделенное... + + + + Export failed with... + +%1 + Экспорт провалился на... + +%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) + + + + 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 + &Экспорт + + + + &Close + &Закрыть + + + + Failed at %1 + Ошибка при %1 + + + + SavegameWidget + + + Savegame Widget + Виджет сохранений + + + + View savegame + Просмотреть сохранение + + + + View + Просмотр + + + + + Export + Экспорт + + + + 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 + НЕИЗВЕСТНО + + + + Are you sure to delete %1 from your savegames? + Вы уверены, что хотите удалить сохранение %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 + Не удалось экспортировать текущее сохранение + + + + No valid file is selected + Выбранный файл неверен + + + + SnapmaticEditor + + + + + + + + + + + Snapmatic Properties + Свойства Snapmatic + + + + Snapmatic Type + Тип Snapmatic + + + + Editor + Редактор + + + + Selfie + Автопортрет + + + + Regular + Обычный + + + + Mugshot + Под арестом + + + + Director + Director + + + + Snapmatic Values + Значения в Snapmatic + + + + Crew: %1 (%2) + Банда: %1 (%2) + + + + Meme + Meme + + + + + Snapmatic Title + Заголовок Snapmatic + + + + Title: %1 (%2) + Заголовок: %1 (%2) + + + + 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 + + + + 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: + + + + 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 + + + Snapmatic Widget + Виджет 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 + + + + 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 + Ск&рыть в игре + + + + &Export + &Экспорт + + + + &View + По&казать + + + + &Remove + У&далить + + + + + &Select + &Выделить + + + + + &Deselect + Сн&ять выделение + + + + + Select &All + В&ыбрать все + + + + + &Deselect All + Снять выбо&р со всех + + + + Copy picture + Скопировать картинку + + + + Export picture + Экспорт картинки + + + + 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 + &ОК + + + + UserInterface + + + Select profile + Выбор профиля + + + + %1 %2 + %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 + Закрыть п&рофиль + + + + &Settings + &Настройки + + + + &Import files... + &Импортировать файлы... + + + + + Select &GTA V Folder... + Выбрать &папку GTA V... + + + + Show In-gam&e + Показывать в и&гре + + + + Hi&de In-game + Скры&ть в игре + + + + + + Change &Players... + &Изменить игрока... + + + + + + Change &Title... + Изменить &Заголовок... + + + + + + Change &Crew... + Изменить &банду... + + + + + + &Qualify as Avatar + &Пометить как Аватар + + + + + + + &Close + &Закрыть + + + + &Selection visibility + В&идимость выделенного + + + + Selection &mass tools + Инструменты &массовой выборки + + + + Select &All + В&ыбрать все + + + + &Deselect All + Снять выбо&р со всех + + + + &Export selected... + &Экпортировать выделенное... + + + + &Remove selected + &Удалить выделенное + + + + &Open File... + &Открыть файл... + + + + + + Select Profile + Выбор профиля + + + + + + + Select GTA V Folder... + Выбрать папку GTA V... + + + + + %2 - %1 + %2 - %1 + + + + + + &About %1 + &О программе %1 + + + + + &Donate + По&жертвовать + + + + Donate + Пожертвовать + + + + Donation methods + Способы для взноса + + + + &Copy + &Копировать + + + View + Просмотр + + + Copy + Копировать + + + + Open File... + Открыть файл... + + + + + + + Open File + Открыть файл + + + + Can't open %1 because of not valid file format + Не удалось открыть %1 из-за неверного формата файла + + + + %1 - Messages + %1 - Новости + + + + &Reload + Пере&загрузить + + + + + + 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 168bfd0..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 new file mode 100644 index 0000000..3f57eaf --- /dev/null +++ b/res/gta5view.exe.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/res/gta5view.icns b/res/gta5view.icns new file mode 100644 index 0000000..12f8c6f Binary files /dev/null and b/res/gta5view.icns 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/lang/qt_ru.qm b/res/qt4/qt_ru.qm old mode 100755 new mode 100644 similarity index 100% rename from lang/qt_ru.qm rename to res/qt4/qt_ru.qm diff --git a/res/qt4/qt_uk.qm b/res/qt4/qt_uk.qm new file mode 100644 index 0000000..112ca5c Binary files /dev/null and b/res/qt4/qt_uk.qm differ diff --git a/res/qt4/qt_zh_TW.qm b/res/qt4/qt_zh_TW.qm new file mode 100644 index 0000000..a9a25b2 Binary files /dev/null and b/res/qt4/qt_zh_TW.qm differ diff --git a/res/qt4/tr_qt.qrc b/res/qt4/tr_qt.qrc new file mode 100644 index 0000000..0b79a15 --- /dev/null +++ b/res/qt4/tr_qt.qrc @@ -0,0 +1,10 @@ + + + qt_de.qm + qt_fr.qm + qt_ko.qm + qt_ru.qm + qt_uk.qm + qt_zh_TW.qm + + diff --git a/res/qt5/qtbase_de.qm b/res/qt5/qtbase_de.qm new file mode 100644 index 0000000..4a4c988 Binary files /dev/null and b/res/qt5/qtbase_de.qm differ diff --git a/res/qt5/qtbase_en_GB.qm b/res/qt5/qtbase_en_GB.qm new file mode 100644 index 0000000..8a7e376 Binary files /dev/null and b/res/qt5/qtbase_en_GB.qm differ diff --git a/res/qt5/qtbase_en_GB.ts b/res/qt5/qtbase_en_GB.ts new file mode 100644 index 0000000..0fbfffd --- /dev/null +++ b/res/qt5/qtbase_en_GB.ts @@ -0,0 +1,65 @@ + + + + + QColorDialog + + Hu&e: + + + + &Sat: + + + + &Val: + + + + &Red: + + + + &Green: + + + + Bl&ue: + + + + A&lpha channel: + + + + &HTML: + + + + Cursor at %1, %2 +Press ESC to cancel + Cursour at %1, %2 +Press ESC to cancel + + + Select Color + Select Colour + + + &Basic colors + &Basic colours + + + &Custom colors + &Custom colours + + + &Add to Custom Colors + &Add to Custom Colours + + + &Pick Screen Color + &Pick Screen Colour + + + diff --git a/res/qtbase_fr.qm b/res/qt5/qtbase_fr.qm similarity index 99% rename from res/qtbase_fr.qm rename to res/qt5/qtbase_fr.qm index 8353f0a..009fb5a 100644 Binary files a/res/qtbase_fr.qm and b/res/qt5/qtbase_fr.qm differ diff --git a/res/qt5/qtbase_ko.qm b/res/qt5/qtbase_ko.qm new file mode 100644 index 0000000..20e4661 Binary files /dev/null and b/res/qt5/qtbase_ko.qm differ diff --git a/res/qt5/qtbase_ru.qm b/res/qt5/qtbase_ru.qm new file mode 100644 index 0000000..c1a2286 Binary files /dev/null and b/res/qt5/qtbase_ru.qm differ diff --git a/res/qt5/qtbase_uk.qm b/res/qt5/qtbase_uk.qm new file mode 100644 index 0000000..21a3038 Binary files /dev/null and b/res/qt5/qtbase_uk.qm differ diff --git a/res/qt5/qtbase_zh_TW.qm b/res/qt5/qtbase_zh_TW.qm new file mode 100644 index 0000000..6205298 Binary files /dev/null and b/res/qt5/qtbase_zh_TW.qm differ diff --git a/res/qt5/tr_qt.qrc b/res/qt5/tr_qt.qrc new file mode 100644 index 0000000..7bfe7cc --- /dev/null +++ b/res/qt5/tr_qt.qrc @@ -0,0 +1,11 @@ + + + qtbase_en_GB.qm + qtbase_de.qm + qtbase_fr.qm + qtbase_ko.qm + qtbase_ru.qm + qtbase_uk.qm + qtbase_zh_TW.qm + + diff --git a/res/qt6/qtbase_de.qm b/res/qt6/qtbase_de.qm new file mode 100644 index 0000000..a0d2672 Binary files /dev/null and b/res/qt6/qtbase_de.qm differ diff --git a/res/qt6/qtbase_en_GB.qm b/res/qt6/qtbase_en_GB.qm new file mode 100644 index 0000000..8a7e376 Binary files /dev/null and b/res/qt6/qtbase_en_GB.qm differ diff --git a/res/qt6/qtbase_en_GB.ts b/res/qt6/qtbase_en_GB.ts new file mode 100644 index 0000000..0fbfffd --- /dev/null +++ b/res/qt6/qtbase_en_GB.ts @@ -0,0 +1,65 @@ + + + + + QColorDialog + + Hu&e: + + + + &Sat: + + + + &Val: + + + + &Red: + + + + &Green: + + + + Bl&ue: + + + + A&lpha channel: + + + + &HTML: + + + + Cursor at %1, %2 +Press ESC to cancel + Cursour at %1, %2 +Press ESC to cancel + + + Select Color + Select Colour + + + &Basic colors + &Basic colours + + + &Custom colors + &Custom colours + + + &Add to Custom Colors + &Add to Custom Colours + + + &Pick Screen Color + &Pick Screen Colour + + + diff --git a/res/qt6/qtbase_fr.qm b/res/qt6/qtbase_fr.qm new file mode 100644 index 0000000..009fb5a Binary files /dev/null and b/res/qt6/qtbase_fr.qm differ diff --git a/res/qt6/qtbase_ko.qm b/res/qt6/qtbase_ko.qm new file mode 100644 index 0000000..20e4661 Binary files /dev/null and b/res/qt6/qtbase_ko.qm differ diff --git a/res/qt6/qtbase_ru.qm b/res/qt6/qtbase_ru.qm new file mode 100644 index 0000000..c1a2286 Binary files /dev/null and b/res/qt6/qtbase_ru.qm differ diff --git a/res/qt6/qtbase_uk.qm b/res/qt6/qtbase_uk.qm new file mode 100644 index 0000000..21a3038 Binary files /dev/null and b/res/qt6/qtbase_uk.qm differ diff --git a/res/qt6/qtbase_zh_TW.qm b/res/qt6/qtbase_zh_TW.qm new file mode 100644 index 0000000..f32a72f Binary files /dev/null and b/res/qt6/qtbase_zh_TW.qm differ diff --git a/res/qt6/tr_qt.qrc b/res/qt6/tr_qt.qrc new file mode 100644 index 0000000..7bfe7cc --- /dev/null +++ b/res/qt6/tr_qt.qrc @@ -0,0 +1,11 @@ + + + qtbase_en_GB.qm + qtbase_de.qm + qtbase_fr.qm + qtbase_ko.qm + qtbase_ru.qm + qtbase_uk.qm + qtbase_zh_TW.qm + + diff --git a/res/qtbase_de.qm b/res/qtbase_de.qm deleted file mode 100755 index ed026f9..0000000 Binary files a/res/qtbase_de.qm and /dev/null differ diff --git a/res/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 new file mode 100644 index 0000000..98c204e Binary files /dev/null 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 new file mode 100644 index 0000000..c74ba7f Binary files /dev/null and b/res/template.g5e differ diff --git a/res/template.qrc b/res/template.qrc new file mode 100644 index 0000000..06dcf5a --- /dev/null +++ b/res/template.qrc @@ -0,0 +1,5 @@ + + + template.g5e + + diff --git a/res/tr_g5p.qrc b/res/tr_g5p.qrc new file mode 100644 index 0000000..b51e8fd --- /dev/null +++ b/res/tr_g5p.qrc @@ -0,0 +1,11 @@ + + + gta5sync_en_US.qm + gta5sync_de.qm + gta5sync_fr.qm + gta5sync_ko.qm + gta5sync_ru.qm + gta5sync_uk.qm + gta5sync_zh_TW.qm + + diff --git a/res/watermark_1b.png b/res/watermark_1b.png new file mode 100644 index 0000000..c0a8adc Binary files /dev/null and b/res/watermark_1b.png differ diff --git a/res/watermark_2b.png b/res/watermark_2b.png new file mode 100644 index 0000000..0cf74b9 Binary files /dev/null and b/res/watermark_2b.png differ diff --git a/res/watermark_2r.png b/res/watermark_2r.png new file mode 100644 index 0000000..51aa4b2 Binary files /dev/null and b/res/watermark_2r.png differ diff --git a/res/xmr.str b/res/xmr.str new file mode 100644 index 0000000..131342e --- /dev/null +++ b/res/xmr.str @@ -0,0 +1 @@ +Monero \ No newline at end of file diff --git a/res/xmr.svgz b/res/xmr.svgz new file mode 100644 index 0000000..55c269a Binary files /dev/null and b/res/xmr.svgz differ diff --git a/tmext/TelemetryClassAuthenticator.cpp b/tmext/TelemetryClassAuthenticator.cpp new file mode 100644 index 0000000..fc523f7 --- /dev/null +++ b/tmext/TelemetryClassAuthenticator.cpp @@ -0,0 +1,99 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2018 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "TelemetryClassAuthenticator.h" +#include +#include + +#ifndef GTA5SYNC_TELEMETRY_PUSHURL +#define GTA5SYNC_TELEMETRY_PUSHURL "" +#endif + +#ifndef GTA5SYNC_TELEMETRY_REGURL +#define GTA5SYNC_TELEMETRY_REGURL "" +#endif + +#ifndef GTA5SYNC_TELEMETRY_AUTHID +#define GTA5SYNC_TELEMETRY_AUTHID "" +#endif + +#ifndef GTA5SYNC_TELEMETRY_AUTHPW +#define GTA5SYNC_TELEMETRY_AUTHPW "" +#endif + +const QUrl TelemetryClassAuthenticator::getTrackingPushURL() +{ + if (haveAccessData()) + { + QUrl pushUrl(GTA5SYNC_TELEMETRY_PUSHURL); + QUrlQuery pushQuery(pushUrl); + if (!getTrackingAuthID().isEmpty()) { pushQuery.addQueryItem("tid", getTrackingAuthID()); } + if (!getTrackingAuthPW().isEmpty()) { pushQuery.addQueryItem("tpw", getTrackingAuthPW()); } + pushUrl.setQuery(pushQuery.query(QUrl::FullyEncoded)); + return pushUrl; + } + else + { + QUrl pushUrl(GTA5SYNC_TELEMETRY_PUSHURL); + return pushUrl; + } +} + +const QUrl TelemetryClassAuthenticator::getTrackingRegURL() +{ + if (haveAccessData()) + { + QUrl regUrl(GTA5SYNC_TELEMETRY_REGURL); + QUrlQuery regQuery(regUrl); + if (!getTrackingAuthID().isEmpty()) { regQuery.addQueryItem("tid", getTrackingAuthID()); } + if (!getTrackingAuthPW().isEmpty()) { regQuery.addQueryItem("tpw", getTrackingAuthPW()); } + regUrl.setQuery(regQuery.query(QUrl::FullyEncoded)); + return regUrl; + } + else + { + QUrl regUrl(GTA5SYNC_TELEMETRY_REGURL); + return regUrl; + } +} + +const QString TelemetryClassAuthenticator::getTrackingAuthID() +{ + return QString(GTA5SYNC_TELEMETRY_AUTHID); +} + +const QString TelemetryClassAuthenticator::getTrackingAuthPW() +{ + return QString(GTA5SYNC_TELEMETRY_AUTHPW); +} + +bool TelemetryClassAuthenticator::havePushURL() +{ + return !getTrackingPushURL().isEmpty(); +} + +bool TelemetryClassAuthenticator::haveRegURL() +{ + return !getTrackingRegURL().isEmpty(); +} + +bool TelemetryClassAuthenticator::haveAccessData() +{ + if (getTrackingAuthID().isEmpty() && getTrackingAuthPW().isEmpty()) { return false; } + return true; +} diff --git a/tmext/TelemetryClassAuthenticator.h b/tmext/TelemetryClassAuthenticator.h new file mode 100644 index 0000000..4180029 --- /dev/null +++ b/tmext/TelemetryClassAuthenticator.h @@ -0,0 +1,41 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2018 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef TELEMETRYCLASSAUTHENTICATOR_H +#define TELEMETRYCLASSAUTHENTICATOR_H + +#include +#include +#include +#include + +class TelemetryClassAuthenticator : public QObject +{ + Q_OBJECT +public: + static const QUrl getTrackingPushURL(); + static const QUrl getTrackingRegURL(); + static const QString getTrackingAuthID(); + static const QString getTrackingAuthPW(); + static bool havePushURL(); + static bool haveRegURL(); + static bool haveAccessData(); +}; + + +#endif // TELEMETRYCLASSAUTHENTICATOR_H diff --git a/uimod/JSHighlighter.cpp b/uimod/JSHighlighter.cpp new file mode 100644 index 0000000..42a988c --- /dev/null +++ b/uimod/JSHighlighter.cpp @@ -0,0 +1,99 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2017-2020 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#include "JSHighlighter.h" + +JSHighlighter::JSHighlighter(QTextDocument *parent) : + QSyntaxHighlighter(parent) +{ + HighlightingRule rule; + + QBrush keywordBrush(QColor::fromRgb(66, 137, 244)); + keywordFormat.setForeground(keywordBrush); + keywordFormat.setFontItalic(true); + QStringList keywordPatterns; + keywordPatterns << "\\btrue\\b" << "\\bfalse\\b"; + for (QString pattern : keywordPatterns) + { +#if QT_VERSION >= 0x050000 + rule.pattern = QRegularExpression(pattern); +#else + rule.pattern = QRegExp(pattern); +#endif + rule.format = keywordFormat; + highlightingRules.append(rule); + } + + QBrush doubleBrush(QColor::fromRgb(66, 137, 244)); + doubleFormat.setForeground(doubleBrush); +#if QT_VERSION >= 0x050000 + rule.pattern = QRegularExpression("[+-]?\\d*\\.?\\d+"); +#else + rule.pattern = QRegExp("[+-]?\\d*\\.?\\d+"); +#endif + rule.format = doubleFormat; + highlightingRules.append(rule); + + QBrush quotationBrush(QColor::fromRgb(66, 244, 104)); + quotationFormat.setForeground(quotationBrush); +#if QT_VERSION >= 0x050000 + rule.pattern = QRegularExpression("\"[^\"]*\""); +#else + rule.pattern = QRegExp("\"[^\"]*\""); +#endif + rule.format = quotationFormat; + highlightingRules.append(rule); + + QBrush objectBrush(QColor::fromRgb(255, 80, 80)); + objectFormat.setForeground(objectBrush); +#if QT_VERSION >= 0x050000 + rule.pattern = QRegularExpression("\"[^\"]*\"(?=:)"); +#else + rule.pattern = QRegExp("\"[^\"]*\"(?=:)"); +#endif + rule.format = objectFormat; + highlightingRules.append(rule); +} + +void JSHighlighter::highlightBlock(const QString &text) +{ +#if QT_VERSION >= 0x050000 + for (const HighlightingRule &rule : qAsConst(highlightingRules)) +#else + for (const HighlightingRule &rule : highlightingRules) +#endif + { +#if QT_VERSION >= 0x050000 + QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text); + while (matchIterator.hasNext()) { + QRegularExpressionMatch match = matchIterator.next(); + setFormat(match.capturedStart(), match.capturedLength(), rule.format); + } +#else + QRegExp expression(rule.pattern); + int index = expression.indexIn(text); + while (index >= 0) + { + int length = expression.matchedLength(); + setFormat(index, length, rule.format); + index = expression.indexIn(text, index + length); + } +#endif + } + setCurrentBlockState(0); +} diff --git a/uimod/JSHighlighter.h b/uimod/JSHighlighter.h new file mode 100644 index 0000000..d93bfa9 --- /dev/null +++ b/uimod/JSHighlighter.h @@ -0,0 +1,65 @@ +/***************************************************************************** +* gta5view Grand Theft Auto V Profile Viewer +* Copyright (C) 2017-2020 Syping +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*****************************************************************************/ + +#ifndef JSHIGHLIGHTER_H +#define JSHIGHLIGHTER_H + +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x050000 +#include +#else +#include +#endif + +class QTextDocument; + +class JSHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT + +public: + struct HighlightingRule + { +#if QT_VERSION >= 0x050000 + QRegularExpression pattern; +#else + QRegExp pattern; +#endif + QTextCharFormat format; + }; + QVector highlightingRules; + + QTextCharFormat keywordFormat; + QTextCharFormat doubleFormat; + QTextCharFormat quotationFormat; + QTextCharFormat objectFormat; + + JSHighlighter(QTextDocument *parent = 0); + +protected: + void highlightBlock(const QString &text) override; +}; + +#endif // JSHIGHLIGHTER_H diff --git a/uimod/UiModLabel.cpp b/uimod/UiModLabel.cpp old mode 100755 new mode 100644 index f069fa5..4a2c2d4 --- a/uimod/UiModLabel.cpp +++ b/uimod/UiModLabel.cpp @@ -1,68 +1,75 @@ -/***************************************************************************** -* 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 "UiModLabel.h" -#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::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 8d63f9d..0988a4e --- a/uimod/UiModLabel.h +++ b/uimod/UiModLabel.h @@ -1,51 +1,53 @@ -/***************************************************************************** -* 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 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 resizeEvent(QResizeEvent *ev); - -signals: - void mouseMoved(); - void mousePressed(Qt::MouseButton button); - void mouseReleased(Qt::MouseButton button); - void mouseDoubleClicked(Qt::MouseButton button); - 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 new file mode 100644 index 0000000..990ce7f --- /dev/null +++ b/uimod/UiModWidget.cpp @@ -0,0 +1,81 @@ +/***************************************************************************** +* 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 new file mode 100644 index 0000000..469c000 --- /dev/null +++ b/uimod/UiModWidget.h @@ -0,0 +1,49 @@ +/***************************************************************************** +* 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/PictureCopy.h b/wrapper.h old mode 100755 new mode 100644 similarity index 71% rename from PictureCopy.h rename to wrapper.h index 68b7ef2..9ea0443 --- a/PictureCopy.h +++ b/wrapper.h @@ -1,32 +1,30 @@ -/***************************************************************************** -* 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 PICTURECOPY_H -#define PICTURECOPY_H - -#include -#include - -class PictureCopy -{ -public: - PictureCopy(); - static void copyPicture(QWidget *parent, QString picPath); -}; - -#endif // PICTURECOPY_H +/***************************************************************************** +* 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