├── CMakeLists.txt
├── CMakeLists.txt.user
├── README.md
├── Readme_ru.md
├── android
├── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── res
│ ├── drawable-hdpi
│ └── icon.png
│ ├── drawable-ldpi
│ └── icon.png
│ ├── drawable-mdpi
│ └── icon.png
│ ├── drawable-xhdpi
│ └── icon.png
│ ├── drawable-xxhdpi
│ └── icon.png
│ ├── drawable-xxxhdpi
│ └── icon.png
│ ├── values
│ └── libs.xml
│ └── xml
│ └── qtprovider_paths.xml
├── headers
├── mainwindow.h
└── processing.h
├── res
├── Pusia-Bold.otf
├── icon.ico
├── rc.rc
├── res.qrc
└── translate
│ ├── X-coder_ru.qm
│ └── X-coder_ru.ts
├── source
├── SWFFile.hpp
├── config.hpp
├── json.hpp
├── main.cpp
├── mainwindow.cpp
└── processing.cpp
└── ui
├── mainwindow.ui
└── processing.ui
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.16)
2 |
3 | project(X-coder VERSION 0.1 LANGUAGES CXX)
4 |
5 | set(CMAKE_AUTOUIC ON)
6 | set(CMAKE_AUTOMOC ON)
7 | set(CMAKE_AUTORCC ON)
8 |
9 | set(CMAKE_CXX_STANDARD 17)
10 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
11 |
12 | find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
13 | find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Qml LinguistTools)
14 |
15 |
16 | set(TS_FILES res/translate/X-coder_ru.ts)
17 | set(PROJECT_SOURCES
18 | source/main.cpp
19 | source/mainwindow.cpp
20 | source/processing.cpp
21 | source/json.hpp
22 | source/SWFFile.hpp
23 | source/config.hpp
24 | headers/mainwindow.h
25 | headers/processing.h
26 | ui/mainwindow.ui
27 | ui/processing.ui
28 | ${TS_FILES}
29 | )
30 |
31 | if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
32 | qt_add_executable(X-coder
33 | MANUAL_FINALIZATION
34 | ${PROJECT_SOURCES}
35 | android/AndroidManifest.xml
36 | res/rc.rc
37 | res/res.qrc
38 | )
39 | endif()
40 | target_link_libraries(X-coder PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt6::Network)
41 | include_directories(headers ui source)
42 | if(${QT_VERSION} VERSION_LESS 6.1.0)
43 | set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.X-coder)
44 | endif()
45 | set_target_properties(X-coder PROPERTIES
46 | ${BUNDLE_ID_OPTION}
47 | MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
48 | MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
49 | MACOSX_BUNDLE TRUE
50 | WIN32_EXECUTABLE TRUE
51 | )
52 |
53 | include(GNUInstallDirs)
54 | install(TARGETS X-coder
55 | BUNDLE DESTINATION .
56 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
57 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
58 | )
59 | set_property(TARGET X-coder APPEND PROPERTY
60 | QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android
61 | )
62 | include(FetchContent)
63 | # Adding SupercellFlash
64 | FetchContent_Declare(
65 | SupercellFlash
66 | GIT_REPOSITORY https://github.com/sc-workshop/SupercellFlash.git
67 | GIT_TAG dev
68 | )
69 | FetchContent_MakeAvailable(SupercellFlash)
70 |
71 | # CPP 17
72 | target_compile_features(
73 | X-coder PRIVATE
74 | cxx_std_17
75 | )
76 |
77 | target_link_libraries(
78 | X-coder PRIVATE
79 | SupercellFlash
80 | )
81 | qt6_add_translations(X-coder
82 | TS_FILES ${TS_FILES}
83 | QM_FILES_OUTPUT_VARIABLE qm_files)
84 | #install(FILES ${qm_files} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR})
85 | if (ANDROID)
86 | include(C:/DEV/ANDROID/SDK/android_openssl/android_openssl.cmake)
87 | add_android_openssl_libraries(X-coder)
88 | set_property (TARGET X-coder APPEND PROPERTY
89 | QT_ANDROID_EXTRA_LIBS "C:/DEV/ANDROID/SDK/android_openssl/ssl_3/arm64-v8a/libcrypto_3.so" "C:/DEV/ANDROID/SDK/android_openssl/ssl_3/arm64-v8a/libssl_3.so" "C:/DEV/ANDROID/SDK/android_openssl/ssl_3/armeabi-v7a/libcrypto_3.so" "C:/DEV/ANDROID/SDK/android_openssl/ssl_3/armeabi-v7a/libssl_3.so"
90 | )
91 | endif()
92 | if(QT_VERSION_MAJOR EQUAL 6)
93 | qt_finalize_executable(X-coder)
94 | endif()
95 |
96 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #
X-coder
2 | [ДЛЯ РУССКИХ](https://github.com/lilmuff2/X-coder/blob/master/Readme_ru.md)
3 |
4 | A tool for decoding (sc to png) and encoding (png to sc) sc files used in Supercell games.
5 | 
6 | # How to use
7 | 1. Download the version you need from [releases](https://github.com/lilmuff2/X-coder/releases/tag/v2.2 ) (apk - for android, setup.exe - installer for Windows, zip archive with exe for Windows)
8 | 3. Select the desired option (each one is described below)
9 | 5. Select or type the input file or folder
10 | 7. Optionally select either type the out folder or file, or it will be selected automatically !IT WILL BE DELETED!
11 | 8. Press the button at the bottom (not close) and wait
12 |
13 |
14 | # DECODE FILE:
15 | Converts sc to png
16 | Input file - _tex.sc , not .sc! or _dl.sc in rare cases
17 | Starting with the 54th version of brawl stars, zktx files appeared. They need to be placed in the same folder as the input file
18 | Out folder - the folder in which the apg will be saved
19 |
20 | 
21 |
22 | # ENCODE FOLDER:
23 | Converts png to sc
24 | Input folder - folder with png and [json file](#json-file)(optionally)
25 | The name of the json file must match the name of the folder
26 | Out file - where to save sc
27 | If you need sc for the old version, change the compression in the json file from Zstandard to Lzma
28 |
29 | 
30 |
31 |
32 | # DECODE FILES:
33 | Converts a lot of sc files to png, it is usfull to simply copy the sc folder from the apk and decode them all at once
34 | Input folder - folder with _tex.sc , not .sc! or _dl.sc files
35 | Outfolder - the folder where the folders will be created in which the pngs will be saved
36 |
37 | 
38 |
39 |
40 | # ENCODE FOLDERS:
41 | Converts many png folders to sc files
42 | Input folder - a folder with folders containing png
43 | Output folder - the folder where the sc files will be saved
44 |
45 | 
46 | # .json file:
47 | This file generated after decoding example:
48 | ```javascript
49 | {
50 | "Textures": [
51 | {
52 | "Encoding": "khronos",
53 | "PixelFormat": "RGBA8",
54 | "Filtering": "LINEAR_NEAREST",
55 | "Linear": true
56 | },
57 | {
58 | "Encoding": "khronos",
59 | "PixelFormat": "RGBA8",
60 | "Filtering": "LINEAR_NEAREST",
61 | "Linear": true
62 | }
63 | ],
64 | "IsDL": false,
65 | "HasZKTX": true,
66 | "Compression": "Zstandard"
67 | }
68 | ```
69 |
70 | ## Encoding
71 | Can be khronos (low weight) and raw (fast encoding) if HasZKTX enabled textures are encoded in khronos
72 |
73 | Example ui_highres_tex with Zstandard compression:
74 | Encoding | Encoding time | File size
75 | ---|---|---
76 | khronos | 35 s | 30 MB
77 | raw | 7 s | 41 MB
78 | > I recommend using raw during mod development and khronos on release.
79 | ## HasZKTX
80 | Parameter responsible for the existence of zktk files
81 | ## ISDL
82 | Parameter responsible for creating dl file (combining sc and tex_sc), usually used in Clash Royale requires sc file in the folder.
83 | ### Compression
84 | File compression, with HasZKTX enabled, Zstandard is always used.
85 | Example of compression on ui_highres_tex
86 | Compression | File encoding time | File weight | Total
87 | --- | --- | --- | ---
88 | Lzma | 41 s | 26MB| Longer but compresses better.
89 | Lzham | 73sec | 27MB | It's useless.
90 | Zstandard | 35secs | 30MB | Standard, fast and compresses well.
91 |
92 | # Any questions?
93 | My social networking nickname is lilmuff1 message me or join [Telegram](https://t.me/XcoderBS) or [Discord](https://discord.com/invite/yNajwpBe)
94 |
--------------------------------------------------------------------------------
/Readme_ru.md:
--------------------------------------------------------------------------------
1 | #
X-coder
2 | #X-coder
3 | Инструмент для декодирования (sc в png) и кодирования (png в sc) файлов sc которые используются в играх Supercell
4 | # Как пользоваться
5 | 1. Скачиваем нужную нам версию с [релизов](https://github.com/lilmuff2/X-coder/releases/tag/v2.2) (apk - на андроид, setup.exe - установщик на винду, zip - архив с exe для винды)
6 | 3. Выбрать нужную опцию (о каждой расписано ниже)
7 | 5. Выбрать или ввести входной файл или папку
8 | 7. По желанию выбрать или ввести выходную папку или файл, или она будет выбрана автоматически !ОНА БУДЕТ УДАЛЕНА!
9 | 8. Нажать на кнопку снизу (не закрыть) и ждать
10 |
11 |
12 | 
13 |
14 | # ДЕКОД ФАЙЛ:
15 | Конвертирует sc в пнг
16 | Входной файл - _tex.sc, не .sc! или _dl.sc в редких случаях
17 | Начиная с 54 версии бравл старса появились zktx файлы их нужно поместить в ту же папку, что и входной файл
18 | Выходная папка - папка в которую будут сохранены пнг
19 |
20 | 
21 |
22 |
23 | # ЕНКОД ПАПКУ:
24 | Конвертирует пнг в sc
25 | Входная папка - папка с пнг и [json файлом](#json-%D1%84%D0%B0%D0%B9%D0%BB) (желательно)
26 | Имя json файла должно совпадать с названием папки
27 | Выходной файл - куда сохранять sc
28 | Если вам нужен sc для старой версии измените сжатие в json файле с Zstandard на Lzma
29 |
30 | 
31 |
32 |
33 | # ДЕКОД ФАЙЛЫ:
34 | Конвертирует много sc файлов в пнг, удобно просто скопировать папку sc из апк и декодировать их все разом
35 | Входная папка - папка с _tex.sc, не .sc! или _dl.sc файлами
36 | Выходная папка - папка где будут созданы папки в которых будут пнг
37 |
38 | 
39 |
40 |
41 | # ЕНКОД ПАПКИ:
42 | Конвертирует много папок с пнг в sc файлы
43 | Входная папка - папка с папками в которых лежат пнг
44 | Выходная папка - папка куда будут сохранены sc файлы
45 |
46 | 
47 |
48 |
49 | # .json файл:
50 | Этот файл генерируется при декодировании
51 | Пример:
52 | ```javascript
53 | {
54 | "Textures": [
55 | {
56 | "Encoding": "khronos",
57 | "PixelFormat": "RGBA8",
58 | "Filtering": "LINEAR_NEAREST",
59 | "Linear": true
60 | },
61 | {
62 | "Encoding": "khronos",
63 | "PixelFormat": "RGBA8",
64 | "Filtering": "LINEAR_NEAREST",
65 | "Linear": true
66 | }
67 | ],
68 | "IsDL": false,
69 | "HasZKTX": true,
70 | "Compression": "Zstandard"
71 | }
72 | ```
73 |
74 | ## Encoding
75 | Может быть khronos (мало весит) и raw (быстро кодируется), при включённом HasZKTX текстуры кодируются в khronos
76 | Пример ui_highres_tex с Zstandard сжатием:
77 | Encoding | Время кодирования | Размер файла
78 | ---|---|---
79 | khronos | 35 с | 30 MB
80 | raw | 7 с | 41 MB
81 | > Я рекомендую во время разработки мода использовать raw а при релизе khronos
82 | ## HasZKTX
83 | Параметр отвечающий за наличие zktk файлов
84 | ### ISDL
85 | Параметр отвечающий за создание dl файла (объединение sc и tex_sc), обычно используется в Clash Royale требует наличия sc файла в папке
86 | ## Compression
87 | Сжатие файла, при включённом HasZKTX всегда используется Zstandard.
88 | Пример сжатия на ui_highres_tex:
89 | Compression | Время кодирования файла | Вес файла | Итог
90 | --- | --- | --- | ---
91 | Lzma | 41 с | 26MB| Дольше, но сжимает лучше
92 | Lzham | 73с | 27MB | Оно бесполезно
93 | Zstandard | 35 с | 30 MB| Стандартное, быстрое и хорошо сжимает
94 |
95 | # Остались вопросы?
96 | Мой ник в соцсетях lilmuff1 пишите или присоединяйтесь к [Telegram](https://t.me/XcoderBS) or [Discord](https://discord.com/invite/yNajwpBe)
97 |
--------------------------------------------------------------------------------
/android/AndroidManifest.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:7.4.1'
9 | }
10 | }
11 |
12 | repositories {
13 | google()
14 | mavenCentral()
15 | }
16 |
17 | apply plugin: 'com.android.application'
18 |
19 | dependencies {
20 | implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
21 | implementation 'androidx.documentfile:documentfile:1.0.1'
22 | implementation 'androidx.appcompat:appcompat:1.0.0'
23 | }
24 |
25 | android {
26 | /*******************************************************
27 | * The following variables:
28 | * - androidBuildToolsVersion,
29 | * - androidCompileSdkVersion
30 | * - qtAndroidDir - holds the path to qt android files
31 | * needed to build any Qt application
32 | * on Android.
33 | *
34 | * are defined in gradle.properties file. This file is
35 | * updated by QtCreator and androiddeployqt tools.
36 | * Changing them manually might break the compilation!
37 | *******************************************************/
38 |
39 | compileSdkVersion androidCompileSdkVersion
40 | buildToolsVersion androidBuildToolsVersion
41 | ndkVersion androidNdkVersion
42 |
43 | // Extract native libraries from the APK
44 | packagingOptions.jniLibs.useLegacyPackaging true
45 |
46 | sourceSets {
47 | main {
48 | manifest.srcFile 'AndroidManifest.xml'
49 | java.srcDirs = [qtAndroidDir + '/src', 'src', 'java']
50 | aidl.srcDirs = [qtAndroidDir + '/src', 'src', 'aidl']
51 | res.srcDirs = [qtAndroidDir + '/res', 'res']
52 | resources.srcDirs = ['resources']
53 | renderscript.srcDirs = ['src']
54 | assets.srcDirs = ['assets']
55 | jniLibs.srcDirs = ['libs']
56 | }
57 | }
58 |
59 | tasks.withType(JavaCompile) {
60 | options.incremental = true
61 | }
62 |
63 | compileOptions {
64 | sourceCompatibility JavaVersion.VERSION_1_8
65 | targetCompatibility JavaVersion.VERSION_1_8
66 | }
67 |
68 | lintOptions {
69 | abortOnError false
70 | }
71 |
72 | // Do not compress Qt binary resources file
73 | aaptOptions {
74 | noCompress 'rcc'
75 | }
76 |
77 | defaultConfig {
78 | resConfig "en"
79 | minSdkVersion qtMinSdkVersion
80 | targetSdkVersion qtTargetSdkVersion
81 | ndk.abiFilters = qtTargetAbiList.split(",")
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # For more details on how to configure your build environment visit
3 | # http://www.gradle.org/docs/current/userguide/build_environment.html
4 | # Specifies the JVM arguments used for the daemon process.
5 | # The setting is particularly useful for tweaking memory settings.
6 | org.gradle.jvmargs=-Xmx2500m -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
7 |
8 | # Enable building projects in parallel
9 | org.gradle.parallel=true
10 |
11 | # Gradle caching allows reusing the build artifacts from a previous
12 | # build with the same inputs. However, over time, the cache size will
13 | # grow. Uncomment the following line to enable it.
14 | #org.gradle.caching=true
15 | #org.gradle.configuration-cache=true
16 |
17 | # Allow AndroidX usage
18 | android.useAndroidX=true
19 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilmuff2/X-coder/285c36067251cb3b118f006329bc94785d09f183/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
88 |
89 | # Use the maximum available, or set MAX_FD != -1 to use that value.
90 | MAX_FD=maximum
91 |
92 | warn () {
93 | echo "$*"
94 | } >&2
95 |
96 | die () {
97 | echo
98 | echo "$*"
99 | echo
100 | exit 1
101 | } >&2
102 |
103 | # OS specific support (must be 'true' or 'false').
104 | cygwin=false
105 | msys=false
106 | darwin=false
107 | nonstop=false
108 | case "$( uname )" in #(
109 | CYGWIN* ) cygwin=true ;; #(
110 | Darwin* ) darwin=true ;; #(
111 | MSYS* | MINGW* ) msys=true ;; #(
112 | NONSTOP* ) nonstop=true ;;
113 | esac
114 |
115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
116 |
117 |
118 | # Determine the Java command to use to start the JVM.
119 | if [ -n "$JAVA_HOME" ] ; then
120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
121 | # IBM's JDK on AIX uses strange locations for the executables
122 | JAVACMD=$JAVA_HOME/jre/sh/java
123 | else
124 | JAVACMD=$JAVA_HOME/bin/java
125 | fi
126 | if [ ! -x "$JAVACMD" ] ; then
127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
128 |
129 | Please set the JAVA_HOME variable in your environment to match the
130 | location of your Java installation."
131 | fi
132 | else
133 | JAVACMD=java
134 | if ! command -v java >/dev/null 2>&1
135 | then
136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 | fi
142 |
143 | # Increase the maximum file descriptors if we can.
144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
145 | case $MAX_FD in #(
146 | max*)
147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
148 | # shellcheck disable=SC3045
149 | MAX_FD=$( ulimit -H -n ) ||
150 | warn "Could not query maximum file descriptor limit"
151 | esac
152 | case $MAX_FD in #(
153 | '' | soft) :;; #(
154 | *)
155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
156 | # shellcheck disable=SC3045
157 | ulimit -n "$MAX_FD" ||
158 | warn "Could not set maximum file descriptor limit to $MAX_FD"
159 | esac
160 | fi
161 |
162 | # Collect all arguments for the java command, stacking in reverse order:
163 | # * args from the command line
164 | # * the main class name
165 | # * -classpath
166 | # * -D...appname settings
167 | # * --module-path (only if needed)
168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
169 |
170 | # For Cygwin or MSYS, switch paths to Windows format before running java
171 | if "$cygwin" || "$msys" ; then
172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
174 |
175 | JAVACMD=$( cygpath --unix "$JAVACMD" )
176 |
177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
178 | for arg do
179 | if
180 | case $arg in #(
181 | -*) false ;; # don't mess with options #(
182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183 | [ -e "$t" ] ;; #(
184 | *) false ;;
185 | esac
186 | then
187 | arg=$( cygpath --path --ignore --mixed "$arg" )
188 | fi
189 | # Roll the args list around exactly as many times as the number of
190 | # args, so each arg winds up back in the position where it started, but
191 | # possibly modified.
192 | #
193 | # NB: a `for` loop captures its iteration list before it begins, so
194 | # changing the positional parameters here affects neither the number of
195 | # iterations, nor the values presented in `arg`.
196 | shift # remove old arg
197 | set -- "$@" "$arg" # push replacement arg
198 | done
199 | fi
200 |
201 |
202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203 | DEFAULT_JVM_OPTS='-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m"'
204 |
205 | # Collect all arguments for the java command;
206 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
207 | # shell script including quotes and variable substitutions, so put them in
208 | # double quotes to make sure that they get re-expanded; and
209 | # * put everything else in single quotes, so that it's not re-expanded.
210 |
211 | set -- \
212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
213 | -classpath "$CLASSPATH" \
214 | org.gradle.wrapper.GradleWrapperMain \
215 | "$@"
216 |
217 | # Stop when "xargs" is not available.
218 | if ! command -v xargs >/dev/null 2>&1
219 | then
220 | die "xargs is not available"
221 | fi
222 |
223 | # Use "xargs" to parse quoted args.
224 | #
225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
226 | #
227 | # In Bash we could simply go:
228 | #
229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
230 | # set -- "${ARGS[@]}" "$@"
231 | #
232 | # but POSIX shell has neither arrays nor command substitution, so instead we
233 | # post-process each arg (as a line of input to sed) to backslash-escape any
234 | # character that might be a shell metacharacter, then use eval to reverse
235 | # that process (while maintaining the separation between arguments), and wrap
236 | # the whole thing up as a single "set" statement.
237 | #
238 | # This will of course break if any of these variables contains a newline or
239 | # an unmatched quote.
240 | #
241 |
242 | eval "set -- $(
243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
244 | xargs -n1 |
245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
246 | tr '\n' ' '
247 | )" '"$@"'
248 |
249 | exec "$JAVACMD" "$@"
250 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS=-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/android/res/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilmuff2/X-coder/285c36067251cb3b118f006329bc94785d09f183/android/res/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/android/res/drawable-ldpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilmuff2/X-coder/285c36067251cb3b118f006329bc94785d09f183/android/res/drawable-ldpi/icon.png
--------------------------------------------------------------------------------
/android/res/drawable-mdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilmuff2/X-coder/285c36067251cb3b118f006329bc94785d09f183/android/res/drawable-mdpi/icon.png
--------------------------------------------------------------------------------
/android/res/drawable-xhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilmuff2/X-coder/285c36067251cb3b118f006329bc94785d09f183/android/res/drawable-xhdpi/icon.png
--------------------------------------------------------------------------------
/android/res/drawable-xxhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilmuff2/X-coder/285c36067251cb3b118f006329bc94785d09f183/android/res/drawable-xxhdpi/icon.png
--------------------------------------------------------------------------------
/android/res/drawable-xxxhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilmuff2/X-coder/285c36067251cb3b118f006329bc94785d09f183/android/res/drawable-xxxhdpi/icon.png
--------------------------------------------------------------------------------
/android/res/values/libs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/android/res/xml/qtprovider_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/headers/mainwindow.h:
--------------------------------------------------------------------------------
1 | #ifndef MAINWINDOW_H
2 | #define MAINWINDOW_H
3 |
4 | #include
5 | #include "config.hpp"
6 |
7 | QT_BEGIN_NAMESPACE
8 | namespace Ui {
9 | class MainWindow;
10 | }
11 | QT_END_NAMESPACE
12 |
13 | class MainWindow : public QMainWindow
14 | {
15 | Q_OBJECT
16 |
17 | public:
18 | MainWindow(QWidget *parent = nullptr);
19 | ~MainWindow();
20 | Config *cfg;
21 | std::vector langs = {"en","ru"};
22 | bool lanchanged = false;
23 | int CheckUpdates();
24 | private slots:
25 | void BtnCLicked();
26 |
27 | void on_About_clicked();
28 |
29 | void on_comboBox_currentIndexChanged(int index);
30 |
31 | private:
32 | Ui::MainWindow *ui;
33 | };
34 | #endif // MAINWINDOW_H
35 |
--------------------------------------------------------------------------------
/headers/processing.h:
--------------------------------------------------------------------------------
1 | #ifndef PROCESSING_H
2 | #define PROCESSING_H
3 |
4 | #include
5 | #include
6 |
7 |
8 | namespace Ui {
9 | class Processing;
10 | }
11 |
12 | class Processing : public QWidget
13 | {
14 | Q_OBJECT
15 |
16 | public:
17 | explicit Processing(QWidget *parent = nullptr);
18 | ~Processing();
19 | void ChangeTitle(QString newtitle);
20 | bool Folder = false;
21 | bool Encode = false;
22 | void print(std::string str);
23 | void decode(std::filesystem::path input_path,std::filesystem::path output_path);
24 | void encode(std::filesystem::path input_path,std::filesystem::path output_path);
25 | void print(QString str);
26 | private slots:
27 | void on_Select1_clicked();
28 |
29 | void on_Select2_clicked();
30 |
31 | void on_Run_clicked();
32 |
33 | void on_ExitBtn_clicked();
34 |
35 | private:
36 | Ui::Processing *ui;
37 | };
38 | #endif // PROCESSING_H
39 |
--------------------------------------------------------------------------------
/res/Pusia-Bold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilmuff2/X-coder/285c36067251cb3b118f006329bc94785d09f183/res/Pusia-Bold.otf
--------------------------------------------------------------------------------
/res/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilmuff2/X-coder/285c36067251cb3b118f006329bc94785d09f183/res/icon.ico
--------------------------------------------------------------------------------
/res/rc.rc:
--------------------------------------------------------------------------------
1 | IDI_ICON1 ICON "icon.ico"
--------------------------------------------------------------------------------
/res/res.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | translate/X-coder_ru.qm
4 | Pusia-Bold.otf
5 |
6 |
7 |
--------------------------------------------------------------------------------
/res/translate/X-coder_ru.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lilmuff2/X-coder/285c36067251cb3b118f006329bc94785d09f183/res/translate/X-coder_ru.qm
--------------------------------------------------------------------------------
/res/translate/X-coder_ru.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | MainWindow
6 |
7 |
8 |
9 | X CODER
10 |
11 |
12 |
13 |
14 | <html><head/><body><p><a href="tg://resolve?domain=XcoderBS"><span style=" font-size:14pt; text-decoration: underline; color:#007af4;">https://t.me/XcoderBS</span></a></p></body></html>
15 |
16 |
17 |
18 |
19 | DECODE
20 | ДЕКОД
21 |
22 |
23 |
24 | FILE
25 | ФАЙЛ
26 |
27 |
28 |
29 | FILES
30 | ФАЙЛЫ
31 |
32 |
33 |
34 | FOLDER
35 | ПАПКУ
36 |
37 |
38 |
39 | ENCODE
40 | ЕНКОД
41 |
42 |
43 |
44 | FOLDERS
45 | ПАПКИ
46 |
47 |
48 |
49 | LANGUAGE
50 | ЯЗЫК
51 |
52 |
53 |
54 | English
55 |
56 |
57 |
58 |
59 | Русский
60 |
61 |
62 |
63 |
64 |
65 | ABOUT
66 | ИНФО
67 |
68 |
69 |
70 | en
71 | ru
72 |
73 |
74 |
75 |
76 | UPDATE AVAILABE
77 | ДОСТУПНО ОБНОВЛЕНИЕ
78 |
79 |
80 |
81 |
82 | You can find new version on <a href='https://github.com/lilmuff2/X-coder/releases'>https://github.com/lilmuff2/X-coder/releases</a>
83 | Ты можешь найти новую версию на <a href='https://github.com/lilmuff2/X-coder/releases'>https://github.com/lilmuff2/X-coder/releases</a>
84 |
85 |
86 |
87 |
88 | UPDATE
89 | ОБНОВИТЬ
90 |
91 |
92 |
93 | EXIT
94 | ВЫЙТИ
95 |
96 |
97 |
98 | Author: <a href='tg://resolve?domain=lilmuff1'>lilmuff1</a>
99 | <br>THX FOR HELP: <a href='https://github.com/Daniil-SV'>Danil SV</a><br>DISCORD: <a href='https://discord.com/invite/yNajwpBe'>discord.com/invite/yNajwpBe</a><br>TG: <a href='tg://resolve?domain=XcoderBS'>t.me/XcoderBS</a><br>GITHUB: <a href='https://github.com/lilmuff2/X-coder'>github.com/lilmuff2/X-coder</a>
100 | Автор: <a href='tg://resolve?domain=lilmuff1'>lilmuff1</a>
101 | <br>ПОМОГАЛ: <a href='https://github.com/Daniil-SV'>Danil SV</a><br>DISCORD: <a href='https://discord.com/invite/yNajwpBe'>discord.com/invite/yNajwpBe</a><br>TG: <a href='tg://resolve?domain=XcoderBS'>t.me/XcoderBS</a><br>GITHUB: <a href='https://github.com/lilmuff2/X-coder'>github.com/lilmuff2/X-coder</a>
102 |
103 |
104 |
105 | Processing
106 |
107 |
108 |
109 |
110 | DECODE
111 | ДЕКОД
112 |
113 |
114 |
115 | <html><head/><body><p><a href="tg://resolve?domain=XcoderBS"><span style=" font-size:14pt; text-decoration: underline; color:#007af4;">https://t.me/XcoderBS</span></a></p></body></html>
116 |
117 |
118 |
119 |
120 |
121 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
122 | <html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css">
123 | p, li { white-space: pre-wrap; }
124 | hr { height: 1px; border-width: 0; }
125 | li.unchecked::marker { content: "\2610"; }
126 | li.checked::marker { content: "\2612"; }
127 | </style></head><body style=" font-family:'Segoe UI'; font-size:15pt; font-weight:700; font-style:normal;">
128 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;"><br /></p></body></html>
129 |
130 |
131 |
132 |
133 | OUT FOLDER (OPTIONALLY)
134 | ВЫХОДНЯ ПАПКА (ОПЦИОНАЛЬНО)
135 |
136 |
137 |
138 | INPUT FILE
139 | ВХОДНОЙ ФАЙЛ
140 |
141 |
142 |
143 |
144 | SELECT
145 | ВЫБРАТЬ
146 |
147 |
148 |
149 |
150 | CONSOLE:
151 | КОНСОЛЬ:
152 |
153 |
154 |
155 | CLOSE
156 | ЗАКРЫТЬ
157 |
158 |
159 |
160 |
161 |
162 | ENCODE
163 | ЕНКОД
164 |
165 |
166 |
167 | OUT FILE (OPTIONALLY)
168 | ВЫХОДНОЙ ФАЙЛ (ОПЦИОНАЛЬНО)
169 |
170 |
171 |
172 |
173 | INPUT FOLDER
174 | ВХОДНАЯ ПАПКА
175 |
176 |
177 |
178 |
179 | Decoding took:
180 | Декод занял:
181 |
182 |
183 |
184 |
185 | Encoding took:
186 | Енкод занял:
187 |
188 |
189 |
190 |
191 |
192 | ERROR: Input folder doesnt exist!
193 | ОШИБКА: Входной папки не существует!
194 |
195 |
196 |
197 |
198 |
199 |
200 | INFO: Out path is empty, so it will be:
201 |
202 | ИНФО: Выходной путь пустой, он выбран автоматически:
203 |
204 |
205 |
206 | Decoding %d files took:
207 | Декод %d файлов занял:
208 |
209 |
210 |
211 | ERROR: No _tex.sc in input folder. Folder must contains texture files!
212 | ОШИБКА: Нету _tex.sc файлов в входной папке. Папка должна содержать файлы текстур!
213 |
214 |
215 |
216 | Encoding %d files took:
217 | Енкод %d файлов занял:
218 |
219 |
220 |
221 | ERROR: No folders in input folder. Folder must contains folders with pngs!
222 | ОШИБКА: Нету папок в входной папке. Пака должа содержать папки с пнгшками!
223 |
224 |
225 |
226 | ERROR: Input file doesnt exist!
227 | ОШИБКА: Входной файл не существует!
228 |
229 |
230 |
231 | WARNING: INPUT FILE MAY NOT BE _TEX.SC if so out will be empty. INPUT FILE MUST BE TEXTURE FILE
232 | ПРЕДДУПРЕЖДЕНИЕ: Возможно это не файл текстуры. Входной файл должен быть _tex.sc
233 |
234 |
235 |
236 |
237 | Working on
238 | Работаем над
239 |
240 |
241 |
242 | Loading file...
243 | Загружаем файл...
244 |
245 |
246 |
247 | INFO: It is dl file, so it will be coped to out folder
248 | ИНФО: Это dl файл, он быдет скопирован в выходнкю папку
249 |
250 |
251 |
252 | Decoding file...
253 | Декод файла....
254 |
255 |
256 |
257 |
258 | ERROR:
259 | ОШИБКА:
260 |
261 |
262 |
263 | INFO: It is dl, so loading sc...
264 | ИНФО: это dl, загружаем sc...
265 |
266 |
267 |
268 | Loading images...
269 | Загружем картинки...
270 |
271 |
272 |
273 | Saving file...
274 | Сохраняем файл...
275 |
276 |
277 |
278 | QTranslator
279 |
280 |
281 | en
282 | ru
283 |
284 |
285 |
286 |
--------------------------------------------------------------------------------
/source/SWFFile.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | #include "json.hpp"
11 |
12 | using json = nlohmann::ordered_json;
13 |
14 | namespace sc
15 | {
16 | class SWFFile : public SupercellSWF
17 | {
18 | public:
19 | SWFFile() {};
20 | bool isdl = false;
21 | bool has_zktx = false;
22 | // If the file is a real texture, only the texture tags are loaded, otherwise the entire file is loaded.
23 | SWFFile(std::filesystem::path path)
24 | {
25 | current_file = path;
26 | stream.open_file(path);
27 |
28 | // Path Check
29 | if (path.string().find("_tex") != std::string::npos)
30 | {
31 | load_texures_from_binary();
32 |
33 | return;
34 | }
35 | // Whole file loading
36 | if(path.string().find("_dl") != std::string::npos)
37 | {
38 | isdl=true;
39 | // Skip of all count fields
40 | stream.seek(17);
41 |
42 | // export names skip
43 | uint16_t exports_count = stream.read_unsigned_short();
44 |
45 | for (uint16_t i = 0; exports_count > i; i++)
46 | {
47 | stream.read_unsigned_short();
48 | }
49 |
50 | for (uint16_t i = 0; exports_count > i; i++)
51 | {
52 | uint8_t length = stream.read_unsigned_byte();
53 | stream.seek(length, sc::Seek::Add);
54 | }
55 |
56 | load_texures_from_binary();
57 | }
58 | }
59 |
60 | public:
61 | void load_texures_from_binary()
62 | {
63 | while (true)
64 | {
65 | uint8_t tag = stream.read_unsigned_byte();
66 | int32_t tag_length = stream.read_int();
67 |
68 | if (tag == TAG_END)
69 | break;
70 |
71 | if (tag_length < 0)
72 | throw NegativeTagLengthException(tag, stream.position());
73 |
74 | switch (tag)
75 | {
76 | case TAG_TEXTURE_10:
77 | has_zktx = true;
78 | case TAG_TEXTURE:
79 | case TAG_TEXTURE_2:
80 | case TAG_TEXTURE_3:
81 | case TAG_TEXTURE_4:
82 | case TAG_TEXTURE_5:
83 | case TAG_TEXTURE_6:
84 | case TAG_TEXTURE_7:
85 | case TAG_TEXTURE_8:
86 | case TAG_TEXTURE_9:
87 | textures.emplace_back().load(*this, tag, true);
88 | break;
89 |
90 | default:
91 | stream.seek(tag_length, Seek::Add);
92 | break;
93 | }
94 | }
95 | }
96 |
97 | void save_textures_to_folder(std::filesystem::path output_path)
98 | {
99 | json texture_infos = json::array();
100 |
101 | for (uint16_t i = 0; textures.size() > i; i++)
102 | {
103 | SWFTexture& texture = textures[i];
104 |
105 | // Texture Info
106 | {
107 | std::string encoding;
108 | {
109 | switch (texture.encoding())
110 | {
111 | case SWFTexture::TextureEncoding::KhronosTexture:
112 | encoding = "khronos";
113 | break;
114 | case SWFTexture::TextureEncoding::Raw:
115 | encoding = "raw";
116 | break;
117 | default:
118 | break;
119 | }
120 | }
121 |
122 | std::string pixel_type = "RGBA8";
123 |
124 | switch (texture.pixel_format())
125 | {
126 | case SWFTexture::PixelFormat::RGBA4:
127 | pixel_type = "RGBA4";
128 | break;
129 | case SWFTexture::PixelFormat::RGB5_A1:
130 | pixel_type = "RGB5_A1";
131 | break;
132 | case SWFTexture::PixelFormat::RGB565:
133 | pixel_type = "RGB565";
134 | break;
135 | case SWFTexture::PixelFormat::LUMINANCE8_ALPHA8:
136 | pixel_type = "LUMINANCE8_ALPHA8";
137 | break;
138 | case SWFTexture::PixelFormat::LUMINANCE8:
139 | pixel_type = "LUMINANCE8";
140 | break;
141 |
142 | case SWFTexture::PixelFormat::RGBA8:
143 | default:
144 | break;
145 | }
146 |
147 | std::string filtering = "LINEAR_NEAREST";
148 |
149 | switch (texture.filtering)
150 | {
151 | case SWFTexture::Filter::LINEAR_MIPMAP_NEAREST:
152 | filtering = "LINEAR_MIPMAP_NEAREST";
153 | break;
154 | case SWFTexture::Filter::NEAREST_NEAREST:
155 | filtering = "NEAREST_NEAREST";
156 | break;
157 | case SWFTexture::Filter::LINEAR_NEAREST:
158 | default:
159 | break;
160 | }
161 |
162 | json texture_info = {
163 | {"Encoding", encoding},
164 | {"PixelFormat", pixel_type},
165 | {"Filtering", filtering},
166 | {"Linear", texture.linear()},
167 | };
168 |
169 | texture_infos.push_back(texture_info);
170 | }
171 |
172 | // Texture Image
173 | std::filesystem::path basename = output_path.stem();
174 | std::filesystem::path output_image_path = output_path / basename.concat("_").concat(std::to_string(i)).concat(".png");
175 | OutputFileStream output_image(output_image_path);
176 |
177 | stb::ImageFormat format = stb::ImageFormat::PNG;
178 |
179 | switch (texture.encoding())
180 | {
181 | case SWFTexture::TextureEncoding::KhronosTexture:
182 | {
183 | RawImage image(
184 | texture.image()->width(), texture.image()->height(),
185 | texture.image()->depth()
186 | );
187 |
188 | sc::MemoryStream image_data(image.data(), image.data_length());
189 | ((sc::KhronosTexture*)(texture.image()))->decompress_data(image_data);
190 |
191 | stb::write_image(image, format, output_image);
192 | }
193 | break;
194 |
195 | case SWFTexture::TextureEncoding::Raw:
196 | texture.linear(true);
197 | stb::write_image(
198 | *(sc::RawImage*)(texture.image()),
199 | format,
200 | output_image
201 | );
202 | break;
203 |
204 | default:
205 | break;
206 | }
207 | }
208 | json info = {{"Textures",texture_infos},{"IsDL",isdl},{"HasZKTX",has_zktx},{"Compression","Zstandard"}};
209 | std::string serialized_data = info.dump(4);
210 |
211 | OutputFileStream file_info(output_path / output_path.stem().concat(".json"));
212 | file_info.write(serialized_data.data(), serialized_data.size());
213 | }
214 |
215 | void load_textures_from_folder(std::filesystem::path input)
216 | {
217 | current_file = input;
218 | std::filesystem::path basename = input.stem();
219 | std::filesystem::path texture_info_path = std::filesystem::path(input / basename.concat(".json"));
220 |
221 | // Texture Info Parsing
222 | json texture_infos = json::array({});
223 |
224 | if (std::filesystem::exists(texture_info_path))
225 | {
226 | std::ifstream file(texture_info_path);
227 | texture_infos = json::parse(file)["Textures"];
228 | }
229 |
230 |
231 | // Texture Images path gather
232 | SWFVector texture_images_paths;
233 |
234 | for (auto const& file_descriptor : std::filesystem::directory_iterator(input))
235 | {
236 | std::filesystem::path filepath = file_descriptor.path();
237 | std::filesystem::path file_extension = filepath.extension();
238 |
239 | if (file_extension == ".png")
240 | {
241 | texture_images_paths.push_back(filepath);
242 |
243 | }
244 | }
245 |
246 | //Texture Converting
247 | for (uint16_t i = 0; texture_images_paths.size() > i; i++)
248 | {
249 | // Image Loading
250 | RawImage* image = nullptr;
251 | InputFileStream image_file(texture_images_paths[i]);
252 | stb::load_image(image_file, &image);
253 | MemoryStream image_data(image->data(), image->data_length());
254 |
255 | // Image Converting
256 | SWFTexture texture;
257 | texture.load_from_image(*image);
258 |
259 | if (texture_infos.size() > i)
260 | {
261 | json texture_info = texture_infos[i];
262 |
263 | SWFTexture::PixelFormat texture_type = SWFTexture::PixelFormat::RGBA8;
264 |
265 | if (texture_info["PixelFormat"] == "RGBA4")
266 | {
267 | texture_type = SWFTexture::PixelFormat::RGBA4;
268 | }
269 | else if (texture_info["PixelFormat"] == "RGB5_A1")
270 | {
271 | texture_type = SWFTexture::PixelFormat::RGB5_A1;
272 | }
273 | else if (texture_info["PixelFormat"] == "RGB565")
274 | {
275 | texture_type = SWFTexture::PixelFormat::RGB565;
276 | }
277 | else if (texture_info["PixelFormat"] == "LUMINANCE8_ALPHA8")
278 | {
279 | texture_type = SWFTexture::PixelFormat::LUMINANCE8_ALPHA8;
280 | }
281 | else if (texture_info["PixelFormat"] == "LUMINANCE8")
282 | {
283 | texture_type = SWFTexture::PixelFormat::LUMINANCE8;
284 | }
285 |
286 | if (texture_info["Filtering"] == "LINEAR_NEAREST")
287 | {
288 | texture.filtering = SWFTexture::Filter::LINEAR_NEAREST;
289 | }
290 | else if (texture_info["Filtering"] == "LINEAR_MIPMAP_NEAREST")
291 | {
292 | texture.filtering = SWFTexture::Filter::LINEAR_MIPMAP_NEAREST;
293 | }
294 | else if (texture_info["Filtering"] == "NEAREST_NEAREST")
295 | {
296 | texture.filtering = SWFTexture::Filter::NEAREST_NEAREST;
297 | }
298 |
299 | if (texture_info["Encoding"] == "khronos")
300 | {
301 | texture.encoding(SWFTexture::TextureEncoding::KhronosTexture);
302 | }
303 | else if (texture_info["Encoding"] == "raw")
304 | {
305 | texture.encoding(SWFTexture::TextureEncoding::Raw);
306 | texture.pixel_format(texture_type);
307 | }
308 | }
309 |
310 | // Texture Insert
311 | if (textures.size() <= i)
312 | {
313 | textures.push_back(texture);
314 | }
315 | else
316 | {
317 | textures[i] = texture;
318 | }
319 | }
320 | }
321 | };
322 | }
323 |
--------------------------------------------------------------------------------
/source/config.hpp:
--------------------------------------------------------------------------------
1 | #include "json.hpp"
2 | using json = nlohmann::ordered_json;
3 | #include
4 | #include
5 | class Config
6 | {
7 | public :
8 | json config;
9 | std::filesystem::path path = "config.json";
10 |
11 | json basecfg = {{"language","none"},{"ActualVersion",0},{"MinVersion",0}};
12 | Config() {
13 | if(!std::filesystem::exists(path)){
14 | config = basecfg;
15 | update();
16 | }else{
17 | try{
18 | std::ifstream cfile(path);
19 | config = json::parse(cfile);
20 | for (auto& [key, value] : basecfg.items()) {
21 | if(!config.contains(key)) config[key] = value;
22 | }
23 | } catch(...){
24 | config = basecfg;
25 | }
26 | update();
27 | }
28 | };
29 | // std::string lang(){
30 | // return config["language"];
31 | // }
32 | int* versions(){
33 | static int v[2] = {config["ActualVersion"],config["MinVersion"]};
34 | return v;
35 | }
36 | void versions(int ActualVersion,int MinVersion){
37 | config["ActualVersion"] = ActualVersion;
38 | config["MinVersion"] = MinVersion;
39 | update();
40 | }
41 | QString lang(){
42 | return QString::fromStdString(config["language"]);
43 | }
44 | void lang(QString lan){
45 | config["language"] = lan.toStdString();
46 | update();
47 | }
48 | void update(){
49 | std::ofstream o(path);
50 | o << config.dump(4);
51 | }
52 | } ;
53 |
--------------------------------------------------------------------------------
/source/main.cpp:
--------------------------------------------------------------------------------
1 | #include "mainwindow.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #if defined Q_OS_ANDROID
8 | #include
9 | #include
10 | #endif
11 |
12 | int main(int argc, char *argv[])
13 | {
14 | int currentExitCode = 0;
15 | do {
16 | QApplication a(argc, argv);
17 | QTranslator translator;
18 | Config *cfg = new Config();
19 | QString lan = cfg->lang();
20 | if(lan=="none"){
21 | for (const QString &locale : QLocale::system().uiLanguages()) {
22 | if (translator.load("X-coder_" + QLocale(locale).name()) || translator.load(":/translate/X-coder_" + QLocale(locale).name())) {
23 | a.installTranslator(&translator);
24 | break;
25 | }
26 | }
27 | cfg->lang(QTranslator::tr("en"));
28 | }else{
29 | if (translator.load("X-coder_" + lan) || translator.load(":/translate/X-coder_" +lan)) {
30 | a.installTranslator(&translator);
31 | }else if(lan!="en"){
32 | cfg->lang("en");
33 | }
34 | }
35 | QFontDatabase::addApplicationFont(":/Pusia-Bold.otf");
36 | a.setFont(QFont("Pusia-Bold", 11, QFont::Normal, false));
37 | MainWindow w;
38 | w.show();
39 | #if defined Q_OS_ANDROID
40 | if (QtAndroidPrivate::androidSdkVersion() >= 30){
41 | #define PACKAGE_NAME "package:lilmuff1.xcoder"
42 | jboolean value = QJniObject::callStaticMethod("android/os/Environment", "isExternalStorageManager");
43 | if(value == false) {
44 | QJniObject filepermit = QJniObject::getStaticObjectField( "android/provider/Settings", "ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION","Ljava/lang/String;" );
45 | QJniObject pkgName = QJniObject::fromString(PACKAGE_NAME);
46 | QJniObject parsedUri = QJniObject::callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", pkgName.object());
47 | QJniObject intent("android/content/Intent", "(Ljava/lang/String;Landroid/net/Uri;)V", filepermit.object(),parsedUri.object());
48 | QtAndroidPrivate::startActivity(intent, 0);
49 | }
50 | }
51 | else{
52 | const QVector permissions({
53 | "android.permission.WRITE_EXTERNAL_STORAGE",
54 | "android.permission.READ_EXTERNAL_STORAGE"});
55 | for(const QString &permission : permissions){
56 | auto result = QtAndroidPrivate::checkPermission(permission).result();
57 | if(result == QtAndroidPrivate::Denied){
58 | QtAndroidPrivate::requestPermission(permission);
59 | }
60 | }
61 | }
62 | #endif
63 | #if not defined Q_OS_ANDROID
64 | if(!currentExitCode){
65 | if(w.CheckUpdates()){
66 | return currentExitCode;
67 | }
68 | }
69 | #else
70 | std::thread th(
71 | [¤tExitCode, &w, &a](){
72 | if(!currentExitCode){
73 | if(w.CheckUpdates()){
74 | a.exit(currentExitCode);
75 | }
76 | }
77 | });
78 | th.detach();
79 | #endif
80 | currentExitCode = a.exec();
81 | } while( currentExitCode == 69 );
82 | return currentExitCode;
83 | }
84 |
--------------------------------------------------------------------------------
/source/mainwindow.cpp:
--------------------------------------------------------------------------------
1 | #include "mainwindow.h"
2 | #include "./../ui/ui_mainwindow.h"
3 | #include "processing.h"
4 | #include
5 | #include
6 | #include "json.hpp"
7 | using json = nlohmann::ordered_json;
8 | #include
9 | #include
10 | #include
11 | #include
12 | void htmlGet(const QUrl &url, const std::function &fun) {
13 | QScopedPointer manager(new QNetworkAccessManager);
14 | QNetworkReply *response = manager->get(QNetworkRequest(QUrl(url)));
15 | QObject::connect(response, &QNetworkReply::finished, [response, fun]{
16 | response->deleteLater();
17 | response->manager()->deleteLater();
18 | if (response->error() != QNetworkReply::NoError) return;
19 | auto const html = QString::fromUtf8(response->readAll());
20 | fun(html); // do something with the data
21 |
22 | }) && manager.take();
23 | }
24 | MainWindow::MainWindow(QWidget *parent)
25 | : QMainWindow(parent)
26 | , ui(new Ui::MainWindow)
27 | {
28 | cfg = new Config();
29 | ui->setupUi(this);
30 | connect(ui->DecodeFile, SIGNAL(clicked()),this,SLOT(BtnCLicked()));
31 | connect(ui->DecodeFolder, SIGNAL(clicked()),this,SLOT(BtnCLicked()));
32 | connect(ui->EncodeFile, SIGNAL(clicked()),this,SLOT(BtnCLicked()));
33 | connect(ui->EncodeFolder, SIGNAL(clicked()),this,SLOT(BtnCLicked()));
34 | for(int i = 0;icomboBox->setCurrentIndex(i);
37 | break;
38 | }
39 | }
40 | lanchanged = true;
41 | }
42 |
43 | MainWindow::~MainWindow()
44 | {
45 | delete ui;
46 | }
47 |
48 | int MainWindow::CheckUpdates()
49 | {
50 | htmlGet({"https://raw.githubusercontent.com/lilmuff2/X-coder/images/v.json"}, [this](const QString &body){
51 | json v = json::parse(body.toStdString());
52 | int ActualVersion = v["ActualVersion"];
53 | int MinVersion = v["MinVersion"];
54 | cfg->versions(ActualVersion,MinVersion);
55 | });
56 | if(cfg->versions()[1]>22){
57 | QMessageBox msgBox;
58 | msgBox.setWindowTitle(tr("UPDATE AVAILABE"));
59 | msgBox.setTextFormat(Qt::RichText); //this is what makes the links clickable
60 | msgBox.setText(tr("You can find new version on https://github.com/lilmuff2/X-coder/releases"));
61 | QPushButton *btn = msgBox.addButton(tr("UPDATE"), QMessageBox::ActionRole);
62 | msgBox.addButton(tr("EXIT"), QMessageBox::ActionRole);
63 | msgBox.exec();
64 | if (msgBox.clickedButton() == btn) {
65 | QDesktopServices::openUrl(QUrl("https://github.com/lilmuff2/X-coder/releases"));
66 | }
67 | return 1;
68 | }else if(cfg->versions()[0]>22){
69 | QMessageBox msgBox;
70 | msgBox.setWindowTitle(tr("UPDATE AVAILABE"));
71 | msgBox.setTextFormat(Qt::RichText); //this is what makes the links clickable
72 | msgBox.setText(tr("You can find new version on https://github.com/lilmuff2/X-coder/releases"));
73 | QPushButton *btn = msgBox.addButton(tr("UPDATE"), QMessageBox::ActionRole);
74 | msgBox.setStandardButtons(QMessageBox::Ok);
75 | msgBox.exec();
76 | if (msgBox.clickedButton() == btn) {
77 | QDesktopServices::openUrl(QUrl("https://github.com/lilmuff2/X-coder/releases"));
78 | }
79 | }
80 | return 0;
81 | }
82 |
83 | void MainWindow::BtnCLicked()
84 | {
85 | Processing *processWindow = new Processing();
86 | processWindow->show();
87 | QString action = sender()->objectName();
88 | processWindow->ChangeTitle(action);
89 | }
90 |
91 | void MainWindow::on_About_clicked()
92 | {
93 | QMessageBox msgBox;
94 | msgBox.setWindowTitle(tr("ABOUT"));
95 | msgBox.setTextFormat(Qt::RichText); //this is what makes the links clickable
96 | msgBox.setText(tr("Author: lilmuff1\n
THX FOR HELP: Danil SV"
97 | "
DISCORD: discord.com/invite/yNajwpBe"
98 | "
TG: t.me/XcoderBS"
99 | "
GITHUB: github.com/lilmuff2/X-coder"));
100 | msgBox.setStandardButtons(QMessageBox::Ok);
101 | msgBox.exec();
102 | }
103 |
104 | void MainWindow::on_comboBox_currentIndexChanged(int index)
105 | {
106 | if(lanchanged){
107 | cfg->lang(langs[index]);
108 | qApp->exit(69);
109 | }
110 | }
111 |
112 |
--------------------------------------------------------------------------------
/source/processing.cpp:
--------------------------------------------------------------------------------
1 | #include "processing.h"
2 | #include "../ui/ui_processing.h"
3 | #include "SWFFile.hpp"
4 | #include "logger/time.h"
5 | #include "json.hpp"
6 | #include
7 | #include
8 | #if defined Q_OS_ANDROID
9 | #include
10 | #include
11 | #endif
12 | using json = nlohmann::ordered_json;
13 | Processing::Processing(QWidget *parent)
14 | : QWidget(parent)
15 | , ui(new Ui::Processing)
16 | {
17 | ui->setupUi(this);
18 | #if defined Q_OS_ANDROID
19 | ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
20 | #endif
21 | }
22 | static QString getRealPathFromUri(const QUrl &url)
23 | {
24 | QString path = "";
25 | #ifdef Q_OS_ANDROID
26 | QJniObject jUrl = QJniObject::fromString(url.toString());
27 | QJniObject jContext = QtAndroidPrivate::context();
28 | QJniObject jContentResolver = jContext.callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;");
29 | QJniObject jUri = QJniObject::callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", jUrl.object());
30 | QJniObject jCursor = jContentResolver.callObjectMethod("query", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;", jUri.object(), nullptr, nullptr, nullptr, nullptr);
31 | QJniObject jScheme = jUri.callObjectMethod("getScheme", "()Ljava/lang/String;");
32 | QJniObject authority;
33 | if(jScheme.isValid())
34 | {
35 | authority = jUri.callObjectMethod("getAuthority", "()Ljava/lang/String;");
36 | }
37 | if(authority.isValid() && authority.toString() == "com.android.externalstorage.documents")
38 | {
39 | QJniObject jPath = jUri.callObjectMethod("getPath", "()Ljava/lang/String;");
40 | path = jPath.toString();
41 | }
42 | else if(jCursor.isValid() && jCursor.callMethod("moveToFirst"))
43 | {
44 | QJniObject jColumnIndex = QJniObject::fromString("_data");
45 | jint columnIndex = jCursor.callMethod("getColumnIndexOrThrow", "(Ljava/lang/String;)I", jColumnIndex.object());
46 | QJniObject jRealPath = jCursor.callObjectMethod("getString", "(I)Ljava/lang/String;", columnIndex);
47 | path = jRealPath.toString();
48 | if(authority.isValid() && authority.toString().startsWith("com.android.providers") && !url.toString().startsWith("content://media/external/"))
49 | {
50 | QStringList list = path.split(":");
51 | if(list.count() == 2)
52 | {
53 | QString type = list.at(0);
54 | QString id = list.at(1);
55 | if(type == "image")
56 | type = type + "s";
57 | if(type == "document" || type == "documents")
58 | type = "file";
59 | if(type == "msf")
60 | type = "downloads";
61 | if(QList({"images","video","audio"}).contains(type))
62 | type = type + "/media";
63 | path = "content://media/external/"+type;
64 | path = path + "/" + id;
65 | return getRealPathFromUri(path);
66 | }
67 | }
68 | }
69 | else
70 | {
71 | QJniObject jPath = jUri.callObjectMethod("getPath", "()Ljava/lang/String;");
72 | path = jPath.toString();
73 | qDebug() << QFile::exists(path) <retranslateUi(this);
117 | if(newtitle.startsWith("Encode")){
118 | ui->Run->setText(tr("ENCODE"));
119 | ui->Title->setText(tr("ENCODE"));
120 | Processing::setWindowTitle(tr("ENCODE"));
121 | Processing::Encode = true;
122 | if(!newtitle.endsWith("Folder")){ui->Input2->setPlaceholderText(tr("OUT FILE (OPTIONALLY)"));}
123 | ui->Input1->setPlaceholderText(tr("INPUT FOLDER"));
124 | }
125 | if(newtitle.endsWith("Folder")){
126 | ui->Input1->setPlaceholderText(tr("INPUT FOLDER"));
127 | Processing::Folder = true;
128 | Processing::setWindowTitle(Processing::windowTitle()+" FOLDER");
129 | }
130 | }
131 |
132 |
133 |
134 |
135 | void Processing::on_Select1_clicked()
136 | {
137 | QString text;
138 | if(Processing::Encode || Processing::Folder){
139 | text = QFileDialog::getExistingDirectory(this);
140 | }else{
141 | #if defined Q_OS_ANDROID
142 | text = QFileDialog::getOpenFileName(this);
143 | #else
144 | text = QFileDialog::getOpenFileName(this,nullptr,nullptr,"SC FILE (*.sc)");
145 | #endif
146 | }
147 | #if defined Q_OS_ANDROID
148 | text = getRealPathFromUri(text);
149 | #endif
150 | ui->Input1->setText(text);
151 | }
152 |
153 |
154 | void Processing::on_Select2_clicked()
155 | {
156 | QString text;
157 | if(Processing::Encode && !Processing::Folder){
158 | #if defined Q_OS_ANDROID
159 | text = QFileDialog::getSaveFileName(this);
160 | #else
161 | text = QFileDialog::getSaveFileName(this,nullptr,nullptr,"SC FILE (*.sc)");
162 | #endif
163 | }else{
164 | text = QFileDialog::getExistingDirectory(this);
165 | }
166 | #if defined Q_OS_ANDROID
167 | text = getRealPathFromUri(text);
168 | #endif
169 | ui->Input2->setText(text);
170 | }
171 | void Processing::print(std::string str)
172 | {
173 | ui->Output->setText(QString::fromStdString(ui->Output->text().toStdString()+"\n"+str));
174 | ui->scrollArea->verticalScrollBar()->setValue(10000000);
175 | }
176 | void Processing::print(QString str)
177 | {
178 | ui->Output->setText((ui->Output->text()+"\n"+str));
179 | ui->scrollArea->verticalScrollBar()->setValue(10000000);
180 | }
181 | void Processing::on_ExitBtn_clicked()
182 | {
183 | this->close();
184 | }
185 |
186 |
187 | void Processing::on_Run_clicked()
188 | {
189 | ui->Run->setEnabled(false);
190 | ui->Output->setText(tr("CONSOLE:"));
191 | if(!Processing::Encode && !Processing::Folder){//decoding
192 | std::thread th(
193 | [this](){
194 | std::chrono::time_point doperation_start = std::chrono::high_resolution_clock::now();
195 | decode(ui->Input1->toPlainText().toStdString(),ui->Input2->toPlainText().toStdString());
196 | print(tr("Decoding took: ").toStdString()+sc::time::calculate_time(doperation_start, std::chrono::high_resolution_clock::now()));
197 | QMetaObject::invokeMethod(ui->Run,"setEnabled", Qt::QueuedConnection,Q_ARG(bool, true));
198 |
199 | }
200 | );
201 | th.detach();
202 | }
203 | else if(Processing::Encode && !Processing::Folder){//encode
204 | std::thread th(
205 | [this](){
206 | std::chrono::time_point eoperation_start = std::chrono::high_resolution_clock::now();
207 | encode(ui->Input1->toPlainText().toStdString(),ui->Input2->toPlainText().toStdString());
208 | print(tr("Encoding took: ").toStdString()+sc::time::calculate_time(eoperation_start, std::chrono::high_resolution_clock::now()));
209 | QMetaObject::invokeMethod(ui->Run,"setEnabled", Qt::QueuedConnection,Q_ARG(bool, true));
210 | }
211 | );
212 | th.detach();
213 | }
214 | else if(!Processing::Encode && Processing::Folder){//decode folder
215 | std::thread th(
216 | [this](){
217 | std::chrono::time_point operation_start = std::chrono::high_resolution_clock::now();
218 | fs::path input_path =ui->Input1->toPlainText().toStdString(); fs::path output_path = ui->Input2->toPlainText().toStdString();
219 | if(!fs::exists(input_path)){
220 | print(tr("ERROR: Input folder doesnt exist!"));
221 | QMetaObject::invokeMethod(ui->Run,"setEnabled", Qt::QueuedConnection,Q_ARG(bool, true));
222 | return;
223 | }
224 | if(output_path==""){
225 | output_path = input_path.string() + "/Decoded";
226 | print(tr("INFO: Out path is empty, so it will be:\n").toStdString() + output_path.string());
227 | }
228 | if (fs::exists(output_path))
229 | {
230 | fs::remove_all(output_path);
231 | }
232 | int dec = 0;
233 | fs::create_directories(output_path);
234 | for (auto const& file_descriptor : std::filesystem::directory_iterator(input_path))
235 | {
236 | std::filesystem::path filepath = file_descriptor.path();
237 | if (file_descriptor.is_regular_file() && (filepath.stem().string().find("_tex") != std::string::npos || filepath.stem().string().find("_dl") != std::string::npos))
238 | {
239 | dec++;
240 | std::chrono::time_point doperation_start = std::chrono::high_resolution_clock::now();
241 | decode(filepath,output_path/filepath.stem());
242 | print(tr("Decoding took: ").toStdString()+sc::time::calculate_time(doperation_start, std::chrono::high_resolution_clock::now()));
243 | }
244 | }
245 | if(dec>0){
246 | print(tr("Decoding %d files took: ").replace("%d",QString::number(dec)).toStdString()+sc::time::calculate_time(operation_start, std::chrono::high_resolution_clock::now()));
247 | }else{
248 | print(tr("ERROR: No _tex.sc in input folder. Folder must contains texture files!"));
249 | }
250 | QMetaObject::invokeMethod(ui->Run,"setEnabled", Qt::QueuedConnection,Q_ARG(bool, true));
251 |
252 | }
253 | );
254 | th.detach();
255 | }
256 | else if(Processing::Encode && Processing::Folder){//encode folder
257 | std::thread th(
258 | [this](){
259 | std::chrono::time_point operation_start = std::chrono::high_resolution_clock::now();
260 | fs::path input_path =ui->Input1->toPlainText().toStdString(); fs::path output_path = ui->Input2->toPlainText().toStdString();
261 |
262 | if(!fs::exists(input_path)){
263 | print(tr("ERROR: Input folder doesnt exist!"));
264 | QMetaObject::invokeMethod(ui->Run,"setEnabled", Qt::QueuedConnection,Q_ARG(bool, true));
265 | return;
266 | }
267 | if(output_path==""){
268 | output_path = input_path.string() + "/Encoded";
269 | print(tr("INFO: Out path is empty, so it will be:\n").toStdString() + output_path.string());
270 | }
271 | if (fs::exists(output_path))
272 | {
273 | fs::remove_all(output_path);
274 | }
275 | fs::create_directories(output_path);
276 | int enc = 0;
277 | for (auto const& file_descriptor : std::filesystem::directory_iterator(input_path))
278 | {
279 | std::filesystem::path filepath = file_descriptor.path();
280 | if (file_descriptor.is_directory() && (filepath.stem().string().find("_tex") != std::string::npos || filepath.stem().string().find("_dl") != std::string::npos))
281 | {
282 | enc++;
283 | std::chrono::time_point eoperation_start = std::chrono::high_resolution_clock::now();
284 | encode(filepath,output_path/filepath.stem().concat(".sc"));
285 | print(tr("Encoding took: ").toStdString()+sc::time::calculate_time(eoperation_start, std::chrono::high_resolution_clock::now()));
286 | }
287 | }
288 | if(enc>0){
289 | print(tr("Encoding %d files took: ").replace("%d",QString::number(enc)).toStdString()+sc::time::calculate_time(operation_start, std::chrono::high_resolution_clock::now()));
290 | }else{
291 | print(tr("ERROR: No folders in input folder. Folder must contains folders with pngs!"));
292 | }
293 | QMetaObject::invokeMethod(ui->Run,"setEnabled", Qt::QueuedConnection,Q_ARG(bool, true));
294 | }
295 | );
296 | th.detach();
297 | }
298 |
299 | }
300 | void Processing::decode(fs::path input_path,fs::path output_path){
301 | try {
302 | if(!fs::exists(input_path)){
303 | print(tr("ERROR: Input file doesnt exist!"));
304 | return;
305 | }
306 | if(input_path.stem().string().find("_tex") == std::string::npos && input_path.stem().string().find("_dl") == std::string::npos){
307 | print(tr("WARNING: INPUT FILE MAY NOT BE _TEX.SC if so out will be empty. INPUT FILE MUST BE TEXTURE FILE"));
308 | }
309 | print(tr("Working on ").toStdString() + input_path.stem().string() + "...");
310 | print(tr("Loading file..."));
311 | sc::SWFFile file(input_path);
312 | if(output_path==""){
313 | output_path = input_path;
314 | output_path.replace_extension();
315 | print(tr("INFO: Out path is empty, so it will be:\n").toStdString() + output_path.string());
316 | }
317 | if (fs::exists(output_path))
318 | {
319 | fs::remove_all(output_path);
320 | }
321 | fs::create_directories(output_path);
322 | if(file.isdl){
323 | print(tr("INFO: It is dl file, so it will be coped to out folder"));
324 | if(fs::exists(output_path/input_path.filename())){
325 | fs::remove(output_path/input_path.filename());
326 | }
327 | copy_file(input_path,output_path/input_path.filename());
328 | }
329 | print(tr("Decoding file..."));
330 | file.save_textures_to_folder(output_path);
331 | }
332 | catch (sc::GeneralRuntimeException& e) {
333 | print(tr("ERROR:"));
334 | print(tr(e.what()));
335 |
336 | }
337 | }
338 |
339 | void Processing::encode(std::filesystem::path input_path, std::filesystem::path output_path)
340 | {
341 | try{
342 | if(!fs::exists(input_path)){
343 | print(tr("ERROR: Input folder doesnt exist!"));
344 | return;
345 | }
346 | print(tr("Working on ").toStdString() + input_path.stem().string() + "...");
347 | json config={{"IsDL",false},{"HasZKTX",true},{"Compression","Zstandard"}};
348 | if(fs::exists(input_path / input_path.stem().concat(".json"))){std::ifstream cfile(input_path / input_path.stem().concat(".json")); config = json::parse(cfile);}
349 | sc::SWFFile file;
350 | if(config["IsDL"]){
351 | print(tr("INFO: It is dl, so loading sc..."));
352 | file.load(input_path / input_path.stem().concat(".sc"));
353 | }
354 | print(tr("Loading images..."));
355 | file.load_textures_from_folder(input_path);
356 | if(output_path==""){
357 | output_path = input_path/input_path.stem().concat(config["IsDL"]? "_new.sc" : ".sc");
358 | print(tr("INFO: Out path is empty, so it will be:\n").toStdString() + output_path.string());
359 | }
360 | print(tr("Saving file..."));
361 | file.use_external_texture_files = config["HasZKTX"];
362 | sc::SWFStream::Signature sig = sc::SWFStream::Signature::Zstandard;
363 | if(config["Compression"]=="Lzma"){sig=sc::SWFStream::Signature::Lzma;}else if(config["Compression"]=="Lzham"){sig = sc::SWFStream::Signature::Lzham;}
364 | if (config["IsDL"])
365 | {
366 | file.save(output_path,sig);
367 | }
368 | else
369 | {
370 | file.current_file = output_path;
371 | file.save_internal(true, false);
372 | file.stream.save_file(output_path, sig);
373 | file.stream.clear();
374 | }
375 |
376 | }
377 | catch (sc::GeneralRuntimeException& e) {
378 | print(tr("ERROR:"));
379 | print(tr(e.what()));
380 | }
381 | }
382 |
--------------------------------------------------------------------------------
/ui/mainwindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 507
10 | 905
11 |
12 |
13 |
14 |
15 | 600
16 | 950
17 |
18 |
19 |
20 | X CODER
21 |
22 |
23 |
24 | icon.icoicon.ico
25 |
26 |
27 | background-color: rgb(4, 15, 15)
28 |
29 |
30 |
31 | true
32 |
33 |
34 |
35 |
36 |
37 |
38 | 6
39 |
40 | -
41 |
42 |
43 | 0
44 |
45 |
46 | QLayout::SizeConstraint::SetDefaultConstraint
47 |
48 |
-
49 |
50 |
51 | true
52 |
53 |
54 |
55 | 16777215
56 | 100
57 |
58 |
59 |
60 |
61 | 80
62 | true
63 |
64 |
65 |
66 | color: rgb(0, 210, 105);
67 |
68 |
69 | X CODER
70 |
71 |
72 | false
73 |
74 |
75 | Qt::AlignmentFlag::AlignCenter
76 |
77 |
78 | false
79 |
80 |
81 | 0
82 |
83 |
84 |
85 | -
86 |
87 |
88 |
89 | 16777215
90 | 25
91 |
92 |
93 |
94 |
95 | 13
96 | false
97 | true
98 | false
99 | false
100 | true
101 |
102 |
103 |
104 | <html><head/><body><p><a href="tg://resolve?domain=XcoderBS"><span style=" font-size:14pt; text-decoration: underline; color:#007af4;">https://t.me/XcoderBS</span></a></p></body></html>
105 |
106 |
107 | false
108 |
109 |
110 | Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop
111 |
112 |
113 | true
114 |
115 |
116 |
117 |
118 |
119 | -
120 |
121 |
122 |
123 | 16777215
124 | 100000
125 |
126 |
127 |
128 | background-color: rgba(255, 255,255, 60);
129 | border: 2px solid rgba(252, 255, 255, 80);
130 | border-radius: 10px;
131 |
132 |
133 |
134 | 9
135 |
136 |
137 | 9
138 |
139 |
140 | 9
141 |
142 |
143 | 9
144 |
145 |
146 | 9
147 |
148 |
-
149 |
150 |
151 |
152 | 80
153 | true
154 |
155 |
156 |
157 | background-color: none;
158 | color: white;
159 | border: 0px;
160 |
161 |
162 | DECODE
163 |
164 |
165 | Qt::AlignmentFlag::AlignCenter
166 |
167 |
168 |
169 | -
170 |
171 |
-
172 |
173 |
174 |
175 | 0
176 | 65
177 |
178 |
179 |
180 |
181 | 40
182 | true
183 | PreferDefaultHinting
184 |
185 |
186 |
187 | QPushButton {color: white;}
188 | QPushButton:hover:!pressed
189 | {
190 | background-color: rgba(255, 255,255, 80);
191 | border: 2px solid rgba(252, 255, 255, 210);
192 | }
193 | QPushButton:pressed
194 | {
195 | background-color: rgba(255, 255,255, 100);
196 | border: 2px solid rgba(252, 255, 255, 220);
197 | }
198 |
199 |
200 | FILE
201 |
202 |
203 |
204 | -
205 |
206 |
207 |
208 | 0
209 | 65
210 |
211 |
212 |
213 |
214 | 40
215 | true
216 |
217 |
218 |
219 | QPushButton {color: white;}
220 | QPushButton:hover:!pressed
221 | {
222 | background-color: rgba(255, 255,255, 80);
223 | border: 2px solid rgba(252, 255, 255, 210);
224 | }
225 | QPushButton:pressed
226 | {
227 | background-color: rgba(255, 255,255, 100);
228 | border: 2px solid rgba(252, 255, 255, 220);
229 | }
230 |
231 |
232 | FILES
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 | -
242 |
243 |
244 | background-color: rgba(255, 255,255, 60);
245 | border: 2px solid rgba(252, 255, 255, 80);
246 | border-radius: 10px;
247 |
248 |
249 |
250 | 0
251 |
252 |
-
253 |
254 |
255 |
256 | 80
257 | true
258 |
259 |
260 |
261 | background-color: none;
262 | color: white;
263 | border: 0px;
264 |
265 |
266 | ENCODE
267 |
268 |
269 | Qt::AlignmentFlag::AlignCenter
270 |
271 |
272 |
273 | -
274 |
275 |
276 | 6
277 |
278 |
-
279 |
280 |
281 |
282 | 0
283 | 65
284 |
285 |
286 |
287 |
288 | 40
289 | true
290 |
291 |
292 |
293 | QPushButton {color: white;}
294 | QPushButton:hover:!pressed
295 | {
296 | background-color: rgba(255, 255,255, 80);
297 | border: 2px solid rgba(252, 255, 255, 210);
298 | }
299 | QPushButton:pressed
300 | {
301 | background-color: rgba(255, 255,255, 100);
302 | border: 2px solid rgba(252, 255, 255, 220);
303 | }
304 |
305 |
306 | FOLDER
307 |
308 |
309 |
310 | -
311 |
312 |
313 |
314 | 0
315 | 65
316 |
317 |
318 |
319 |
320 | 40
321 | true
322 |
323 |
324 |
325 | QPushButton {color: white;}
326 | QPushButton:hover:!pressed
327 | {
328 | background-color: rgba(255, 255,255, 80);
329 | border: 2px solid rgba(252, 255, 255, 210);
330 | }
331 | QPushButton:pressed
332 | {
333 | background-color: rgba(255, 255,255, 100);
334 | border: 2px solid rgba(252, 255, 255, 220);
335 | }
336 |
337 |
338 | FOLDERS
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 | -
348 |
349 |
350 | background-color: rgba(255, 255,255, 60);
351 | border: 2px solid rgba(252, 255, 255, 80);
352 | border-radius: 10px;
353 |
354 |
355 |
356 | 6
357 |
358 |
-
359 |
360 |
361 |
362 | 65
363 | true
364 |
365 |
366 |
367 | background-color: none;
368 | color: white;
369 | border: 0px;
370 |
371 |
372 | LANGUAGE
373 |
374 |
375 | Qt::AlignmentFlag::AlignCenter
376 |
377 |
378 |
379 | -
380 |
381 |
382 |
383 | 0
384 | 0
385 |
386 |
387 |
388 |
389 | 30
390 | true
391 |
392 |
393 |
394 | false
395 |
396 |
397 | QComboBox {
398 | color:white;
399 | margin: 5px;
400 | }
401 |
402 | QComboBox::drop-down {
403 | width: 40px;
404 | }
405 | QComboBox:editable {
406 | background: white;
407 | }
408 |
409 |
410 | false
411 |
412 |
-
413 |
414 | English
415 |
416 |
417 | -
418 |
419 | Русский
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 | -
428 |
429 |
430 |
431 | 0
432 | 50
433 |
434 |
435 |
436 |
437 | 20
438 | true
439 |
440 |
441 |
442 | QPushButton:hover:!pressed
443 | {
444 | background-color: rgba(255, 255,255, 100);
445 | border: 2px solid rgba(252, 255, 255, 210);
446 | }
447 | QPushButton:pressed
448 | {
449 | background-color: rgba(255, 255,255, 110);
450 | border: 2px solid rgba(252, 255, 255, 220);
451 | }
452 | QPushButton
453 | {
454 | color: white;
455 | background-color: rgba(255, 255, 255,90);
456 | border: 2px solid rgba(252, 255, 255, 100);
457 | border-radius: 10px;
458 | }
459 |
460 |
461 |
462 | ABOUT
463 |
464 |
465 |
466 | -
467 |
468 |
469 | Qt::Orientation::Vertical
470 |
471 |
472 |
473 | 20
474 | 40
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
--------------------------------------------------------------------------------
/ui/processing.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Processing
4 |
5 |
6 |
7 | 0
8 | 0
9 | 540
10 | 900
11 |
12 |
13 |
14 |
15 | 540
16 | 900
17 |
18 |
19 |
20 | DECODE
21 |
22 |
23 | background-color: rgb(4, 15, 15);
24 |
25 |
26 |
27 | 6
28 |
29 | -
30 |
31 |
32 | 0
33 |
34 |
-
35 |
36 |
37 |
38 | 16777215
39 | 100
40 |
41 |
42 |
43 |
44 | 70
45 | true
46 |
47 |
48 |
49 | color: rgb(0, 210, 105);
50 |
51 |
52 | DECODE
53 |
54 |
55 | Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop
56 |
57 |
58 |
59 | -
60 |
61 |
62 |
63 | 0
64 | 0
65 |
66 |
67 |
68 |
69 | 16777215
70 | 30
71 |
72 |
73 |
74 |
75 | false
76 | true
77 | true
78 |
79 |
80 |
81 | <html><head/><body><p><a href="tg://resolve?domain=XcoderBS"><span style=" font-size:14pt; text-decoration: underline; color:#007af4;">https://t.me/XcoderBS</span></a></p></body></html>
82 |
83 |
84 | Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop
85 |
86 |
87 |
88 |
89 |
90 | -
91 |
92 |
93 |
94 | 16777215
95 | 80
96 |
97 |
98 |
99 |
100 |
101 |
102 |
-
103 |
104 |
105 |
106 | 0
107 | 70
108 |
109 |
110 |
111 |
112 | 16777215
113 | 70
114 |
115 |
116 |
117 |
118 | 15
119 | true
120 |
121 |
122 |
123 | color: white;
124 | background-color: rgba(255, 255,255, 60);
125 | border: 2px solid rgba(252, 255, 255, 80);
126 | border-radius: 10px;
127 |
128 |
129 | Qt::ScrollBarPolicy::ScrollBarAlwaysOff
130 |
131 |
132 | Qt::ScrollBarPolicy::ScrollBarAlwaysOff
133 |
134 |
135 |
136 |
137 |
138 | QTextEdit::LineWrapMode::WidgetWidth
139 |
140 |
141 |
142 |
143 |
144 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
145 | <html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css">
146 | p, li { white-space: pre-wrap; }
147 | hr { height: 1px; border-width: 0; }
148 | li.unchecked::marker { content: "\2610"; }
149 | li.checked::marker { content: "\2612"; }
150 | </style></head><body style=" font-family:'Segoe UI'; font-size:15pt; font-weight:700; font-style:normal;">
151 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;"><br /></p></body></html>
152 |
153 |
154 | INPUT FILE
155 |
156 |
157 |
158 | -
159 |
160 |
161 |
162 | 100
163 | 70
164 |
165 |
166 |
167 |
168 | 30
169 | true
170 |
171 |
172 |
173 |
174 | QPushButton{
175 | background-color: rgba(255, 255,255, 60);
176 | border: 2px solid rgba(252, 255, 255, 80);
177 | border-radius: 10px;
178 | color: white;}
179 | QPushButton:hover:!pressed{
180 | background-color: rgba(255, 255,255, 80);
181 | border: 2px solid rgba(252, 255, 255, 210);
182 | }
183 | QPushButton:pressed{
184 | background-color: rgba(255, 255,255, 100);
185 | border: 2px solid rgba(252, 255, 255, 220);}
186 |
187 |
188 | SELECT
189 |
190 |
191 |
192 |
193 |
194 |
195 | -
196 |
197 |
198 |
199 | 16777215
200 | 80
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 | 6
209 |
210 |
211 | 0
212 |
213 |
-
214 |
215 |
216 |
217 | 0
218 | 70
219 |
220 |
221 |
222 |
223 | 16777215
224 | 70
225 |
226 |
227 |
228 |
229 | 15
230 | true
231 |
232 |
233 |
234 | color: white;
235 | background-color: rgba(255, 255,255, 60);
236 | border: 2px solid rgba(252, 255, 255, 80);
237 | border-radius: 10px;
238 |
239 |
240 | Qt::ScrollBarPolicy::ScrollBarAlwaysOff
241 |
242 |
243 | Qt::ScrollBarPolicy::ScrollBarAlwaysOff
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
253 | <html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css">
254 | p, li { white-space: pre-wrap; }
255 | hr { height: 1px; border-width: 0; }
256 | li.unchecked::marker { content: "\2610"; }
257 | li.checked::marker { content: "\2612"; }
258 | </style></head><body style=" font-family:'Segoe UI'; font-size:15pt; font-weight:700; font-style:normal;">
259 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;"><br /></p></body></html>
260 |
261 |
262 | OUT FOLDER (OPTIONALLY)
263 |
264 |
265 |
266 | -
267 |
268 |
269 |
270 | 100
271 | 70
272 |
273 |
274 |
275 |
276 | 30
277 | true
278 |
279 |
280 |
281 |
282 | QPushButton{
283 | background-color: rgba(255, 255,255, 60);
284 | border: 2px solid rgba(252, 255, 255, 80);
285 | border-radius: 10px;
286 | color: white;}
287 | QPushButton:hover:!pressed{
288 | background-color: rgba(255, 255,255, 80);
289 | border: 2px solid rgba(252, 255, 255, 210);
290 | }
291 | QPushButton:pressed{
292 | background-color: rgba(255, 255,255, 100);
293 | border: 2px solid rgba(252, 255, 255, 220);}
294 |
295 |
296 | SELECT
297 |
298 |
299 |
300 |
301 |
302 |
303 | -
304 |
305 |
306 |
307 | 16777215
308 | 16777215
309 |
310 |
311 |
312 |
313 | border: 2px solid rgba(252, 255, 255, 80);
314 | border-radius: 10px;
315 |
316 |
317 | Qt::ScrollBarPolicy::ScrollBarAlwaysOff
318 |
319 |
320 | Qt::ScrollBarPolicy::ScrollBarAlwaysOff
321 |
322 |
323 | QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents
324 |
325 |
326 | true
327 |
328 |
329 | Qt::AlignmentFlag::AlignCenter
330 |
331 |
332 |
333 |
334 | 0
335 | 0
336 | 518
337 | 374
338 |
339 |
340 |
341 |
342 | 1111111
343 | 16777215
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 | 0
352 |
353 |
354 | 0
355 |
356 |
357 | 0
358 |
359 |
360 | 0
361 |
362 |
363 | 0
364 |
365 |
-
366 |
367 |
368 |
369 | 0
370 | 0
371 |
372 |
373 |
374 |
375 | 11111
376 | 16777215
377 |
378 |
379 |
380 |
381 | 20
382 | true
383 |
384 |
385 |
386 | background-color: rgba(255, 255,255, 60); color:white;
387 |
388 |
389 | QFrame::Shape::NoFrame
390 |
391 |
392 | CONSOLE:
393 |
394 |
395 | Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop
396 |
397 |
398 | true
399 |
400 |
401 | 0
402 |
403 |
404 | Qt::TextInteractionFlag::LinksAccessibleByMouse
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 | -
413 |
414 |
415 |
416 | 50
417 | true
418 |
419 |
420 |
421 |
422 | QPushButton{
423 | background-color: rgba(255, 255,255, 60);
424 | border: 2px solid rgba(252, 255, 255, 80);
425 | border-radius: 10px;
426 | color: white;}
427 | QPushButton:hover:!pressed{
428 | background-color: rgba(255, 255,255, 80);
429 | border: 2px solid rgba(252, 255, 255, 210);
430 | }
431 | QPushButton:pressed{
432 | background-color: rgba(255, 255,255, 100);
433 | border: 2px solid rgba(252, 255, 255, 220);}
434 |
435 |
436 | DECODE
437 |
438 |
439 |
440 | -
441 |
442 |
443 |
444 | 50
445 | true
446 |
447 |
448 |
449 |
450 | QPushButton{
451 | background-color: rgba(255, 255,255, 60);
452 | border: 2px solid rgba(252, 255, 255, 80);
453 | border-radius: 10px;
454 | color: white;}
455 | QPushButton:hover:!pressed{
456 | background-color: rgba(255, 255,255, 80);
457 | border: 2px solid rgba(252, 255, 255, 210);
458 | }
459 | QPushButton:pressed{
460 | background-color: rgba(255, 255,255, 100);
461 | border: 2px solid rgba(252, 255, 255, 220);}
462 |
463 |
464 | CLOSE
465 |
466 |
467 |
468 |
469 |
470 |
471 | Input1
472 | Run
473 | Input2
474 | Select1
475 | Select2
476 |
477 |
478 |
479 |
480 |
--------------------------------------------------------------------------------