#!/bin/bash # DragonX Wallet - Windows Cross-Compile Build Script # Requires: mingw-w64 toolchain with POSIX threads # # On Ubuntu/Debian: # sudo apt install mingw-w64 cmake # sudo update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix # sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix # # On Arch Linux: # sudo pacman -S mingw-w64-gcc cmake set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" BUILD_DIR="$SCRIPT_DIR/build/windows" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' echo -e "${GREEN}DragonX Wallet - Windows Cross-Compile Build${NC}" echo "==============================================" # Check for mingw toolchain (prefer POSIX variant) MINGW_GCC="" MINGW_GXX="" if command -v x86_64-w64-mingw32-gcc-posix &> /dev/null; then MINGW_GCC="x86_64-w64-mingw32-gcc-posix" MINGW_GXX="x86_64-w64-mingw32-g++-posix" echo -e "${GREEN}Using POSIX thread model (recommended)${NC}" elif command -v x86_64-w64-mingw32-gcc &> /dev/null; then # Check if it's the posix variant if x86_64-w64-mingw32-gcc -v 2>&1 | grep -q "posix"; then MINGW_GCC="x86_64-w64-mingw32-gcc" MINGW_GXX="x86_64-w64-mingw32-g++" echo -e "${GREEN}Using POSIX thread model${NC}" else echo -e "${YELLOW}Warning: Using win32 thread model - may have threading issues${NC}" echo "Run these commands to switch to POSIX threads:" echo " sudo update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix" echo " sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix" MINGW_GCC="x86_64-w64-mingw32-gcc" MINGW_GXX="x86_64-w64-mingw32-g++" fi else echo -e "${RED}Error: mingw-w64 not found!${NC}" echo "Install with:" echo " Ubuntu/Debian: sudo apt install mingw-w64" echo " Arch Linux: sudo pacman -S mingw-w64-gcc" exit 1 fi echo "C compiler: $MINGW_GCC" echo "C++ compiler: $MINGW_GXX" # Clean and create build directory if [ "$1" == "clean" ]; then echo -e "${YELLOW}Cleaning build directory...${NC}" rm -rf "$BUILD_DIR" fi mkdir -p "$BUILD_DIR" cd "$BUILD_DIR" # Create CMake toolchain file for MinGW cat > mingw-toolchain.cmake << EOF # MinGW-w64 Cross-Compilation Toolchain set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_SYSTEM_PROCESSOR x86_64) # Compilers (using POSIX threads) set(CMAKE_C_COMPILER $MINGW_GCC) set(CMAKE_CXX_COMPILER $MINGW_GXX) set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) # Target environment set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) # Search for programs in the build host directories set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # Search for libraries and headers in the target directories set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # Static linking - no DLLs needed set(CMAKE_EXE_LINKER_FLAGS "-static -static-libgcc -static-libstdc++ -Wl,-Bstatic,--whole-archive -lwinpthread -Wl,--no-whole-archive") set(CMAKE_CXX_FLAGS "\${CMAKE_CXX_FLAGS} -static") set(CMAKE_C_FLAGS "\${CMAKE_C_FLAGS} -static") # Prefer static libraries set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") set(BUILD_SHARED_LIBS OFF) EOF # ----------------------------------------------------------------------------- # Generate embedded resources (Sapling params, asmap.dat) # ----------------------------------------------------------------------------- echo -e "${GREEN}Generating embedded resources...${NC}" GENERATED_DIR="$BUILD_DIR/generated" mkdir -p "$GENERATED_DIR" # Find resource files SAPLING_SPEND="" SAPLING_OUTPUT="" ASMAP_DAT="" # Look for Sapling params PARAMS_PATHS=( "$HOME/.zcash-params" "$HOME/.hush-params" "$SCRIPT_DIR/../SilentDragonX" "$SCRIPT_DIR/prebuilt-binaries/dragonxd-win" "$SCRIPT_DIR" ) for ppath in "${PARAMS_PATHS[@]}"; do if [ -f "$ppath/sapling-spend.params" ] && [ -f "$ppath/sapling-output.params" ]; then SAPLING_SPEND="$ppath/sapling-spend.params" SAPLING_OUTPUT="$ppath/sapling-output.params" echo " Found Sapling params in $ppath" break fi done # Look for asmap.dat ASMAP_PATHS=( "$HOME/dragonx/asmap.dat" "$HOME/dragonx/contrib/asmap/asmap.dat" "$SCRIPT_DIR/../asmap.dat" "$SCRIPT_DIR/asmap.dat" "$SCRIPT_DIR/../SilentDragonX/asmap.dat" ) for apath in "${ASMAP_PATHS[@]}"; do if [ -f "$apath" ]; then ASMAP_DAT="$apath" echo " Found asmap.dat at $apath" break fi done # --------------------------------------------------------------------------- # Generate embedded_data.h using INCBIN (assembler .incbin directive). # # Instead of converting binaries to giant hex arrays (530MB+ C source), # we copy the raw files into generated/res/ and generate a small header # that uses INCBIN() macros. The assembler streams the bytes directly # into the object file, using near-zero compile-time RAM. # --------------------------------------------------------------------------- if [ -n "$SAPLING_SPEND" ] && [ -n "$SAPLING_OUTPUT" ]; then echo -e "${GREEN}Embedding resources via INCBIN (assembler .incbin)...${NC}" # Stage raw binaries into generated/res/ so .incbin can find them EMBED_RES_DIR="$GENERATED_DIR/res" mkdir -p "$EMBED_RES_DIR" cp -f "$SAPLING_SPEND" "$EMBED_RES_DIR/sapling-spend.params" echo " Staged sapling-spend.params ($(du -h "$SAPLING_SPEND" | cut -f1))" cp -f "$SAPLING_OUTPUT" "$EMBED_RES_DIR/sapling-output.params" echo " Staged sapling-output.params ($(du -h "$SAPLING_OUTPUT" | cut -f1))" if [ -n "$ASMAP_DAT" ]; then cp -f "$ASMAP_DAT" "$EMBED_RES_DIR/asmap.dat" echo " Staged asmap.dat ($(du -h "$ASMAP_DAT" | cut -f1))" HAS_ASMAP=1 else HAS_ASMAP=0 fi # Start writing the header — use absolute paths for .incbin cat > "$GENERATED_DIR/embedded_data.h" << HEADER_START // Auto-generated embedded resource data — INCBIN edition // DO NOT EDIT — generated by build-windows.sh // // Uses assembler .incbin directive via incbin.h so the compiler never // has to parse hundreds of millions of hex literals. Compile-time RAM // drops from 12 GB+ to well under 1 GB. #pragma once #include #include #include "incbin.h" // ---- Sapling params (always present when this header exists) ---- INCBIN(sapling_spend_params, "$EMBED_RES_DIR/sapling-spend.params"); INCBIN(sapling_output_params, "$EMBED_RES_DIR/sapling-output.params"); HEADER_START # asmap.dat if [ "$HAS_ASMAP" = "1" ]; then echo "INCBIN(asmap_dat, \"$EMBED_RES_DIR/asmap.dat\");" >> "$GENERATED_DIR/embedded_data.h" else echo 'extern "C" { static const uint8_t* g_asmap_dat_data = nullptr; }' >> "$GENERATED_DIR/embedded_data.h" echo 'static const unsigned int g_asmap_dat_size = 0;' >> "$GENERATED_DIR/embedded_data.h" fi echo "" >> "$GENERATED_DIR/embedded_data.h" # Daemon binaries DAEMON_DIR="$SCRIPT_DIR/prebuilt-binaries/dragonxd-win" if [ -d "$DAEMON_DIR" ] && [ -f "$DAEMON_DIR/hushd.exe" ]; then echo -e "${GREEN}Embedding daemon binaries via INCBIN...${NC}" echo "" >> "$GENERATED_DIR/embedded_data.h" echo "#define HAS_EMBEDDED_DAEMON 1" >> "$GENERATED_DIR/embedded_data.h" echo "" >> "$GENERATED_DIR/embedded_data.h" cp -f "$DAEMON_DIR/hushd.exe" "$EMBED_RES_DIR/hushd.exe" echo " Staged hushd.exe ($(du -h "$DAEMON_DIR/hushd.exe" | cut -f1))" echo "INCBIN(hushd_exe, \"$EMBED_RES_DIR/hushd.exe\");" >> "$GENERATED_DIR/embedded_data.h" if [ -f "$DAEMON_DIR/hush-cli.exe" ]; then cp -f "$DAEMON_DIR/hush-cli.exe" "$EMBED_RES_DIR/hush-cli.exe" echo " Staged hush-cli.exe ($(du -h "$DAEMON_DIR/hush-cli.exe" | cut -f1))" echo "INCBIN(hush_cli_exe, \"$EMBED_RES_DIR/hush-cli.exe\");" >> "$GENERATED_DIR/embedded_data.h" else echo 'extern "C" { static const uint8_t* g_hush_cli_exe_data = nullptr; }' >> "$GENERATED_DIR/embedded_data.h" echo 'static const unsigned int g_hush_cli_exe_size = 0;' >> "$GENERATED_DIR/embedded_data.h" fi echo "" >> "$GENERATED_DIR/embedded_data.h" if [ -f "$DAEMON_DIR/dragonxd.bat" ]; then cp -f "$DAEMON_DIR/dragonxd.bat" "$EMBED_RES_DIR/dragonxd.bat" echo " Staged dragonxd.bat" echo "INCBIN(dragonxd_bat, \"$EMBED_RES_DIR/dragonxd.bat\");" >> "$GENERATED_DIR/embedded_data.h" else echo 'extern "C" { static const uint8_t* g_dragonxd_bat_data = nullptr; }' >> "$GENERATED_DIR/embedded_data.h" echo 'static const unsigned int g_dragonxd_bat_size = 0;' >> "$GENERATED_DIR/embedded_data.h" fi echo "" >> "$GENERATED_DIR/embedded_data.h" if [ -f "$DAEMON_DIR/hush-tx.exe" ]; then cp -f "$DAEMON_DIR/hush-tx.exe" "$EMBED_RES_DIR/hush-tx.exe" echo " Staged hush-tx.exe ($(du -h "$DAEMON_DIR/hush-tx.exe" | cut -f1))" echo "INCBIN(hush_tx_exe, \"$EMBED_RES_DIR/hush-tx.exe\");" >> "$GENERATED_DIR/embedded_data.h" else echo 'extern "C" { static const uint8_t* g_hush_tx_exe_data = nullptr; }' >> "$GENERATED_DIR/embedded_data.h" echo 'static const unsigned int g_hush_tx_exe_size = 0;' >> "$GENERATED_DIR/embedded_data.h" fi echo "" >> "$GENERATED_DIR/embedded_data.h" if [ -f "$DAEMON_DIR/dragonx-cli.bat" ]; then cp -f "$DAEMON_DIR/dragonx-cli.bat" "$EMBED_RES_DIR/dragonx-cli.bat" echo " Staged dragonx-cli.bat" echo "INCBIN(dragonx_cli_bat, \"$EMBED_RES_DIR/dragonx-cli.bat\");" >> "$GENERATED_DIR/embedded_data.h" else echo 'extern "C" { static const uint8_t* g_dragonx_cli_bat_data = nullptr; }' >> "$GENERATED_DIR/embedded_data.h" echo 'static const unsigned int g_dragonx_cli_bat_size = 0;' >> "$GENERATED_DIR/embedded_data.h" fi else echo -e "${YELLOW}Note: Daemon binaries not found in prebuilt-binaries/dragonxd-win/ — wallet only${NC}" fi # ── xmrig binary (from prebuilt-binaries/xmrig-hac/) ──────────────── XMRIG_DIR="$SCRIPT_DIR/prebuilt-binaries/xmrig-hac" if [ -f "$XMRIG_DIR/xmrig.exe" ]; then cp -f "$XMRIG_DIR/xmrig.exe" "$EMBED_RES_DIR/xmrig.exe" echo " Staged xmrig.exe ($(du -h "$XMRIG_DIR/xmrig.exe" | cut -f1))" echo "" >> "$GENERATED_DIR/embedded_data.h" echo "#define HAS_EMBEDDED_XMRIG 1" >> "$GENERATED_DIR/embedded_data.h" echo "INCBIN(xmrig_exe, \"$EMBED_RES_DIR/xmrig.exe\");" >> "$GENERATED_DIR/embedded_data.h" else echo 'extern "C" { static const uint8_t* g_xmrig_exe_data = nullptr; }' >> "$GENERATED_DIR/embedded_data.h" echo 'static const unsigned int g_xmrig_exe_size = 0;' >> "$GENERATED_DIR/embedded_data.h" fi # ---- Theme images (backgrounds + logos) ---- # Embed ALL images from res/img/backgrounds/ subdirectories and res/img/logos/ # so the Windows single-file distribution can display theme backgrounds and logos # without needing res/ on disk. echo "" >> "$GENERATED_DIR/embedded_data.h" echo "// ---- Embedded theme images ----" >> "$GENERATED_DIR/embedded_data.h" IMAGE_TABLE_ENTRIES="" IMAGE_COUNT=0 for IMG_DIR in "$SCRIPT_DIR/res/img/backgrounds/texture" "$SCRIPT_DIR/res/img/backgrounds/gradient" "$SCRIPT_DIR/res/img/logos"; do if [ -d "$IMG_DIR" ]; then for IMG_FILE in "$IMG_DIR"/*.png; do [ -f "$IMG_FILE" ] || continue IMG_BASENAME=$(basename "$IMG_FILE") IMG_SYMBOL=$(echo "$IMG_BASENAME" | sed 's/[^a-zA-Z0-9]/_/g') echo " Staged image: $IMG_BASENAME ($(du -h "$IMG_FILE" | cut -f1))" cp -f "$IMG_FILE" "$EMBED_RES_DIR/$IMG_BASENAME" echo "INCBIN(img_${IMG_SYMBOL}, \"$EMBED_RES_DIR/$IMG_BASENAME\");" >> "$GENERATED_DIR/embedded_data.h" IMAGE_TABLE_ENTRIES="${IMAGE_TABLE_ENTRIES} { g_img_${IMG_SYMBOL}_data, g_img_${IMG_SYMBOL}_size, \"${IMG_BASENAME}\" },\n" IMAGE_COUNT=$((IMAGE_COUNT + 1)) done fi done echo "" >> "$GENERATED_DIR/embedded_data.h" echo "#define HAS_EMBEDDED_IMAGES 1" >> "$GENERATED_DIR/embedded_data.h" echo "#define EMBEDDED_IMAGE_COUNT $IMAGE_COUNT" >> "$GENERATED_DIR/embedded_data.h" # Backward compat defines (referenced by s_resources[] guards) echo "#define HAS_EMBEDDED_GRADIENT 1" >> "$GENERATED_DIR/embedded_data.h" echo "#define HAS_EMBEDDED_LOGO 1" >> "$GENERATED_DIR/embedded_data.h" # Backward compat aliases for the old symbol names echo "static const uint8_t* g_dark_gradient_png_data = g_img_dark_gradient_png_data;" >> "$GENERATED_DIR/embedded_data.h" echo "static const unsigned int g_dark_gradient_png_size = g_img_dark_gradient_png_size;" >> "$GENERATED_DIR/embedded_data.h" echo "static const uint8_t* g_logo_ObsidianDragon_dark_png_data = g_img_logo_ObsidianDragon_dark_png_data;" >> "$GENERATED_DIR/embedded_data.h" echo "static const unsigned int g_logo_ObsidianDragon_dark_png_size = g_img_logo_ObsidianDragon_dark_png_size;" >> "$GENERATED_DIR/embedded_data.h" echo "" >> "$GENERATED_DIR/embedded_data.h" echo "struct EmbeddedImageEntry { const uint8_t* data; unsigned int size; const char* filename; };" >> "$GENERATED_DIR/embedded_data.h" echo "static const EmbeddedImageEntry s_embedded_images[] = {" >> "$GENERATED_DIR/embedded_data.h" echo -e "$IMAGE_TABLE_ENTRIES" >> "$GENERATED_DIR/embedded_data.h" echo " { nullptr, 0, nullptr }" >> "$GENERATED_DIR/embedded_data.h" echo "};" >> "$GENERATED_DIR/embedded_data.h" # Embed bundled overlay themes (dark.toml, light.toml, obsidian.toml, etc.) # These are extracted to the config dir on first run so the theme selector can find them. THEMES_DIR="$SCRIPT_DIR/res/themes" THEME_COUNT=0 THEME_TABLE_ENTRIES="" echo "" >> "$GENERATED_DIR/embedded_data.h" echo "// ---- Bundled overlay themes ----" >> "$GENERATED_DIR/embedded_data.h" for THEME_FILE in "$THEMES_DIR"/*.toml; do THEME_BASENAME=$(basename "$THEME_FILE") # Skip ui.toml — it's embedded separately via cmake if [ "$THEME_BASENAME" = "ui.toml" ]; then continue fi THEME_SYMBOL=$(echo "$THEME_BASENAME" | sed 's/[^a-zA-Z0-9]/_/g') cp -f "$THEME_FILE" "$EMBED_RES_DIR/$THEME_BASENAME" echo " Staged $THEME_BASENAME" echo "INCBIN(theme_${THEME_SYMBOL}, \"$EMBED_RES_DIR/$THEME_BASENAME\");" >> "$GENERATED_DIR/embedded_data.h" THEME_TABLE_ENTRIES="${THEME_TABLE_ENTRIES} { g_theme_${THEME_SYMBOL}_data, g_theme_${THEME_SYMBOL}_size, \"${THEME_BASENAME}\" },\n" THEME_COUNT=$((THEME_COUNT + 1)) done echo "" >> "$GENERATED_DIR/embedded_data.h" echo "#define EMBEDDED_THEME_COUNT $THEME_COUNT" >> "$GENERATED_DIR/embedded_data.h" echo "" >> "$GENERATED_DIR/embedded_data.h" # Generate the theme table struct echo "// Auto-generated theme table" >> "$GENERATED_DIR/embedded_data.h" echo "struct EmbeddedThemeEntry { const uint8_t* data; unsigned int size; const char* filename; };" >> "$GENERATED_DIR/embedded_data.h" echo "static const EmbeddedThemeEntry s_embedded_themes[] = {" >> "$GENERATED_DIR/embedded_data.h" echo -e "$THEME_TABLE_ENTRIES" >> "$GENERATED_DIR/embedded_data.h" echo " { nullptr, 0, nullptr }" >> "$GENERATED_DIR/embedded_data.h" echo "};" >> "$GENERATED_DIR/embedded_data.h" echo "" >> "$GENERATED_DIR/embedded_data.h" echo -e "${GREEN}Embedded resources header generated (INCBIN — near-zero compile RAM)${NC}" else echo -e "${YELLOW}Warning: Sapling params not found — building without embedded resources${NC}" echo "The wallet will require external param files." fi # Ensure libsodium is available for Windows cross-compile if [ ! -f "$SCRIPT_DIR/libs/libsodium-win/lib/libsodium.a" ]; then echo -e "${YELLOW}libsodium for Windows not found, fetching...${NC}" "$SCRIPT_DIR/scripts/fetch-libsodium.sh" --win fi echo -e "${GREEN}Configuring with CMake...${NC}" cmake .. \ -DCMAKE_TOOLCHAIN_FILE=mingw-toolchain.cmake \ -DCMAKE_BUILD_TYPE=Release \ -DDRAGONX_USE_SYSTEM_SDL3=OFF echo -e "${GREEN}Building...${NC}" cmake --build . -j$(nproc) # Check if build succeeded if [ -f "bin/ObsidianDragon.exe" ]; then echo "" echo -e "${GREEN}Build successful!${NC}" echo "Output: $BUILD_DIR/bin/ObsidianDragon.exe" # Show file size and check if statically linked ls -lh bin/ObsidianDragon.exe echo "" echo -e "${GREEN}Statically linked - no DLLs required!${NC}" # Bundle daemon files if available from dragonxd-win directory DAEMON_DIR="$SCRIPT_DIR/prebuilt-binaries/dragonxd-win" DAEMON_BUNDLED=0 if [ -d "$DAEMON_DIR" ]; then echo -e "${GREEN}Found daemon directory: $DAEMON_DIR${NC}" # Copy all daemon files if [ -f "$DAEMON_DIR/dragonxd.bat" ]; then cp "$DAEMON_DIR/dragonxd.bat" bin/ echo " - dragonxd.bat" DAEMON_BUNDLED=1 fi if [ -f "$DAEMON_DIR/dragonx-cli.bat" ]; then cp "$DAEMON_DIR/dragonx-cli.bat" bin/ echo " - dragonx-cli.bat" fi if [ -f "$DAEMON_DIR/hushd.exe" ]; then cp "$DAEMON_DIR/hushd.exe" bin/ echo " - hushd.exe ($(du -h "$DAEMON_DIR/hushd.exe" | cut -f1))" fi if [ -f "$DAEMON_DIR/hush-cli.exe" ]; then cp "$DAEMON_DIR/hush-cli.exe" bin/ echo " - hush-cli.exe" fi if [ -f "$DAEMON_DIR/hush-tx.exe" ]; then cp "$DAEMON_DIR/hush-tx.exe" bin/ echo " - hush-tx.exe" fi else echo -e "${YELLOW}Daemon directory not found: $DAEMON_DIR${NC}" echo " Place prebuilt-binaries/dragonxd-win/ in the project directory to bundle the daemon." fi # Create distribution package echo "" echo -e "${GREEN}Creating distribution package...${NC}" cd bin DIST_NAME="ObsidianDragon-Windows-x64" rm -rf "$DIST_NAME" "$DIST_NAME.zip" mkdir -p "$DIST_NAME" cp ObsidianDragon.exe "$DIST_NAME/" # Copy all daemon files [ -f dragonxd.bat ] && cp dragonxd.bat "$DIST_NAME/" [ -f dragonx-cli.bat ] && cp dragonx-cli.bat "$DIST_NAME/" [ -f hushd.exe ] && cp hushd.exe "$DIST_NAME/" [ -f hush-cli.exe ] && cp hush-cli.exe "$DIST_NAME/" [ -f hush-tx.exe ] && cp hush-tx.exe "$DIST_NAME/" # Create README cat > "$DIST_NAME/README.txt" << 'READMEEOF' DragonX Wallet - Windows Edition ================================ SINGLE-FILE DISTRIBUTION ======================== This wallet is a true single-file executable with all resources embedded. Just run ObsidianDragon.exe - no additional files needed! On first run, the wallet will automatically extract: - Sapling parameters to %APPDATA%\ZcashParams\ - asmap.dat to %APPDATA%\Hush\DRAGONX\ The wallet will look for the daemon config at: %APPDATA%\Hush\DRAGONX\DRAGONX.conf This will be auto-created on first run if the daemon is present. For support: https://git.dragonx.is/dragonx/ObsidianDragon READMEEOF if command -v zip &> /dev/null; then zip -r "$DIST_NAME.zip" "$DIST_NAME" # Copy zip + single-file exe to release/windows/ local OUT_DIR="$SCRIPT_DIR/release/windows" mkdir -p "$OUT_DIR" cp "$DIST_NAME.zip" "$OUT_DIR/" cp ObsidianDragon.exe "$OUT_DIR/" echo -e "${GREEN}Distribution package: $OUT_DIR/$DIST_NAME.zip${NC}" echo -e "${GREEN}Single-file exe: $OUT_DIR/ObsidianDragon.exe${NC}" ls -lh "$OUT_DIR/" else echo "Install 'zip' to create distribution archive" fi cd .. echo "" echo -e "${GREEN}============================================${NC}" echo -e "${GREEN}SINGLE-FILE DISTRIBUTION READY!${NC}" echo -e "${GREEN}============================================${NC}" echo "" echo "The executable contains embedded:" echo " - Sapling spend params (~48MB)" echo " - Sapling output params (~3MB)" echo " - asmap.dat (~1MB)" echo "" echo "Just copy ObsidianDragon.exe to Windows and run it!" echo "Resources will be extracted automatically on first launch." else echo -e "${RED}Build failed!${NC}" exit 1 fi