Compare commits

..

281 Commits

Author SHA1 Message Date
Rafael 20de3627d5
fix manifest release year
continuous-integration/drone/push Build is passing Details
2023-03-15 09:19:24 +01:00
Syping 13b6d614fc gta5view 1.10.2 release
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
- fix crash bug and upload issue
2023-03-15 07:10:50 +01:00
Syping d7b28c2468 fix Content Mode options and few other changes
continuous-integration/drone/push Build is passing Details
- fix setupRadioButtons (Content Mode options)
- update integrated qtbase_de.qm from Qt 6.4
- bump copyright year and some version numbers (again)
2022-12-25 22:11:51 +01:00
Syping 5e6c4e6ad1 fix Qt 6.4 build and few other changes
continuous-integration/drone/push Build is passing Details
- include QStringEncoder and QStringDecoder for Qt 6.4
- update integrated qtbase_de.qm from Qt 6.3
- fix macOS theme detection on Qt 6
- bump copyright year and some version numbers
- remove ZEC donation
- rework README.md
2022-08-21 21:39:19 +02:00
Syping 3cd6cc903d gta5view 1.10.1 release
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2021-06-17 06:14:35 +02:00
Syping c42cc1dc26 Add Korean strings for Desktop and Metainfo files
continuous-integration/drone/push Build is passing Details
2021-05-28 15:21:07 +02:00
Syping 204f55a11d gta5view 1.10.0 release
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2021-05-27 05:42:05 +02:00
Syping de17747c38 gta5view 1.10.0 release
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2021-05-27 05:23:49 +02:00
Syping aa83221db2 gta5view 1.10.0 release
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2021-05-27 05:03:27 +02:00
Syping b6241a8ff5 gta5view 1.10.0 release
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2021-05-27 04:08:37 +02:00
Syping 4a293b6acd Minor changes and MimeType support
continuous-integration/drone/push Build is passing Details
2021-05-27 02:28:41 +02:00
Syping f4b4450f69 Fix Qt4 builds
continuous-integration/drone/push Build is passing Details
2021-05-23 07:20:20 +02:00
Syping 268e8d3468 Feature: Auto-detect new files in Profile folder
continuous-integration/drone/push Build is passing Details
2021-05-23 07:12:56 +02:00
Syping b47a0119a5 SnapmaticPicture: define default PhotoBuffer
continuous-integration/drone/push Build is passing Details
2021-05-23 05:06:32 +02:00
Syping 1770ec23e0 Improve ImportDialog layouts
continuous-integration/drone/push Build is passing Details
2021-05-22 20:22:02 +02:00
Syping cbe92af194 Force Borderless -> Crop to Aspect Ratio (more clear)
continuous-integration/drone/push Build is passing Details
2021-05-22 19:47:06 +02:00
Syping 35924c4696 MapLocationDialog zoom is now experimental (incomplete)
continuous-integration/drone/push Build is passing Details
2021-05-09 07:42:14 +02:00
Syping 9c937ff270 rework donation UI
continuous-integration/drone/push Build is passing Details
2021-04-25 16:47:27 +02:00
Syping fc08d29fe8 add QR codes for donation addresses
continuous-integration/drone/push Build is passing Details
2021-04-25 13:24:05 +02:00
VenJam1n bdf2c35f35 Update Ukrainian translation
continuous-integration/drone/push Build is passing Details
2021-04-08 20:42:27 +02:00
Syping a952eff26d Fix donate icon
continuous-integration/drone/push Build is passing Details
2021-04-02 20:06:58 +02:00
Syping 955f015de3 Fix Windows resource folder
continuous-integration/drone/push Build is passing Details
2021-04-02 20:02:35 +02:00
Syping 7013ade1b5 fix IconLoader typo
continuous-integration/drone/push Build is passing Details
2021-04-02 19:54:07 +02:00
Syping 5244f72ec2 QCONF build separates images
continuous-integration/drone/push Build is passing Details
2021-04-02 19:51:05 +02:00
Syping 522cb469b7 ProfileLoader check if regular file
continuous-integration/drone/push Build is passing Details
2021-04-02 03:05:54 +02:00
Syping 2f135cd373 optimise ProfileLoader
continuous-integration/drone/push Build is passing Details
2021-04-02 02:54:28 +02:00
Syping 3c673beed1 Update Korean translation
continuous-integration/drone/push Build is passing Details
2021-03-27 11:08:46 +01:00
Syping e572c9c60e code cleanup and improvements
continuous-integration/drone/push Build is passing Details
2021-03-27 08:23:05 +01:00
VADemon 6fdab04c05 Update Russian translation
continuous-integration/drone/push Build is passing Details
2021-03-26 19:05:48 +01:00
Syping 1c02c37e9c improve donate icon
continuous-integration/drone/push Build is passing Details
2021-03-26 18:44:01 +01:00
Syping 8a24060fb2 add donate icon
continuous-integration/drone/push Build is passing Details
2021-03-26 18:08:30 +01:00
Ray 694ebc030a Updated Taiwanese translation
continuous-integration/drone/push Build is passing Details
2021-03-26 15:34:37 +01:00
Syping 817f356b33 add donation system
continuous-integration/drone/push Build is passing Details
2021-03-26 14:58:54 +01:00
Syping 04603af8ff Fix Qt4 qAsConst
continuous-integration/drone/push Build is passing Details
2021-03-26 10:59:34 +01:00
Syping 44ab5caedf small RagePhoto cleanup
continuous-integration/drone/push Build is passing Details
2021-03-26 09:51:39 +01:00
Syping b747f7cda5 add RAGEPHOTO_BENCHMARK for parser benchmarks
continuous-integration/drone/push Build is passing Details
2021-03-26 09:40:30 +01:00
Syping 23e9c48248 macOS CI script update, ProfileLoader debug code removal
continuous-integration/drone/push Build is passing Details
2021-03-22 07:12:37 +01:00
Syping a4bc9a3b75 Fix RagePhoto runtime crashes, const improvements
continuous-integration/drone/push Build is passing Details
2021-03-22 05:40:30 +01:00
Syping 4e004b1d68 unfinished zoom support
continuous-integration/drone/push Build is passing Details
2021-02-26 19:59:05 +01:00
Syping b0f95d1cae Fix default font loading
continuous-integration/drone/push Build is passing Details
2021-02-17 23:31:26 +01:00
Syping 962b1c82c3 Build system update, minor fix
continuous-integration/drone/push Build is passing Details
2021-02-17 23:16:00 +01:00
Syping 0686b9a65d Add Cayo Perico Map, optimisations
continuous-integration/drone/push Build is passing Details
2021-02-15 23:12:44 +01:00
Syping 3ed26d4f9d CMake: Fix gta5view.icns location
continuous-integration/drone/push Build is passing Details
2021-02-11 23:34:42 +01:00
Syping 263e714022 Fix Qt6 Windows build, improve build success consistency
continuous-integration/drone/push Build is passing Details
2021-02-11 23:09:49 +01:00
Syping dd9c4a7f16 Improve DWM code, remove WinExtras dependency
continuous-integration/drone/push Build is passing Details
2021-02-11 20:20:52 +01:00
Syping c1b0053ac8 Force Borderless mode added
continuous-integration/drone/push Build is passing Details
2021-02-03 20:22:00 +01:00
Syping 5fd4b48538 Improved PictureDialog resizing
continuous-integration/drone/push Build is passing Details
2021-01-30 12:02:55 +01:00
Syping 929d341826 code cleanup, remove first start prompt
continuous-integration/drone/push Build is passing Details
2021-01-29 19:46:01 +01:00
Syping fd5006e70f CMake: Full macOS Support, macOS CI uses CMake
continuous-integration/drone/push Build is passing Details
2021-01-28 18:39:42 +01:00
Syping 2f7e4a154d Qt6 telemetry build fix, PictureDialog crash fix
continuous-integration/drone/push Build is passing Details
2021-01-27 14:35:42 +01:00
Syping d4a6db068b CI: Add Debian backports before updating
continuous-integration/drone/push Build is passing Details
2021-01-24 02:35:53 +01:00
Syping 01ad3fd04e Debian Buster target, CI script update
continuous-integration/drone/push Build is passing Details
2021-01-22 06:35:35 +01:00
Syping 03f696877e Fix -Wstring-compare warning
continuous-integration/drone/push Build is passing Details
2021-01-21 23:11:50 +01:00
Syping bcbe69491b CMake: Support Qt6 translation build 2021-01-21 22:44:10 +01:00
Syping 056ef79aff Fix wrong placed const 2021-01-21 21:54:15 +01:00
Syping 013bfeb8ed CMake and build type recognition improvements 2021-01-21 21:50:18 +01:00
Syping d9eb8d5f95 Year 2021, 1.10 initial dev branch 2021-01-21 18:48:19 +01:00
Syping dd7667b9f7 Build fix Qt6 ifdef
continuous-integration/drone/push Build is passing Details
2021-01-10 01:33:18 +01:00
Syping 7adf8f2c90 1.9.1: Cayo Perico DLC update, Multi-select bugfix
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-12-16 13:02:11 +01:00
Syping 5dbc06f393 gta5view 1.9.0 release
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-11-30 13:19:42 +01:00
Syping 9adc8d9650 Aspect Ratio fixes, HiDPI fullscreen fix
continuous-integration/drone/push Build is passing Details
2020-11-25 00:36:12 +01:00
VenJam1n 0fcd4615cd Fix Ukrainian translation template
continuous-integration/drone/push Build is passing Details
2020-11-23 16:27:20 +01:00
VenJam1n 47fdacc8bf Ukrainian translation updated
continuous-integration/drone/push Build is passing Details
2020-11-23 04:55:54 +01:00
Syping e448bd4f63 content mode now in options
continuous-integration/drone/push Build is passing Details
2020-11-23 03:47:17 +01:00
Syping 099400b439 added new content modes
continuous-integration/drone/push Build is passing Details
2020-11-23 03:13:07 +01:00
Syping 563b09e060 CMake dependency correction, Russian strings for App
continuous-integration/drone/push Build is passing Details
2020-11-21 14:25:22 +01:00
Syping f432ae068e Improve watermark placing
continuous-integration/drone/push Build is passing Details
2020-11-17 16:38:30 +01:00
VADemon 48c4aa4a97 Update Russian translation
continuous-integration/drone/push Build is passing Details
2020-11-17 11:02:45 +01:00
Syping e82cec5fd5 Add Font Dialog for Font changing
continuous-integration/drone/push Build is passing Details
2020-11-17 10:46:37 +01:00
Syping 070b82d530 Qt6: Fix QVariant::Type deprecated warning
continuous-integration/drone/push Build is passing Details
2020-11-17 10:20:50 +01:00
Syping 25ec67ec8b Fix resources and Qt4 build
continuous-integration/drone/push Build is passing Details
2020-11-17 06:58:29 +01:00
Ray 4a699f2f56 update taiwanese translation
continuous-integration/drone/push Build is passing Details
2020-11-17 06:04:58 +01:00
Syping 8c6e609ecc add custom font feature
continuous-integration/drone/push Build is passing Details
2020-11-17 04:43:04 +01:00
Syping 40e1b288c3 updated korean translation
continuous-integration/drone/push Build is passing Details
2020-11-17 03:46:07 +01:00
Ray b50170645c update taiwanese translation
continuous-integration/drone/push Build is passing Details
2020-11-17 03:31:12 +01:00
VADemon 1b8aea96d7 update russian translation 2020-11-17 03:29:33 +01:00
Syping 5a34dd7b05 [skip ci] fix ImportDialog string, update translations 2020-11-17 03:25:30 +01:00
Syping 2b2cfc210f add unlimited buffer and import as-is
continuous-integration/drone/push Build is passing Details
2020-11-16 15:29:31 +01:00
Syping f6f75c1f89 [skip ci] add FileTools to desktop file 2020-11-16 05:37:28 +01:00
Syping 94a263f501 SnapmaticPicture: fix eXtend bug
continuous-integration/drone/push Build is passing Details
2020-11-15 13:55:06 +01:00
Syping 1b6cb511be SnapmaticPicture: add eXtendMode
continuous-integration/drone/push Build is passing Details
2020-11-15 13:15:16 +01:00
Syping c0a3e08527 RagePhoto: add setPhotoBuffer
continuous-integration/drone/push Build is passing Details
2020-11-15 12:29:33 +01:00
Syping 2cf9119c54 RagePhoto: Improve casting
continuous-integration/drone/push Build is passing Details
2020-11-12 00:24:21 +01:00
Syping 05ccdc6689 RagePhoto: Fix Qt4 build
continuous-integration/drone/push Build is passing Details
2020-11-11 19:43:54 +01:00
Syping 0c6c6ca9fb RagePhoto: Qt < 5.10 build fix
continuous-integration/drone/push Build is passing Details
2020-11-11 18:14:50 +01:00
Syping 6befe33596 RagePhoto: Qt6 support added
continuous-integration/drone/push Build is passing Details
2020-11-11 17:58:15 +01:00
Syping e34ee22ed9 RagePhoto: Add saving and fix few issues
continuous-integration/drone/push Build is passing Details
2020-11-11 07:04:39 +01:00
Syping 2c725c796e RagePhoto: added save for GTA
continuous-integration/drone/push Build is passing Details
2020-11-09 03:43:46 +01:00
Syping 3cf6aedc85 RagePhoto: add G5E1P parser, fix G5E2P
continuous-integration/drone/push Build is passing Details
2020-11-09 02:13:06 +01:00
Syping 2bb3f7f62d SnapmaticPicture: wrap getContentMaxLength
continuous-integration/drone/push Build is passing Details
2020-11-09 01:42:11 +01:00
Syping 18083a3b0f SnapmaticPicture: fix IO memleak
continuous-integration/drone/push Build is failing Details
2020-11-09 01:23:01 +01:00
Syping 186df05cea WIP: SnapmaticPicture using RagePhoto now
continuous-integration/drone/push Build is failing Details
2020-11-09 01:08:51 +01:00
Syping b3e1520a8f RagePhoto: add int to char
continuous-integration/drone/push Build is passing Details
2020-11-08 20:53:43 +01:00
Syping b70396f77e RagePhoto: G5E2P and G5E3P parser added
continuous-integration/drone/push Build is passing Details
2020-11-08 18:05:44 +01:00
Syping a798be0b53 RagePhoto: Load parser added
continuous-integration/drone/push Build is passing Details
2020-11-08 03:50:06 +01:00
Syping 4d9cd2d9ff Qt6: Avoid timestamp string conversion
continuous-integration/drone/push Build is passing Details
2020-11-05 18:32:20 +01:00
Syping 699f5b8f55 NSIS: Bump version to 1.9.0
continuous-integration/drone/push Build is passing Details
2020-11-05 16:29:00 +01:00
Syping d55ea46b47 NSIS: Fix Qt5 translation path
continuous-integration/drone/push Build is passing Details
2020-11-05 16:28:11 +01:00
Syping 6af5f94d70 CMake: Support Qt6 build
continuous-integration/drone/push Build is failing Details
2020-11-05 16:21:33 +01:00
Syping fcbe264d63 Small Flatpak fixes, Qt6 beta build fix
continuous-integration/drone/push Build is passing Details
2020-10-21 19:14:20 +02:00
Syping f8049c8178 add metainfo
continuous-integration/drone/push Build is passing Details
2020-10-20 09:31:22 +02:00
Syping d0899623ad README: add Arch/Manjaro build instruction
continuous-integration/drone/push Build is passing Details
2020-10-13 22:35:17 +02:00
Syping 547922329a gta5view 1.8.0 release
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2020-10-11 17:53:45 +02:00
Syping 1fbe4d9e67 gta5view 1.8.0 release
continuous-integration/drone/push Build is passing Details
2020-10-11 17:46:45 +02:00
Syping 678425a7c5 Ported now to Qt 6
continuous-integration/drone/push Build is passing Details
2020-09-28 05:33:04 +02:00
Syping f9129f8364 CI: Strip binaries to smaller size
continuous-integration/drone/push Build is passing Details
2020-09-26 21:18:55 +02:00
Syping 4e5592566a CMake build improved and does now Windows build
continuous-integration/drone/push Build is failing Details
2020-09-26 18:52:05 +02:00
Syping 39436a6dbf CMake improvements, deprecate QMake for Qt5, GTA5SYNC_WIN -> Q_OS_WIN,
continuous-integration/drone/push Build is passing Details
APV removed
2020-09-26 16:12:55 +02:00
Syping 15dfc2c84b added motd message system
continuous-integration/drone/push Build is passing Details
2020-09-13 02:10:15 +02:00
Syping 57c32eea58 QMake: No need for 'make depend' anymore
continuous-integration/drone/push Build is passing Details
2020-09-03 00:03:04 +02:00
Syping 90744ced60 correct GTA5VIEW_COMMIT in debian_build.sh
continuous-integration/drone/push Build is passing Details
2020-08-30 18:56:26 +02:00
Syping 90846e6fb6 install CMake to fix CI build
continuous-integration/drone/push Build is passing Details
2020-08-30 18:52:41 +02:00
Syping 2735325f4c use CMake for Qt5 Linux build
continuous-integration/drone/push Build is passing Details
2020-08-30 18:48:33 +02:00
Syping 8b8e22c958 [skip ci] make flatpak-builder using local directory 2020-08-30 17:57:04 +02:00
Syping 91ccdca77a [skip ci] update translation templates 2020-08-30 17:47:09 +02:00
Syping ae0c7c2905 CMake: Build variables and telemetry support added
continuous-integration/drone/push Build is passing Details
2020-08-30 17:39:10 +02:00
Syping 4a026640e3 Fix icon naming and desktop entry file
continuous-integration/drone/push Build is passing Details
2020-08-28 22:11:52 +02:00
Syping 8be9fd6d5e CMake: Installing icons to hicolor directory
continuous-integration/drone/push Build is passing Details
2020-08-28 17:11:04 +02:00
Syping 8295e38ab4 fix CI builds
continuous-integration/drone/push Build is passing Details
2020-08-28 13:37:59 +02:00
Syping 24cd9a16f9 updated gta5view icon naming
continuous-integration/drone/push Build is passing Details
2020-08-28 13:33:35 +02:00
Syping aa142c9ec9 QMake: fix desktop entry installation, minor desktop entry fixes
continuous-integration/drone/push Build is passing Details
2020-08-28 13:06:27 +02:00
Syping 4c42f06b39 added Flatpak manifest and CMake support for Flatpak
continuous-integration/drone/push Build is passing Details
2020-08-28 12:54:39 +02:00
Syping 94fb78c59a CMake: add QCONF_BUILD, missing Qt translation in binary fixed
continuous-integration/drone/push Build is passing Details
2020-08-28 00:26:30 +02:00
Syping 3f910c16b0 update Qt translations 2020-08-28 00:24:36 +02:00
Syping bbf7f08980 cmake improvements, default *nix user build to CMake
continuous-integration/drone/push Build is passing Details
2020-08-27 18:14:30 +02:00
Syping ec93fa8ce0 add basic CMakeLists.txt (*nix only)
continuous-integration/drone/push Build is passing Details
2020-08-26 20:51:01 +02:00
Syping d37fbdf055 [skip ci] update translation templates 2020-08-25 20:50:41 +02:00
Syping 1c00871e80 [skip ci] add correct version number to gta5view.desktop 2020-08-25 20:11:45 +02:00
Syping a74e7847b2 change the way how MapLocationDialog sets marker on Windows
continuous-integration/drone/push Build is passing Details
2020-08-25 19:17:30 +02:00
Syping e41d4946f8 autoupdate app.rc on CI builds
continuous-integration/drone/push Build is passing Details
2020-08-25 19:09:56 +02:00
Syping 28fedfdd0b fix some deprecations, more compatibility
continuous-integration/drone/push Build is passing Details
2020-08-25 17:29:41 +02:00
Syping a882738735 [skip ci] fix indent spelling 2020-08-25 15:47:08 +02:00
Syping 23245a3324 [skip ci] make font fixed for Qt below than 5.2 2020-08-25 15:32:32 +02:00
Syping af86e70f4b [skip ci] prettify qjson4 output aswell 2020-08-25 15:18:23 +02:00
Syping 2bf6bb5630 options players tab only show now when filled
continuous-integration/drone/push Build is passing Details
2020-08-25 14:07:42 +02:00
Syping 6b2b18ed16 improved more manual DPI scaling
continuous-integration/drone/push Build is passing Details
2020-08-25 12:53:21 +02:00
Syping c936fed2f8 fix broken Map drawing on some platforms 2020-08-25 12:52:45 +02:00
Syping cb8793f3b3 make performance improvements for Windows aswell
continuous-integration/drone/push Build is passing Details
2020-08-24 17:48:09 +02:00
Syping 1038f000f7 more performance improvements
continuous-integration/drone/push Build is passing Details
2020-08-24 17:44:32 +02:00
Syping 3c124f9f27 improve hover detection performance
continuous-integration/drone/push Build is passing Details
2020-08-24 17:23:10 +02:00
Syping 27075778c6 enable NavigationBar for non-Windows systems
continuous-integration/drone/push Build is passing Details
2020-08-24 16:35:52 +02:00
Syping 2b0455e2ff fix Qt 5.15 build
continuous-integration/drone/push Build is passing Details
2020-08-03 17:23:39 +02:00
Syping ab248413ea drone cleanup previous build
continuous-integration/drone/push Build is passing Details
2020-06-07 21:50:26 +02:00
Syping 279cd5ac3b fix .drone.yml steps
continuous-integration/drone/push Build is failing Details
2020-06-07 21:42:49 +02:00
Syping 1f62f6fe48 fix drone.sh permission
continuous-integration/drone/push Build is passing Details
2020-06-07 20:38:43 +02:00
Syping c8a955e8de remove unused translation files
continuous-integration/drone/push Build is failing Details
2020-06-07 20:37:31 +02:00
Syping 5250bad4b6 add drone build 2020-06-07 20:30:53 +02:00
Syping 9121987142 fix Qt plugins in NSIS script 2020-04-24 05:45:18 +02:00
Syping cabd44f066 fix dumb NSIS mistake 2020-04-24 05:42:57 +02:00
Syping 01fb80fdd1 fix NSIS script 2020-04-24 05:41:20 +02:00
Syping f63645a1fb 1.8 bump, fix res regression from 1.7, other fixes. 2020-04-24 05:36:11 +02:00
Syping 8732b9d64d 1.7.1 release, fix installation
continuous-integration/drone/push Build is failing Details
2019-08-04 15:56:16 +02:00
Syping bb4a9b484c 1.7.1 release, korean translation added 2019-08-04 15:49:32 +02:00
Syping c79c8dbe6e 1.7.0 release 2019-07-24 20:23:40 +02:00
Syping 4e31d159fa 1.7.0 release 2019-07-24 20:16:48 +02:00
Syping 0633d14d6b install Lua, change buildtype to Release 2019-07-24 20:12:26 +02:00
Syping c6b39546ba remove function to click numbers (don't work anymore) 2019-07-24 20:05:34 +02:00
Syping eac9caa2c6 fix compatibility issue 2019-07-24 19:47:08 +02:00
Syping a80b5843a7 fix build script 2019-07-24 19:10:14 +02:00
Syping 07797f6e60 remove upx compression step 2019-07-24 18:57:00 +02:00
Syping 55186e8b88 improve MapLocationDialog 2019-07-24 18:35:32 +02:00
Syping eee9100d8b remove OpenSSL changes 2019-04-22 20:39:18 +02:00
Syping b73719ed4e add WindowsVista style 2019-04-22 05:25:28 +02:00
Syping a143b43d15 fix uninstallation 2019-04-22 05:10:39 +02:00
Syping 032475ddfd remove audio 2019-04-22 05:07:04 +02:00
Syping bdde72573b gta5view.nsi now UTF-8 2019-04-22 05:05:12 +02:00
Syping 827676768a update Qt to 5.12.3 2019-04-22 04:59:26 +02:00
Syping 0c02c3ce98 use icons for PlayerListDialog buttons 2019-01-16 02:11:42 +01:00
Syping 6063803d5e improve PlayerListDialog 2019-01-13 14:56:35 +01:00
Syping 1f3c036b47 navigation bar fixed 2019-01-13 14:40:16 +01:00
Syping ea0526ae9d massive DPI improvements 2019-01-13 14:32:12 +01:00
Syping aeae6c9311 fix DPI scaling in PictureDialog 2018-12-25 21:05:09 +01:00
Syping c39c3a3e9f always scale savegame graphic 2018-12-02 18:10:34 +01:00
Syping 9989d9d869 replace savegame.png with scaleable savegame.svgz 2018-12-01 17:47:28 +01:00
Syping fa86e8f8a7 fix Windows installer build 2018-11-22 21:54:16 +01:00
Syping f9880fff70 update toolchain 2018-11-22 21:50:53 +01:00
Syping 7ba5322643 fix CI script SVG module installation 2018-11-18 20:39:32 +01:00
Syping 1f409b0f25 Scaleable navigation bar icons 2018-11-18 18:08:18 +01:00
Syping dea33f8ab0 1.7 development 2018-09-09 19:25:28 +02:00
Syping 6b54b2e6ae 1.6.0 release 2018-08-27 19:40:44 +02:00
VADemon 38f2877ea3 Russian translation updated 2018-08-27 17:19:18 +02:00
Syping c67a0a0fc9 add new crews to list 2018-08-25 03:11:37 +02:00
Syping 7a15c3e56b fix ProfileInterface hover bug 2018-08-17 16:35:14 +02:00
Syping feabaac48e fix warning & readme update 2018-08-05 15:29:30 +02:00
Jean 6ab75420ec
Update french translation 2018-08-05 15:05:38 +02:00
Syping cda974d5df update resources 2018-08-01 20:32:24 +02:00
VenJam1n 36677ed07a Ukrainian translation updated 2018-08-01 20:31:51 +02:00
Syping 17014925c5 fix retranslateUi 2018-07-30 02:54:51 +02:00
Syping f151035574 updated chinese translation 2018-07-30 02:17:47 +02:00
Syping 53ef2bdb54 1.6.0 RC 2018-07-28 05:18:01 +02:00
Syping 2606cd8965 fix some submit code 2018-07-28 05:06:46 +02:00
Syping 973fb58a5d don't spam telemetry 2018-07-28 05:00:57 +02:00
Syping 39f20aca9d added ability to read GTA V game language 2018-07-28 04:55:55 +02:00
Syping c29cc44717 added the ability to import duplicates 2018-07-27 04:26:10 +02:00
Syping 9ced2253fc update for travis ci 2018-07-27 03:09:40 +02:00
Syping 778abdd36a update for travis ci 2018-07-27 03:00:18 +02:00
Syping 30a6b55c9e ci update 2018-07-27 02:56:22 +02:00
Syping b02f06ae97 ImportDialog improved 2018-07-25 20:52:30 +02:00
Syping 193bb60caa image overwriter improved 2018-07-21 18:33:08 +02:00
Syping 414867f13e imge cropping added 2018-07-20 15:58:51 +02:00
Syping 7b68bb10b5 add ability to change picture while import process 2018-07-17 16:41:38 +02:00
Syping 2487a188d5 [ci skip] translation updated 2018-07-13 12:08:57 +02:00
Syping c34d3331fb remove unneccessary Capacity view, add Commit tag on version 2018-07-13 12:07:13 +02:00
Syping afeab6120d added GPUs to telemetry data 2018-07-13 09:06:53 +02:00
Syping 4c6962ab23 increase Snapmatic Max Capacity to 512 KB, support for pre May 2015
Snapmatic added
2018-07-12 10:52:33 +02:00
Syping 08ecd5ca61 delete crowdin because i can't afford it 2018-07-10 04:40:57 +02:00
Syping 5f35428cd0 add watermark 2018-07-09 20:39:27 +02:00
Syping 714be43280 fix crowdin file name 2018-07-06 03:12:31 +02:00
Syping 1165c3b536 Update Crowdin configuration file 2018-07-06 01:00:08 +00:00
Syping 06eb2e4277 update translations for crowdin 2018-07-06 02:59:11 +02:00
Syping 00db9ecab2 Update Crowdin configuration file 2018-07-06 00:48:32 +00:00
Syping dc101a66da fix remote import 2018-06-29 09:57:06 +02:00
Syping 541a7d18bd improve CPU string + GitLab CI 2018-06-29 09:36:25 +02:00
Syping f932a8d5ee GitLab artifacts updated 2018-06-29 09:27:54 +02:00
Syping 81ea0490cf add ability to drop images 2018-06-29 08:52:43 +02:00
Syping a8db3985a2 importUrls added 2018-06-29 08:20:01 +02:00
Syping 0127bc61e6 improve clipboard processing 2018-06-29 08:02:28 +02:00
Syping 25c64b7f5d improving player parser 2018-06-29 02:50:00 +02:00
Syping c3d684436b GitLab builds Windows Installer and Portable only now 2018-06-28 03:31:57 +02:00
Syping 4135b1f588 minor changes 2018-06-24 02:25:34 +02:00
Syping 7c08e1486e add Debian build to GitLab CI 2018-06-22 20:00:18 +02:00
Syping d61cbd4743 add Telemetry to GitLab build 2018-06-22 19:00:36 +02:00
Syping efa88cc46e add artifacts in .gitlab-ci.yml 2018-06-22 18:23:38 +02:00
Syping f1cfbbf573 change GitLab Lua to 5.2 2018-06-22 18:11:16 +02:00
Syping ca8003c2e8 fix .gitlab-ci.yml 2018-06-22 18:08:09 +02:00
Syping 69a7ec8dbe add .gitlab-ci.yml 2018-06-22 18:03:22 +02:00
Syping 0321d79136 rename some CI scripts and add GitLab main script 2018-06-22 17:25:27 +02:00
Syping e2b7062e26 update CI scripts 2018-06-22 17:11:18 +02:00
Syping 99f3c22ab0 update telemetry blob 2018-06-22 14:59:39 +02:00
Syping 9b5cb46c35 single selection hotkeys improved 2018-06-17 22:02:41 +02:00
Syping c909e20178 improve configure script 2018-06-17 10:52:50 +02:00
Syping 0e337b4dec fix Qt4 compilation 2018-06-17 09:51:46 +02:00
Syping 3b305fb809 add clipboard import 2018-06-17 09:27:10 +02:00
Syping 06a8657423 fixed mass import 2018-06-16 05:28:56 +02:00
Syping caffd9f246 update readme 2018-06-16 04:58:51 +02:00
Syping caaa6a1d85 making config.h C compatible 2018-06-15 05:04:20 +02:00
Syping 196cb8bc55 stop precompile config.h because it's not C compatible 2018-06-15 04:52:29 +02:00
Syping 2d62bbb97e random generator now based on pcg-random 2018-06-15 04:43:13 +02:00
Syping c3e030e827 G5E 2.0 implemented 2018-06-14 12:28:12 +02:00
Syping 291236ff2c better and more efficient uid generation 2018-06-14 11:36:01 +02:00
Syping c7ec038e26 don't do make depend automatically 2018-06-12 06:37:05 +02:00
Syping cde4b380f8 update configure to accept arguments 2018-06-12 06:30:08 +02:00
Syping bcd7e3cd15 update readme 2018-06-10 02:19:11 +02:00
Syping 1f1025787e fix QT_SELECT 2018-06-10 02:11:49 +02:00
Syping fd5ce958bd remove .gitlab folder 2018-06-10 02:00:20 +02:00
Syping 681e76737e fix indentation 2018-06-10 01:56:55 +02:00
Syping 24ca667537 add basic configure script 2018-06-10 01:53:08 +02:00
Syping e6c8a48e12 use RCC path 2018-06-09 21:53:28 +02:00
Syping 867281f80a deliver precompiled Resource again 2018-06-09 21:25:05 +02:00
Syping a33bc8145b make depend for resource generation 2018-06-09 21:16:33 +02:00
Syping e3a8edae2d compress global files 2018-06-09 20:38:12 +02:00
Syping 609efb7fba update README, Qt translation 2018-06-09 19:18:51 +02:00
Syping 29f883555d fixed UI issues, Personal Usage data check box added 2018-06-07 17:07:30 +02:00
Syping 8fc3dc6c7c Update to Qt 5.9.6 build 2018-06-06 22:13:59 +02:00
Syping 72377e49eb delete audio folder now at uninstall 2018-06-05 17:55:38 +02:00
Syping 5840d8e8e9 fix copy paste mistake with removing cd 2018-06-03 16:20:55 +02:00
Syping d0e157bf1c update Windows Qt to 5.9.5 2018-06-03 16:10:20 +02:00
Syping 9b41d80d9a change SPV screenshot format to .png 2018-06-02 07:17:29 +02:00
Syping 06ada3d770 updated README pictures 2018-06-02 07:15:07 +02:00
Syping cd20e0b512 Telemetry system updated 2018-05-31 06:12:47 +02:00
Syping bd97ee86ba update community translation files 2018-05-31 04:49:17 +02:00
Syping 7b98e75f8d clean up resources 2018-05-31 04:45:19 +02:00
Syping 99ffbf4178 update en_US translation 2018-05-31 04:41:34 +02:00
Syping 108f1725f6 remove unused strings 2018-05-31 04:40:03 +02:00
Syping 5c7cb24c36 update travis script 2018-05-30 11:00:27 +02:00
Syping 6e335638a3 fix dropbox 2018-05-30 10:42:27 +02:00
Syping 5a4b2f1d67 add dropbox_uploader to docker 2018-05-30 10:27:40 +02:00
Syping d91b9f5f43 install curl for Dropbox build 2018-05-30 10:05:04 +02:00
Syping 3dfc2e001b fix dropbox build 2018-05-30 09:51:27 +02:00
Syping 1b54c8c456 add dropbox_uploader 2018-05-30 09:39:10 +02:00
Syping 979747b8f5 update scripts to dev build 2018-05-30 09:31:47 +02:00
Syping e74b19fed0 update travis scripts with PACKAGE_CODE var 2018-05-30 09:09:15 +02:00
Syping d94a3712be add git head if not tagged 2018-05-30 08:53:27 +02:00
Syping e765511614 fix release labels 2018-05-30 08:29:46 +02:00
Syping 61864c65eb rename installer after packaging 2018-05-30 08:27:21 +02:00
Syping bf81aea0c9 fix OS build, add deployment label 2018-05-30 08:17:12 +02:00
Syping 48650633a5 fix OS X build 2018-05-30 07:57:51 +02:00
Syping 2b3a22b893 fix OS X build 2018-05-30 07:49:33 +02:00
Syping 9b373669b2 add future watermark to resources 2018-05-27 11:13:13 +02:00
Syping 55a01e7fe4 readme updated 2018-05-24 23:33:01 +02:00
Syping fdf07dd681 SnapmaticPicture now stay alone, gta5sync references reduced 2018-05-24 22:32:00 +02:00
249 changed files with 21195 additions and 24467 deletions

33
.ci/app.rc Normal file
View File

@ -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 <windows.h>
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

72
.ci/ci.sh Executable file
View File

@ -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}

38
.ci/debian_build.sh Executable file
View File

@ -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 \<dpkg@syping.de\>" --requires=libqt5core5a,libqt5gui5,libqt5network5,libqt5svg5,libqt5widgets5,qttranslations5-l10n --conflicts=gta5view-qt4,gta5view-qt5 --replaces=gta5view-qt4,gta5view-qt5 --pakdir=${PROJECT_DIR}/assets

8
.ci/debian_ci.sh Executable file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Install packages
.ci/debian_install.sh && \
# Build gta5view
.ci/debian_build.sh && \
cd ${PROJECT_DIR}

15
.ci/debian_docker.sh Executable file
View File

@ -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"

13
.ci/debian_install.sh Executable file
View File

@ -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

1
.ci/donate.txt Normal file
View File

@ -0,0 +1 @@
btc:187NSQSPzdMpQDGhxZAuw4AhZ7LgoAPV7D,eth:0x19d71DfCa86104d37a13D3c5d419936421CDC569,ltc:LKr6yvBoMMGmcxViS8Kc1A2sDjVSWTXn4m,xmr:43TB3ZMP5nk1pu5EQXRGPzdTKvmFEBGgccX3tNhRknLLiUYQ7z7dNedVHEA6WrWdByZv1isvFmjSGhCF7ddx3eRxFdm5Fzz

View File

@ -1,11 +1,12 @@
######################################################################
!define APP_NAME "gta5view"
!define APP_EXT ".g5e"
!define COMP_NAME "Syping"
!define WEB_SITE "https://gta5view.syping.de/"
!define VERSION "1.5.5.0"
!define COPYRIGHT "Copyright © 2016-2018 Syping"
!define DESCRIPTION "Grand Theft Auto V Savegame and Snapmatic Viewer/Editor"
!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"
@ -33,6 +34,7 @@ Caption "${APP_NAME}"
OutFile "${INSTALLER_NAME}"
#BrandingText "${APP_NAME}"
XPStyle on
Unicode true
InstallDirRegKey "${REG_ROOT}" "${REG_APP_PATH}" ""
InstallDir "$PROGRAMFILES64\Syping\gta5view"
@ -78,6 +80,7 @@ InstallDir "$PROGRAMFILES64\Syping\gta5view"
!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"
@ -89,7 +92,7 @@ InstallDir "$PROGRAMFILES64\Syping\gta5view"
Function .onInit
!insertmacro MUI_LANGDLL_DISPLAY
!ifdef WIN32
MessageBox MB_OK|MB_ICONSTOP "Can't install the 64bit version on a 32bit system, please download the 32bit version!"
MessageBox MB_OK|MB_ICONSTOP "Windows 32-Bit is not supported anymore!"
Quit
!endif
SetRegView 64
@ -101,45 +104,80 @@ Section -MainProgram
${INSTALL_TYPE}
SetOverwrite ifnewer
SetOutPath "$INSTDIR"
File "../build/release/gta5view.exe"
File "/usr/lib/gcc/x86_64-w64-mingw32/6.3-win32/libgcc_s_seh-1.dll"
File "/usr/lib/gcc/x86_64-w64-mingw32/6.3-win32/libstdc++-6.dll"
File "/opt/windev/libressl-latest_qt64d/bin/libcrypto-43.dll"
File "/opt/windev/libressl-latest_qt64d/bin/libssl-45.dll"
File "/opt/windev/libjpeg-turbo-latest_qt64d/bin/libjpeg-62.dll"
File "/opt/windev/qt64d-latest/bin/Qt5Core.dll"
File "/opt/windev/qt64d-latest/bin/Qt5Gui.dll"
File "/opt/windev/qt64d-latest/bin/Qt5Network.dll"
File "/opt/windev/qt64d-latest/bin/Qt5Svg.dll"
File "/opt/windev/qt64d-latest/bin/Qt5Widgets.dll"
File "/opt/windev/qt64d-latest/bin/Qt5WinExtras.dll"
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 "../res/gta5sync_en_US.qm"
File "../res/gta5sync_de.qm"
File "../res/gta5sync_fr.qm"
File "../res/gta5sync_ru.qm"
File "../res/gta5sync_uk.qm"
File "../res/gta5sync_zh_TW.qm"
File "../res/qtbase_en_GB.qm"
File "../res/qtbase_de.qm"
File "../res/qtbase_fr.qm"
File "../res/qtbase_ru.qm"
File "../res/qtbase_uk.qm"
File "../res/qtbase_zh_TW.qm"
SetOutPath "$INSTDIR\audio"
File "/opt/windev/qt64d-latest/plugins/audio/qtaudio_windows.dll"
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 "/opt/windev/qt64d-latest/plugins/imageformats/qgif.dll"
File "/opt/windev/qt64d-latest/plugins/imageformats/qicns.dll"
File "/opt/windev/qt64d-latest/plugins/imageformats/qico.dll"
File "/opt/windev/qt64d-latest/plugins/imageformats/qjpeg.dll"
File "/opt/windev/qt64d-latest/plugins/imageformats/qsvg.dll"
File "/opt/windev/qt64d-latest/plugins/imageformats/qtga.dll"
File "/opt/windev/qt64d-latest/plugins/imageformats/qtiff.dll"
File "/opt/windev/qt64d-latest/plugins/imageformats/qwbmp.dll"
File "/opt/windev/qt64d-latest/plugins/imageformats/qwebp.dll"
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 "/opt/windev/qt64d-latest/plugins/platforms/qwindows.dll"
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
######################################################################
@ -186,33 +224,88 @@ 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\libgcc_s_seh-1.dll"
Delete "$INSTDIR\libstdc++-6.dll"
Delete "$INSTDIR\libcrypto-43.dll"
Delete "$INSTDIR\libssl-45.dll"
Delete "$INSTDIR\libjpeg-62.dll"
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\Qt5WinExtras.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\audio\qtaudio_windows.dll"
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"
@ -223,10 +316,13 @@ 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\platforms"
RmDir "$INSTDIR\imageformats"
RmDir "$INSTDIR\platforms"
RmDir "$INSTDIR\styles"
Delete "$INSTDIR\uninstall.exe"
!ifdef WEB_SITE

24
.ci/osx_build.sh Executable file
View File

@ -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

8
.ci/osx_ci.sh Executable file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Install packages
.ci/osx_install.sh && \
# Build gta5view
.ci/osx_build.sh && \
cd ${PROJECT_DIR}

4
.ci/osx_install.sh Executable file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env bash
# Install packages
brew upgrade cmake qt

28
.ci/windows_build.sh Executable file
View File

@ -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

25
.ci/windows_docker.sh Executable file
View File

@ -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

29
.ci/wininstall_build.sh Executable file
View File

@ -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

11
.ci/wininstall_docker.sh Executable file
View File

@ -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"

36
.drone.yml Normal file
View File

@ -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

Binary file not shown.

33
.drone/drone.sh Executable file
View File

@ -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}/

View File

@ -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: ../

4
.gitignore vendored
View File

@ -29,3 +29,7 @@
# Qt project user file
*.pro.user
# Gettext translation files
*.po
*.pot

31
.gitlab-ci.yml Normal file
View File

@ -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"

Binary file not shown.

24
.gitlab/gitlab.sh Executable file
View File

@ -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/* ./

View File

@ -1,45 +1,36 @@
dist: trusty
dist: bionic
sudo: required
language: cpp
service:
services:
- docker
env:
global:
- BUILD_TYPE=REL
matrix:
include:
- env:
- BUILD_SCRIPT=debian_travis.sh
- QMAKE_FLAGS_QT4=QMAKE_CXXFLAGS+=-Wno-missing-field-initializers
- DEBIAN_VERSION=jessie
- DOCKER_USER=i386
- APT_INSTALL=clang
- env:
- BUILD_SCRIPT=debian_travis.sh
- QMAKE_FLAGS_QT4=QMAKE_CXXFLAGS+=-Wno-missing-field-initializers
- DEBIAN_VERSION=jessie
- BUILD_SCRIPT=debian_docker.sh
- RELEASE_LABEL="Debian 64-Bit Package"
- DEBIAN_VERSION=buster
- DOCKER_USER=amd64
- APT_INSTALL=clang
- env:
- BUILD_SCRIPT=windows_travis.sh
- QT_SELECT=qt5-i686-w64-mingw32
- env:
- BUILD_SCRIPT=windows_travis.sh
- BUILD_SCRIPT=windows_docker.sh
- QT_SELECT=qt5-x86_64-w64-mingw32
- EXECUTABLE_ARCH=_x64
- RELEASE_LABEL="Windows 64-Bit Portable"
- env:
- BUILD_SCRIPT=windows_travis.sh
- QT_SELECT=qt5-x86_64-w64-mingw32
- PACKAGE_CODE=Dropbox
- env:
- BUILD_SCRIPT=windows_travis.sh
- QT_SELECT=qt5-x86_64-w64-mingw32
- PACKAGE_CODE=gta5-mods
- env:
- BUILD_SCRIPT=wininstall_travis.sh
- BUILD_SCRIPT=wininstall_docker.sh
- QT_SELECT=qt5-x86_64-w64-mingw32
- RELEASE_LABEL="Windows 64-Bit Installer"
- os: osx
env: BUILD_SCRIPT=osx_travis.sh
osx_image: xcode14.2
env:
- BUILD_SCRIPT=osx_ci.sh
- RELEASE_LABEL="Mac OS X 64-Bit Disk Image"
before_install:
- ".travis/source.sh"
@ -51,6 +42,7 @@ 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

Binary file not shown.

View File

@ -1,31 +0,0 @@
#!/bin/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/gta5view.desktop res/gta5view.png && \
cd build && \
mkdir -p qt4 && \
cd qt4 && \
echo "Grand Theft Auto V Snapmatic and Savegame viewer/manager" > ./description-pak && \
cd .. && \
mkdir -p qt5 && \
cd qt5 && \
echo "Grand Theft Auto V Snapmatic and Savegame viewer/manager" > ./description-pak && \
cd .. && \
# Prepare checkinstall step
mkdir -p /usr/share/gta5view && \
# Starting build
cd qt5 && \
qmake -qt=5 -spec linux-clang GTA5SYNC_PREFIX=/usr QMAKE_CXXFLAGS+=-std=gnu++11 ${QMAKE_FLAGS_QT5} DEFINES+=GTA5SYNC_BUILDTYPE_REL "DEFINES+=GTA5SYNC_BUILDCODE=\\\\\\\"${PACKAGE_CODE}\\\\\\\"" "DEFINES+=GTA5SYNC_APPVER=\\\\\\\"${APPLICATION_VERSION}\\\\\\\"" DEFINES+=GTA5SYNC_QCONF DEFINES+=GTA5SYNC_TELEMETRY "DEFINES+=GTA5SYNC_TELEMETRY_WEBURL=\\\\\\\"https://dev.syping.de/gta5view-userstats/\\\\\\\"" ../../gta5view.pro && \
make -j 4 && \
checkinstall -D --default --nodoc --install=no --pkgname=gta5view-qt5 --pkgversion=${PACKAGE_VERSION} --pkgrelease=${PACKAGE_BUILD} --pkggroup=utility --maintainer="Syping \<dpkg@syping.de\>" --requires=libqt5core5a,libqt5gui5,libqt5network5,libqt5widgets5,qttranslations5-l10n --conflicts=gta5view,gta5view-qt4 --replaces=gta5view,gta5view-qt4 --pakdir=${PROJECT_DIR}/assets && \
cd .. && \
cd qt4 && \
qmake -qt=4 GTA5SYNC_PREFIX=/usr QMAKE_CXXFLAGS+=-std=gnu++11 ${QMAKE_FLAGS_QT4} DEFINES+=GTA5SYNC_BUILDTYPE_REL "DEFINES+=GTA5SYNC_BUILDCODE=\\\\\\\"${PACKAGE_CODE}\\\\\\\"" "DEFINES+=GTA5SYNC_APPVER=\\\\\\\"${APPLICATION_VERSION}\\\\\\\"" DEFINES+=GTA5SYNC_QCONF ../../gta5view.pro && \
make -j 4 && \
checkinstall -D --default --nodoc --install=no --pkgname=gta5view-qt4 --pkgversion=${PACKAGE_VERSION} --pkgrelease=${PACKAGE_BUILD} --pkggroup=utility --maintainer="Syping \<dpkg@syping.de\>" --requires=libqtcore4,libqtgui4,libqt4-network,qtcore4-l10n --conflicts=gta5view,gta5view-qt5 --replaces=gta5view,gta5view-qt5 --pakdir=${PROJECT_DIR}/assets

View File

@ -1,5 +0,0 @@
#!/bin/bash
# Install packages
apt-get update -qq && \
apt-get install -qq ${APT_INSTALL} checkinstall dpkg-dev fakeroot g++ gcc qtbase5-dev qt5-qmake qttranslations5-l10n libqt4-dev

View File

@ -1,15 +0,0 @@
#!/bin/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 APT_INSTALL=${APT_INSTALL} && export QMAKE_FLAGS_QT4=${QMAKE_FLAGS_QT4} && export QMAKE_FLAGS_QT5=${QMAKE_FLAGS_QT5} && 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} && .travis/debian_install.sh && .travis/debian_build.sh"

View File

@ -1,14 +0,0 @@
#!/bin/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/opt/qt/bin/qmake ${QMAKE_FLAGS_QT5} DEFINES+=GTA5SYNC_BUILDTYPE_REL "DEFINES+=GTA5SYNC_BUILDCODE=\\\\\\\"${PACKAGE_CODE}\\\\\\\"" "DEFINES+=GTA5SYNC_APPVER=\\\\\\\"${APPLICATION_VERSION}\\\\\\\"" DEFINES+=GTA5SYNC_TELEMETRY "DEFINES+=GTA5SYNC_TELEMETRY_WEBURL=\\\\\\\"https://dev.syping.de/gta5view-userstats/\\\\\\\"" ../gta5view.pro && \
make -j 4 && \
/usr/local/opt/qt/bin/macdeployqt gta5view.app -dmg && \
cp -Rf gta5view.dmg ../assets/gta5view-osx_${APPLICATION_VERSION}.dmg

View File

@ -1,4 +0,0 @@
#!/bin/bash
# Install packages
brew install qt

View File

@ -1,8 +0,0 @@
#!/bin/bash
# Install packages
.travis/osx_install.sh && \
# Build gta5view
.travis/osx_build.sh && \
cd ${PROJECT_DIR}

View File

@ -1,5 +1,5 @@
#!/bin/bash
rm -rf tmext/TelemetryClassAuthenticator.cpp && \
openssl aes-256-cbc -K $encrypted_55502862a724_key -iv $encrypted_55502862a724_iv -in tmext/TelemetryClassAuthenticator.cpp.enc -out tmext/TelemetryClassAuthenticator.cpp -d && \
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

View File

@ -1,11 +1,11 @@
#!/bin/bash
# Install lua
if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
if [ "${TRAVIS_OS_NAME}" == "osx" ]; then
brew install lua
else
sudo apt-get update -qq && \
sudo apt-get install -qq lua5.2
elif [ "${TRAVIS_OS_NAME}" == "osx" ]; then
brew install lua
fi
# Check if build is not tagged
@ -20,11 +20,8 @@ if [ "${PACKAGE_CODE}" == "" ]; then
export PACKAGE_CODE=GitHub
fi
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}
if [[ ! ${PACKAGE_BUILD} ]]; then export PACKAGE_BUILD=1; fi
export PROJECT_DIR=$(pwd)
# Init Application Commit Hash
export APPLICATION_COMMIT=$(git rev-parse --short HEAD)
.travis/${BUILD_SCRIPT}
# Start CI script
.ci/ci.sh

View File

@ -1,8 +1,8 @@
#!/bin/bash
# Install packages
sudo .travis/debian_install.sh && \
sudo .ci/debian_install.sh && \
# Build gta5view
sudo .travis/debian_build.sh && \
sudo .ci/debian_build.sh && \
cd ${PROJECT_DIR}

View File

@ -1,31 +0,0 @@
#!/bin/bash
apt-get update -qq && \
apt-get install -qq curl && \
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 && \
qmake-static ${QMAKE_FLAGS} DEFINES+=GTA5SYNC_BUILDTYPE_REL "DEFINES+=GTA5SYNC_BUILDCODE=\\\\\\\"${PACKAGE_CODE}\\\\\\\"" "DEFINES+=GTA5SYNC_APPVER=\\\\\\\"${APPLICATION_VERSION}\\\\\\\"" DEFINES+=GTA5SYNC_TELEMETRY "DEFINES+=GTA5SYNC_TELEMETRY_WEBURL=\\\\\\\"https://dev.syping.de/gta5view-userstats/\\\\\\\"" ../gta5view.pro && \
make -j 4 && \
cp -Rf release/*.exe ${PROJECT_DIR}/assets/${GTA5VIEW_EXECUTABLE} && \
cd ${PROJECT_DIR}/assets && \
upx --best ${GTA5VIEW_EXECUTABLE} && \
if [ "${PACKAGE_CODE}" == "Dropbox" ]; then
${PROJECT_DIR}/.travis/dropbox_uploader.sh mkdir gta5view-builds/${PACKAGE_VERSION}
${PROJECT_DIR}/.travis/dropbox_uploader.sh upload ${GTA5VIEW_EXECUTABLE} gta5view-builds/${PACKAGE_VERSION}/${GTA5VIEW_EXECUTABLE} && \
rm -rf ${GTA5VIEW_EXECUTABLE}
elif [ "${PACKAGE_CODE}" == "gta5-mods" ]; then
${PROJECT_DIR}/.travis/dropbox_uploader.sh mkdir gta5-mods/${PACKAGE_VERSION}
${PROJECT_DIR}/.travis/dropbox_uploader.sh upload ${GTA5VIEW_EXECUTABLE} gta5-mods/${PACKAGE_VERSION}/${GTA5VIEW_EXECUTABLE} && \
rm -rf ${GTA5VIEW_EXECUTABLE}
fi

View File

@ -1,13 +0,0 @@
#!/bin/bash
QT_VERSION=5.6.3
DOCKER_IMAGE=syping/qt5-static-mingw:${QT_VERSION}
PROJECT_DIR_DOCKER=/gta5view
cd ${PROJECT_DIR} && \
docker pull ${DOCKER_IMAGE} && \
docker run --rm \
-v "${PROJECT_DIR}:${PROJECT_DIR_DOCKER}" \
-v "${HOME}/.dropbox_uploader:/root/.dropbox_uploader" \
${DOCKER_IMAGE} \
/bin/bash -c "export PROJECT_DIR=${PROJECT_DIR_DOCKER} && export QT_SELECT=${QT_SELECT} && export APPLICATION_VERSION=${APPLICATION_VERSION} && export QMAKE_FLAGS_QT4=${QMAKE_FLAGS_QT4} && export QMAKE_FLAGS_QT5=${QMAKE_FLAGS_QT5} && 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} && .travis/windows_build.sh"

View File

@ -1,18 +0,0 @@
#!/bin/bash
apt-get update -qq && \
apt-get install -qq nsis && \
# Creating folders
cd ${PROJECT_DIR} && \
echo "gta5view build version is ${APPLICATION_VERSION}" && \
mkdir -p build && \
mkdir -p assets && \
# Starting build
cd build && \
qmake ${QMAKE_FLAGS} DEFINES+=GTA5SYNC_BUILDTYPE_REL "DEFINES+=GTA5SYNC_BUILDCODE=\\\\\\\"${PACKAGE_CODE}\\\\\\\"" "DEFINES+=GTA5SYNC_APPVER=\\\\\\\"${APPLICATION_VERSION}\\\\\\\"" DEFINES+=GTA5SYNC_TELEMETRY "DEFINES+=GTA5SYNC_TELEMETRY_WEBURL=\\\\\\\"https://dev.syping.de/gta5view-userstats/\\\\\\\"" DEFINES+=GTA5SYNC_QCONF DEFINES+=GTA5SYNC_INLANG='\\\"RUNDIR:SEPARATOR:lang\\\"' DEFINES+=GTA5SYNC_LANG='\\\"RUNDIR:SEPARATOR:lang\\\"' DEFINES+=GTA5SYNC_PLUG='\\\"RUNDIR:SEPARATOR:plugins\\\"' ../gta5view.pro && \
make -j 4 && \
cd ${PROJECT_DIR}/assets && \
makensis -NOCD ${PROJECT_DIR}/.travis/gta5view.nsi && \
mv -f gta5view_setup.exe gta5view-${EXECUTABLE_VERSION}_setup.exe

View File

@ -1,12 +0,0 @@
#!/bin/bash
QT_VERSION=5.6.3
DOCKER_IMAGE=syping/qt5-shared-mingw:${QT_VERSION}
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 QMAKE_FLAGS_QT4=${QMAKE_FLAGS_QT4} && export QMAKE_FLAGS_QT5=${QMAKE_FLAGS_QT5} && 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} && .travis/wininstall_build.sh"

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2018 Syping
* 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
@ -29,43 +29,47 @@ AboutDialog::AboutDialog(QWidget *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 = qApp->applicationVersion();
QString buildType = tr(GTA5SYNC_BUILDTYPE);
buildType.replace("_", " ");
QString projectBuild = AppEnv::getBuildDateTime();
QString buildStr = GTA5SYNC_BUILDSTRING;
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
QString translatedByStr = tr("Translated by %1");
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
QString translatorVal = tr("TRANSLATOR");
const QString translatorVal = tr("TRANSLATOR");
QStringList translatorContent;
if (translatorVal != "TRANSLATOR")
{
if (translatorVal != "TRANSLATOR") {
const QStringList translatorList = translatorVal.split('\n');
for (const QString &translatorStr : translatorList)
{
for (const QString &translatorStr : translatorList) {
QStringList translatorStrList = translatorStr.split(',');
QString translatorName = translatorStrList.at(0);
const QString translatorName = translatorStrList.at(0);
translatorStrList.removeFirst();
QString translatorProfile = translatorStrList.join(QString());
if (!translatorProfile.isEmpty())
{
if (!translatorProfile.isEmpty()) {
translatorContent += QString("<a href=\"%1\">%2</a>").arg(translatorProfile, translatorName);
}
else
{
else {
translatorContent += translatorName;
}
}
}
// Project Description
QString projectDes = tr("A project for viewing Grand Theft Auto V Snapmatic<br/>\nPictures and Savegames");
const QString projectDes = tr("A project for viewing Grand Theft Auto V Snapmatic<br/>\nPictures and Savegames");
// Copyright Description
QString copyrightDes1 = tr("Copyright &copy; <a href=\"%1\">%2</a> %3");
@ -73,40 +77,34 @@ AboutDialog::AboutDialog(QWidget *parent) :
QString copyrightDes2 = tr("%1 is licensed under <a href=\"https://www.gnu.org/licenses/gpl-3.0.html#content\">GNU GPLv3</a>");
copyrightDes2 = copyrightDes2.arg(GTA5SYNC_APPSTR);
QString copyrightDesA;
if (!translatorContent.isEmpty())
{
if (!translatorContent.isEmpty()) {
copyrightDesA = copyrightDes1 % "<br/>" % translatedByStr.arg(translatorContent.join(", ")) % "<br/>" % copyrightDes2;
}
else
{
else {
copyrightDesA = copyrightDes1 % "<br/>" % copyrightDes2;
}
// Setup User Interface
ui->setupUi(this);
aboutStr = ui->labAbout->text();
titleStr = this->windowTitle();
titleStr = windowTitle();
ui->labAbout->setText(aboutStr.arg(GTA5SYNC_APPSTR, projectDes, appVersion % " (" % buildType % ")", projectBuild, buildStr, qVersion(), copyrightDesA));
this->setWindowTitle(titleStr.arg(GTA5SYNC_APPSTR));
setWindowTitle(titleStr.arg(GTA5SYNC_APPSTR));
// Set Icon for Close Button
if (QIcon::hasThemeIcon("dialog-close"))
{
if (QIcon::hasThemeIcon("dialog-close")) {
ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close"));
}
else if (QIcon::hasThemeIcon("gtk-close"))
{
else if (QIcon::hasThemeIcon("gtk-close")) {
ui->cmdClose->setIcon(QIcon::fromTheme("gtk-close"));
}
// DPI calculation
qreal screenRatio = AppEnv::screenRatio();
if (!translatorContent.isEmpty())
{
if (!translatorContent.isEmpty()) {
resize(375 * screenRatio, 270 * screenRatio);
}
else
{
else {
resize(375 * screenRatio, 260 * screenRatio);
}
}
@ -118,13 +116,11 @@ AboutDialog::~AboutDialog()
void AboutDialog::on_labAbout_linkActivated(const QString &link)
{
if (link.left(12) == "g5e://about?")
{
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
{
else {
QDesktopServices::openUrl(QUrl(link));
}
}

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -28,7 +28,6 @@ class AboutDialog;
class AboutDialog : public QDialog
{
Q_OBJECT
public:
explicit AboutDialog(QWidget *parent = 0);
~AboutDialog();

View File

@ -26,7 +26,7 @@
</sizepolicy>
</property>
<property name="text">
<string>&lt;span style=&quot; font-weight:600;&quot;&gt;%1&lt;/span&gt;&lt;br/&gt;
<string>&lt;span style=&quot;font-weight:600&quot;&gt;%1&lt;/span&gt;&lt;br/&gt;
&lt;br/&gt;
%2&lt;br/&gt;
&lt;br/&gt;

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -22,19 +22,19 @@
#include "StandardPaths.h"
#include <QtGlobal>
#include <QStringBuilder>
#include <QDesktopWidget>
#include <QApplication>
#include <QSettings>
#include <QScreen>
#include <QDebug>
#include <QRect>
#include <QDir>
#include <iostream>
using namespace std;
#if QT_VERSION < 0x050000
#include <QDesktopWidget>
#endif
AppEnv::AppEnv()
{
}
// Build Stuff
@ -55,18 +55,17 @@ QString AppEnv::getGameFolder(bool *ok)
{
QDir dir;
QString GTAV_FOLDER = QString::fromUtf8(qgetenv("GTAV_FOLDER"));
if (GTAV_FOLDER != "")
{
if (GTAV_FOLDER != "") {
dir.setPath(GTAV_FOLDER);
if (dir.exists())
{
if (ok != NULL) *ok = true;
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";
const QString GTAV_defaultFolder = StandardPaths::documentsLocation() % "/Rockstar Games/GTA V";
QString GTAV_returnFolder = GTAV_defaultFolder;
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
@ -75,46 +74,44 @@ QString AppEnv::getGameFolder(bool *ok)
GTAV_returnFolder = settings.value("dir", GTAV_defaultFolder).toString();
settings.endGroup();
if (forceDir)
{
if (forceDir) {
dir.setPath(GTAV_returnFolder);
if (dir.exists())
{
if (ok != 0) *ok = true;
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;
if (dir.exists()) {
if (ok != 0)
*ok = true;
qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8());
return dir.absolutePath();
}
if (!forceDir)
{
if (!forceDir) {
dir.setPath(GTAV_returnFolder);
if (dir.exists())
{
if (ok != 0) *ok = true;
if (dir.exists()) {
if (ok != 0)
*ok = true;
qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8());
return dir.absolutePath();
}
}
if (ok != 0) *ok = false;
return "";
if (ok != 0)
*ok = false;
return QString();
}
bool AppEnv::setGameFolder(QString gameFolder)
{
QDir dir;
dir.setPath(gameFolder);
if (dir.exists())
{
if (dir.exists()) {
qputenv("GTAV_FOLDER", dir.absolutePath().toUtf8());
return true;
}
@ -132,13 +129,13 @@ QString AppEnv::getInLangFolder()
#ifdef GTA5SYNC_INLANG
return StringParser::convertBuildedString(GTA5SYNC_INLANG);
#else
return StringParser::convertBuildedString(GTA5SYNC_SHARE % QLatin1String("SEPARATOR:APPNAME:SEPARATOR:translations"));
return StringParser::convertBuildedString(GTA5SYNC_SHARE % QLatin1String("/APPNAME:/translations"));
#endif
#else
#ifdef GTA5SYNC_INLANG
return StringParser::convertBuildedString(GTA5SYNC_INLANG);
#else
return QString(":/tr");
return QLatin1String(":/tr");
#endif
#endif
}
@ -148,18 +145,57 @@ 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()
{
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();
#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/reference/crewfeed/%1").arg(crewID));
// }
QUrl AppEnv::getCrewFetchingUrl(QString crewID)
{
return QUrl(QString("https://socialclub.rockstargames.com/crew/%1/%1").arg(crewID));
@ -167,7 +203,7 @@ QUrl AppEnv::getCrewFetchingUrl(QString crewID)
QUrl AppEnv::getPlayerFetchingUrl(QString crewID, QString pageNumber)
{
return QUrl(QString("https://socialclub.rockstargames.com/crewsapi/GetMembersList?crewId=%1&pageNumber=%2").arg(crewID, 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)
@ -175,12 +211,234 @@ 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 = QGuiApplication::primaryScreen()->logicalDotsPerInch();
qreal dpi = QApplication::primaryScreen()->logicalDotsPerInch();
#else
qreal dpi = qApp->desktop()->logicalDpiX();
qreal dpi = QApplication::desktop()->logicalDpiX();
#endif
#ifdef Q_OS_MAC
return (dpi / 72);
@ -188,3 +446,12 @@ qreal AppEnv::screenRatio()
return (dpi / 96);
#endif
}
qreal AppEnv::screenRatioPR()
{
#if QT_VERSION >= 0x050600
return QApplication::primaryScreen()->devicePixelRatio();
#else
return 1;
#endif
}

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2018 Syping
* 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
@ -22,6 +22,9 @@
#include <QString>
#include <QUrl>
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:
@ -36,7 +39,9 @@ public:
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();
@ -44,8 +49,16 @@ public:
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

382
CMakeLists.txt Normal file
View File

@ -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()

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -44,137 +44,65 @@ void DatabaseThread::run()
{
QEventLoop threadLoop;
QStringList crewList;
QStringList crewListR;
// Register thread loop end signal
QObject::connect(this, SIGNAL(threadTerminated()), &threadLoop, SLOT(quit()));
// Setup crewList for Quick time scan
crewList = crewDB->getCrews();
if (!crewList.isEmpty())
{
crewListR = deleteCompatibleCrews(crewList);
}
else
{
while (crewList.isEmpty() && threadRunning)
{
QTimer::singleShot(1000, &threadLoop, SLOT(quit()));
threadLoop.exec();
if (!crewDB->isAddingCrews())
{
crewList = crewDB->getCrews();
}
}
if (threadRunning)
{
crewListR = deleteCompatibleCrews(crewList);
}
}
// Only do QTS when Thread should be run
if (threadRunning)
{
// Quick time scan
#ifdef GTA5SYNC_DEBUG
qDebug() << "Start QTS";
#endif
if (crewListR.length() <= 5)
{
scanCrewReference(crewListR, 2500);
emit crewNameUpdated();
}
if (crewList.length() <= 3)
{
scanCrewMembersList(crewList, 3, 2500);
emit playerNameUpdated();
}
else if (crewList.length() <= 5)
{
scanCrewMembersList(crewList, 2, 2500);
emit playerNameUpdated();
}
if (threadRunning)
{
QTimer::singleShot(10000, &threadLoop, SLOT(quit()));
threadLoop.exec();
}
}
while (threadRunning)
{
crewList = crewDB->getCrews();
crewListR = deleteCompatibleCrews(crewList);
// Long time scan
#ifdef GTA5SYNC_DEBUG
qDebug() << "Start LTS";
#endif
scanCrewReference(crewListR, 10000);
emit crewNameUpdated();
scanCrewMembersList(crewList, crewMaxPages, 10000);
emit playerNameUpdated();
if (threadRunning)
{
QTimer::singleShot(300000, &threadLoop, SLOT(quit()));
threadLoop.exec();
}
while (threadRunning) {
QTimer::singleShot(300000, &threadLoop, SLOT(quit()));
threadLoop.exec();
}
}
void DatabaseThread::scanCrewReference(const QStringList &crewList, const int &requestDelay)
{
for (QString crewID : crewList)
{
if (threadRunning && crewID != QLatin1String("0"))
{
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,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("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())); }
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())
{
if (netReply->isFinished()) {
QString crewName;
QByteArray crewHtml = netReply->readAll();
QStringList crewHtmlSplit1 = QString::fromUtf8(crewHtml).split("<title>Rockstar Games Social Club - Crew : ");
if (crewHtmlSplit1.length() >= 2)
{
if (crewHtmlSplit1.length() >= 2) {
QStringList crewHtmlSplit2 = QString(crewHtmlSplit1.at(1)).split("</title>");
if (crewHtmlSplit2.length() >= 1)
{
if (crewHtmlSplit2.length() >= 1) {
crewName = crewHtmlSplit2.at(0);
}
}
if (!crewName.isEmpty())
{
if (!crewName.isEmpty()) {
emit crewNameFound(crewID.toInt(), crewName);
}
}
else {
netReply->abort();
}
if (threadRunning)
{
if (threadRunning) {
QEventLoop *waitingLoop = new QEventLoop();
QTimer::singleShot(requestDelay, waitingLoop, SLOT(quit()));
if (!continueLastCrew) { QObject::connect(this, SIGNAL(threadTerminated()), waitingLoop, SLOT(quit())); }
if (!continueLastCrew)
QObject::connect(this, SIGNAL(threadTerminated()), waitingLoop, SLOT(quit()));
waitingLoop->exec();
waitingLoop->disconnect();
delete waitingLoop;
@ -188,39 +116,39 @@ void DatabaseThread::scanCrewReference(const QStringList &crewList, const int &r
void DatabaseThread::scanCrewMembersList(const QStringList &crewList, const int &maxPages, const int &requestDelay)
{
for (QString crewID : crewList)
{
if (threadRunning && crewID != QLatin1String("0"))
{
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))
{
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", "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("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())); }
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())
{
if (netReply->isFinished()) {
QByteArray crewJson = netReply->readAll();
QJsonDocument crewDocument = QJsonDocument::fromJson(crewJson);
QJsonObject crewObject = crewDocument.object();
@ -228,32 +156,25 @@ void DatabaseThread::scanCrewMembersList(const QStringList &crewList, const int
if (crewMap.contains("Total")) { totalPlayers = crewMap["Total"].toInt(); }
if (crewMap.contains("Members"))
{
if (crewMap.contains("Members")) {
const QList<QVariant> memberList = crewMap["Members"].toList();
for (QVariant memberVariant : memberList)
{
for (const QVariant &memberVariant : memberList) {
QMap<QString, QVariant> memberMap = memberVariant.toMap();
if (memberMap.contains("RockstarId") && memberMap.contains("Name"))
{
if (memberMap.contains("RockstarId") && memberMap.contains("Name")) {
int RockstarId = memberMap["RockstarId"].toInt();
QString memberName = memberMap["Name"].toString();
if (!memberName.isEmpty() && RockstarId != 0)
{
if (!memberName.isEmpty() && RockstarId != 0) {
foundPlayers++;
emit playerNameFound(RockstarId, memberName);
}
}
}
}
currentPage++;
}
else
{
else {
currentFail++;
if (currentFail == maxLoadFails)
{
if (currentFail == maxLoadFails) {
currentFail = 0;
currentPage++;
}
@ -262,8 +183,7 @@ void DatabaseThread::scanCrewMembersList(const QStringList &crewList, const int
delete netReply;
delete netManager;
if (foundPlayers < totalPlayers && currentPage < maxPages && (continueLastCrew ? true : threadRunning))
{
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())); }
@ -278,10 +198,8 @@ void DatabaseThread::scanCrewMembersList(const QStringList &crewList, const int
void DatabaseThread::deleteCompatibleCrews(QStringList *crewList)
{
for (QString crewNID : *crewList)
{
if (crewDB->isCompatibleCrew(crewNID))
{
for (const QString &crewNID : *crewList) {
if (crewDB->isCompatibleCrew(crewNID)) {
crewList->removeAll(crewNID);
}
}
@ -290,10 +208,8 @@ void DatabaseThread::deleteCompatibleCrews(QStringList *crewList)
QStringList DatabaseThread::deleteCompatibleCrews(const QStringList &crewList)
{
QStringList crewListR = crewList;
for (QString crewNID : crewListR)
{
if (crewDB->isCompatibleCrew(crewNID))
{
for (const QString &crewNID : crewListR) {
if (crewDB->isCompatibleCrew(crewNID)) {
crewListR.removeAll(crewNID);
}
}

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -22,14 +22,19 @@
#include "ProfileWidget.h"
#include "ExportThread.h"
#include "SavegameData.h"
#include "AppEnv.h"
#include "config.h"
#include <QStringBuilder>
#include <QDesktopWidget>
#include <QApplication>
#include <QFileInfo>
#include <QDebug>
#include <QFile>
#if QT_VERSION >= 0x050000
#include <QScreen>
#else
#include <QDesktopWidget>
#endif
ExportThread::ExportThread(QMap<ProfileWidget*,QString> profileMap, QString exportDirectory, bool pictureCopyEnabled, bool pictureExportEnabled, int exportCount, QObject *parent) : QThread(parent),
profileMap(profileMap), exportDirectory(exportDirectory), pictureCopyEnabled(pictureCopyEnabled), pictureExportEnabled(pictureExportEnabled), exportCount(exportCount)
{
@ -44,7 +49,7 @@ void ExportThread::run()
// Quality Settings
settings.beginGroup("Pictures");
int defaultQuality = 100;
QSize defExportSize = QSize(960, 536);
QSize defExportSize = SnapmaticPicture::getSnapmaticResolution();
int customQuality = settings.value("CustomQuality", defaultQuality).toInt();
if (customQuality < 1 || customQuality > 100)
{
@ -101,8 +106,17 @@ void ExportThread::run()
QImage exportPicture = picture->getImage();
if (sizeMode == "Desktop")
{
QRect desktopResolution = qApp->desktop()->screenGeometry();
exportPicture = exportPicture.scaled(desktopResolution.width(), desktopResolution.height(), aspectRatio, Qt::SmoothTransformation);
#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")
{

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2018 Syping
* 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
@ -28,17 +28,17 @@
GlobalString::GlobalString()
{
}
QMap<QString, QString> GlobalString::getGlobalMap()
{
QMap<QString, QString> globalMap;
QSettings globalFile(getLanguageFile(), QSettings::IniFormat);
#if QT_VERSION < 0x060000
globalFile.setIniCodec("UTF-8");
#endif
globalFile.beginGroup("Global");
for (QString globalStr : globalFile.childKeys())
{
for (const QString &globalStr : globalFile.childKeys()) {
globalMap[globalStr] = globalFile.value(globalStr, globalStr).toString();
}
globalFile.endGroup();
@ -49,12 +49,14 @@ 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;
if (globalStrList.contains(valueStr)) {
if (ok != nullptr)
*ok = true;
globalString = globalFile.value(valueStr, valueStr).toString();
}
globalFile.endGroup();
@ -65,10 +67,14 @@ QString GlobalString::getLanguageFile()
{
QString language = getLanguage();
QString languageFile = ":/global/global." % language % ".ini";
if (!QFileInfo(languageFile).exists())
{
#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;
}

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -17,34 +17,45 @@
*****************************************************************************/
#include "IconLoader.h"
#include "AppEnv.h"
#include <QStringBuilder>
#include <QIcon>
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));
#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;
pointmakerIcon.addFile(":/img/pointmaker-8.png", QSize(8, 8));
pointmakerIcon.addFile(":/img/pointmaker-16.png", QSize(16, 16));
pointmakerIcon.addFile(":/img/pointmaker-24.png", QSize(24, 24));
pointmakerIcon.addFile(":/img/pointmaker-32.png", QSize(32, 32));
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;
}

View File

@ -1,211 +0,0 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2017-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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "ImageEditorDialog.h"
#include "ui_ImageEditorDialog.h"
#include "ProfileInterface.h"
#include "SidebarGenerator.h"
#include "StandardPaths.h"
#include "ImportDialog.h"
#include "AppEnv.h"
#include "config.h"
#include <QStringBuilder>
#include <QImageReader>
#include <QFileDialog>
#include <QMessageBox>
ImageEditorDialog::ImageEditorDialog(SnapmaticPicture *picture, QString profileName, QWidget *parent) :
QDialog(parent), smpic(picture), profileName(profileName),
ui(new Ui::ImageEditorDialog)
{
// Set Window Flags
setWindowFlags(windowFlags()^Qt::WindowContextHelpButtonHint);
ui->setupUi(this);
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 Import Button
if (QIcon::hasThemeIcon("document-import"))
{
ui->cmdReplace->setIcon(QIcon::fromTheme("document-import"));
}
// Set Icon for Overwrite 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"));
}
// DPI calculation
qreal screenRatio = AppEnv::screenRatio();
snapmaticResolutionLW = 516 * screenRatio; // 430
snapmaticResolutionLH = 288 * screenRatio; // 240
ui->labPicture->setMinimumSize(snapmaticResolutionLW, snapmaticResolutionLH);
ui->labCapacity->setText(tr("Capacity: %1").arg(QString::number(qRound((double)picture->getContentMaxLength() / 1024)) % " KB"));
imageIsChanged = false;
pictureCache = picture->getImage();
ui->labPicture->setPixmap(QPixmap::fromImage(pictureCache).scaled(snapmaticResolutionLW, snapmaticResolutionLH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
setMaximumSize(sizeHint());
setMinimumSize(sizeHint());
setFixedSize(sizeHint());
}
ImageEditorDialog::~ImageEditorDialog()
{
delete ui;
}
void ImageEditorDialog::on_cmdClose_clicked()
{
close();
}
void ImageEditorDialog::on_cmdReplace_clicked()
{
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
settings.beginGroup("FileDialogs");
bool dontUseNativeDialog = settings.value("DontUseNativeDialog", false).toBool();
settings.beginGroup("ImportReplace");
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(ProfileInterface::tr("Import..."));
fileDialog.setLabelText(QFileDialog::Accept, ProfileInterface::tr("Import"));
// Getting readable Image formats
QString imageFormatsStr = " ";
for (QByteArray imageFormat : QImageReader::supportedImageFormats())
{
imageFormatsStr += QString("*.") % QString::fromUtf8(imageFormat).toLower() % " ";
}
QStringList filters;
filters << ProfileInterface::tr("All image files (%1)").arg(imageFormatsStr.trimmed());
filters << ProfileInterface::tr("All files (**)");
fileDialog.setNameFilters(filters);
QList<QUrl> 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, ProfileInterface::tr("Import"), ProfileInterface::tr("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, ProfileInterface::tr("Import"), ProfileInterface::tr("Can't import %1 because file can't be parsed properly").arg("\""+selectedFileName+"\""));
delete importImage;
goto fileDialogPreOpen;
}
ImportDialog *importDialog = new ImportDialog(this);
importDialog->setImage(importImage);
importDialog->setModal(true);
importDialog->show();
importDialog->exec();
if (importDialog->isImportAgreed())
{
pictureCache = importDialog->image();
ui->labPicture->setPixmap(QPixmap::fromImage(pictureCache).scaled(snapmaticResolutionLW, snapmaticResolutionLH, Qt::KeepAspectRatio, Qt::SmoothTransformation));
imageIsChanged = true;
}
delete importDialog;
}
}
settings.setValue(profileName % "+Geometry", fileDialog.saveGeometry());
settings.setValue(profileName % "+Directory", fileDialog.directory().absolutePath());
settings.endGroup();
settings.endGroup();
}
void ImageEditorDialog::on_cmdSave_clicked()
{
if (imageIsChanged)
{
const QByteArray previousPicture = smpic->getPictureStream();
bool success = smpic->setImage(pictureCache);
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, tr("Snapmatic Image Editor"), tr("Patching of Snapmatic Image failed because of I/O Error"));
return;
}
smpic->emitCustomSignal("PictureUpdated");
}
else
{
QMessageBox::warning(this, tr("Snapmatic Image Editor"), tr("Patching of Snapmatic Image failed because of Image Error"));
return;
}
}
close();
}
void ImageEditorDialog::on_cmdQuestion_clicked()
{
QMessageBox::information(this, tr("Snapmatic Image Editor"), tr("Every taken Snapmatic have a different Capacity, a Snapmatic with higher Capacity can store a picture with better quality."));
}

View File

@ -1,130 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ImageEditorDialog</class>
<widget class="QDialog" name="ImageEditorDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>516</width>
<height>335</height>
</rect>
</property>
<property name="windowTitle">
<string>Overwrite Image...</string>
</property>
<layout class="QVBoxLayout" name="vlInterface">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="labPicture">
<property name="minimumSize">
<size>
<width>516</width>
<height>288</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="buttomFrame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QVBoxLayout" name="vlButtons">
<item>
<layout class="QHBoxLayout" name="hlCapacity">
<item>
<widget class="QLabel" name="labCapacity">
<property name="text">
<string>Capacity: %1</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="cmdQuestion">
<property name="text">
<string>?</string>
</property>
</widget>
</item>
<item>
<spacer name="hsCapacity">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="hlButtons">
<item>
<widget class="QPushButton" name="cmdReplace">
<property name="text">
<string>&amp;Import...</string>
</property>
</widget>
</item>
<item>
<spacer name="hsButtons">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cmdSave">
<property name="text">
<string>&amp;Overwrite</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cmdClose">
<property name="text">
<string>&amp;Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2017-2018 Syping
* 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
@ -16,13 +16,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "ImportDialog.h"
#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 <QStringBuilder>
#include <QInputDialog>
#include <QImageReader>
#include <QColorDialog>
#include <QFileDialog>
@ -37,42 +40,45 @@
#include <QRgb>
// IMAGES VALUES
#define snapmaticResolutionW 960
#define snapmaticResolutionH 536
#define snapmaticAvatarResolution 470
#define snapmaticAvatarPlacementW 145
#define snapmaticAvatarPlacementH 66
ImportDialog::ImportDialog(QWidget *parent) :
QDialog(parent),
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(":/img/avatarareaimport.png");
avatarAreaImage = QImage(AppEnv::getImagesFolder() % "/avatarareaimport.png");
selectedColour = QColor::fromRgb(0, 0, 0, 255);
// Set Icon for OK Button
if (QIcon::hasThemeIcon("dialog-ok"))
{
if (QIcon::hasThemeIcon("dialog-ok")) {
ui->cmdOK->setIcon(QIcon::fromTheme("dialog-ok"));
}
else if (QIcon::hasThemeIcon("gtk-ok"))
{
else if (QIcon::hasThemeIcon("gtk-ok")) {
ui->cmdOK->setIcon(QIcon::fromTheme("gtk-ok"));
}
// Set Icon for Cancel Button
if (QIcon::hasThemeIcon("dialog-cancel"))
{
if (QIcon::hasThemeIcon("dialog-cancel")) {
ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel"));
}
else if (QIcon::hasThemeIcon("gtk-cancel"))
{
else if (QIcon::hasThemeIcon("gtk-cancel")) {
ui->cmdCancel->setIcon(QIcon::fromTheme("gtk-cancel"));
}
@ -81,6 +87,21 @@ ImportDialog::ImportDialog(QWidget *parent) :
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
@ -91,19 +112,29 @@ ImportDialog::ImportDialog(QWidget *parent) :
#ifndef Q_OS_MAC
ui->vlButtom->setContentsMargins(9 * screenRatio, 6 * screenRatio, 9 * screenRatio, 9 * screenRatio);
#else
if (QApplication::style()->objectName() == "macintosh")
{
#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
{
else {
ui->vlButtom->setContentsMargins(9 * screenRatio, 6 * screenRatio, 9 * screenRatio, 9 * screenRatio);
}
#endif
setMaximumSize(sizeHint());
setMinimumSize(sizeHint());
setFixedSize(sizeHint());
// 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()
@ -113,134 +144,591 @@ ImportDialog::~ImportDialog()
void ImportDialog::processImage()
{
if (workImage.isNull()) return;
if (workImage.isNull())
return;
QImage snapmaticImage = workImage;
QPixmap snapmaticPixmap(snapmaticResolutionW, snapmaticResolutionH);
QPixmap snapmaticPixmap(snapmaticResolution);
snapmaticPixmap.fill(selectedColour);
QPainter snapmaticPainter(&snapmaticPixmap);
if (!backImage.isNull())
{
if (!ui->cbStretch->isChecked())
{
qreal screenRatioPR = AppEnv::screenRatioPR();
if (!backImage.isNull()) {
if (!ui->cbStretch->isChecked()) {
int diffWidth = 0;
int diffHeight = 0;
if (backImage.width() != snapmaticResolutionW)
{
diffWidth = snapmaticResolutionW - backImage.width();
if (backImage.width() != snapmaticResolution.width()) {
diffWidth = snapmaticResolution.width() - backImage.width();
diffWidth = diffWidth / 2;
}
else if (backImage.height() != snapmaticResolutionH)
{
diffHeight = snapmaticResolutionH - backImage.height();
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(snapmaticResolutionW, snapmaticResolutionH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
else {
snapmaticPainter.drawImage(0, 0, QImage(backImage).scaled(snapmaticResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
if (ui->cbAvatar->isChecked() && ui->cbForceAvatarColour->isChecked())
{
if (ui->cbAvatar->isChecked() && ui->cbForceAvatarColour->isChecked()) {
snapmaticPainter.fillRect(snapmaticAvatarPlacementW, snapmaticAvatarPlacementH, snapmaticAvatarResolution, snapmaticAvatarResolution, selectedColour);
}
}
if (insideAvatarZone)
{
if (insideAvatarZone) {
// Avatar mode
int diffWidth = 0;
int diffHeight = 0;
if (!ui->cbIgnore->isChecked())
{
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())
{
if (snapmaticImage.width() > snapmaticImage.height()) {
diffHeight = snapmaticAvatarResolution - snapmaticImage.height();
diffHeight = diffHeight / 2;
}
else if (snapmaticImage.width() < snapmaticImage.height())
{
else if (snapmaticImage.width() < snapmaticImage.height()) {
diffWidth = snapmaticAvatarResolution - snapmaticImage.width();
diffWidth = diffWidth / 2;
}
}
else
{
snapmaticImage = snapmaticImage.scaled(snapmaticAvatarResolution, snapmaticAvatarResolution, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
snapmaticPainter.drawImage(snapmaticAvatarPlacementW + diffWidth, snapmaticAvatarPlacementH + diffHeight, snapmaticImage);
if (ui->cbWatermark->isChecked())
processWatermark(&snapmaticPainter);
imageTitle = tr("Custom Avatar", "Custom Avatar Description in SC, don't use Special Character!");
}
else
{
else {
// Picture mode
int diffWidth = 0;
int diffHeight = 0;
if (!ui->cbIgnore->isChecked())
{
snapmaticImage = snapmaticImage.scaled(snapmaticResolutionW, snapmaticResolutionH, Qt::KeepAspectRatio, Qt::SmoothTransformation);
if (snapmaticImage.width() != snapmaticResolutionW)
{
diffWidth = snapmaticResolutionW - snapmaticImage.width();
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() != snapmaticResolutionH)
{
diffHeight = snapmaticResolutionH - snapmaticImage.height();
else if (snapmaticImage.height() != snapmaticResolution.height()) {
diffHeight = snapmaticResolution.height() - snapmaticImage.height();
diffHeight = diffHeight / 2;
}
}
else
{
snapmaticImage = snapmaticImage.scaled(snapmaticResolutionW, snapmaticResolutionH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
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();
ui->labPicture->setPixmap(snapmaticPixmap.scaled(snapmaticResolutionLW, snapmaticResolutionLH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
#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<QImage>(settings.value("BackgroundImage", QImage()));
selectedColour = qvariant_cast<QColor>(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: <span style=\"color: %1\">%1</span>").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<QUrl> 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()
{
return newImage;
if (ui->cbImportAsIs->isChecked()) {
return origImage;
}
else {
return newImage;
}
}
void ImportDialog::setImage(QImage *image_)
{
origImage = *image_;
workImage = QImage();
if (image_->width() == image_->height())
{
insideAvatarZone = true;
ui->cbAvatar->setChecked(true);
if (image_->height() > snapmaticResolutionH)
{
workImage = image_->scaled(snapmaticResolutionH, snapmaticResolutionH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
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
{
else {
workImage = *image_;
delete image_;
}
}
else if (image_->width() > snapmaticResolutionW && image_->width() > image_->height())
{
workImage = image_->scaledToWidth(snapmaticResolutionW, Qt::SmoothTransformation);
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() > snapmaticResolutionH && image_->height() > image_->width())
{
workImage = image_->scaledToHeight(snapmaticResolutionH, Qt::SmoothTransformation);
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
{
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()
@ -248,29 +736,53 @@ bool ImportDialog::isImportAgreed()
return importAgreed;
}
bool ImportDialog::isUnlimitedBuffer()
{
return ui->cbUnlimited->isChecked();
}
bool ImportDialog::areSettingsLocked()
{
return settingsLocked;
}
QString ImportDialog::getImageTitle()
{
return imageTitle;
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)
{
Q_UNUSED(checked)
ui->cbBorderless->setDisabled(checked);
processImage();
}
void ImportDialog::on_cbAvatar_toggled(bool checked)
{
if (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))
{
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();
}
@ -287,11 +799,9 @@ void ImportDialog::on_cmdOK_clicked()
void ImportDialog::on_labPicture_labelPainted()
{
if (insideAvatarZone)
{
if (insideAvatarZone) {
QImage avatarAreaFinalImage(avatarAreaImage);
if (selectedColour.lightness() > 127)
{
if (selectedColour.lightness() > 127) {
avatarAreaFinalImage.setColor(1, qRgb(0, 0, 0));
}
QPainter labelPainter(ui->labPicture);
@ -303,8 +813,7 @@ void ImportDialog::on_labPicture_labelPainted()
void ImportDialog::on_cmdColourChange_clicked()
{
QColor newSelectedColour = QColorDialog::getColor(selectedColour, this, tr("Select Colour..."));
if (newSelectedColour.isValid())
{
if (newSelectedColour.isValid()) {
selectedColour = newSelectedColour;
ui->labColour->setText(tr("Background Colour: <span style=\"color: %1\">%1</span>").arg(selectedColour.name()));
processImage();
@ -330,8 +839,7 @@ fileDialogPreOpen:
// Getting readable Image formats
QString imageFormatsStr = " ";
for (QByteArray imageFormat : QImageReader::supportedImageFormats())
{
for (const QByteArray &imageFormat : QImageReader::supportedImageFormats()) {
imageFormatsStr += QString("*.") % QString::fromUtf8(imageFormat).toLower() % " ";
}
@ -346,17 +854,14 @@ fileDialogPreOpen:
fileDialog.setDirectory(settings.value("Directory", StandardPaths::documentsLocation()).toString());
fileDialog.restoreGeometry(settings.value("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);
QString selectedFileName = QFileInfo(selectedFile).fileName();
QFile snapmaticFile(selectedFile);
if (!snapmaticFile.open(QFile::ReadOnly))
{
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;
}
@ -364,12 +869,11 @@ fileDialogPreOpen:
QImageReader snapmaticImageReader;
snapmaticImageReader.setDecideFormatFromContent(true);
snapmaticImageReader.setDevice(&snapmaticFile);
if (!snapmaticImageReader.read(&importImage))
{
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(snapmaticResolutionW, snapmaticResolutionH, Qt::KeepAspectRatio, Qt::SmoothTransformation);
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);
@ -402,3 +906,69 @@ 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();
}
}
}

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2017 Syping
* 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
@ -20,6 +20,7 @@
#define IMPORTDIALOG_H
#include <QDialog>
#include <QMenu>
namespace Ui {
class ImportDialog;
@ -30,15 +31,24 @@ class ImportDialog : public QDialog
Q_OBJECT
public:
explicit ImportDialog(QWidget *parent = 0);
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();
@ -49,20 +59,35 @@ private slots:
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

View File

@ -7,13 +7,13 @@
<x>0</x>
<y>0</y>
<width>516</width>
<height>425</height>
<height>673</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>516</width>
<height>425</height>
<height>512</height>
</size>
</property>
<property name="windowTitle">
@ -85,30 +85,32 @@
</property>
<layout class="QVBoxLayout" name="vlSettings">
<item>
<layout class="QHBoxLayout" name="hlCheckboxes">
<item>
<widget class="QCheckBox" name="cbAvatar">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<layout class="QGridLayout" name="glPicture">
<item row="0" column="1">
<widget class="QCheckBox" name="cbIgnore">
<property name="text">
<string>Ignore Aspect Ratio</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="cbAvatar">
<property name="text">
<string>Avatar</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbIgnore">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item row="1" column="0">
<widget class="QCheckBox" name="cbWatermark">
<property name="text">
<string>Ignore Aspect Ratio</string>
<string>Watermark</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="cbBorderless">
<property name="text">
<string>Crop to Aspect Ratio</string>
</property>
</widget>
</item>
@ -124,8 +126,8 @@
</property>
<layout class="QVBoxLayout" name="vlBackground">
<item>
<layout class="QHBoxLayout" name="hlColor">
<item>
<layout class="QGridLayout" name="glBackground">
<item row="0" column="0">
<layout class="QHBoxLayout" name="hlColourManage">
<item>
<widget class="QLabel" name="labColour">
@ -153,8 +155,11 @@
</property>
<item>
<widget class="QToolButton" name="cmdColourChange">
<property name="toolTip">
<string>Select background colour</string>
</property>
<property name="text">
<string>...</string>
<string notr="true">...</string>
</property>
</widget>
</item>
@ -175,7 +180,24 @@
</item>
</layout>
</item>
<item>
<item row="2" column="1">
<widget class="QCheckBox" name="cbStretch">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Ignore Aspect Ratio</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="cbForceAvatarColour">
<property name="text">
<string>Force Colour in Avatar Zone</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="hlBackgroundManage">
<item>
<widget class="QLabel" name="labBackgroundImage">
@ -203,15 +225,21 @@
</property>
<item>
<widget class="QToolButton" name="cmdBackgroundChange">
<property name="toolTip">
<string>Select background image</string>
</property>
<property name="text">
<string>...</string>
<string notr="true">...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="cmdBackgroundWipe">
<property name="toolTip">
<string>Remove background image</string>
</property>
<property name="text">
<string>X</string>
<string notr="true">X</string>
</property>
</widget>
</item>
@ -234,25 +262,61 @@
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gbAdvanced">
<property name="title">
<string>Advanced</string>
</property>
<layout class="QVBoxLayout" name="vlAdvanced">
<item>
<layout class="QHBoxLayout" name="hlBackground">
<item>
<widget class="QCheckBox" name="cbForceAvatarColour">
<layout class="QGridLayout" name="glAdvanced">
<item row="1" column="0">
<widget class="QCheckBox" name="cbUnlimited">
<property name="toolTip">
<string>Avoid compression and expand buffer instead, improves picture quality, but may break Snapmatic</string>
</property>
<property name="text">
<string>Force Colour in Avatar Zone</string>
<string>Unlimited Buffer</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbStretch">
<property name="enabled">
<bool>true</bool>
<item row="1" column="1">
<widget class="QCheckBox" name="cbImportAsIs">
<property name="toolTip">
<string>Import as-is, don't change the picture at all, guaranteed to break Snapmatic unless you know what you doing</string>
</property>
<property name="text">
<string>Ignore Aspect Ratio</string>
<string>Import as-is</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="hlResolution">
<item>
<widget class="QLabel" name="labResolution">
<property name="text">
<string>Resolution:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbResolution">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Snapmatic resolution</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
@ -273,6 +337,19 @@
</item>
<item>
<layout class="QHBoxLayout" name="hlButtons">
<item>
<widget class="QPushButton" name="cmdOptions">
<property name="toolTip">
<string>Import options</string>
</property>
<property name="text">
<string>&amp;Options</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="hsButtons">
<property name="orientation">

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2017-2018 Syping
* 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
@ -20,13 +20,18 @@
#include "ui_JsonEditorDialog.h"
#include "SnapmaticEditor.h"
#include "AppEnv.h"
#include "config.h"
#include <QStringBuilder>
#include <QJsonDocument>
#include <QJsonObject>
#include <QMessageBox>
#if QT_VERSION >= 0x050200
#include <QFontDatabase>
#include <QDebug>
#endif
#ifdef GTA5SYNC_TELEMETRY
#include "TelemetryClass.h"
#endif
JsonEditorDialog::JsonEditorDialog(SnapmaticPicture *picture, QWidget *parent) :
@ -34,28 +39,30 @@ JsonEditorDialog::JsonEditorDialog(SnapmaticPicture *picture, QWidget *parent) :
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"))
{
if (QIcon::hasThemeIcon("dialog-close")) {
ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close"));
}
else if (QIcon::hasThemeIcon("gtk-close"))
{
else if (QIcon::hasThemeIcon("gtk-close")) {
ui->cmdClose->setIcon(QIcon::fromTheme("gtk-close"));
}
// Set Icon for Save Button
if (QIcon::hasThemeIcon("document-save"))
{
if (QIcon::hasThemeIcon("document-save")) {
ui->cmdSave->setIcon(QIcon::fromTheme("document-save"));
}
else if (QIcon::hasThemeIcon("gtk-save"))
{
else if (QIcon::hasThemeIcon("gtk-save")) {
ui->cmdSave->setIcon(QIcon::fromTheme("gtk-save"));
}
@ -63,9 +70,18 @@ JsonEditorDialog::JsonEditorDialog(SnapmaticPicture *picture, QWidget *parent) :
#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);}");
@ -83,10 +99,10 @@ JsonEditorDialog::JsonEditorDialog(SnapmaticPicture *picture, QWidget *parent) :
ui->hlButtons->setContentsMargins(9 * screenRatio, 0, 9 * screenRatio, 0);
ui->vlInterface->setContentsMargins(0, 0, 0, 9 * screenRatio);
#endif
if (screenRatio > 1)
{
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);
}
@ -104,28 +120,22 @@ void JsonEditorDialog::closeEvent(QCloseEvent *ev)
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)
{
if (newCode != originalCode) {
QMessageBox::StandardButton button = QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("<h4>Unsaved changes detected</h4>You want to save the JSON content before you quit?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Cancel);
if (button == QMessageBox::Yes)
{
if (saveJsonContent())
{
if (button == QMessageBox::Yes) {
if (saveJsonContent()) {
ev->accept();
}
else
{
else {
ev->ignore();
}
return;
}
else if (button == QMessageBox::No)
{
else if (button == QMessageBox::No) {
ev->accept();
return;
}
else
{
else {
ev->ignore();
return;
}
@ -136,59 +146,69 @@ bool JsonEditorDialog::saveJsonContent()
{
QString jsonPatched = QString(ui->txtJSON->toPlainText()).replace("\t", " ");
QJsonDocument jsonNew = QJsonDocument::fromJson(jsonPatched.toUtf8());
if (!jsonNew.isEmpty())
{
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)
{
if (newCode != originalCode) {
QString currentFilePath = smpic->getPictureFilePath();
QString originalFilePath = smpic->getOriginalPictureFilePath();
QString backupFileName = originalFilePath % ".bak";
if (!QFile::exists(backupFileName))
{
if (!QFile::exists(backupFileName)) {
QFile::copy(currentFilePath, backupFileName);
}
smpic->setJsonStr(newCode, true);
if (!smpic->isJsonOk())
{
if (!smpic->isJsonOk()) {
QString lastStep = smpic->getLastStep(false);
QString readableError;
if (lastStep.contains("JSONINCOMPLETE") && lastStep.contains("JSONERROR"))
{
if (lastStep.contains("JSONINCOMPLETE") && lastStep.contains("JSONERROR")) {
readableError = SnapmaticPicture::tr("JSON is incomplete and malformed");
}
else if (lastStep.contains("JSONINCOMPLETE"))
{
else if (lastStep.contains("JSONINCOMPLETE")) {
readableError = SnapmaticPicture::tr("JSON is incomplete");
}
else if (lastStep.contains("JSONERROR"))
{
else if (lastStep.contains("JSONERROR")) {
readableError = SnapmaticPicture::tr("JSON is malformed");
}
else
{
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))
{
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
{
else {
QMessageBox::warning(this, SnapmaticEditor::tr("Snapmatic Properties"), SnapmaticEditor::tr("Patching of Snapmatic Properties failed because of JSON Error"));
return false;
}
@ -196,13 +216,11 @@ bool JsonEditorDialog::saveJsonContent()
void JsonEditorDialog::on_cmdClose_clicked()
{
this->close();
close();
}
void JsonEditorDialog::on_cmdSave_clicked()
{
if (saveJsonContent())
{
this->close();
}
close();
}

View File

@ -112,6 +112,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Apply changes</string>
</property>
<property name="text">
<string>&amp;Save</string>
</property>
@ -125,6 +128,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Discard changes</string>
</property>
<property name="text">
<string>&amp;Close</string>
</property>

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2017 Syping
* 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
@ -20,8 +20,8 @@
#include "ui_MapLocationDialog.h"
#include "IconLoader.h"
#include "AppEnv.h"
#include <QStringBuilder>
#include <QPainter>
#include <QDebug>
#include <QStyle>
MapLocationDialog::MapLocationDialog(double x, double y, QWidget *parent) :
@ -29,7 +29,11 @@ MapLocationDialog::MapLocationDialog(double x, double y, QWidget *parent) :
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);
@ -45,12 +49,10 @@ MapLocationDialog::MapLocationDialog(double x, double y, QWidget *parent) :
ui->vlMapDialog->setSpacing(widgetMargin);
setMinimumSize(500 * screenRatio, 600 * screenRatio);
setMaximumSize(500 * screenRatio, 600 * screenRatio);
setFixedSize(500 * screenRatio, 600 * screenRatio);
setMouseTracking(true);
zoomPercent = 100;
changeMode = false;
propUpdate = false;
drawPointOnMap(xpos_old, ypos_old);
}
MapLocationDialog::~MapLocationDialog()
@ -60,36 +62,224 @@ MapLocationDialog::~MapLocationDialog()
void MapLocationDialog::drawPointOnMap(double xpos_d, double ypos_d)
{
qreal screenRatio = AppEnv::screenRatio();
int pointMakerSize = 8 * screenRatio;
QPixmap pointMakerPixmap = IconLoader::loadingPointmakerIcon().pixmap(QSize(pointMakerSize, pointMakerSize));
QSize mapPixelSize = size();
int pointMakerHalfSize = pointMakerSize / 2;
long xpos_ms = qRound(xpos_d);
long ypos_ms = qRound(ypos_d);
double xpos_ma = xpos_ms + 4000;
double ypos_ma = ypos_ms + 4000;
double xrat = (double)mapPixelSize.width() / 10000;
double yrat = (double)mapPixelSize.height() / 12000;
long xpos_mp = qRound(xpos_ma * xrat);
long ypos_mp = qRound(ypos_ma * yrat);
long xpos_pr = xpos_mp - pointMakerHalfSize;
long ypos_pr = ypos_mp + pointMakerHalfSize;
QPixmap mapPixmap(mapPixelSize);
QPainter mapPainter(&mapPixmap);
mapPainter.drawPixmap(0, 0, mapPixelSize.width(), mapPixelSize.height(), QPixmap(":/img/mappreview.jpg").scaled(mapPixelSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
mapPainter.drawPixmap(xpos_pr, mapPixelSize.height() - ypos_pr, pointMakerSize, pointMakerSize, pointMakerPixmap);
mapPainter.end();
QPalette backgroundPalette;
backgroundPalette.setBrush(backgroundRole(), QBrush(mapPixmap));
setPalette(backgroundPalette);
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;
ui->labPos->setText(tr("X: %1\nY: %2", "X and Y position").arg(QString::number(xpos_d), QString::number(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<double>(zoomPercent) / 100;
const QSize mapImageSize = mapImage.size();
const QPointF mapImageMid(static_cast<double>(mapImageSize.width()) / 2, static_cast<double>(mapImageSize.height()) / 2);
const QSizeF srcImageSize(static_cast<double>(mapImageSize.width()) / zoomLevel , static_cast<double>(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()
@ -111,58 +301,14 @@ void MapLocationDialog::on_cmdDone_clicked()
{
ui->cmdDone->setVisible(false);
ui->cmdChange->setVisible(true);
if (xpos_new != xpos_old || ypos_new != ypos_old)
{
if (xpos_new != xpos_old || ypos_new != ypos_old) {
ui->cmdApply->setVisible(true);
ui->cmdRevert->setVisible(true);
}
setCursor(Qt::ArrowCursor);
changeMode = false;
}
void MapLocationDialog::updatePosFromEvent(int x, int y)
{
QSize mapPixelSize = size();
int xpos_ad = x;
int ypos_ad = mapPixelSize.height() - y;
double xrat = 10000 / (double)mapPixelSize.width();
double yrat = 12000 / (double)mapPixelSize.height();
double xpos_rv = xrat * xpos_ad;
double ypos_rv = yrat * ypos_ad;
double xpos_fp = xpos_rv - 4000;
double ypos_fp = ypos_rv - 4000;
drawPointOnMap(xpos_fp, ypos_fp);
}
void MapLocationDialog::mouseMoveEvent(QMouseEvent *ev)
{
if (!changeMode) { ev->ignore(); }
else if (ev->buttons() & Qt::LeftButton)
{
updatePosFromEvent(ev->x(), ev->y());
ev->accept();
}
else
{
ev->ignore();
}
}
void MapLocationDialog::mouseReleaseEvent(QMouseEvent *ev)
{
if (!changeMode) { ev->ignore(); }
else if (ev->button() == Qt::LeftButton)
{
updatePosFromEvent(ev->x(), ev->y());
ev->accept();
}
else
{
ev->ignore();
}
}
void MapLocationDialog::on_cmdApply_clicked()
{
propUpdate = true;

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2017 Syping
* 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
@ -33,30 +33,40 @@ class MapLocationDialog : public QDialog
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_cmdDone_clicked();
void on_cmdApply_clicked();
void on_cmdDone_clicked();
void on_cmdClose_clicked();
void on_cmdChange_clicked();
void on_cmdRevert_clicked();
void updatePosFromEvent(int x, int y);
void on_cmdClose_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;
};

View File

@ -25,6 +25,11 @@
<property name="windowTitle">
<string>Snapmatic Map Viewer</string>
</property>
<property name="styleSheet">
<string notr="true">QDialog#MapLocationDialog {
background-color: transparent;
}</string>
</property>
<layout class="QVBoxLayout" name="vlMapPreview">
<property name="spacing">
<number>0</number>
@ -134,6 +139,9 @@ color: rgb(255, 255, 255);
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Close viewer</string>
</property>
<property name="text">
<string>&amp;Close</string>
</property>
@ -160,6 +168,9 @@ color: rgb(255, 255, 255);
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Apply new position</string>
</property>
<property name="text">
<string>&amp;Apply</string>
</property>
@ -173,6 +184,9 @@ color: rgb(255, 255, 255);
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Revert old position</string>
</property>
<property name="text">
<string>&amp;Revert</string>
</property>
@ -186,8 +200,11 @@ color: rgb(255, 255, 255);
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Select new position</string>
</property>
<property name="text">
<string>&amp;Set</string>
<string>&amp;Select</string>
</property>
<property name="autoDefault">
<bool>false</bool>
@ -199,6 +216,9 @@ color: rgb(255, 255, 255);
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Quit select position</string>
</property>
<property name="text">
<string>&amp;Done</string>
</property>

111
MessageThread.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "TranslationClass.h"
#include "MessageThread.h"
#include "AppEnv.h"
#include "config.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QEventLoop>
#include <QUrlQuery>
#include <QTimer>
#include <QDebug>
#include <QUrl>
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<QString,QString> 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();
}

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -16,38 +16,33 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef IMAGEEDITORDIALOG_H
#define IMAGEEDITORDIALOG_H
#ifndef MESSAGETHREAD_H
#define MESSAGETHREAD_H
#include "SnapmaticPicture.h"
#include <QDialog>
#include <QJsonObject>
#include <QObject>
#include <QThread>
namespace Ui {
class ImageEditorDialog;
}
class ImageEditorDialog : public QDialog
class MessageThread : public QThread
{
Q_OBJECT
public:
explicit ImageEditorDialog(SnapmaticPicture *picture, QString profileName, QWidget *parent = 0);
~ImageEditorDialog();
explicit MessageThread(uint cacheId, QObject *parent = 0);
private slots:
void on_cmdClose_clicked();
void on_cmdReplace_clicked();
void on_cmdSave_clicked();
void on_cmdQuestion_clicked();
public slots:
void terminateThread();
private:
SnapmaticPicture *smpic;
QString profileName;
Ui::ImageEditorDialog *ui;
int snapmaticResolutionLW;
int snapmaticResolutionLH;
bool imageIsChanged;
QImage pictureCache;
bool threadRunning;
uint cacheId;
protected:
void run();
signals:
void messagesArrived(const QJsonObject &messageObject);
void updateCacheId(uint cacheId);
void threadTerminated();
};
#endif // IMAGEEDITORDIALOG_H
#endif // MESSAGETHREAD_H

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2018 Syping
* 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
@ -21,15 +21,16 @@
#include "TranslationClass.h"
#include "StandardPaths.h"
#include "UserInterface.h"
#include "wrapper.h"
#include "AppEnv.h"
#include "config.h"
#include <QStringBuilder>
#include <QDesktopWidget>
#include <QJsonDocument>
#include <QStyleFactory>
#include <QApplication>
#include <QJsonObject>
#include <QFileDialog>
#include <QFontDialog>
#include <QMessageBox>
#include <QStringList>
#include <QClipboard>
@ -40,6 +41,12 @@
#include <QList>
#include <QDir>
#if QT_VERSION >= 0x050000
#include <QScreen>
#else
#include <QDesktopWidget>
#endif
#ifdef GTA5SYNC_TELEMETRY
#include "TelemetryClass.h"
#endif
@ -49,19 +56,31 @@ OptionsDialog::OptionsDialog(ProfileDatabase *profileDB, QWidget *parent) :
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 = QSize(960, 536);
defExportSize = SnapmaticPicture::getSnapmaticResolution();
cusExportSize = defExportSize;
defaultQuality = 100;
customQuality = 100;
@ -74,25 +93,26 @@ OptionsDialog::OptionsDialog(ProfileDatabase *profileDB, QWidget *parent) :
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"))
{
if (QIcon::hasThemeIcon("dialog-ok")) {
ui->cmdOK->setIcon(QIcon::fromTheme("dialog-ok"));
}
else if (QIcon::hasThemeIcon("gtk-ok"))
{
else if (QIcon::hasThemeIcon("gtk-ok")) {
ui->cmdOK->setIcon(QIcon::fromTheme("gtk-ok"));
}
// Set Icon for Cancel Button
if (QIcon::hasThemeIcon("dialog-cancel"))
{
if (QIcon::hasThemeIcon("dialog-cancel")) {
ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel"));
}
else if (QIcon::hasThemeIcon("gtk-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();
@ -102,6 +122,7 @@ OptionsDialog::OptionsDialog(ProfileDatabase *profileDB, QWidget *parent) :
setupInterfaceSettings();
setupStatisticsSettings();
setupSnapmaticPictureViewer();
setupWindowsGameSettings();
#ifndef Q_QS_ANDROID
// DPI calculation
@ -109,7 +130,9 @@ OptionsDialog::OptionsDialog(ProfileDatabase *profileDB, QWidget *parent) :
resize(435 * screenRatio, 405 * screenRatio);
#endif
this->setWindowTitle(windowTitle().arg(GTA5SYNC_APPSTR));
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()
@ -122,24 +145,28 @@ OptionsDialog::~OptionsDialog()
void OptionsDialog::setupTreeWidget()
{
for (QString playerIDStr : profileDB->getPlayers())
{
bool ok;
int playerID = playerIDStr.toInt(&ok);
if (ok)
{
QString playerName = profileDB->getPlayerName(playerID);
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 += playerIDStr;
playerTreeViewList += playerName;
QStringList playerTreeViewList;
playerTreeViewList += *it;
playerTreeViewList += playerName;
QTreeWidgetItem *playerItem = new QTreeWidgetItem(playerTreeViewList);
ui->twPlayers->addTopLevelItem(playerItem);
playerItems += playerItem;
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));
}
ui->twPlayers->sortItems(1, Qt::AscendingOrder);
}
void OptionsDialog::setupLanguageBox()
@ -149,9 +176,19 @@ void OptionsDialog::setupLanguageBox()
currentAreaLanguage = settings->value("AreaLanguage", "Auto").toString();
settings->endGroup();
QString cbSysStr = tr("%1 (Next Closest Language)", "First language a person can talk with a different person/application. \"Native\" or \"Not Native\".").arg(tr("System",
"System in context of System default"));
QString cbAutoStr = tr("%1 (Closest to Interface)", "Next closest language compared to the Interface").arg(tr("Auto", "Automatic language choice."));
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");
@ -164,15 +201,18 @@ void OptionsDialog::setupLanguageBox()
availableLanguages.removeDuplicates();
availableLanguages.sort();
for (QString lang : availableLanguages)
{
for (const QString &lang : qAsConst(availableLanguages)) {
QLocale langLocale(lang);
QString cbLangStr = langLocale.nativeLanguageName() % " (" % langLocale.nativeCountryName() % ") [" % lang % "]";
QString langIconStr = "flag-" % TranslationClass::getCountryCode(langLocale);
const QString cbLangStr = langLocale.nativeLanguageName() % " (" % langLocale.nativeCountryName() % ") [" % lang % "]";
const QString langIconPath = AppEnv::getImagesFolder() % "/flag-" % TranslationClass::getCountryCode(langLocale) % ".png";
ui->cbLanguage->addItem(QIcon::fromTheme(langIconStr), cbLangStr, lang);
if (currentLanguage == lang)
{
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
@ -183,7 +223,8 @@ void OptionsDialog::setupLanguageBox()
}
QString aCurrentLanguage = QString("en_GB");
if (Translator->isLanguageLoaded()) { aCurrentLanguage = Translator->getCurrentLanguage(); }
if (Translator->isLanguageLoaded())
aCurrentLanguage = Translator->getCurrentLanguage();
QLocale currentLocale = QLocale(aCurrentLanguage);
ui->labCurrentLanguage->setText(tr("Current: %1").arg(currentLocale.nativeLanguageName() % " (" % currentLocale.nativeCountryName() % ") [" % aCurrentLanguage % "]"));
@ -192,27 +233,21 @@ void OptionsDialog::setupLanguageBox()
availableLanguages.removeDuplicates();
availableLanguages.sort();
for (QString lang : availableLanguages)
{
for (const QString &lang : qAsConst(availableLanguages)) {
// correcting Language Location if possible
QString aLang = lang;
if (QFile::exists(":/global/global." % lang % ".loc"))
{
if (QFile::exists(":/global/global." % lang % ".loc")) {
QFile locFile(":/global/global." % lang % ".loc");
if (locFile.open(QFile::ReadOnly))
{
if (locFile.open(QFile::ReadOnly)) {
aLang = QString::fromUtf8(locFile.readLine()).trimmed();
locFile.close();
}
}
QLocale langLocale(aLang);
QString cbLangStr = langLocale.nativeLanguageName() % " (" % langLocale.nativeCountryName() % ") [" % aLang % "]";
QString langIconStr = "flag-" % TranslationClass::getCountryCode(langLocale);
ui->cbAreaLanguage->addItem(QIcon::fromTheme(langIconStr), cbLangStr, lang);
if (currentAreaLanguage == lang)
{
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
@ -223,12 +258,10 @@ void OptionsDialog::setupLanguageBox()
}
QString aCurrentAreaLanguage = Translator->getCurrentAreaLanguage();
if (QFile::exists(":/global/global." % aCurrentAreaLanguage % ".loc"))
{
if (QFile::exists(":/global/global." % aCurrentAreaLanguage % ".loc")) {
qDebug() << "locFile found";
QFile locFile(":/global/global." % aCurrentAreaLanguage % ".loc");
if (locFile.open(QFile::ReadOnly))
{
if (locFile.open(QFile::ReadOnly)) {
aCurrentAreaLanguage = QString::fromUtf8(locFile.readLine()).trimmed();
locFile.close();
}
@ -244,18 +277,26 @@ void OptionsDialog::setupRadioButtons()
contentMode = settings->value("ContentMode", 0).toInt(&contentModeOk);
settings->endGroup();
if (contentModeOk)
{
switch (contentMode)
{
if (contentModeOk) {
switch (contentMode) {
case 0:
ui->rbOpenWithSC->setChecked(true);
case 20:
ui->rbModern->setChecked(true);
ui->cbDoubleclick->setChecked(false);
break;
case 1:
ui->rbOpenWithDC->setChecked(true);
break;
case 2:
ui->rbSelectWithSC->setChecked(true);
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;
}
}
@ -264,55 +305,40 @@ void OptionsDialog::setupRadioButtons()
void OptionsDialog::setupInterfaceSettings()
{
settings->beginGroup("Startup");
bool alwaysUseMessageFont = settings->value("AlwaysUseMessageFont", false).toBool();
ui->cbAlwaysUseMessageFont->setChecked(alwaysUseMessageFont);
#ifdef GTA5SYNC_WIN
if (QSysInfo::windowsVersion() >= 0x0080)
{
ui->gbFont->setVisible(false);
ui->cbAlwaysUseMessageFont->setVisible(false);
}
#else
ui->gbFont->setVisible(false);
ui->cbAlwaysUseMessageFont->setVisible(false);
#endif
QString currentStyle = QApplication::style()->objectName();
QString appStyle = settings->value("AppStyle", currentStyle).toString();
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))
{
if (availableStyles.contains(appStyle, Qt::CaseInsensitive)) {
// use 'for' for select to be sure it's case insensitive
int currentIndex = 0;
for (QString currentStyleFF : availableStyles)
{
if (currentStyleFF.toLower() == appStyle.toLower())
{
for (const QString &currentStyleFF : availableStyles) {
if (currentStyleFF.toLower() == appStyle.toLower()) {
ui->cbStyleList->setCurrentIndex(currentIndex);
}
currentIndex++;
}
}
else
{
if (availableStyles.contains(currentStyle, Qt::CaseInsensitive))
{
else {
if (availableStyles.contains(currentStyle, Qt::CaseInsensitive)) {
int currentIndex = 0;
for (QString currentStyleFF : availableStyles)
{
if (currentStyleFF.toLower() == currentStyle.toLower())
{
for (const QString &currentStyleFF : availableStyles) {
if (currentStyleFF.toLower() == currentStyle.toLower()) {
ui->cbStyleList->setCurrentIndex(currentIndex);
}
currentIndex++;
}
}
}
if (customStyle)
{
ui->cbDefaultStyle->setChecked(false);
}
ui->cbDefaultStyle->setChecked(!customStyle);
ui->cbStyleList->setEnabled(customStyle);
const QFont currentFont = QApplication::font();
const QFont appFont = qvariant_cast<QFont>(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();
}
@ -332,26 +358,25 @@ void OptionsDialog::applySettings()
settings->setValue("Language", ui->cbLanguage->itemData(ui->cbLanguage->currentIndex()));
settings->setValue("AreaLanguage", ui->cbAreaLanguage->itemData(ui->cbAreaLanguage->currentIndex()));
#endif
#ifdef GTA5SYNC_WIN
#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 = 0;
if (ui->rbOpenWithSC->isChecked())
{
newContentMode = 0;
int newContentMode = 20;
if (ui->rbModern->isChecked()) {
newContentMode = 20;
}
else if (ui->rbOpenWithDC->isChecked())
{
newContentMode = 1;
else if (ui->rbClassic->isChecked()) {
newContentMode = 10;
}
else if (ui->rbSelectWithSC->isChecked())
{
newContentMode = 2;
if (ui->cbDoubleclick->isChecked()) {
newContentMode++;
}
settings->setValue("ContentMode", newContentMode);
#if QT_VERSION >= 0x050000
@ -362,18 +387,15 @@ void OptionsDialog::applySettings()
settings->endGroup();
settings->beginGroup("Pictures");
if (ui->cbPicCustomQuality->isChecked())
{
if (ui->cbPicCustomQuality->isChecked()) {
settings->setValue("CustomQuality", ui->hsPicQuality->value());
}
settings->setValue("CustomQualityEnabled", ui->cbPicCustomQuality->isChecked());
QString sizeMode = "Default";
if (ui->rbPicDesktopRes->isChecked())
{
if (ui->rbPicDesktopRes->isChecked()) {
sizeMode = "Desktop";
}
else if (ui->rbPicCustomRes->isChecked())
{
else if (ui->rbPicCustomRes->isChecked()) {
sizeMode = "Custom";
settings->setValue("CustomSize", QSize(ui->sbPicExportWidth->value(), ui->sbPicExportHeight->value()));
}
@ -389,27 +411,47 @@ void OptionsDialog::applySettings()
bool defaultStyle = ui->cbDefaultStyle->isChecked();
settings->beginGroup("Startup");
if (!defaultStyle)
{
if (!defaultStyle) {
QString newStyle = ui->cbStyleList->currentText();
settings->setValue("CustomStyle", true);
settings->setValue("AppStyle", newStyle);
QApplication::setStyle(QStyleFactory::create(newStyle));
}
else
{
else {
settings->setValue("CustomStyle", false);
}
settings->setValue("AlwaysUseMessageFont", ui->cbAlwaysUseMessageFont->isChecked());
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
@ -419,21 +461,19 @@ void OptionsDialog::applySettings()
bool languageChanged = ui->cbLanguage->itemData(ui->cbLanguage->currentIndex()).toString() != currentLanguage;
bool languageAreaChanged = ui->cbAreaLanguage->itemData(ui->cbLanguage->currentIndex()).toString() != currentAreaLanguage;
#endif
if (languageChanged)
{
if (languageChanged) {
Translator->unloadTranslation(qApp);
Translator->initUserLanguage();
Translator->loadTranslation(qApp);
}
else if (languageAreaChanged)
{
else if (languageAreaChanged) {
Translator->initUserLanguage();
}
settings->sync();
emit settingsApplied(newContentMode, languageChanged);
if ((forceCustomFolder && ui->txtFolder->text() != currentCFolder) || (forceCustomFolder != currentFFolder && forceCustomFolder))
{
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));
}
}
@ -450,11 +490,9 @@ void OptionsDialog::setupDefaultProfile()
void OptionsDialog::commitProfiles(const QStringList &profiles)
{
for (QString profile : profiles)
{
for (const QString &profile : profiles) {
ui->cbProfiles->addItem(tr("Profile: %1").arg(profile), profile);
if (defaultProfile == profile)
{
if (defaultProfile == profile) {
#if QT_VERSION >= 0x050000
ui->cbProfiles->setCurrentText(tr("Profile: %1").arg(profile));
#else
@ -492,8 +530,7 @@ void OptionsDialog::setupPictureSettings()
// Quality Settings
customQuality = settings->value("CustomQuality", defaultQuality).toInt();
if (customQuality < 1 || customQuality > 100)
{
if (customQuality < 1 || customQuality > 100) {
customQuality = 100;
}
ui->hsPicQuality->setValue(customQuality);
@ -501,42 +538,34 @@ void OptionsDialog::setupPictureSettings()
// Size Settings
cusExportSize = settings->value("CustomSize", defExportSize).toSize();
if (cusExportSize.width() > 3840)
{
if (cusExportSize.width() > 3840) {
cusExportSize.setWidth(3840);
}
else if (cusExportSize.height() > 2160)
{
else if (cusExportSize.height() > 2160) {
cusExportSize.setHeight(2160);
}
if (cusExportSize.width() < 1)
{
if (cusExportSize.width() < 1) {
cusExportSize.setWidth(1);
}
else if (cusExportSize.height() < 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")
{
if (sizeMode == "Desktop") {
ui->rbPicDesktopRes->setChecked(true);
}
else if (sizeMode == "Custom")
{
else if (sizeMode == "Custom") {
ui->rbPicCustomRes->setChecked(true);
}
else
{
else {
ui->rbPicDefaultRes->setChecked(true);
}
aspectRatio = (Qt::AspectRatioMode)settings->value("AspectRatio", Qt::KeepAspectRatio).toInt();
if (aspectRatio == Qt::IgnoreAspectRatio)
{
if (aspectRatio == Qt::IgnoreAspectRatio) {
ui->cbIgnoreAspectRatio->setChecked(true);
}
@ -549,30 +578,20 @@ void OptionsDialog::setupStatisticsSettings()
ui->cbParticipateStats->setText(tr("Participate in %1 User Statistics").arg(GTA5SYNC_APPSTR));
ui->labUserStats->setText(QString("<a href=\"%2\">%1</a>").arg(tr("View %1 User Statistics Online").arg(GTA5SYNC_APPSTR), TelemetryClass::getWebURL().toString()));
ui->gbUserFeedback->setVisible(false);
// settings->beginGroup("Startup");
// if (settings->value("IsFirstStart", true).toBool() == true)
// {
// ui->gbUserFeedback->setVisible(false);
// }
// settings->endGroup();
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())
{
if (Telemetry->isStateForced()) {
ui->cbParticipateStats->setEnabled(false);
}
if (Telemetry->isRegistered())
{
if (Telemetry->isRegistered()) {
ui->labParticipationID->setText(tr("Participation ID: %1").arg(Telemetry->getRegisteredID()));
}
else
{
else {
ui->labParticipationID->setText(tr("Participation ID: %1").arg(tr("Not registered")));
ui->cmdCopyStatsID->setVisible(false);
}
@ -581,14 +600,70 @@ void OptionsDialog::setupStatisticsSettings()
#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("<span style=\"color: green\">%1</span>").arg(tr("Yes"))));
ui->labSteamFound->setText(tr("Found: %1").arg(QString("<span style=\"color: red\">%1</span>").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("<span style=\"color: red\">%1</span>").arg(tr("No"))));
ui->labSteamFound->setText(tr("Found: %1").arg(QString("<span style=\"color: green\">%1</span>").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("<span style=\"color: green\">%1</span>").arg(tr("Yes"))));
ui->labSteamFound->setText(tr("Found: %1").arg(QString("<span style=\"color: green\">%1</span>").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)
{
if (checked) {
aspectRatio = Qt::IgnoreAspectRatio;
}
else
{
else {
aspectRatio = Qt::KeepAspectRatio;
}
}
@ -600,8 +675,7 @@ void OptionsDialog::setupCustomGTAFolder()
settings->beginGroup("dir");
currentCFolder = settings->value("dir", "").toString();
currentFFolder = settings->value("force", false).toBool();
if (currentCFolder == "" && ok)
{
if (currentCFolder == "" && ok) {
currentCFolder = defaultGameFolder;
}
ui->txtFolder->setText(currentCFolder);
@ -611,26 +685,26 @@ void OptionsDialog::setupCustomGTAFolder()
void OptionsDialog::setupSnapmaticPictureViewer()
{
#ifdef GTA5SYNC_WIN
#ifdef Q_OS_WIN
#if QT_VERSION >= 0x050200
settings->beginGroup("Interface");
ui->cbSnapmaticNavigationBar->setChecked(settings->value("NavigationBar", false).toBool());
ui->cbSnapmaticNavigationBar->setChecked(settings->value("NavigationBar", true).toBool());
settings->endGroup();
#else
ui->cbSnapmaticNavigationBar->setVisible(false);
ui->gbSnapmaticPictureViewer->setVisible(false);
#endif
#else
ui->cbSnapmaticNavigationBar->setVisible(false);
ui->gbSnapmaticPictureViewer->setVisible(false);
settings->beginGroup("Interface");
ui->cbSnapmaticNavigationBar->setChecked(settings->value("NavigationBar", true).toBool());
settings->endGroup();
#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())
{
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);
}
}
@ -638,25 +712,14 @@ void OptionsDialog::on_cmdExploreFolder_clicked()
void OptionsDialog::on_cbDefaultStyle_toggled(bool checked)
{
ui->cbStyleList->setDisabled(checked);
ui->labStyle->setDisabled(checked);
}
void OptionsDialog::on_cmdUserFeedbackSend_clicked()
void OptionsDialog::on_cbDefaultFont_toggled(bool checked)
{
#ifdef GTA5SYNC_TELEMETRY
if (ui->txtUserFeedback->toPlainText().length() < 1024 && ui->txtUserFeedback->toPlainText().length() >= 3)
{
QJsonDocument feedback;
QJsonObject feedbackObject;
feedbackObject["Message"] = ui->txtUserFeedback->toPlainText();
feedback.setObject(feedbackObject);
Telemetry->push(TelemetryCategory::UserFeedback, feedback);
ui->txtUserFeedback->setPlainText(QString());
}
else
{
QMessageBox::information(this, tr("User Feedback"), tr("A feedback message have to between 3-1024 characters long"));
}
#endif
ui->cbFont->setDisabled(checked);
ui->cmdFont->setDisabled(checked);
ui->labFont->setDisabled(checked);
}
void OptionsDialog::on_cmdCopyStatsID_clicked()
@ -665,3 +728,18 @@ void OptionsDialog::on_cmdCopyStatsID_clicked()
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);
}
}

View File

@ -47,8 +47,10 @@ private slots:
void on_cbIgnoreAspectRatio_toggled(bool checked);
void on_cmdExploreFolder_clicked();
void on_cbDefaultStyle_toggled(bool checked);
void on_cmdUserFeedbackSend_clicked();
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);
@ -80,6 +82,7 @@ private:
void setupInterfaceSettings();
void setupStatisticsSettings();
void setupSnapmaticPictureViewer();
void setupWindowsGameSettings();
void applySettings();
};

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>435</width>
<height>474</height>
<height>524</height>
</rect>
</property>
<property name="windowTitle">
@ -34,9 +34,9 @@
</property>
<layout class="QVBoxLayout" name="vlProfileContentMode">
<item>
<widget class="QRadioButton" name="rbOpenWithSC">
<widget class="QRadioButton" name="rbModern">
<property name="text">
<string>Open with Singleclick</string>
<string notr="true">%1 1.9+</string>
</property>
<property name="checked">
<bool>true</bool>
@ -44,16 +44,16 @@
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbOpenWithDC">
<widget class="QRadioButton" name="rbClassic">
<property name="text">
<string>Open with Doubleclick</string>
<string notr="true">%1 1.0-1.8</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbSelectWithSC">
<widget class="QCheckBox" name="cbDoubleclick">
<property name="text">
<string>Select with Singleclick</string>
<string>Open with Doubleclick</string>
</property>
</widget>
</item>
@ -382,6 +382,72 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tabGame">
<attribute name="title">
<string>Game</string>
</attribute>
<layout class="QVBoxLayout" name="vlGame">
<item>
<widget class="QGroupBox" name="gbSocialClub">
<property name="title">
<string>Social Club Version</string>
</property>
<layout class="QVBoxLayout" name="vlGameSocialClub">
<item>
<widget class="QLabel" name="labSocialClubFound">
<property name="text">
<string>Found: %1</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labSocialClubLanguage">
<property name="text">
<string>Language: %1</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gbSteam">
<property name="title">
<string>Steam Version</string>
</property>
<layout class="QVBoxLayout" name="vlGameSteam">
<item>
<widget class="QLabel" name="labSteamFound">
<property name="text">
<string>Found: %1</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labSteamLanguage">
<property name="text">
<string>Language: %1</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="vsGame">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabStats">
<attribute name="title">
<string>Feedback</string>
@ -420,11 +486,11 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="gbCategorys">
<widget class="QGroupBox" name="gbCategories">
<property name="title">
<string>Categories</string>
</property>
<layout class="QVBoxLayout" name="vlCategorys">
<layout class="QVBoxLayout" name="vlCategories">
<item>
<widget class="QCheckBox" name="cbGeneralStats">
<property name="enabled">
@ -458,6 +524,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbUsageData">
<property name="text">
<string>Personal Usage Data</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -466,74 +539,32 @@
<property name="title">
<string>Other</string>
</property>
<layout class="QHBoxLayout" name="hlOtherStats">
<layout class="QVBoxLayout" name="vlFeedbackOther">
<item>
<widget class="QLabel" name="labParticipationID">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Participation ID: %1</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cmdCopyStatsID">
<property name="text">
<string>&amp;Copy</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gbUserFeedback">
<property name="title">
<string>User Feedback</string>
</property>
<layout class="QVBoxLayout" name="vlUserFeedback">
<item>
<widget class="QPlainTextEdit" name="txtUserFeedback"/>
</item>
<item>
<layout class="QHBoxLayout" name="hlUserFeedbackButtons">
<layout class="QHBoxLayout" name="hlParticipation">
<item>
<widget class="QLabel" name="labUserFeedback">
<widget class="QLabel" name="labParticipationID">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Limit: 1 message/day</string>
<string>Participation ID: %1</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="hsUserFeedbackButtons">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cmdUserFeedbackSend">
<widget class="QPushButton" name="cmdCopyStatsID">
<property name="text">
<string>&amp;Send</string>
<string>&amp;Copy</string>
</property>
<property name="autoDefault">
<bool>false</bool>
@ -629,6 +660,9 @@
<layout class="QHBoxLayout" name="hlStyle">
<item>
<widget class="QLabel" name="labStyle">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Style:</string>
</property>
@ -659,12 +693,52 @@
</property>
<layout class="QVBoxLayout" name="vlFont">
<item>
<widget class="QCheckBox" name="cbAlwaysUseMessageFont">
<widget class="QCheckBox" name="cbDefaultFont">
<property name="text">
<string>Always use Message Font (Windows 2003 and earlier)</string>
<string>Use Default Font (Restart)</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="hlFont">
<item>
<widget class="QLabel" name="labFont">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Font:</string>
</property>
</widget>
</item>
<item>
<widget class="QFontComboBox" name="cbFont">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="cmdFont">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -29,14 +29,6 @@
#include <QEvent>
#include <QMenu>
#ifdef GTA5SYNC_WIN
#if QT_VERSION >= 0x050200
#ifdef GTA5SYNC_APV
#include <dwmapi.h>
#endif
#endif
#endif
namespace Ui {
class PictureDialog;
}
@ -56,17 +48,17 @@ public:
void setSnapmaticPicture(SnapmaticPicture *picture, int index);
void setSnapmaticPicture(SnapmaticPicture *picture);
void addPreviousNextButtons();
void stylizeDialog();
void styliseDialog();
bool isIndexed();
int getIndex();
~PictureDialog();
public slots:
void adaptDialogSize();
void crewNameUpdated();
void playerNameUpdated();
void dialogNextPictureRequested();
void dialogPreviousPictureRequested();
void adaptNewDialogSize(QSize newLabelSize);
void exportCustomContextMenuRequested(const QPoint &pos);
private slots:
@ -97,14 +89,11 @@ protected:
void closeEvent(QCloseEvent *ev);
bool eventFilter(QObject *obj, QEvent *ev);
void mousePressEvent(QMouseEvent *ev);
bool event(QEvent *event);
#ifdef GTA5SYNC_WIN
#if QT_VERSION >= 0x050200
#ifdef GTA5SYNC_APV
#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);
LRESULT HitTestNCA(HWND hWnd, LPARAM lParam);
void resizeEvent(QResizeEvent *event);
#endif
#endif
#endif
@ -136,8 +125,8 @@ private:
int avatarLocY;
int avatarSize;
QMenu *manageMenu;
#ifdef GTA5SYNC_WIN
#if QT_VERSION >= 0x050200
#ifdef Q_OS_WIN
#if QT_VERSION >= 0x050000
QPoint dragPosition;
bool dragStart;
#endif

View File

@ -48,19 +48,6 @@
</property>
</widget>
</item>
<item>
<spacer name="vsJSONUpper">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QFrame" name="jsonFrame">
<property name="frameShape">
@ -111,10 +98,10 @@
</sizepolicy>
</property>
<property name="text">
<string>&lt;span style=&quot; font-weight:600;&quot;&gt;Title: &lt;/span&gt;%6&lt;br/&gt;
&lt;span style=&quot; font-weight:600;&quot;&gt;Location: &lt;/span&gt;%7 (%1, %2, %3)&lt;br/&gt;
&lt;span style=&quot; font-weight:600;&quot;&gt;Players: &lt;/span&gt;%4 (Crew %5)&lt;br/&gt;
&lt;span style=&quot; font-weight:600;&quot;&gt;Created: &lt;/span&gt;%8</string>
<string>&lt;span style=&quot;font-weight:600&quot;&gt;Title: &lt;/span&gt;%6&lt;br/&gt;
&lt;span style=&quot;font-weight:600&quot;&gt;Location: &lt;/span&gt;%7 (%1, %2, %3)&lt;br/&gt;
&lt;span style=&quot;font-weight:600&quot;&gt;Players: &lt;/span&gt;%4 (Crew %5)&lt;br/&gt;
&lt;span style=&quot;font-weight:600&quot;&gt;Created: &lt;/span&gt;%8</string>
</property>
<property name="wordWrap">
<bool>true</bool>

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -17,26 +17,29 @@
*****************************************************************************/
#include "config.h"
#include "AppEnv.h"
#include "PictureExport.h"
#include "PictureDialog.h"
#include "StandardPaths.h"
#include "SidebarGenerator.h"
#include <QStringBuilder>
#include <QDesktopWidget>
#include <QApplication>
#include <QMessageBox>
#include <QFileDialog>
#include <QSettings>
#include <QRegExp>
#include <QDebug>
#if QT_VERSION < 0x050000
#include <QDesktopWidget>
#endif
#if QT_VERSION >= 0x050000
#include <QSaveFile>
#include <QScreen>
#endif
PictureExport::PictureExport()
{
}
void PictureExport::exportAsPicture(QWidget *parent, SnapmaticPicture *picture)
@ -47,30 +50,25 @@ void PictureExport::exportAsPicture(QWidget *parent, SnapmaticPicture *picture)
// Quality Settings
settings.beginGroup("Pictures");
int defaultQuality = 100;
QSize defExportSize = QSize(960, 536);
QSize defExportSize = SnapmaticPicture::getSnapmaticResolution();
int customQuality = settings.value("CustomQuality", defaultQuality).toInt();
if (customQuality < 1 || customQuality > 100)
{
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)
{
if (cusExportSize.width() > 3840) {
cusExportSize.setWidth(3840);
}
else if (cusExportSize.height() > 2160)
{
else if (cusExportSize.height() > 2160) {
cusExportSize.setHeight(2160);
}
if (cusExportSize.width() < 1)
{
if (cusExportSize.width() < 1) {
cusExportSize.setWidth(1);
}
else if (cusExportSize.height() < 1)
{
else if (cusExportSize.height() < 1) {
cusExportSize.setHeight(1);
}
QString sizeMode = settings.value("ExportSizeMode", "Default").toString();
@ -109,59 +107,55 @@ fileDialogPreSave: //Work?
QString newPictureFileName = getPictureFileName(picture) % defaultExportFormat;
fileDialog.selectFile(newPictureFileName);
if (fileDialog.exec())
{
if (fileDialog.exec()) {
QStringList selectedFiles = fileDialog.selectedFiles();
if (selectedFiles.length() == 1)
{
if (selectedFiles.length() == 1) {
QString saveFileFormat;
QString selectedFile = selectedFiles.at(0);
if (selectedFile.right(4) == ".jpg")
{
if (selectedFile.right(4) == ".jpg") {
saveFileFormat = "JPEG";
}
else if (selectedFile.right(4) == ".jpeg")
{
else if (selectedFile.right(4) == ".jpeg") {
saveFileFormat = "JPEG";
}
else if (selectedFile.right(4) == ".png")
{
else if (selectedFile.right(4) == ".png") {
saveFileFormat = "PNG";
}
else if (selectedFile.right(7) == ".suffix")
{
if (fileDialog.selectedNameFilter() == "JPEG picture (*.jpg)")
{
else if (selectedFile.right(7) == ".suffix") {
if (fileDialog.selectedNameFilter() == "JPEG picture (*.jpg)") {
selectedFile.replace(".suffix", ".jpg");
}
else if (fileDialog.selectedNameFilter() == "Portable Network Graphics (*.png)")
{
else if (fileDialog.selectedNameFilter() == "Portable Network Graphics (*.png)") {
selectedFile.replace(".suffix", ".png");
}
else
{
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))
{
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 (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();
exportPicture = exportPicture.scaled(desktopResolution.width(), desktopResolution.height(), aspectRatio, Qt::SmoothTransformation);
int desktopSizeWidth = desktopResolution.width();
int desktopSizeHeight = desktopResolution.height();
#endif
exportPicture = exportPicture.scaled(desktopSizeWidth, desktopSizeHeight, aspectRatio, Qt::SmoothTransformation);
}
else if (sizeMode == "Custom")
{
else if (sizeMode == "Custom") {
exportPicture = exportPicture.scaled(cusExportSize, aspectRatio, Qt::SmoothTransformation);
}
@ -172,32 +166,26 @@ fileDialogPreSave: //Work?
#else
QFile *picFile = new QFile(selectedFile);
#endif
if (picFile->open(QIODevice::WriteOnly))
{
if (picFile->open(QIODevice::WriteOnly)) {
isSaved = exportPicture.save(picFile, saveFileFormat.toStdString().c_str(), useCustomQuality ? customQuality : defaultQuality);
#if QT_VERSION >= 0x050000
if (isSaved)
{
if (isSaved) {
isSaved = picFile->commit();
}
else
{
else {
errorId = 1;
}
#else
picFile->close();
#endif
}
else
{
else {
errorId = 2;
}
delete picFile;
if (!isSaved)
{
switch (errorId)
{
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;
@ -213,8 +201,7 @@ fileDialogPreSave: //Work?
goto fileDialogPreSave; //Work?
}
}
else
{
else {
QMessageBox::warning(parent, PictureDialog::tr("Export as Picture"), PictureDialog::tr("No valid file is selected"));
goto fileDialogPreSave; //Work?
}
@ -250,7 +237,9 @@ fileDialogPreSave: //Work?
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);
@ -261,58 +250,48 @@ fileDialogPreSave: //Work?
fileDialog.restoreGeometry(settings.value(parent->objectName() % "+Geometry", "").toByteArray());
fileDialog.selectFile(QString(picture->getExportPictureFileName() % ".g5e"));
if (fileDialog.exec())
{
if (fileDialog.exec()) {
QStringList selectedFiles = fileDialog.selectedFiles();
if (selectedFiles.length() == 1)
{
if (selectedFiles.length() == 1) {
QString selectedFile = selectedFiles.at(0);
bool isAutoExt = false;
if (selectedFile.right(5) == ".auto")
{
#ifndef GTA5SYNC_FLATPAK
if (selectedFile.right(5) == ".auto") {
isAutoExt = true;
QString dirPath = QFileInfo(selectedFile).dir().path();
QString stockFileName = sgdFileInfo.fileName();
selectedFile = dirPath % "/" % stockFileName;
}
else if (selectedFile.right(4) == ".rem")
{
#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))
{
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")
{
if (selectedFile.right(4) == ".g5e") {
bool isExported = picture->exportPicture(selectedFile, SnapmaticFormat::G5E_Format);
if (!isExported)
{
if (!isExported) {
QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Failed to export current Snapmatic picture"));
goto fileDialogPreSave; //Work?
}
}
else
{
else {
bool isCopied = picture->exportPicture(selectedFile, SnapmaticFormat::PGTA_Format);
if (!isCopied)
{
if (!isCopied) {
QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("Failed to export current Snapmatic picture"));
goto fileDialogPreSave; //Work?
}
else
{
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
{
else {
QMessageBox::warning(parent, PictureDialog::tr("Export as Snapmatic"), PictureDialog::tr("No valid file is selected"));
goto fileDialogPreSave; //Work?
}

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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 <QDesktopWidget>
#include "AppEnv.h"
#include <QApplication>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QPixmap>
#include <QEvent>
#include <QDebug>
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);
}

View File

@ -46,7 +46,6 @@ private:
private slots:
void pictureDoubleClicked(Qt::MouseButton button);
void updateWindowSize(int screenID);
signals:
void nextPictureRequested();

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2018 Syping
* 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
@ -18,7 +18,9 @@
#include "PlayerListDialog.h"
#include "ui_PlayerListDialog.h"
#include "wrapper.h"
#include "AppEnv.h"
#include <QStringBuilder>
#include <QFontMetrics>
#include <QInputDialog>
#include <QMessageBox>
@ -31,54 +33,72 @@ PlayerListDialog::PlayerListDialog(QStringList players, ProfileDatabase *profile
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"))
{
if (QIcon::hasThemeIcon("dialog-ok-apply")) {
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok-apply"));
}
else if (QIcon::hasThemeIcon("dialog-apply"))
{
else if (QIcon::hasThemeIcon("dialog-apply")) {
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-apply"));
}
else if (QIcon::hasThemeIcon("gtk-apply"))
{
else if (QIcon::hasThemeIcon("gtk-apply")) {
ui->cmdApply->setIcon(QIcon::fromTheme("gtk-apply"));
}
else if (QIcon::hasThemeIcon("dialog-ok"))
{
else if (QIcon::hasThemeIcon("dialog-ok")) {
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok"));
}
else if (QIcon::hasThemeIcon("gtk-ok"))
{
else if (QIcon::hasThemeIcon("gtk-ok")) {
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok"));
}
// Set Icon for Cancel Button
if (QIcon::hasThemeIcon("dialog-cancel"))
{
if (QIcon::hasThemeIcon("dialog-cancel")) {
ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel"));
}
else if (QIcon::hasThemeIcon("gtk-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 (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
{
drawSwitchButtons();
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();
@ -89,90 +109,15 @@ PlayerListDialog::PlayerListDialog(QStringList players, ProfileDatabase *profile
PlayerListDialog::~PlayerListDialog()
{
for (QObject *object : ui->listAvPlayers->children())
{
for (QObject *object : ui->listAvPlayers->children()) {
delete object;
}
for (QObject *object : ui->listSePlayers->children())
{
for (QObject *object : ui->listSePlayers->children()) {
delete object;
}
delete ui;
}
void PlayerListDialog::drawSwitchButtons()
{
QFont painterFont = ui->cmdApply->font();
QPalette palette;
QFontMetrics fontMetrics(painterFont);
QRect makeAvRect = fontMetrics.boundingRect(QRect(0, 0, 0, 0), Qt::AlignCenter | Qt::TextDontClip, "<");
QRect makeSeRect = fontMetrics.boundingRect(QRect(0, 0, 0, 0), Qt::AlignCenter | Qt::TextDontClip, ">");
QRect makeAdRect = fontMetrics.boundingRect(QRect(0, 0, 0, 0), Qt::AlignCenter | Qt::TextDontClip, "+");
int makeAvSize;
if (makeAvRect.height() > makeAvRect.width())
{
makeAvSize = makeAvRect.height();
}
else
{
makeAvSize = makeAvRect.width();
}
int makeSeSize;
if (makeSeRect.height() > makeSeRect.width())
{
makeSeSize = makeSeRect.height();
}
else
{
makeSeSize = makeSeRect.width();
}
int makeAdSize;
if (makeAdRect.height() > makeAdRect.width())
{
makeAdSize = makeAdRect.height();
}
else
{
makeAdSize = makeAdRect.width();
}
QImage avImage(makeAvSize, makeAvSize, QImage::Format_ARGB32_Premultiplied);
avImage.fill(Qt::transparent);
QImage seImage(makeSeSize, makeSeSize, QImage::Format_ARGB32_Premultiplied);
seImage.fill(Qt::transparent);
QImage adImage(makeAdSize, makeAdSize, QImage::Format_ARGB32_Premultiplied);
adImage.fill(Qt::transparent);
QPainter avPainter(&avImage);
avPainter.setFont(painterFont);
avPainter.setBrush(palette.buttonText());
avPainter.setPen(palette.buttonText().color());
avPainter.drawText(0, 0, makeAvSize, makeAvSize, Qt::AlignCenter | Qt::TextDontClip, "<");
avPainter.end();
QPainter sePainter(&seImage);
sePainter.setFont(painterFont);
sePainter.setBrush(palette.buttonText());
sePainter.setPen(palette.buttonText().color());
sePainter.drawText(0, 0, makeSeSize, makeSeSize, Qt::AlignCenter | Qt::TextDontClip, ">");
sePainter.end();
QPainter adPainter(&adImage);
adPainter.setFont(painterFont);
adPainter.setBrush(palette.buttonText());
adPainter.setPen(palette.buttonText().color());
adPainter.drawText(0, 0, makeAdSize, makeAdSize, Qt::AlignCenter | Qt::TextDontClip, "+");
adPainter.end();
ui->cmdMakeAv->setIconSize(avImage.size());
ui->cmdMakeSe->setIconSize(seImage.size());
ui->cmdMakeAd->setIconSize(adImage.size());
ui->cmdMakeAv->setIcon(QIcon(QPixmap::fromImage(avImage)));
ui->cmdMakeSe->setIcon(QIcon(QPixmap::fromImage(seImage)));
ui->cmdMakeAd->setIcon(QIcon(QPixmap::fromImage(adImage)));
}
void PlayerListDialog::on_cmdCancel_clicked()
{
close();
@ -181,16 +126,13 @@ void PlayerListDialog::on_cmdCancel_clicked()
void PlayerListDialog::buildInterface()
{
const QStringList dbPlayers = profileDB->getPlayers();
for (QString sePlayer : players)
{
for (const QString &sePlayer : qAsConst(players)) {
QListWidgetItem *playerItem = new QListWidgetItem(profileDB->getPlayerName(sePlayer));
playerItem->setData(Qt::UserRole, sePlayer);
ui->listSePlayers->addItem(playerItem);
}
for (QString dbPlayer : dbPlayers)
{
if (!players.contains(dbPlayer))
{
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);
@ -201,8 +143,7 @@ void PlayerListDialog::buildInterface()
void PlayerListDialog::on_cmdMakeAv_clicked()
{
for (QListWidgetItem *item : ui->listSePlayers->selectedItems())
{
for (QListWidgetItem *item : ui->listSePlayers->selectedItems()) {
QString playerName = item->text();
int playerID = item->data(Qt::UserRole).toInt();
delete item;
@ -216,13 +157,11 @@ void PlayerListDialog::on_cmdMakeAv_clicked()
void PlayerListDialog::on_cmdMakeSe_clicked()
{
int maxPlayers = 30;
if (maxPlayers < ui->listSePlayers->count() + ui->listAvPlayers->selectedItems().count())
{
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())
{
for (QListWidgetItem *item : ui->listAvPlayers->selectedItems()) {
QString playerName = item->text();
int playerID = item->data(Qt::UserRole).toInt();
delete item;
@ -236,15 +175,12 @@ 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)
{
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)
{
if (itemPlayerID == playerID) {
delete item;
QListWidgetItem *playerItem = new QListWidgetItem(itemPlayerName);
playerItem->setData(Qt::UserRole, playerID);
@ -252,8 +188,7 @@ void PlayerListDialog::on_cmdMakeAd_clicked()
return;
}
}
for (int i = 0; i < ui->listSePlayers->count(); ++i)
{
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)
@ -271,8 +206,7 @@ void PlayerListDialog::on_cmdMakeAd_clicked()
void PlayerListDialog::on_cmdApply_clicked()
{
players.clear();
for (int i = 0; i < ui->listSePlayers->count(); ++i)
{
for (int i = 0; i < ui->listSePlayers->count(); ++i) {
players += ui->listSePlayers->item(i)->data(Qt::UserRole).toString();
}
emit playerListUpdated(players);

View File

@ -48,7 +48,6 @@ private:
ProfileDatabase *profileDB;
Ui::PlayerListDialog *ui;
bool listUpdated;
void drawSwitchButtons();
void buildInterface();
signals:

View File

@ -51,8 +51,8 @@
</item>
<item>
<widget class="QPushButton" name="cmdMakeSe">
<property name="text">
<string/>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="autoDefault">
<bool>false</bool>
@ -61,6 +61,9 @@
</item>
<item>
<widget class="QPushButton" name="cmdMakeAv">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
@ -71,6 +74,9 @@
</item>
<item>
<widget class="QPushButton" name="cmdMakeAd">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -29,6 +29,8 @@
#include "ExportThread.h"
#include "SavegameData.h"
#include "CrewDatabase.h"
#include "pcg_basic.h"
#include <QFileSystemWatcher>
#include <QProgressDialog>
#include <QSpacerItem>
#include <QDateTime>
@ -51,8 +53,6 @@ public:
void settingsApplied(int contentMode, bool languageChanged);
void setupProfileInterface();
void massTool(MassTool tool);
void disableSelected();
void enableSelected();
int selectedWidgets();
void retranslateUi();
~ProfileInterface();
@ -63,8 +63,11 @@ public slots:
void hoverProfileWidgetCheck();
void selectAllWidgets();
void deselectAllWidgets();
void disableSelected();
void enableSelected();
void exportSelected();
void deleteSelected();
void deleteSelectedR();
void updatePalette();
void importFiles();
@ -80,9 +83,17 @@ private slots:
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<QString> savegameFiles, QVector<QString> snapmaticPics);
#endif
protected:
bool eventFilter(QObject *watched, QEvent *event);
@ -98,32 +109,41 @@ private:
QList<SavegameData*> savegames;
QList<SnapmaticPicture*> pictures;
QMap<ProfileWidget*,QString> widgets;
#if QT_VERSION >= 0x050000
QFileSystemWatcher fileSystemWatcher;
QVector<QString> savegameFiles;
QVector<QString> snapmaticPics;
#endif
QSpacerItem *saSpacerItem;
QStringList fixedPictures;
QColor highlightBackColor;
QColor highlightTextColor;
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, int *currentTime, bool notMultiple);
void importFilesProgress(QStringList selectedFiles);
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();

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -16,80 +16,99 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "ProfileLoader.h"
#include "SnapmaticPicture.h"
#include "ProfileLoader.h"
#include "SavegameData.h"
#include "CrewDatabase.h"
#include "wrapper.h"
#include <QStringBuilder>
#include <QStringList>
#include <QVector>
#include <QString>
#include <QThread>
#include <QList>
#include <QFile>
#ifdef Q_OS_WIN
#include <QDir>
#include <QList>
#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;
QDir profileDir;
QList<int> crewList;
profileDir.setPath(profileFolder);
int maximumV = 0;
QVector<int> crewList;
QVector<QString> savegameFiles;
QVector<QString> snapmaticPics;
// 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 += SnapmaticPics.filter(".bak", Qt::CaseInsensitive);
SavegameFiles.removeDuplicates();
SnapmaticPics.removeDuplicates();
for (QString BackupFile : BackupFiles)
{
SavegameFiles.removeAll(BackupFile);
SnapmaticPics.removeAll(BackupFile);
#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
int maximumV = SavegameFiles.length() + SnapmaticPics.length();
// Directory successfully scanned
emit directoryScanned(savegameFiles, snapmaticPics);
// Loading pictures and savegames
emit loadingProgress(curFile, maximumV);
for (QString SavegameFile : SavegameFiles)
{
for (const QString &SavegameFile : qAsConst(savegameFiles)) {
emit loadingProgress(curFile, maximumV);
QString sgdPath = profileFolder % "/" % SavegameFile;
const QString sgdPath = profileFolder % "/" % SavegameFile;
SavegameData *savegame = new SavegameData(sgdPath);
if (savegame->readingSavegame())
{
if (savegame->readingSavegame()) {
emit savegameLoaded(savegame, sgdPath);
}
curFile++;
}
for (QString SnapmaticPic : SnapmaticPics)
{
for (const QString &SnapmaticPic : qAsConst(snapmaticPics)) {
emit loadingProgress(curFile, maximumV);
QString picturePath = profileFolder % "/" % SnapmaticPic;
const QString picturePath = profileFolder % "/" % SnapmaticPic;
SnapmaticPicture *picture = new SnapmaticPicture(picturePath);
if (picture->readingPicture(true, true, true))
{
if (picture->isFormatSwitched())
{
if (picture->readingPicture(true)) {
if (picture->isFormatSwitched()) {
picture->setSnapmaticFormat(SnapmaticFormat::PGTA_Format);
if (picture->exportPicture(picturePath, 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))
{
if (!crewList.contains(crewNumber)) {
crewList += crewNumber;
}
}
@ -98,8 +117,7 @@ void ProfileLoader::run()
// adding found crews
crewDB->setAddingCrews(true);
for (int crewID : crewList)
{
for (int crewID : qAsConst(crewList)) {
crewDB->addCrew(crewID);
}
crewDB->setAddingCrews(false);
@ -107,10 +125,9 @@ void ProfileLoader::run()
void ProfileLoader::preloaded()
{
}
void ProfileLoader::loaded()
{
}

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -48,6 +48,7 @@ signals:
void pictureFixed(SnapmaticPicture *picture);
void savegameLoaded(SavegameData *savegame, QString savegamePath);
void loadingProgress(int value, int maximum);
void directoryScanned(QVector<QString> savegameFiles, QVector<QString> snapmaticPics);
};
#endif // PROFILELOADER_H

View File

@ -1,42 +1,45 @@
## gta5view
Grand Theft Auto V Savegame and Snapmatic viewer/editor
Open Source Snapmatic and Savegame viewer/editor for GTA V
- View Snapmatics with the ability to disable them in-game
- Edit Snapmatic pictures and properties in multiple ways
- Import/Export Snapmatics, Savegames and pictures
- Let choose between multiple Social Club accounts as GTA V profiles IDs
- 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 for Debian/Ubuntu
# Note: You can use 'sudo make install' instead of 'sudo checkinstall'
sudo apt-get install git gcc g++ qtbase5-dev qttranslations5-l10n qt5-qmake make checkinstall
git clone https://github.com/SyDevTeam/gta5view && cd gta5view
mkdir -p build && cd build
qmake -qt=5 GTA5SYNC_PREFIX=/usr ../gta5view.pro # or just qmake GTA5SYNC_PREFIX=/usr ../gta5view.pro
make -j $(nproc --all)
sudo checkinstall --pkgname=gta5view --pkggroup=utility --requires=libqt5core5a,libqt5gui5,libqt5network5,libqt5widgets5,qttranslations5-l10n
#### Build gta5view for Windows
# Note: Install Docker Community Edition and Git before continuing
git clone https://github.com/SyDevTeam/gta5view && cd gta5view
docker pull syping/qt5-static-mingw
docker run --rm -v ${PWD}:/gta5view -it syping/qt5-static-mingw
cd /gta5view && mkdir -p build && cd build
qmake-static ../gta5view.pro
make -j $(nproc --all)
# 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 for Windows (Beginner)
#### Build gta5view for Debian/Ubuntu
Download the [Qt Framework](https://www.qt.io/) and install the MinGW version.
Download the Source Code over [GitHub](https://github.com/SyDevTeam/gta5view/archive/1.5.x.zip) or with your Git client.
Open the gta5view.pro Project 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](https://github.com/SyDevTeam/gta5view/releases) and download the .exe file for Windows, .deb file for Debian/Ubuntu and .dmg file for OS X.
sudo pacman -S cmake gcc git make qt5-base qt5-svg qt5-tools qt5-translations
git clone https://gitlab.com/Syping/gta5view
cmake -B gta5view-build gta5view
cmake --build gta5view-build
sudo cmake --install gta5view-build
#### Build gta5view for Fedora/RHEL
sudo dnf install cmake git gcc gcc-c++ make qt5-qtbase-devel qt5-qtsvg-devel qt5-qttranslations
git clone https://gitlab.com/Syping/gta5view
cmake -B gta5view-build gta5view
cmake --build gta5view-build
sudo cmake --install gta5view-build

893
RagePhoto.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#include "RagePhoto.h"
#include <QJsonDocument>
#include <QBuffer>
#include <QFile>
#if QT_VERSION < 0x060000
#include <QTextCodec>
#else
#include <QStringEncoder>
#include <QStringDecoder>
#endif
#ifdef RAGEPHOTO_BENCHMARK
#include <QFileInfo>
#include <chrono>
#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<quint32>(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<char*>(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<char*>(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<char*>(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<char*>(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<std::chrono::nanoseconds>(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<quint32>(PhotoFormat::G5EX)) {
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
format = charToUInt32LE(uInt32Buffer);
if (format == static_cast<quint32>(ExportFormat::G5E3P)) {
size = dataBuffer.read(uInt32Buffer, 4);
if (size != 4)
return false;
quint32 compressedSize = charToUInt32LE(uInt32Buffer);
char *compressedPhotoHeader = static_cast<char*>(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<char*>(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<char*>(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<char*>(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<char*>(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<std::chrono::nanoseconds>(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<quint32>(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<quint32>(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<int>(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<quint32>(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<quint32>(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<quint32>(PhotoFormat::G5EX);
uInt32ToCharLE(format, uInt32Buffer);
ioDevice->write(uInt32Buffer, 4);
format = static_cast<quint32>(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<quint32>(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<unsigned char>(x[0]) << 24 |
static_cast<unsigned char>(x[1]) << 16 |
static_cast<unsigned char>(x[2]) << 8 |
static_cast<unsigned char>(x[3]));
}
quint32 RagePhoto::charToUInt32LE(char *x)
{
return (static_cast<unsigned char>(x[3]) << 24 |
static_cast<unsigned char>(x[2]) << 16 |
static_cast<unsigned char>(x[1]) << 8 |
static_cast<unsigned char>(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
}

110
RagePhoto.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef RAGEPHOTO_H
#define RAGEPHOTO_H
#include <QJsonObject>
#include <QIODevice>
#include <QObject>
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

View File

@ -20,7 +20,6 @@
#include "StringParser.h"
#include "SavegameData.h"
#include <QStringBuilder>
#include <QTextCodec>
#include <QByteArray>
#include <QDebug>
#include <QFile>

View File

@ -28,7 +28,11 @@ SavegameDialog::SavegameDialog(QWidget *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);

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>400</width>
<height>105</height>
<height>112</height>
</rect>
</property>
<property name="windowTitle">
@ -20,7 +20,7 @@
<item>
<widget class="QLabel" name="labSavegameText">
<property name="text">
<string>&lt;span style=&quot; font-weight:600;&quot;&gt;Savegame&lt;/span&gt;&lt;br&gt;&lt;br&gt;%1</string>
<string>&lt;span style=&quot;font-weight:600&quot;&gt;Savegame&lt;/span&gt;&lt;br&gt;&lt;br&gt;%1</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -24,6 +24,8 @@
#include "SavegameData.h"
#include "SavegameCopy.h"
#include "AppEnv.h"
#include "config.h"
#include <QStringBuilder>
#include <QFileDialog>
#include <QMessageBox>
#include <QSettings>
@ -37,6 +39,13 @@
#include <QMenu>
#include <QUrl>
#ifdef GTA5SYNC_TELEMETRY
#include "TelemetryClass.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QDateTime>
#endif
SavegameWidget::SavegameWidget(QWidget *parent) :
ProfileWidget(parent),
ui(new Ui::SavegameWidget)
@ -50,9 +59,8 @@ SavegameWidget::SavegameWidget(QWidget *parent) :
qreal screenRatio = AppEnv::screenRatio();
ui->labSavegamePic->setFixedSize(48 * screenRatio, 27 * screenRatio);
QPixmap savegamePixmap(":/img/savegame.png");
if (screenRatio != 1) savegamePixmap = savegamePixmap.scaledToHeight(ui->labSavegamePic->height(), Qt::SmoothTransformation);
ui->labSavegamePic->setPixmap(savegamePixmap);
ui->labSavegamePic->setScaledContents(true);
ui->labSavegamePic->setPixmap(QPixmap(AppEnv::getImagesFolder() % "/savegame.svgz"));
QString exportSavegameStr = tr("Export Savegame...");
Q_UNUSED(exportSavegameStr)
@ -134,9 +142,49 @@ void SavegameWidget::on_cmdDelete_clicked()
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))
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
@ -178,7 +226,8 @@ void SavegameWidget::mouseReleaseEvent(QMouseEvent *ev)
}
else
{
if (getContentMode() == 0 && rect().contains(ev->pos()) && ev->button() == Qt::LeftButton)
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))
{
@ -189,7 +238,7 @@ void SavegameWidget::mouseReleaseEvent(QMouseEvent *ev)
on_cmdView_clicked();
}
}
else if (!ui->cbSelected->isVisible() && getContentMode() == 1 && ev->button() == Qt::LeftButton && ev->modifiers().testFlag(Qt::ShiftModifier))
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());
}
@ -200,7 +249,8 @@ void SavegameWidget::mouseDoubleClickEvent(QMouseEvent *ev)
{
ProfileWidget::mouseDoubleClickEvent(ev);
if (!ui->cbSelected->isVisible() && getContentMode() == 1 && ev->button() == Qt::LeftButton)
const int contentMode = getContentMode();
if (!ui->cbSelected->isVisible() && (contentMode == 1 || contentMode == 11 || contentMode == 21) && ev->button() == Qt::LeftButton)
{
on_cmdView_clicked();
}

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2018 Syping
* 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
@ -21,8 +21,9 @@
#include "SnapmaticPicture.h"
#include "PlayerListDialog.h"
#include "StringParser.h"
#include "wrapper.h"
#include "AppEnv.h"
#include <QStringListIterator>
#include "config.h"
#include <QStringBuilder>
#include <QTextDocument>
#include <QInputDialog>
@ -30,45 +31,49 @@
#include <QDebug>
#include <QFile>
#ifdef GTA5SYNC_TELEMETRY
#include "TelemetryClass.h"
#include <QJsonDocument>
#include <QJsonObject>
#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"))
{
if (QIcon::hasThemeIcon("dialog-ok-apply")) {
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok-apply"));
}
else if (QIcon::hasThemeIcon("dialog-apply"))
{
else if (QIcon::hasThemeIcon("dialog-apply")) {
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-apply"));
}
else if (QIcon::hasThemeIcon("gtk-apply"))
{
else if (QIcon::hasThemeIcon("gtk-apply")) {
ui->cmdApply->setIcon(QIcon::fromTheme("gtk-apply"));
}
else if (QIcon::hasThemeIcon("dialog-ok"))
{
else if (QIcon::hasThemeIcon("dialog-ok")) {
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok"));
}
else if (QIcon::hasThemeIcon("gtk-ok"))
{
else if (QIcon::hasThemeIcon("gtk-ok")) {
ui->cmdApply->setIcon(QIcon::fromTheme("dialog-ok"));
}
// Set Icon for Cancel Button
if (QIcon::hasThemeIcon("dialog-cancel"))
{
if (QIcon::hasThemeIcon("dialog-cancel")) {
ui->cmdCancel->setIcon(QIcon::fromTheme("dialog-cancel"));
}
else if (QIcon::hasThemeIcon("gtk-cancel"))
{
else if (QIcon::hasThemeIcon("gtk-cancel")) {
ui->cmdCancel->setIcon(QIcon::fromTheme("gtk-cancel"));
}
@ -89,27 +94,18 @@ SnapmaticEditor::~SnapmaticEditor()
void SnapmaticEditor::selfie_toggled(bool checked)
{
if (checked)
{
isSelfie = true;
}
else
{
isSelfie = false;
}
isSelfie = checked;
}
void SnapmaticEditor::mugshot_toggled(bool checked)
{
if (checked)
{
if (checked) {
isMugshot = true;
ui->cbDirector->setEnabled(false);
ui->cbDirector->setChecked(false);
}
else
{
else {
isMugshot = false;
ui->cbDirector->setEnabled(true);
}
@ -117,14 +113,12 @@ void SnapmaticEditor::mugshot_toggled(bool checked)
void SnapmaticEditor::editor_toggled(bool checked)
{
if (checked)
{
if (checked) {
isEditor = true;
ui->cbDirector->setEnabled(false);
ui->cbDirector->setChecked(false);
}
else
{
else {
isEditor = false;
ui->cbDirector->setEnabled(true);
}
@ -132,8 +126,7 @@ void SnapmaticEditor::editor_toggled(bool checked)
void SnapmaticEditor::on_rbSelfie_toggled(bool checked)
{
if (checked)
{
if (checked) {
mugshot_toggled(false);
editor_toggled(false);
selfie_toggled(true);
@ -142,8 +135,7 @@ void SnapmaticEditor::on_rbSelfie_toggled(bool checked)
void SnapmaticEditor::on_rbMugshot_toggled(bool checked)
{
if (checked)
{
if (checked) {
selfie_toggled(false);
editor_toggled(false);
mugshot_toggled(true);
@ -152,8 +144,7 @@ void SnapmaticEditor::on_rbMugshot_toggled(bool checked)
void SnapmaticEditor::on_rbEditor_toggled(bool checked)
{
if (checked)
{
if (checked) {
selfie_toggled(false);
mugshot_toggled(false);
editor_toggled(true);
@ -162,8 +153,7 @@ void SnapmaticEditor::on_rbEditor_toggled(bool checked)
void SnapmaticEditor::on_rbCustom_toggled(bool checked)
{
if (checked)
{
if (checked) {
selfie_toggled(false);
mugshot_toggled(false);
editor_toggled(false);
@ -182,20 +172,16 @@ void SnapmaticEditor::setSnapmaticPicture(SnapmaticPicture *picture)
playersList = snapmaticProperties.playersList;
ui->cbDirector->setChecked(snapmaticProperties.isFromDirector);
ui->cbMeme->setChecked(snapmaticProperties.isMeme);
if (isSelfie)
{
if (isSelfie) {
ui->rbSelfie->setChecked(true);
}
else if (isMugshot)
{
else if (isMugshot) {
ui->rbMugshot->setChecked(true);
}
else if (isEditor)
{
else if (isEditor) {
ui->rbEditor->setChecked(true);
}
else
{
else {
ui->rbCustom->setChecked(true);
}
setSnapmaticCrew(returnCrewName(crewID));
@ -205,8 +191,7 @@ void SnapmaticEditor::setSnapmaticPicture(SnapmaticPicture *picture)
void SnapmaticEditor::insertPlayerNames(QStringList *players)
{
for (int i = 0; i < players->size(); ++i)
{
for (int i = 0; i < players->size(); ++i) {
players->replace(i, profileDB->getPlayerName(players->at(i)));
}
}
@ -222,54 +207,48 @@ void SnapmaticEditor::setSnapmaticPlayers(const QStringList &players)
{
QString editStr = QString("<a href=\"g5e://editplayers\" style=\"text-decoration: none;\">%1</a>").arg(tr("Edit"));
QString playersStr;
if (players.length() != 1)
{
if (players.length() != 1) {
playersStr = tr("Players: %1 (%2)", "Multiple Player are inserted here");
}
else
{
else {
playersStr = tr("Player: %1 (%2)", "One Player is inserted here");
}
if (players.length() != 0)
{
if (players.length() != 0) {
ui->labPlayers->setText(playersStr.arg(players.join(", "), editStr));
}
else
{
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())); }
if (heightForWidth(width()) > height())
resize(width(), heightForWidth(width()));
#endif
}
void SnapmaticEditor::setSnapmaticTitle(const QString &title)
{
if (title.length() > 39)
{
if (title.length() > 39) {
snapmaticTitle = title.left(39);
}
else
{
else {
snapmaticTitle = title;
}
QString editStr = QString("<a href=\"g5e://edittitle\" style=\"text-decoration: none;\">%1</a>").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("<span style=\"color: green\">%1</a>").arg(tr("Yes", "Yes, should work fine"))));
if (SnapmaticPicture::verifyTitle(snapmaticTitle)) {
ui->labAppropriate->setText(tr("Appropriate: %1").arg(QString("<span style=\"color: green\">%1</span>").arg(tr("Yes", "Yes, should work fine"))));
}
else
{
ui->labAppropriate->setText(tr("Appropriate: %1").arg(QString("<span style=\"color: red\">%1</a>").arg(tr("No", "No, could lead to issues"))));
else {
ui->labAppropriate->setText(tr("Appropriate: %1").arg(QString("<span style=\"color: red\">%1</span>").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())); }
if (heightForWidth(width()) > height())
resize(width(), heightForWidth(width()));
#endif
}
@ -281,7 +260,8 @@ void SnapmaticEditor::setSnapmaticCrew(const QString &crew)
#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())); }
if (heightForWidth(width()) > height())
resize(width(), heightForWidth(width()));
#endif
}
@ -297,8 +277,7 @@ void SnapmaticEditor::on_cmdCancel_clicked()
void SnapmaticEditor::on_cmdApply_clicked()
{
if (ui->cbQualify->isChecked())
{
if (ui->cbQualify->isChecked()) {
qualifyAvatar();
}
snapmaticProperties.crewID = crewID;
@ -308,28 +287,44 @@ void SnapmaticEditor::on_cmdApply_clicked()
snapmaticProperties.isFromDirector = ui->cbDirector->isChecked();
snapmaticProperties.isMeme = ui->cbMeme->isChecked();
snapmaticProperties.playersList = playersList;
if (smpic)
{
if (smpic) {
QString currentFilePath = smpic->getPictureFilePath();
QString originalFilePath = smpic->getOriginalPictureFilePath();
QString backupFileName = originalFilePath % ".bak";
if (!QFile::exists(backupFileName))
{
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))
{
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
{
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();
@ -345,8 +340,7 @@ void SnapmaticEditor::qualifyAvatar()
void SnapmaticEditor::on_cbQualify_toggled(bool checked)
{
if (checked)
{
if (checked) {
ui->cbMeme->setEnabled(false);
ui->cbDirector->setEnabled(false);
ui->rbCustom->setEnabled(false);
@ -354,15 +348,13 @@ void SnapmaticEditor::on_cbQualify_toggled(bool checked)
ui->rbEditor->setEnabled(false);
ui->rbMugshot->setEnabled(false);
}
else
{
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())
{
if (ui->rbSelfie->isChecked() || ui->rbCustom->isChecked()) {
ui->cbDirector->setEnabled(true);
}
}
@ -370,8 +362,7 @@ void SnapmaticEditor::on_cbQualify_toggled(bool checked)
void SnapmaticEditor::on_labPlayers_linkActivated(const QString &link)
{
if (link == "g5e://editplayers")
{
if (link == "g5e://editplayers") {
PlayerListDialog *playerListDialog = new PlayerListDialog(playersList, profileDB, this);
connect(playerListDialog, SIGNAL(playerListUpdated(QStringList)), this, SLOT(playerListUpdated(QStringList)));
playerListDialog->setModal(true);
@ -383,12 +374,10 @@ void SnapmaticEditor::on_labPlayers_linkActivated(const QString &link)
void SnapmaticEditor::on_labTitle_linkActivated(const QString &link)
{
if (link == "g5e://edittitle")
{
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())
{
if (ok && !newTitle.isEmpty()) {
setSnapmaticTitle(newTitle);
}
}
@ -396,37 +385,35 @@ void SnapmaticEditor::on_labTitle_linkActivated(const QString &link)
void SnapmaticEditor::on_labCrew_linkActivated(const QString &link)
{
if (link == "g5e://editcrew")
{
if (link == "g5e://editcrew") {
bool ok;
int indexNum = 0;
QStringList itemList;
QStringList crewList = crewDB->getCrews();
if (!crewList.contains(QLatin1String("0")))
{
if (!crewList.contains(QLatin1String("0"))) {
crewList += QLatin1String("0");
}
crewList.sort();
for (QString crew : crewList)
{
for (const QString &crew : crewList) {
itemList += QString("%1 (%2)").arg(crew, returnCrewName(crew.toInt()));
}
if (crewList.contains(QString::number(crewID)))
{
indexNum = crewList.indexOf(QRegExp(QString::number(crewID)));
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 (QChar crewChar : newCrew)
{
if (!crewChar.isNumber())
{
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));
}

View File

@ -236,6 +236,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Apply changes</string>
</property>
<property name="text">
<string>&amp;Apply</string>
</property>
@ -249,6 +252,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Discard changes</string>
</property>
<property name="text">
<string>&amp;Cancel</string>
</property>

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5spv Grand Theft Auto Snapmatic Picture Viewer
* Copyright (C) 2016-2018 Syping
* 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,6 +19,7 @@
#ifndef SNAPMATICPICTURE_H
#define SNAPMATICPICTURE_H
#include "RagePhoto.h"
#include <QStringList>
#include <QDateTime>
#include <QObject>
@ -34,6 +35,7 @@ struct SnapmaticProperties {
double x;
double y;
double z;
bool isCayoPerico;
};
int uid;
int crewID;
@ -57,17 +59,15 @@ public:
~SnapmaticPicture();
void reset();
bool preloadFile();
bool readingPictureFromFile(const QString &fileName, bool writeEnabled = true, bool cacheEnabled = false, bool fastLoad = true, bool lowRamMode = false);
bool readingPicture(bool writeEnabled = true, bool cacheEnabled = false, bool fastLoad = true, bool lowRamMode = false);
bool readingPictureFromFile(const QString &fileName, bool cacheEnabled = false);
bool readingPicture(bool cacheEnabled = false);
bool isPicOk(); // Please use isPictureOk instead
void clearCache();
QImage getImage(bool fastLoad = false);
QImage getImage();
QByteArray getPictureStream();
QString getLastStep(bool readable = true);
QString getPictureStr();
QString getPictureHead();
QString getPictureTitl();
QString getPictureDesc();
QString getPictureSortStr();
QString getPictureFileName();
QString getPictureFilePath();
@ -75,7 +75,7 @@ public:
QString getOriginalPictureFileName();
QString getOriginalPictureFilePath();
int getContentMaxLength();
bool setImage(const QImage &picture);
bool setImage(const QImage &picture, bool eXtendMode = false);
bool setPictureTitl(const QString &newTitle); // Please use setPictureTitle instead
bool setPictureStream(const QByteArray &streamArray);
void updateStrings();
@ -106,7 +106,6 @@ public:
QString getPictureJson() { return getJsonStr(); }
QString getPictureTitle() { return getPictureTitl(); }
QString getPictureString() { return getPictureStr(); }
QString getPictureDescription() { return getPictureDesc(); }
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); }
@ -120,11 +119,7 @@ public:
bool setVisible() { return setPictureVisible(); } // Please use setPictureVisible instead
// PREDEFINED PROPERTIES
QSize getSnapmaticResolution();
// SNAPMATIC DEFAULTS
bool isSnapmaticDefaultsEnforced();
void setSnapmaticDefaultsEnforced(bool enforced);
static QSize getSnapmaticResolution();
// SNAPMATIC FORMAT
SnapmaticFormat getSnapmaticFormat();
@ -139,43 +134,32 @@ public:
static QString convertDrawStringForLog(const QString &inputStr);
static QString convertLogStringForDraw(const QString &inputStr);
// RAGEPHOTO
RagePhoto* ragePhoto();
private:
QString getSnapmaticHeaderString(const QByteArray &snapmaticHeader);
QString getSnapmaticJSONString(const QByteArray &jsonBytes);
QString getSnapmaticTIDEString(const QByteArray &tideBytes);
QImage cachePicture;
QString picExportFileName;
QString picFileName;
QString picFilePath;
QString pictureHead;
QString pictureStr;
QString lastStep;
QString sortStr;
QString titlStr;
QString descStr;
bool picOk;
bool lowRamMode;
bool writeEnabled;
bool cacheEnabled;
bool isLoadedInRAM;
bool isCustomFormat;
bool isFormatSwitch;
bool careSnapDefault;
int jpegRawContentSize;
int jpegRawContentSizeE;
// PICTURE STREAM
QByteArray rawPicContent;
// JSON
void parseJsonContent();
bool jsonOk;
QString jsonStr;
SnapmaticProperties localProperties;
// VERIFY CONTENT
static bool verifyTitleChar(const QChar &titleChar);
// RAGEPHOTO
RagePhoto p_ragePhoto;
signals:
void customSignal(QString signal);
void preloaded();

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -18,7 +18,6 @@
#include "SnapmaticWidget.h"
#include "ui_SnapmaticWidget.h"
#include "ImageEditorDialog.h"
#include "MapLocationDialog.h"
#include "JsonEditorDialog.h"
#include "SnapmaticPicture.h"
@ -27,16 +26,23 @@
#include "PictureDialog.h"
#include "PictureExport.h"
#include "StringParser.h"
#include "ImportDialog.h"
#include "AppEnv.h"
#include "config.h"
#include <QStringBuilder>
#include <QMessageBox>
#include <QPainter>
#include <QPixmap>
#include <QTimer>
#include <QDebug>
#include <QMenu>
#include <QFile>
#ifdef GTA5SYNC_TELEMETRY
#include "TelemetryClass.h"
#include <QJsonDocument>
#include <QJsonObject>
#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)
@ -70,12 +76,33 @@ void SnapmaticWidget::setSnapmaticPicture(SnapmaticPicture *picture)
QObject::connect(picture, SIGNAL(updated()), this, SLOT(snapmaticUpdated()));
QObject::connect(picture, SIGNAL(customSignal(QString)), this, SLOT(customSignal(QString)));
qreal screenRatio = AppEnv::screenRatio();
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
QPixmap SnapmaticPixmap = QPixmap::fromImage(picture->getImage().scaled(ui->labPicture->width(), ui->labPicture->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::AutoColor);
ui->labPicStr->setText(smpic->getPictureStr() % "\n" % smpic->getPictureTitl());
ui->labPicture->setPixmap(SnapmaticPixmap);
ui->labPicture->setPixmap(renderPixmap);
picture->clearCache();
@ -89,8 +116,7 @@ void SnapmaticWidget::snapmaticUpdated()
void SnapmaticWidget::customSignal(QString signal)
{
if (signal == "PictureUpdated")
{
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);
}
@ -106,7 +132,7 @@ void SnapmaticWidget::on_cmdView_clicked()
{
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
settings.beginGroup("Interface");
bool navigationBar = settings.value("NavigationBar", false).toBool();
bool navigationBar = settings.value("NavigationBar", true).toBool();
settings.endGroup();
PictureDialog *picDialog = new PictureDialog(profileDB, crewDB, profileName, this);
@ -120,7 +146,8 @@ void SnapmaticWidget::on_cmdView_clicked()
QObject::connect(picDialog, SIGNAL(previousPictureRequested()), this, SLOT(dialogPreviousPictureRequested()));
// add previous next buttons
if (navigationBar) picDialog->addPreviousNextButtons();
if (navigationBar)
picDialog->addPreviousNextButtons();
// show picture dialog
#ifdef Q_OS_ANDROID
@ -128,7 +155,7 @@ void SnapmaticWidget::on_cmdView_clicked()
picDialog->showMaximized();
#else
picDialog->show();
if (navigationBar) picDialog->stylizeDialog();
if (navigationBar) picDialog->styliseDialog();
//picDialog->adaptNewDialogSize();
picDialog->setMinimumSize(picDialog->size());
picDialog->setMaximumSize(picDialog->size());
@ -149,20 +176,38 @@ void SnapmaticWidget::on_cmdExport_clicked()
void SnapmaticWidget::on_cmdDelete_clicked()
{
if (deletePicture()) emit pictureDeleted();
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->deletePicFile())
{
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
{
else {
QMessageBox::warning(this, tr("Delete picture"), tr("Failed at deleting %1 from your Snapmatic pictures").arg("\""+smpic->getPictureTitle()+"\""));
}
}
@ -177,28 +222,22 @@ void SnapmaticWidget::mousePressEvent(QMouseEvent *ev)
void SnapmaticWidget::mouseReleaseEvent(QMouseEvent *ev)
{
ProfileWidget::mouseReleaseEvent(ev);
if (ui->cbSelected->isVisible())
{
if (rect().contains(ev->pos()) && ev->button() == Qt::LeftButton)
{
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)
{
if (ev->modifiers().testFlag(Qt::ShiftModifier))
{
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
{
else {
on_cmdView_clicked();
}
}
else if (!ui->cbSelected->isVisible() && getContentMode() == 1 && ev->button() == Qt::LeftButton && ev->modifiers().testFlag(Qt::ShiftModifier))
{
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());
}
}
@ -208,8 +247,8 @@ void SnapmaticWidget::mouseDoubleClickEvent(QMouseEvent *ev)
{
ProfileWidget::mouseDoubleClickEvent(ev);
if (!ui->cbSelected->isVisible() && getContentMode() == 1 && ev->button() == Qt::LeftButton)
{
const int contentMode = getContentMode();
if (!ui->cbSelected->isVisible() && (contentMode == 1 || contentMode == 11 || contentMode == 21) && ev->button() == Qt::LeftButton) {
on_cmdView_clicked();
}
}
@ -241,32 +280,27 @@ void SnapmaticWidget::dialogPreviousPictureRequested()
void SnapmaticWidget::on_cbSelected_stateChanged(int arg1)
{
if (arg1 == Qt::Checked)
{
if (arg1 == Qt::Checked) {
emit widgetSelected();
}
else if (arg1 == Qt::Unchecked)
{
else if (arg1 == Qt::Unchecked) {
emit widgetDeselected();
}
}
void SnapmaticWidget::adjustTextColor()
{
if (isHidden())
{
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
{
else {
ui->labPicStr->setStyleSheet("");
}
}
bool SnapmaticWidget::makePictureHidden()
{
if (smpic->setPictureHidden())
{
if (smpic->setPictureHidden()) {
adjustTextColor();
return true;
}
@ -275,8 +309,7 @@ bool SnapmaticWidget::makePictureHidden()
bool SnapmaticWidget::makePictureVisible()
{
if (smpic->setPictureVisible())
{
if (smpic->setPictureVisible()) {
adjustTextColor();
return true;
}
@ -286,17 +319,13 @@ bool SnapmaticWidget::makePictureVisible()
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()
@ -320,43 +349,107 @@ void SnapmaticWidget::editSnapmaticRawJson()
void SnapmaticWidget::editSnapmaticImage()
{
ImageEditorDialog *imageEditor = new ImageEditorDialog(smpic, profileName, this);
imageEditor->setModal(true);
imageEditor->show();
imageEditor->exec();
delete imageEditor;
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;
MapLocationDialog *mapLocDialog = new MapLocationDialog(picture->getSnapmaticProperties().location.x, picture->getSnapmaticProperties().location.y, this);
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())
{
if (mapLocDialog->propUpdated()) {
// Update Snapmatic Properties
SnapmaticProperties localSpJson = picture->getSnapmaticProperties();
localSpJson.location.x = mapLocDialog->getXpos();
localSpJson.location.y = mapLocDialog->getYpos();
localSpJson.location.z = 0;
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))
{
if (!QFile::exists(backupFileName)) {
QFile::copy(currentFilePath, backupFileName);
}
SnapmaticProperties fallbackProperties = picture->getSnapmaticProperties();
picture->setSnapmaticProperties(localSpJson);
if (!picture->exportPicture(currentFilePath))
{
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;
}

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -23,11 +23,6 @@
#include <QDesktopServices>
#endif
StandardPaths::StandardPaths()
{
}
QString StandardPaths::applicationsLocation()
{
#if QT_VERSION >= 0x050000
@ -48,7 +43,9 @@ QString StandardPaths::cacheLocation()
QString StandardPaths::dataLocation()
{
#if QT_VERSION >= 0x050000
#if QT_VERSION >= 0x060000
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
#elif QT_VERSION >= 0x050000
return QStandardPaths::writableLocation(QStandardPaths::DataLocation);
#else
return QDesktopServices::storageLocation(QDesktopServices::DataLocation);

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -24,7 +24,6 @@
class StandardPaths
{
public:
StandardPaths();
static QString applicationsLocation();
static QString cacheLocation();
static QString dataLocation();

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -17,25 +17,16 @@
*****************************************************************************/
#include "StringParser.h"
#include "config.h"
#include <QTextDocument>
#include <QApplication>
#include <QLibraryInfo>
#include <QTextCodec>
#include <QByteArray>
#include <QFileInfo>
#include <QString>
#include <QList>
#include <QDir>
#ifdef GTA5SYNC_PROJECT
#include <QApplication>
#include "config.h"
#endif
StringParser::StringParser()
{
}
QString StringParser::escapeString(const QString &toEscape)
{
#if QT_VERSION >= 0x050000
@ -45,17 +36,19 @@ QString StringParser::escapeString(const QString &toEscape)
#endif
}
#ifdef GTA5SYNC_PROJECT
QString StringParser::convertBuildedString(const QString &buildedStr)
{
QString outputStr = buildedStr;
QByteArray sharePath = GTA5SYNC_SHARE;
outputStr.replace("APPNAME:", GTA5SYNC_APPSTR);
outputStr.replace("SHAREDDIR:", QString::fromUtf8(sharePath));
outputStr.replace("RUNDIR:", QFileInfo(qApp->applicationFilePath()).absoluteDir().absolutePath());
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;
}
#endif

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2017 Syping
* 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
@ -25,11 +25,8 @@
class StringParser
{
public:
StringParser();
static QString escapeString(const QString &toEscape);
#ifdef GTA5SYNC_PROJECT
static QString convertBuildedString(const QString &buildedStr);
#endif
};
#endif // STRINGPARSER_H

View File

@ -41,9 +41,10 @@
#define GTA5SYNC_TELEMETRY_WEBURL ""
#endif
#ifdef GTA5SYNC_WIN
#ifdef Q_OS_WIN
#include "windows.h"
#include "intrin.h"
#include "d3d9.h"
#endif
TelemetryClass TelemetryClass::telemetryClassInstance;
@ -168,6 +169,8 @@ void TelemetryClass::push(TelemetryCategory category)
break;
case TelemetryCategory::UserFeedback:
break;
case TelemetryCategory::PersonalData:
break;
case TelemetryCategory::CustomEmitted:
break;
}
@ -198,6 +201,7 @@ void TelemetryClass::push(TelemetryCategory category, QJsonDocument json)
QNetworkAccessManager *netManager = new QNetworkAccessManager();
QNetworkRequest netRequest(TelemetryClassAuthenticator::getTrackingPushURL());
netRequest.setRawHeader("User-Agent", AppEnv::getUserAgent());
QNetworkReply *netReply = netManager->post(netRequest, httpMultiPart);
httpMultiPart->setParent(netReply);
@ -209,6 +213,10 @@ 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
@ -220,21 +228,21 @@ QJsonDocument TelemetryClass::getSystemHardware()
{
QJsonDocument jsonDocument;
QJsonObject jsonObject;
#ifdef GTA5SYNC_WIN
#ifdef Q_OS_WIN
{
int CPUInfo[4] = {-1};
unsigned nExIds, i = 0;
unsigned nExIds, ic = 0;
char CPUBrandString[0x40];
__cpuid(CPUInfo, 0x80000000);
nExIds = CPUInfo[0];
for (i = 0x80000000; i <= nExIds; ++i)
for (ic = 0x80000000; ic <= nExIds; ic++)
{
__cpuid(CPUInfo, i);
if (i == 0x80000002) { memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo)); }
else if (i == 0x80000003) { memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo)); }
else if (i == 0x80000004) { memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo)); }
__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(CPUBrandString).trimmed();
jsonObject["CPUName"] = QString::fromLatin1(CPUBrandString).simplified();
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
jsonObject["CPUThreads"] = QString::number(sysInfo.dwNumberOfProcessors);
@ -242,6 +250,21 @@ QJsonDocument TelemetryClass::getSystemHardware()
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");
@ -260,7 +283,7 @@ QJsonDocument TelemetryClass::getSystemHardware()
QByteArray cpuData = cpuInfoBuffer.readLine();
if (cpuData.left(toFind.length()) == toFind)
{
jsonObject["CPUName"] = QString::fromUtf8(cpuData).split(':').at(1).trimmed();
jsonObject["CPUName"] = QString::fromUtf8(cpuData).split(':').at(1).simplified();
break;
}
}
@ -315,6 +338,9 @@ QJsonDocument TelemetryClass::getApplicationSpec()
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;
@ -369,6 +395,7 @@ QJsonDocument TelemetryClass::getApplicationConf()
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();
@ -410,11 +437,14 @@ QString TelemetryClass::categoryToString(TelemetryCategory category)
case TelemetryCategory::ApplicationConf:
return QString("ApplicationConf");
break;
case TelemetryCategory::ApplicationSpec:
return QString("ApplicationSpec");
break;
case TelemetryCategory::UserFeedback:
return QString("UserFeedback");
break;
case TelemetryCategory::ApplicationSpec:
return QString("ApplicationSpec");
case TelemetryCategory::PersonalData:
return QString("PersonalData");
break;
case TelemetryCategory::CustomEmitted:
return QString("CustomEmitted");
@ -434,6 +464,7 @@ 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*)));
@ -464,6 +495,10 @@ void TelemetryClass::work_p(bool doWork)
{
push(TelemetryCategory::ApplicationConf);
}
else
{
push(TelemetryCategory::ApplicationConf, QJsonDocument());
}
}
}

View File

@ -25,7 +25,7 @@
#include <QString>
#include <QUrl>
enum class TelemetryCategory : int { OperatingSystemSpec = 0, HardwareSpec = 1, UserLocaleData = 2, ApplicationConf = 3, UserFeedback = 4, ApplicationSpec = 5, CustomEmitted = 99};
enum class TelemetryCategory : int { OperatingSystemSpec = 0, HardwareSpec = 1, UserLocaleData = 2, ApplicationConf = 3, UserFeedback = 4, ApplicationSpec = 5, PersonalData = 6, CustomEmitted = 99 };
class TelemetryClass : public QObject
{

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2017 Syping
* 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
@ -48,22 +48,27 @@ void TranslationClass::initUserLanguage()
void TranslationClass::loadTranslation(QApplication *app)
{
if (isLangLoaded) { unloadTranslation(app); }
else { currentLangIndex = 0; }
QString exLangPath = AppEnv::getExLangFolder();
QString inLangPath = AppEnv::getInLangFolder();
if (userLanguage == "en" || userLanguage == "en_GB")
{
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))
{
if (loadQtTranslation_p(exLangPath, &exQtTranslator)) {
app->installTranslator(&exQtTranslator);
}
else if (loadQtTranslation_p(inLangPath, &inQtTranslator))
{
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;
}
@ -73,33 +78,28 @@ void TranslationClass::loadTranslation(QApplication *app)
bool externalEnglishMode = false;
bool loadInternalLang = false;
bool trLoadSuccess = false;
if (isUserLanguageSystem_p())
{
if (isUserLanguageSystem_p()) {
#ifdef GTA5SYNC_DEBUG
qDebug() << "loadExSystemLanguage";
#endif
trLoadSuccess = loadSystemTranslation_p(exLangPath, &exAppTranslator);
}
else
{
else {
#ifdef GTA5SYNC_DEBUG
qDebug() << "loadExUserLanguage";
#endif
trLoadSuccess = loadUserTranslation_p(exLangPath, &exAppTranslator);
if (!trLoadSuccess)
{
if (!trLoadSuccess) {
#ifdef GTA5SYNC_DEBUG
qDebug() << "loadInUserLanguage";
#endif
trLoadSuccess = loadUserTranslation_p(inLangPath, &inAppTranslator);
if (!trLoadSuccess)
{
if (!trLoadSuccess) {
#ifdef GTA5SYNC_DEBUG
qDebug() << "loadUserLanguageFailed";
#endif
}
else
{
else {
#ifdef GTA5SYNC_DEBUG
qDebug() << "loadUserLanguageSuccess";
#endif
@ -107,18 +107,16 @@ void TranslationClass::loadTranslation(QApplication *app)
isLangLoaded = true;
}
}
else
{
else {
#ifdef GTA5SYNC_DEBUG
qDebug() << "loadUserLanguageSuccess";
#endif
isLangLoaded = true;
}
}
if (trLoadSuccess)
{
if (currentLangIndex != 0 || isEnglishMode) // Don't install the language until we know we not have a better language for the user
{
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
@ -126,33 +124,31 @@ void TranslationClass::loadTranslation(QApplication *app)
externalLanguageStr = currentLanguage;
externalLanguageReady = true;
}
else
{
else {
#ifdef GTA5SYNC_DEBUG
qDebug() << "installTranslation";
#endif
if (loadInternalLang)
{
if (loadInternalLang) {
app->installTranslator(&inAppTranslator);
}
else
{
else {
app->installTranslator(&exAppTranslator);
}
if (loadQtTranslation_p(exLangPath, &exQtTranslator))
{
if (loadQtTranslation_p(exLangPath, &exQtTranslator)) {
app->installTranslator(&exQtTranslator);
}
else if (loadQtTranslation_p(inLangPath, &inQtTranslator))
{
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)
{
if (externalLanguageReady) {
#ifdef GTA5SYNC_DEBUG
qDebug() << "loadInSystemLanguage";
#endif
@ -162,113 +158,114 @@ void TranslationClass::loadTranslation(QApplication *app)
qDebug() << "externalLangIndex" << externalLangIndex << "internalLangIndex" << currentLangIndex;
qDebug() << "externalEnglishMode" << externalEnglishMode << "internalEnglishMode" << isEnglishMode;
#endif
if ((trLoadSuccess && externalLangIndex > currentLangIndex) || (trLoadSuccess && externalEnglishMode && !isEnglishMode))
{
if ((trLoadSuccess && externalLangIndex > currentLangIndex) || (trLoadSuccess && externalEnglishMode && !isEnglishMode)) {
#ifdef GTA5SYNC_DEBUG
qDebug() << "installInternalTranslation";
#endif
app->installTranslator(&inAppTranslator);
if (loadQtTranslation_p(exLangPath, &exQtTranslator))
{
if (loadQtTranslation_p(exLangPath, &exQtTranslator)) {
app->installTranslator(&exQtTranslator);
}
else if (loadQtTranslation_p(inLangPath, &inQtTranslator))
{
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
{
else {
#ifdef GTA5SYNC_DEBUG
qDebug() << "installExternalTranslation";
#endif
isEnglishMode = externalEnglishMode;
currentLanguage = externalLanguageStr;
app->installTranslator(&exAppTranslator);
if (loadQtTranslation_p(exLangPath, &exQtTranslator))
{
if (loadQtTranslation_p(exLangPath, &exQtTranslator)) {
app->installTranslator(&exQtTranslator);
}
else if (loadQtTranslation_p(inLangPath, &inQtTranslator))
{
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)
{
else if (!isLangLoaded) {
#ifdef GTA5SYNC_DEBUG
qDebug() << "loadInSystemLanguage";
#endif
trLoadSuccess = loadSystemTranslation_p(inLangPath, &inAppTranslator);
if (trLoadSuccess)
{
if (trLoadSuccess) {
#ifdef GTA5SYNC_DEBUG
qDebug() << "installInternalTranslation";
#endif
app->installTranslator(&inAppTranslator);
if (loadQtTranslation_p(exLangPath, &exQtTranslator))
{
if (loadQtTranslation_p(exLangPath, &exQtTranslator)) {
app->installTranslator(&exQtTranslator);
}
else if (loadQtTranslation_p(inLangPath, &inQtTranslator))
{
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)
{
else if (!trLoadSuccess) {
#ifdef GTA5SYNC_DEBUG
qDebug() << "fallbackToDefaultApplicationLanguage";
#endif
currentLanguage = "en_GB";
if (loadQtTranslation_p(exLangPath, &exQtTranslator))
{
if (loadQtTranslation_p(exLangPath, &exQtTranslator)) {
app->installTranslator(&exQtTranslator);
}
else if (loadQtTranslation_p(inLangPath, &inQtTranslator))
{
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())
{
if (isUserLanguageSystem_p()) {
trLoadSuccess = loadSystemTranslation_p(inLangPath, &inAppTranslator);
}
else
{
else {
trLoadSuccess = loadUserTranslation_p(inLangPath, &inAppTranslator);
}
if (!trLoadSuccess && !isUserLanguageSystem_p())
{
if (!trLoadSuccess && !isUserLanguageSystem_p()) {
trLoadSuccess = loadSystemTranslation_p(inLangPath, &inAppTranslator);
}
if (trLoadSuccess)
{
if (trLoadSuccess) {
#ifdef GTA5SYNC_DEBUG
qDebug() << "installTranslation" << currentLanguage;
#endif
app->installTranslator(&inAppTranslator);
if (loadQtTranslation_p(exLangPath, &exQtTranslator))
{
if (loadQtTranslation_p(exLangPath, &exQtTranslator)) {
app->installTranslator(&exQtTranslator);
}
else if (loadQtTranslation_p(inLangPath, &inQtTranslator))
{
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
@ -280,8 +277,7 @@ QStringList TranslationClass::listTranslations(const QString &langPath)
langDir.setNameFilters(QStringList("gta5sync_*.qm"));
langDir.setPath(langPath);
QStringList availableLanguages;
for (QString lang : langDir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::NoSort))
{
for (const QString &lang : langDir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::NoSort)) {
availableLanguages << QString(lang).remove("gta5sync_").remove(".qm");
}
return availableLanguages;
@ -293,8 +289,7 @@ QStringList TranslationClass::listAreaTranslations()
langDir.setNameFilters(QStringList("global.*.ini"));
langDir.setPath(":/global");
QStringList availableLanguages;
for (QString lang : langDir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::NoSort))
{
for (const QString &lang : langDir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::NoSort)) {
availableLanguages << QString(lang).remove("global.").remove(".ini");
}
return availableLanguages;
@ -306,21 +301,17 @@ bool TranslationClass::loadSystemTranslation_p(const QString &langPath, QTransla
qDebug() << "loadSystemTranslation_p";
#endif
int currentLangCounter = 0;
for (QString languageName : QLocale::system().uiLanguages())
{
for (const QString &languageName : QLocale::system().uiLanguages()) {
#ifdef GTA5SYNC_DEBUG
qDebug() << "loadLanguage" << languageName;
#endif
QStringList langList = QString(languageName).replace("-","_").split("_");
if (langList.length() == 2)
{
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"))
{
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
@ -333,10 +324,8 @@ bool TranslationClass::loadSystemTranslation_p(const QString &langPath, QTransla
#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"))
{
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
@ -345,8 +334,7 @@ bool TranslationClass::loadSystemTranslation_p(const QString &langPath, QTransla
currentLangIndex = currentLangCounter;
return true;
}
else if (langList.at(0) == "en")
{
else if (langList.at(0) == "en") {
#ifdef GTA5SYNC_DEBUG
qDebug() << "languageEnglishMode index" << currentLangCounter;
#endif
@ -356,8 +344,7 @@ bool TranslationClass::loadSystemTranslation_p(const QString &langPath, QTransla
return true;
}
}
else if (langList.at(0) == "en")
{
else if (langList.at(0) == "en") {
#ifdef GTA5SYNC_DEBUG
qDebug() << "languageEnglishMode index" << currentLangCounter;
#endif
@ -367,15 +354,12 @@ bool TranslationClass::loadSystemTranslation_p(const QString &langPath, QTransla
return true;
}
}
else if (langList.length() == 1)
{
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"))
{
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
@ -399,17 +383,14 @@ bool TranslationClass::loadUserTranslation_p(const QString &langPath, QTranslato
#ifdef GTA5SYNC_DEBUG
qDebug() << "loadUserTranslation_p";
#endif
QString languageName = userLanguage;
QStringList langList = QString(languageName).replace("-","_").split("_");
if (langList.length() == 2)
{
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"))
{
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
@ -420,10 +401,8 @@ bool TranslationClass::loadUserTranslation_p(const QString &langPath, QTranslato
#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"))
{
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
@ -432,15 +411,12 @@ bool TranslationClass::loadUserTranslation_p(const QString &langPath, QTranslato
}
}
}
else if (langList.length() == 1)
{
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"))
{
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
@ -457,17 +433,14 @@ bool TranslationClass::loadQtTranslation_p(const QString &langPath, QTranslator
#ifdef GTA5SYNC_DEBUG
qDebug() << "loadQtTranslation_p" << currentLanguage;
#endif
QString languageName = currentLanguage;
QStringList langList = QString(languageName).replace("-","_").split("_");
if (langList.length() == 2)
{
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"))
{
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
@ -477,10 +450,8 @@ bool TranslationClass::loadQtTranslation_p(const QString &langPath, QTranslator
#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"))
{
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
@ -488,15 +459,12 @@ bool TranslationClass::loadQtTranslation_p(const QString &langPath, QTranslator
}
}
}
else if (langList.length() == 1)
{
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"))
{
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
@ -515,40 +483,61 @@ bool TranslationClass::isUserLanguageSystem_p()
QString TranslationClass::getCurrentAreaLanguage()
{
const QStringList areaTranslations = listAreaTranslations();
if (userAreaLanguage == "Auto" || userAreaLanguage.trimmed().isEmpty())
{
if (userAreaLanguage == "Auto" || userAreaLanguage.trimmed().isEmpty()) {
const GameLanguage gameLanguage = AppEnv::getGameLanguage(AppEnv::getGameVersion());
if (gameLanguage == GameLanguage::Undefined) {
#ifdef GTA5SYNC_DEBUG
qDebug() << "autoAreaLanguageMode";
qDebug() << "autoAreaLanguageModeInterface";
#endif
QString langCode = QString(currentLanguage).replace("-", "_");
if (areaTranslations.contains(langCode))
{
QString langCode = QString(currentLanguage).replace("-", "_");
if (areaTranslations.contains(langCode)) {
#ifdef GTA5SYNC_DEBUG
qDebug() << "autoAreaLanguageSelected" << langCode;
qDebug() << "autoAreaLanguageSelected" << langCode;
#endif
return langCode;
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 (langCode.contains("_"))
{
langCode = langCode.split("_").at(0);
if (!areaTranslations.contains(langCode)) goto outputDefaultLanguage;
else {
#ifdef GTA5SYNC_DEBUG
qDebug() << "autoAreaLanguageSelected" << langCode;
qDebug() << "autoAreaLanguageModeGame";
#endif
return langCode;
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))
{
else if (areaTranslations.contains(userAreaLanguage)) {
#ifdef GTA5SYNC_DEBUG
qDebug() << "userAreaLanguageSelected" << userAreaLanguage;
#endif
return userAreaLanguage;
}
else if (userAreaLanguage.contains("_"))
{
QString langCode = QString(userAreaLanguage).replace("-", "_").split("_").at(0);
if (!areaTranslations.contains(langCode)) goto outputDefaultLanguage;
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
@ -573,8 +562,7 @@ bool TranslationClass::isLanguageLoaded()
void TranslationClass::unloadTranslation(QApplication *app)
{
if (isLangLoaded)
{
if (isLangLoaded) {
#ifndef GTA5SYNC_QCONF
app->removeTranslator(&exAppTranslator);
app->removeTranslator(&exQtTranslator);
@ -597,30 +585,21 @@ void TranslationClass::unloadTranslation(QApplication *app)
QString TranslationClass::getCountryCode(QLocale::Country country)
{
QList<QLocale> locales = QLocale::matchingLocales(QLocale::AnyLanguage,
QLocale::AnyScript,
country);
if (locales.isEmpty()) return QString();
QStringList localeStrList = locales.at(0).name().split("_");
if (localeStrList.length() >= 2)
{
return localeStrList.at(1).toLower();
}
else
{
return QString();
const QList<QLocale> 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)
{
if (localeStrList.length() >= 2) {
return localeStrList.at(1).toLower();
}
else
{
return QString();
}
return QString();
}

View File

@ -1,6 +1,6 @@
/*****************************************************************************
* gta5view Grand Theft Auto V Profile Viewer
* Copyright (C) 2016-2018 Syping
* 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
@ -40,14 +40,31 @@
#include <QMessageBox>
#include <QSettings>
#include <QFileInfo>
#include <QTimer>
#include <QDebug>
#include <QFile>
#include <QDir>
#include <QMap>
#ifdef GTA5SYNC_DONATE
#ifdef GTA5SYNC_DONATE_ADDRESSES
#include <QSvgRenderer>
#include <QClipboard>
#include <QPainter>
#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;
@ -56,107 +73,244 @@ UserInterface::UserInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, D
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);
this->setWindowTitle(defaultWindowTitle.arg(tr("Select Profile")));
ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER));
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"))
{
if (QIcon::hasThemeIcon("dialog-close")) {
ui->cmdClose->setIcon(QIcon::fromTheme("dialog-close"));
}
else if (QIcon::hasThemeIcon("gtk-close"))
{
else if (QIcon::hasThemeIcon("gtk-close")) {
ui->cmdClose->setIcon(QIcon::fromTheme("gtk-close"));
}
// Set Icon for Reload Button
if (QIcon::hasThemeIcon("view-refresh"))
{
if (QIcon::hasThemeIcon("view-refresh")) {
ui->cmdReload->setIcon(QIcon::fromTheme("view-refresh"));
}
else if (QIcon::hasThemeIcon("reload"))
{
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"))
{
if (QIcon::hasThemeIcon("document-open-folder")) {
ui->actionSelect_GTA_Folder->setIcon(QIcon::fromTheme("document-open-folder"));
}
else if (QIcon::hasThemeIcon("gtk-directory"))
{
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"))
{
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"))
{
if (QIcon::hasThemeIcon("dialog-close")) {
ui->actionSelect_profile->setIcon(QIcon::fromTheme("dialog-close"));
}
else if (QIcon::hasThemeIcon("gtk-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"))
{
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"))
{
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"))
{
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"))
{
if (QIcon::hasThemeIcon("document-import")) {
ui->action_Import->setIcon(QIcon::fromTheme("document-import"));
}
else if (QIcon::hasThemeIcon("document-open"))
{
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"))
{
if (QIcon::hasThemeIcon("document-export")) {
ui->actionExport_selected->setIcon(QIcon::fromTheme("document-export"));
}
else if (QIcon::hasThemeIcon("document-save"))
{
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"))
{
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("<b>%1</b>").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
@ -166,30 +320,26 @@ UserInterface::UserInterface(ProfileDatabase *profileDB, CrewDatabase *crewDB, D
ui->vlUserInterface->setContentsMargins(9 * screenRatio, 9 * screenRatio, 9 * screenRatio, 9 * screenRatio);
}
void UserInterface::setupDirEnv()
void UserInterface::setupDirEnv(bool showFolderDialog)
{
// settings init
QSettings settings(GTA5SYNC_APPVENDOR, GTA5SYNC_APPSTR);
bool folderExists;
GTAV_Folder = AppEnv::getGameFolder(&folderExists);
if (folderExists)
{
if (folderExists) {
QDir::setCurrent(GTAV_Folder);
}
else
{
else if (showFolderDialog) {
GTAV_Folder = QFileDialog::getExistingDirectory(this, tr("Select GTA V Folder..."), StandardPaths::documentsLocation(), QFileDialog::ShowDirsOnly);
if (QFileInfo(GTAV_Folder).exists())
{
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())
{
if (settings.value("dir", "").toString().isEmpty()) {
settings.setValue("dir", GTAV_Folder);
}
settings.endGroup();
@ -200,15 +350,15 @@ void UserInterface::setupDirEnv()
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;
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)
{
if (folderExists) {
QDir GTAV_ProfilesDir;
GTAV_ProfilesFolder = GTAV_Folder % "/Profiles";
GTAV_ProfilesDir.setPath(GTAV_ProfilesFolder);
@ -216,17 +366,14 @@ void UserInterface::setupDirEnv()
GTAV_Profiles = GTAV_ProfilesDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::NoSort);
setupProfileUi();
if (GTAV_Profiles.length() == 1)
{
if (GTAV_Profiles.length() == 1) {
openProfile(GTAV_Profiles.at(0));
}
else if(GTAV_Profiles.contains(defaultProfile))
{
else if(GTAV_Profiles.contains(defaultProfile)) {
openProfile(defaultProfile);
}
}
else
{
else {
GTAV_Profiles = QStringList();
setupProfileUi();
}
@ -236,8 +383,7 @@ void UserInterface::setupDirEnv()
void UserInterface::setupProfileUi()
{
qreal screenRatio = AppEnv::screenRatio();
if (GTAV_Profiles.isEmpty())
{
if (GTAV_Profiles.isEmpty()) {
QPushButton *changeDirBtn = new QPushButton(tr("Select &GTA V Folder..."), ui->swSelection);
changeDirBtn->setObjectName("cmdChangeDir");
changeDirBtn->setMinimumSize(0, 40 * screenRatio);
@ -247,8 +393,7 @@ void UserInterface::setupProfileUi()
QObject::connect(changeDirBtn, SIGNAL(clicked(bool)), this, SLOT(changeFolder_clicked()));
}
else for (QString GTAV_Profile : GTAV_Profiles)
{
else for (const QString &GTAV_Profile : GTAV_Profiles) {
QPushButton *profileBtn = new QPushButton(GTAV_Profile, ui->swSelection);
profileBtn->setObjectName(GTAV_Profile);
profileBtn->setMinimumSize(0, 40 * screenRatio);
@ -268,8 +413,7 @@ void UserInterface::changeFolder_clicked()
void UserInterface::on_cmdReload_clicked()
{
for (QPushButton *profileBtn : profileBtns)
{
for (QPushButton *profileBtn : profileBtns) {
ui->vlButtons->removeWidget(profileBtn);
delete profileBtn;
}
@ -295,16 +439,15 @@ void UserInterface::openProfile(const QString &profileName_)
profileUI->setupProfileInterface();
QObject::connect(profileUI, SIGNAL(profileClosed()), this, SLOT(closeProfile()));
QObject::connect(profileUI, SIGNAL(profileLoaded()), this, SLOT(profileLoaded()));
this->setWindowTitle(defaultWindowTitle.arg(profileName));
setWindowTitle(defaultWindowTitle.arg(profileName));
}
void UserInterface::closeProfile()
{
if (profileOpen)
{
if (profileOpen) {
closeProfile_p();
}
this->setWindowTitle(defaultWindowTitle.arg(tr("Select Profile")));
setWindowTitle(defaultWindowTitle.arg(tr("Select Profile")));
}
void UserInterface::closeProfile_p()
@ -322,17 +465,18 @@ void UserInterface::closeProfile_p()
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)
{
for (QPushButton *profileBtn : profileBtns) {
delete profileBtn;
}
profileBtns.clear();
@ -341,7 +485,7 @@ UserInterface::~UserInterface()
void UserInterface::on_actionExit_triggered()
{
this->close();
close();
}
void UserInterface::on_actionSelect_profile_triggered()
@ -379,33 +523,25 @@ void UserInterface::profileLoaded()
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()
@ -430,9 +566,7 @@ void UserInterface::on_actionOptions_triggered()
void UserInterface::on_action_Import_triggered()
{
if (profileOpen)
{
profileUI->importFiles();
}
}
void UserInterface::on_actionOpen_File_triggered()
@ -446,7 +580,11 @@ fileDialogPreOpen:
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;
@ -463,11 +601,9 @@ fileDialogPreOpen:
fileDialog.setDirectory(settings.value("OpenDialogDirectory", StandardPaths::documentsLocation()).toString());
fileDialog.restoreGeometry(settings.value("OpenDialogGeometry","").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 (!openFile(selectedFile, true)) goto fileDialogPreOpen;
}
@ -481,68 +617,61 @@ fileDialogPreOpen:
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")
{
if (QFile::exists(selectedFile)) {
if (selectedFileName.left(4) == "PGTA" || selectedFileName.right(4) == ".g5e") {
SnapmaticPicture *picture = new SnapmaticPicture(selectedFile);
if (picture->readingPicture())
{
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"));
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")
{
else if (selectedFileName.left(4) == "SGTA") {
SavegameData *savegame = new SavegameData(selectedFile);
if (savegame->readingSavegame())
{
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"));
else {
if (warn)
QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("Failed to read Savegame file"));
delete savegame;
return false;
}
}
else
{
else {
SnapmaticPicture *picture = new SnapmaticPicture(selectedFile);
SavegameData *savegame = new SavegameData(selectedFile);
if (picture->readingPicture())
{
if (picture->readingPicture()) {
delete savegame;
openSnapmaticFile(picture);
delete picture;
return true;
}
else if (savegame->readingSavegame())
{
else if (savegame->readingSavegame()) {
delete picture;
openSavegameFile(savegame);
delete savegame;
return true;
}
else
{
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+"\""));
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"));
if (warn)
QMessageBox::warning(this, tr("Open File"), ProfileInterface::tr("No valid file is selected"));
return false;
}
@ -553,7 +682,8 @@ void UserInterface::openSnapmaticFile(SnapmaticPicture *picture)
picDialog.setModal(true);
int crewID = picture->getSnapmaticProperties().crewID;
if (crewID != 0) { crewDB->addCrew(crewID); }
if (crewID != 0)
crewDB->addCrew(crewID);
QObject::connect(threadDB, SIGNAL(crewNameUpdated()), &picDialog, SLOT(crewNameUpdated()));
QObject::connect(threadDB, SIGNAL(playerNameUpdated()), &picDialog, SLOT(playerNameUpdated()));
@ -586,24 +716,139 @@ void UserInterface::openSavegameFile(SavegameData *savegame)
void UserInterface::settingsApplied(int _contentMode, bool languageChanged)
{
if (languageChanged)
{
if (languageChanged) {
retranslateUi();
}
contentMode = _contentMode;
if (profileOpen)
{
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 (QFileInfo(GTAV_Folder_Temp).exists())
{
if (profileOpen)
{
if (QDir(GTAV_Folder_Temp).exists()) {
if (profileOpen) {
closeProfile_p();
}
GTAV_Folder = GTAV_Folder_Temp;
@ -616,31 +861,41 @@ void UserInterface::on_actionSelect_GTA_Folder_triggered()
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));
ui->labVersion->setText(QString("%1 %2").arg(GTA5SYNC_APPSTR, GTA5SYNC_APPVER));
if (profileOpen)
{
this->setWindowTitle(defaultWindowTitle.arg(profileName));
#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
{
this->setWindowTitle(defaultWindowTitle.arg(tr("Select Profile")));
else {
setWindowTitle(defaultWindowTitle.arg(tr("Select Profile")));
}
}

View File

@ -31,6 +31,10 @@
#include <QString>
#include <QMap>
#ifdef GTA5SYNC_MOTD
#include "MessageThread.h"
#endif
namespace Ui {
class UserInterface;
}
@ -39,8 +43,12 @@ 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);
void setupDirEnv();
#endif
void setupDirEnv(bool showFolderDialog = true);
~UserInterface();
private slots:
@ -67,6 +75,11 @@ private slots:
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);
@ -75,6 +88,14 @@ 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<QPushButton*> profileBtns;

862
anpro/QrCode.cpp Normal file
View File

@ -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 <algorithm>
#include <climits>
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <stdexcept>
#include <utility>
#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<uint8_t> &data) {
if (data.size() > static_cast<unsigned int>(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<int>(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<uint32_t>(accumData), 10);
accumData = 0;
accumCount = 0;
}
}
if (accumCount > 0) // 1 or 2 digits remaining
bb.appendBits(static_cast<uint32_t>(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<int>(temp - ALPHANUMERIC_CHARSET);
accumCount++;
if (accumCount == 2) {
bb.appendBits(static_cast<uint32_t>(accumData), 11);
accumData = 0;
accumCount = 0;
}
}
if (accumCount > 0) // 1 character remaining
bb.appendBits(static_cast<uint32_t>(accumData), 6);
return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb));
}
vector<QrSegment> QrSegment::makeSegments(const char *text) {
// Select the most efficient segment encoding automatically
vector<QrSegment> 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<uint8_t> bytes;
for (; *text != '\0'; text++)
bytes.push_back(static_cast<uint8_t>(*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<uint32_t>(assignVal), 8);
else if (assignVal < (1 << 14)) {
bb.appendBits(2, 2);
bb.appendBits(static_cast<uint32_t>(assignVal), 14);
} else if (assignVal < 1000000L) {
bb.appendBits(6, 3);
bb.appendBits(static_cast<uint32_t>(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<bool> &dt) :
mode(md),
numChars(numCh),
data(dt) {
if (numCh < 0)
throw std::domain_error("Invalid value");
}
QrSegment::QrSegment(Mode md, int numCh, std::vector<bool> &&dt) :
mode(md),
numChars(numCh),
data(std::move(dt)) {
if (numCh < 0)
throw std::domain_error("Invalid value");
}
int QrSegment::getTotalBits(const vector<QrSegment> &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<unsigned int>(INT_MAX - result))
return -1; // The sum will overflow an int type
result += static_cast<int>(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<bool> &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<QrSegment> segs = QrSegment::makeSegments(text);
return encodeSegments(segs, ecl);
}
QrCode QrCode::encodeBinary(const vector<uint8_t> &data, Ecc ecl) {
vector<QrSegment> segs{QrSegment::makeBytes(data)};
return encodeSegments(segs, ecl);
}
QrCode QrCode::encodeSegments(const vector<QrSegment> &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>{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<uint32_t>(seg.getMode().getModeBits()), 4);
bb.appendBits(static_cast<uint32_t>(seg.getNumChars()), seg.getMode().numCharCountBits(version));
bb.insert(bb.end(), seg.getData().begin(), seg.getData().end());
}
if (bb.size() != static_cast<unsigned int>(dataUsedBits))
throw std::logic_error("Assertion error");
// Add terminator and pad up to a byte if applicable
size_t dataCapacityBits = static_cast<size_t>(getNumDataCodewords(version, ecl)) * 8;
if (bb.size() > dataCapacityBits)
throw std::logic_error("Assertion error");
bb.appendBits(0, std::min(4, static_cast<int>(dataCapacityBits - bb.size())));
bb.appendBits(0, (8 - static_cast<int>(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<uint8_t> 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<uint8_t> &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_t>(size);
modules = vector<vector<bool> >(sz, vector<bool>(sz)); // Initially all white
isFunction = vector<vector<bool> >(sz, vector<bool>(sz));
// Compute ECC, draw modules
drawFunctionPatterns();
const vector<uint8_t> 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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
sb << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
sb << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 ";
sb << (size + border * 2) << " " << (size + border * 2) << "\" stroke=\"none\">\n";
sb << "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n";
sb << "\t<path d=\"";
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
if (getModule(x, y)) {
if (x != 0 || y != 0)
sb << " ";
sb << "M" << (x + border) << "," << (y + border) << "h1v1h-1z";
}
}
}
sb << "\" fill=\"#000000\"/>\n";
sb << "</svg>\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<int> 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<long>(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<size_t>(x);
size_t uy = static_cast<size_t>(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<size_t>(y)).at(static_cast<size_t>(x));
}
vector<uint8_t> QrCode::addEccAndInterleave(const vector<uint8_t> &data) const {
if (data.size() != static_cast<unsigned int>(getNumDataCodewords(version, errorCorrectionLevel)))
throw std::invalid_argument("Invalid argument");
// Calculate parameter numbers
int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(errorCorrectionLevel)][version];
int blockEccLen = ECC_CODEWORDS_PER_BLOCK [static_cast<int>(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<vector<uint8_t> > blocks;
const vector<uint8_t> rsDiv = reedSolomonComputeDivisor(blockEccLen);
for (int i = 0, k = 0; i < numBlocks; i++) {
vector<uint8_t> dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)));
k += static_cast<int>(dat.size());
const vector<uint8_t> 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<uint8_t> 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<unsigned int>(shortBlockLen - blockEccLen) || j >= static_cast<unsigned int>(numShortBlocks))
result.push_back(blocks.at(j).at(i));
}
}
if (result.size() != static_cast<unsigned int>(rawCodewords))
throw std::logic_error("Assertion error");
return result;
}
void QrCode::drawCodewords(const vector<uint8_t> &data) {
if (data.size() != static_cast<unsigned int>(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<size_t>(right - j); // Actual x coordinate
bool upward = ((right + 1) & 2) == 0;
size_t y = static_cast<size_t>(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<int>(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_t>(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<int,7> 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<int,7> 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<bool> &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<int>((std::abs(black * 20L - total * 10L) + total - 1) / total) - 1;
result += k * PENALTY_N4;
return result;
}
vector<int> QrCode::getAlignmentPatternPositions() const {
if (version == 1)
return vector<int>();
else {
int numAlign = version / 7 + 2;
int step = (version == 32) ? 26 :
(version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2;
vector<int> 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<int>(ecl)][ver]
* NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(ecl)][ver];
}
vector<uint8_t> 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<uint8_t> result(static_cast<size_t>(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<uint8_t> QrCode::reedSolomonComputeRemainder(const vector<uint8_t> &data, const vector<uint8_t> &divisor) {
vector<uint8_t> 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<uint8_t>(z);
}
int QrCode::finderPenaltyCountPatterns(const std::array<int,7> &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<int,7> &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<int,7> &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<bool>() {}
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);
}
}

556
anpro/QrCode.h Normal file
View File

@ -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 <array>
#include <cstdint>
#include <stdexcept>
#include <string>
#include <vector>
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<std::uint8_t> &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<QrSegment> 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<bool> 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<bool> &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<bool> &&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<bool> &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<QrSegment> &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<std::uint8_t> &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<QrSegment> &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<std::vector<bool> > modules;
// Indicates function modules that are not subjected to masking. Discarded when constructor finishes.
private: std::vector<std::vector<bool> > 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<std::uint8_t> &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<std::uint8_t> addEccAndInterleave(const std::vector<std::uint8_t> &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<std::uint8_t> &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<int> 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<std::uint8_t> reedSolomonComputeDivisor(int degree);
// Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials.
private: static std::vector<std::uint8_t> reedSolomonComputeRemainder(const std::vector<std::uint8_t> &data, const std::vector<std::uint8_t> &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<int,7> &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<int,7> &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<int,7> &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<bool> {
/*---- 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);
};
}

Some files were not shown because too many files have changed in this diff Show More