Skip to content
AEspinosa-FoxglowGamesPublic

About

REXPaint .xp map loader for C++/OpenGL — color-to-tile palette mapping, multi-layer support / C++/OpenGL向けREXPaintマップローダー

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

Repository files navigation

xp-loader

A lightweight C++ loader for REXPaint .xp map files, with an optional palette system for mapping colors to tile types.

Drop two files into your project and go from a .xp file to a populated tile map in under ten lines of code.

Features

  • Loads any .xp file (gzip-compressed, all REXPaint versions)
  • Full multi-layer support — access any layer, not just layer 0
  • Two-tier API: raw struct access or callback-based cell iteration
  • XPPalette<T> — maps (charCode, RGB) pairs to any user-defined type with configurable color tolerance
  • Neighbor lookup in callbacks — infer door orientation, auto-tile, etc.
  • No dependencies beyond miniz.h (single-header, drop-in)
  • C++17, header + one .cpp file

Quick Start

Tier 1 — Raw load

XPImage img;
if (loadXP("level.xp", img)) {
    std::cout << img.layers[0].width << "x" << img.layers[0].height << "\n";

    const XPCell* cell = img.layers[0].at(5, 3);
    if (cell)
        std::cout << "code=" << cell->code << "\n";
}

Tier 2 — Callback with palette

enum class Tile { Floor, WallBrick, WallStone, Door };

XPPalette<Tile> palette;
palette.add(219, {102, 178, 255}, Tile::Floor);
palette.add(219, {153, 102,   0}, Tile::WallBrick);
palette.add(219, {  0,   0,   0}, Tile::WallStone);

loadAndParseXP("level.xp", [&](int col, int row,
                                const XPCell& cell,
                                const XPLayer&) {
    myMap[col][row] = palette.matchOrDefault(
        cell.code, cell.fg_r, cell.fg_g, cell.fg_b, Tile::WallStone);
});

Neighbor-aware callback (e.g. door orientation)

loadAndParseXP("level.xp", [&](int col, int row,
                                const XPCell& cell,
                                const XPLayer& layer) {
    auto t = palette.match(cell.code, cell.fg_r, cell.fg_g, cell.fg_b);
    if (t == Tile::Door) {
        bool wallE = isWall(layer, col + 1, row);
        bool wallW = isWall(layer, col - 1, row);
        myMap[col][row] = (wallE || wallW) ? Tile::DoorH : Tile::DoorV;
    } else {
        myMap[col][row] = t.value_or(Tile::WallStone);
    }
});

Integration

  1. Copy XPLoader.h, XPLoader.cpp, and XPPalette.h into your project
  2. Drop miniz.h into the same folder
  3. Add XPLoader.cpp to your build
  4. #include "XPLoader.h" and optionally #include "XPPalette.h"

No CMake changes required beyond adding the .cpp file.

API Reference

XPLoader.h

// Load a .xp file into an XPImage struct.
bool loadXP(const std::string& path, XPImage& out);

// Walk every cell of one layer and invoke callback for each.
bool parseXPLayer(const XPImage& image, XPCellCallback callback, int layerIndex = 0);

// Convenience: load and parse in a single call.
bool loadAndParseXP(const std::string& path, XPCellCallback callback, int layerIndex = 0);

Data types:

struct XPCell {
    int32_t code;               // CP437 character code
    uint8_t fg_r, fg_g, fg_b;  // Foreground color
    uint8_t bg_r, bg_g, bg_b;  // Background color
};

struct XPLayer {
    int32_t width, height;
    std::vector<XPCell> cells;  // Column-major: cell(col, row) = cells[col * height + row]

    const XPCell* at(int col, int row) const;  // Bounds-checked accessor
};

struct XPImage {
    std::vector<XPLayer> layers;
    int layerCount() const;
};

Note: Cells are stored column-major to match REXPaint's native format. Use layer.at(col, row) rather than indexing directly.

XPPalette.h

XPPalette<T> palette;

// Add a color → value mapping (default tolerance: 18 per channel, Chebyshev)
palette.add(int charCode, RGB color, T value, int tolerance = 18);

// Match any foreground color for a given character code
palette.addAnyColor(int charCode, T value);

// Try to match — returns std::nullopt if no entry matches
std::optional<T> palette.match(int32_t charCode, uint8_t r, uint8_t g, uint8_t b);

// Match with a fallback default
T palette.matchOrDefault(int32_t charCode, uint8_t r, uint8_t g, uint8_t b, T defaultValue);

Constants:

XPPalette<T>::ANY_CODE  // Wildcard: match any character code
XPPalette<T>::ANY_RGB   // Wildcard: match any foreground color
XPPalette<T>::DEFAULT_TOLERANCE  // 18 (Chebyshev per-channel)

Entries are matched in insertion order — first match wins. Color distance uses Chebyshev (max-channel) distance, which handles REXPaint's compressed color values well.

Building the Example

Requires CMake and vcpkg with miniz installed.

git clone https://github.com/AEspinosa-FoxglowGames/xp-loader
cd xp-loader
cmake --preset x64-debug
cmake --build out/build/x64-debug

Place your .xp files in a maps/ folder next to the executable. The build copies the maps/ directory automatically post-build.

License

MIT — see LICENSE.

About

Built by Austin Espinosa — indie developer and Information Science student at ICU Tokyo.

Originally developed for use in raycaster-3d.

🌐 foxglowgames.com


xp-loader(日本語)

REXPaint の .xp マップファイル用の軽量C++ローダーです。色情報をタイルタイプにマッピングするオプションのパレットシステムも付属しています。

ファイルを2つプロジェクトにコピーするだけで、10行以内のコードで .xp ファイルからタイルマップを構築できます。

特徴

  • あらゆる .xp ファイルをロード(gzip圧縮、全REXPaintバージョン対応)
  • マルチレイヤー完全対応 — レイヤー0だけでなく任意のレイヤーにアクセス可
  • 二段階API:生のストラクトアクセス、またはコールバックベースのセル反復
  • XPPalette<T> — (charCode, RGB) のペアをユーザー定義型にマッピング、カラー許容値設定可
  • コールバック内で隣接セルを参照可能 — ドアの向き推定やオートタイルなどに対応
  • 依存関係は miniz.h のみ(シングルヘッダー、ドロップイン)
  • C++17、ヘッダー + .cpp 1ファイル

クイックスタート

Tier 1 — 生のロード

XPImage img;
if (loadXP("level.xp", img)) {
    std::cout << img.layers[0].width << "x" << img.layers[0].height << "\n";

    const XPCell* cell = img.layers[0].at(5, 3);
    if (cell)
        std::cout << "code=" << cell->code << "\n";
}

Tier 2 — コールバックとパレットの組み合わせ

enum class Tile { Floor, WallBrick, WallStone, Door };

XPPalette<Tile> palette;
palette.add(219, {102, 178, 255}, Tile::Floor);
palette.add(219, {153, 102,   0}, Tile::WallBrick);
palette.add(219, {  0,   0,   0}, Tile::WallStone);

loadAndParseXP("level.xp", [&](int col, int row,
                                const XPCell& cell,
                                const XPLayer&) {
    myMap[col][row] = palette.matchOrDefault(
        cell.code, cell.fg_r, cell.fg_g, cell.fg_b, Tile::WallStone);
});

隣接セル参照コールバック(例:ドアの向き推定)

loadAndParseXP("level.xp", [&](int col, int row,
                                const XPCell& cell,
                                const XPLayer& layer) {
    auto t = palette.match(cell.code, cell.fg_r, cell.fg_g, cell.fg_b);
    if (t == Tile::Door) {
        bool wallE = isWall(layer, col + 1, row);
        bool wallW = isWall(layer, col - 1, row);
        myMap[col][row] = (wallE || wallW) ? Tile::DoorH : Tile::DoorV;
    } else {
        myMap[col][row] = t.value_or(Tile::WallStone);
    }
});

導入方法

  1. XPLoader.h・XPLoader.cpp・XPPalette.h をプロジェクトにコピー
  2. miniz.h を同じフォルダに配置
  3. XPLoader.cpp をビルドに追加
  4. #include "XPLoader.h"(必要に応じて #include "XPPalette.h" も)

CMakeの変更は .cpp ファイルの追加のみで完了します。

APIリファレンス

XPLoader.h

// .xp ファイルを XPImage 構造体にロード
bool loadXP(const std::string& path, XPImage& out);

// レイヤーの全セルを反復してコールバックを呼び出す
bool parseXPLayer(const XPImage& image, XPCellCallback callback, int layerIndex = 0);

// ロードとパースを1回の呼び出しで実行
bool loadAndParseXP(const std::string& path, XPCellCallback callback, int layerIndex = 0);

データ型:

struct XPCell {
    int32_t code;               // CP437文字コード
    uint8_t fg_r, fg_g, fg_b;  // 前景色
    uint8_t bg_r, bg_g, bg_b;  // 背景色
};

struct XPLayer {
    int32_t width, height;
    std::vector<XPCell> cells;  // 列優先: cell(col, row) = cells[col * height + row]

    const XPCell* at(int col, int row) const;  // 境界チェック付きアクセサ
};

struct XPImage {
    std::vector<XPLayer> layers;
    int layerCount() const;
};

注意: REXPaintのネイティブフォーマットに合わせ、セルは列優先で格納されています。 直接インデックス指定ではなく layer.at(col, row) を使用してください。

XPPalette.h

XPPalette<T> palette;

// 色 → 値のマッピングを追加(デフォルト許容値:チェビシェフ距離18)
palette.add(int charCode, RGB color, T value, int tolerance = 18);

// 前景色を問わず指定文字コードにマッチするエントリを追加
palette.addAnyColor(int charCode, T value);

// マッチを試みる — マッチなしの場合は std::nullopt を返す
std::optional<T> palette.match(int32_t charCode, uint8_t r, uint8_t g, uint8_t b);

// マッチ結果またはデフォルト値を返す
T palette.matchOrDefault(int32_t charCode, uint8_t r, uint8_t g, uint8_t b, T defaultValue);

定数:

XPPalette<T>::ANY_CODE         // ワイルドカード:任意の文字コードにマッチ
XPPalette<T>::ANY_RGB          // ワイルドカード:任意の前景色にマッチ
XPPalette<T>::DEFAULT_TOLERANCE  // 18(チェビシェフ距離、チャンネルごと)

エントリは登録順に照合され、最初にマッチしたものが採用されます。色距離にはチェビシェフ距離(最大チャンネル差)を使用しており、REXPaintの圧縮カラー値に適しています。

サンプルのビルド

CMakeと、miniz をインストール済みのvcpkgが必要です。

git clone https://github.com/AEspinosa-FoxglowGames/xp-loader
cd xp-loader
cmake --preset x64-debug
cmake --build out/build/x64-debug

.xp ファイルは実行ファイルの隣の maps/ フォルダに配置してください。ビルド後に maps/ ディレクトリは自動的にコピーされます。

ライセンス

MIT — LICENSE を参照。

開発者について

Austin Espinosa 制作 — インディーゲーム開発者・ICU東京在籍の情報科学専攻学生。

raycaster-3d プロジェクトでの使用を目的として開発。

🌐 foxglowgames.com

About

REXPaint .xp map loader for C++/OpenGL — color-to-tile palette mapping, multi-layer support / C++/OpenGL向けREXPaintマップローダー

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages