rodecomp/Source/Utility/Text/Base64.cpp
ragnarok-clanker a9f108a6ee Decompile fn_04411 (DecodeBase64)
Batch #17404 by eAthena: 0 byte-identical, 1 confirmed by peer review.

- fn_04411 DecodeBase64: logic confirmed by peer review

Accepted without human review: peers confirmed the logic ("same" ahead by at least 3, with 70% of the votes).
2026-11-11 22:23:39 +00:00

133 lines
3.8 KiB
C++

#include "Utility/Text/Base64.h"
#include <algorithm>
namespace {
// STRING: st_01143
const char standardAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// STRING: st_01144
const char urlAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
// GLOBAL: gl_24501
const char* const alphabets[] = {standardAlphabet, urlAlphabet};
// OFFSET: 0x0061AD10
// FUNCTION: fn_04416
unsigned Position(unsigned char value) {
if (value >= 'A' && value <= 'Z') {
return value - 'A';
}
if (value >= 'a' && value <= 'z') {
return value - 'a' + 26;
}
if (value >= '0' && value <= '9') {
return value - '0' + 52;
}
if (value == '+' || value == '-') {
return 62;
}
if (value == '/' || value == '_') {
return 63;
}
return value;
}
}
// OFFSET: 0x0061AB10
// FUNCTION: fn_04415
std::string InsertLinebreaks(std::string text, unsigned int distance) {
if (text.empty()) {
return "";
}
for (unsigned int pos = distance; pos < text.size(); pos += distance + 1) {
text.insert(pos, "\n", 1);
}
return text;
}
// OFFSET: 0x006195C0
// FUNCTION: fn_04411
std::string DecodeBase64(std::string encoded, bool removeLinebreaks) {
const std::size_t length = encoded.size();
if (length == 0) {
return {};
}
if (removeLinebreaks) {
std::string stripped = encoded;
stripped.erase(std::remove(stripped.begin(), stripped.end(), '\n'), stripped.end());
return Base64Decode(stripped, false);
}
const std::size_t capacity = length / 4 * 3;
std::size_t pos = 0;
std::string decoded;
decoded.reserve(capacity);
for (; pos < length; pos += 4) {
unsigned second = Position(encoded[pos + 1]);
decoded.push_back(static_cast<char>((Position(encoded[pos]) << 2) + ((second >> 4) & 3)));
if (pos + 2 < length && encoded[pos + 2] != '=' && encoded[pos + 2] != '.') {
unsigned third = Position(encoded[pos + 2]);
decoded.push_back(static_cast<char>((second << 4) ^ ((third >> 2) & 15)));
if (pos + 3 < length && encoded[pos + 3] != '=' && encoded[pos + 3] != '.') {
decoded.push_back(static_cast<char>((third << 6) + Position(encoded[pos + 3])));
}
}
}
return decoded;
}
// OFFSET: 0x00619CF0
// FUNCTION: fn_04412
__declspec(noinline) std::string Base64Decode(const std::string& encoded, bool removeLinebreaks) {
return DecodeBase64(encoded, removeLinebreaks);
}
// INLINED
template <typename String> static std::string Encode(String bytes, bool urlSafe) {
return EncodeBase64(reinterpret_cast<const unsigned char*>(bytes.data()), static_cast<unsigned int>(bytes.length()), urlSafe);
}
// OFFSET: 0x00619D70
// FUNCTION: fn_04413
std::string EncodeBase64(const std::string& input, bool urlSafe) {
return Encode(input, urlSafe);
}
// OFFSET: 0x00619E10
// FUNCTION: fn_04414
std::string EncodeBase64(const unsigned char* input, unsigned int length, bool urlSafe) {
const unsigned int encodedLength = 4 * ((length + 2) / 3);
const char padding = urlSafe ? '.' : '=';
const char* alphabet = alphabets[urlSafe];
std::string encoded;
encoded.reserve(encodedLength);
for (unsigned int index = 0; index < length; index += 3) {
encoded.push_back(alphabet[input[index] >> 2]);
if (index + 1 < length) {
encoded.push_back(alphabet[((input[index] & 3) << 4) + (input[index + 1] >> 4)]);
if (index + 2 < length) {
encoded.push_back(alphabet[((input[index + 1] & 15) << 2) + (input[index + 2] >> 6)]);
encoded.push_back(alphabet[input[index + 2] & 63]);
} else {
encoded.push_back(alphabet[(input[index + 1] & 15) << 2]);
encoded.push_back(padding);
}
} else {
encoded.push_back(alphabet[(input[index] & 3) << 4]);
encoded.push_back(padding);
encoded.push_back(padding);
}
}
return encoded;
}