#include "ImXui/XurFormat.h" #include "ImXui/XurWriter.h" #include "ImXui/XurXml.h" #include #include #include #include #include #include #include #include #include #include "zlib.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #endif namespace { const char* TOOL_VERSION = "0.1.0"; enum class FileFormat { Unknown, XUI, XUR }; struct XurHeaderInfo { bool valid; uint32_t version; uint32_t flags; uint32_t fileSize; uint16_t sectionsCount; XurHeaderInfo() : valid(false), version(0), flags(0), fileSize(0), sectionsCount(0) {} }; std::string lower(const std::string& s) { std::string r = s; for (size_t i = 0; i < r.size(); i++) if (r[i] >= 'A' && r[i] <= 'Z') r[i] = (char)(r[i] - 'A' + 'a'); return r; } std::string ws2s(const std::wstring& ws) { return std::string(ws.begin(), ws.end()); } bool hasExtension(const std::string& path, const char* ext) { if (path.size() < strlen(ext)) return false; return lower(path.substr(path.size() - strlen(ext))) == ext; } FileFormat detectFormat(const std::string& path) { std::vector head(16, 0); { std::ifstream ifs(path, std::ios::binary); if (ifs) ifs.read((char*)head.data(), 16); } if (head.size() >= 4 && memcmp(head.data(), "XUIB", 4) == 0) return FileFormat::XUR; if (head.size() >= 1 && (head[0] == '<' || head[0] == 0xEF || head[0] == 0xFF || head[0] == 0xFE)) return FileFormat::XUI; if (hasExtension(path, ".xur")) return FileFormat::XUR; if (hasExtension(path, ".xui")) return FileFormat::XUI; return FileFormat::Unknown; } XurHeaderInfo readXurHeader(const std::string& path) { XurHeaderInfo info; std::ifstream ifs(path, std::ios::binary); if (!ifs) return info; uint8_t bytes[20]; ifs.read((char*)bytes, 20); if (ifs.gcount() < 20) return info; if (memcmp(bytes, "XUIB", 4) != 0) return info; auto be32 = [&](size_t off) -> uint32_t { return ((uint32_t)bytes[off] << 24) | ((uint32_t)bytes[off + 1] << 16) | ((uint32_t)bytes[off + 2] << 8) | (uint32_t)bytes[off + 3]; }; auto be16 = [&](size_t off) -> uint16_t { return (uint16_t)(((uint16_t)bytes[off] << 8) | (uint16_t)bytes[off + 1]); }; info.valid = true; info.version = be32(4); info.flags = be32(8); info.fileSize = be32(14); info.sectionsCount = be16(18); return info; } const char* formatName(FileFormat f) { switch (f) { case FileFormat::XUI: return "XUI v12"; case FileFormat::XUR: return "XUR"; default: return "unknown"; } } void usage() { std::cout << "XUITool " << TOOL_VERSION << " - ImXui alternative to XUIHelper / XuiTool\n" "Reads, writes and converts XUI and XUR files (Xbox 360 UI format).\n" "\n" "Usage:\n" " xuitool about\n" " Print version and supported formats.\n" " xuitool info \n" " Detect format and print a summary of a .xui / .xur file.\n" " xuitool validate \n" " Parse a file and report validity. Exit code 0 = valid.\n" " xuitool conv -s -o [-f xurv5|xuiv12]\n" " Convert a single file. Input format is auto-detected; output\n" " format comes from -f or is inferred from the output extension.\n" " xuitool massconv -s -o [-f xurv5|xuiv12] [-r]\n" " Convert every .xui / .xur file in a directory (recursive with -r).\n" " xuitool swf2xui [output.xui]\n" " Convert a Flash SWF to XUI XML (shapes, sprites, buttons).\n"; } bool loadObject(const std::string& path, FileFormat fmt, ImXui::XurFormat::XurObject& out) { if (fmt == FileFormat::XUR) { auto doc = ImXui::XurFormat::XurParser::parseFromFile(path); if (!doc || !ImXui::XurFormat::XurParser::validate(doc.get())) return false; out = doc->rootObject; return true; } out = ImXui::XurFormat::xuiFileToObject(path); return !out.className.empty(); } bool writeObject(const std::string& dst, const ImXui::XurFormat::XurObject& obj, bool toXur) { if (toXur) return ImXui::XurFormat::XurWriter::writeToFile(dst, obj); return ImXui::XurFormat::xurObjectToXuiXml(obj, dst); } void dumpObjectTree(const ImXui::XurFormat::XurObject& obj, int depth) { std::string indent((size_t)depth * 2, ' '); std::cout << indent << obj.className << "\n"; for (auto& p : obj.properties) { if (p.name == "Id" && p.type == ImXui::XurFormat::XurPropertyType_String) std::cout << indent << " id=" << ws2s(p.value.stringVal) << "\n"; else if (p.name == "Visual" && p.type == ImXui::XurFormat::XurPropertyType_String) std::cout << indent << " visual=" << ws2s(p.value.stringVal) << "\n"; else if (p.name == "Text" && p.type == ImXui::XurFormat::XurPropertyType_String) std::cout << indent << " text=\"" << ws2s(p.value.stringVal) << "\"\n"; } for (auto& c : obj.children) dumpObjectTree(c, depth + 1); } int cmdAbout() { std::cout << "XUITool " << TOOL_VERSION << "\n" "An ImXui-based alternative to XUIHelper / XuiTool for editing the Xbox 360 UI format.\n" "\n" "Supported formats:\n" " XUI v12 - XML scene files (version=\"000c\"), read/write\n" " XUR v5 - compiled binary scenes, read/write\n" " XUR v8+ - detected and reported, but not read/write capable in this build\n" "\n" "Library: ImXui (XurFormat, XurParser, XurWriter, XurXml)\n"; return 0; } int cmdInfo(const std::string& path) { FileFormat fmt = detectFormat(path); std::cout << "File: " << path << "\n"; std::cout << "Type: " << formatName(fmt) << "\n"; if (fmt == FileFormat::XUR) { XurHeaderInfo hdr = readXurHeader(path); if (!hdr.valid) { std::cerr << "Error: not a XUR file\n"; return 1; } std::cout << "XUR version: " << hdr.version << "\n"; std::cout << "Flags: " << hdr.flags << "\n"; std::cout << "File size: " << hdr.fileSize << " bytes\n"; std::cout << "Sections: " << hdr.sectionsCount << "\n"; if (hdr.version != 5) { std::cout << "Note: this build can only read/write XUR v5.\n"; return 1; } } ImXui::XurFormat::XurObject obj; if (!loadObject(path, fmt, obj)) { std::cerr << "Error: failed to parse file\n"; return 1; } std::cout << "Root object: " << obj.className << " (" << obj.properties.size() << " props, " << obj.children.size() << " children)\n"; std::cout << "Object tree:\n"; dumpObjectTree(obj, 0); return 0; } int cmdValidate(const std::string& path) { FileFormat fmt = detectFormat(path); if (fmt == FileFormat::XUR) { XurHeaderInfo hdr = readXurHeader(path); if (!hdr.valid) { std::cerr << "Error: not a XUR file\n"; return 1; } if (hdr.version != 5) { std::cerr << "Error: XUR v" << hdr.version << " is not supported (only v5)\n"; return 1; } auto doc = ImXui::XurFormat::XurParser::parseFromFile(path); if (!doc || !ImXui::XurFormat::XurParser::validate(doc.get())) { std::cerr << "Error: invalid XUR v5\n"; return 1; } std::cout << "OK: valid XUR v5\n"; return 0; } if (fmt == FileFormat::XUI) { ImXui::XurFormat::XurObject obj = ImXui::XurFormat::xuiFileToObject(path); if (obj.className.empty()) { std::cerr << "Error: invalid XUI v12\n"; return 1; } std::cout << "OK: valid XUI v12\n"; return 0; } std::cerr << "Error: unrecognized file format\n"; return 1; } bool convertFile(const std::string& src, const std::string& dst, FileFormat srcFmt, const std::string& outFormat) { ImXui::XurFormat::XurObject obj; if (!loadObject(src, srcFmt, obj)) { std::cerr << "Error: failed to read '" << src << "'\n"; return false; } bool toXur; if (outFormat == "xurv5") toXur = true; else if (outFormat == "xuiv12") toXur = false; else toXur = hasExtension(dst, ".xur"); if (!writeObject(dst, obj, toXur)) { std::cerr << "Error: failed to write '" << dst << "'\n"; return false; } std::cout << "Converted " << src << " -> " << dst << "\n"; return true; } int cmdConv(int argc, char* argv[]) { std::string src, dst, outFormat; for (int i = 2; i < argc; i++) { std::string a = argv[i]; if (a == "-s" && i + 1 < argc) src = argv[++i]; else if (a == "-o" && i + 1 < argc) dst = argv[++i]; else if (a == "-f" && i + 1 < argc) outFormat = lower(argv[++i]); else { std::cerr << "Unknown argument: " << a << "\n"; usage(); return 1; } } if (src.empty() || dst.empty()) { std::cerr << "conv requires -s and -o \n"; usage(); return 1; } if (!outFormat.empty() && outFormat != "xurv5" && outFormat != "xuiv12") { std::cerr << "Unknown output format '" << outFormat << "' (must be xurv5 or xuiv12)\n"; return 1; } FileFormat srcFmt = detectFormat(src); if (srcFmt == FileFormat::Unknown) { std::cerr << "Error: cannot detect format of '" << src << "'\n"; return 1; } if (srcFmt == FileFormat::XUR) { XurHeaderInfo hdr = readXurHeader(src); if (hdr.valid && hdr.version != 5) { std::cerr << "Error: XUR v" << hdr.version << " is not supported (only v5)\n"; return 1; } } return convertFile(src, dst, srcFmt, outFormat) ? 0 : 1; } #ifdef _WIN32 std::vector listFiles(const std::string& dir, bool recursive) { std::vector files; std::string pattern = dir + "\\*"; WIN32_FIND_DATAA fd; HANDLE h = FindFirstFileA(pattern.c_str(), &fd); if (h == INVALID_HANDLE_VALUE) return files; do { std::string name = fd.cFileName; if (name == "." || name == "..") continue; std::string full = dir + "\\" + name; if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (recursive) { std::vector sub = listFiles(full, recursive); files.insert(files.end(), sub.begin(), sub.end()); } } else if (hasExtension(name, ".xui") || hasExtension(name, ".xur")) { files.push_back(full); } } while (FindNextFileA(h, &fd)); FindClose(h); return files; } #else #include #include static std::vector listFiles(const std::string& dir, bool recursive) { std::vector files; DIR* d = opendir(dir.c_str()); if (!d) return files; struct dirent* ent; while ((ent = readdir(d)) != nullptr) { std::string name = ent->d_name; if (name == "." || name == "..") continue; std::string full = dir + "/" + name; struct stat st; if (stat(full.c_str(), &st) != 0) continue; if (S_ISDIR(st.st_mode)) { if (recursive) { std::vector sub = listFiles(full, recursive); files.insert(files.end(), sub.begin(), sub.end()); } } else if (hasExtension(name, ".xui") || hasExtension(name, ".xur")) { files.push_back(full); } } closedir(d); return files; } #endif int cmdMassConv(int argc, char* argv[]) { std::string srcDir, dstDir, outFormat; bool recursive = false; for (int i = 2; i < argc; i++) { std::string a = argv[i]; if (a == "-s" && i + 1 < argc) srcDir = argv[++i]; else if (a == "-o" && i + 1 < argc) dstDir = argv[++i]; else if (a == "-f" && i + 1 < argc) outFormat = lower(argv[++i]); else if (a == "-r") recursive = true; else { std::cerr << "Unknown argument: " << a << "\n"; usage(); return 1; } } if (srcDir.empty() || dstDir.empty()) { std::cerr << "massconv requires -s and -o \n"; usage(); return 1; } if (!outFormat.empty() && outFormat != "xurv5" && outFormat != "xuiv12") { std::cerr << "Unknown output format '" << outFormat << "' (must be xurv5 or xuiv12)\n"; return 1; } if (outFormat.empty()) outFormat = "xuiv12"; #ifdef _WIN32 CreateDirectoryA(dstDir.c_str(), nullptr); #else mkdir(dstDir.c_str(), 0755); #endif std::vector files = listFiles(srcDir, recursive); int ok = 0, failed = 0, skipped = 0; for (auto& src : files) { FileFormat srcFmt = detectFormat(src); if (srcFmt == FileFormat::Unknown) { skipped++; continue; } size_t sep = src.find_last_of("\\/"); std::string base = (sep == std::string::npos) ? src : src.substr(sep + 1); size_t dot = base.find_last_of('.'); if (dot != std::string::npos) base = base.substr(0, dot); std::string dst = dstDir + "\\" + base + (outFormat == "xurv5" ? ".xur" : ".xui"); bool srcIsXur = (srcFmt == FileFormat::XUR); bool dstIsXur = (outFormat == "xurv5"); if (srcIsXur == dstIsXur) { std::cout << "Skipping (already " << (srcIsXur ? "xurv5" : "xuiv12") << "): " << src << "\n"; skipped++; continue; } if (srcFmt == FileFormat::XUR) { XurHeaderInfo hdr = readXurHeader(src); if (hdr.valid && hdr.version != 5) { std::cout << "Skipping (unsupported XUR v" << hdr.version << "): " << src << "\n"; skipped++; continue; } } if (convertFile(src, dst, srcFmt, outFormat)) ok++; else failed++; } std::cout << "massconv: " << ok << " converted, " << failed << " failed, " << skipped << " skipped\n"; return failed == 0 ? 0 : 1; } static bool decompressSWF(const uint8_t* compressed, size_t csize, std::vector& out, size_t uncompressedSize) { if (csize < 2) return false; out.reserve(uncompressedSize > 0 ? uncompressedSize : 65536); z_stream zs; memset(&zs, 0, sizeof(zs)); // CWS is a zlib stream; skip the 2-byte header and inflate the raw deflate if (inflateInit2(&zs, -MAX_WBITS) != Z_OK) return false; zs.next_in = (Bytef*)(compressed + 2); zs.avail_in = (uInt)(csize - 2); std::vector buf(65536); int ret = Z_OK; while (ret != Z_STREAM_END) { zs.next_out = buf.data(); zs.avail_out = (uInt)buf.size(); ret = inflate(&zs, Z_NO_FLUSH); if (ret != Z_OK && ret != Z_STREAM_END) { inflateEnd(&zs); return false; } out.insert(out.end(), buf.begin(), buf.end() - zs.avail_out); } inflateEnd(&zs); return true; } #pragma pack(push, 1) struct SwfHeader { uint8_t signature[3]; uint8_t version; uint32_t fileLength; }; #pragma pack(pop) struct SwfRect { int32_t xMin, xMax, yMin, yMax; }; struct SwfTag { uint16_t type; uint32_t length; std::vector data; }; static uint8_t readU8(const uint8_t*& p) { return *p++; } static uint16_t readU16(const uint8_t*& p) { uint16_t v; memcpy(&v, p, 2); p += 2; return v; } static uint32_t readU32(const uint8_t*& p) { uint32_t v; memcpy(&v, p, 4); p += 4; return v; } struct SwfMatrix { float scaleX, scaleY; float rotateSkew0, rotateSkew1; float translateX, translateY; }; static SwfRect readRect(const uint8_t*& p) { SwfRect r = {}; uint8_t nBits = (*p) >> 3; uint32_t bitBuf = 0; int bitsRead = 0; auto readBits = [&](int n) -> uint32_t { uint32_t val = 0; for (int i = 0; i < n; i++) { if (bitsRead % 8 == 0) { bitBuf = *p++; } val = (val << 1) | ((bitBuf >> (7 - (bitsRead % 8))) & 1); bitsRead++; } return val; }; bitBuf = *p++; bitsRead = 5; int32_t vals[4]; for (int i = 0; i < 4; i++) { uint32_t v = readBits(nBits); vals[i] = (int32_t)v; } r.xMin = vals[0] / 20; r.xMax = vals[1] / 20; r.yMin = vals[2] / 20; r.yMax = vals[3] / 20; if (bitsRead % 8 != 0) { int skip = 8 - (bitsRead % 8); readBits(skip); } return r; } static SwfMatrix readMatrix(const uint8_t*& p) { SwfMatrix m = {1, 0, 0, 1, 0, 0}; uint32_t bitBuf = 0; int bitsRead = 0; auto readBits = [&](int n) -> uint32_t { uint32_t val = 0; for (int i = 0; i < n; i++) { if (bitsRead % 8 == 0) { bitBuf = *p++; } val = (val << 1) | ((bitBuf >> (7 - (bitsRead % 8))) & 1); bitsRead++; } return val; }; auto readSBits = [&](int n) -> int32_t { uint32_t v = readBits(n); if (v & (1u << (n - 1))) { v |= ~((1u << n) - 1); } return (int32_t)v; }; uint8_t hasScale = readBits(1); if (hasScale) { int nScaleBits = readBits(5); m.scaleX = (float)readSBits(nScaleBits) / 65536.0f; m.scaleY = (float)readSBits(nScaleBits) / 65536.0f; } uint8_t hasRotate = readBits(1); if (hasRotate) { int nRotateBits = readBits(5); m.rotateSkew0 = (float)readSBits(nRotateBits) / 65536.0f; m.rotateSkew1 = (float)readSBits(nRotateBits) / 65536.0f; } int nTranslateBits = readBits(5); if (nTranslateBits > 0) { m.translateX = (float)readSBits(nTranslateBits) / 20.0f; m.translateY = (float)readSBits(nTranslateBits) / 20.0f; } if (bitsRead % 8 != 0) { int skip = 8 - (bitsRead % 8); readBits(skip); } return m; } class SwfParser { public: std::vector data; SwfHeader header; SwfRect frameSize; float frameRate; uint16_t frameCount; std::vector tags; bool parse(const std::string& path) { std::ifstream ifs(path, std::ios::binary | std::ios::ate); if (!ifs) return false; std::streamsize sz = ifs.tellg(); ifs.seekg(0); data.resize((size_t)sz); if (!ifs.read((char*)data.data(), sz)) return false; const uint8_t* p = data.data(); memcpy(&header, p, sizeof(SwfHeader)); p += sizeof(SwfHeader); if (header.signature[0] != 'F' && header.signature[0] != 'C') return false; if (header.signature[1] != 'W' || header.signature[2] != 'S') return false; bool compressed = (header.signature[0] == 'C'); if (compressed) { size_t uncompLen = header.fileLength; const uint8_t* compStart = p; size_t compSize = data.size() - (size_t)(p - data.data()); std::vector decomp; if (!decompressSWF(compStart, compSize, decomp, uncompLen)) { std::cerr << " Decompression failed\n"; return false; } size_t hdrBytes = (size_t)(p - data.data()); std::vector newData(hdrBytes + decomp.size()); memcpy(newData.data(), data.data(), hdrBytes); if (!decomp.empty()) memcpy(newData.data() + hdrBytes, decomp.data(), decomp.size()); newData.swap(data); p = data.data() + sizeof(SwfHeader); header.signature[0] = 'F'; } frameSize = readRect(p); frameRate = (float)readU16(p) / 256.0f; frameCount = readU16(p); while ((size_t)(p - data.data()) < data.size()) { uint16_t tagTypeAndLen = readU16(p); uint16_t tagType = tagTypeAndLen >> 6; uint32_t tagLen = tagTypeAndLen & 0x3F; if (tagLen == 0x3F) { tagLen = readU32(p); } if (tagType == 0) break; SwfTag tag; tag.type = tagType; tag.length = tagLen; if (tagLen > 0 && (size_t)(p - data.data() + tagLen) <= data.size()) { tag.data.assign(p, p + tagLen); p += tagLen; } tags.push_back(tag); } return true; } }; static std::string escapeXml(const std::string& s) { std::string r; for (char c : s) { switch (c) { case '&': r += "&"; break; case '<': r += "<"; break; case '>': r += ">"; break; case '\"': r += """; break; case '\'': r += "'"; break; default: r += c; } } return r; } struct XuiElement { std::string className; std::map props; std::vector children; }; static void writeXuiElement(std::ostream& os, const XuiElement& el, int depth) { std::string indent(depth * 2, ' '); os << indent << "<" << el.className << ">\n"; if (!el.props.empty()) { os << indent << " \n"; for (auto& prop : el.props) { os << indent << " <" << prop.first << ">" << escapeXml(prop.second) << "\n"; } os << indent << " \n"; } for (auto& child : el.children) { writeXuiElement(os, child, depth + 1); } os << indent << "\n"; } static void addProp(XuiElement& el, const std::string& key, const std::string& val) { el.props[key] = val; } static std::string floatStr(float v) { std::ostringstream ss; ss << v; return ss.str(); } int cmdSwf2Xui(int argc, char* argv[]) { if (argc < 2) { std::cerr << "Usage: xuitool swf2xui [output.xui]\n"; std::cerr << " Converts basic Flash SWF files to XUI XML format.\n"; std::cerr << " Handles shapes, sprites, place/remove objects, and buttons.\n"; return 1; } std::string inPath = argv[1]; std::string outPath; if (argc >= 3) { outPath = argv[2]; } else { if (inPath.size() >= 4 && inPath.substr(inPath.size() - 4) == ".swf") { outPath = inPath.substr(0, inPath.size() - 4) + ".xui"; } else { outPath = inPath + ".xui"; } } SwfParser parser; if (!parser.parse(inPath)) { std::cerr << "Error: failed to parse SWF file '" << inPath << "'\n"; std::cerr << "Note: Corrupt or unsupported SWF format\n"; return 1; } std::cout << "SWF: " << inPath << "\n"; std::cout << " Version: " << (int)parser.header.version << "\n"; std::cout << " FrameSize: " << parser.frameSize.xMax << "x" << parser.frameSize.yMax << "\n"; std::cout << " FrameRate: " << parser.frameRate << "\n"; std::cout << " FrameCount: " << parser.frameCount << "\n"; std::cout << " Tags: " << parser.tags.size() << "\n"; std::cout << " Tag types: "; for (auto& tag : parser.tags) std::cout << tag.type << "(" << tag.length << ") "; std::cout << "\n"; XuiElement root; root.className = "XuiCanvas"; addProp(root, "Width", floatStr((float)parser.frameSize.xMax)); addProp(root, "Height", floatStr((float)parser.frameSize.yMax)); std::map>> characters; for (auto& tag : parser.tags) { if (tag.type >= 1 && tag.type <= 88) { switch (tag.type) { case 76: { const uint8_t* sp = tag.data.data(); const uint8_t* send = tag.data.data() + tag.data.size(); uint16_t numSymbols = readU16(sp); std::cout << " SymbolClass: " << numSymbols << " symbols\n"; for (uint16_t si = 0; si < numSymbols && sp < send; si++) { uint16_t tagId = readU16(sp); std::string name; while (sp < send && *sp) { name += (char)*sp++; } if (sp < send) sp++; std::cout << " Symbol " << tagId << " -> " << name << "\n"; } break; } case 2: case 22: case 32: case 83: case 11: case 33: case 37: case 39: case 34: case 35: { const uint8_t* p = tag.data.data(); uint16_t charId = readU16(p); characters[charId] = std::make_pair(tag.type, tag.data); std::cout << " CharDef id=" << charId << " type=" << tag.type << " len=" << tag.length << "\n"; break; } } } } std::map displayElements; for (auto& tag : parser.tags) { switch (tag.type) { case 26: { const uint8_t* tp = tag.data.data(); const uint8_t* tend = tag.data.data() + tag.data.size(); uint8_t flags = readU8(tp); uint8_t charIdByte = readU8(tp); uint16_t charId = charIdByte; SwfMatrix mat = {1, 0, 0, 1, 0, 0}; if (tp < tend) mat = readMatrix(tp); static int po26Count = 0; po26Count++; auto it = characters.find(charId); if (it != characters.end()) { XuiElement el; if (it->second.first == 39) { el.className = "XuiScene"; } else if (it->second.first >= 11 && it->second.first <= 37) { el.className = "XuiLabel"; } else { el.className = "XuiImage"; } addProp(el, "Id", "swf_char_" + std::to_string(charId) + "_p" + std::to_string(po26Count)); addProp(el, "X", floatStr(mat.translateX)); addProp(el, "Y", floatStr(mat.translateY)); addProp(el, "Width", "100.000000"); addProp(el, "Height", "100.000000"); addProp(el, "Show", "true"); std::cout << " PlaceObject26 flags=0x" << std::hex << (int)flags << std::dec << " charId=" << charId << " pos=(" << mat.translateX << "," << mat.translateY << ")\n"; displayElements[po26Count] = el; } else { std::cout << " PlaceObject26 flags=0x" << std::hex << (int)flags << std::dec << " charId=" << charId << " NOT FOUND\n"; } break; } case 28: { const uint8_t* tp = tag.data.data(); uint16_t charId = readU16(tp); int32_t depth = readU16(tp); std::cout << " RemoveObject28 charId=" << charId << " depth=" << depth << "\n"; displayElements.erase(depth); break; } case 33: { const uint8_t* tp = tag.data.data(); int32_t depth = readU16(tp); std::cout << " RemoveObject33 depth=" << depth << "\n"; displayElements.erase(depth); break; } case 43: { const uint8_t* tp = tag.data.data(); const uint8_t* tend = tag.data.data() + tag.data.size(); uint8_t flags = readU8(tp); int32_t depth = readU16(tp); uint16_t charId = 0; SwfMatrix mat = {1, 0, 0, 1, 0, 0}; if (flags & 0x02) { charId = readU16(tp); } if (flags & 0x04) { if (tp < tend) mat = readMatrix(tp); } if (flags & 0x08) { } if (flags & 0x10) { readU16(tp); } if (flags & 0x20) { while (tp < tend && *tp) tp++; if (tp < tend) tp++; } if (flags & 0x40) { readU16(tp); } if (flags & 0x80) { } std::cout << " PlaceObject43 depth=" << depth << " charId=" << charId << " flags=0x" << std::hex << (int)flags << std::dec << " pos=(" << mat.translateX << "," << mat.translateY << ") found=" << characters.count(charId) << "\n"; if (flags & 0x01) { displayElements.erase(depth); } else { auto it = characters.find(charId); if (it != characters.end()) { XuiElement el; if (it->second.first == 39) { el.className = "XuiScene"; } else if (it->second.first >= 34 && it->second.first <= 35) { el.className = "XuiButton"; } else if (it->second.first >= 11 && it->second.first <= 37) { el.className = "XuiLabel"; } else { el.className = "XuiImage"; } addProp(el, "Id", "swf_char_" + std::to_string(charId) + "_d" + std::to_string(depth)); addProp(el, "X", floatStr(mat.translateX)); addProp(el, "Y", floatStr(mat.translateY)); addProp(el, "Width", floatStr((float)parser.frameSize.xMax / 10.0f)); addProp(el, "Height", floatStr((float)parser.frameSize.yMax / 10.0f)); displayElements[depth] = el; } else { XuiElement el; el.className = "XuiControl"; addProp(el, "Id", "swf_unknown_d" + std::to_string(depth)); addProp(el, "X", floatStr(mat.translateX)); addProp(el, "Y", floatStr(mat.translateY)); addProp(el, "Width", "50.000000"); addProp(el, "Height", "50.000000"); displayElements[depth] = el; } } break; } case 70: { const uint8_t* tp = tag.data.data(); const uint8_t* tend = tag.data.data() + tag.data.size(); uint8_t flags = readU8(tp); uint8_t flags2 = readU8(tp); int32_t depth = readU16(tp); std::string className; while (tp < tend && *tp) { className += (char)*tp; } if (tp < tend) tp++; std::string instanceName; { const uint8_t* s = tend - 1; while (s > tp && *s == 0) s--; const uint8_t* strEnd = s + 1; while (s > tp && *s != 0 && *s >= 0x20 && *s < 0x7F) s--; s++; if (strEnd > s) { for (const uint8_t* p2 = s; p2 < strEnd; p2++) instanceName += (char)*p2; while (!instanceName.empty() && !((instanceName[0] >= 'A' && instanceName[0] <= 'Z') || (instanceName[0] >= 'a' && instanceName[0] <= 'z') || (instanceName[0] >= '0' && instanceName[0] <= '9') || instanceName[0] == '_')) { instanceName.erase(0, 1); } } } std::cout << " PlaceObject70 depth=" << depth << " class=\"" << className << "\""; if (!instanceName.empty()) std::cout << " instance=\"" << instanceName << "\""; std::cout << " tagLen=" << tag.length << "\n"; if (flags & 0x01) { displayElements.erase(depth); } else { XuiElement el; if (className.find("Button") != std::string::npos || className.find("button") != std::string::npos) { el.className = "XuiButton"; } else if (className.find("Label") != std::string::npos || className.find("Text") != std::string::npos || className.find("text") != std::string::npos) { el.className = "XuiLabel"; } else if (className.find("Image") != std::string::npos || className.find("Graphic") != std::string::npos) { el.className = "XuiImage"; } else if (className.find("Scene") != std::string::npos || className.find("Menu") != std::string::npos || className.find("Panel") != std::string::npos || className.find("Dialog") != std::string::npos || className.find("Canvas") != std::string::npos) { el.className = "XuiScene"; } else { el.className = "XuiControl"; } std::string id = !instanceName.empty() ? instanceName : className; addProp(el, "Id", id); addProp(el, "ClassName", className); addProp(el, "X", "0.000000"); addProp(el, "Y", "0.000000"); addProp(el, "Width", floatStr((float)parser.frameSize.xMax / 10.0f)); addProp(el, "Height", floatStr((float)parser.frameSize.yMax / 10.0f)); displayElements[depth] = el; } break; } } } for (auto& de : displayElements) { root.children.push_back(de.second); } std::ofstream ofs(outPath); if (!ofs) { std::cerr << "Error: cannot write to '" << outPath << "'\n"; return 1; } writeXuiElement(ofs, root, 0); std::cout << "Wrote " << outPath << "\n"; return 0; } } int main(int argc, char* argv[]) { if (argc < 2) { usage(); return 1; } std::string cmd = lower(argv[1]); if (cmd == "about") return cmdAbout(); if (cmd == "info") { if (argc < 3) { std::cerr << "Usage: xuitool info \n"; return 1; } return cmdInfo(argv[2]); } if (cmd == "validate") { if (argc < 3) { std::cerr << "Usage: xuitool validate \n"; return 1; } return cmdValidate(argv[2]); } if (cmd == "conv") return cmdConv(argc, argv); if (cmd == "massconv") return cmdMassConv(argc, argv); if (cmd == "swf2xui") return cmdSwf2Xui(argc - 1, argv + 1); std::cerr << "Unknown command: " << argv[1] << "\n"; usage(); return 1; }