#ifndef UTILS_H #define UTILS_H #include "enums.h" #include #include #include #include #include #include class Utils { public: 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; } 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; } 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(hash[0]); int g = static_cast(hash[1]); int b = static_cast(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; } 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(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(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; } /* 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; } static QString GetOpenFastFileName(QWidget *parent = nullptr) { // Open file dialog to steam apps const QString steamPath = "C:/Program Files (x86)/Steam/steamapps/common/Call of Duty World at War/zone/english/"; const QString fastFilePath = QFileDialog::getOpenFileName(parent, "Open Fast File", steamPath, "Fast File (*.ff);;All Files (*.*)"); if (fastFilePath.isNull()) { // User pressed cancel return ""; } else if (!QFile::exists(fastFilePath)) { QMessageBox::warning(parent, "Warning!", QString("%1 does not exist!.").arg(fastFilePath)); return ""; } return fastFilePath; } static QString GetOpenZoneFileName(QWidget *parent = nullptr) { // Open file dialog to steam apps const QString steamPath = "C:/Program Files (x86)/Steam/steamapps/common/Call of Duty World at War/zone/english/"; const QString zoneFilePath = QFileDialog::getOpenFileName(parent, "Open Zone File", steamPath, "Zone File (*.zone);;All Files (*.*)"); if (zoneFilePath.isNull()) { // User pressed cancel return ""; } else if (!QFile::exists(zoneFilePath)) { QMessageBox::warning(parent, "Warning!", QString("%1 does not exist!.").arg(zoneFilePath)); return nullptr; } return zoneFilePath; } 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"; } return "None"; } static QString FileTypeEnumToStr(FF_FILETYPE aFileType) { switch (aFileType) { case FILETYPE_NONE: return "None"; case FILETYPE_FAST_FILE: return "Fast File"; } return "None"; } static QString SignageEnumToStr(FF_SIGNAGE aSignage) { switch (aSignage) { case SIGNAGE_NONE: return "None"; case SIGNAGE_SIGNED: return "Signed"; case SIGNAGE_UNSIGNED: return "Unsigned"; } 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(r * 255); int green = static_cast(g * 255); int blue = static_cast(b * 255); int alpha = static_cast(a * 255); return QColor(red, green, blue, alpha); } }; #endif // UTILS_H