Skip to content

Commit 3689832

Browse files
committed
decrypt - close #6
1 parent c62ca2a commit 3689832

19 files changed

+579
-81
lines changed

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[submodule "quazip"]
22
path = src/quazip/quazip
33
url = ../quazip
4+
[submodule "xxtea-c"]
5+
path = src/xxtea/xxtea-c
6+
url = ../xxtea-c

src/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
add_subdirectory(xxtea)
12
add_subdirectory(quazip)
23
add_subdirectory(librmessentials)
34
add_subdirectory(rmessentials)

src/librmessentials/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ set(LIBRMESS_HEADERS
88
src/rmesongstruct.h
99
src/rmeuncompresser.h
1010
src/rmeutils.h
11+
src/rmecrypt.h
1112
)
1213

1314
set(LIBRMESS_SOURCES
@@ -19,6 +20,7 @@ set(LIBRMESS_SOURCES
1920
src/rmeuncompresser.cpp
2021
src/rmeutils.cpp
2122
src/rmeglobal.cpp
23+
src/rmecrypt.cpp
2224
)
2325

2426
set(LIBRMESS_RESOURCES
@@ -71,6 +73,7 @@ if (Qt6_FOUND)
7173
target_link_libraries(RmEss6
7274
PUBLIC Qt6::Core
7375
PUBLIC Qt6::Network
76+
PRIVATE xxtea
7477
)
7578
if (RMESSENTIALS_USE_QUAZIP)
7679
add_dependencies(RmEss6 quazip)
@@ -139,6 +142,7 @@ if (Qt5_FOUND)
139142
target_link_libraries(RmEss5
140143
PUBLIC Qt5::Core
141144
PUBLIC Qt5::Network
145+
PRIVATE xxtea
142146
)
143147
if (RMESSENTIALS_USE_QUAZIP)
144148
add_dependencies(RmEss5 quazip)

src/librmessentials/src/rmecrypt.cpp

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#include "rmecrypt.h"
2+
3+
#include <QByteArray>
4+
#include <QScopedPointer>
5+
6+
#include <xxtea/xxtea.h>
7+
#include <zlib.h>
8+
9+
QByteArray RmeCrypt::decryptXxteaOnly(const QByteArray &encrypted, const QByteArray &_decryptKey)
10+
{
11+
QByteArray decryptKey = _decryptKey;
12+
decryptKey.resize(16); // TO PREVENT UNDEFINED BEHAVIOR IN xxtea_decrypt
13+
14+
QByteArray encryptedBase64 = QByteArray::fromBase64(encrypted);
15+
size_t outLen = 0;
16+
17+
QScopedPointer<char, QScopedPointerPodDeleter> decrypted(
18+
reinterpret_cast<char *>(xxtea_decrypt(encryptedBase64.constData(), encryptedBase64.length(), decryptKey.constData(), &outLen)));
19+
20+
// Let's assume that outLen != 0......
21+
// There seems to be no way other than deep copy, since decrypted should be freed to prevent memory leak
22+
// how to make the above variable owned by QByteArray without deep copy?
23+
if (decrypted != nullptr)
24+
return QByteArray(decrypted.data(), outLen);
25+
26+
return {};
27+
}
28+
29+
QByteArray RmeCrypt::decryptFull(const QByteArray &encrypted, const QByteArray &decryptKey)
30+
{
31+
QByteArray decryptedXxtea = RmeCrypt::decryptXxteaOnly(encrypted, decryptKey);
32+
33+
if (!decryptedXxtea.isEmpty()) {
34+
QByteArray decryptedBase64 = QByteArray::fromBase64(decryptedXxtea);
35+
36+
QByteArray finalBa;
37+
for (size_t s = 4096; s < 4096 * 4096; s *= 2) {
38+
finalBa.resize(s);
39+
uLongf destLen = uLongf(s - 1);
40+
int err = uncompress(reinterpret_cast<Bytef *>(finalBa.data()), &destLen, reinterpret_cast<const Bytef *>(decryptedBase64.constData()), decryptedBase64.length());
41+
if (err == Z_OK) {
42+
finalBa.resize(destLen);
43+
break;
44+
} else if (err != Z_BUF_ERROR) {
45+
finalBa.clear();
46+
break;
47+
}
48+
}
49+
50+
if (!finalBa.isEmpty())
51+
return finalBa;
52+
}
53+
54+
return {};
55+
}
56+
57+
QByteArray RmeCrypt::encryptXxteaOnly(const QByteArray &original, const QByteArray &_encryptKey)
58+
{
59+
QByteArray encryptKey = _encryptKey;
60+
encryptKey.resize(16); // TO PREVENT UNDEFINED BEHAVIOR IN xxtea_encrypt
61+
size_t outLen = 0;
62+
QScopedPointer<char, QScopedPointerPodDeleter> encrypted(reinterpret_cast<char *>(xxtea_encrypt(original.constData(), original.length(), encryptKey.constData(), &outLen)));
63+
64+
// Let's assume that outLen != 0......
65+
if (encrypted != nullptr) {
66+
QByteArray encryptedBa = QByteArray::fromRawData(encrypted.data(), outLen);
67+
return encryptedBa.toBase64();
68+
}
69+
70+
return {};
71+
}
72+
73+
QByteArray RmeCrypt::encryptFull(const QByteArray &original, const QByteArray &encryptKey)
74+
{
75+
QByteArray initialBa;
76+
uLong s = compressBound(original.length()) + 1;
77+
initialBa.resize(s);
78+
uLongf destLen = uLongf(s - 1);
79+
int err = compress(reinterpret_cast<Bytef *>(initialBa.data()), &destLen, reinterpret_cast<const Bytef *>(original.constData()), original.length());
80+
if (err == Z_OK) {
81+
initialBa.resize(destLen);
82+
return RmeCrypt::encryptXxteaOnly(initialBa.toBase64(), encryptKey);
83+
}
84+
85+
return {};
86+
}

src/librmessentials/src/rmecrypt.h

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#ifndef RMECRYPT_H__INCLUDED
2+
#define RMECRYPT_H__INCLUDED
3+
4+
#include "rmeglobal.h"
5+
6+
#include <QByteArray>
7+
8+
#if 0
9+
class LIBRMESSENTIALS_EXPORT RmeCrypt
10+
#endif
11+
12+
namespace RmeCrypt {
13+
LIBRMESSENTIALS_EXPORT QByteArray decryptXxteaOnly(const QByteArray &encrypted, const QByteArray &decryptKey);
14+
LIBRMESSENTIALS_EXPORT QByteArray decryptFull(const QByteArray &encrypted, const QByteArray &decryptKey);
15+
16+
LIBRMESSENTIALS_EXPORT QByteArray encryptXxteaOnly(const QByteArray &original, const QByteArray &encryptKey);
17+
LIBRMESSENTIALS_EXPORT QByteArray encryptFull(const QByteArray &original, const QByteArray &encryptKey);
18+
}
19+
20+
#endif

src/librmessentials/src/rmeglobal.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ QuaZipUsage RmeQuaZipUsage()
1616
#endif
1717
}
1818

19+
XxteaUsage RmeXxteaUsage()
20+
{
21+
#ifdef RMESSENTIALS_BUILD_STATIC_XXTEA
22+
return XxteaBundled;
23+
#else
24+
return XxteaDynamicLinked;
25+
#endif
26+
}
27+
1928
QVersionNumber RmeVersionNumber()
2029
{
2130
return QVersionNumber::fromString(QStringLiteral(RMEVERSIONNUMBER));

src/librmessentials/src/rmeglobal.h

+9
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,20 @@ enum QuaZipUsage
5454
QuaZipDynamicLinked = 0x12
5555
};
5656

57+
enum XxteaUsage
58+
{
59+
XxteaNotUsed = 0x00,
60+
XxteaUsing = 0x01,
61+
XxteaBundled = 0x11,
62+
XxteaDynamicLinked = 0x12,
63+
};
64+
5765
#ifdef __cplusplus
5866
extern "C" {
5967
#endif
6068
LIBRMESSENTIALS_EXPORT const char *RmeVersion();
6169
LIBRMESSENTIALS_EXPORT enum QuaZipUsage RmeQuaZipUsage();
70+
LIBRMESSENTIALS_EXPORT enum XxteaUsage RmeXxteaUsage();
6271
#ifdef __cplusplus
6372
}
6473
LIBRMESSENTIALS_EXPORT QVersionNumber RmeVersionNumber();

src/librmessentials/src/rmerenamer.cpp

+123-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "rmerenamer.h"
22
#include "rmechart.h"
3+
#include "rmecrypt.h"
34
#include "rmeutils.h"
45

56
#include <QDir>
@@ -13,6 +14,8 @@ using namespace RmeUtils;
1314
class RmeRenamerPrivate
1415
{
1516
public:
17+
bool renameSingleRmp(const QString &oldName, const QString &newName);
18+
1619
bool renameMp3();
1720
bool renameBigPng();
1821
bool renameSmallPng();
@@ -85,6 +88,38 @@ bool RmeRenamer::runToEasy()
8588
return true;
8689
}
8790

91+
bool RmeRenamerPrivate::renameSingleRmp(const QString &oldName, const QString &newName)
92+
{
93+
static const QByteArray rmpKeyPrefix("RMP4TT3RN");
94+
95+
QFileInfo fi(m_dir.absoluteFilePath(oldName));
96+
QFile f(fi.absolutePath());
97+
if (!f.open(QFile::ReadOnly))
98+
return false;
99+
100+
QByteArray arr = f.readAll();
101+
f.close();
102+
103+
QByteArray rmpKeyOld = rmpKeyPrefix + fi.baseName().toLatin1();
104+
rmpKeyOld.resize(16);
105+
QByteArray decryptedArr = RmeCrypt::decryptXxteaOnly(arr, rmpKeyOld);
106+
107+
QFileInfo fiNew(m_dir.absoluteFilePath(newName));
108+
QByteArray rmpKeyNew = rmpKeyPrefix + fiNew.baseName().toLatin1();
109+
rmpKeyNew.resize(16);
110+
QByteArray encryptedArr = RmeCrypt::encryptXxteaOnly(decryptedArr, rmpKeyNew);
111+
112+
QFile fNew(fiNew.absolutePath());
113+
if (!fNew.open(QFile::WriteOnly | QFile::Truncate))
114+
return false;
115+
fNew.write(encryptedArr);
116+
fNew.close();
117+
118+
m_dir.remove(oldName);
119+
120+
return true;
121+
}
122+
88123
bool RmeRenamerPrivate::renameImdsToEasy()
89124
{
90125
for (ExistNote i = IMD_4K_EZ; i <= MDE_HD; i = static_cast<ExistNote>(i << 1)) {
@@ -141,7 +176,7 @@ bool RmeRenamerPrivate::renameRmpsToEasy()
141176
i_easiest = static_cast<ExistNote>(i_easiest << 1);
142177

143178
if ((m_dir.dirName() + noteFileNameSuffix(i_easiest)) != file_name)
144-
m_dir.rename(file_name, (m_dir.dirName() + noteFileNameSuffix(i_easiest)));
179+
renameSingleRmp(file_name, (m_dir.dirName() + noteFileNameSuffix(i_easiest)));
145180
}
146181
}
147182

@@ -281,7 +316,7 @@ bool RmeRenamerPrivate::renameRmps()
281316
QString file_name;
282317
file_name.append(m_dir.dirName()).append(noteFileNameSuffix(i));
283318
if (m_dir.exists(file_name))
284-
m_dir.rename(file_name, m_toRename + noteFileNameSuffix(i));
319+
renameSingleRmp(file_name, m_toRename + noteFileNameSuffix(i));
285320
}
286321

287322
return true;
@@ -465,6 +500,92 @@ bool RmeConverter::convertImdJsonToImd()
465500
return flag;
466501
}
467502

503+
bool RmeConverter::convertRmpToImdJson()
504+
{
505+
Q_D(RmeConverter);
506+
if (!d->m_dir.exists())
507+
return false;
508+
509+
bool flag = true;
510+
511+
for (ExistNote i = RMP_4K_EZ; i <= RMP_6K_HD; i = static_cast<ExistNote>(i << 1)) {
512+
static const QByteArray rmpKeyPrefix("RMP4TT3RN");
513+
514+
QString file_name;
515+
file_name.append(d->m_dir.dirName()).append(noteFileNameSuffix(i));
516+
if (d->m_dir.exists(file_name)) {
517+
QFile f(d->m_dir.absoluteFilePath(file_name));
518+
if (!f.open(QIODevice::ReadOnly)) {
519+
flag = false;
520+
continue;
521+
}
522+
523+
QByteArray arr = f.readAll();
524+
f.close();
525+
526+
ExistNote converted = static_cast<ExistNote>(i >> 9);
527+
QString toFileName;
528+
toFileName.append(d->m_dir.dirName()).append(noteFileNameSuffix(converted));
529+
530+
QByteArray rmpKeySuffix = file_name.chopped(4).toLatin1();
531+
QByteArray imdjson = RmeCrypt::decryptFull(arr, rmpKeyPrefix + rmpKeySuffix);
532+
533+
QFile fImdJson(d->m_dir.absoluteFilePath(toFileName));
534+
if (!fImdJson.open(QFile::WriteOnly)) {
535+
flag = false;
536+
continue;
537+
}
538+
fImdJson.write(imdjson);
539+
fImdJson.close();
540+
}
541+
}
542+
543+
return flag;
544+
}
545+
546+
bool RmeConverter::convertImdJsonToRmp()
547+
{
548+
Q_D(RmeConverter);
549+
if (!d->m_dir.exists())
550+
return false;
551+
552+
bool flag = true;
553+
554+
for (ExistNote i = IMDJSON_4K_EZ; i <= IMDJSON_6K_HD; i = static_cast<ExistNote>(i << 1)) {
555+
static const QByteArray rmpKeyPrefix("RMP4TT3RN");
556+
557+
QString file_name;
558+
file_name.append(d->m_dir.dirName()).append(noteFileNameSuffix(i));
559+
if (d->m_dir.exists(file_name)) {
560+
QFile f(d->m_dir.absoluteFilePath(file_name));
561+
if (!f.open(QIODevice::ReadOnly)) {
562+
flag = false;
563+
continue;
564+
}
565+
566+
QByteArray arr = f.readAll();
567+
f.close();
568+
569+
ExistNote converted = static_cast<ExistNote>(i << 9);
570+
QString toFileName;
571+
toFileName.append(d->m_dir.dirName()).append(noteFileNameSuffix(converted));
572+
573+
QByteArray rmpKeySuffix = toFileName.chopped(4).toLatin1();
574+
QByteArray rmp = RmeCrypt::encryptFull(arr, rmpKeyPrefix + rmpKeySuffix);
575+
576+
QFile fRmp(d->m_dir.absoluteFilePath(toFileName));
577+
if (!fRmp.open(QFile::WriteOnly)) {
578+
flag = false;
579+
continue;
580+
}
581+
fRmp.write(rmp);
582+
fRmp.close();
583+
}
584+
}
585+
586+
return flag;
587+
}
588+
468589
void RmeConverter::setDir(const QDir &dir)
469590
{
470591
Q_D(RmeConverter);

src/librmessentials/src/rmerenamer.h

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class LIBRMESSENTIALS_EXPORT RmeConverter
3838

3939
bool convertImdToImdJson(const RmeChartVersion &version);
4040
bool convertImdJsonToImd();
41+
bool convertRmpToImdJson();
42+
bool convertImdJsonToRmp();
4143

4244
void setDir(const QDir &d);
4345
const QDir &dir() const;

0 commit comments

Comments
 (0)