├── .gitattributes
├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── OnexExplorer.pro
├── README.MD
├── Source
├── Converters
│ ├── IModelConverter.h
│ ├── ImageConverter.cpp
│ ├── ImageConverter.h
│ ├── LittleEndianConverter.cpp
│ ├── LittleEndianConverter.h
│ ├── NosModelConverter.cpp
│ ├── NosModelConverter.h
│ ├── ObjConverter.cpp
│ └── ObjConverter.h
├── Decryptors
│ ├── INosDecryptor.h
│ ├── NosTextDatFileDecryptor.cpp
│ ├── NosTextDatFileDecryptor.h
│ ├── NosTextOthersFileDecryptor.cpp
│ ├── NosTextOthersFileDecryptor.h
│ ├── NosZlibDecryptor.cpp
│ └── NosZlibDecryptor.h
├── MainWindow.cpp
├── MainWindow.h
├── NosEnumTypes.h
├── Openers
│ ├── INosFileOpener.cpp
│ ├── INosFileOpener.h
│ ├── JsonConfigOpener.cpp
│ ├── JsonConfigOpener.h
│ ├── NosCCInfOpener.cpp
│ ├── NosCCInfOpener.h
│ ├── NosTextOpener.cpp
│ ├── NosTextOpener.h
│ ├── NosZlibOpener.cpp
│ └── NosZlibOpener.h
├── Ui
│ ├── FileInfo.cpp
│ ├── FileInfo.h
│ ├── OnexTreeItem.cpp
│ ├── OnexTreeItem.h
│ ├── Previews
│ │ ├── MultiImagePreview.cpp
│ │ ├── MultiImagePreview.h
│ │ ├── MultiImagePreview.ui
│ │ ├── SingleImagePreview.cpp
│ │ ├── SingleImagePreview.h
│ │ ├── SingleImagePreview.ui
│ │ ├── SingleModelPreview.cpp
│ │ ├── SingleModelPreview.h
│ │ ├── SingleTextFilePreview.cpp
│ │ ├── SingleTextFilePreview.h
│ │ └── SingleTextFilePreview.ui
│ ├── Settings.cpp
│ ├── Settings.h
│ ├── Settings.ui
│ └── TreeItems
│ │ ├── OnexNS4BbData.cpp
│ │ ├── OnexNS4BbData.h
│ │ ├── OnexNSipData.cpp
│ │ ├── OnexNSipData.h
│ │ ├── OnexNSmcData.cpp
│ │ ├── OnexNSmcData.h
│ │ ├── OnexNSmnData.cpp
│ │ ├── OnexNSmnData.h
│ │ ├── OnexNSmpData.cpp
│ │ ├── OnexNSmpData.h
│ │ ├── OnexNSmpFrame.cpp
│ │ ├── OnexNSmpFrame.h
│ │ ├── OnexNStcData.cpp
│ │ ├── OnexNStcData.h
│ │ ├── OnexNStgData.cpp
│ │ ├── OnexNStgData.h
│ │ ├── OnexNStpData.cpp
│ │ ├── OnexNStpData.h
│ │ ├── OnexNStpMipMap.cpp
│ │ ├── OnexNStpMipMap.h
│ │ ├── OnexTreeImage.cpp
│ │ ├── OnexTreeImage.h
│ │ ├── OnexTreeText.cpp
│ │ ├── OnexTreeText.h
│ │ ├── OnexTreeZlibItem.cpp
│ │ └── OnexTreeZlibItem.h
├── main.cpp
└── mainwindow.ui
├── resources.qrc
└── resources
├── kofi4.png
└── oxe_icon_trans.ico
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # C++ objects and libs
2 |
3 | *.slo
4 | *.lo
5 | *.o
6 | *.a
7 | *.la
8 | *.lai
9 | *.so
10 | *.dll
11 | *.dylib
12 |
13 | # Qt-es
14 |
15 | /.qmake.cache
16 | /.qmake.stash
17 | *.pro.user
18 | *.pro.user.*
19 | *.qbs.user
20 | *.qbs.user.*
21 | *.moc
22 | moc_*.cpp
23 | moc_predefs.h
24 | qrc_*.cpp
25 | ui_*.h
26 | Makefile*
27 | *build-*
28 |
29 | # QtCreator
30 |
31 | *.autosave
32 |
33 | # QtCtreator Qml
34 | *.qmlproject.user
35 | *.qmlproject.user.*
36 |
37 | # QtCtreator CMake
38 | CMakeLists.txt.user*
39 |
40 | # Windows trash
41 | *.db
42 |
43 | #VS Code
44 | .vscode/*
45 | .clang-format
46 |
47 | #CLion
48 | .idea/*
49 | cmake-build-debug/*
50 | cmake-build-release/*
51 |
52 | OnexExplorer
53 | *.NOS
54 |
55 | freeglut/*
56 |
57 | *.xmi
58 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 | project(OnexExplorer)
3 |
4 | set(DTARGET_64 ON)
5 |
6 | set(CMAKE_CXX_STANDARD 14)
7 |
8 | set(CMAKE_AUTOMOC ON)
9 | set(CMAKE_AUTOUIC ON)
10 | set(CMAKE_AUTORCC ON)
11 |
12 | set(CMAKE_INCLUDE_CURRENT_DIR ON)
13 |
14 | find_package(Qt5Core REQUIRED)
15 | find_package(Qt5Widgets REQUIRED)
16 | find_package(Qt5Gui REQUIRED)
17 |
18 | set(GLUT_LIBRARY_DIR "freeglut/lib")
19 | set(GLUT_INCLUDE_DIR "freeglut/include/GL")
20 | find_package(GLUT REQUIRED)
21 | include_directories(${GLUT_INCLUDE_DIR})
22 |
23 | set(SOURCES
24 | Source/main.cpp
25 | Source/MainWindow.cpp
26 | Source/Converters/ImageConverter.cpp
27 | Source/Converters/LittleEndianConverter.cpp
28 | Source/Converters/NosModelConverter.cpp
29 | Source/Converters/ObjConverter.cpp
30 | Source/Decryptors/NosTextDatFileDecryptor.cpp
31 | Source/Decryptors/NosTextOthersFileDecryptor.cpp
32 | Source/Decryptors/NosZlibDecryptor.cpp
33 | Source/Openers/NosZlibOpener.cpp
34 | Source/Openers/NosCCInfOpener.cpp
35 | Source/Openers/NosTextOpener.cpp
36 | Source/Openers/INosFileOpener.cpp
37 | Source/Openers/JsonConfigOpener.cpp
38 | Source/Ui/OnexTreeItem.cpp
39 | Source/Ui/FileInfo.cpp
40 | Source/Ui/Settings.cpp
41 | Source/Ui/Previews/SingleTextFilePreview.cpp
42 | Source/Ui/Previews/SingleImagePreview.cpp
43 | Source/Ui/Previews/MultiImagePreview.cpp
44 | Source/Ui/Previews/SingleModelPreview.cpp
45 | Source/Ui/TreeItems/OnexTreeImage.cpp
46 | Source/Ui/TreeItems/OnexTreeText.cpp
47 | Source/Ui/TreeItems/OnexTreeZlibItem.cpp
48 | Source/Ui/TreeItems/OnexNSipData.cpp
49 | Source/Ui/TreeItems/OnexNS4BbData.cpp
50 | Source/Ui/TreeItems/OnexNStcData.cpp
51 | Source/Ui/TreeItems/OnexNStpData.cpp
52 | Source/Ui/TreeItems/OnexNStpMipMap.cpp
53 | Source/Ui/TreeItems/OnexNSmpData.cpp
54 | Source/Ui/TreeItems/OnexNSmpFrame.cpp
55 | Source/Ui/TreeItems/OnexNStgData.cpp
56 | Source/Ui/TreeItems/OnexNSmnData.cpp
57 | Source/Ui/TreeItems/OnexNSmcData.cpp
58 | )
59 |
60 | set(MOC_HEADERS
61 | Source/MainWindow.h
62 | Source/NosEnumTypes.h
63 | Source/Converters/ImageConverter.h
64 | Source/Converters/LittleEndianConverter.h
65 | Source/Converters/IModelConverter.h
66 | Source/Converters/NosModelConverter.h
67 | Source/Converters/ObjConverter.h
68 | Source/Decryptors/NosTextDatFileDecryptor.h
69 | Source/Decryptors/NosTextOthersFileDecryptor.h
70 | Source/Decryptors/NosZlibDecryptor.h
71 | Source/Openers/NosZlibOpener.h
72 | Source/Openers/NosCCInfOpener.h
73 | Source/Openers/NosTextOpener.h
74 | Source/Openers/INosFileOpener.h
75 | Source/Openers/JsonConfigOpener.h
76 | Source/Ui/OnexTreeItem.h
77 | Source/Ui/FileInfo.h
78 | Source/Ui/Settings.h
79 | Source/Ui/Previews/SingleTextFilePreview.h
80 | Source/Ui/Previews/SingleImagePreview.h
81 | Source/Ui/Previews/MultiImagePreview.h
82 | Source/Ui/Previews/SingleModelPreview.h
83 | Source/Ui/TreeItems/OnexTreeImage.h
84 | Source/Ui/TreeItems/OnexTreeText.h
85 | Source/Ui/TreeItems/OnexTreeZlibItem.h
86 | Source/Ui/TreeItems/OnexNSipData.h
87 | Source/Ui/TreeItems/OnexNS4BbData.h
88 | Source/Ui/TreeItems/OnexNStcData.h
89 | Source/Ui/TreeItems/OnexNStpData.h
90 | Source/Ui/TreeItems/OnexNStpMipMap.h
91 | Source/Ui/TreeItems/OnexNSmpData.h
92 | Source/Ui/TreeItems/OnexNSmpFrame.h
93 | Source/Ui/TreeItems/OnexNStgData.h
94 | Source/Ui/TreeItems/OnexNSmnData.h
95 | Source/Ui/TreeItems/OnexNSmcData.h
96 | )
97 |
98 | set(UIS
99 | Source/mainwindow.ui
100 | Source/Ui/Previews/SingleTextFilePreview.ui
101 | Source/Ui/Previews/SingleImagePreview.ui
102 | Source/Ui/Previews/MultiImagePreview.ui
103 | Source/Ui/Settings.ui
104 | )
105 |
106 | set(RESOURCES
107 | resources.qrc
108 | )
109 |
110 |
111 | # compile
112 | #add_executable( PROJECT_NAME ${SOURCES} ${MOC_SRCS} ${RES_SOURCES} ${UI_HEADERS} )
113 | # or use line below instead, if you using Windows ™ Operating System.
114 | add_executable(OnexExplorer WIN32 ${SOURCES} ${MOC_SRCS} ${RES_SOURCES} ${UI_HEADERS} ${RESOURCES})
115 |
116 | # build it (link libraries)
117 | target_link_libraries(OnexExplorer Qt5::Core)
118 | target_link_libraries(OnexExplorer Qt5::Widgets)
119 | target_link_libraries(OnexExplorer Qt5::Gui)
120 | target_link_libraries(OnexExplorer ${GLUT_LIBRARIES})
121 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Boost Software License - Version 1.0 - August 17th, 2003
2 |
3 | Permission is hereby granted, free of charge, to any person or organization
4 | obtaining a copy of the software and accompanying documentation covered by
5 | this license (the "Software") to use, reproduce, display, distribute,
6 | execute, and transmit the Software, and to prepare derivative works of the
7 | Software, and to permit third-parties to whom the Software is furnished to
8 | do so, all subject to the following:
9 |
10 | The copyright notices in the Software and this entire statement, including
11 | the above license grant, this restriction and the following disclaimer,
12 | must be included in all copies of the Software, in whole or in part, and
13 | all derivative works of the Software, unless such copies or derivative
14 | works are solely in the form of machine-executable object code generated by
15 | a source language processor.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/OnexExplorer.pro:
--------------------------------------------------------------------------------
1 | #-------------------------------------------------
2 | #
3 | # Project created by QtCreator 2017-03-12T15:48:27
4 | #
5 | #-------------------------------------------------
6 |
7 | QT += core gui widgets opengl
8 |
9 | TARGET = OnexExplorer
10 | TEMPLATE = app
11 |
12 | # The following define makes your compiler emit warnings if you use
13 | # any feature of Qt which as been marked as deprecated (the exact warnings
14 | # depend on your compiler). Please consult the documentation of the
15 | # deprecated API in order to know how to port your code away from it.
16 | DEFINES += QT_DEPRECATED_WARNINGS
17 |
18 | # You can also make your code fail to compile if you use deprecated APIs.
19 | # In order to do so, uncomment the following line.
20 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
21 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
22 |
23 |
24 | SOURCES += \
25 | Source/main.cpp \
26 | Source/MainWindow.cpp \
27 | Source/Converters/ImageConverter.cpp \
28 | Source/Converters/LittleEndianConverter.cpp \
29 | Source/Converters/NosModelConverter.cpp \
30 | Source/Converters/ObjConverter.cpp \
31 | Source/Decryptors/NosTextDatFileDecryptor.cpp \
32 | Source/Decryptors/NosTextOthersFileDecryptor.cpp \
33 | Source/Decryptors/NosZlibDecryptor.cpp \
34 | Source/Openers/NosZlibOpener.cpp \
35 | Source/Openers/NosCCInfOpener.cpp \
36 | Source/Openers/NosTextOpener.cpp \
37 | Source/Openers/INosFileOpener.cpp \
38 | Source/Openers/JsonConfigOpener.cpp \
39 | Source/Ui/OnexTreeItem.cpp \
40 | Source/Ui/FileInfo.cpp \
41 | Source/Ui/Settings.cpp \
42 | Source/Ui/Previews/SingleTextFilePreview.cpp \
43 | Source/Ui/Previews/SingleImagePreview.cpp \
44 | Source/Ui/Previews/MultiImagePreview.cpp \
45 | Source/Ui/Previews/SingleModelPreview.cpp \
46 | Source/Ui/TreeItems/OnexTreeImage.cpp \
47 | Source/Ui/TreeItems/OnexTreeText.cpp \
48 | Source/Ui/TreeItems/OnexTreeZlibItem.cpp \
49 | Source/Ui/TreeItems/OnexNSipData.cpp \
50 | Source/Ui/TreeItems/OnexNS4BbData.cpp \
51 | Source/Ui/TreeItems/OnexNStcData.cpp \
52 | Source/Ui/TreeItems/OnexNStpData.cpp \
53 | Source/Ui/TreeItems/OnexNStpMipMap.cpp \
54 | Source/Ui/TreeItems/OnexNSmpData.cpp \
55 | Source/Ui/TreeItems/OnexNSmpFrame.cpp \
56 | Source/Ui/TreeItems/OnexNStgData.cpp \
57 | Source/Ui/TreeItems/OnexNSmnData.cpp \
58 | Source/Ui/TreeItems/OnexNSmcData.cpp
59 |
60 |
61 |
62 | HEADERS += \
63 | Source/MainWindow.h \
64 | Source/NosEnumTypes.h \
65 | Source/Converters/ImageConverter.h \
66 | Source/Converters/LittleEndianConverter.h \
67 | Source/Converters/IModelConverter.h \
68 | Source/Converters/NosModelConverter.h \
69 | Source/Converters/ObjConverter.h \
70 | Source/Decryptors/NosTextDatFileDecryptor.h \
71 | Source/Decryptors/NosTextOthersFileDecryptor.h \
72 | Source/Decryptors/NosZlibDecryptor.h \
73 | Source/Openers/NosZlibOpener.h \
74 | Source/Openers/NosCCInfOpener.h \
75 | Source/Openers/NosTextOpener.h \
76 | Source/Openers/INosFileOpener.h \
77 | Source/Openers/JsonConfigOpener.h \
78 | Source/Ui/OnexTreeItem.h \
79 | Source/Ui/FileInfo.h \
80 | Source/Ui/settings.h \
81 | Source/Ui/Previews/SingleTextFilePreview.h \
82 | Source/Ui/Previews/SingleImagePreview.h \
83 | Source/Ui/Previews/MultiImagePreview.h \
84 | Source/Ui/Previews/SingleModelPreview.h \
85 | Source/Ui/TreeItems/OnexTreeImage.h \
86 | Source/Ui/TreeItems/OnexTreeText.h \
87 | Source/Ui/TreeItems/OnexTreeZlibItem.h \
88 | Source/Ui/TreeItems/OnexNSipData.h \
89 | Source/Ui/TreeItems/OnexNS4BbData.h \
90 | Source/Ui/TreeItems/OnexNStcData.h \
91 | Source/Ui/TreeItems/OnexNStpData.h \
92 | Source/Ui/TreeItems/OnexNStpMipMap.h \
93 | Source/Ui/TreeItems/OnexNSmpData.h \
94 | Source/Ui/TreeItems/OnexNSmpFrame.h \
95 | Source/Ui/TreeItems/OnexNStgData.h \
96 | Source/Ui/TreeItems/OnexNSmnData.h \
97 | Source/Ui/TreeItems/OnexNSmcData.h
98 |
99 | FORMS += \
100 | Source/mainwindow.ui \
101 | Source/Ui/Previews/SingleTextFilePreview.ui \
102 | Source/Ui/Previews/SingleImagePreview.ui \
103 | Source/Ui/Previews/MultiImagePreview.ui \
104 | Source/Ui/Settings.ui
105 |
106 | DISTFILES +=
107 |
108 | RESOURCES += \
109 | resources.qrc
110 |
111 | RC_ICONS = ./resources/oxe_icon_trans.ico
112 |
113 |
114 | win32: INCLUDEPATH += $$PWD/freeglut/include/GL
115 | else:unix: /usr/include/GL/
116 |
117 | win32: LIBS += -L$$PWD/freeglut/lib -lfreeglut
118 | else:unix: LIBS += -lglut -lGLU -lGL
119 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | # OnexExplorer #
2 |
3 | OnexExplorer is an open-source tool for unpacking and repacking .NOS data files from game called NosTale. It can open almost all .NOS files and allows to view the Content, export and replace it and even add own Content to them. Our software works on Qt 5.5+ with OpenGl.
4 |
5 | Client Modding Discord: [
](https://discord.gg/t73Ayca)
6 |
7 |
8 | ## Currently supported archives ##
9 |
10 | - NSgtdData (Items, quests, skills, mobs etc. data)
11 | - NSlangData (_code_lang_list (ztsXXXe))
12 | - NScliData (constring.dat)
13 | - NSetcData (Word list for Memory & TabooStr.lst)
14 | - NS4BbData (Big images)
15 | - NSipData (Icons)
16 | - NStpData (Map and model textures)
17 | - NStpeData (Effect textures)
18 | - NStpuData (UI textures)
19 | - NStcData (Map Grids)
20 | - NSmpData (Monster-related sprites)
21 | - NSppData (Player-related sprites)
22 | - NSmnData (Mob-related sprite infos)
23 | - NSpnData (Player-related sprite infos)
24 | - NSmcData (Monster-related animation kits)
25 | - NSpcData (Player-related animation kits)
26 | - NStgData (3D Models)
27 | - NStgeData (3D VFX models)
28 |
29 |
30 | **Other files will be added in future updates.**
31 |
32 |
33 | ## Unsupported archives ##
34 |
35 | - NStuData (Map configs)
36 | - NSeffData (VFX configs)
37 | - NStsData
38 | - NStkData
39 | - NSemData
40 | - NSesData
41 | - NSedData
42 | - NSgrdData
43 | - NSpmData
44 |
45 |
46 | ## License ##
47 |
48 | Our project is based on **Boost Software License**:
49 | > [LICENSE](/LICENSE)
50 |
51 |
52 | ## Installation ##
53 |
54 | **Download** a **[release](https://github.com/Pumbaa98/OnexExplorer/releases)** or:
55 |
56 | 1. Compile given sourcecode using Qt 5.5+ and OpenGl (freeglut)
57 | 2. Move required Qt DLL's to EXE's location
58 | 3. Run the program
59 |
60 | The experimental branch and releases flaged with **pre-release** are in development and don't guarantee that everything works as expected.
61 | **If you have any problems please try to compile the master branch or use regular releases!**
62 |
63 |
64 | ## Report Bugs and suggest features ##
65 | If you have any **problems** with OnexExplorer or want to suggest new **features** feel free to open **[issues](https://github.com/Pumbaa98/OnexExplorer/issues)**.
66 | Please dont forget to mention the **version** you're using.
67 |
--------------------------------------------------------------------------------
/Source/Converters/IModelConverter.h:
--------------------------------------------------------------------------------
1 | #ifndef IMODELCONVERTER_H
2 | #define IMODELCONVERTER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include "LittleEndianConverter.h"
10 |
11 | struct ModelAnimationPositionFrame {
12 | int timestamp;
13 | QVector3D position;
14 | };
15 | struct ModelAnimationRotationFrame {
16 | int timestamp;
17 | QVector4D rotation;
18 | };
19 | struct ModelAnimationScaleFrame {
20 | int timestamp;
21 | QVector3D scale;
22 | };
23 | struct ModelGroup {
24 | int number;
25 | QVector faces;
26 | int texture;
27 | };
28 | struct ModelObject {
29 | QVector groups;
30 | QVector3D position;
31 | QVector4D rotation;
32 | QVector3D scale;
33 | QVector animationPositions;
34 | QVector animationRotations;
35 | QVector animationScales;
36 | };
37 | struct Model {
38 | QVector vertices;
39 | QVector normals;
40 | QVector uv;
41 | QVector groups;
42 | QVector objects;
43 | float uvScale;
44 | };
45 |
46 | class IModelConverter {
47 | protected:
48 | LittleEndianConverter *littleEndianConverter;
49 | };
50 |
51 | #endif // IMODELCONVERTER_H
52 |
--------------------------------------------------------------------------------
/Source/Converters/ImageConverter.cpp:
--------------------------------------------------------------------------------
1 | #include "ImageConverter.h"
2 | #include
3 |
4 |
5 | QImage ImageConverter::convertGBAR4444(QByteArray &array, int width, int height, int startByte)
6 | /// GBAR = ARGB (endianness)
7 | {
8 | qDebug() << "Opened GBAR4444 image.";
9 | QImage img(width, height, QImage::Format_ARGB32);
10 | img.fill(Qt::transparent);
11 | for (int y = 0; y < height; ++y) {
12 | for (int x = 0; x < width; ++x) {
13 | uchar gb = array.at(startByte + y * 2 * width + x * 2);
14 | uchar ar = array.at(startByte + y * 2 * width + x * 2 + 1);
15 | uchar g = gb >> 4;
16 | uchar b = gb & 0xF;
17 | uchar a = ar >> 4;
18 | uchar r = ar & 0xF;
19 | img.setPixel(x, y, qRgba(r * 0x11, g * 0x11, b * 0x11, a * 0x11));
20 | }
21 | }
22 | return img;
23 | }
24 |
25 | QImage ImageConverter::convertBGRA8888(QByteArray &array, int width, int height, int startByte) {
26 | qDebug() << "Opened BGRA8888 image.";
27 | QImage img(width, height, QImage::Format_ARGB32);
28 | img.fill(Qt::transparent);
29 | for (int y = 0; y < height; ++y) {
30 | for (int x = 0; x < width; ++x) {
31 | uchar b = array.at(startByte + y * 4 * width + x * 4);
32 | uchar g = array.at(startByte + y * 4 * width + x * 4 + 1);
33 | uchar r = array.at(startByte + y * 4 * width + x * 4 + 2);
34 | uchar a = array.at(startByte + y * 4 * width + x * 4 + 3);
35 | img.setPixel(x, y, qRgba(r, g, b, a));
36 | }
37 | }
38 | return img;
39 | }
40 |
41 | QImage ImageConverter::convertARGB555(QByteArray &array, int width, int height, int startByte) {
42 | qDebug() << "Opened ARGB555 image.";
43 | QImage img(width, height, QImage::Format_ARGB32);
44 | img.fill(Qt::transparent);
45 | for (int y = 0; y < height; ++y) {
46 | for (int x = 0; x < width; ++x) {
47 | ushort bytes = littleEndianConverter->fromShort(array.mid(startByte + y * 2 * width + x * 2, 2));
48 | uchar a = 255 * (bytes & 0x8000) >> 15; // 0x8000 = 0b1000000000000000
49 | uchar r = 8 * (bytes & 0x7C00) >> 10; // 0x7C00 = 0b0111110000000000
50 | uchar g = 8 * (bytes & 0x3E0) >> 5; // 0x3E0 = 0b0000001111100000
51 | uchar b = 8 * (bytes & 0x1F); // 0x1F = 0b0000000000011111
52 |
53 | img.setPixel(x, y, qRgba(r, g, b, a));
54 | }
55 | }
56 | return img;
57 | }
58 |
59 | QImage ImageConverter::convertNSTC(QByteArray &array, int width, int height, int startByte) {
60 | qDebug() << "Opened NSTC image.";
61 | QImage img(width, height, QImage::Format_ARGB32);
62 | img.fill(Qt::transparent);
63 | for (int x = 0; x < width; ++x) {
64 | for (int y = 0; y < height; ++y) {
65 | uchar value = array.at(startByte + y * width + x);
66 | if (colors.contains(value))
67 | img.setPixel(x, y, colors[value].rgb());
68 | else
69 | img.setPixel(x, y, colors[0xFF].rgb());
70 | }
71 | }
72 | return img.scaled(QSize(width * 2, height * 2));
73 | }
74 |
75 | QImage ImageConverter::convertBGRA8888_INTERLACED(QByteArray &array, int width, int height, int startByte) {
76 | qDebug() << "Opened BGRA888_INTERLACED image.";
77 | QImage img(width, height, QImage::Format_ARGB32);
78 | img.fill(Qt::transparent);
79 | short num = 0, x = 0, y = 0;
80 | for (int i = 0; i < array.size() - startByte;) {
81 | uchar b = array.at(startByte + i++);
82 | uchar g = array.at(startByte + i++);
83 | uchar r = array.at(startByte + i++);
84 | uchar a = array.at(startByte + i++);
85 | img.setPixel(x, y, qRgba(r, g, b, a));
86 | x++;
87 | if (x == (num + 256) || x == width) {
88 | x = num;
89 | y++;
90 | }
91 | if (y == height) {
92 | y = 0;
93 | num += 256;
94 | x = num;
95 | }
96 | }
97 | return img;
98 | }
99 |
100 | QImage ImageConverter::convertBARG4444(QByteArray &array, int width, int height, int startByte)
101 | /// BARG = RGBA (endianness)
102 | {
103 | qDebug() << "Opened BARG4444 image.";
104 | QImage img(width, height, QImage::Format_ARGB32);
105 | img.fill(Qt::transparent);
106 | for (int y = 0; y < height; ++y) {
107 | for (int x = 0; x < width; ++x) {
108 | uchar ba = array.at(startByte + y * 2 * width + x * 2);
109 | uchar rg = array.at(startByte + y * 2 * width + x * 2 + 1);
110 | uchar b = ba >> 4;
111 | uchar a = ba & 0xF;
112 | uchar r = rg >> 4;
113 | uchar g = rg & 0xF;
114 | img.setPixel(x, y, qRgba(r * 0x11, g * 0x11, b * 0x11, a * 0x11));
115 | }
116 | }
117 | return img;
118 | }
119 |
120 | QImage ImageConverter::convertGrayscale(QByteArray &array, int width, int height, int startByte) {
121 | //qDebug() << "Opened Grayscale image.";
122 | QImage img(width, height, QImage::Format_ARGB32);
123 | img.fill(Qt::transparent);
124 | for (int y = 0; y < height; ++y) {
125 | for (int x = 0; x < width; ++x) {
126 | uchar gray = array.at(startByte + y * width + x);
127 | img.setPixel(x, y, qRgba(gray, gray, gray, gray));
128 | }
129 | }
130 | return img;
131 | }
132 |
133 | // ////////////////////////////////////////////////////////////////////////////////////////////////////////
134 | QByteArray ImageConverter::toGBAR4444(QImage &image) {
135 | qDebug() << "Replaced GBAR4444 image.";
136 | QByteArray data;
137 | for (int y = 0; y < image.height(); ++y) {
138 | for (int x = 0; x < image.width(); ++x) {
139 | QRgb currentPixel = image.pixel(x, y);
140 | uchar green = qGreen(currentPixel) / 0x11;
141 | uchar blue = qBlue(currentPixel) / 0x11;
142 | uchar alpha = qAlpha(currentPixel) / 0x11;
143 | uchar red = qRed(currentPixel) / 0x11;
144 | uchar gb = (green << 4) + blue;
145 | uchar ar = (alpha << 4) + red;
146 | data.push_back(gb);
147 | data.push_back(ar);
148 | }
149 | }
150 | return data;
151 | }
152 |
153 | QByteArray ImageConverter::toNSTC(QImage &image) {
154 | qDebug() << "Replaced NSTC image.";
155 | QByteArray data;
156 | int i = 0, j = 0;
157 | for (int y = 0; y < image.height(); ++y) {
158 | for (int x = 0; x < image.width(); ++x) {
159 | QRgb currentPixel = image.pixel(x, y);
160 | QColor pixColor = qRgb(qRed(currentPixel), qGreen(currentPixel), qBlue(currentPixel));
161 | if (colors.values().contains(pixColor)) {
162 | i++;
163 | data.push_back(colors.key(pixColor));
164 | } else {
165 | j++;
166 | data.push_back(0xFF);
167 | }
168 | }
169 | }
170 | return data;
171 | }
172 |
173 | QByteArray ImageConverter::toBGRA8888(QImage &image) {
174 | qDebug() << "Replaced BGRA8888 image.";
175 | QByteArray data;
176 | for (int y = 0; y < image.height(); ++y) {
177 | for (int x = 0; x < image.width(); ++x) {
178 | QRgb currentPixel = image.pixel(x, y);
179 | uchar alpha = qAlpha(currentPixel);
180 | uchar red = qRed(currentPixel);
181 | uchar green = qGreen(currentPixel);
182 | uchar blue = qBlue(currentPixel);
183 | data.push_back(blue);
184 | data.push_back(green);
185 | data.push_back(red);
186 | data.push_back(alpha);
187 | }
188 | }
189 | return data;
190 | }
191 |
192 | QByteArray ImageConverter::toARGB555(QImage &image) {
193 | qDebug() << "Replaced ARGB555 image.";
194 | // NOPE
195 | QByteArray data;
196 | for (int y = 0; y < image.height(); ++y) {
197 | for (int x = 0; x < image.width(); ++x) {
198 | QRgb currentPixel = image.pixel(x, y);
199 | uchar alpha = qAlpha(currentPixel) / 0xFF; // full alpha or gtfo
200 | uchar red = qRed(currentPixel) / 0x08;
201 | uchar green = qGreen(currentPixel) / 0x08;
202 | uchar blue = qBlue(currentPixel) / 0x08;
203 | ushort bytes = (alpha << 15) | (red << 10) | (green << 5) | (blue);
204 | uchar first = bytes >> 8;
205 | uchar second = bytes & 0xFF;
206 | data.push_back(second);
207 | data.push_back(first);
208 | }
209 | }
210 | return data;
211 | }
212 |
213 | QByteArray ImageConverter::toBGRA8888_INTERLACED(QImage &image) {
214 | qDebug() << "Replaced BGRA8888_INTERLACED image.";
215 | QByteArray data;
216 | short num = 0, x = 0, y = 0;
217 | for (int i = 0; i < image.width() * image.height(); i++) {
218 | QRgb currentPixel = image.pixel(x, y);
219 | uchar b = qBlue(currentPixel);
220 | uchar g = qGreen(currentPixel);
221 | uchar r = qRed(currentPixel);
222 | uchar a = qAlpha(currentPixel);
223 | data.push_back(b);
224 | data.push_back(g);
225 | data.push_back(r);
226 | data.push_back(a);
227 | x++;
228 | if (x == (num + 256) || x == image.width()) {
229 | x = num;
230 | y++;
231 | }
232 | if (y == image.height()) {
233 | y = 0;
234 | num += 256;
235 | x = num;
236 | }
237 | }
238 | return data;
239 | }
240 |
241 | QByteArray ImageConverter::toBARG4444(QImage &image) {
242 | qDebug() << "Replaced BARG4444 image.";
243 | // only in 2k6 NSip, not tested, should work tho as its the same as GBAR4444 but different order.
244 | QByteArray data;
245 | for (int y = 0; y < image.height(); ++y) {
246 | for (int x = 0; x < image.width(); ++x) {
247 | QRgb currentPixel = image.pixel(x, y);
248 | uchar blue = qBlue(currentPixel) / 0x11;
249 | uchar alpha = qAlpha(currentPixel) / 0x11;
250 | uchar red = qRed(currentPixel) / 0x11;
251 | uchar green = qGreen(currentPixel) / 0x11;
252 | uchar ba = (blue << 4) + alpha;
253 | uchar rg = (red << 4) + green;
254 | data.push_back(ba);
255 | data.push_back(rg);
256 | }
257 | }
258 | return data;
259 | }
260 |
261 | QByteArray ImageConverter::toGrayscale(QImage &image) {
262 | //qDebug() << "Replaced Grayscale image.";
263 | QByteArray data;
264 | for (int y = 0; y < image.height(); ++y) {
265 | for (int x = 0; x < image.width(); ++x) {
266 | QRgb currentPixel = image.pixel(x, y);
267 | uchar gray = qGreen(currentPixel);
268 | data.push_back(gray);
269 | }
270 | }
271 | return data;
272 | }
273 |
274 | ImageConverter::ImageConverter(LittleEndianConverter *littleEndianConverter) : littleEndianConverter(littleEndianConverter) {
275 | colors[0x0] = qRgb(255, 255, 255);
276 | colors[0x1] = qRgb(100, 100, 100);
277 | colors[0x2] = qRgb(100, 150, 100);
278 | colors[0x3] = qRgb(0, 0, 0);
279 | colors[0x9] = qRgb(100, 100, 150);
280 | colors[0x0a] = qRgb(0, 50, 200);
281 | colors[0x0b] = qRgb(150, 100, 100);
282 | colors[0x0d] = qRgb(150, 150, 100);
283 | colors[0x10] = qRgb(150, 100, 150);
284 | colors[0x11] = qRgb(0, 200, 50);
285 | colors[0x12] = qRgb(200, 50, 0);
286 | colors[0x13] = qRgb(250, 230, 10);
287 | colors[0xFF] = qRgb(150, 0, 255);
288 | }
289 |
--------------------------------------------------------------------------------
/Source/Converters/ImageConverter.h:
--------------------------------------------------------------------------------
1 | #ifndef IMAGECONVERTER_H
2 | #define IMAGECONVERTER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "LittleEndianConverter.h"
9 |
10 | class ImageConverter {
11 | public:
12 | QImage convertGBAR4444(QByteArray &array, int width, int height, int startByte = 0);
13 | QImage convertBGRA8888(QByteArray &array, int width, int height, int startByte = 0);
14 | QImage convertARGB555(QByteArray &array, int width, int height, int startByte = 0);
15 | QImage convertNSTC(QByteArray &array, int width, int height, int startByte = 0);
16 | QImage convertBGRA8888_INTERLACED(QByteArray &array, int width, int height, int startByte = 0);
17 | QImage convertBARG4444(QByteArray &array, int width, int height, int startByte = 0);
18 | QImage convertGrayscale(QByteArray &array, int width, int height, int startByte = 0);
19 | QByteArray toGBAR4444(QImage &image);
20 | QByteArray toNSTC(QImage &image);
21 | QByteArray toBGRA8888(QImage &image);
22 | QByteArray toARGB555(QImage &image);
23 | QByteArray toBGRA8888_INTERLACED(QImage &image);
24 | QByteArray toBARG4444(QImage &image);
25 | QByteArray toGrayscale(QImage &image);
26 | explicit ImageConverter(LittleEndianConverter *littleEndianConverter);
27 | private:
28 | QMap colors;
29 | LittleEndianConverter *littleEndianConverter;
30 | };
31 |
32 | #endif // IMAGECONVERTER_H
33 |
--------------------------------------------------------------------------------
/Source/Converters/LittleEndianConverter.cpp:
--------------------------------------------------------------------------------
1 | #include "LittleEndianConverter.h"
2 | #include
3 |
4 | short LittleEndianConverter::fromShort(QByteArray array) {
5 | return qFromLittleEndian(reinterpret_cast(array.data()));
6 | }
7 |
8 | int LittleEndianConverter::fromInt(QByteArray array) {
9 | return qFromLittleEndian(reinterpret_cast(array.data()));
10 | }
11 |
12 | float LittleEndianConverter::fromFloat(QByteArray array) {
13 | return qFromLittleEndian(reinterpret_cast(array.data()));
14 | }
15 |
16 | QByteArray LittleEndianConverter::toShort(short number) {
17 | QByteArray writeArray;
18 | writeArray.resize(2);
19 | qToLittleEndian(number, reinterpret_cast(writeArray.data()));
20 | return writeArray;
21 | }
22 |
23 | QByteArray LittleEndianConverter::toInt(int number) {
24 | QByteArray writeArray;
25 | writeArray.resize(4);
26 | qToLittleEndian(number, reinterpret_cast(writeArray.data()));
27 | return writeArray;
28 | }
29 |
30 | QByteArray LittleEndianConverter::toFloat(float number) {
31 | QByteArray writeArray;
32 | writeArray.resize(4);
33 | qToLittleEndian(number, reinterpret_cast(writeArray.data()));
34 | return writeArray;
35 | }
36 |
37 | int LittleEndianConverter::readInt(QFile &file) {
38 | return fromInt(file.read(4));
39 | }
40 |
41 | short LittleEndianConverter::readShort(QFile &file) {
42 | return fromShort(file.read(2));
43 | }
--------------------------------------------------------------------------------
/Source/Converters/LittleEndianConverter.h:
--------------------------------------------------------------------------------
1 | #ifndef LITTLEENDIANCONVERTER_H
2 | #define LITTLEENDIANCONVERTER_H
3 |
4 | #include
5 | #include
6 |
7 | class LittleEndianConverter {
8 | public:
9 | short fromShort(QByteArray array);
10 | int fromInt(QByteArray array);
11 | float fromFloat(QByteArray array);
12 | QByteArray toShort(short number);
13 | QByteArray toInt(int number);
14 | QByteArray toFloat(float number);
15 | int readInt(QFile &file);
16 | short readShort(QFile &file);
17 | };
18 |
19 | #endif // LITTLEENDIANCONVERTER_H
--------------------------------------------------------------------------------
/Source/Converters/NosModelConverter.cpp:
--------------------------------------------------------------------------------
1 | #include "NosModelConverter.h"
2 | #include "LittleEndianConverter.h"
3 | #include
4 | #include
5 |
6 | NosModelConverter::NosModelConverter() {
7 | this->littleEndianConverter = new LittleEndianConverter();
8 | }
9 |
10 | Model *NosModelConverter::fromBinary(const QByteArray &obj) {
11 | auto *model = new Model();
12 | float uvScale = littleEndianConverter->fromFloat(obj.mid(0x30, 4));
13 | int verticesCount = littleEndianConverter->fromShort(obj.mid(0x34, 2));
14 | int offset = 0x36;
15 | model->uvScale = uvScale;
16 | model->vertices = readNosVertices(obj, offset, verticesCount);
17 | model->uv = readNosUV(obj, offset, verticesCount, uvScale);
18 | model->normals = readNosNormals(obj, offset, verticesCount);
19 | QVector faces = readNosFaces(obj, offset);
20 | model = readModelConstruction(obj, offset, model, faces);
21 | return model;
22 | }
23 |
24 | QByteArray NosModelConverter::toBinary(Model *model) {
25 | QByteArray newContent;
26 | newContent.append(littleEndianConverter->toFloat(model->uvScale));
27 | newContent.append(littleEndianConverter->toShort(model->vertices.size()));
28 | for (auto &vertex : model->vertices) {
29 | newContent.append(littleEndianConverter->toFloat(vertex.x()));
30 | newContent.append(littleEndianConverter->toFloat(vertex.y()));
31 | newContent.append(littleEndianConverter->toFloat(vertex.z()));
32 | }
33 | for (int i = 0; i < model->uv.size(); i++) {
34 | float u = model->uv[i].x();
35 | u /= model->uvScale;
36 | float v = model->uv[i].y();
37 | v = (1.0 - v) / model->uvScale;
38 | newContent.append(littleEndianConverter->toShort(u));
39 | newContent.append(littleEndianConverter->toShort(v));
40 | }
41 | for (auto &normal : model->normals) {
42 | uint8_t x = normal.x() * 0x7F;
43 | uint8_t y = normal.y() * 0x7F;
44 | uint8_t z = normal.z() * 0x7F;
45 | newContent.append(x);
46 | newContent.append(y);
47 | newContent.append(z);
48 | }
49 | newContent.append(littleEndianConverter->toShort(model->groups.size()));
50 | for (auto &group : model->groups) {
51 | newContent.append(littleEndianConverter->toShort(group.faces.size() * 3)); // GroupSize
52 | for (auto &face : group.faces) {
53 | newContent.append(littleEndianConverter->toShort(face.x()));
54 | newContent.append(littleEndianConverter->toShort(face.y()));
55 | newContent.append(littleEndianConverter->toShort(face.z()));
56 | }
57 | }
58 | newContent.append(littleEndianConverter->toShort(model->objects.size())); // ObjectCount
59 | for (int m = 0; m < model->objects.size(); m++) {
60 | newContent.append(littleEndianConverter->toFloat(model->objects[m].position.x())); // x
61 | newContent.append(littleEndianConverter->toFloat(model->objects[m].position.y())); // y
62 | newContent.append(littleEndianConverter->toFloat(model->objects[m].position.z())); // z
63 |
64 | newContent.append(littleEndianConverter->toShort(model->objects[m].rotation.x() * 0x7FFF)); // x rotation
65 | newContent.append(littleEndianConverter->toShort(model->objects[m].rotation.y() * 0x7FFF)); // y rotation
66 | newContent.append(littleEndianConverter->toShort(model->objects[m].rotation.z() * 0x7FFF)); // z rotation
67 | newContent.append(littleEndianConverter->toShort(model->objects[m].rotation.w() * 0x7FFF)); // w rotation
68 |
69 | newContent.append(littleEndianConverter->toFloat(model->objects[m].scale.x())); // x scale
70 | newContent.append(littleEndianConverter->toFloat(model->objects[m].scale.y())); // y scale
71 | newContent.append(littleEndianConverter->toFloat(model->objects[m].scale.z())); // z scale
72 |
73 | newContent.append(
74 | littleEndianConverter->toShort(model->objects[m].animationPositions.size())); // translationFrameCount
75 | for (auto &animationPosition : model->objects[m].animationPositions) {
76 | newContent.append(littleEndianConverter->toShort(animationPosition.timestamp));
77 | newContent.append(littleEndianConverter->toFloat(animationPosition.position.x()));
78 | newContent.append(littleEndianConverter->toFloat(animationPosition.position.y()));
79 | newContent.append(littleEndianConverter->toFloat(animationPosition.position.z()));
80 | }
81 | newContent.append(littleEndianConverter->toShort(model->objects[m].animationRotations.size())); // rotationFrameCount
82 | for (auto &animationRotation : model->objects[m].animationRotations) {
83 | newContent.append(littleEndianConverter->toShort(animationRotation.timestamp));
84 | newContent.append(littleEndianConverter->toShort(animationRotation.rotation.x() * 0x7FFF));
85 | newContent.append(littleEndianConverter->toShort(animationRotation.rotation.y() * 0x7FFF));
86 | newContent.append(littleEndianConverter->toShort(animationRotation.rotation.z() * 0x7FFF));
87 | newContent.append(littleEndianConverter->toShort(animationRotation.rotation.w() * 0x7FFF));
88 | }
89 | newContent.append(littleEndianConverter->toShort(model->objects[m].animationScales.size())); // scaleFrameCount
90 | for (auto &animationScale : model->objects[m].animationScales) {
91 | newContent.append(littleEndianConverter->toShort(animationScale.timestamp));
92 | newContent.append(littleEndianConverter->toFloat(animationScale.scale.x()));
93 | newContent.append(littleEndianConverter->toFloat(animationScale.scale.y()));
94 | newContent.append(littleEndianConverter->toFloat(animationScale.scale.z()));
95 | }
96 | newContent.append(littleEndianConverter->toShort(model->objects[m].groups.size()));
97 | for (int i = 0; i < model->objects[m].groups.size(); i++) {
98 | newContent.append(littleEndianConverter->toInt(model->groups[model->objects[m].groups[i]].texture));
99 | newContent.append((char) 0); // bool
100 | newContent.append(littleEndianConverter->toShort(model->groups[model->objects[m].groups[i]].number)); // group
101 | }
102 | newContent.append(littleEndianConverter->toShort(0));
103 | }
104 | return newContent;
105 | }
106 |
107 | QVector NosModelConverter::readNosVertices(const QByteArray &obj, int &offset, int verticesCount) {
108 | QVector vertices;
109 | for (int i = 0; i < verticesCount; i++) {
110 | float x = littleEndianConverter->fromFloat(obj.mid(offset, 4));
111 | float y = littleEndianConverter->fromFloat(obj.mid(offset + 4, 4));
112 | float z = littleEndianConverter->fromFloat(obj.mid(offset + 8, 4));
113 | vertices.append(QVector3D(x, y, z));
114 | offset += 12;
115 | }
116 | return vertices;
117 | }
118 |
119 | QVector NosModelConverter::readNosNormals(const QByteArray &obj, int &offset, int verticesCount) {
120 | QVector normals;
121 | for (int i = 0; i < verticesCount; i++) {
122 | float x = (float) obj.at(offset) / 0x7F;
123 | float y = (float) obj.at(offset + 1) / 0x7F;
124 | float z = (float) obj.at(offset + 2) / 0x7F;
125 | normals.append(QVector3D(x, y, z));
126 | offset += 3;
127 | }
128 | return normals;
129 | }
130 |
131 | QVector NosModelConverter::readNosUV(const QByteArray &obj, int &offset, int verticesCount, float uvScale) {
132 | QVector uv;
133 | for (int i = 0; i < verticesCount; i++) {
134 | float u = uvScale * littleEndianConverter->fromShort(obj.mid(offset, 2));
135 | float v = 1.0 - (uvScale * littleEndianConverter->fromShort(obj.mid(offset + 2, 2)));
136 | uv.append(QVector2D(u, v));
137 | offset += 4;
138 | }
139 | return uv;
140 | }
141 |
142 | QVector NosModelConverter::readNosFaces(const QByteArray &obj, int &offset) {
143 | QVector groups;
144 | int groupCount = littleEndianConverter->fromShort(obj.mid(offset, 2));
145 | offset += 2;
146 | for (int g = 0; g < groupCount; g++) {
147 | ModelGroup group;
148 | group.number = g;
149 | int points = (uint16_t) littleEndianConverter->fromShort(obj.mid(offset, 2));
150 | offset += 2;
151 | for (int i = 0; i < points / 3; i++) {
152 | int p1 = littleEndianConverter->fromShort(obj.mid(offset, 2));
153 | int p2 = littleEndianConverter->fromShort(obj.mid(offset + 2, 2));
154 | int p3 = littleEndianConverter->fromShort(obj.mid(offset + 4, 2));
155 | group.faces.append(QVector3D(p1, p2, p3));
156 | offset += 6;
157 | }
158 | groups.append(group);
159 | }
160 | return groups;
161 | }
162 |
163 | Model *NosModelConverter::readModelConstruction(const QByteArray &obj, int &offset, Model *model, QVector groups) {
164 | int parts = littleEndianConverter->fromShort(obj.mid(offset, 2));
165 | offset += 2;
166 | for (int p = 0; p < parts; p++) {
167 | ModelObject modelPart;
168 | float x = littleEndianConverter->fromFloat(obj.mid(offset, 4));
169 | float y = littleEndianConverter->fromFloat(obj.mid(offset + 4, 4));
170 | float z = littleEndianConverter->fromFloat(obj.mid(offset + 8, 4));
171 | modelPart.position = QVector3D(x, y, z);
172 | offset += 12;
173 | float rotation_x = (float) littleEndianConverter->fromShort(obj.mid(offset, 2)) / 0x7FFF;
174 | float rotation_y = (float) littleEndianConverter->fromShort(obj.mid(offset + 2, 2)) / 0x7FFF;
175 | float rotation_z = (float) littleEndianConverter->fromShort(obj.mid(offset + 4, 2)) / 0x7FFF;
176 | float rotation_w = (float) littleEndianConverter->fromShort(obj.mid(offset + 6, 2)) / 0x7FFF;
177 | modelPart.rotation = QVector4D(rotation_x, rotation_y, rotation_z, rotation_w);
178 | offset += 8;
179 | float scale_x = littleEndianConverter->fromFloat(obj.mid(offset, 4));
180 | float scale_y = littleEndianConverter->fromFloat(obj.mid(offset + 4, 4));
181 | float scale_z = littleEndianConverter->fromFloat(obj.mid(offset + 8, 4));
182 | modelPart.scale = QVector3D(scale_x, scale_y, scale_z);
183 | offset += 12;
184 | int translationFrameCount = littleEndianConverter->fromShort(obj.mid(offset, 2));
185 | offset += 2;
186 | for (int i = 0; i < translationFrameCount; i++) {
187 | short time = littleEndianConverter->fromShort(obj.mid(offset, 2));
188 | float frame_x = littleEndianConverter->fromFloat(obj.mid(offset + 2, 4));
189 | float frame_y = littleEndianConverter->fromFloat(obj.mid(offset + 6, 4));
190 | float frame_z = littleEndianConverter->fromFloat(obj.mid(offset + 10, 4));
191 | ModelAnimationPositionFrame aPos;
192 | aPos.timestamp = time;
193 | aPos.position = QVector3D(frame_x, frame_y, frame_z);
194 | modelPart.animationPositions.append(aPos);
195 | offset += 14;
196 | }
197 | int rotationFrameCount = littleEndianConverter->fromShort(obj.mid(offset, 2));
198 | offset += 2;
199 | for (int i = 0; i < rotationFrameCount; i++) {
200 | short time = littleEndianConverter->fromShort(obj.mid(offset, 2));
201 | float frame_rotation_x = (float) littleEndianConverter->fromShort(obj.mid(offset + 2, 2)) / 0x7FFF;
202 | float frame_rotation_y = (float) littleEndianConverter->fromShort(obj.mid(offset + 4, 2)) / 0x7FFF;
203 | float frame_rotation_z = (float) littleEndianConverter->fromShort(obj.mid(offset + 6, 2)) / 0x7FFF;
204 | float frame_rotation_w = (float) littleEndianConverter->fromShort(obj.mid(offset + 8, 2)) / 0x7FFF;
205 | ModelAnimationRotationFrame aPos;
206 | aPos.timestamp = time;
207 | aPos.rotation = QVector4D(frame_rotation_x, frame_rotation_y, frame_rotation_z, frame_rotation_w);
208 | modelPart.animationRotations.append(aPos);
209 | offset += 10;
210 | }
211 | int scaleFrameCount = littleEndianConverter->fromShort(obj.mid(offset, 2));
212 | offset += 2;
213 | for (int i = 0; i < scaleFrameCount; i++) {
214 | short time = littleEndianConverter->fromShort(obj.mid(offset, 2));
215 | float frame_scale_x = littleEndianConverter->fromFloat(obj.mid(offset + 2, 4));
216 | float frame_scale_y = littleEndianConverter->fromFloat(obj.mid(offset + 6, 4));
217 | float frame_scale_z = littleEndianConverter->fromFloat(obj.mid(offset + 10, 4));
218 | ModelAnimationScaleFrame aPos;
219 | aPos.timestamp = time;
220 | aPos.scale = QVector3D(frame_scale_x, frame_scale_y, frame_scale_z);
221 | modelPart.animationScales.append(aPos);
222 | offset += 14;
223 | }
224 | int c = littleEndianConverter->fromShort(obj.mid(offset, 2));
225 | offset += 2;
226 | for (int i = 0; i < c; i++) {
227 | int img = littleEndianConverter->fromInt(obj.mid(offset, 4));
228 | offset += 4;
229 | bool smooth = obj.at(offset); // maybe means something else
230 | offset++;
231 | int grp = littleEndianConverter->fromShort(obj.mid(offset, 2)); // equals i?
232 | groups[grp].texture = img;
233 | model->groups.append(groups[grp]);
234 | modelPart.groups.append(grp);
235 | offset += 2;
236 | }
237 | littleEndianConverter->fromShort(obj.mid(offset, 2)); // seems to be always 0?
238 | offset += 2;
239 | model->objects.append(modelPart);
240 | }
241 | return model;
242 | }
243 |
--------------------------------------------------------------------------------
/Source/Converters/NosModelConverter.h:
--------------------------------------------------------------------------------
1 | #ifndef NOSMODELCONVERTER_H
2 | #define NOSMODELCONVERTER_H
3 |
4 | #include "IModelConverter.h"
5 | #include "LittleEndianConverter.h"
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | class NosModelConverter : public IModelConverter {
14 | public:
15 | NosModelConverter();
16 | Model *fromBinary(const QByteArray &obj);
17 | QByteArray toBinary(Model *model);
18 | private:
19 | QVector readNosVertices(const QByteArray &obj, int &offset, int verticesCount);
20 | QVector readNosNormals(const QByteArray &obj, int &offset, int verticesCount);
21 | QVector readNosUV(const QByteArray &obj, int &offset, int verticesCount, float uvScale);
22 | QVector readNosFaces(const QByteArray &obj, int &offset);
23 | Model *readModelConstruction(const QByteArray &obj, int &offset, Model *model, QVector groups);
24 | };
25 |
26 | #endif // NOSMODELCONVERTER_H
27 |
--------------------------------------------------------------------------------
/Source/Converters/ObjConverter.cpp:
--------------------------------------------------------------------------------
1 | #include "ObjConverter.h"
2 | #include
3 | #include
4 | #include
5 |
6 | ObjConverter::ObjConverter() {
7 | this->littleEndianConverter = new LittleEndianConverter();
8 | }
9 |
10 | Model *ObjConverter::fromObj(const QString &obj) {
11 | QVector fileVertices;
12 | QVector fileNormals;
13 | QVector fileUV;
14 | QVector faces;
15 | QVector textures;
16 | int group = -1;
17 | int object = -1;
18 | QStringList lines = obj.split("\n");
19 | for (const QString &line : lines) {
20 | QStringList parts = line.split(" ", QString::SplitBehavior::SkipEmptyParts);
21 | if (!parts.empty()) {
22 | if (parts[0] == "v") {
23 | fileVertices.append(QVector3D(parts[1].toFloat(), parts[2].toFloat(), parts[3].toFloat()));
24 | } else if (parts[0] == "vn") {
25 | fileNormals.append(QVector3D(parts[1].toFloat(), parts[2].toFloat(), parts[3].toFloat()));
26 | } else if (parts[0] == "vt") {
27 | fileUV.append(QVector2D(parts[1].toFloat(), parts[2].toFloat()));
28 | } else if (parts[0] == "f") {
29 | QStringList faceOneInfos = parts[1].split("/");
30 | QStringList faceTwoInfos = parts[2].split("/");
31 | QStringList faceThreeInfos = parts[3].split("/");
32 | Face f;
33 | if (object == -1)
34 | object = 0;
35 | if (group == -1)
36 | group = 0;
37 | f.object = object;
38 | f.group = group;
39 | f.v = QVector3D(faceOneInfos[0].toFloat() - 1, faceTwoInfos[0].toFloat() - 1,
40 | faceThreeInfos[0].toFloat() - 1);
41 | f.vt = QVector3D(faceOneInfos[1].toFloat() - 1, faceTwoInfos[1].toFloat() - 1,
42 | faceThreeInfos[1].toFloat() - 1);
43 | f.vn = QVector3D(faceOneInfos[2].toFloat() - 1, faceTwoInfos[2].toFloat() - 1,
44 | faceThreeInfos[2].toFloat() - 1);
45 | faces.append(f);
46 | } else if (parts[0] == "usemtl") {
47 | QStringList texturemtl = parts[1].split("mtl-", QString::SplitBehavior::SkipEmptyParts);
48 | textures.append(texturemtl.at(0).toInt());
49 | group++;
50 | } else if (parts[0] == "o") {
51 | object++;
52 | }
53 | }
54 | }
55 | QVector vertices;
56 | QVector normals;
57 | QVector uv;
58 | vertices.resize(fileVertices.size());
59 | normals.resize(fileVertices.size());
60 | uv.resize(fileVertices.size());
61 | for (auto &face : faces) {
62 | if ((!normals[face.v.x()].isNull() && normals[face.v.x()] != fileNormals[face.vn.x()]) ||
63 | (!uv[face.v.x()].isNull() && uv[face.v.x()] != fileUV[face.vt.x()])) {
64 | vertices.append(fileVertices[face.v.x()]);
65 | normals.append(fileNormals[face.vn.x()]);
66 | uv.append(fileUV[face.vt.x()]);
67 | face.v.setX(vertices.size() - 1);
68 | face.vn.setX(normals.size() - 1);
69 | face.vt.setX(uv.size() - 1);
70 | } else {
71 | vertices[face.v.x()] = fileVertices[face.v.x()];
72 | normals[face.v.x()] = fileNormals[face.vn.x()];
73 | uv[face.v.x()] = fileUV[face.vt.x()];
74 | }
75 | if ((!normals[face.v.y()].isNull() && normals[face.v.y()] != fileNormals[face.vn.y()]) ||
76 | (!uv[face.v.y()].isNull() && uv[face.v.y()] != fileUV[face.vt.y()])) {
77 | vertices.append(fileVertices[face.v.y()]);
78 | normals.append(fileNormals[face.vn.y()]);
79 | uv.append(fileUV[face.vt.y()]);
80 | face.v.setY(vertices.size() - 1);
81 | face.vn.setY(normals.size() - 1);
82 | face.vt.setY(uv.size() - 1);
83 | } else {
84 | vertices[face.v.y()] = fileVertices[face.v.y()];
85 | normals[face.v.y()] = fileNormals[face.vn.y()];
86 | uv[face.v.y()] = fileUV[face.vt.y()];
87 | }
88 | if ((!normals[face.v.z()].isNull() && normals[face.v.z()] != fileNormals[face.vn.z()]) ||
89 | (!uv[face.v.z()].isNull() && uv[face.v.z()] != fileUV[face.vt.z()])) {
90 | vertices.append(fileVertices[face.v.z()]);
91 | normals.append(fileNormals[face.vn.z()]);
92 | uv.append(fileUV[face.vt.z()]);
93 | face.v.setZ(vertices.size() - 1);
94 | face.vn.setZ(normals.size() - 1);
95 | face.vt.setZ(uv.size() - 1);
96 | } else {
97 | vertices[face.v.z()] = fileVertices[face.v.z()];
98 | normals[face.v.z()] = fileNormals[face.vn.z()];
99 | uv[face.v.z()] = fileUV[face.vt.z()];
100 | }
101 | }
102 | Model *model = new Model();
103 | model->vertices = vertices;
104 | model->normals = normals;
105 | model->uv = uv;
106 | model->uvScale = 0.0005;
107 | for (int i = 0; i <= object; i++) {
108 | ModelObject mo;
109 | mo.position = QVector3D(0, 0, 0);
110 | mo.rotation = QVector4D(0, 0, 0, 0);
111 | mo.scale = QVector3D(1, 1, 1);
112 | model->objects.append(mo);
113 | }
114 | for (int i = 0; i <= group; i++) {
115 | ModelGroup mg;
116 | mg.number = i;
117 | mg.texture = textures[i];
118 | model->groups.append(mg);
119 | }
120 | for (auto &face : faces) {
121 | model->groups[face.group].faces.append(face.v);
122 | if (!model->objects[face.object].groups.contains(face.group))
123 | model->objects[face.object].groups.append(face.group);
124 | }
125 | return model;
126 | }
127 |
128 | QStringList ObjConverter::toObj(Model *model, QString name) {
129 | QStringList list;
130 | list.append(generateObjFile(model, name));
131 | list.append(generateMtlFile(model));
132 | return list;
133 | }
134 |
135 | QString ObjConverter::generateObjFile(Model *model, const QString &name) {
136 | QString obj = "";
137 | obj += "mtllib " + name + ".mtl\n\n";
138 | for (QVector3D v : model->vertices) {
139 | obj += "v " + QString::number(v.x()) + " " + QString::number(v.y()) + " " + QString::number(v.z()) + "\n";
140 | }
141 | for (QVector2D p : model->uv) {
142 | obj += "vt " + QString::number(p.x()) + " " + QString::number(p.y()) + "\n";
143 | }
144 | for (QVector3D vn : model->normals) {
145 | obj += "vn " + QString::number(vn.x()) + " " + QString::number(vn.y()) + " " + QString::number(vn.z()) + "\n";
146 | }
147 | for (int o = 0; o < model->objects.size(); o++) {
148 | obj += "\no " + QString::number(o) + "\n";
149 | for (int g = 0; g < model->objects[o].groups.size(); g++) {
150 | obj += "usemtl mtl-" + QString::number(model->groups[model->objects[o].groups[g]].texture) + "\n";
151 | for (QVector3D f : model->groups[model->objects[o].groups[g]].faces) {
152 | obj += "f " + QString::number(f.x() + 1) + "/" + QString::number(f.x() + 1) + "/" +
153 | QString::number(f.x() + 1) + " " + QString::number(f.y() + 1) + "/" +
154 | QString::number(f.y() + 1) + "/" + QString::number(f.y() + 1) + " " +
155 | QString::number(f.z() + 1) + "/" + QString::number(f.z() + 1) + "/" +
156 | QString::number(f.z() + 1) + "\n";
157 | }
158 | }
159 | }
160 | return obj;
161 | }
162 |
163 | QString ObjConverter::generateMtlFile(Model *model) {
164 | QString mtl = "";
165 | for (auto &group : model->groups) {
166 | mtl += "newmtl mtl-" + QString::number(group.texture) + "\n";
167 | mtl += "Ns 10\nKa 0 0 0\nKd 0 0 0\nKs 0 0 0\n";
168 | mtl += "map_Kd " + QString::number(group.texture) + ".png\n";
169 | }
170 | return mtl;
171 | }
172 |
--------------------------------------------------------------------------------
/Source/Converters/ObjConverter.h:
--------------------------------------------------------------------------------
1 | #ifndef OBJCONVERTER_H
2 | #define OBJCONVERTER_H
3 |
4 | #include "IModelConverter.h"
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | struct Face {
13 | int object;
14 | int group;
15 | QVector3D v;
16 | QVector3D vt;
17 | QVector3D vn;
18 | };
19 |
20 | class ObjConverter : public IModelConverter {
21 | public:
22 | ObjConverter();
23 | Model *fromObj(const QString &obj);
24 | QStringList toObj(Model *model, QString name);
25 | private:
26 | QString generateObjFile(Model *model, const QString &name);
27 | QString generateMtlFile(Model *model);
28 | };
29 |
30 | #endif // OBJCONVERTER_H
31 |
--------------------------------------------------------------------------------
/Source/Decryptors/INosDecryptor.h:
--------------------------------------------------------------------------------
1 | #ifndef INOSDECRYPTOR_H
2 | #define INOSDECRYPTOR_H
3 |
4 | #include
5 |
6 | class INosDecryptor {
7 | public:
8 | virtual QByteArray encrypt(QByteArray &array) = 0;
9 | virtual QByteArray decrypt(QByteArray &array) = 0;
10 | };
11 |
12 | #endif // INOSDECRYPTOR_H
13 |
--------------------------------------------------------------------------------
/Source/Decryptors/NosTextDatFileDecryptor.cpp:
--------------------------------------------------------------------------------
1 | #include "NosTextDatFileDecryptor.h"
2 |
3 | NosTextDatFileDecryptor::NosTextDatFileDecryptor()
4 | : cryptoArray(
5 | {0x00, 0x20, 0x2D, 0x2E, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x0A, 0x00}) {}
6 |
7 | QByteArray NosTextDatFileDecryptor::encrypt(QByteArray &array) {
8 | std::vector mask = getMask(array);
9 | QByteArray string;
10 | int iterator = 0;
11 | int size = mask.size();
12 | unsigned char switchByte = 0;
13 | while (iterator < size) {
14 | int len = iterator;
15 | while ((iterator < mask.size() && mask.at(iterator) == 0x30) || (iterator + 1 == mask.size()) ||
16 | (iterator + 2 == mask.size()) || (iterator + 1 < mask.size() && mask.at(iterator + 1) == 0x30) ||
17 | (iterator + 2 < mask.size() && mask.at(iterator + 2) == 0x30))
18 | ++iterator;
19 | if (iterator > len) {
20 | for (int j = iterator - len; j > 0; j -= 0x7E) {
21 | int checker = j;
22 | if (checker > 0x7E) {
23 | checker = 0x7E;
24 | }
25 | string.push_back(checker);
26 | for (; checker > 0; --checker) {
27 | unsigned char byteToAdd = array[len] ^0x33;
28 | len++;
29 | string.push_back(byteToAdd);
30 | }
31 | }
32 | }
33 | if (iterator >= size)
34 | break;
35 | len = iterator;
36 | int v25 = 1;
37 | while (iterator < mask.size() && mask.at(iterator) == 0x31)
38 | ++iterator;
39 | if (iterator > len) {
40 | for (int j = iterator - len; j > 0; j -= 0x7E) {
41 | int checker = j;
42 | if (j > 0x7E)
43 | checker = 0x7E;
44 | string.push_back(checker | 0x80);
45 | for (; checker > 0; --checker) {
46 | unsigned char byteToAdd = array[len];
47 | len++;
48 | switch (byteToAdd) {
49 | case 32:
50 | switchByte = 1;
51 | break;
52 | case 45:
53 | switchByte = 2;
54 | break;
55 | case 46:
56 | switchByte = 3;
57 | break;
58 | case 0xFF:
59 | switchByte = 14;
60 | break;
61 | default:
62 | switchByte = byteToAdd - 0x2C;
63 | break;
64 | }
65 | if (v25) {
66 | string.push_back(0x10 * switchByte);
67 | v25 = 0;
68 | } else {
69 | string[string.size() - 1] = string[string.size() - 1] | switchByte;
70 | v25 = 1;
71 | }
72 | }
73 | }
74 | }
75 | }
76 | string.push_back(0xFF);
77 | return string;
78 | }
79 |
80 | QByteArray NosTextDatFileDecryptor::decrypt(QByteArray &array) {
81 | QByteArray decryptedFile;
82 | int currIndex = 0;
83 | while (currIndex < array.size()) {
84 | unsigned char currentByte = array.at(currIndex);
85 | currIndex++;
86 | if (currentByte == 0xFF) {
87 | decryptedFile.push_back(0xD);
88 | continue;
89 | }
90 | int validate = currentByte & 0x7F;
91 | if (currentByte & 0x80) {
92 | for (; validate > 0; validate -= 2) {
93 | if (currIndex >= array.size())
94 | break;
95 | currentByte = array.at(currIndex);
96 | currIndex++;
97 | int firstByte = cryptoArray.at((currentByte & 0xF0) >> 4);
98 | decryptedFile.push_back(firstByte);
99 | if (validate <= 1)
100 | break;
101 | int secondByte = cryptoArray.at(currentByte & 0xF);
102 | if (!secondByte)
103 | break;
104 | decryptedFile.push_back(secondByte);
105 | }
106 | } else {
107 | for (; validate > 0; --validate) {
108 | if (currIndex >= array.size())
109 | break;
110 | currentByte = array.at(currIndex);
111 | currIndex++;
112 | decryptedFile.push_back(currentByte ^ 0x33);
113 | }
114 | }
115 | }
116 | return decryptedFile;
117 | }
118 |
119 | NosTextDatFileDecryptor::~NosTextDatFileDecryptor() = default;
120 |
121 | std::vector NosTextDatFileDecryptor::getMask(QByteArray &array) {
122 | std::vector mask(array.size(), 0x30);
123 | for (std::size_t i = 0; i < mask.size(); i++) {
124 | unsigned char ch = array.at(i);
125 | if (!ch)
126 | break;
127 | if (ch -= 0x20) {
128 | ch -= 0xD;
129 | if (ch >= 2) {
130 | ch -= 0x3;
131 | bool test1 = ch < 0xA;
132 | ch -= 0xA;
133 | if (!test1 && ch != 0xC5)
134 | continue;
135 | }
136 | }
137 | mask[i] = 0x31;
138 | }
139 | return mask;
140 | }
141 |
--------------------------------------------------------------------------------
/Source/Decryptors/NosTextDatFileDecryptor.h:
--------------------------------------------------------------------------------
1 | #ifndef NOSTEXTFILEDECRYPTOR_H
2 | #define NOSTEXTFILEDECRYPTOR_H
3 |
4 | #include "INosDecryptor.h"
5 | #include
6 | #include
7 |
8 | class NosTextDatFileDecryptor : public INosDecryptor {
9 | public:
10 | NosTextDatFileDecryptor();
11 | ~NosTextDatFileDecryptor();
12 | QByteArray encrypt(QByteArray &array);
13 | QByteArray decrypt(QByteArray &array);
14 | private:
15 | std::array cryptoArray;
16 | std::vector getMask(QByteArray &array);
17 | };
18 |
19 | #endif // NOSTEXTFILEDECRYPTOR_H
20 |
--------------------------------------------------------------------------------
/Source/Decryptors/NosTextOthersFileDecryptor.cpp:
--------------------------------------------------------------------------------
1 | #include "NosTextOthersFileDecryptor.h"
2 |
3 | NosTextOthersFileDecryptor::NosTextOthersFileDecryptor(LittleEndianConverter *littleEndianConverter) {
4 | this->littleEndianConverter = littleEndianConverter;
5 | }
6 |
7 | QByteArray NosTextOthersFileDecryptor::encrypt(QByteArray &array) {
8 | QByteArray result = littleEndianConverter->toInt(array.size());
9 | for (auto &byte : array) {
10 | result.push_back(byte ^ 0x1);
11 | }
12 | return result;
13 | }
14 |
15 | QByteArray NosTextOthersFileDecryptor::decrypt(QByteArray &array) {
16 | QByteArray result;
17 | int lines = littleEndianConverter->fromInt(array.mid(0, 4));
18 | int pos = 4;
19 | for (int i = 0; i < lines; i++) {
20 | int strLen = littleEndianConverter->fromInt(array.mid(pos, 4));
21 | pos += 4;
22 | QByteArray str = array.mid(pos, strLen);
23 | pos += strLen;
24 | for (auto &byte : str)
25 | result.push_back(byte ^ 0x1);
26 | result.push_back('\n');
27 | }
28 | return result;
29 | }
30 |
--------------------------------------------------------------------------------
/Source/Decryptors/NosTextOthersFileDecryptor.h:
--------------------------------------------------------------------------------
1 | #ifndef NOSTEXTOTHERSFILEDECRYPTOR_H
2 | #define NOSTEXTOTHERSFILEDECRYPTOR_H
3 |
4 | #include "INosDecryptor.h"
5 | #include
6 | #include
7 | #include "../Converters/LittleEndianConverter.h"
8 |
9 | class NosTextOthersFileDecryptor : public INosDecryptor {
10 | public:
11 | NosTextOthersFileDecryptor(LittleEndianConverter *littleEndianConverter);
12 | QByteArray encrypt(QByteArray &array);
13 | QByteArray decrypt(QByteArray &array);
14 | private:
15 | LittleEndianConverter *littleEndianConverter;
16 | };
17 |
18 | #endif // NOSTEXTOTHERSFILEDECRYPTOR_H
19 |
--------------------------------------------------------------------------------
/Source/Decryptors/NosZlibDecryptor.cpp:
--------------------------------------------------------------------------------
1 | #include "NosZlibDecryptor.h"
2 |
3 | NosZlibDecryptor::NosZlibDecryptor() = default;
4 |
5 | QByteArray NosZlibDecryptor::encrypt(QByteArray &array, int level) {
6 | return qCompress(array, level);
7 | }
8 |
9 | QByteArray NosZlibDecryptor::decrypt(QByteArray &array) {
10 | return qUncompress(array);
11 | }
12 |
--------------------------------------------------------------------------------
/Source/Decryptors/NosZlibDecryptor.h:
--------------------------------------------------------------------------------
1 | #ifndef NOSZLIBDECRYPTOR_H
2 | #define NOSZLIBDECRYPTOR_H
3 |
4 | #include "INosDecryptor.h"
5 | #include
6 |
7 | class NosZlibDecryptor {
8 | public:
9 | NosZlibDecryptor();
10 | QByteArray encrypt(QByteArray &array, int level = 1);
11 | QByteArray decrypt(QByteArray &array);
12 | };
13 |
14 | #endif // NOSZLIBDECRYPTOR_H
15 |
--------------------------------------------------------------------------------
/Source/MainWindow.h:
--------------------------------------------------------------------------------
1 | #ifndef MAINWINDOW_H
2 | #define MAINWINDOW_H
3 |
4 | #include "Openers/NosCCInfOpener.h"
5 | #include "Openers/NosTextOpener.h"
6 | #include "Openers/NosZlibOpener.h"
7 | #include "Ui/Previews/SingleImagePreview.h"
8 | #include "Ui/Previews/SingleTextFilePreview.h"
9 | #include "Ui/Settings.h"
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | namespace Ui {
18 | class MainWindow;
19 | }
20 | class MainWindow : public QMainWindow {
21 | Q_OBJECT
22 | public:
23 | explicit MainWindow(QWidget *parent = nullptr);
24 | ~MainWindow() override;
25 | const QString VERSION = "v0.7.1";
26 | public slots:
27 | void on_actionOpen_triggered();
28 | void on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *prev);
29 | void replaceInfo(FileInfo *info);
30 | void on_actionReplace_triggered();
31 | void on_actionExport_triggered();
32 | void on_actionExport_to_raw_triggered();
33 | void on_actionReplace_with_raw_triggered();
34 | void on_actionSave_triggered();
35 | void on_actionSave_as_triggered();
36 | void on_actionExport_with_config_triggered();
37 | void on_actionImport_from_config_triggered();
38 | void on_actionApplyPatch_triggered();
39 | void on_addButton_clicked();
40 | void on_deleteButton_clicked();
41 | void filterItems();
42 | void onCustomMenuShow(const QPoint &point);
43 | void clearMenu();
44 | void on_actionClose_selected_triggered();
45 | void on_actionClose_all_triggered();
46 | void on_actionSettings_triggered();
47 | void on_actionExit_triggered();
48 | void on_actionAbout_triggered();
49 | void on_actionHelp_triggered();
50 | void on_actionRename_triggered();
51 | void itemEdited(QTreeWidgetItem *item, int column);
52 | private:
53 | Ui::MainWindow *ui;
54 | Settings *settings;
55 | QMenu *contextMenu = nullptr;
56 | NosTextOpener textOpener;
57 | NosZlibOpener zlibOpener;
58 | NosCCInfOpener ccinfOpener;
59 | JsonConfigOpener jsonOpener;
60 | void openFile(const QString &path);
61 | void handleOpenResults(OnexTreeItem *item, const QString &path);
62 | INosFileOpener *getOpener(const QByteArray &header);
63 | template
64 | void fileOperationOnSelectedItems(TreeFunction treeFunction, const QString &defaultPath, QString operationName, bool saveDialog,
65 | QString filter = QString());
66 | QString getSelectedDirectory(const QString &suggestion);
67 | QString getOpenFile(const QString &suggestion, const QString &filter);
68 | QStringList getOpenFiles(const QString &suggestion, const QString &filter);
69 | QString getSaveFile(const QString &suggestion, const QString &filter);
70 | void dropEvent(QDropEvent *e) override;
71 | void dragEnterEvent(QDragEnterEvent *e) override;
72 | OnexTreeItem *getTreeRoot();
73 | QString neatPath(QString path);
74 | };
75 |
76 | #endif // MAINWINDOW_H
77 |
--------------------------------------------------------------------------------
/Source/NosEnumTypes.h:
--------------------------------------------------------------------------------
1 | #ifndef NOSENUMTYPES_H
2 | #define NOSENUMTYPES_H
3 | #define PCHPKG 1
4 | #define NStuData 2
5 | #define NStkData 3
6 | #define NStcData 5
7 | #define NStgData 6
8 | #define NStpData 7
9 | #define NStsData 9
10 | #define NStgeData 10
11 | #define NStpeData 11
12 | #define NStpuData 12
13 | #define NSpcData 13
14 | #define NSppData 14
15 | #define NSpmData 15
16 | #define NSmcData 16
17 | #define NSmpData 17
18 | #define NSedData 20
19 | #define NSemData 21
20 | #define NSesData 22
21 | #define NSeffData 23
22 | #define NSipData 24
23 | #define NSgrdData 26
24 | #define NS4BbData 101 // temporary
25 | #define NSipData2006 103
26 |
27 | /*
28 | #define NSpnData
29 | #define NSmnData
30 |
31 | #define PCHPKG_DATA
32 |
33 | #define NScliData
34 | #define NSetcData
35 | #define NSlangData
36 | #define NSgtdData
37 | */
38 |
39 | #endif // NOSENUMTYPES_H
40 |
--------------------------------------------------------------------------------
/Source/Openers/INosFileOpener.cpp:
--------------------------------------------------------------------------------
1 | #include "INosFileOpener.h"
2 |
3 | QString INosFileOpener::neatFileName(QString fileName) {
4 | QStringList list = fileName.split("/");
5 | if (list.empty())
6 | return fileName;
7 | return list.back();
8 | }
9 |
10 | LittleEndianConverter *INosFileOpener::getLittleEndianConverter() {
11 | return &littleEndianConverter;
12 | }
--------------------------------------------------------------------------------
/Source/Openers/INosFileOpener.h:
--------------------------------------------------------------------------------
1 | #ifndef INOSFILEOPENER_H
2 | #define INOSFILEOPENER_H
3 |
4 | #include
5 | #include
6 | #include "../Converters/LittleEndianConverter.h"
7 |
8 | class OnexTreeItem;
9 |
10 | class INosFileOpener {
11 | public:
12 | virtual OnexTreeItem *decrypt(QFile &file) = 0;
13 | virtual QByteArray encrypt(OnexTreeItem *item) = 0;
14 | LittleEndianConverter *getLittleEndianConverter();
15 | virtual OnexTreeItem *getEmptyItem(const QByteArray &header) = 0;
16 | protected:
17 | LittleEndianConverter littleEndianConverter;
18 | QString neatFileName(QString fileName);
19 | };
20 |
21 | #endif // INOSFILEOPENER_H
22 |
--------------------------------------------------------------------------------
/Source/Openers/JsonConfigOpener.cpp:
--------------------------------------------------------------------------------
1 | #include "JsonConfigOpener.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | OnexTreeItem *JsonConfigOpener::load(QFile &file, const QString &directory) {
13 | QJsonObject jo = QJsonDocument::fromJson(file.readAll()).object();
14 | int headerNumber = getNTHeaderNumber(QByteArray::fromHex(jo["Header"].toString().toLocal8Bit()));
15 | if (headerNumber == -1) {
16 | QMessageBox::critical(nullptr, "Not supported!", "This feature is not available for this archive");
17 | return nullptr;
18 | }
19 |
20 | OnexTreeItem *root = generateRoot(headerNumber, jo);
21 |
22 | QJsonArray contentArray = jo["content"].toArray();
23 | for (auto &&i : contentArray) {
24 | OnexTreeItem *item = generateItem(headerNumber, i.toObject(), directory);
25 | root->addChild(item);
26 | }
27 |
28 | return root;
29 | }
30 |
31 | void JsonConfigOpener::load(OnexTreeItem *root, QFile &file, const QString &directory) {
32 | QJsonObject jo = QJsonDocument::fromJson(file.readAll()).object();
33 | int headerNumber = getNTHeaderNumber(QByteArray::fromHex(jo["Header"].toString().toLocal8Bit()));
34 | if (headerNumber == -1) {
35 | QMessageBox::critical(nullptr, "Not supported!", "This feature is not available for this archive");
36 | return;
37 | }
38 |
39 | if (root->getContent() != QByteArray::fromHex(jo["Header"].toString().toLocal8Bit())) {
40 | QMessageBox::critical(nullptr, "Error!", "The patch is not valid for this .NOS!");
41 | return;
42 | }
43 |
44 | QJsonArray contentArray = jo["content"].toArray();
45 | int added = 0;
46 | int replaced = 0;
47 | for (auto &&i : contentArray) {
48 | OnexTreeItem *item = generateItem(headerNumber, i.toObject(), directory);
49 | if (findChildByName(root, item->getName()) != -1) {
50 | delete root->takeChild(findChildByName(root, item->getName()));
51 | replaced++;
52 | } else
53 | added++;
54 | root->addChild(item);
55 | }
56 |
57 | QMessageBox::information(nullptr, "End of operation", "Replaced " + QString::number(replaced) + " and added " + QString::number(added) + " file(s)");
58 | }
59 |
60 | OnexTreeItem *JsonConfigOpener::generateRoot(int headerNumber, const QJsonObject &jo) {
61 | switch (headerNumber) {
62 | case NSipData:
63 | return new OnexNSipData(jo["Archive"].toString(), QByteArray::fromHex(jo["Header"].toString().toLocal8Bit()), &zlibOpener);
64 | case NS4BbData:
65 | return new OnexNS4BbData(jo["Archive"].toString(), QByteArray::fromHex(jo["Header"].toString().toLocal8Bit()), &zlibOpener);
66 | case NStcData:
67 | return new OnexNStcData(jo["Archive"].toString(), QByteArray::fromHex(jo["Header"].toString().toLocal8Bit()), &zlibOpener);
68 | case NStpData:
69 | case NStpeData:
70 | case NStpuData:
71 | return new OnexNStpData(jo["Archive"].toString(), QByteArray::fromHex(jo["Header"].toString().toLocal8Bit()), &zlibOpener);
72 | case NSmpData:
73 | case NSppData:
74 | return new OnexNSmpData(jo["Archive"].toString(), QByteArray::fromHex(jo["Header"].toString().toLocal8Bit()), &zlibOpener);
75 | case NStgData:
76 | case NStgeData:
77 | return new OnexNStgData(jo["Archive"].toString(), QByteArray::fromHex(jo["Header"].toString().toLocal8Bit()), &zlibOpener);
78 | case NSmcData:
79 | case NSpcData:
80 | return new OnexNSmcData(jo["Archive"].toString(), QByteArray::fromHex(jo["Header"].toString().toLocal8Bit()), &zlibOpener);
81 | case 199:
82 | return new OnexTreeText(jo["Archive"].toString(), &textOpener, jo["Last Edit"].toString());
83 | default:
84 | return nullptr;
85 | }
86 | }
87 |
88 | OnexTreeItem *JsonConfigOpener::generateItem(int headerNumber, const QJsonObject &jo, const QString &directory) {
89 | switch (headerNumber) {
90 | case NSipData:
91 | return new OnexNSipData(jo, &zlibOpener, directory);
92 | case NS4BbData:
93 | return new OnexNS4BbData(jo, &zlibOpener, directory);
94 | case NStcData:
95 | return new OnexNStcData(jo, &zlibOpener, directory);
96 | case NStpData:
97 | case NStpeData:
98 | case NStpuData:
99 | return new OnexNStpData(jo, &zlibOpener, directory);
100 | case NSmpData:
101 | case NSppData:
102 | return new OnexNSmpData(jo, &zlibOpener, directory);
103 | case NStgData:
104 | case NStgeData:
105 | return new OnexNStgData(jo, &zlibOpener, directory);
106 | case NSmcData:
107 | case NSpcData:
108 | return new OnexNSmcData(jo, &zlibOpener, directory);
109 | case 199:
110 | return new OnexTreeText(jo, &textOpener, directory);
111 | default:
112 | return nullptr;
113 | }
114 | }
115 |
116 | int JsonConfigOpener::getNTHeaderNumber(const QString &header) {
117 | if (header.mid(0, 7) == "NT Data")
118 | return header.mid(8, 2).toInt();
119 | else if (header.mid(0, 10) == "32GBS V1.0")
120 | return NS4BbData;
121 | else if (header.mid(0, 10) == "ITEMS V1.0")
122 | return NSipData2006;
123 | else if (header.isEmpty())
124 | return 199;
125 | else
126 | return -1;
127 | }
128 |
129 | int JsonConfigOpener::findChildByName(OnexTreeItem *item, const QString &searched) {
130 | for (int c = 0; c < item->childCount(); c++) {
131 | if (item->child(c)->text(0) == searched)
132 | return c;
133 | }
134 | return -1;
135 | }
--------------------------------------------------------------------------------
/Source/Openers/JsonConfigOpener.h:
--------------------------------------------------------------------------------
1 | #ifndef JSONCONFIGOPENER_H
2 | #define JSONCONFIGOPENER_H
3 |
4 |
5 | #include
6 | #include
7 | #include "NosZlibOpener.h"
8 | #include "NosTextOpener.h"
9 |
10 | class JsonConfigOpener {
11 | public:
12 | OnexTreeItem *load(QFile &file, const QString &directory);
13 | void load(OnexTreeItem *root, QFile &file, const QString &directory);
14 | private:
15 | NosTextOpener textOpener;
16 | NosZlibOpener zlibOpener;
17 | OnexTreeItem *generateRoot(int headerNumber, const QJsonObject &jo);
18 | OnexTreeItem *generateItem(int headerNumber, const QJsonObject &jo, const QString &directory);
19 | int getNTHeaderNumber(const QString &header);
20 | int findChildByName(OnexTreeItem *item, const QString &searched);
21 | };
22 |
23 |
24 | #endif //JSONCONFIGOPENER_H
25 |
--------------------------------------------------------------------------------
/Source/Openers/NosCCInfOpener.cpp:
--------------------------------------------------------------------------------
1 | #include "NosCCInfOpener.h"
2 | #include "../Ui/TreeItems/OnexNSmnData.h"
3 | #include
4 | #include
5 | #include
6 |
7 | NosCCInfOpener::NosCCInfOpener() = default;
8 |
9 | OnexTreeItem *NosCCInfOpener::decrypt(QFile &file) {
10 | file.seek(0);
11 |
12 | QByteArray header = file.read(0xC);
13 | int creationDate = littleEndianConverter.readInt(file);
14 |
15 | OnexNSmnData *item = new OnexNSmnData(neatFileName(file.fileName()), creationDate, this, header);
16 | bool playerInfos = item->getName().contains("NSpn");
17 |
18 | int fileSize = littleEndianConverter.readInt(file);
19 | littleEndianConverter.readInt(file); // second time the same bytes, idk why
20 | file.read(1); // 0x00
21 | int fileAmount = littleEndianConverter.readInt(file);
22 |
23 | for (int i = 0; i < fileAmount; i++) {
24 |
25 | QJsonObject jo;
26 | jo["direction"] = file.read(1).at(0);
27 | jo["animation"] = file.read(1).at(0);
28 |
29 | if (playerInfos) {
30 | uint8_t classSex = (file.read(1).at(0));
31 | jo["sex"] = (uint8_t) ((classSex & 0x80) != 0);
32 | jo["class"] = classSex & 0x0F;
33 | jo["morph"] = file.read(1).at(0);
34 | } else {
35 | jo["monster"] = littleEndianConverter.readShort(file);
36 | }
37 |
38 | jo["base"] = littleEndianConverter.readInt(file);
39 | jo["nspm"] = littleEndianConverter.readInt(file);
40 | jo["kit"] = littleEndianConverter.readInt(file);
41 |
42 | QJsonObject parts;
43 | for (int j = 0; j < 7; j++) {
44 | QString textureName = QString::number(j);
45 | if (playerInfos) {
46 | textureName = getTextureName(j);
47 | }
48 |
49 | int count = file.read(1).at(0);
50 | QJsonArray sprites;
51 | for (int c = 0; c < count; c++) {
52 | QJsonObject sprite;
53 | sprite["index"] = littleEndianConverter.readShort(file);
54 | sprite["id"] = littleEndianConverter.readInt(file);
55 | sprites.append(sprite);
56 | }
57 | parts.insert(textureName, sprites);
58 | }
59 | jo.insert("parts", parts);
60 |
61 | QByteArray array = QJsonDocument(jo).toJson();
62 | item->addChild(new OnexNSmnData(QString::number(i), creationDate, this, array));
63 | }
64 | return item;
65 | }
66 |
67 | QByteArray NosCCInfOpener::encrypt(OnexTreeItem *item) {
68 | bool playerInfos = item->getName().contains("NSpn");
69 | OnexNSmnData *nsmnItem = static_cast(item);
70 |
71 | QByteArray content;
72 | QByteArray head;
73 |
74 | content.append(littleEndianConverter.toInt(item->childCount()));
75 | for (int i = 0; i != item->childCount(); ++i) {
76 | OnexNSmnData *ct = static_cast(item->child(i));
77 | QJsonObject jo = QJsonDocument::fromJson(ct->getContent()).object();
78 | content.append((uint8_t) jo["direction"].toInt());
79 | content.append((uint8_t) jo["animation"].toInt());
80 | if (playerInfos) {
81 | int sex = jo["sex"].toInt() == 0 ? 0 : 0x80;
82 | int cClass = jo["class"].toInt();
83 | uint8_t classSex = sex + cClass;
84 | content.append((uint8_t) classSex);
85 | content.append((uint8_t) jo["morph"].toInt());
86 | } else {
87 | content.append(littleEndianConverter.toShort(jo["monster"].toInt()));
88 | }
89 |
90 | content.append(littleEndianConverter.toInt(jo["base"].toInt()));
91 | content.append(littleEndianConverter.toInt(jo["nspm"].toInt()));
92 | content.append(littleEndianConverter.toInt(jo["kit"].toInt()));
93 |
94 | QJsonObject parts = jo["parts"].toObject();
95 |
96 | for (int j = 0; j < 7; j++) {
97 | QString textureName = QString::number(j);
98 | if (playerInfos) {
99 | textureName = getTextureName(j);
100 | }
101 |
102 | QJsonArray sprites = parts[textureName].toArray();
103 | content.append((uint8_t) sprites.size());
104 | for (QJsonValueRef sprite : sprites) {
105 | content.append(littleEndianConverter.toShort(sprite.toObject()["index"].toInt()));
106 | content.append(littleEndianConverter.toInt(sprite.toObject()["id"].toInt()));
107 | }
108 | }
109 | }
110 |
111 | head.append(nsmnItem->getContent());
112 | head.append(littleEndianConverter.toInt(nsmnItem->getCreationDate()));
113 | head.append(littleEndianConverter.toInt(content.size())); // fileSize
114 | head.append(littleEndianConverter.toInt(content.size())); // fileSize
115 | head.append((char) 0x0);
116 |
117 | content = head.append(content);
118 |
119 | return content;
120 | }
121 |
122 | QString NosCCInfOpener::getTextureName(int index) {
123 | switch (index) {
124 | case 0:
125 | return "armor";
126 | case 1:
127 | return "hair";
128 | case 2:
129 | return "hat";
130 | case 3:
131 | return "mask";
132 | case 4:
133 | return "secondary-Weapon";
134 | case 5:
135 | return "first-Weapon";
136 | case 6:
137 | return "weapon-part";
138 | default:
139 | return QString::number(index);
140 | }
141 | }
142 |
143 | OnexTreeItem *NosCCInfOpener::getEmptyItem(const QByteArray &header) {
144 | return nullptr;
145 | }
146 |
--------------------------------------------------------------------------------
/Source/Openers/NosCCInfOpener.h:
--------------------------------------------------------------------------------
1 | #ifndef NOSCCINFOPENER_H
2 | #define NOSCCINFOPENER_H
3 |
4 | #include "INosFileOpener.h"
5 | #include "../Ui/OnexTreeItem.h"
6 | #include
7 | #include
8 |
9 | class NosCCInfOpener : public QObject, public INosFileOpener {
10 | Q_OBJECT
11 | public:
12 | NosCCInfOpener();
13 | OnexTreeItem *decrypt(QFile &file);
14 | QByteArray encrypt(OnexTreeItem *item);
15 | OnexTreeItem *getEmptyItem(const QByteArray &header) override;
16 | private:
17 | QString getTextureName(int index);
18 | };
19 |
20 | #endif // NOSCCINFOPENER_H
21 |
--------------------------------------------------------------------------------
/Source/Openers/NosTextOpener.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "NosTextOpener.h"
3 |
4 | NosTextOpener::NosTextOpener() {
5 | datDecryptor = new NosTextDatFileDecryptor();
6 | lstDecryptor = new NosTextOthersFileDecryptor(&littleEndianConverter);
7 | }
8 |
9 | OnexTreeItem *NosTextOpener::decrypt(QFile &file) {
10 | file.seek(0);
11 | auto *item = new OnexTreeText(neatFileName(file.fileName()), this);
12 | int fileAmount = littleEndianConverter.readInt(file);
13 | for (int i = 0; i < fileAmount; ++i) {
14 | int fileNumber = littleEndianConverter.readInt(file);
15 | int stringNameSize = littleEndianConverter.readInt(file);
16 | QString stringName = file.read(stringNameSize);
17 | int isDat = littleEndianConverter.readInt(file);
18 | int fileSize = littleEndianConverter.readInt(file);
19 | QByteArray fileContent = file.read(fileSize);
20 | QByteArray decryptedArray;
21 | if (isDat || stringName.endsWith(".dat"))
22 | decryptedArray = datDecryptor->decrypt(fileContent);
23 | else //.lst
24 | decryptedArray = lstDecryptor->decrypt(fileContent);
25 | item->addChild(new OnexTreeText(stringName, this, fileNumber, isDat, decryptedArray));
26 | }
27 |
28 | if (file.bytesAvailable() == 12) {
29 | file.seek(file.pos() + 8);
30 | if (file.read(4) == QByteArrayLiteral("\xEE\x3E\x32\x01")) {
31 | file.seek(file.pos() - 12);
32 | QString time = readOLETIME(file.read(8));
33 | item->setTime(time, true);
34 | }
35 | }
36 |
37 | return item;
38 | }
39 |
40 | QByteArray NosTextOpener::encrypt(OnexTreeItem *item) {
41 | if (item->hasParent())
42 | return QByteArray();
43 | QByteArray result;
44 | result.push_back(littleEndianConverter.toInt(item->childCount()));
45 | for (int i = 0; i < item->childCount(); ++i) {
46 | auto *currentItem = dynamic_cast(item->child(i));
47 | result.push_back(littleEndianConverter.toInt(currentItem->getFileNumber()));
48 | result.push_back(littleEndianConverter.toInt(currentItem->getName().size()));
49 | result.push_back(currentItem->getName().toLocal8Bit());
50 | result.push_back(littleEndianConverter.toInt(currentItem->getIsCompressed()));
51 | QList splited;
52 | QByteArray encrypted;
53 | if (currentItem->getIsCompressed() || currentItem->getName().endsWith(".dat")) {
54 | splited = currentItem->getContent().split(0xD);
55 | if (splited[splited.size() - 1].isEmpty())
56 | splited = splited.mid(0, splited.size() - 1);
57 | } else {
58 | splited = currentItem->getContent().split(0xA);
59 | if (splited[splited.size() - 1].isEmpty())
60 | splited = splited.mid(0, splited.size() - 1);
61 | encrypted.push_back(littleEndianConverter.toInt(splited.size()));
62 | }
63 | for (auto &line : splited) {
64 | if (currentItem->getIsCompressed() || currentItem->getName().endsWith(".dat"))
65 | encrypted.push_back(datDecryptor->encrypt(line));
66 | else
67 | encrypted.push_back(lstDecryptor->encrypt(line));
68 | }
69 | result.push_back(littleEndianConverter.toInt(encrypted.size()));
70 | result.push_back(encrypted);
71 | }
72 |
73 | QByteArray time = generateOLETIME();
74 | result.push_back(time);
75 | result.push_back(QByteArrayLiteral("\xEE\x3E\x32\x01"));
76 |
77 | auto *text = static_cast(item);
78 | text->setTime(readOLETIME(time), true);
79 |
80 | return result;
81 | }
82 |
83 | OnexTreeItem *NosTextOpener::getEmptyItem(const QByteArray &header) {
84 | return new OnexTreeText("", this);
85 | }
86 |
87 | QByteArray NosTextOpener::generateOLETIME() {
88 | QDateTime dt = QDateTime::currentDateTime();
89 | dt.setTimeSpec(Qt::TimeSpec::UTC);
90 | double variant = ((dt.toSecsSinceEpoch() + 2208988800) / 86400.0) + 2.00001;
91 | QByteArray writeArray;
92 | writeArray.resize(8);
93 | qToLittleEndian(variant, reinterpret_cast(writeArray.data()));
94 | return writeArray;
95 | }
96 |
97 | QString NosTextOpener::readOLETIME(QByteArray array) {
98 | double variant = qFromLittleEndian(reinterpret_cast(array.data()));
99 | unsigned long unixTime = -2208988800 + ((variant - 2.00001) * 86400);
100 | QDateTime dt;
101 | dt.setTimeSpec(Qt::TimeSpec::UTC);
102 | dt.setSecsSinceEpoch(unixTime);
103 | return dt.toString("dd/MM/yyyy hh:mm:ss");
104 | }
--------------------------------------------------------------------------------
/Source/Openers/NosTextOpener.h:
--------------------------------------------------------------------------------
1 | #ifndef NOSTEXTOPENER_H
2 | #define NOSTEXTOPENER_H
3 |
4 | #include "../Decryptors/NosTextDatFileDecryptor.h"
5 | #include "../Decryptors/NosTextOthersFileDecryptor.h"
6 | #include "../Ui/TreeItems/OnexTreeText.h"
7 | #include "INosFileOpener.h"
8 | #include
9 | #include
10 |
11 | class NosTextOpener : public QObject, public INosFileOpener {
12 | Q_OBJECT
13 | public:
14 | NosTextOpener();
15 | OnexTreeItem *decrypt(QFile &file);
16 | QByteArray encrypt(OnexTreeItem *item);
17 | OnexTreeItem *getEmptyItem(const QByteArray &header) override;
18 | private:
19 | NosTextDatFileDecryptor *datDecryptor;
20 | NosTextOthersFileDecryptor *lstDecryptor;
21 | QByteArray generateOLETIME();
22 | QString readOLETIME(QByteArray array);
23 | };
24 |
25 | #endif // NOSTEXTOPENER_H
26 |
--------------------------------------------------------------------------------
/Source/Openers/NosZlibOpener.cpp:
--------------------------------------------------------------------------------
1 | #include "NosZlibOpener.h"
2 | #include "../Ui/TreeItems/OnexNS4BbData.h"
3 | #include "../Ui/TreeItems/OnexNSipData.h"
4 | #include "../Ui/TreeItems/OnexNSmpData.h"
5 | #include "../Ui/TreeItems/OnexNStcData.h"
6 | #include "../Ui/TreeItems/OnexNStpData.h"
7 | #include "../Ui/TreeItems/OnexNStgData.h"
8 | #include "../Ui/TreeItems/OnexNSmcData.h"
9 |
10 | NosZlibOpener::NosZlibOpener() = default;
11 |
12 | OnexTreeItem *NosZlibOpener::decrypt(QFile &file) {
13 | file.seek(0);
14 | QByteArray header = file.read(NOS_HEADER_SIZE);
15 | int fileAmount = littleEndianConverter.readInt(file);
16 | OnexTreeItem *item = createItemFromHeader(header, neatFileName(file.fileName()), header);
17 | QByteArray separatorByte = file.read(1);
18 | for (int i = 0; i != fileAmount; ++i) {
19 | int id = littleEndianConverter.readInt(file);
20 | int offset = littleEndianConverter.readInt(file);
21 | int previousOffset = file.pos();
22 | file.seek(offset);
23 | int creationDate = littleEndianConverter.readInt(file);
24 | int dataSize = littleEndianConverter.readInt(file);
25 | int compressedDataSize = littleEndianConverter.readInt(file);
26 | bool isCompressed = file.read(1).at(0);
27 | QByteArray data = file.read(compressedDataSize);
28 | if (isCompressed) {
29 | QByteArray bigEndian = toBigEndian(dataSize);
30 | data.push_front(bigEndian);
31 | data = decryptor.decrypt(data);
32 | }
33 |
34 | QString name = QString::number(id);
35 | if (containsName(item, name)) {
36 | int a = 2;
37 | while (containsName(item, name + "_" + QString::number(a)))
38 | a++;
39 | name = name + "_" + QString::number(a);
40 | }
41 |
42 | item->addChild(createItemFromHeader(header, name, data, id, creationDate, isCompressed));
43 | file.seek(previousOffset);
44 | }
45 | return item;
46 | }
47 |
48 | QByteArray NosZlibOpener::encrypt(OnexTreeItem *item) {
49 | if (item->hasParent())
50 | return QByteArray();
51 | QByteArray fileHeader = item->getContent();
52 | fileHeader.push_back(littleEndianConverter.toInt(item->childCount()));
53 | fileHeader.push_back((char) 0x0); // separator byte
54 |
55 | QByteArray offsetArray;
56 | int sizeOfOffsetArray = item->childCount() * 8;
57 | QByteArray contentArray;
58 | int firstFileOffset = fileHeader.size() + sizeOfOffsetArray;
59 | for (int i = 0; i != item->childCount(); ++i) {
60 | int currentFileOffset = firstFileOffset + contentArray.size();
61 | auto *currentItem = dynamic_cast(item->child(i));
62 | contentArray.push_back(littleEndianConverter.toInt(currentItem->getCreationDate()));
63 | QByteArray content = currentItem->getContent();
64 | contentArray.push_back(littleEndianConverter.toInt(content.size()));
65 | if (currentItem->isCompressed()) {
66 | int headerNumber = getNTHeaderNumber(fileHeader);
67 | if (headerNumber == NS4BbData || headerNumber == NStcData || headerNumber == NStuData)
68 | content = decryptor.encrypt(content, 9);
69 | else
70 | content = decryptor.encrypt(content);
71 | }
72 | int compressedContentSize = content.size();
73 | if (currentItem->isCompressed())
74 | compressedContentSize -= 4; // qCompress add the size at the front of array
75 |
76 | contentArray.push_back(littleEndianConverter.toInt(compressedContentSize));
77 | contentArray.push_back(currentItem->isCompressed());
78 | if (currentItem->isCompressed())
79 | contentArray.push_back(content.mid(4));
80 | else
81 | contentArray.push_back(content);
82 | offsetArray.push_back(littleEndianConverter.toInt(currentItem->getId()));
83 | offsetArray.push_back(littleEndianConverter.toInt(currentFileOffset));
84 | }
85 | QByteArray result;
86 | result.push_back(fileHeader);
87 | result.push_back(offsetArray);
88 | result.push_back(contentArray);
89 | return result;
90 | }
91 |
92 | OnexTreeItem *NosZlibOpener::getEmptyItem(const QByteArray &header) {
93 | return createItemFromHeader(header, "", QByteArray());
94 | }
95 |
96 | OnexTreeItem *NosZlibOpener::createItemFromHeader(QByteArray header, const QString &name, const QByteArray &array, int fileId,
97 | int creationDate, bool compressed) {
98 | int headerNumber = getNTHeaderNumber(header);
99 | switch (headerNumber) {
100 | case NSipData:
101 | return new OnexNSipData(name, array, this, fileId, creationDate, compressed);
102 | case NS4BbData:
103 | return new OnexNS4BbData(name, array, this, fileId, creationDate, compressed);
104 | case NStcData:
105 | return new OnexNStcData(name, array, this, fileId, creationDate, compressed);
106 | case NStpData:
107 | case NStpeData:
108 | case NStpuData:
109 | return new OnexNStpData(name, array, this, fileId, creationDate, compressed);
110 | case NSmpData:
111 | case NSppData:
112 | return new OnexNSmpData(name, array, this, fileId, creationDate, compressed);
113 | case NStgData:
114 | case NStgeData:
115 | return new OnexNStgData(name, array, this, fileId, creationDate, compressed);
116 | case NSmcData:
117 | case NSpcData:
118 | return static_cast(new OnexNSmcData(name, array, this, fileId, creationDate, compressed));
119 | default:
120 | return new OnexTreeZlibItem(name, this, array, fileId, creationDate, compressed);
121 | }
122 | }
123 |
124 | int NosZlibOpener::getNTHeaderNumber(QByteArray &array) {
125 | if (array.mid(0, 7) == "NT Data")
126 | return array.mid(8, 2).toInt();
127 | else if (array.mid(0, 10) == "32GBS V1.0")
128 | return NS4BbData;
129 | else if (array.mid(0, 10) == "ITEMS V1.0")
130 | return NSipData2006;
131 | else
132 | return 199;
133 | }
134 |
135 | QByteArray NosZlibOpener::toBigEndian(qint32 value) {
136 | QByteArray result;
137 | result.resize(4);
138 | qToBigEndian(value, reinterpret_cast(result.data()));
139 | return result;
140 | }
141 |
142 | bool NosZlibOpener::containsName(OnexTreeItem *item, QString searched) {
143 | for (int c = 0; c < item->childCount(); c++) {
144 | if (item->child(c)->text(0) == searched)
145 | return true;
146 | }
147 | return false;
148 | }
149 |
--------------------------------------------------------------------------------
/Source/Openers/NosZlibOpener.h:
--------------------------------------------------------------------------------
1 | #ifndef NOSZLIBOPENER_H
2 | #define NOSZLIBOPENER_H
3 |
4 | #include "../Decryptors/NosZlibDecryptor.h"
5 | #include "../Converters/ImageConverter.h"
6 | #include "../NosEnumTypes.h"
7 | #include "INosFileOpener.h"
8 | #include
9 |
10 | class NosZlibOpener : public INosFileOpener {
11 | public:
12 | NosZlibOpener();
13 | OnexTreeItem *decrypt(QFile &file) override;
14 | QByteArray encrypt(OnexTreeItem *item) override;
15 | OnexTreeItem *getEmptyItem(const QByteArray &header) override;
16 | private:
17 | static const int NOS_HEADER_SIZE = 0x10;
18 | NosZlibDecryptor decryptor;
19 | OnexTreeItem *createItemFromHeader(QByteArray header, const QString &name, const QByteArray &array, int fileId = -1,
20 | int creationDate = 0, bool compressed = false);
21 | int getNTHeaderNumber(QByteArray &array);
22 | QByteArray toBigEndian(qint32 value);
23 | bool containsName(OnexTreeItem *item, QString searched);
24 | };
25 |
26 | #endif // NOSZLIBOPENER_H
27 |
--------------------------------------------------------------------------------
/Source/Ui/FileInfo.cpp:
--------------------------------------------------------------------------------
1 | #include "FileInfo.h"
2 |
3 | FileInfo::FileInfo(QWidget *parent) : QWidget(parent) {
4 | grid = new QGridLayout();
5 | setLayout(grid);
6 | setMinimumWidth(225);
7 | setMaximumWidth(225);
8 | rows = 0;
9 | }
10 |
11 | QLineEdit *FileInfo::addStringLineEdit(const QString &title, const QString &value) {
12 | auto *input = addLineEdit(title, value);
13 | input->setProperty("type", "QString");
14 | return input;
15 | }
16 |
17 | QLineEdit *FileInfo::addIntLineEdit(const QString &title, int value) {
18 | auto *input = addLineEdit(title, QString::number(value));
19 | input->setProperty("type", "Integer");
20 | return input;
21 | }
22 |
23 | QLineEdit *FileInfo::addFloatLineEdit(const QString &title, float value) {
24 | auto *input = addLineEdit(title, QString::number(value));
25 | input->setProperty("type", "Float");
26 | return input;
27 | }
28 |
29 | QCheckBox *FileInfo::addCheckBox(const QString &title, bool value) {
30 | auto *cb = new QCheckBox(title);
31 | cb->setChecked(value);
32 | grid->addWidget(cb, rows, 0, 1, 2);
33 | rows++;
34 | return cb;
35 | }
36 |
37 | QComboBox *FileInfo::addSelection(const QString &title, const QStringList &items, int value) {
38 | auto *label = new QLabel(title);
39 | label->setMinimumWidth(50);
40 | grid->addWidget(label, rows, 0);
41 | auto *box = new QComboBox();
42 | for (const QString &s : items) {
43 | box->addItem(s);
44 | }
45 | box->setCurrentIndex(value);
46 | grid->addWidget(box, rows, 1);
47 | rows++;
48 | return box;
49 | }
50 |
51 | QPushButton *FileInfo::addReplaceButton(const QString &title) {
52 | auto *button = new QPushButton(title);
53 | connect(button, &QPushButton::clicked, [=]() { emit replaceFile(); });
54 | grid->addWidget(button, rows, 0, 1, 2);
55 | rows++;
56 | return button;
57 | }
58 |
59 | QPushButton *FileInfo::addReplaceRawButton(const QString &title) {
60 | auto *button = new QPushButton(title);
61 | connect(button, &QPushButton::clicked, [=]() { emit replaceRawFile(); });
62 | grid->addWidget(button, rows, 0, 1, 2);
63 | rows++;
64 | return button;
65 | }
66 |
67 | void FileInfo::update(const QString &title, const QString &value) {
68 | for (int i = 0; i < grid->rowCount(); i++) {
69 | if (grid->itemAtPosition(i, 0) == nullptr || dynamic_cast(grid->itemAtPosition(i, 0)->widget()) == nullptr)
70 | continue;
71 | if (dynamic_cast(grid->itemAtPosition(i, 0)->widget())->text() == title) {
72 | if (grid->itemAtPosition(i, 1) != nullptr) {
73 | auto *in = dynamic_cast(grid->itemAtPosition(i, 1)->widget());
74 | if (in != nullptr) {
75 | in->setText(value);
76 | break;
77 | } else {
78 | auto *box = dynamic_cast(grid->itemAtPosition(i, 1)->widget());
79 | if (box != nullptr) {
80 | box->setCurrentIndex(value.toInt());
81 | break;
82 | }
83 | }
84 | }
85 | }
86 | }
87 | }
88 |
89 | void FileInfo::update(const QString &title, int value) {
90 | update(title, QString::number(value));
91 | }
92 |
93 | void FileInfo::update(const QString &title, float value) {
94 | update(title, QString::number(value));
95 | }
96 |
97 | void FileInfo::update(const QString &title, bool value) {
98 | for (int i = 0; i < grid->rowCount(); i++) {
99 | if (grid->itemAtPosition(i, 0) == nullptr)
100 | continue;
101 | auto *current = dynamic_cast(grid->itemAtPosition(i, 0)->widget());
102 | if (current != nullptr && current->text() == title) {
103 | auto *cb = dynamic_cast(grid->itemAtPosition(i, 0)->widget());
104 | cb->setChecked(value);
105 | break;
106 | }
107 | }
108 | }
109 |
110 | void FileInfo::replace(FileInfo *info) {
111 | emit updateInfo(info);
112 | }
113 |
114 | QJsonObject FileInfo::toJson() {
115 | QJsonObject jo;
116 |
117 | for (int i = 0; i < grid->rowCount(); i++) {
118 | if (grid->itemAtPosition(i, 0) == nullptr)
119 | continue;
120 | if (dynamic_cast(grid->itemAtPosition(i, 0)->widget()) != nullptr) {
121 | if (grid->itemAtPosition(i, 1) == nullptr)
122 | continue;
123 | QString key = dynamic_cast(grid->itemAtPosition(i, 0)->widget())->text();
124 | if (key == "Width" || key == "Height")
125 | continue;
126 | if (dynamic_cast(grid->itemAtPosition(i, 1)->widget()) != nullptr) {
127 | auto *lineEdit = dynamic_cast(grid->itemAtPosition(i, 1)->widget());
128 | if (key == "Header")
129 | jo[key] = QString(lineEdit->text().toLocal8Bit().toHex());
130 | else if (lineEdit->property("type") == "QString")
131 | jo[key] = lineEdit->text();
132 | else if (lineEdit->property("type") == "Integer")
133 | jo[key] = lineEdit->text().toInt();
134 | else if (lineEdit->property("type") == "Float")
135 | jo[key] = lineEdit->text().toFloat();
136 | } else if (dynamic_cast(grid->itemAtPosition(i, 1)->widget()) != nullptr) {
137 | auto *box = dynamic_cast(grid->itemAtPosition(i, 1)->widget());
138 | int value = box->currentIndex();
139 | jo[key] = value;
140 | }
141 | } else if (dynamic_cast(grid->itemAtPosition(i, 0)->widget()) != nullptr) {
142 | auto *box = dynamic_cast(grid->itemAtPosition(i, 0)->widget());
143 | QString key = box->text();
144 | bool value = box->isChecked();
145 | jo[key] = value;
146 | }
147 | }
148 | return jo;
149 | }
150 |
151 | QLineEdit *FileInfo::addLineEdit(const QString &title, const QString &value) {
152 | auto *label = new QLabel(title);
153 | label->setMinimumWidth(50);
154 | grid->addWidget(label, rows, 0);
155 | auto *input = new QLineEdit(value);
156 | input->setMaximumWidth(120);
157 | grid->addWidget(input, rows, 1);
158 | rows++;
159 | return input;
160 | }
161 |
--------------------------------------------------------------------------------
/Source/Ui/FileInfo.h:
--------------------------------------------------------------------------------
1 | #ifndef FILEINFO_H
2 | #define FILEINFO_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | class FileInfo : public QWidget {
15 | Q_OBJECT
16 | public:
17 | QGridLayout *grid;
18 | explicit FileInfo(QWidget *parent = nullptr);
19 | QLineEdit *addStringLineEdit(const QString &title, const QString &value);
20 | QLineEdit *addIntLineEdit(const QString &title, int value);
21 | QLineEdit *addFloatLineEdit(const QString &title, float value);
22 | QCheckBox *addCheckBox(const QString &title, bool checked);
23 | QComboBox *addSelection(const QString &title, const QStringList &items, int value);
24 | QPushButton *addReplaceButton(const QString &title);
25 | QPushButton *addReplaceRawButton(const QString &title);
26 | QJsonObject toJson();
27 | public slots:
28 | void update(const QString &title, const QString &value);
29 | void update(const QString &title, int value);
30 | void update(const QString &title, float value);
31 | void update(const QString &title, bool value);
32 | void replace(FileInfo *newInfo);
33 | signals:
34 | void updateInfo(FileInfo *info);
35 | void replaceFile();
36 | void replaceRawFile();
37 | private:
38 | int rows;
39 | QLineEdit *addLineEdit(const QString &title, const QString &value);
40 | };
41 |
42 | #endif // FILEINFO_H
43 |
--------------------------------------------------------------------------------
/Source/Ui/OnexTreeItem.cpp:
--------------------------------------------------------------------------------
1 | #include "OnexTreeItem.h"
2 |
3 | OnexTreeItem::OnexTreeItem(const QString &name, INosFileOpener *opener, QByteArray content)
4 | : name(name), opener(opener), content(content) {
5 | this->setText(0, name);
6 | this->setFlags(this->flags() | Qt::ItemIsEditable);
7 | }
8 |
9 | OnexTreeItem::~OnexTreeItem() = default;
10 |
11 | QWidget *OnexTreeItem::getPreview() {
12 | return nullptr;
13 | }
14 |
15 | FileInfo *OnexTreeItem::getInfos() {
16 | FileInfo *infos = generateInfos();
17 | if (infos != nullptr)
18 | connect(this, SIGNAL(replaceInfo(FileInfo * )), infos, SLOT(replace(FileInfo * )));
19 | return infos;
20 | }
21 |
22 | bool OnexTreeItem::hasParent() {
23 | return QTreeWidgetItem::parent() != nullptr;
24 | }
25 |
26 | QString OnexTreeItem::getName() {
27 | return name;
28 | }
29 |
30 | QByteArray OnexTreeItem::getContent() {
31 | return content;
32 | }
33 |
34 | int OnexTreeItem::getContentSize() {
35 | return content.size();
36 | }
37 |
38 | bool OnexTreeItem::isEmpty() {
39 | return getContent().isEmpty();
40 | }
41 |
42 | QString OnexTreeItem::getExportExtension() {
43 | return "";
44 | }
45 |
46 | QString OnexTreeItem::getExportExtensionFilter() {
47 | if (getExportExtension() == ".png")
48 | return "PNG Image (*.png)";
49 | else if (getExportExtension() == ".txt")
50 | return "Text File (*.txt)";
51 | else if (getExportExtension() == ".lst")
52 | return "List File (*.lst)";
53 | else if (getExportExtension() == ".dat")
54 | return "DAT File (*.dat)";
55 | else if (getExportExtension() == ".obj")
56 | return "OBJ File (*.obj)";
57 | else if (getExportExtension() == ".json")
58 | return "JSON File (*.json)";
59 | return "All files (*.*)";
60 | }
61 |
62 | void OnexTreeItem::setName(QString name) {
63 | this->name = name;
64 | setText(0, name);
65 | }
66 |
67 | void OnexTreeItem::setContent(QByteArray content) {
68 | this->content = content;
69 | }
70 |
71 | int OnexTreeItem::onExport(QString directory) {
72 | if (childCount() > 0) {
73 | int count = 0;
74 | for (int i = 0; i != this->childCount(); ++i) {
75 | auto *item = dynamic_cast(this->child(i));
76 | count += item->onExport(directory);
77 | }
78 | return count;
79 | } else {
80 | QString path = getCorrectPath(directory);
81 | return saveAsFile(path);
82 | }
83 | }
84 |
85 | int OnexTreeItem::onExportRaw(QString directory) {
86 | QString path = getCorrectPath(directory, ".bin");
87 | QFile file(path);
88 | file.open(QIODevice::WriteOnly);
89 | if (file.write(this->getContent()) == -1) {
90 | return 0;
91 | }
92 | file.close();
93 | return 1;
94 | }
95 |
96 | int OnexTreeItem::onExportAsOriginal(QString path) {
97 | if (path.isEmpty())
98 | return 0;
99 | if (!path.endsWith(".NOS"))
100 | path += ".NOS";
101 | QFile file(path);
102 | if (!file.open(QIODevice::WriteOnly)) {
103 | QMessageBox::critical(nullptr, "Woops", "Couldn't open this file for writing");
104 | return 0;
105 | }
106 | if (file.write(opener->encrypt(this)) == -1) {
107 | QMessageBox::critical(nullptr, "Woops", "Couldn't write to this file");
108 | return 0;
109 | }
110 | file.close();
111 | QMessageBox::information(nullptr, "Yeah", "File exported into .NOS");
112 | return 1;
113 | }
114 |
115 | int OnexTreeItem::onReplace(QString directory) {
116 | int count = 0;
117 | if (this->childCount() > 0) {
118 | for (int i = 0; i < this->childCount(); i++) {
119 | auto *item = dynamic_cast(this->child(i));
120 | count += item->onReplace(directory);
121 | }
122 | if (hasParent())
123 | afterReplace(QByteArray());
124 | }
125 | if (count > 0)
126 | return count;
127 | QString path = getCorrectPath(directory);
128 | QFile file(path);
129 | if (file.open(QIODevice::ReadOnly))
130 | return afterReplace(file.readAll());
131 | else {
132 | QMessageBox::critical(nullptr, "Woops", "Couldn't open " + path);
133 | return 0;
134 | }
135 | }
136 |
137 | int OnexTreeItem::onReplaceRaw(QString directory) {
138 | QString path = getCorrectPath(directory, ".bin");
139 | QFile file(path);
140 | if (file.open(QIODevice::ReadOnly)) {
141 | content = file.readAll();
142 | file.close();
143 | return 1;
144 | }
145 | return 0;
146 | }
147 |
148 | int OnexTreeItem::afterReplace(QByteArray content) {
149 | emit replaceInfo(generateInfos());
150 | return 1;
151 | }
152 |
153 | QJsonObject OnexTreeItem::generateConfig() {
154 | QJsonObject jo = generateInfos()->toJson();
155 |
156 | if (childCount() > 0) {
157 | QJsonArray contentArray;
158 | for (int i = 0; i < childCount(); i++) {
159 | auto item = static_cast(child(i));
160 | contentArray.append(item->generateConfig());
161 | }
162 | jo["content"] = contentArray;
163 | } else {
164 | QString path = this->getName();
165 | if (!path.endsWith(getExportExtension()))
166 | path += getExportExtension();
167 | jo["path"] = path;
168 | }
169 | return jo;
170 | }
171 |
172 | QString OnexTreeItem::getCorrectPath(QString input, QString extension) {
173 | if (extension.isEmpty())
174 | extension = getExportExtension();
175 |
176 | if (!input.endsWith(extension)) {
177 | input = input + this->getName();
178 | if (!input.endsWith(extension))
179 | input += extension;
180 | }
181 | return input;
182 | }
183 |
184 | FileInfo *OnexTreeItem::generateInfos() {
185 | FileInfo *infos = new FileInfo();
186 |
187 | if (hasParent() && isEmpty()) {
188 | infos->addReplaceButton("Load from File");
189 | infos->addReplaceRawButton("Load from raw File");
190 | } else if (hasParent()) {
191 | infos->addReplaceButton("Replace");
192 | infos->addReplaceRawButton("Replace with raw");
193 | }
194 | if (!hasParent()) {
195 | infos->addStringLineEdit("Archive", text(0))->setEnabled(false);
196 | }
197 |
198 | connect(this, SIGNAL(changeSignal(QString, QString)), infos, SLOT(update(QString, QString)));
199 | connect(this, SIGNAL(changeSignal(QString, int)), infos, SLOT(update(QString, int)));
200 | connect(this, SIGNAL(changeSignal(QString, float)), infos, SLOT(update(QString, float)));
201 | connect(this, SIGNAL(changeSignal(QString, bool)), infos, SLOT(update(QString, bool)));
202 | return infos;
203 | }
204 |
205 | int OnexTreeItem::saveAsFile(const QString &path, QByteArray content) {
206 | QFile file(path);
207 | if (file.open(QIODevice::WriteOnly)) {
208 | file.write(content.isEmpty() ? getContent() : content);
209 | file.close();
210 | return 1;
211 | }
212 | return 0;
213 | }
214 |
215 | bool OnexTreeItem::operator<(const QTreeWidgetItem &other) const {
216 | int column = treeWidget()->sortColumn();
217 | static QRegExp regExp("^(\\d*)_(\\d*)x(\\d*)$");
218 |
219 | bool t1IsInt;
220 | bool t2IsInt;
221 | int t1 = text(column).toInt(&t1IsInt);
222 | int t2 = other.text(column).toInt(&t2IsInt);
223 | if (t1IsInt && t2IsInt || regExp.exactMatch(text(column)))
224 | return t1 < t2;
225 | else
226 | return false;
227 | // return text(column).toLower() < other.text(column).toLower();
228 | }
229 |
--------------------------------------------------------------------------------
/Source/Ui/OnexTreeItem.h:
--------------------------------------------------------------------------------
1 | #ifndef ONEXTREEITEM_H
2 | #define ONEXTREEITEM_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include "../Openers/INosFileOpener.h"
11 | #include "FileInfo.h"
12 | #include "../Converters/LittleEndianConverter.h"
13 |
14 | class OnexTreeItem : public QObject, public QTreeWidgetItem {
15 | Q_OBJECT
16 | public:
17 | OnexTreeItem(const QString &name, INosFileOpener *opener, QByteArray content = QByteArray());
18 | ~OnexTreeItem() override;
19 | virtual QWidget *getPreview();
20 | virtual FileInfo *getInfos();
21 | bool hasParent();
22 | virtual QString getName();
23 | virtual QByteArray getContent();
24 | int getContentSize();
25 | virtual bool isEmpty();
26 | virtual QString getExportExtension();
27 | QString getExportExtensionFilter();
28 | public slots:
29 | virtual void setName(QString name);
30 | virtual int onExportAsOriginal(QString path);
31 | virtual void setContent(QByteArray content);
32 | virtual int onExport(QString directory);
33 | virtual int onExportRaw(QString directory);
34 | virtual int onReplace(QString directory);
35 | virtual int onReplaceRaw(QString directory);
36 | virtual int afterReplace(QByteArray content);
37 | virtual QJsonObject generateConfig();
38 | signals:
39 | void changeSignal(QString title, QString value);
40 | void changeSignal(QString title, int value);
41 | void changeSignal(QString title, float value);
42 | void changeSignal(QString title, bool value);
43 | void replaceInfo(FileInfo *info);
44 | protected:
45 | QByteArray content;
46 | QString name;
47 | INosFileOpener *opener;
48 | virtual FileInfo *generateInfos();
49 | virtual int saveAsFile(const QString &path, QByteArray content = QByteArray());
50 | QString getCorrectPath(QString input, QString extension = QString());
51 | bool operator<(const QTreeWidgetItem &other) const override;
52 | };
53 |
54 | #endif // ONEXTREEITEM_H
55 |
--------------------------------------------------------------------------------
/Source/Ui/Previews/MultiImagePreview.cpp:
--------------------------------------------------------------------------------
1 | #include "MultiImagePreview.h"
2 | #include "ui_MultiImagePreview.h"
3 | #include
4 |
5 | MultiImagePreview::MultiImagePreview(QList *images, QWidget *parent)
6 | : QWidget(parent), ui(new Ui::MultiImagePreview) {
7 | ui->setupUi(this);
8 | this->images = images;
9 | QImage image = generateImage();
10 | ui->imgContent->setPixmap(QPixmap::fromImage(image));
11 | }
12 |
13 | MultiImagePreview::~MultiImagePreview() {
14 | delete ui;
15 | }
16 |
17 | void MultiImagePreview::onReplaced(QList *newImages) {
18 | images = newImages;
19 | ui->imgContent->setPixmap(QPixmap::fromImage(generateImage()));
20 | }
21 |
22 | QImage MultiImagePreview::generateImage() {
23 | QPair res = this->getResolution();
24 | QImage image = QImage(res.first, res.second, QImage::Format_ARGB32);
25 | image.fill(Qt::transparent);
26 | int offset = 0;
27 | for (auto img : *this->images) {
28 | QPainter painter(&image);
29 | painter.drawImage(QRect(0, offset, img.width(), img.height()), img,
30 | QRect(0, 0, img.width(), img.height()));
31 | offset += img.height() + 20;
32 | }
33 | return image;
34 | }
35 |
36 | QPair MultiImagePreview::getResolution() {
37 | QPair r;
38 | for (auto img : *this->images) {
39 | if (img.width() > r.first)
40 | r.first = img.width();
41 | r.second += img.height() + 20;
42 | }
43 | return r;
44 | }
--------------------------------------------------------------------------------
/Source/Ui/Previews/MultiImagePreview.h:
--------------------------------------------------------------------------------
1 | #ifndef MULTIIMAGEPREVIEW_H
2 | #define MULTIIMAGEPREVIEW_H
3 |
4 | #include "../../Converters/ImageConverter.h"
5 | #include "../../NosEnumTypes.h"
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | namespace Ui {
15 | class MultiImagePreview;
16 | }
17 | class MultiImagePreview : public QWidget {
18 | Q_OBJECT
19 | public:
20 | explicit MultiImagePreview(QList *image, QWidget *parent = nullptr);
21 | ~MultiImagePreview() override;
22 | private slots:
23 | void onReplaced(QList *newImages);
24 | private:
25 | Ui::MultiImagePreview *ui;
26 | QList *images;
27 | QImage generateImage();
28 | QPair getResolution();
29 | };
30 |
31 | #endif // MULTIIMAGEPREVIEW_H
--------------------------------------------------------------------------------
/Source/Ui/Previews/MultiImagePreview.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MultiImagePreview
4 |
5 |
6 |
7 | 0
8 | 0
9 | 400
10 | 300
11 |
12 |
13 |
14 | Image
15 |
16 |
17 | -
18 |
19 |
20 | TextLabel
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Source/Ui/Previews/SingleImagePreview.cpp:
--------------------------------------------------------------------------------
1 | #include "SingleImagePreview.h"
2 | #include "ui_SingleImagePreview.h"
3 |
4 | SingleImagePreview::SingleImagePreview(const QImage &image, QWidget *parent)
5 | : QWidget(parent), ui(new Ui::SingleImagePreview) {
6 | ui->setupUi(this);
7 | ui->imgContent->setPixmap(QPixmap::fromImage(image));
8 | }
9 |
10 | SingleImagePreview::~SingleImagePreview() {
11 | delete ui;
12 | }
13 |
14 | void SingleImagePreview::onReplaced(const QImage &newImage) {
15 | ui->imgContent->setPixmap(QPixmap::fromImage(newImage));
16 | }
17 |
--------------------------------------------------------------------------------
/Source/Ui/Previews/SingleImagePreview.h:
--------------------------------------------------------------------------------
1 | #ifndef SINGLEIMAGEPREVIEW_H
2 | #define SINGLEIMAGEPREVIEW_H
3 |
4 | #include "../../Converters/ImageConverter.h"
5 | #include "../../NosEnumTypes.h"
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | namespace Ui {
15 | class SingleImagePreview;
16 | }
17 | class SingleImagePreview : public QWidget {
18 | Q_OBJECT
19 | public:
20 | explicit SingleImagePreview(const QImage &image, QWidget *parent = nullptr);
21 | ~SingleImagePreview() override;
22 | private slots:
23 | void onReplaced(const QImage &newImage);
24 | private:
25 | Ui::SingleImagePreview *ui;
26 | };
27 |
28 | #endif // SINGLEIMAGEPREVIEW_H
29 |
--------------------------------------------------------------------------------
/Source/Ui/Previews/SingleImagePreview.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | SingleImagePreview
4 |
5 |
6 |
7 | 0
8 | 0
9 | 400
10 | 300
11 |
12 |
13 |
14 | Image
15 |
16 |
17 | -
18 |
19 |
20 | TextLabel
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Source/Ui/Previews/SingleModelPreview.cpp:
--------------------------------------------------------------------------------
1 | #include "SingleModelPreview.h"
2 | #include
3 | #include
4 | #include
5 |
6 | SingleModelPreview::SingleModelPreview(Model *model, QWidget *parent) : QOpenGLWidget(parent) {
7 | this->model = model;
8 | camera.X = 0;
9 | camera.Y = 0;
10 | camera.Z = -20;
11 | camera.angleX = 20;
12 | camera.angleY = 0;
13 | mouse.X = 0;
14 | mouse.Y = 0;
15 | setMinimumHeight(600);
16 | setMaximumHeight(600);
17 | setMinimumWidth(650);
18 | setMaximumWidth(650);
19 | }
20 |
21 | SingleModelPreview::~SingleModelPreview() = default;
22 |
23 | void SingleModelPreview::onReplaced(Model *model) {
24 | this->model = model;
25 | repaint();
26 | }
27 |
28 | void SingleModelPreview::initializeGL() {
29 | glClearColor(1, 1, 1, 1);
30 | glMatrixMode(GL_PROJECTION);
31 | glLoadIdentity();
32 | glViewport(0, 0, width(), height());
33 | gluPerspective(65, (float) width() / (float) height(), 0.1, 300);
34 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
35 | // glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
36 |
37 | glEnable(GL_LIGHTING);
38 | }
39 |
40 | void SingleModelPreview::paintGL() {
41 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
42 | glMatrixMode(GL_MODELVIEW);
43 | glLoadIdentity();
44 | glPushMatrix();
45 | // Camera
46 | glTranslatef(camera.X, camera.Y, camera.Z);
47 | glRotatef(camera.angleY, 0, 1, 0);
48 | glRotatef(camera.angleX, 1, 0, 0);
49 |
50 | // glEnable(GL_LIGHT0);
51 | // glEnable(GL_COLOR_MATERIAL);
52 | // glColor3f(0.5f, 0.5f, 0.5f);
53 |
54 | for (auto &object : model->objects) {
55 | glPushMatrix();
56 | QQuaternion qr(object.rotation);
57 | float xAxis, yAxis, zAxis, angle;
58 | qr.getAxisAndAngle(&xAxis, &yAxis, &zAxis, &angle);
59 | glTranslatef(object.position.x(), object.position.y(), object.position.z());
60 | glRotatef(angle, xAxis, yAxis, zAxis);
61 | glScalef(object.scale.x(), object.scale.y(), object.scale.z());
62 | for (int group : object.groups) {
63 | for (auto &face : model->groups[group].faces) {
64 | glBegin(GL_TRIANGLES);
65 | glPushMatrix();
66 | int p1 = face.x();
67 | int p2 = face.y();
68 | int p3 = face.z();
69 | glNormal3f(model->normals[p1].x(), model->normals[p1].y(), model->normals[p1].z());
70 | glVertex3f(model->vertices[p1].x(), model->vertices[p1].y(), model->vertices[p1].z());
71 | glNormal3f(model->normals[p2].x(), model->normals[p2].y(), model->normals[p2].z());
72 | glVertex3f(model->vertices[p2].x(), model->vertices[p2].y(), model->vertices[p2].z());
73 | glNormal3f(model->normals[p3].x(), model->normals[p3].y(), model->normals[p3].z());
74 | glVertex3f(model->vertices[p3].x(), model->vertices[p3].y(), model->vertices[p3].z());
75 | glPopMatrix();
76 | glEnd();
77 | }
78 | }
79 | glPopMatrix();
80 | }
81 | glPopMatrix();
82 | }
83 |
84 | void SingleModelPreview::mousePressEvent(QMouseEvent *event) {
85 | mouse.X = event->x();
86 | mouse.Y = event->y();
87 | }
88 |
89 | void SingleModelPreview::mouseMoveEvent(QMouseEvent *event) {
90 | int x = event->x();
91 | int y = event->y();
92 | Qt::MouseButtons buttons = event->buttons();
93 | if (buttons & Qt::LeftButton) {
94 | if (y < mouse.Y) {
95 | camera.angleX -= 1;
96 | } else if (y > mouse.Y) {
97 | camera.angleX += 1;
98 | }
99 | if (x < mouse.X) {
100 | camera.angleY -= 2;
101 | } else if (x > mouse.X) {
102 | camera.angleY += 2;
103 | }
104 | } else if (buttons & Qt::RightButton) {
105 | camera.X += (x - mouse.X) / 5;
106 | camera.Y -= (y - mouse.Y) / 5;
107 | }
108 | mouse.X = x;
109 | mouse.Y = y;
110 | repaint();
111 | }
112 |
113 | void SingleModelPreview::wheelEvent(QWheelEvent *event) {
114 | camera.Z += event->delta() / 120;
115 | repaint();
116 | }
117 |
118 |
--------------------------------------------------------------------------------
/Source/Ui/Previews/SingleModelPreview.h:
--------------------------------------------------------------------------------
1 | #ifndef SINGLEMODELPREVIEW_H
2 | #define SINGLEMODELPREVIEW_H
3 |
4 | #include "../../Converters/IModelConverter.h"
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | struct Mouse {
12 | int X;
13 | int Y;
14 | };
15 | struct Camera {
16 | float X;
17 | float Y;
18 | float Z;
19 | float angleX;
20 | float angleY;
21 | };
22 |
23 | class SingleModelPreview : public QOpenGLWidget {
24 | Q_OBJECT
25 | public:
26 | explicit SingleModelPreview(Model *model, QWidget *parent = nullptr);
27 | ~SingleModelPreview() override;
28 | private slots:
29 | void onReplaced(Model *model);
30 | private:
31 | Model *model;
32 | Camera camera{};
33 | Mouse mouse{};
34 | void initializeGL() override;
35 | void paintGL() override;
36 | void mousePressEvent(QMouseEvent *event) override;
37 | void mouseMoveEvent(QMouseEvent *event) override;
38 | void wheelEvent(QWheelEvent *event) override;
39 | };
40 |
41 | #endif // SINGLEMODELPREVIEW_H
42 |
--------------------------------------------------------------------------------
/Source/Ui/Previews/SingleTextFilePreview.cpp:
--------------------------------------------------------------------------------
1 | #include "SingleTextFilePreview.h"
2 | #include "ui_SingleTextFilePreview.h"
3 |
4 | SingleTextFilePreview::SingleTextFilePreview(QByteArray &item, const QString &encoding, QWidget *parent)
5 | : QWidget(parent), ui(new Ui::SingleTextFilePreview) {
6 | ui->setupUi(this);
7 | codec = QTextCodec::codecForName(encoding.toLocal8Bit());
8 | QString encodeContent = codec->toUnicode(item);
9 | ui->label_encoding->setText(encoding);
10 | ui->plainTextEdit->appendPlainText(encodeContent);
11 | ui->plainTextEdit->moveCursor(QTextCursor::Start);
12 | }
13 |
14 | SingleTextFilePreview::~SingleTextFilePreview() {
15 | delete ui;
16 | }
17 |
18 | void SingleTextFilePreview::onReplaced(const QByteArray &text) {
19 | ui->plainTextEdit->clear();
20 | QString encodeContent = codec->toUnicode(text);
21 | ui->plainTextEdit->appendPlainText(encodeContent);
22 | ui->plainTextEdit->moveCursor(QTextCursor::Start);
23 | }
--------------------------------------------------------------------------------
/Source/Ui/Previews/SingleTextFilePreview.h:
--------------------------------------------------------------------------------
1 | #ifndef SINGLETEXTFILEPREVIEW_H
2 | #define SINGLETEXTFILEPREVIEW_H
3 |
4 | #include "../TreeItems/OnexTreeText.h"
5 | #include
6 | #include
7 | #include
8 |
9 | namespace Ui {
10 | class SingleTextFilePreview;
11 | }
12 | class SingleTextFilePreview : public QWidget {
13 | Q_OBJECT
14 | public:
15 | explicit SingleTextFilePreview(QByteArray &item, const QString &encoding = "Windows-1250", QWidget *parent = nullptr);
16 | ~SingleTextFilePreview() override;
17 | private slots:
18 | void onReplaced(const QByteArray &text);
19 | private:
20 | Ui::SingleTextFilePreview *ui;
21 | QTextCodec *codec;
22 | };
23 |
24 | #endif // SINGLETEXTFILEPREVIEW_H
25 |
--------------------------------------------------------------------------------
/Source/Ui/Previews/SingleTextFilePreview.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | SingleTextFilePreview
4 |
5 |
6 |
7 | 0
8 | 0
9 | 531
10 | 421
11 |
12 |
13 |
14 |
15 | 0
16 | 0
17 |
18 |
19 |
20 | Text
21 |
22 |
23 | -
24 |
25 |
26 | Encoding:
27 |
28 |
29 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
30 |
31 |
32 |
33 | -
34 |
35 |
36 |
37 |
38 |
39 |
40 | -
41 |
42 |
43 | true
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/Source/Ui/Settings.cpp:
--------------------------------------------------------------------------------
1 | #include "Settings.h"
2 | #include "ui_settings.h"
3 | #include
4 | #include
5 | #include
6 |
7 | Settings::Settings(QWidget *parent) : QDialog(parent), ui(new Ui::Settings) {
8 | ui->setupUi(this);
9 | settingsPath = QApplication::applicationDirPath().left(1) + ":/Settings.ini";
10 | load();
11 | }
12 |
13 | Settings::~Settings() {
14 | delete ui;
15 | }
16 |
17 | QString Settings::getExportPath() const {
18 | return ui->label_export->text() + "/";
19 | }
20 |
21 | QString Settings::getReplacePath() const {
22 | return ui->label_replace->text() + "/";
23 | }
24 |
25 | QString Settings::getOpenPath() const {
26 | return ui->label_open->text() + "/";
27 | }
28 |
29 | QString Settings::getSavePath() const {
30 | return ui->label_save->text() + "/";
31 | }
32 |
33 | void Settings::on_btn_export_clicked() {
34 | QString dir = QFileDialog::getExistingDirectory(nullptr, tr("Select Directory"));
35 | if (dir.isEmpty())
36 | return;
37 | ui->label_export->setText(dir);
38 | if (ui->label_replace->text().isEmpty())
39 | ui->label_replace->setText(dir);
40 | }
41 |
42 | void Settings::on_btn_replace_clicked() {
43 | QString dir = QFileDialog::getExistingDirectory(nullptr, tr("Select Directory"));
44 | if (dir.isEmpty())
45 | return;
46 | ui->label_replace->setText(dir);
47 | if (ui->label_export->text().isEmpty())
48 | ui->label_export->setText(dir);
49 | }
50 |
51 | void Settings::on_btn_open_clicked() {
52 | QString dir = QFileDialog::getExistingDirectory(nullptr, tr("Select Directory"));
53 | if (dir.isEmpty())
54 | return;
55 | ui->label_open->setText(dir);
56 | if (ui->label_save->text().isEmpty())
57 | ui->label_save->setText(dir);
58 | }
59 |
60 | void Settings::on_btn_save_clicked() {
61 | QString dir = QFileDialog::getExistingDirectory(nullptr, tr("Select Directory"));
62 | if (dir.isEmpty())
63 | return;
64 | ui->label_save->setText(dir);
65 | if (ui->label_open->text().isEmpty())
66 | ui->label_open->setText(dir);
67 | }
68 |
69 | void Settings::on_btn_reset_clicked() {
70 | ui->label_export->clear();
71 | ui->label_replace->clear();
72 | ui->label_open->clear();
73 | ui->label_save->clear();
74 | }
75 |
76 | void Settings::accept() {
77 | save();
78 | this->close();
79 | }
80 |
81 | void Settings::showEvent(QShowEvent *event) {
82 | QDialog::showEvent(event);
83 | load();
84 | }
85 |
86 | void Settings::save() {
87 | QSettings settingsFile(settingsPath, QSettings::IniFormat);
88 | settingsFile.setValue("ExportPath", ui->label_export->text());
89 | settingsFile.setValue("ReplacePath", ui->label_replace->text());
90 | settingsFile.setValue("OpenPath", ui->label_open->text());
91 | settingsFile.setValue("SavePath", ui->label_save->text());
92 | }
93 |
94 | void Settings::load() {
95 | QSettings settingsFile(settingsPath, QSettings::IniFormat);
96 | ui->label_export->setText(settingsFile.value("ExportPath", "").toString());
97 | ui->label_replace->setText(settingsFile.value("ReplacePath", "").toString());
98 | ui->label_open->setText(settingsFile.value("OpenPath", "").toString());
99 | ui->label_save->setText(settingsFile.value("SavePath", "").toString());
100 | }
--------------------------------------------------------------------------------
/Source/Ui/Settings.h:
--------------------------------------------------------------------------------
1 | #ifndef SETTINGS_H
2 | #define SETTINGS_H
3 |
4 | #include
5 |
6 | namespace Ui {
7 | class Settings;
8 | }
9 |
10 | class Settings : public QDialog {
11 | Q_OBJECT
12 |
13 | public:
14 | explicit Settings(QWidget *parent = nullptr);
15 | ~Settings() override;
16 | QString getExportPath() const;
17 | QString getReplacePath() const;
18 | QString getOpenPath() const;
19 | QString getSavePath() const;
20 |
21 | private slots:
22 | void on_btn_export_clicked();
23 | void on_btn_replace_clicked();
24 | void on_btn_open_clicked();
25 | void on_btn_save_clicked();
26 | void on_btn_reset_clicked();
27 |
28 | protected:
29 | void accept() override;
30 | void showEvent(QShowEvent *event) override;
31 |
32 | private:
33 | Ui::Settings *ui;
34 | QString settingsPath;
35 | void save();
36 | void load();
37 | };
38 |
39 | #endif // SETTINGS_H
40 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNS4BbData.cpp:
--------------------------------------------------------------------------------
1 | #include "OnexNS4BbData.h"
2 |
3 | OnexNS4BbData::OnexNS4BbData(QString name, QByteArray content, NosZlibOpener *opener, int id,
4 | int creationDate, bool compressed)
5 | : OnexTreeImage(name, opener, content, id, creationDate, compressed) {
6 | if (content.isEmpty())
7 | this->content = QByteArrayLiteral("\x00\x00\x00\x00");
8 | }
9 |
10 | OnexNS4BbData::OnexNS4BbData(QJsonObject jo, NosZlibOpener *opener, const QString &directory) : OnexTreeImage(jo["ID"].toString(), opener) {
11 | this->content = QByteArrayLiteral("\x00\x00\x00\x00");
12 | setId(jo["ID"].toInt(), true);
13 | setCreationDate(jo["Date"].toString(), true);
14 | setCompressed(jo["isCompressed"].toBool(), true);
15 | onReplace(directory + jo["path"].toString());
16 | }
17 |
18 | OnexNS4BbData::~OnexNS4BbData() = default;
19 |
20 | QImage OnexNS4BbData::getImage() {
21 | ImageResolution resolution = this->getResolution();
22 | return imageConverter->convertBGRA8888_INTERLACED(content, resolution.x, resolution.y, 4);
23 | }
24 |
25 | ImageResolution OnexNS4BbData::getResolution() {
26 | int x = opener->getLittleEndianConverter()->fromShort(content.mid(0, 2));
27 | int y = opener->getLittleEndianConverter()->fromShort(content.mid(2, 2));
28 | return ImageResolution{x, y};
29 | }
30 |
31 | int OnexNS4BbData::afterReplace(QImage image) {
32 | QByteArray newContent;
33 | newContent.push_back(opener->getLittleEndianConverter()->toShort(image.width()));
34 | newContent.push_back(opener->getLittleEndianConverter()->toShort(image.height()));
35 | newContent.push_back(imageConverter->toBGRA8888_INTERLACED(image));
36 | setContent(newContent);
37 | setWidth(image.width(), true);
38 | setHeight(image.height(), true);
39 |
40 | emit OnexTreeImage::replaceSignal(this->getImage());
41 | emit replaceInfo(generateInfos());
42 | return 1;
43 | }
44 |
45 | void OnexNS4BbData::setWidth(int width, bool update) {
46 | content.replace(0, 2, opener->getLittleEndianConverter()->toShort(width));
47 | if (update)
48 | emit changeSignal("Width", width);
49 | }
50 |
51 | void OnexNS4BbData::setHeight(int height, bool update) {
52 | content.replace(2, 2, opener->getLittleEndianConverter()->toShort(height));
53 | if (update)
54 | emit changeSignal("Height", height);
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNS4BbData.h:
--------------------------------------------------------------------------------
1 | #ifndef ONEXNS4BBDATA_H
2 | #define ONEXNS4BBDATA_H
3 |
4 | #include "OnexTreeImage.h"
5 |
6 | class OnexNS4BbData : public OnexTreeImage {
7 | Q_OBJECT
8 | public:
9 | OnexNS4BbData(QString name, QByteArray content, NosZlibOpener *opener, int id = -1,
10 | int creationDate = 0, bool compressed = false);
11 | OnexNS4BbData(QJsonObject jo, NosZlibOpener *opener, const QString &directory);
12 | ~OnexNS4BbData() override;
13 | QImage getImage() override;
14 | ImageResolution getResolution() override;
15 | public slots:
16 | int afterReplace(QImage image) override;
17 | void setWidth(int width, bool update = false) override;
18 | void setHeight(int height, bool update = false) override;
19 | };
20 |
21 | #endif // ONEXNS4BBDATA_H
22 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNSipData.cpp:
--------------------------------------------------------------------------------
1 | #include "OnexNSipData.h"
2 |
3 | OnexNSipData::OnexNSipData(QString name, QByteArray content, NosZlibOpener *opener, int id,
4 | int creationDate, bool compressed)
5 | : OnexTreeImage(name, opener, content, id, creationDate, compressed) {
6 | if (content.isEmpty())
7 | this->content = QByteArrayLiteral("\x01\x00\x00\x00\x00\x00\x00\x00\x00\x0D\x00\x00\x00");
8 | }
9 |
10 | OnexNSipData::OnexNSipData(QJsonObject jo, NosZlibOpener *opener, const QString &directory) : OnexTreeImage(jo["ID"].toString(), opener) {
11 | this->content = QByteArrayLiteral("\x01\x00\x00\x00\x00\x00\x00\x00\x00\x0D\x00\x00\x00");
12 | setId(jo["ID"].toInt(), true);
13 | setCreationDate(jo["Date"].toString(), true);
14 | setCompressed(jo["isCompressed"].toBool(), true);
15 | setCenterX(jo["Center-X"].toInt(), true);
16 | setCenterY(jo["Center-Y"].toInt(), true);
17 | onReplace(directory + jo["path"].toString());
18 | }
19 |
20 | OnexNSipData::~OnexNSipData() = default;
21 |
22 | QImage OnexNSipData::getImage() {
23 | ImageResolution resolution = this->getResolution();
24 | return imageConverter->convertGBAR4444(content, resolution.x, resolution.y, 13);
25 | }
26 |
27 | ImageResolution OnexNSipData::getResolution() {
28 | int x = opener->getLittleEndianConverter()->fromShort(content.mid(1, 2));
29 | int y = opener->getLittleEndianConverter()->fromShort(content.mid(3, 2));
30 | return ImageResolution{x, y};
31 | }
32 |
33 | ImageResolution OnexNSipData::getCenter() {
34 | int x = opener->getLittleEndianConverter()->fromShort(content.mid(5, 2));
35 | int y = opener->getLittleEndianConverter()->fromShort(content.mid(7, 2));
36 | return ImageResolution{x, y};
37 | }
38 |
39 | int OnexNSipData::afterReplace(QImage image) {
40 | QByteArray newContent;
41 | newContent.push_back(content.mid(0, 1));
42 | newContent.push_back(opener->getLittleEndianConverter()->toShort(image.width()));
43 | newContent.push_back(opener->getLittleEndianConverter()->toShort(image.height()));
44 | newContent.push_back(content.mid(5, 8));
45 | newContent.push_back(imageConverter->toGBAR4444(image));
46 | setContent(newContent);
47 | setWidth(image.width(), true);
48 | setHeight(image.height(), true);
49 |
50 | emit OnexTreeImage::replaceSignal(this->getImage());
51 | emit replaceInfo(generateInfos());
52 | return 1;
53 | }
54 |
55 | void OnexNSipData::setWidth(int width, bool update) {
56 | content.replace(1, 2, opener->getLittleEndianConverter()->toShort(width));
57 | if (update)
58 | emit changeSignal("Width", width);
59 | }
60 |
61 | void OnexNSipData::setHeight(int height, bool update) {
62 | content.replace(3, 2, opener->getLittleEndianConverter()->toShort(height));
63 | if (update)
64 | emit changeSignal("Height", height);
65 | }
66 |
67 | void OnexNSipData::setCenterX(int center, bool update) {
68 | content.replace(5, 2, opener->getLittleEndianConverter()->toShort(center));
69 | if (update)
70 | emit changeSignal("Center-X", center);
71 | }
72 |
73 | void OnexNSipData::setCenterY(int center, bool update) {
74 | content.replace(7, 2, opener->getLittleEndianConverter()->toShort(center));
75 | if (update)
76 | emit changeSignal("Center-Y", center);
77 | }
78 |
79 | FileInfo *OnexNSipData::generateInfos() {
80 | FileInfo *infos = OnexTreeImage::generateInfos();
81 | if (hasParent()) {
82 | connect(infos->addIntLineEdit("Center-X", getCenter().x), &QLineEdit::textChanged,
83 | [=](const QString &value) { setCenterY(value.toInt()); });
84 | connect(infos->addIntLineEdit("Center-Y", getCenter().y), &QLineEdit::textChanged,
85 | [=](const QString &value) { setCenterX(value.toInt()); });
86 | }
87 | return infos;
88 | }
89 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNSipData.h:
--------------------------------------------------------------------------------
1 | #ifndef ONEXNSIPDATA_H
2 | #define ONEXNSIPDATA_H
3 |
4 | #include "OnexTreeImage.h"
5 |
6 | class OnexNSipData : public OnexTreeImage {
7 | Q_OBJECT
8 | public:
9 | OnexNSipData(QString name, QByteArray content, NosZlibOpener *opener, int id = -1,
10 | int creationDate = 0, bool compressed = false);
11 | OnexNSipData(QJsonObject jo, NosZlibOpener *opener, const QString &directory);
12 | ~OnexNSipData() override;
13 | QImage getImage() override;
14 | ImageResolution getResolution() override;
15 | ImageResolution getCenter();
16 | public slots:
17 | int afterReplace(QImage image) override;
18 | void setWidth(int width, bool update = false);
19 | void setHeight(int height, bool update = false);
20 | void setCenterX(int center, bool update = false);
21 | void setCenterY(int center, bool update = false);
22 | protected:
23 | FileInfo *generateInfos() override;
24 | };
25 |
26 | #endif // ONEXNSIPDATA_H
27 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNSmcData.cpp:
--------------------------------------------------------------------------------
1 | #include "OnexNSmcData.h"
2 | #include "../Previews/SingleTextFilePreview.h"
3 | #include
4 | #include
5 | #include
6 |
7 | OnexNSmcData::OnexNSmcData(const QString &name, QByteArray content, NosZlibOpener *opener, int id, int creationDate,
8 | bool compressed) : OnexTreeZlibItem(name, opener, content, id, creationDate, compressed) {
9 | }
10 |
11 | OnexNSmcData::OnexNSmcData(QJsonObject jo, NosZlibOpener *opener, const QString &directory) : OnexTreeZlibItem(jo["ID"].toString(), opener) {
12 | setId(jo["ID"].toInt(), true);
13 | setCreationDate(jo["Date"].toString(), true);
14 | setCompressed(jo["isCompressed"].toBool(), true);
15 | onReplace(directory + jo["path"].toString());
16 | }
17 |
18 | OnexNSmcData::~OnexNSmcData() = default;
19 |
20 | QWidget *OnexNSmcData::getPreview() {
21 | if (!hasParent())
22 | return nullptr;
23 | QByteArray json = QJsonDocument(toJson()).toJson();
24 | auto *textPreview = new SingleTextFilePreview(json);
25 | connect(this, SIGNAL(replaceSignal(QByteArray)), textPreview, SLOT(onReplaced(QByteArray)));
26 | return textPreview;
27 | }
28 |
29 | QString OnexNSmcData::getExportExtension() {
30 | return ".json";
31 | }
32 |
33 | int OnexNSmcData::saveAsFile(const QString &path, QByteArray content) {
34 | return OnexTreeItem::saveAsFile(path, QJsonDocument(toJson()).toJson());
35 | }
36 |
37 | int OnexNSmcData::afterReplace(QByteArray content) {
38 | setContent(fromJson(content));
39 | emit replaceSignal(this->getContent());
40 | return 1;
41 | }
42 |
43 | QJsonObject OnexNSmcData::toJson() {
44 | QJsonObject jo;
45 | int amount = content.at(0);
46 | uint8_t flag = content.at(1);
47 | jo["loop"] = (bool) (flag >> 7);
48 | jo["unknown"] = (bool) ((flag >> 6) & 1);
49 | int offset = 2;
50 |
51 | QJsonArray array;
52 | for (int i = 0; i < amount; i++) {
53 | QJsonObject frame;
54 | frame["spriteIndex"] = content.at(offset);
55 | frame["triggerEffect"] = content.at(offset + 1) == 9; // 0=false, 9=true
56 | array.append(frame);
57 | offset += 2;
58 | }
59 | jo["frames"] = array;
60 |
61 | return jo;
62 | }
63 |
64 | QByteArray OnexNSmcData::fromJson(const QByteArray &data) {
65 | QJsonObject jo = QJsonDocument::fromJson(data).object();
66 | QJsonArray array = jo["frames"].toArray();
67 | QByteArray newContent;
68 | newContent.append((uint8_t) array.size());
69 | bool loop = jo["loop"].toBool();
70 | bool unknown = jo["unknown"].toBool();
71 | uint8_t flag = (loop << 7) + (unknown << 6);
72 | newContent.append(flag);
73 | for (QJsonValueRef value : array) {
74 | QJsonObject frame = value.toObject();
75 | newContent.append((uint8_t) frame["spriteIndex"].toInt());
76 | newContent.append((uint8_t) (frame["triggerEffect"].toBool() ? 9 : 0));
77 | }
78 | return newContent;
79 | }
80 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNSmcData.h:
--------------------------------------------------------------------------------
1 | #ifndef ONEXNSMCDATA_H
2 | #define ONEXNSMCDATA_H
3 |
4 |
5 | #include "OnexTreeZlibItem.h"
6 |
7 | class OnexNSmcData : public OnexTreeZlibItem {
8 | Q_OBJECT
9 | public:
10 | OnexNSmcData(const QString &name, QByteArray content, NosZlibOpener *opener, int id = -1, int creationDate = 0,
11 | bool compressed = false);
12 | OnexNSmcData(QJsonObject jo, NosZlibOpener *opener, const QString &directory);
13 | ~OnexNSmcData() override;
14 | QWidget *getPreview() override;
15 | QString getExportExtension() override;
16 | public slots:
17 | int afterReplace(QByteArray content) override;
18 | signals:
19 | void replaceSignal(QByteArray text);
20 | protected:
21 | int saveAsFile(const QString &path, QByteArray content) override;
22 | QJsonObject toJson();
23 | QByteArray fromJson(const QByteArray &data);
24 | };
25 |
26 |
27 | #endif //ONEXNSMCDATA_H
28 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNSmnData.cpp:
--------------------------------------------------------------------------------
1 | #include "OnexNSmnData.h"
2 | #include "../Previews/SingleTextFilePreview.h"
3 | #include
4 | #include
5 | #include
6 |
7 | OnexNSmnData::OnexNSmnData(const QString &name, int creationDate, INosFileOpener *opener, QByteArray content) : OnexTreeItem(name, opener, content),
8 | creationDate(creationDate) {
9 |
10 | }
11 |
12 | int OnexNSmnData::getCreationDate() {
13 | return creationDate;
14 | }
15 |
16 | QString OnexNSmnData::getDateAsString() {
17 | int year = (getCreationDate() & 0xFFFF0000) >> 0x10;
18 | int month = (getCreationDate() & 0xFF00) >> 0x08;
19 | int day = getCreationDate() & 0xFF;
20 | return QString("%1/%2/%3").arg(day, 2, 16, QChar('0')).arg(month, 2, 16, QChar('0')).arg(year, 4, 16, QChar('0'));
21 | }
22 |
23 | void OnexNSmnData::loadJson(QJsonArray array) {
24 | for (auto &&i : array) {
25 | QJsonObject jo = i.toObject();
26 | addChild(new OnexNSmnData(QString::number(childCount()), creationDate, opener, QJsonDocument(jo).toJson()));
27 | }
28 | }
29 |
30 | void OnexNSmnData::setCreationDate(const QString &date, bool update) {
31 | QStringList parts = date.split("/", QString::SplitBehavior::SkipEmptyParts);
32 | if (parts.size() != 3)
33 | this->creationDate = 0;
34 | else {
35 | int year = parts[2].toInt(nullptr, 16) << 0x10;
36 | int month = parts[1].toInt(nullptr, 16) << 0x08;
37 | int day = parts[0].toInt(nullptr, 16);
38 | this->creationDate = year + month + day;
39 | }
40 | if (update)
41 | emit changeSignal("Date", getDateAsString());
42 | }
43 |
44 | FileInfo *OnexNSmnData::generateInfos() {
45 | auto *infos = OnexTreeItem::generateInfos();
46 | if (!hasParent()) {
47 | infos->addStringLineEdit("Header", getContent())->setEnabled(false);
48 | connect(infos->addStringLineEdit("Date", getDateAsString()), &QLineEdit::textChanged,
49 | [=](const QString &value) { setCreationDate(value); });
50 | }
51 | return infos;
52 | }
53 |
54 | QWidget *OnexNSmnData::getPreview() {
55 | if (!hasParent())
56 | return nullptr;
57 | auto *textPreview = new SingleTextFilePreview(content);
58 | connect(this, SIGNAL(replaceSignal(QByteArray)), textPreview, SLOT(onReplaced(QByteArray)));
59 | return textPreview;
60 | }
61 |
62 | QString OnexNSmnData::getExportExtension() {
63 | return ".json";
64 | }
65 |
66 | int OnexNSmnData::onExport(QString directory) {
67 | if (!hasParent()) {
68 | QMessageBox::StandardButton message = QMessageBox::question(nullptr, "Export?", "Export all items together as one JSON?");
69 | if (message == QMessageBox::Yes) {
70 | QJsonArray fullJson;
71 | for (int i = 0; i < childCount(); i++) {
72 | auto *ct = static_cast(child(i));
73 | QJsonObject jo = QJsonDocument::fromJson(ct->getContent()).object();
74 | fullJson.append(jo);
75 | }
76 | saveAsFile(directory + name + ".json", QJsonDocument(fullJson).toJson());
77 | return 1;
78 | } else {
79 | return OnexTreeItem::onExport(directory);
80 | }
81 | }
82 | return OnexTreeItem::onExport(directory);
83 | }
84 |
85 | int OnexNSmnData::onReplace(QString directory) {
86 | if (!hasParent()) {
87 | QMessageBox::StandardButton message = QMessageBox::question(nullptr, "Replace?", "Replace all items together from one JSON?");
88 | if (message == QMessageBox::Yes) {
89 | QString path = directory + name + ".json";
90 | QFile file(path);
91 | if (file.open(QIODevice::ReadOnly)) {
92 | QJsonArray fullJson = QJsonDocument::fromJson(file.readAll()).array();
93 | file.close();
94 | takeChildren().clear();
95 | loadJson(fullJson);
96 | return childCount();
97 | } else {
98 | QMessageBox::critical(nullptr, "Woops", "Couldn't open " + path);
99 | return 0;
100 | }
101 | }
102 | }
103 | return OnexTreeItem::onReplace(directory);
104 | }
105 |
106 | int OnexNSmnData::afterReplace(QByteArray content) {
107 | setContent(content);
108 | emit replaceSignal(this->getContent());
109 | return 1;
110 | }
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNSmnData.h:
--------------------------------------------------------------------------------
1 | #ifndef ONEXNSMNDATA_H
2 | #define ONEXNSMNDATA_H
3 |
4 | #include "../OnexTreeItem.h"
5 |
6 | class OnexNSmnData : public OnexTreeItem {
7 | Q_OBJECT
8 |
9 | public:
10 | OnexNSmnData(const QString &name, int creationDate, INosFileOpener *opener, QByteArray content);
11 | QWidget *getPreview() override;
12 | QString getExportExtension() override;
13 | int getCreationDate();
14 | public slots:
15 | int onExport(QString directory) override;
16 | int onReplace(QString directory) override;
17 | int afterReplace(QByteArray content) override;
18 | void setCreationDate(const QString &date, bool update = false);
19 | signals:
20 | void replaceSignal(QByteArray text);
21 | protected:
22 | int creationDate;
23 | void loadJson(QJsonArray array);
24 | FileInfo *generateInfos() override;
25 | QString getDateAsString();
26 | };
27 |
28 |
29 | #endif // ONEXNSMNDATA_H
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNSmpData.cpp:
--------------------------------------------------------------------------------
1 | #include "OnexNSmpData.h"
2 | #include "../Previews/MultiImagePreview.h"
3 | #include "OnexNSmpFrame.h"
4 |
5 | OnexNSmpData::OnexNSmpData(const QString &name, QByteArray content, NosZlibOpener *opener,
6 | int id, int creationDate, bool compressed)
7 | : OnexTreeZlibItem(name, opener, content, id, creationDate, compressed) {
8 | if (content.isEmpty())
9 | this->content = QByteArray(0x0);
10 | if (id == -1)
11 | return;
12 | int amount = content.at(0);
13 | for (int i = 0; i < amount; i++) {
14 | int width = opener->getLittleEndianConverter()->fromShort(content.mid(1 + i * 12, 2));
15 | int height = opener->getLittleEndianConverter()->fromShort(content.mid(3 + i * 12, 2));
16 | int xOrigin = opener->getLittleEndianConverter()->fromShort(content.mid(5 + i * 12, 2));
17 | int yOrigin = opener->getLittleEndianConverter()->fromShort(content.mid(7 + i * 12, 2));
18 | int offset = opener->getLittleEndianConverter()->fromInt(content.mid(9 + i * 12, 4));
19 | QByteArray subContent = content.mid(offset, (width * 2 * height));
20 | addFrame(subContent, width, height, xOrigin, yOrigin);
21 | }
22 | }
23 |
24 | OnexNSmpData::OnexNSmpData(QJsonObject jo, NosZlibOpener *opener, const QString &directory) : OnexTreeZlibItem(jo["ID"].toString(), opener) {
25 | this->content = QByteArrayLiteral("\x00");
26 | setId(jo["ID"].toInt(), true);
27 | setCreationDate(jo["Date"].toString(), true);
28 | setCompressed(jo["isCompressed"].toBool(), true);
29 |
30 | QJsonArray contentArray = jo["content"].toArray();
31 |
32 | for (auto &&i : contentArray) {
33 | QJsonObject frameJo = i.toObject();
34 | this->addChild(new OnexNSmpFrame(name + "_" + QString::number(this->childCount()), frameJo, opener, directory));
35 | }
36 | }
37 |
38 | OnexNSmpData::~OnexNSmpData() = default;
39 |
40 | QWidget *OnexNSmpData::getPreview() {
41 | if (!hasParent())
42 | return nullptr;
43 | auto *images = new QList();
44 | for (int i = 0; i != this->childCount(); ++i) {
45 | auto *item = dynamic_cast(this->child(i));
46 | images->append(item->getImage());
47 | }
48 | auto *imagePreview = new MultiImagePreview(images);
49 | connect(this, SIGNAL(replaceSignal(QList * )), imagePreview, SLOT(onReplaced(QList * )));
50 | return imagePreview;
51 | }
52 |
53 | QByteArray OnexNSmpData::getContent() {
54 | int amount = childCount();
55 | if (!hasParent() || amount <= 0)
56 | return content;
57 | QByteArray offsetArray;
58 | offsetArray.push_back((uint8_t) amount);
59 | int sizeOfOffsetArray = 1 + amount * 12;
60 | QByteArray contentArray;
61 | for (int i = 0; i < amount; i++) {
62 | int currentFileOffset = sizeOfOffsetArray + contentArray.size();
63 | auto *currentItem = dynamic_cast(this->child(i));
64 | offsetArray.push_back(opener->getLittleEndianConverter()->toShort(currentItem->getWidth()));
65 | offsetArray.push_back(opener->getLittleEndianConverter()->toShort(currentItem->getHeight()));
66 | offsetArray.push_back(opener->getLittleEndianConverter()->toShort(currentItem->getXOrigin()));
67 | offsetArray.push_back(opener->getLittleEndianConverter()->toShort(currentItem->getYOrigin()));
68 | offsetArray.push_back(opener->getLittleEndianConverter()->toInt(currentFileOffset));
69 | contentArray.push_back(currentItem->getContent());
70 | }
71 | this->content = QByteArray();
72 | content.push_back(offsetArray);
73 | content.push_back(contentArray);
74 | return content;
75 | }
76 |
77 | void OnexNSmpData::setName(QString name) {
78 | OnexTreeZlibItem::setName(name);
79 | QList childList = takeChildren();
80 | for (int i = 0; i < childList.size(); i++) {
81 | auto *item = static_cast(childList.at(i));
82 | item->OnexTreeItem::setName(name + "_" + QString::number(i));
83 | }
84 | addChildren(childList);
85 | }
86 |
87 | int OnexNSmpData::afterReplace(QByteArray content) {
88 | auto *images = new QList();
89 | for (int i = 0; i != this->childCount(); ++i) {
90 | auto *item = dynamic_cast(this->child(i));
91 | images->append(item->getImage());
92 | }
93 | emit replaceSignal(images);
94 | emit replaceInfo(generateInfos());
95 | return 1;
96 | }
97 |
98 | OnexTreeItem *OnexNSmpData::addFrame(QByteArray content, short width, short height, short xOrigin, short yOrigin) {
99 | OnexTreeItem *frame = new OnexNSmpFrame(name + "_" + QString::number(this->childCount()), content, width, height, xOrigin, yOrigin,
100 | (NosZlibOpener *) opener, id, creationDate, compressed);
101 | this->addChild(frame);
102 | return frame;
103 | }
104 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNSmpData.h:
--------------------------------------------------------------------------------
1 | #ifndef ONEXNSMPDATA_H
2 | #define ONEXNSMPDATA_H
3 |
4 | #include "../../Openers/NosZlibOpener.h"
5 | #include "OnexTreeZlibItem.h"
6 |
7 | class OnexNSmpData : public OnexTreeZlibItem {
8 | Q_OBJECT
9 | public:
10 | OnexNSmpData(const QString &name, QByteArray content, NosZlibOpener *opener,
11 | int id = -1, int creationDate = 0, bool compressed = false);
12 | OnexNSmpData(QJsonObject jo, NosZlibOpener *opener, const QString &directory);
13 | ~OnexNSmpData() override;
14 | QWidget *getPreview() override;
15 | QByteArray getContent() override;
16 | OnexTreeItem *addFrame(QByteArray content = QByteArray(), short width = 0, short height = 0, short xOrigin = 0, short yOrigin = 0);
17 | public slots:
18 | void setName(QString name) override;
19 | int afterReplace(QByteArray content) override;
20 | signals:
21 | void replaceSignal(QList *newImages);
22 | };
23 |
24 | #endif // ONEXNSMPDATA_H
25 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNSmpFrame.cpp:
--------------------------------------------------------------------------------
1 | #include "OnexNSmpFrame.h"
2 |
3 | OnexNSmpFrame::OnexNSmpFrame(QString name, QByteArray content, int width, int height, int xOrigin, int yOrigin, NosZlibOpener *opener,
4 | int id, int creationDate, bool compressed)
5 | : OnexTreeImage(name, opener, content, id, creationDate, compressed) {
6 | setFlags(this->flags() & (~Qt::ItemIsEditable));
7 | this->width = width;
8 | this->height = height;
9 | this->xOrigin = xOrigin;
10 | this->yOrigin = yOrigin;
11 | }
12 |
13 | OnexNSmpFrame::OnexNSmpFrame(QString name, QJsonObject jo, NosZlibOpener *opener, const QString &directory) : OnexTreeImage(name, opener) {
14 | setXOrigin(jo["x-Origin"].toInt(), true);
15 | setYOrigin(jo["y-Origin"].toInt(), true);
16 | onReplace(directory + jo["path"].toString());
17 | }
18 |
19 | OnexNSmpFrame::~OnexNSmpFrame() = default;
20 |
21 | QImage OnexNSmpFrame::getImage() {
22 | ImageResolution resolution = this->getResolution();
23 | return imageConverter->convertGBAR4444(content, resolution.x, resolution.y);
24 | }
25 |
26 | ImageResolution OnexNSmpFrame::getResolution() {
27 | return ImageResolution{this->width, this->height};
28 | }
29 |
30 | int OnexNSmpFrame::afterReplace(QImage image) {
31 | setWidth(image.width(), true);
32 | setHeight(image.height(), true);
33 | setContent(imageConverter->toGBAR4444(image));
34 | emit OnexTreeImage::replaceSignal(this->getImage());
35 | FileInfo *newInfo = generateInfos();
36 | emit replaceInfo(newInfo);
37 | emit replaceInfo(generateInfos());
38 | return 1;
39 | }
40 |
41 | int OnexNSmpFrame::getWidth() {
42 | return width;
43 | }
44 |
45 | int OnexNSmpFrame::getHeight() {
46 | return height;
47 | }
48 |
49 | int OnexNSmpFrame::getXOrigin() {
50 | return xOrigin;
51 | }
52 |
53 | int OnexNSmpFrame::getYOrigin() {
54 | return yOrigin;
55 | }
56 |
57 | void OnexNSmpFrame::setId(int id, bool update) {
58 | this->id = id;
59 | }
60 |
61 | void OnexNSmpFrame::setWidth(int width, bool update) {
62 | this->width = width;
63 | if (update)
64 | emit changeSignal("Width", width);
65 | }
66 |
67 | void OnexNSmpFrame::setHeight(int height, bool update) {
68 | this->height = height;
69 | if (update)
70 | emit changeSignal("Height", height);
71 | }
72 |
73 | void OnexNSmpFrame::setXOrigin(int xOrigin, bool update) {
74 | this->xOrigin = xOrigin;
75 | if (update)
76 | emit changeSignal("x-Origin", xOrigin);
77 | }
78 |
79 | void OnexNSmpFrame::setYOrigin(int yOrigin, bool update) {
80 | this->yOrigin = yOrigin;
81 | if (update)
82 | emit changeSignal("y-Origin", yOrigin);
83 | }
84 |
85 | FileInfo *OnexNSmpFrame::generateInfos() {
86 | FileInfo *infos = OnexTreeItem::generateInfos();
87 | if (hasParent()) {
88 | infos->addIntLineEdit("Width", getWidth())->setDisabled(true);
89 | infos->addIntLineEdit("Height", getHeight())->setDisabled(true);
90 | connect(infos->addIntLineEdit("x-Origin", getXOrigin()), &QLineEdit::textChanged,
91 | [=](const QString &value) { setXOrigin(value.toInt()); });
92 | connect(infos->addIntLineEdit("y-Origin", getYOrigin()), &QLineEdit::textChanged,
93 | [=](const QString &value) { setYOrigin(value.toInt()); });
94 | }
95 | return infos;
96 | }
97 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNSmpFrame.h:
--------------------------------------------------------------------------------
1 | #ifndef ONEXNSMPFRAME_H
2 | #define ONEXNSMPFRAME_H
3 |
4 | #include "OnexTreeImage.h"
5 |
6 | class OnexNSmpFrame : public OnexTreeImage {
7 | Q_OBJECT
8 | public:
9 | OnexNSmpFrame(QString name, QByteArray content, int width, int height, int xOrigin, int yOrigin, NosZlibOpener *opener,
10 | int id = -1, int creationDate = 0, bool compressed = false);
11 | OnexNSmpFrame(QString name, QJsonObject jo, NosZlibOpener *opener, const QString &directory);
12 | ~OnexNSmpFrame() override;
13 | QImage getImage() override;
14 | ImageResolution getResolution() override;
15 | int getWidth();
16 | int getHeight();
17 | int getXOrigin();
18 | int getYOrigin();
19 | public slots:
20 | void setId(int id, bool update = false) override;
21 | int afterReplace(QImage image) override;
22 | void setWidth(int width, bool update = false) override;
23 | void setHeight(int height, bool update = false) override;
24 | void setXOrigin(int xOrigin, bool update = false);
25 | void setYOrigin(int yOrigin, bool update = false);
26 | protected:
27 | int width;
28 | int height;
29 | int xOrigin;
30 | int yOrigin;
31 | FileInfo *generateInfos() override;
32 | };
33 |
34 | #endif // ONEXNSMPFRAME_H
35 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNStcData.cpp:
--------------------------------------------------------------------------------
1 | #include "OnexNStcData.h"
2 |
3 | OnexNStcData::OnexNStcData(QString name, QByteArray content, NosZlibOpener *opener, int id,
4 | int creationDate, bool compressed)
5 | : OnexTreeImage(name, opener, content, id, creationDate, compressed) {
6 | if (content.isEmpty())
7 | this->content = QByteArrayLiteral("\x00\x00\x00\x00\x00\x00\x00\x00");
8 | }
9 |
10 | OnexNStcData::OnexNStcData(QJsonObject jo, NosZlibOpener *opener, const QString &directory) : OnexTreeImage(jo["ID"].toString(), opener) {
11 | this->content = QByteArrayLiteral("\x00\x00\x00\x00\x00\x00\x00\x00");
12 | setId(jo["ID"].toInt(), true);
13 | setCreationDate(jo["Date"].toString(), true);
14 | setCompressed(jo["isCompressed"].toBool(), true);
15 | onReplace(directory + jo["path"].toString());
16 | }
17 |
18 | OnexNStcData::~OnexNStcData() = default;
19 |
20 | QImage OnexNStcData::getImage() {
21 | ImageResolution resolution = this->getResolution();
22 | return imageConverter->convertNSTC(content, resolution.x, resolution.y, 4);
23 | }
24 |
25 | ImageResolution OnexNStcData::getResolution() {
26 | int x = opener->getLittleEndianConverter()->fromShort(content.mid(0, 2));
27 | int y = opener->getLittleEndianConverter()->fromShort(content.mid(2, 2));
28 | return ImageResolution{x, y};
29 | }
30 |
31 | int OnexNStcData::afterReplace(QImage image) {
32 | QByteArray newContent;
33 | newContent.push_back(opener->getLittleEndianConverter()->toShort(image.width()));
34 | newContent.push_back(opener->getLittleEndianConverter()->toShort(image.height()));
35 | newContent.push_back(imageConverter->toNSTC(image));
36 | setContent(newContent);
37 | setWidth(image.width(), true);
38 | setHeight(image.height(), true);
39 |
40 | emit OnexTreeImage::replaceSignal(this->getImage());
41 | emit replaceInfo(generateInfos());
42 | return 1;
43 | }
44 |
45 | void OnexNStcData::setWidth(int width, bool update) {
46 | content.replace(0, 2, opener->getLittleEndianConverter()->toShort(width));
47 | if (update)
48 | emit changeSignal("Width", width);
49 | }
50 |
51 | void OnexNStcData::setHeight(int height, bool update) {
52 | content.replace(2, 2, opener->getLittleEndianConverter()->toShort(height));
53 | if (update)
54 | emit changeSignal("Width", height);
55 | }
56 |
57 | bool OnexNStcData::hasGoodResolution(int x, int y) {
58 | ImageResolution currentResolution = this->getResolution();
59 | return (x / 2 == currentResolution.x && y / 2 == currentResolution.y);
60 | }
61 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNStcData.h:
--------------------------------------------------------------------------------
1 | #ifndef ONEXNSTCDATA_H
2 | #define ONEXNSTCDATA_H
3 |
4 | #include "OnexTreeImage.h"
5 |
6 | class OnexNStcData : public OnexTreeImage {
7 | Q_OBJECT
8 | public:
9 | OnexNStcData(QString name, QByteArray content, NosZlibOpener *opener, int id = -1,
10 | int creationDate = 0, bool compressed = false);
11 | OnexNStcData(QJsonObject jo, NosZlibOpener *opener, const QString &directory);
12 | ~OnexNStcData() override;
13 | QImage getImage() override;
14 | ImageResolution getResolution() override;
15 | public slots:
16 | int afterReplace(QImage image) override;
17 | void setWidth(int width, bool update = false);
18 | void setHeight(int height, bool update = false);
19 | protected:
20 | bool hasGoodResolution(int x, int y) override;
21 | };
22 |
23 | #endif // ONEXNSTCDATA_H
24 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNStgData.cpp:
--------------------------------------------------------------------------------
1 | #include "OnexNStgData.h"
2 | #include "../Previews/SingleModelPreview.h"
3 |
4 | ObjConverter OnexNStgData::objConverter = ObjConverter();
5 | NosModelConverter OnexNStgData::nosModelConverter = NosModelConverter();
6 |
7 | OnexNStgData::OnexNStgData(QString name, QByteArray content, NosZlibOpener *opener, int id,
8 | int creationDate, bool compressed)
9 | : OnexTreeZlibItem(name, opener, content, id, creationDate, compressed) {
10 | model = nullptr;
11 | }
12 |
13 | OnexNStgData::OnexNStgData(QJsonObject jo, NosZlibOpener *opener, const QString &directory) : OnexTreeZlibItem(jo["ID"].toString(), opener) {
14 | model = new Model();
15 | setId(jo["ID"].toInt(), true);
16 | setCreationDate(jo["Date"].toString(), true);
17 | setCompressed(jo["isCompressed"].toBool(), true);
18 | model->uvScale = jo["UV-Scale"].toDouble();
19 | onReplace(directory + jo["path"].toString());
20 |
21 | for (int i = 0; i < model->objects.size(); i++) {
22 | model->objects[i].position.setX(jo["O-" + QString::number(i) + "-x-Position"].toDouble());
23 | model->objects[i].position.setY(jo["O-" + QString::number(i) + "-y-Position"].toDouble());
24 | model->objects[i].position.setZ(jo["O-" + QString::number(i) + "-z-Position"].toDouble());
25 | model->objects[i].rotation.setX(jo["O-" + QString::number(i) + "-x-Rotation"].toDouble());
26 | model->objects[i].rotation.setY(jo["O-" + QString::number(i) + "-y-Rotation"].toDouble());
27 | model->objects[i].rotation.setZ(jo["O-" + QString::number(i) + "-z-Rotation"].toDouble());
28 | model->objects[i].rotation.setW(jo["O-" + QString::number(i) + "-w-Rotation"].toDouble());
29 | model->objects[i].scale.setX(jo["O-" + QString::number(i) + "-x-Scale"].toDouble());
30 | model->objects[i].scale.setY(jo["O-" + QString::number(i) + "-y-Scale"].toDouble());
31 | model->objects[i].scale.setZ(jo["O-" + QString::number(i) + "-z-Scale"].toDouble());
32 | }
33 | }
34 |
35 | OnexNStgData::~OnexNStgData() = default;
36 |
37 | QWidget *OnexNStgData::getPreview() {
38 | if (!hasParent())
39 | return nullptr;
40 | if (model == nullptr) {
41 | model = nosModelConverter.fromBinary(content);
42 | }
43 | auto *modelPreview = new SingleModelPreview(model);
44 | connect(this, SIGNAL(replaceSignal(Model * )), modelPreview, SLOT(onReplaced(Model * )));
45 |
46 | auto *wrapper = new QWidget();
47 | wrapper->setLayout(new QGridLayout());
48 | wrapper->layout()->addWidget(modelPreview);
49 | return wrapper;
50 | }
51 |
52 | QByteArray OnexNStgData::getContent() {
53 | if (!hasParent() || model == nullptr)
54 | return content;
55 | QByteArray newContent = content.mid(0, 0x30);
56 | newContent.append(nosModelConverter.toBinary(model));
57 | content = newContent;
58 | return content;
59 | }
60 |
61 | int OnexNStgData::saveAsFile(const QString &path, QByteArray content) {
62 | if (model == nullptr) {
63 | model = nosModelConverter.fromBinary(this->content);
64 | }
65 | QStringList obj = objConverter.toObj(model, name);
66 |
67 | if (OnexTreeItem::saveAsFile(path, obj.at(0).toLocal8Bit()) == 0)
68 | return 0;
69 | if (OnexTreeItem::saveAsFile(path.split(".").at(0) + ".mtl", obj.at(1).toLocal8Bit()) == 0)
70 | return 0;
71 | return 1;
72 | }
73 |
74 | QString OnexNStgData::getExportExtension() {
75 | return ".obj";
76 | }
77 |
78 | int OnexNStgData::afterReplace(QByteArray content) {
79 | float scale = model->uvScale;
80 | model = objConverter.fromObj(content);
81 | model->uvScale = scale;
82 |
83 | emit replaceSignal(this->model);
84 | emit replaceInfo(generateInfos());
85 | return 1;
86 | }
87 |
88 | void OnexNStgData::setXPosition(int index, float x, bool update) {
89 | model->objects[index].position.setX(x);
90 | if (update)
91 | emit changeSignal("O-" + QString::number(index) + "-x-Position", x);
92 | }
93 |
94 | void OnexNStgData::setYPosition(int index, float y, bool update) {
95 | model->objects[index].position.setY(y);
96 | if (update)
97 | emit changeSignal("O-" + QString::number(index) + "-y-Position", y);
98 | }
99 |
100 | void OnexNStgData::setZPosition(int index, float z, bool update) {
101 | model->objects[index].position.setZ(z);
102 | if (update)
103 | emit changeSignal("O-" + QString::number(index) + "-z-Position", z);
104 | }
105 |
106 | void OnexNStgData::setXRotation(int index, float x, bool update) {
107 | model->objects[index].rotation.setX(x);
108 | if (update)
109 | emit changeSignal("O-" + QString::number(index) + "-x-Rotation", x);
110 | }
111 |
112 | void OnexNStgData::setYRotation(int index, float y, bool update) {
113 | model->objects[index].rotation.setY(y);
114 | if (update)
115 | emit changeSignal("O-" + QString::number(index) + "-y-Rotation", y);
116 | }
117 |
118 | void OnexNStgData::setZRotation(int index, float z, bool update) {
119 | model->objects[index].rotation.setZ(z);
120 | if (update)
121 | emit changeSignal("O-" + QString::number(index) + "-z-Rotation", z);
122 | }
123 |
124 | void OnexNStgData::setWRotation(int index, float w, bool update) {
125 | model->objects[index].rotation.setW(w);
126 | if (update)
127 | emit changeSignal("O-" + QString::number(index) + "-w-Rotation", w);
128 | }
129 |
130 | void OnexNStgData::setXScale(int index, float x, bool update) {
131 | model->objects[index].scale.setX(x);
132 | if (update)
133 | emit changeSignal("O-" + QString::number(index) + "-x-Scale", x);
134 | }
135 |
136 | void OnexNStgData::setYScale(int index, float y, bool update) {
137 | model->objects[index].scale.setY(y);
138 | if (update)
139 | emit changeSignal("O-" + QString::number(index) + "-y-Scale", y);
140 | }
141 |
142 | void OnexNStgData::setZScale(int index, float z, bool update) {
143 | model->objects[index].scale.setZ(z);
144 | if (update)
145 | emit changeSignal("O-" + QString::number(index) + "-z-Scale", z);
146 | }
147 |
148 | void OnexNStgData::setTexture(int index, int texture, bool update) {
149 | model->groups[index].texture = texture;
150 | if (update)
151 | emit changeSignal("Texture-" + QString::number(index), texture);
152 | }
153 |
154 | void OnexNStgData::setUVScale(float scale, bool update) {
155 | model->uvScale = scale;
156 | if (update)
157 | emit changeSignal("UV-Scale", scale);
158 | }
159 |
160 | FileInfo *OnexNStgData::generateInfos() {
161 | FileInfo *infos = OnexTreeZlibItem::generateInfos();
162 | if (hasParent()) {
163 | for (int i = 0; i < model->objects.size(); i++) {
164 | connect(infos->addFloatLineEdit("UV-Scale", model->uvScale),
165 | &QLineEdit::textChanged, [=](const QString &value) { setUVScale(value.toFloat()); });
166 | connect(infos->addFloatLineEdit("O-" + QString::number(i) + "-x-Position", model->objects[i].position.x()),
167 | &QLineEdit::textChanged, [=](const QString &value) { setXPosition(i, value.toFloat()); });
168 | connect(infos->addFloatLineEdit("O-" + QString::number(i) + "-y-Position", model->objects[i].position.y()),
169 | &QLineEdit::textChanged, [=](const QString &value) { setYPosition(i, value.toFloat()); });
170 | connect(infos->addFloatLineEdit("O-" + QString::number(i) + "-z-Position", model->objects[i].position.z()),
171 | &QLineEdit::textChanged, [=](const QString &value) { setZPosition(i, value.toFloat()); });
172 | connect(infos->addFloatLineEdit("O-" + QString::number(i) + "-x-Rotation", model->objects[i].rotation.x()),
173 | &QLineEdit::textChanged, [=](const QString &value) { setXRotation(i, value.toFloat()); });
174 | connect(infos->addFloatLineEdit("O-" + QString::number(i) + "-y-Rotation", model->objects[i].rotation.y()),
175 | &QLineEdit::textChanged, [=](const QString &value) { setYRotation(i, value.toFloat()); });
176 | connect(infos->addFloatLineEdit("O-" + QString::number(i) + "-z-Rotation", model->objects[i].rotation.z()),
177 | &QLineEdit::textChanged, [=](const QString &value) { setZRotation(i, value.toFloat()); });
178 | connect(infos->addFloatLineEdit("O-" + QString::number(i) + "-w-Rotation", model->objects[i].rotation.w()),
179 | &QLineEdit::textChanged, [=](const QString &value) { setWRotation(i, value.toFloat()); });
180 | connect(infos->addFloatLineEdit("O-" + QString::number(i) + "-x-Scale", model->objects[i].scale.x()),
181 | &QLineEdit::textChanged, [=](const QString &value) { setXScale(i, value.toFloat()); });
182 | connect(infos->addFloatLineEdit("O-" + QString::number(i) + "-y-Scale", model->objects[i].scale.y()),
183 | &QLineEdit::textChanged, [=](const QString &value) { setYScale(i, value.toFloat()); });
184 | connect(infos->addFloatLineEdit("O-" + QString::number(i) + "-z-Scale", model->objects[i].scale.z()),
185 | &QLineEdit::textChanged, [=](const QString &value) { setZScale(i, value.toFloat()); });
186 | }
187 | for (int i = 0; i < model->groups.size(); i++) {
188 | connect(infos->addIntLineEdit("Texture-" + QString::number(i), model->groups[i].texture),
189 | &QLineEdit::textChanged, [=](const QString &newValue) { setTexture(i, newValue.toInt()); });
190 | }
191 | }
192 |
193 | return infos;
194 | }
195 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNStgData.h:
--------------------------------------------------------------------------------
1 | #ifndef ONEXNSTGDATA_H
2 | #define ONEXNSTGDATA_H
3 |
4 | #include "../../Openers/NosZlibOpener.h"
5 | #include "OnexTreeZlibItem.h"
6 | #include "../../Converters/NosModelConverter.h"
7 | #include "../../Converters/ObjConverter.h"
8 | #include
9 | #include
10 |
11 | class OnexNStgData : public OnexTreeZlibItem {
12 | Q_OBJECT
13 | public:
14 | OnexNStgData(QString name, QByteArray content, NosZlibOpener *opener, int id = -1,
15 | int creationDate = 0, bool compressed = false);
16 | OnexNStgData(QJsonObject jo, NosZlibOpener *opener, const QString &directory);
17 | ~OnexNStgData() override;
18 | QWidget *getPreview() override;
19 | QByteArray getContent() override;
20 | int saveAsFile(const QString &path, QByteArray content = QByteArray()) override;
21 | QString getExportExtension() override;
22 |
23 | private slots:
24 | int afterReplace(QByteArray content) override;
25 | void setXPosition(int index, float x, bool update = false);
26 | void setYPosition(int index, float y, bool update = false);
27 | void setZPosition(int index, float z, bool update = false);
28 | void setXRotation(int index, float x, bool update = false);
29 | void setYRotation(int index, float y, bool update = false);
30 | void setZRotation(int index, float z, bool update = false);
31 | void setWRotation(int index, float w, bool update = false);
32 | void setXScale(int index, float x, bool update = false);
33 | void setYScale(int index, float y, bool update = false);
34 | void setZScale(int index, float z, bool update = false);
35 | void setTexture(int index, int texture, bool update = false);
36 | void setUVScale(float scale, bool update = false);
37 | signals:
38 | void replaceSignal(Model *newModel);
39 | protected:
40 | static ObjConverter objConverter;
41 | static NosModelConverter nosModelConverter;
42 | Model *model;
43 | FileInfo *generateInfos() override;
44 | };
45 |
46 | #endif // ONEXNSTGDATA_H
47 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNStpData.cpp:
--------------------------------------------------------------------------------
1 | #include "OnexNStpData.h"
2 | #include "OnexNStpMipMap.h"
3 |
4 | OnexNStpData::OnexNStpData(const QString &name, QByteArray content, NosZlibOpener *opener,
5 |
6 | int id, int creationDate, bool compressed)
7 | : OnexTreeImage(name, opener, content, id, creationDate, compressed) {
8 | if (content.isEmpty())
9 | this->content = QByteArrayLiteral("\x00\x00\x00\x00\x00\x00\x00\x00");
10 | if (id == -1)
11 | return;
12 | if (getFileAmount() > 0)
13 | generateMipMap(true);
14 | }
15 |
16 | OnexNStpData::OnexNStpData(QJsonObject jo, NosZlibOpener *opener, const QString &directory) : OnexTreeImage(jo["ID"].toString(), opener) {
17 | this->content = QByteArrayLiteral("\x00\x00\x00\x00\x00\x00\x00\x00");
18 | setId(jo["ID"].toInt(), true);
19 | setCreationDate(jo["Date"].toString(), true);
20 | setCompressed(jo["isCompressed"].toBool(), true);
21 | setFormat(jo["Format"].toInt());
22 | setSmoothScaling(jo["SmoothScaling"].toBool(), true);
23 | setUnknownValue(jo["Unknown"].toBool(), true);
24 | bool mip = jo["MipMap"].toBool();
25 |
26 | if (mip) {
27 | int amount = 0;
28 | int x = getResolution().x;
29 | while (x >= 2) {
30 | x /= 2;
31 | amount++;
32 | }
33 | setFileAmount(amount);
34 | }
35 |
36 | if (!jo["path"].toString().isEmpty())
37 | onReplace(directory + jo["path"].toString());
38 |
39 | if (getFileAmount() > 0) {
40 | ImageResolution res = getResolution();
41 | int format = getFormat();
42 | for (auto &&child : jo["content"].toArray()) {
43 | OnexTreeItem *item = new OnexNStpMipMap(name + "_" + QString::number(res.x) + "x" + QString::number(res.y),
44 | QByteArray(), res.x, res.y, format, (NosZlibOpener *) opener, id,
45 | creationDate, compressed);
46 | this->addChild(item);
47 | item->onReplace(directory + child.toObject()["path"].toString());
48 | res.x /= 2;
49 | res.y /= 2;
50 | }
51 | if (childCount() == 0)
52 | generateMipMap(true);
53 | }
54 |
55 | }
56 |
57 | OnexNStpData::~OnexNStpData() = default;
58 |
59 | QByteArray OnexNStpData::getContent() {
60 | int amount = childCount();
61 | if (!hasParent() || amount <= 0)
62 | return content;
63 | QByteArray contentArray = content.mid(0, 8);
64 | for (int i = 0; i < amount; i++) {
65 | auto *currentItem = dynamic_cast(this->child(i));
66 | contentArray.push_back(currentItem->getContent());
67 | }
68 | this->content = contentArray;
69 | return content;
70 | }
71 |
72 | QImage OnexNStpData::getImage() {
73 | if (childCount() > 0)
74 | return dynamic_cast(this->child(0))->getImage();
75 | ImageResolution resolution = this->getResolution();
76 | int format = this->getFormat();
77 | if (format == 0)
78 | return imageConverter->convertGBAR4444(content, resolution.x, resolution.y, 8);
79 | else if (format == 1)
80 | return imageConverter->convertARGB555(content, resolution.x, resolution.y, 8);
81 | else if (format == 2)
82 | return imageConverter->convertBGRA8888(content, resolution.x, resolution.y, 8);
83 | else if (format == 3 || format == 4)
84 | return imageConverter->convertGrayscale(content, resolution.x, resolution.y, 8);
85 | else {
86 | qDebug().noquote().nospace() << "Unknown format! (" << format << ")";
87 | return QImage(resolution.x, resolution.y, QImage::Format_Invalid);
88 | }
89 | }
90 |
91 | ImageResolution OnexNStpData::getResolution() {
92 | int x = opener->getLittleEndianConverter()->fromShort(content.mid(0, 2));
93 | int y = opener->getLittleEndianConverter()->fromShort(content.mid(2, 2));
94 | return ImageResolution{x, y};
95 | }
96 |
97 | int OnexNStpData::getFormat() {
98 | return content.at(4);
99 | }
100 |
101 | bool OnexNStpData::getSmoothScaling() {
102 | return content.at(5);
103 | }
104 |
105 | bool OnexNStpData::getUnknownValue() {
106 | return content.at(6);
107 | }
108 |
109 | int OnexNStpData::getFileAmount() {
110 | return content.at(7);
111 | }
112 |
113 | int OnexNStpData::afterReplace(QImage image) {
114 | if (image.isNull()) {
115 | emit OnexTreeImage::replaceSignal(this->getImage());
116 | return 0;
117 | }
118 |
119 | qDebug() << getFileAmount();
120 | int format = this->getFormat();
121 | if (format < 0 || format > 4) {
122 | QMessageBox::critical(nullptr, "Woops", "Format of " + name + "is not supported!");
123 | return 0;
124 | }
125 | QByteArray newContent;
126 | newContent.push_back(opener->getLittleEndianConverter()->toShort(image.width()));
127 | newContent.push_back(opener->getLittleEndianConverter()->toShort(image.height()));
128 | newContent.push_back(content.mid(4, 4));
129 | if (format == 0)
130 | newContent.push_back(imageConverter->toGBAR4444(image));
131 | else if (format == 1)
132 | newContent.push_back(imageConverter->toARGB555(image));
133 | else if (format == 2)
134 | newContent.push_back(imageConverter->toBGRA8888(image));
135 | else if (format == 3 || format == 4)
136 | newContent.push_back(imageConverter->toGrayscale(image));
137 | setContent(newContent);
138 | setWidth(image.width(), true);
139 | setHeight(image.height(), true);
140 | qDebug() << getFileAmount();
141 | generateMipMap(getFileAmount() != 0);
142 |
143 |
144 | emit OnexTreeImage::replaceSignal(this->getImage());
145 | emit replaceInfo(generateInfos());
146 | return 1;
147 | }
148 |
149 | void OnexNStpData::setWidth(int width, bool update) {
150 | content.replace(0, 2, opener->getLittleEndianConverter()->toShort(width));
151 | if (update)
152 | emit changeSignal("Width", width);
153 | }
154 |
155 | void OnexNStpData::setHeight(int height, bool update) {
156 | content.replace(2, 2, opener->getLittleEndianConverter()->toShort(height));
157 | if (update)
158 | emit changeSignal("Height", height);
159 | }
160 |
161 | void OnexNStpData::setFormat(uint8_t format, bool update) {
162 | content[4] = format;
163 | if (update)
164 | emit changeSignal("Format", format);
165 | for (int i = 0; i < childCount(); i++) {
166 | auto *item = static_cast(child(i));
167 | item->setFormat(format, update);
168 | }
169 | }
170 |
171 | void OnexNStpData::setSmoothScaling(bool smooth, bool update) {
172 | content[5] = smooth;
173 | if (update)
174 | emit changeSignal("SmoothScaling", smooth);
175 | }
176 |
177 | void OnexNStpData::setUnknownValue(bool unkown, bool update) {
178 | content[6] = unkown;
179 | if (update)
180 | emit changeSignal("Unknown", unkown);
181 | }
182 |
183 | void OnexNStpData::setFileAmount(uint8_t value, bool update) {
184 | content[7] = value;
185 | if (update)
186 | emit changeSignal("MipMap", value != 0);
187 | }
188 |
189 | FileInfo *OnexNStpData::generateInfos() {
190 | FileInfo *infos = OnexTreeImage::generateInfos();
191 | if (hasParent()) {
192 | const QStringList formats = {"ARGB4444", "ARGB1555 ", "ARGB8888", "Grayscale_1", "Grayscale_2"};
193 | connect(infos->addSelection("Format", formats, getFormat()), QOverload::of(&QComboBox::currentIndexChanged),
194 | [=](const int &value) { setFormat(value); });
195 | connect(infos->addCheckBox("SmoothScaling", getSmoothScaling()), &QCheckBox::clicked,
196 | [=](const bool value) { setSmoothScaling(value); });
197 | connect(infos->addCheckBox("Unknown", getUnknownValue()), &QCheckBox::clicked,
198 | [=](const bool value) { setUnknownValue(value); });
199 | connect(infos->addCheckBox("MipMap", getFileAmount() != 0), &QCheckBox::clicked,
200 | [=](const bool value) { generateMipMap(value); });
201 | }
202 | return infos;
203 | }
204 |
205 | void OnexNStpData::generateMipMap(bool generate) {
206 | this->takeChildren();
207 |
208 | if (!generate) {
209 | setFileAmount(0);
210 | return;
211 | }
212 |
213 | int amount = 0;
214 | int x = getResolution().x;
215 | while (x >= 2) {
216 | x /= 2;
217 | amount++;
218 | }
219 |
220 | ImageResolution res = getResolution();
221 | int format = getFormat();
222 | int offset = 8;
223 | for (int i = 0; i < amount; i++) {
224 | int nextOffset = offset;
225 | if (format == 0 || format == 1)
226 | nextOffset += res.x * res.y * 2;
227 | else if (format == 2)
228 | nextOffset += res.x * res.y * 4;
229 | else
230 | nextOffset += res.x * res.y;
231 |
232 | if (nextOffset > content.size()) {
233 | QImage subImage = getImage();
234 | subImage = subImage.scaled(res.x, res.y, Qt::KeepAspectRatio, Qt::SmoothTransformation);
235 | QByteArray subContent;
236 | if (format == 0)
237 | subContent = imageConverter->toGBAR4444(subImage);
238 | else if (format == 1)
239 | subContent = imageConverter->toARGB555(subImage);
240 | else if (format == 2)
241 | subContent = imageConverter->toBGRA8888(subImage);
242 | else if (format == 3 || format == 4)
243 | subContent = imageConverter->toGrayscale(subImage);
244 |
245 | this->addChild(new OnexNStpMipMap(name + "_" + QString::number(res.x) + "x" + QString::number(res.y),
246 | subContent, res.x, res.y, format, (NosZlibOpener *) opener, id, creationDate, compressed));
247 | } else {
248 | this->addChild(new OnexNStpMipMap(name + "_" + QString::number(res.x) + "x" + QString::number(res.y),
249 | content.mid(offset, nextOffset - offset), res.x, res.y, format, (NosZlibOpener *) opener, id,
250 | creationDate, compressed));
251 | }
252 | offset = nextOffset;
253 | res.x /= 2;
254 | res.y /= 2;
255 | }
256 |
257 | setFileAmount(amount);
258 | }
259 |
260 | void OnexNStpData::setName(QString name) {
261 | OnexTreeZlibItem::setName(name);
262 | QList childList = takeChildren();
263 | for (int i = 0; i < childList.size(); i++) {
264 | auto *item = static_cast(childList.at(i));
265 | item->OnexTreeItem::setName(name + "_" + QString::number(item->getWidth()) + "x" + QString::number(item->getHeight()));
266 | }
267 | addChildren(childList);
268 | }
269 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNStpData.h:
--------------------------------------------------------------------------------
1 | #ifndef ONEXNSTPDATA_H
2 | #define ONEXNSTPDATA_H
3 |
4 | #include "OnexTreeImage.h"
5 |
6 | class OnexNStpData : public OnexTreeImage {
7 | Q_OBJECT
8 | public:
9 | OnexNStpData(const QString &name, QByteArray content, NosZlibOpener *opener,
10 | int id = -1, int creationDate = 0, bool compressed = false);
11 | OnexNStpData(QJsonObject jo, NosZlibOpener *opener, const QString &directory);
12 | ~OnexNStpData() override;
13 | QByteArray getContent() override;
14 | QImage getImage() override;
15 | ImageResolution getResolution() override;
16 | int getFormat();
17 | bool getSmoothScaling();
18 | bool getUnknownValue();
19 | int getFileAmount();
20 | public slots:
21 | void setName(QString name);
22 | int afterReplace(QImage image) override;
23 | void setWidth(int width, bool update = false) override;
24 | void setHeight(int height, bool update = false) override;
25 | void setFormat(uint8_t format, bool update = false);
26 | void setSmoothScaling(bool smooth, bool update = false);
27 | void setUnknownValue(bool unkown, bool update = false);
28 | void setFileAmount(uint8_t format, bool update = false);
29 | protected:
30 | FileInfo *generateInfos() override;
31 | void generateMipMap(bool generate);
32 | };
33 |
34 | #endif // ONEXNSTPDATA_H
35 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNStpMipMap.cpp:
--------------------------------------------------------------------------------
1 | #include "OnexNStpMipMap.h"
2 | #include "OnexNStpData.h"
3 |
4 | OnexNStpMipMap::OnexNStpMipMap(QString name, QByteArray content, int width, int height, int format, NosZlibOpener *opener,
5 | int id, int creationDate, bool compressed)
6 | : OnexTreeImage(name, opener, content, id, creationDate, compressed), width(width), height(height), format(format) {
7 | setFlags(this->flags() & (~Qt::ItemIsEditable));
8 | }
9 |
10 | OnexNStpMipMap::~OnexNStpMipMap() = default;
11 |
12 | int OnexNStpMipMap::onReplace(QString directory) {
13 | QString path = getCorrectPath(directory);
14 | QFile file(path);
15 | if (file.open(QIODevice::ReadOnly))
16 | return OnexTreeImage::afterReplace(file.readAll());
17 | else {
18 | auto *parent = static_cast(QTreeWidgetItem::parent());
19 | QString parentPath = path.mid(0, path.lastIndexOf("/") + 1) + parent->getName() + ".png";
20 | QFile pfile(parentPath);
21 | if (pfile.open(QIODevice::ReadOnly))
22 | pfile.close();
23 | else
24 | QMessageBox::critical(nullptr, "Woops", "Couldn't open " + path);
25 | return 0;
26 | }
27 | }
28 |
29 | QImage OnexNStpMipMap::getImage() {
30 | ImageResolution resolution = this->getResolution();
31 | int format = this->getFormat();
32 | if (format == 0)
33 | return imageConverter->convertGBAR4444(content, resolution.x, resolution.y);
34 | else if (format == 1)
35 | return imageConverter->convertARGB555(content, resolution.x, resolution.y);
36 | else if (format == 2)
37 | return imageConverter->convertBGRA8888(content, resolution.x, resolution.y);
38 | else if (format == 3 || format == 4)
39 | return imageConverter->convertGrayscale(content, resolution.x, resolution.y);
40 | else {
41 | qDebug().noquote().nospace() << "Unknown format! (" << format << ")";
42 | return QImage(resolution.x, resolution.y, QImage::Format_Invalid);
43 | }
44 | }
45 |
46 | ImageResolution OnexNStpMipMap::getResolution() {
47 | return ImageResolution{this->width, this->height};
48 | }
49 |
50 | int OnexNStpMipMap::getWidth() {
51 | return width;
52 | }
53 |
54 | int OnexNStpMipMap::getHeight() {
55 | return height;
56 | }
57 |
58 | int OnexNStpMipMap::getFormat() {
59 | return format;
60 | }
61 |
62 | int OnexNStpMipMap::afterReplace(QImage image) {
63 | int format = this->getFormat();
64 | if (format < 0 || format > 4) {
65 | QMessageBox::critical(nullptr, "Woops", "Format of " + name + "is not supported!");
66 | return 0;
67 | }
68 | QByteArray newContent;
69 | if (format == 0)
70 | newContent.push_back(imageConverter->toGBAR4444(image));
71 | else if (format == 1)
72 | newContent.push_back(imageConverter->toARGB555(image));
73 | else if (format == 2)
74 | newContent.push_back(imageConverter->toBGRA8888(image));
75 | else if (format == 3 || format == 4)
76 | newContent.push_back(imageConverter->toGrayscale(image));
77 | setContent(newContent);
78 | setWidth(image.width(), true);
79 | setHeight(image.height(), true);
80 |
81 | emit OnexTreeImage::replaceSignal(this->getImage());
82 | emit replaceInfo(generateInfos());
83 | return 1;
84 | }
85 |
86 | void OnexNStpMipMap::setWidth(int width, bool update) {
87 | this->width = width;
88 | if (update)
89 | emit changeSignal("Width", width);
90 | }
91 |
92 | void OnexNStpMipMap::setHeight(int height, bool update) {
93 | this->height = height;
94 | if (update)
95 | emit changeSignal("Height", height);
96 | }
97 |
98 | void OnexNStpMipMap::setFormat(uint8_t format, bool update) {
99 | this->format = format;
100 | if (update)
101 | emit changeSignal("Format", format);
102 | }
103 |
104 | FileInfo *OnexNStpMipMap::generateInfos() {
105 | return OnexTreeItem::generateInfos();
106 | }
107 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexNStpMipMap.h:
--------------------------------------------------------------------------------
1 | #ifndef ONEXNSTPMAPMAP_H
2 | #define ONEXNSTPMAPMAP_H
3 |
4 | #include "OnexTreeImage.h"
5 |
6 | class OnexNStpMipMap : public OnexTreeImage {
7 | Q_OBJECT
8 | public:
9 | OnexNStpMipMap(QString name, QByteArray content, int width, int height, int format, NosZlibOpener *opener,
10 | int id = -1, int creationDate = 0, bool compressed = false);
11 | OnexNStpMipMap(QJsonObject jo, NosZlibOpener *opener, const QString &directory);
12 | ~OnexNStpMipMap() override;
13 | int onReplace(QString directory) override;
14 | QImage getImage() override;
15 | ImageResolution getResolution() override;
16 | int getWidth();
17 | int getHeight();
18 | int getFormat();
19 | public slots:
20 | int afterReplace(QImage image) override;
21 | void setWidth(int width, bool update = false) override;
22 | void setHeight(int height, bool update = false) override;
23 | void setFormat(uint8_t format, bool update = false);
24 | protected:
25 | int width;
26 | int height;
27 | int format;
28 | FileInfo *generateInfos() override;
29 | };
30 |
31 | #endif // ONEXNSTPMAPMAP_H
32 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexTreeImage.cpp:
--------------------------------------------------------------------------------
1 | #include "OnexTreeImage.h"
2 |
3 | ImageConverter *OnexTreeImage::imageConverter;
4 |
5 | OnexTreeImage::OnexTreeImage(QString name, NosZlibOpener *opener, QByteArray content, int id, int creationDate, bool compressed)
6 | : OnexTreeZlibItem(name, opener, content, id, creationDate, compressed) {
7 | if (imageConverter == nullptr)
8 | imageConverter = new ImageConverter(opener->getLittleEndianConverter());
9 | }
10 |
11 | QWidget *OnexTreeImage::getPreview() {
12 | if (!hasParent())
13 | return nullptr;
14 | auto *imagePreview = new SingleImagePreview(this->getImage());
15 | connect(this, SIGNAL(replaceSignal(QImage)), imagePreview, SLOT(onReplaced(QImage)));
16 | return imagePreview;
17 | }
18 |
19 | bool OnexTreeImage::isEmpty() {
20 | ImageResolution ir = getResolution();
21 | return ir.x == 0 && ir.y == 0;
22 | }
23 |
24 | QString OnexTreeImage::getExportExtension() {
25 | return ".png";
26 | }
27 |
28 | int OnexTreeImage::onReplaceRaw(QString directory) {
29 | int ret = OnexTreeItem::onReplaceRaw(directory);
30 | emit replaceSignal(getImage());
31 | emit replaceInfo(generateInfos());
32 | return ret;
33 | }
34 |
35 | int OnexTreeImage::afterReplace(QByteArray content) {
36 | if (content.isEmpty() && childCount() > 0)
37 | return afterReplace(QImage());
38 | QImage image = QImage::fromData(content);
39 | if (image.isNull() && this->getResolution().x != 0 && this->getResolution().y != 0) {
40 | QMessageBox::critical(nullptr, "Woops", "Couldn't read image " + name);
41 | return 0;
42 | }
43 | if (!hasGoodResolution(image.width(), image.height()) && !(this->getResolution().x == 0 && this->getResolution().y == 0)) {
44 | QMessageBox::StandardButton reply = QMessageBox::question(
45 | nullptr, "Resolution changed",
46 | "The resolution of the image " + name + " doesn't match!\nDo you want to replace it anyway?");
47 | if (reply == QMessageBox::No)
48 | return 0;
49 | }
50 | return afterReplace(image);
51 | }
52 |
53 | FileInfo *OnexTreeImage::generateInfos() {
54 | FileInfo *infos = OnexTreeZlibItem::generateInfos();
55 | if (hasParent()) {
56 | ImageResolution ir = getResolution();
57 | infos->addIntLineEdit("Width", ir.x)->setDisabled(true);
58 | infos->addIntLineEdit("Height", ir.y)->setDisabled(true);
59 | }
60 | return infos;
61 | }
62 |
63 | int OnexTreeImage::saveAsFile(const QString &path, QByteArray content) {
64 | if (this->getImage().save(path, "PNG", 100))
65 | return 1;
66 | else if (this->getResolution().x == 0 || this->getResolution().y == 0) {
67 | QFile file(path);
68 | if (file.open(QIODevice::WriteOnly)) {
69 | file.close();
70 | return 1;
71 | }
72 | }
73 | return 0;
74 | }
75 |
76 | bool OnexTreeImage::hasGoodResolution(int x, int y) {
77 | ImageResolution currentResolution = this->getResolution();
78 | return (x == currentResolution.x && y == currentResolution.y);
79 | }
80 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexTreeImage.h:
--------------------------------------------------------------------------------
1 | #ifndef ONEXTREEIMAGE_H
2 | #define ONEXTREEIMAGE_H
3 |
4 | #include "OnexTreeZlibItem.h"
5 | #include "../Previews/SingleImagePreview.h"
6 | #include "../../Converters/ImageConverter.h"
7 |
8 | struct ImageResolution {
9 | int x;
10 | int y;
11 | };
12 |
13 | class OnexTreeImage : public OnexTreeZlibItem {
14 | Q_OBJECT
15 | public:
16 | OnexTreeImage(QString name, NosZlibOpener *opener, QByteArray content = QByteArray(), int id = -1, int creationDate = 0, bool compressed = false);
17 | QWidget *getPreview() override;
18 | bool isEmpty() override;
19 | QString getExportExtension() override;
20 | virtual QImage getImage() = 0;
21 | virtual ImageResolution getResolution() = 0;
22 | public slots:
23 | int onReplaceRaw(QString directory) override;
24 | int afterReplace(QByteArray content) override;
25 | virtual int afterReplace(QImage image) = 0;
26 | virtual void setWidth(int width, bool update = false) = 0;
27 | virtual void setHeight(int height, bool update = false) = 0;
28 | signals:
29 | void replaceSignal(QImage newImage);
30 | protected:
31 | static ImageConverter *imageConverter;
32 | FileInfo *generateInfos() override;
33 | int saveAsFile(const QString &path, QByteArray content = QByteArray()) override;
34 | virtual bool hasGoodResolution(int x, int y);
35 | };
36 |
37 | #endif // ONEXTREEIMAGE_H
38 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexTreeText.cpp:
--------------------------------------------------------------------------------
1 | #include "OnexTreeText.h"
2 | #include "../../Openers/NosTextOpener.h"
3 | #include "../Previews/SingleTextFilePreview.h"
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | OnexTreeText::OnexTreeText(const QString &name, NosTextOpener *opener, int fileNumber, int isCompressed, QByteArray content)
10 | : OnexTreeItem(name, opener, content), fileNumber(fileNumber), isCompressed(isCompressed) {
11 | }
12 |
13 | OnexTreeText::OnexTreeText(const QString &name, NosTextOpener *opener, const QString &time) : OnexTreeItem(name, opener, QByteArray()), fileNumber(0),
14 | isCompressed(0), time(time) {
15 | }
16 |
17 | OnexTreeText::OnexTreeText(QJsonObject jo, NosTextOpener *opener, const QString &directory) : OnexTreeItem(jo["Name"].toString(), opener) {
18 | setFileNumber(jo["Filenumber"].toInt(), true);
19 | setIsCompressed(jo["isCompressed"].toBool());
20 | onReplace(directory + jo["path"].toString());
21 | }
22 |
23 | OnexTreeText::~OnexTreeText() = default;
24 |
25 | QWidget *OnexTreeText::getPreview() {
26 | if (!hasParent())
27 | return nullptr;
28 | auto *textPreview = new SingleTextFilePreview(content, getEncoding());
29 | connect(this, SIGNAL(replaceSignal(QByteArray)), textPreview, SLOT(onReplaced(QByteArray)));
30 | return textPreview;
31 | }
32 |
33 | QString OnexTreeText::getExportExtension() {
34 | QStringList split = name.split(".", QString::SplitBehavior::SkipEmptyParts);
35 | if (split.size() > 1)
36 | return split.at(split.size() - 1);
37 | return "";
38 | }
39 |
40 | int OnexTreeText::getFileNumber() const {
41 | return fileNumber;
42 | }
43 |
44 | int OnexTreeText::getIsCompressed() const {
45 | return isCompressed;
46 | }
47 |
48 | int OnexTreeText::afterReplace(QByteArray content) {
49 | this->setContent(content);
50 | emit replaceSignal(this->getContent());
51 | emit replaceInfo(generateInfos());
52 | return 1;
53 | }
54 |
55 | void OnexTreeText::setFileNumber(int fileNumber, bool update) {
56 | this->fileNumber = fileNumber;
57 | if (update)
58 | emit changeSignal("Filenumber", fileNumber);
59 | }
60 |
61 | void OnexTreeText::setIsCompressed(bool isCompressed, bool update) {
62 | this->isCompressed = isCompressed;
63 | if (update)
64 | emit changeSignal("isCompressed", isCompressed);
65 | }
66 |
67 | void OnexTreeText::setTime(QString time, bool update) {
68 | this->time = time;
69 | if (update)
70 | emit changeSignal("Last Edit", time);
71 | }
72 |
73 | FileInfo *OnexTreeText::generateInfos() {
74 | FileInfo *infos = OnexTreeItem::generateInfos();
75 | if (!hasParent()) {
76 | infos->addStringLineEdit("Last Edit", time)->setEnabled(false);
77 | } else {
78 | infos = OnexTreeItem::generateInfos();
79 | connect(infos->addStringLineEdit("Name", text(0)), &QLineEdit::textChanged,
80 | [=](const QString &value) { setText(0, value); });
81 | connect(infos->addIntLineEdit("Filenumber", getFileNumber()), &QLineEdit::textChanged,
82 | [=](const QString &value) { setFileNumber(value.toInt()); });
83 | connect(infos->addCheckBox("isCompressed", getIsCompressed()), &QCheckBox::clicked, [=](const bool value) { setIsCompressed(value); });
84 | }
85 | connect(this, SIGNAL(changeSignal(QString, QString)), infos, SLOT(update(QString, QString)));
86 | connect(this, SIGNAL(changeSignal(QString, int)), infos, SLOT(update(QString, int)));
87 | connect(this, SIGNAL(changeSignal(QString, float)), infos, SLOT(update(QString, float)));
88 | connect(this, SIGNAL(changeSignal(QString, bool)), infos, SLOT(update(QString, bool)));
89 | return infos;
90 | }
91 |
92 | QString OnexTreeText::getEncoding() {
93 | QRegExp rx = QRegExp("^_code_\\w{2}_\\w*\\.txt$");
94 | int match = rx.indexIn(getName());
95 | if (match == -1)
96 | return "EUC-KR";
97 | QString region = getName().mid(6, 2);
98 | if (region == "de" || region == "pl" || region == "it" || region == "cz")
99 | return "Windows-1250";
100 | else if (region == "ru")
101 | return "Windows-1251";
102 | else if (region == "uk" || region == "fr" || region == "es")
103 | return "Windows-1252";
104 | else if (region == "tr")
105 | return "Windows-1254";
106 | else if (region == "hk" || region == "tw")
107 | return "Big5";
108 | return "Windows-1250";
109 | }
110 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexTreeText.h:
--------------------------------------------------------------------------------
1 | #ifndef ONEXTREETEXT_H
2 | #define ONEXTREETEXT_H
3 |
4 | #include "../OnexTreeItem.h"
5 |
6 | class NosTextOpener;
7 |
8 | class OnexTreeText : public OnexTreeItem {
9 | Q_OBJECT
10 | public:
11 | OnexTreeText(const QString &name, NosTextOpener *opener, int fileNumber = 0, int isCompressed = 0,
12 | QByteArray content = QByteArray());
13 | OnexTreeText(const QString &name, NosTextOpener *opener, const QString &time);
14 | OnexTreeText(QJsonObject jo, NosTextOpener *opener, const QString &directory);
15 | ~OnexTreeText() override;
16 | QWidget *getPreview() override;
17 | QString getExportExtension() override;
18 | int getFileNumber() const;
19 | int getIsCompressed() const;
20 | public slots:
21 | int afterReplace(QByteArray content) override;
22 | void setFileNumber(int number, bool update = false);
23 | void setIsCompressed(bool isCompressed, bool update = false);
24 | void setTime(QString time, bool update = false);
25 | signals:
26 | void replaceSignal(QByteArray text);
27 | protected:
28 | int fileNumber;
29 | int isCompressed;
30 | QString time;
31 | FileInfo *generateInfos() override;
32 | QString getEncoding();
33 | };
34 |
35 | #endif // ONEXTREETEXT_H
36 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexTreeZlibItem.cpp:
--------------------------------------------------------------------------------
1 | #include "OnexTreeZlibItem.h"
2 | #include
3 |
4 | OnexTreeZlibItem::OnexTreeZlibItem(const QString &name, NosZlibOpener *opener, QByteArray content, int id, int creationDate, bool compressed)
5 | : OnexTreeItem(name, opener, content), id(id), creationDate(creationDate),
6 | compressed(compressed) {
7 | if (creationDate == 0)
8 | setCreationDate(QDate::currentDate().toString("dd/MM/yyyy"), true);
9 | }
10 |
11 |
12 | OnexTreeZlibItem::~OnexTreeZlibItem() = default;
13 |
14 | int OnexTreeZlibItem::getId() {
15 | return id;
16 | }
17 |
18 | int OnexTreeZlibItem::getCreationDate() {
19 | return creationDate;
20 | }
21 |
22 | QString OnexTreeZlibItem::getDateAsString() {
23 | int year = (getCreationDate() & 0xFFFF0000) >> 0x10;
24 | int month = (getCreationDate() & 0xFF00) >> 0x08;
25 | int day = getCreationDate() & 0xFF;
26 | return QString("%1/%2/%3").arg(day, 2, 16, QChar('0')).arg(month, 2, 16, QChar('0')).arg(year, 4, 16, QChar('0'));
27 | }
28 |
29 | bool OnexTreeZlibItem::isCompressed() {
30 | return compressed;
31 | }
32 |
33 | void OnexTreeZlibItem::setName(QString name) {
34 | OnexTreeItem::setName(name);
35 | setId(name.toInt(), true);
36 | }
37 |
38 | void OnexTreeZlibItem::setId(int id, bool update) {
39 | this->id = id;
40 | OnexTreeItem::setName(QString::number(id));
41 | if (update)
42 | emit changeSignal("ID", id);
43 | }
44 |
45 | void OnexTreeZlibItem::setCreationDate(const QString &date, bool update) {
46 | QStringList parts = date.split("/", QString::SplitBehavior::SkipEmptyParts);
47 | if (parts.size() != 3)
48 | this->creationDate = 0;
49 | else {
50 | int year = parts[2].toInt(nullptr, 16) << 0x10;
51 | int month = parts[1].toInt(nullptr, 16) << 0x08;
52 | int day = parts[0].toInt(nullptr, 16);
53 | this->creationDate = year + month + day;
54 | }
55 | if (update)
56 | emit changeSignal("Date", getDateAsString());
57 | }
58 |
59 | void OnexTreeZlibItem::setCompressed(bool compressed, bool update) {
60 | this->compressed = compressed;
61 | if (update)
62 | emit changeSignal("isCompressed", compressed);
63 | }
64 |
65 | FileInfo *OnexTreeZlibItem::generateInfos() {
66 | auto *infos = OnexTreeItem::generateInfos();
67 | if (getId() == -1) {
68 | infos->addStringLineEdit("Header", QString::fromUtf8(getContent(), getContent().size()))->setEnabled(false);
69 | } else {
70 | connect(infos->addIntLineEdit("ID", getId()), &QLineEdit::textChanged,
71 | [=](const QString &value) { setId(value.toInt()); });
72 | connect(infos->addStringLineEdit("Date", getDateAsString()), &QLineEdit::textChanged,
73 | [=](const QString &value) { setCreationDate(value); });
74 | connect(infos->addCheckBox("isCompressed", isCompressed()), &QCheckBox::clicked,
75 | [=](const bool value) { setCompressed(value); });
76 | }
77 | return infos;
78 | }
79 |
--------------------------------------------------------------------------------
/Source/Ui/TreeItems/OnexTreeZlibItem.h:
--------------------------------------------------------------------------------
1 | #ifndef ONEXTREEZLIBITEM_H
2 | #define ONEXTREEZLIBITEM_H
3 |
4 | #include "../../Openers/NosZlibOpener.h"
5 | #include "../OnexTreeItem.h"
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | class OnexTreeZlibItem : public OnexTreeItem {
12 | Q_OBJECT
13 | public:
14 | OnexTreeZlibItem(const QString &name, NosZlibOpener *opener, QByteArray content = QByteArray(), int id = -1, int creationDate = 0, bool compressed = false);
15 | ~OnexTreeZlibItem() override;
16 | int getId();
17 | int getCreationDate();
18 | bool isCompressed();
19 | public slots:
20 | void setName(QString name) override;
21 | virtual void setId(int id, bool update = false);
22 | void setCreationDate(const QString &date, bool update = false);
23 | void setCompressed(bool compressed, bool update = false);
24 | protected:
25 | int id;
26 | int creationDate;
27 | bool compressed;
28 | FileInfo *generateInfos() override;
29 | QString getDateAsString();
30 | };
31 |
32 | #endif // ONEXTREEZLIBITEM_H
33 |
--------------------------------------------------------------------------------
/Source/main.cpp:
--------------------------------------------------------------------------------
1 | #include "MainWindow.h"
2 | #include
3 |
4 | int main(int argc, char *argv[]) {
5 | QApplication a(argc, argv);
6 | MainWindow w;
7 | w.show();
8 | return a.exec();
9 | }
10 |
--------------------------------------------------------------------------------
/Source/mainwindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 1200
10 | 699
11 |
12 |
13 |
14 |
15 | 500
16 | 300
17 |
18 |
19 |
20 | false
21 |
22 |
23 | OnexExplorer [Beta]
24 |
25 |
26 |
27 | :/resources/oxe_icon_trans.ico
28 | :/resources/oxe_icon_trans.ico:/resources/oxe_icon_trans.ico
29 |
30 |
31 |
32 |
33 | -
34 |
35 |
36 |
37 | 224
38 | 16777215
39 |
40 |
41 |
42 | Search... example1;example2;example3
43 |
44 |
45 |
46 | -
47 |
48 |
-
49 |
50 |
51 |
52 | 26
53 | 75
54 | true
55 |
56 |
57 |
58 | +
59 |
60 |
61 |
62 | -
63 |
64 |
65 |
66 | 26
67 | 75
68 | true
69 |
70 |
71 |
72 | -
73 |
74 |
75 |
76 |
77 |
78 | -
79 |
80 |
-
81 |
82 |
83 | <html><head/><body><p><a href="https://discord.gg/t73Ayca"><span
84 | style=" font-size:18pt; font-weight:600; text-decoration: underline;
85 | color:#000000;">Custom Client Modding Discord</span></a></p></body></html>
86 |
87 |
88 |
89 | Qt::AlignCenter
90 |
91 |
92 | true
93 |
94 |
95 |
96 | -
97 |
98 |
99 | <html><head/><body><p><a href="https://ko-fi.com/Pumbaa"><img
100 | src=":/resources/kofi4.png" height="36"/></a></p></body></html>
101 |
102 |
103 |
104 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
105 |
106 |
107 | true
108 |
109 |
110 |
111 |
112 |
113 | -
114 |
115 |
116 |
-
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
125 | -
126 |
127 |
128 |
129 | 0
130 | 0
131 |
132 |
133 |
134 |
135 | 224
136 | 16777215
137 |
138 |
139 |
140 | QAbstractItemView::NoEditTriggers
141 |
142 |
143 | QAbstractItemView::ExtendedSelection
144 |
145 |
146 | false
147 |
148 |
149 | true
150 |
151 |
152 | false
153 |
154 |
155 |
156 | 1
157 |
158 |
159 |
160 |
161 |
162 |
163 |
217 |
218 |
219 | Open
220 |
221 |
222 | Ctrl+O
223 |
224 |
225 |
226 |
227 | true
228 |
229 |
230 | Save as...
231 |
232 |
233 | Ctrl+S
234 |
235 |
236 |
237 |
238 | Close selected item
239 |
240 |
241 | Ctrl+W
242 |
243 |
244 |
245 |
246 | true
247 |
248 |
249 | Export
250 |
251 |
252 | Ctrl+E
253 |
254 |
255 |
256 |
257 | true
258 |
259 |
260 | Help
261 |
262 |
263 |
264 |
265 | Exit
266 |
267 |
268 | Ctrl+Q
269 |
270 |
271 |
272 |
273 | true
274 |
275 |
276 | Close all items
277 |
278 |
279 | Ctrl+Shift+W
280 |
281 |
282 |
283 |
284 | Replace
285 |
286 |
287 | Ctrl+R
288 |
289 |
290 |
291 |
292 | About
293 |
294 |
295 |
296 |
297 | Export to raw
298 |
299 |
300 | Export as raw data
301 |
302 |
303 | Ctrl+Shift+E
304 |
305 |
306 |
307 |
308 | Replace with raw
309 |
310 |
311 | Replace with raw data
312 |
313 |
314 | Ctrl+Shift+R
315 |
316 |
317 |
318 |
319 | true
320 |
321 |
322 | Save
323 |
324 |
325 | Save into original .NOS
326 |
327 |
328 | Ctrl+Shift+S
329 |
330 |
331 |
332 |
333 | Rename
334 |
335 |
336 | Rename an item
337 |
338 |
339 | F2
340 |
341 |
342 |
343 |
344 | Export with config
345 |
346 |
347 | Export content with a config for external usage
348 |
349 |
350 |
351 |
352 | Import from config
353 |
354 |
355 | Import content defined in a Config
356 |
357 |
358 | Ctrl+Shift+O
359 |
360 |
361 |
362 |
363 | Settings
364 |
365 |
366 |
367 |
368 | Add and replace by config
369 |
370 |
371 | Load items that should be added or replaced from a config file
372 |
373 |
374 |
375 |
376 |
377 | treeWidget
378 |
379 |
380 |
381 |
382 |
383 |
384 |
--------------------------------------------------------------------------------
/resources.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | resources/oxe_icon_trans.ico
4 | resources/kofi4.png
5 |
6 |
7 |
--------------------------------------------------------------------------------
/resources/kofi4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pumba98/OnexExplorer/eaee2aa9f0e71b9960da586f425f79e628013021/resources/kofi4.png
--------------------------------------------------------------------------------
/resources/oxe_icon_trans.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Pumba98/OnexExplorer/eaee2aa9f0e71b9960da586f425f79e628013021/resources/oxe_icon_trans.ico
--------------------------------------------------------------------------------