2025-01-09 17:54:44 -05:00
|
|
|
#ifndef UTILS_H
|
|
|
|
|
#define UTILS_H
|
|
|
|
|
|
|
|
|
|
#include "enums.h"
|
2025-07-10 00:10:49 -04:00
|
|
|
#include "QtZlib/zlib.h"
|
|
|
|
|
#include "qdir.h"
|
|
|
|
|
#include "qicon.h"
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-07-10 00:10:49 -04:00
|
|
|
#include <QMetaEnum>
|
2025-06-04 22:32:34 -04:00
|
|
|
#include <QPainter>
|
|
|
|
|
#include <QCryptographicHash>
|
2025-01-09 17:54:44 -05:00
|
|
|
|
2025-07-10 00:10:49 -04:00
|
|
|
class Utils : public QObject {
|
|
|
|
|
Q_OBJECT
|
2025-01-09 17:54:44 -05:00
|
|
|
public:
|
2025-07-10 00:10:49 -04:00
|
|
|
enum AssetType {
|
|
|
|
|
ASSET_NONE = 0x00,
|
|
|
|
|
ASSET_RAWFILE = 0x01,
|
|
|
|
|
ASSET_SCRIPT_PARSE_TREE = 0x02,
|
|
|
|
|
ASSET_FX = 0x03,
|
|
|
|
|
ASSET_SOUND_CURVE = 0x04,
|
|
|
|
|
ASSET_Animation = 0x05,
|
|
|
|
|
ASSET_COLLISION_MAP = 0x06,
|
|
|
|
|
ASSET_STRINGTABLE = 0x07,
|
|
|
|
|
ASSET_XMODEL_SURFS = 0x08,
|
|
|
|
|
ASSET_TECHNIQUE_SET = 0x09,
|
|
|
|
|
ASSET_GFX_MAP = 0x11,
|
|
|
|
|
ASSET_D3DBSP = 0x15,
|
|
|
|
|
ASSET_GAME_MAP_SP = 0x17,
|
|
|
|
|
ASSET_COL_MAP_SP = 0x18,
|
|
|
|
|
ASSET_PHYSPRESET = 0x19,
|
|
|
|
|
ASSET_DESTRUCTIBLE = 0x20,
|
|
|
|
|
ASSET_LOCALIZE_ENTRY = 0x21,
|
|
|
|
|
ASSET_SHADER = 0x22,
|
|
|
|
|
ASSET_MP_MAP = 0x23,
|
|
|
|
|
ASSET_SP_MAP = 0x24,
|
|
|
|
|
ASSET_SNDDRIVER_GLOBALS = 0x26,
|
|
|
|
|
ASSET_AI_TYPE = 0x27,
|
|
|
|
|
ASSET_COMPUTE_SHADER_SET = 0x29,
|
|
|
|
|
ASSET_LIGHT_DESCRIPTION = 0x30,
|
|
|
|
|
ASSET_BIT_FIELD = 0x31,
|
|
|
|
|
ASSET_STRUCTURED_TABLE = 0x32,
|
|
|
|
|
ASSET_LEADERBOARD_DEF = 0x33,
|
|
|
|
|
ASSET_DDL = 0x34,
|
|
|
|
|
ASSET_KEY_VALUE_PAIRS = 0x35,
|
|
|
|
|
ASSET_SCRIPT_BUNDLE = 0x36,
|
|
|
|
|
ASSET_SCRIPT_BUNDLE_LIST = 0x37,
|
|
|
|
|
ASSET_MAP_TABLE = 0x38,
|
|
|
|
|
ASSET_MAP_TABLE_LOADING_IMAGES = 0x39,
|
|
|
|
|
ASSET_SURFACE_SOUND_DEF = 0x40,
|
|
|
|
|
ASSET_SURFACE_FX_TABLE = 0x41,
|
|
|
|
|
ASSET_RUMBLE = 0x42,
|
|
|
|
|
ASSET_AIM_TABLE = 0x43,
|
|
|
|
|
ASSET_MEDAL = 0x44,
|
|
|
|
|
ASSET_MEDAL_TABLE = 0x45,
|
|
|
|
|
ASSET_OBJECTIVE = 0x46,
|
|
|
|
|
ASSET_OBJECTIVE_LIST = 0x47,
|
|
|
|
|
ASSET_LASER = 0x48,
|
|
|
|
|
ASSET_BEAM = 0x49,
|
|
|
|
|
ASSET_STREAMER_HINT = 0x50,
|
|
|
|
|
ASSET_ANIM_SELECTOR_TABLE = 0x51,
|
|
|
|
|
ASSET_ANIM_MAPPING_TABLE = 0x52,
|
|
|
|
|
ASSET_ANIM_STATE_MACHINE = 0x53,
|
|
|
|
|
ASSET_BEHAVIOR_TREE = 0x54,
|
|
|
|
|
ASSET_BEHAVIOR_STATE_MACHINE = 0x55,
|
|
|
|
|
ASSET_FOOTSTEP_TABLE = 0x56,
|
|
|
|
|
ASSET_ENTITY_FX_IMPACTS = 0x57,
|
|
|
|
|
ASSET_ENTITY_SOUND_IMPACTS = 0x58,
|
|
|
|
|
ASSET_VEHICLE_FX_DEF = 0x59,
|
|
|
|
|
ASSET_VEHICLE_SOUND_DEF = 0x60,
|
|
|
|
|
ASSET_VEHICLE = 0x61,
|
|
|
|
|
ASSET_VEHICLE_TRACER = 0x62,
|
|
|
|
|
ASSET_PLAYER_SOUNDS_TABLE = 0x63,
|
|
|
|
|
ASSET_PLAYER_FX_TABLE = 0x64,
|
|
|
|
|
ASSET_SHARED_WEAPON_SOUNDS = 0x65,
|
|
|
|
|
ASSET_ATTACHMENT = 0x66,
|
|
|
|
|
ASSET_ATTACHMENT_UNIQUE = 0x67,
|
|
|
|
|
ASSET_WEAPON_CAMO = 0x68,
|
|
|
|
|
ASSET_CUSTOMIZATION_TABLE = 0x69,
|
|
|
|
|
ASSET_CUSTOMIZATION_TABLE_FEIMAGES = 0x70,
|
|
|
|
|
ASSET_CUSTOMIZATION_TABLE_COLOR = 0x71,
|
|
|
|
|
ASSET_PHYS_CONSTRAINTS = 0x72,
|
|
|
|
|
ASSET_DESTRUCTIBLE_DEF = 0x73,
|
|
|
|
|
ASSET_XMODEL_MESH = 0x74,
|
|
|
|
|
ASSET_S_ANIM = 0x75,
|
|
|
|
|
ASSET_FONT_ICON = 0x76,
|
|
|
|
|
ASSET_CG_MEDIA_TABLE = 0x77,
|
|
|
|
|
ASSET_SHOCK_FILE = 0x78,
|
|
|
|
|
ASSET_ZONE_FILE = 0x79,
|
|
|
|
|
ASSET_FAST_FILE = 0x80,
|
|
|
|
|
ASSET_SOUND_DRIVER_GLOBALS = 0x81,
|
|
|
|
|
ASSET_XMODELPIECES = 0x82,
|
|
|
|
|
ASSET_XMODEL = 0x85,
|
|
|
|
|
ASSET_MATERIAL = 0x86,
|
|
|
|
|
ASSET_PIXELSHADER = 0x87,
|
|
|
|
|
ASSET_IMAGE = 0x89,
|
|
|
|
|
ASSET_SOUND = 0x90,
|
|
|
|
|
ASSET_LOADED_SOUND = 0x92,
|
|
|
|
|
ASSET_CLIPMAP = 0x93,
|
|
|
|
|
ASSET_CLIPMAP_PVS = 0x94,
|
|
|
|
|
ASSET_COMWORLD = 0x95,
|
|
|
|
|
ASSET_GAMEWORLD_SP = 0x96,
|
|
|
|
|
ASSET_GAMEWORLD_MP = 0x97,
|
|
|
|
|
ASSET_MAP_ENTS = 0x98,
|
|
|
|
|
ASSET_GFXWORLD = 0x99,
|
|
|
|
|
ASSET_LIGHT_DEF = 0x100,
|
|
|
|
|
ASSET_UI_MAP = 0x101,
|
|
|
|
|
ASSET_FONT = 0x102,
|
|
|
|
|
ASSET_MENULIST = 0x103,
|
|
|
|
|
ASSET_MENU = 0x104,
|
|
|
|
|
ASSET_WEAPON = 0x106,
|
|
|
|
|
ASSET_IMPACT_FX = 0x109,
|
|
|
|
|
ASSET_AITYPE = 0x110,
|
|
|
|
|
ASSET_MPTYPE = 0x111,
|
|
|
|
|
ASSET_CHARACTER = 0x112,
|
|
|
|
|
ASSET_XMODELALIAS = 0x113,
|
|
|
|
|
ASSET_COUNT = 0x116,
|
|
|
|
|
ASSET_STRING = 0x117,
|
|
|
|
|
ASSET_ASSETLIST = 0x118,
|
|
|
|
|
ASSET_PHYSCOLLMAP = 0x119,
|
|
|
|
|
ASSET_CLIPMAP_SP = 0x120,
|
|
|
|
|
ASSET_CLIPMAP_MP = 0x121,
|
|
|
|
|
ASSET_FXWORLD = 0x122,
|
|
|
|
|
ASSET_LEADERBOARD = 0x123,
|
|
|
|
|
ASSET_STRUCTURED_DATA_DEF = 0x124,
|
|
|
|
|
ASSET_TRACER = 0x125,
|
|
|
|
|
ASSET_ADDON_MAP_ENTS = 0x126,
|
|
|
|
|
ASSET_GLASSWORLD = 0x127,
|
|
|
|
|
ASSET_PATHDATA = 0x128,
|
|
|
|
|
ASSET_VEHICLE_TRACK = 0x129,
|
|
|
|
|
ASSET_SURFACE_FX = 0x130,
|
|
|
|
|
ASSET_SCRIPTFILE = 0x131,
|
|
|
|
|
ASSET_SOUND_SUBMIX = 0x132,
|
|
|
|
|
ASSET_SOUND_EVENT = 0x133,
|
|
|
|
|
ASSET_LPF_CURVE = 0x134,
|
|
|
|
|
ASSET_REVERB_CURVE = 0x135,
|
|
|
|
|
ASSET_GFXWORLD_TRANSIENT_ZONE = 0x136,
|
|
|
|
|
ASSET_ANIMCLASS = 0x137,
|
|
|
|
|
ASSET_NET_CONST_STRINGS = 0x138,
|
|
|
|
|
ASSET_REVERB_PRESET = 0x139,
|
|
|
|
|
ASSET_LUA_FILE = 0x140,
|
|
|
|
|
ASSET_SCRIPTABLE = 0x141,
|
|
|
|
|
ASSET_EQUIPMENT_SND_TABLE = 0x142,
|
|
|
|
|
ASSET_DOPPLER_PRESET = 0x143,
|
|
|
|
|
ASSET_SKELETON_SCRIPT = 0x144,
|
|
|
|
|
ASSET_PHYSCONSTRAINTS = 0x145,
|
|
|
|
|
ASSET_DESTRUCTIBLEDEF = 0x146,
|
|
|
|
|
ASSET_SOUND_PATCH = 0x147,
|
|
|
|
|
ASSET_WEAPONDEF = 0x148,
|
|
|
|
|
ASSET_WEAPON_VARIANT = 0x149,
|
|
|
|
|
ASSET_MPBODY = 0x150,
|
|
|
|
|
ASSET_MPHEAD = 0x151,
|
|
|
|
|
ASSET_PACK_INDEX = 0x152,
|
|
|
|
|
ASSET_XGLOBALS = 0x153,
|
|
|
|
|
ASSET_GLASSES = 0x154,
|
|
|
|
|
ASSET_EMBLEMSET = 0x155
|
|
|
|
|
};
|
|
|
|
|
Q_ENUM(AssetType)
|
|
|
|
|
|
|
|
|
|
static QString AssetTypeToStr(Utils::AssetType aAssetType) {
|
|
|
|
|
const QMetaObject &mo = Utils::staticMetaObject;
|
|
|
|
|
int index = mo.indexOfEnumerator("AssetType");
|
|
|
|
|
QMetaEnum metaEnum = mo.enumerator(index);
|
|
|
|
|
return QString::fromLatin1(metaEnum.valueToKey(aAssetType));
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-04 20:34:24 -04:00
|
|
|
static bool ExportData(const QString aFileName, const QByteArray aData) {
|
|
|
|
|
QDir workingDir = QDir::currentPath();
|
|
|
|
|
workingDir.mkdir("exports");
|
|
|
|
|
|
|
|
|
|
QFile testFile("exports/" + aFileName);
|
|
|
|
|
if(!testFile.open(QIODevice::WriteOnly)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
testFile.write(aData);
|
|
|
|
|
testFile.close();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2025-06-04 22:32:34 -04:00
|
|
|
static quint8 ReverseBits(quint8 b) {
|
|
|
|
|
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
|
|
|
|
|
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
|
|
|
|
|
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
|
|
|
|
|
return b;
|
|
|
|
|
}
|
2025-07-10 00:10:49 -04:00
|
|
|
static QIcon CreateAssetIcon(Utils::AssetType aAssetType, QColor color = QColor()) {
|
|
|
|
|
const QString assetTypeStr = AssetTypeToStr(aAssetType);
|
|
|
|
|
|
|
|
|
|
QString name;
|
|
|
|
|
const QStringList parts = assetTypeStr.split('_').mid(1);
|
|
|
|
|
foreach (const QString part, parts) {
|
|
|
|
|
name += part[0];
|
|
|
|
|
}
|
|
|
|
|
if (parts.size() == 1) {
|
|
|
|
|
name += parts.first()[1];
|
|
|
|
|
}
|
|
|
|
|
return CreateAssetIcon(name, color);
|
|
|
|
|
}
|
2025-06-04 22:32:34 -04:00
|
|
|
static QIcon CreateAssetIcon(const QString& name, QColor color = QColor()) {
|
|
|
|
|
constexpr int iconSize = 32;
|
|
|
|
|
constexpr int padding = 4;
|
|
|
|
|
|
|
|
|
|
QImage result(iconSize, iconSize, QImage::Format_ARGB32);
|
|
|
|
|
result.fill(Qt::transparent);
|
|
|
|
|
|
|
|
|
|
if (!color.isValid()) {
|
|
|
|
|
color = Utils::StringToColor(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPainter painter(&result);
|
|
|
|
|
painter.setRenderHint(QPainter::Antialiasing, true);
|
|
|
|
|
painter.setRenderHint(QPainter::TextAntialiasing, true);
|
|
|
|
|
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
|
|
|
|
|
|
|
|
|
|
// Draw background
|
|
|
|
|
QBrush brush(color);
|
|
|
|
|
brush.setStyle(Qt::SolidPattern);
|
|
|
|
|
painter.setBrush(color);
|
|
|
|
|
painter.setPen(Qt::NoPen);
|
|
|
|
|
painter.drawRoundedRect(0, 0, iconSize, iconSize, 4, 4);
|
|
|
|
|
|
|
|
|
|
// Set base font
|
|
|
|
|
QFont font("Heavitas");
|
|
|
|
|
font.setPixelSize(iconSize); // Start large
|
|
|
|
|
painter.setFont(font);
|
|
|
|
|
|
|
|
|
|
// Adjust font size to fit text width (only reduce, not increase)
|
|
|
|
|
QFontMetrics fm(font);
|
|
|
|
|
int textWidth = fm.horizontalAdvance(name);
|
|
|
|
|
while (textWidth > iconSize - 2 * padding && font.pixelSize() > 1) {
|
|
|
|
|
font.setPixelSize(font.pixelSize() - 1);
|
|
|
|
|
painter.setFont(font);
|
|
|
|
|
fm = QFontMetrics(font);
|
|
|
|
|
textWidth = fm.horizontalAdvance(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate vertical scaling factor
|
|
|
|
|
qreal scaleY = 1.2 * iconSize / fm.height();
|
|
|
|
|
|
|
|
|
|
// Apply transform: scale vertically, center align
|
|
|
|
|
painter.save();
|
|
|
|
|
painter.translate(iconSize / 2.0, iconSize / 2.0);
|
|
|
|
|
painter.scale(1.0, scaleY);
|
|
|
|
|
painter.translate(-iconSize / 2.0, -iconSize / 2.0);
|
|
|
|
|
|
|
|
|
|
QRect textRect(0, 0, iconSize, iconSize);
|
|
|
|
|
|
|
|
|
|
// Draw stroke
|
|
|
|
|
painter.setPen(Qt::black);
|
|
|
|
|
for (int dx = 0; dx <= 1; ++dx) {
|
|
|
|
|
for (int dy = 0; dy <= 1; ++dy) {
|
|
|
|
|
if (dx || dy)
|
|
|
|
|
painter.drawText(textRect.translated(dx, dy), Qt::AlignCenter, name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw main text
|
|
|
|
|
painter.setPen(Qt::white);
|
|
|
|
|
painter.drawText(textRect, Qt::AlignCenter, name);
|
|
|
|
|
painter.restore();
|
|
|
|
|
|
|
|
|
|
// Debug output
|
|
|
|
|
QDir().mkdir(QDir().absoluteFilePath(".") + "/icons/");
|
|
|
|
|
result.save(QDir().absoluteFilePath(".") + "/icons/" + name + ".png");
|
|
|
|
|
|
|
|
|
|
return QIcon(QPixmap::fromImage(result));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QIcon CreateGameIcon(const int gameNum, QColor color = QColor()) {
|
|
|
|
|
constexpr int size = 32;
|
|
|
|
|
constexpr int padding = 4; // pixels of padding on all sides
|
|
|
|
|
const int contentSize = size - 2 * padding;
|
|
|
|
|
|
|
|
|
|
QImage result(size, size, QImage::Format_ARGB32);
|
|
|
|
|
result.fill(Qt::transparent);
|
|
|
|
|
|
|
|
|
|
if (!color.isValid()) {
|
|
|
|
|
color = Utils::StringToColor(QString("COD%1").arg(gameNum));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPainter painter(&result);
|
|
|
|
|
painter.setRenderHint(QPainter::Antialiasing, false);
|
|
|
|
|
painter.setRenderHint(QPainter::TextAntialiasing, false);
|
|
|
|
|
painter.setRenderHint(QPainter::SmoothPixmapTransform, false);
|
|
|
|
|
|
|
|
|
|
// Draw background
|
|
|
|
|
painter.setBrush(color);
|
|
|
|
|
painter.setPen(Qt::NoPen);
|
|
|
|
|
painter.drawRoundedRect(0, 0, size, size, 4, 4);
|
|
|
|
|
|
|
|
|
|
// === Font Setup ===
|
|
|
|
|
QFont codFont("Heavitas");
|
|
|
|
|
codFont.setPixelSize(contentSize * 0.40);
|
|
|
|
|
|
|
|
|
|
QFont numFont("Heavitas");
|
|
|
|
|
numFont.setPixelSize(contentSize);
|
|
|
|
|
|
|
|
|
|
// === Layout Areas (inside padding) ===
|
|
|
|
|
const QRect contentRect(padding, padding, contentSize, contentSize);
|
|
|
|
|
const int col1 = contentRect.left();
|
|
|
|
|
const int col2 = contentRect.left() + contentRect.width() / 3;
|
|
|
|
|
|
|
|
|
|
const int thirdH = contentRect.height() / 3;
|
|
|
|
|
const QRect codRects[] = {
|
|
|
|
|
QRect(col1, 2 + contentRect.top(), contentRect.width() / 3, thirdH),
|
|
|
|
|
QRect(col1, contentRect.top() + thirdH, contentRect.width() / 3, thirdH),
|
|
|
|
|
QRect(col1, -2 + contentRect.top() + 2 * thirdH, contentRect.width() / 3, thirdH),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const QRect numRect(col2, contentRect.top(), contentRect.width() * 2 / 3, contentRect.height());
|
|
|
|
|
|
|
|
|
|
const QString codLetters[] = { "C", "O", "D" };
|
|
|
|
|
const QString numText = QString::number(gameNum);
|
|
|
|
|
|
|
|
|
|
// === Stroke pass ===
|
|
|
|
|
for (int dx = -1; dx <= 2; ++dx) {
|
|
|
|
|
for (int dy = -1; dy <= 2; ++dy) {
|
|
|
|
|
if (dx == 0 && dy == 0) continue;
|
|
|
|
|
|
|
|
|
|
painter.setPen(Qt::black);
|
|
|
|
|
painter.setFont(codFont);
|
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
|
|
|
painter.drawText(codRects[i].translated(dx, dy), Qt::AlignCenter, codLetters[i]);
|
|
|
|
|
|
|
|
|
|
painter.setFont(numFont);
|
|
|
|
|
painter.drawText(numRect.translated(dx, dy), Qt::AlignCenter, numText);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// === Fill pass ===
|
|
|
|
|
painter.setPen(Qt::white);
|
|
|
|
|
painter.setFont(codFont);
|
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
|
|
|
painter.drawText(codRects[i], Qt::AlignCenter, codLetters[i]);
|
|
|
|
|
|
|
|
|
|
painter.setFont(numFont);
|
|
|
|
|
painter.drawText(numRect, Qt::AlignCenter, numText);
|
|
|
|
|
|
|
|
|
|
// Save & return icon
|
|
|
|
|
QDir().mkdir(QDir().absoluteFilePath(".") + "/icons/");
|
|
|
|
|
result.save(QDir().absoluteFilePath(".") + QString("/icons/COD%1.png").arg(gameNum));
|
|
|
|
|
|
|
|
|
|
return QIcon(QPixmap::fromImage(result));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static QColor StringToColor(const QString& str) {
|
|
|
|
|
// 1. Hash the string using Qt's built-in hash (MD5, SHA1, or any)
|
|
|
|
|
QByteArray hash = QCryptographicHash::hash(str.toUtf8(), QCryptographicHash::Md5);
|
|
|
|
|
|
|
|
|
|
// 2. Use first 3 bytes of hash for RGB
|
|
|
|
|
// This guarantees same string = same color every time
|
|
|
|
|
int r = static_cast<unsigned char>(hash[0]);
|
|
|
|
|
int g = static_cast<unsigned char>(hash[1]);
|
|
|
|
|
int b = static_cast<unsigned char>(hash[2]);
|
|
|
|
|
|
|
|
|
|
// 3. Optionally adjust brightness or saturation to avoid too dark/light colors
|
|
|
|
|
QColor color(r, g, b);
|
|
|
|
|
if (color.value() < 128) { // brighten if too dark
|
|
|
|
|
color = color.lighter(150);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return color;
|
|
|
|
|
}
|
2025-02-14 16:06:27 -05:00
|
|
|
static bool ReadUntilString(QDataStream* stream, const QString& targetString) {
|
|
|
|
|
if (!stream || targetString.isEmpty()) {
|
|
|
|
|
return false; // Invalid input
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray buffer;
|
|
|
|
|
QByteArray targetBytes = targetString.toUtf8(); // Handle multibyte characters
|
|
|
|
|
const int targetLength = targetBytes.size();
|
|
|
|
|
qDebug() << targetBytes << targetLength;
|
|
|
|
|
|
|
|
|
|
// Read as unsigned bytes to handle all possible values (0-255)
|
|
|
|
|
unsigned char byte;
|
|
|
|
|
while (!stream->atEnd()) {
|
|
|
|
|
// Read one byte at a time
|
|
|
|
|
*stream >> byte;
|
|
|
|
|
buffer.append(static_cast<char>(byte)); // Append as char for QByteArray
|
|
|
|
|
|
|
|
|
|
// Keep buffer size limited to the target length
|
|
|
|
|
if (buffer.size() > targetLength) {
|
|
|
|
|
buffer.remove(0, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if the buffer matches the target string in raw bytes
|
|
|
|
|
if (buffer == targetBytes) {
|
|
|
|
|
// Backup to the start of the matched string
|
|
|
|
|
stream->device()->seek(stream->device()->pos() - targetLength);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Target string not found
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ReadUntilHex(QDataStream* stream, const QString& hexString) {
|
|
|
|
|
if (!stream || hexString.isEmpty() || hexString.size() % 2 != 0) {
|
|
|
|
|
return false; // Invalid input
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert hex string to byte array
|
|
|
|
|
QByteArray targetBytes = QByteArray::fromHex(hexString.toUtf8());
|
|
|
|
|
const int targetLength = targetBytes.size();
|
|
|
|
|
|
|
|
|
|
QByteArray buffer;
|
|
|
|
|
unsigned char byte;
|
|
|
|
|
|
|
|
|
|
while (!stream->atEnd()) {
|
|
|
|
|
// Read one byte at a time
|
|
|
|
|
*stream >> byte;
|
|
|
|
|
buffer.append(static_cast<char>(byte)); // Append as char for QByteArray
|
|
|
|
|
|
|
|
|
|
// Keep buffer size limited to the target length
|
|
|
|
|
if (buffer.size() > targetLength) {
|
|
|
|
|
buffer.remove(0, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if the buffer matches the target byte sequence
|
|
|
|
|
if (buffer == targetBytes) {
|
|
|
|
|
// Backup to the start of the matched sequence
|
|
|
|
|
stream->device()->seek(stream->device()->pos() - targetLength);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Target sequence not found
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-09 17:54:44 -05:00
|
|
|
/*
|
|
|
|
|
LumpTypeToString()
|
|
|
|
|
|
|
|
|
|
Convert BSP lump type to desc string
|
|
|
|
|
*/
|
|
|
|
|
static QString LumpTypeToString(LUMP_TYPE aLumpType) {
|
|
|
|
|
switch (aLumpType) {
|
|
|
|
|
case LUMP_MATERIALS: return "LUMP_MATERIALS";
|
|
|
|
|
case LUMP_LIGHTBYTES: return "LUMP_LIGHTBYTES";
|
|
|
|
|
case LUMP_LIGHTGRIDENTRIES: return "LUMP_LIGHTGRIDENTRIES";
|
|
|
|
|
case LUMP_LIGHTGRIDCOLORS: return "LUMP_LIGHTGRIDCOLORS";
|
|
|
|
|
case LUMP_PLANES: return "LUMP_PLANES";
|
|
|
|
|
case LUMP_BRUSHSIDES: return "LUMP_BRUSHSIDES";
|
|
|
|
|
case LUMP_BRUSHSIDEEDGECOUNTS: return "LUMP_BRUSHSIDEEDGECOUNTS";
|
|
|
|
|
case LUMP_BRUSHEDGES: return "LUMP_BRUSHEDGES";
|
|
|
|
|
case LUMP_BRUSHES: return "LUMP_BRUSHES";
|
|
|
|
|
case LUMP_TRIANGLES: return "LUMP_TRIANGLES";
|
|
|
|
|
case LUMP_DRAWVERTS: return "LUMP_DRAWVERTS";
|
|
|
|
|
case LUMP_DRAWINDICES: return "LUMP_DRAWINDICES";
|
|
|
|
|
case LUMP_CULLGROUPS: return "LUMP_CULLGROUPS";
|
|
|
|
|
case LUMP_CULLGROUPINDICES: return "LUMP_CULLGROUPINDICES";
|
|
|
|
|
case LUMP_OBSOLETE_1: return "LUMP_OBSOLETE_1";
|
|
|
|
|
case LUMP_OBSOLETE_2: return "LUMP_OBSOLETE_2";
|
|
|
|
|
case LUMP_OBSOLETE_3: return "LUMP_OBSOLETE_3";
|
|
|
|
|
case LUMP_OBSOLETE_4: return "LUMP_OBSOLETE_4";
|
|
|
|
|
case LUMP_OBSOLETE_5: return "LUMP_OBSOLETE_5";
|
|
|
|
|
case LUMP_PORTALVERTS: return "LUMP_PORTALVERTS";
|
|
|
|
|
case LUMP_OBSOLETE_6: return "LUMP_OBSOLETE_6";
|
|
|
|
|
case LUMP_UINDS: return "LUMP_UINDS";
|
|
|
|
|
case LUMP_BRUSHVERTSCOUNTS: return "LUMP_BRUSHVERTSCOUNTS";
|
|
|
|
|
case LUMP_BRUSHVERTS: return "LUMP_BRUSHVERTS";
|
|
|
|
|
case LUMP_AABBTREES: return "LUMP_AABBTREES";
|
|
|
|
|
case LUMP_CELLS: return "LUMP_CELLS";
|
|
|
|
|
case LUMP_PORTALS: return "LUMP_PORTALS";
|
|
|
|
|
case LUMP_NODES: return "LUMP_NODES";
|
|
|
|
|
case LUMP_LEAFS: return "LUMP_LEAFS";
|
|
|
|
|
case LUMP_LEAFBRUSHES: return "LUMP_LEAFBRUSHES";
|
|
|
|
|
case LUMP_LEAFSURFACES: return "LUMP_LEAFSURFACES";
|
|
|
|
|
case LUMP_COLLISIONVERTS: return "LUMP_COLLISIONVERTS";
|
|
|
|
|
case LUMP_COLLISIONTRIS: return "LUMP_COLLISIONTRIS";
|
|
|
|
|
case LUMP_COLLISIONEDGEWALKABLE: return "LUMP_COLLISIONEDGEWALKABLE";
|
|
|
|
|
case LUMP_COLLISIONBORDERS: return "LUMP_COLLISIONBORDERS";
|
|
|
|
|
case LUMP_COLLISIONPARTITIONS: return "LUMP_COLLISIONPARTITIONS";
|
|
|
|
|
case LUMP_COLLISIONAABBS: return "LUMP_COLLISIONAABBS";
|
|
|
|
|
case LUMP_MODELS: return "LUMP_MODELS";
|
|
|
|
|
case LUMP_VISIBILITY: return "LUMP_VISIBILITY";
|
|
|
|
|
case LUMP_ENTITIES: return "LUMP_ENTITIES";
|
|
|
|
|
case LUMP_PATHCONNECTIONS: return "LUMP_PATHCONNECTIONS";
|
|
|
|
|
case LUMP_REFLECTION_PROBES: return "LUMP_REFLECTION_PROBES";
|
|
|
|
|
case LUMP_VERTEX_LAYER_DATA: return "LUMP_VERTEX_LAYER_DATA";
|
|
|
|
|
case LUMP_PRIMARY_LIGHTS: return "LUMP_PRIMARY_LIGHTS";
|
|
|
|
|
case LUMP_LIGHTGRIDHEADER: return "LUMP_LIGHTGRIDHEADER";
|
|
|
|
|
case LUMP_LIGHTGRIDROWS: return "LUMP_LIGHTGRIDROWS";
|
|
|
|
|
case LUMP_OBSOLETE_10: return "LUMP_OBSOLETE_10";
|
|
|
|
|
case LUMP_UNLAYERED_TRIANGLES: return "LUMP_UNLAYERED_TRIANGLES";
|
|
|
|
|
case LUMP_UNLAYERED_DRAWVERTS: return "LUMP_UNLAYERED_DRAWVERTS";
|
|
|
|
|
case LUMP_UNLAYERED_DRAWINDICES: return "LUMP_UNLAYERED_DRAWINDICES";
|
|
|
|
|
case LUMP_UNLAYERED_CULLGROUPS: return "LUMP_UNLAYERED_CULLGROUPS";
|
|
|
|
|
case LUMP_UNLAYERED_AABBTREES: return "LUMP_UNLAYERED_AABBTREES";
|
|
|
|
|
case LUMP_WATERHEADER: return "LUMP_WATERHEADER";
|
|
|
|
|
case LUMP_WATERCELLS: return "LUMP_WATERCELLS";
|
|
|
|
|
case LUMP_WATERCELLDATA: return "LUMP_WATERCELLDATA";
|
|
|
|
|
case LUMP_BURNABLEHEADER: return "LUMP_BURNABLEHEADER";
|
|
|
|
|
case LUMP_BURNABLECELLS: return "LUMP_BURNABLECELLS";
|
|
|
|
|
case LUMP_BURNABLECELLDATA: return "LUMP_BURNABLECELLDATA";
|
|
|
|
|
case LUMP_SIMPLELIGHTMAPBYTES: return "LUMP_SIMPLELIGHTMAPBYTES";
|
|
|
|
|
case LUMP_LODCHAINS: return "LUMP_LODCHAINS";
|
|
|
|
|
case LUMP_LODINFOS: return "LUMP_LODINFOS";
|
|
|
|
|
case LUMP_LODSURFACES: return "LUMP_LODSURFACES";
|
|
|
|
|
case LUMP_LIGHTREGIONS: return "LUMP_LIGHTREGIONS";
|
|
|
|
|
case LUMP_LIGHTREGION_HULLS: return "LUMP_LIGHTREGION_HULLS";
|
|
|
|
|
case LUMP_LIGHTREGION_AXES: return "LUMP_LIGHTREGION_AXES";
|
|
|
|
|
case LUMP_WIILIGHTGRID: return "LUMP_WIILIGHTGRID";
|
|
|
|
|
case LUMP_LIGHTGRID2D_LIGHTS: return "LUMP_LIGHTGRID2D_LIGHTS";
|
|
|
|
|
case LUMP_LIGHTGRID2D_INDICES: return "LUMP_LIGHTGRID2D_INDICES";
|
|
|
|
|
case LUMP_LIGHTGRID2D_POINTS: return "LUMP_LIGHTGRID2D_POINTS";
|
|
|
|
|
case LUMP_LIGHTGRID2D_CELLS: return "LUMP_LIGHTGRID2D_CELLS";
|
|
|
|
|
case LUMP_LIGHT_CORONAS: return "LUMP_LIGHT_CORONAS";
|
|
|
|
|
case LUMP_SHADOWMAP_VOLUMES: return "LUMP_SHADOWMAP_VOLUMES";
|
|
|
|
|
case LUMP_SHADOWMAP_VOLUME_PLANES: return "LUMP_SHADOWMAP_VOLUME_PLANES";
|
|
|
|
|
case LUMP_EXPOSURE_VOLUMES: return "LUMP_EXPOSURE_VOLUMES";
|
|
|
|
|
case LUMP_EXPOSURE_VOLUME_PLANES: return "LUMP_EXPOSURE_VOLUME_PLANES";
|
|
|
|
|
case LUMP_OCCLUDERS: return "LUMP_OCCLUDERS";
|
|
|
|
|
case LUMP_OUTDOORBOUNDS: return "LUMP_OUTDOORBOUNDS";
|
|
|
|
|
case LUMP_HERO_ONLY_LIGHTS: return "LUMP_HERO_ONLY_LIGHTS";
|
|
|
|
|
default: return "Unknown Lump Type";
|
|
|
|
|
}
|
|
|
|
|
return "Unknown Lump Type";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString ZLibErrorToString(int result) {
|
|
|
|
|
switch (result) {
|
|
|
|
|
case Z_ERRNO:
|
|
|
|
|
return "Encountered file operation error!";
|
|
|
|
|
case Z_STREAM_ERROR:
|
|
|
|
|
return "Stream was not initialized properly!";
|
|
|
|
|
case Z_DATA_ERROR:
|
|
|
|
|
return "The input data was corrupted or incomplete!";
|
|
|
|
|
case Z_MEM_ERROR:
|
|
|
|
|
return "There was not enough memory!";
|
|
|
|
|
case Z_BUF_ERROR:
|
|
|
|
|
return "There was not enough room in the output buffer!";
|
|
|
|
|
case Z_VERSION_ERROR:
|
|
|
|
|
return "Linked libraries are out of date!";
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static quint32 PadInt4(quint32 size) {
|
|
|
|
|
return (size + 3) & ~3; // Align size to 4 bytes
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static quint32 PaddingSize(quint32 size) {
|
|
|
|
|
return PadInt4(size) - size;
|
|
|
|
|
}
|
2025-01-17 00:23:50 -05:00
|
|
|
|
|
|
|
|
static QString CompanyEnumToStr(FF_COMPANY aCompany) {
|
|
|
|
|
switch (aCompany) {
|
|
|
|
|
case COMPANY_NONE:
|
|
|
|
|
return "None";
|
|
|
|
|
case COMPANY_INFINITY_WARD:
|
|
|
|
|
return "Infinity Ward";
|
|
|
|
|
case COMPANY_TREYARCH:
|
|
|
|
|
return "Treyarch";
|
|
|
|
|
case COMPANY_SLEDGEHAMMER:
|
|
|
|
|
return "Sledgehammer";
|
|
|
|
|
case COMPANY_NEVERSOFT:
|
|
|
|
|
return "Neversoft";
|
|
|
|
|
}
|
2025-02-08 19:58:54 -05:00
|
|
|
return "None";
|
2025-01-17 00:23:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString FileTypeEnumToStr(FF_FILETYPE aFileType) {
|
|
|
|
|
switch (aFileType) {
|
|
|
|
|
case FILETYPE_NONE:
|
|
|
|
|
return "None";
|
|
|
|
|
case FILETYPE_FAST_FILE:
|
|
|
|
|
return "Fast File";
|
|
|
|
|
}
|
2025-02-08 19:58:54 -05:00
|
|
|
return "None";
|
2025-01-17 00:23:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString SignageEnumToStr(FF_SIGNAGE aSignage) {
|
|
|
|
|
switch (aSignage) {
|
|
|
|
|
case SIGNAGE_NONE:
|
|
|
|
|
return "None";
|
|
|
|
|
case SIGNAGE_SIGNED:
|
|
|
|
|
return "Signed";
|
|
|
|
|
case SIGNAGE_UNSIGNED:
|
|
|
|
|
return "Unsigned";
|
|
|
|
|
}
|
2025-02-08 19:58:54 -05:00
|
|
|
return "None";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString MenuVAlignToStr(MENU_V_ALIGNMENT align) {
|
|
|
|
|
if (align == VERTICAL_ALIGN_SUBTOP) {
|
|
|
|
|
return "VERTICAL_ALIGN_SUBTOP";
|
|
|
|
|
} else if (align == VERTICAL_ALIGN_TOP) {
|
|
|
|
|
return "VERTICAL_ALIGN_TOP";
|
|
|
|
|
} else if (align == VERTICAL_ALIGN_CENTER) {
|
|
|
|
|
return "VERTICAL_ALIGN_CENTER";
|
|
|
|
|
} else if (align == VERTICAL_ALIGN_BOTTOM) {
|
|
|
|
|
return "VERTICAL_ALIGN_BOTTOM";
|
|
|
|
|
} else if (align == VERTICAL_ALIGN_FULLSCREEN) {
|
|
|
|
|
return "VERTICAL_ALIGN_FULLSCREEN";
|
|
|
|
|
} else if (align == VERTICAL_ALIGN_NOSCALE) {
|
|
|
|
|
return "VERTICAL_ALIGN_NOSCALE";
|
|
|
|
|
} else if (align == VERTICAL_ALIGN_TO480) {
|
|
|
|
|
return "VERTICAL_ALIGN_TO480";
|
|
|
|
|
} else if (align == VERTICAL_ALIGN_CENTER_SAFEAREA) {
|
|
|
|
|
return "VERTICAL_ALIGN_CENTER_SAFEAREA";
|
|
|
|
|
} else if (align == VERTICAL_ALIGN_MAX) {
|
|
|
|
|
return "VERTICAL_ALIGN_CENTER_SAFEAREA";
|
|
|
|
|
} else if (align == VERTICAL_ALIGN_DEFAULT) {
|
|
|
|
|
return "VERTICAL_ALIGN_SUBTOP";
|
|
|
|
|
}
|
|
|
|
|
return "VERTICAL_ALIGN_SUBTOP";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString MenuHAlignToStr(MENU_H_ALIGNMENT align) {
|
|
|
|
|
if (align == HORIZONTAL_ALIGN_SUBLEFT) {
|
|
|
|
|
return "HORIZONTAL_ALIGN_SUBLEFT";
|
|
|
|
|
} else if (align == HORIZONTAL_ALIGN_LEFT) {
|
|
|
|
|
return "HORIZONTAL_ALIGN_LEFT";
|
|
|
|
|
} else if (align == HORIZONTAL_ALIGN_CENTER) {
|
|
|
|
|
return "HORIZONTAL_ALIGN_CENTER";
|
|
|
|
|
} else if (align == HORIZONTAL_ALIGN_RIGHT) {
|
|
|
|
|
return "HORIZONTAL_ALIGN_RIGHT";
|
|
|
|
|
} else if (align == HORIZONTAL_ALIGN_FULLSCREEN) {
|
|
|
|
|
return "HORIZONTAL_ALIGN_FULLSCREEN";
|
|
|
|
|
} else if (align == HORIZONTAL_ALIGN_NOSCALE) {
|
|
|
|
|
return "HORIZONTAL_ALIGN_NOSCALE";
|
|
|
|
|
} else if (align == HORIZONTAL_ALIGN_TO640) {
|
|
|
|
|
return "HORIZONTAL_ALIGN_TO640";
|
|
|
|
|
} else if (align == HORIZONTAL_ALIGN_CENTER_SAFEAREA) {
|
|
|
|
|
return "HORIZONTAL_ALIGN_CENTER_SAFEAREA";
|
|
|
|
|
} else if (align == HORIZONTAL_ALIGN_MAX) {
|
|
|
|
|
return "HORIZONTAL_ALIGN_CENTER_SAFEAREA";
|
|
|
|
|
} else if (align == HORIZONTAL_ALIGN_DEFAULT) {
|
|
|
|
|
return "HORIZONTAL_ALIGN_SUBLEFT";
|
|
|
|
|
}
|
|
|
|
|
return "HORIZONTAL_ALIGN_SUBLEFT";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QColor ColorFromNormalized(float r, float g, float b, float a) {
|
|
|
|
|
// Ensure values are clamped between 0 and 1
|
|
|
|
|
r = qBound(0.0f, r, 1.0f);
|
|
|
|
|
g = qBound(0.0f, g, 1.0f);
|
|
|
|
|
b = qBound(0.0f, b, 1.0f);
|
|
|
|
|
a = qBound(0.0f, a, 1.0f);
|
|
|
|
|
|
|
|
|
|
// Convert to 0-255 scale
|
|
|
|
|
int red = static_cast<int>(r * 255);
|
|
|
|
|
int green = static_cast<int>(g * 255);
|
|
|
|
|
int blue = static_cast<int>(b * 255);
|
|
|
|
|
int alpha = static_cast<int>(a * 255);
|
|
|
|
|
|
|
|
|
|
return QColor(red, green, blue, alpha);
|
2025-01-17 00:23:50 -05:00
|
|
|
}
|
2025-01-09 17:54:44 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#endif // UTILS_H
|