Skip to content

Instantly share code, notes, and snippets.

@zakki
Created August 8, 2026 14:58
Show Gist options
  • Select an option

  • Save zakki/70c5e5eb7af217291d2900375367f9a1 to your computer and use it in GitHub Desktop.

Select an option

Save zakki/70c5e5eb7af217291d2900375367f9a1 to your computer and use it in GitHub Desktop.

Unicode・マルチプラットフォーム対応パス処理 修正仕様案

改訂メモ(2026-08-07): 本リポジトリのソースを実際に調査し、CMemBufgetpathhsp3_fopendpmread/FilePack#packopt nameHSPUTF8 ビルド構成に関する記述を実装と突き合わせて更新した。誤りは見つからなかったが、一部の記述は実態がより深刻、または帰属先が不正確だったため修正した。

1. 目的

OpenHSPのコンパイラ、ランタイムおよび関連ツールで、Unicode文字を含むファイル名とディレクトリ名をプラットフォームに依存せず扱えるようにする。

特に、UTF-8ソース内の #include、日本語名のソースファイル、#packopt name、パック対象ファイルなどについて、WindowsのANSIコードページに依存した失敗や文字化けを解消する。

本仕様では、内部で扱うパスの文字コードを原則としてUTF-8に統一し、OSや外部APIとの境界でのみ必要な文字コードへ変換する。

2. 対象範囲

対象は、ファイルシステム上のパスを生成、分解、比較または外部APIへ渡す処理である。

  • hspcmp の主ソースおよび #include ファイル読み込み
  • common_pathsearch_path などの検索パス
  • CMemBuf::PutFileCMemBuf::SaveFile などの通常ファイルI/O
  • supiogetpath などのパス操作
  • hsp3utfcnv の通常ファイルI/O
  • FilePack / dpmread の通常ファイルへのフォールバック
  • packfileの生成および読み込み
  • #packopt name から出力ファイルを作成する処理
  • Windows版エディタ、コンパイラDLL、コマンドラインツールとの文字列受け渡し

HSP言語の一般文字列をすべてUTF-8へ変更することは、本仕様の直接の目的には含めない。従来のCP932ランタイムとの互換性を維持しつつ、パスの内部表現を明確にすることを目的とする。

3. 現時点の問題

3.1 パスの文字コードが呼び出し経路によって異なる

現在の char * パラメータには、次の文字列が区別されずに渡される。

  • Windowsのコマンドラインや従来エディタから得たCP932文字列
  • CP932ソース内の文字列
  • UTF-8ソース内の文字列
  • UTF-8を内部表現とするランタイムの文字列
  • プラットフォームAPIから得た文字列

型とAPI名からエンコーディングを判断できないため、呼び出し先で pp_utf8 やビルド時の HSPUTF8 を参照しても、個々のパスの実際のエンコーディングを確定できない。

なお hspcmp のWindows向けプロジェクト(src/hspcmp/win32/hspcmp.vcxprojsrc/hspcmp/win32dll/hspcmp.vcxproj)は現状 HSPUTF8 を定義しておらず、CharacterSetMultiByte 固定で、リンクする supio も非Unicode版の supio_win.cpp のみである(supio_win_unicode.cpp はプロジェクトに含まれていない)。したがって「参照しても確定できない」だけでなく、配布中のWindows版コンパイラCLI・DLLに限れば HSPUTF8 は常にOFFであり、現状は常にCP932/ナロー文字API前提で動作している。一方、非Windows GCC系ターゲット(HSPLINUX/HSPMAC/HSPIOS/HSPNDK/HSPEMSCRIPTEN)は hsp3config.hHSPUTF8 が常時強制ONになっており、Windows版コンパイラとは対照的である。

3.2 Windowsのナロー文字ファイルAPIへUTF-8パスを渡している

CMemBuf::PutFile などには fopen(char *) を直接使用する箇所がある。Windowsのナロー文字版APIはUTF-8パスを一貫して扱わないため、UTF-8ソースから得た日本語ファイル名などを開けない。

3.3 getpath がロケールと従来文字コードに依存している

Windows版 getpathmbstowcs_wsplitpath を使用しているが、mbstowcs の変換元文字コードはCランタイムのロケールに依存する。UTF-8、CP932のどちらを入力とするかがAPI契約に含まれておらず、変換失敗も十分に検査されていない。

また、Shift_JISの先行バイトを仮定した手動走査は、いわゆるダメ文字対策にはなってもUTF-8入力には適用できない。

3.4 ファイルI/Oの抽象化が複数箇所に存在する

通常ファイルを開く処理が CMemBufhsp3utfcnvdpmreadFilePack および一部のツール内に分散している。

hsp3utfcnvhsp3_fopen は、Windowsの HSPUTF8 ビルドではUTF-8からUTF-16へ変換して _wfopen を使用している。一方で、この関数にはHSPTV検索、Android/iOS固有処理、オフセット処理なども含まれ、単純なOSファイルI/O以上の責務がある。

dpmread(dpmread.cpp)自体はDPM、埋め込みファイル、メモリファイルおよび通常ファイルを統合する仮想ファイル層の実体ではなく、dpm_* 系関数のほぼ全てが1行で FilePack(filepack.cpp)の pack_* メソッドへ転送するC API向けの薄いファサードである。統合ロジックの実体は FilePack 側にあり、dpmread はその上に被さる互換レイヤーという位置づけになる。コンパイラのソース読み込みへ直接流用すると不要なランタイム依存と状態を持ち込む、という結論自体は FilePack を経由しても変わらない。

3.5 パスの連結とバッファ管理が安全でない

strcpystrcat と固定長配列によるパス生成が多く、UTF-8化によって同じ文字数でも必要なバイト数が増える。MAX_PATHHSP_MAX_PATH 固定では、切り捨て、バッファオーバーフロー、長いWindowsパスへの非対応が残る。

該当箇所は局所的ではない。strcpy/strcat の呼び出しは token.cpp に58件、filepack_write.cpp に20件、filepack.cpp に7件、ahtobj.cpp に4件確認できる。代表例として、CTokencommon_path/search_path(token.hchar [HSP_MAX_PATH])、#include 解決を行う ExpandFile#pack/#packopt を扱う AddPackfile、EXE生成を行う FilePack::MakeEXEFile(char [HFP_PATH_MAX] を複数使用)が挙げられる。また CMemBuf のファイル名フィールドは char name[256](membuf.h)の固定バッファで、PutFile/SaveFile はここへ長さチェックなしに strcpy している。HSP_MAX_PATH 自体も HSPWIN で260、HSPGCC 系で256の固定値であり、UTF-8化で1文字あたりの必要バイト数が増える分、上限はより早く問題化する。

3.6 #packopt name の文字コード境界が不明確である

UTF-8ソースから得た #packopt name の値が、そのまま従来形式のpackfileへ書かれる。packfileを読む側や実際に出力ファイルを作る側がCP932を前提としている場合、文字化けまたは作成失敗が発生する。

プリプロセッサだけを修正しても、最終的なファイル作成を行うエディタやpackagerがナロー文字APIを使っていれば、CP932に存在しない文字は扱えない。

3.7 UTF-8検証が不完全である

現在のUTF-8先頭バイト判定には、現在のUTF-8では無効な5バイト・6バイト形式や F5 以上を許容する箇所がある。また、後続バイト、過長形式、サロゲート範囲、U+10FFFF 超過を検証していない処理がある。

これはファイルオープン失敗の直接原因ではないが、内部パスをUTF-8と定義するには、不正な入力をどこで拒否するかを決める必要がある。

4. 基本方針

4.1 内部パスはUTF-8とする

文字コード変換を終えた後の内部パスは、有効なUTF-8のNUL終端文字列とする。

  • UTF-8ソース内のパスは、検証後にそのまま内部パスとする。
  • CP932ソース内のパスは、トークン取得後またはパス用APIへ渡す前にUTF-8へ変換する。
  • WindowsのOS由来文字列は、可能な限りUTF-16 APIで取得してUTF-8へ変換する。
  • Linux、macOSなどのOS由来パスは、原則としてUTF-8として受け取る。
  • 内部パスをCP932へ一時変換してから連結、分解または保存しない。

pp_utf8 はソーステキストの入力形式を示すものであり、正規化後のパスを開くAPIには渡さない。

現状でもこの原則は概ね守られており、getpathCMemBuf::PutFilepp_utf8 を参照しない。ただし例外が1箇所ある。CToken::to_hsp_string_literal(src, filename)(token.cpp)は __file__ マクロやデバッグ行マーカー用のファイル名リテラルを生成する際、filename=true かつ pp_utf8 が真であればファイル名をCP_ACPからUTF-8へ変換している(ConvSJis2Utf8)。この処理は「OS由来のファイル名は常にCP_ACPである」という前提を置いているため、UTF-8ソース中の #include 文字列トークンに由来するファイル名(既にUTF-8)を通すと誤変換になり得る。内部パスのUTF-8統一に伴い、この箇所も本仕様の契約に合わせて整理する必要がある。

4.2 OS境界でのみ変換する

Windowsでは、内部UTF-8パスをUTF-16へ変換し、原則としてワイド文字版APIを使用する。

  • _wfopen
  • CreateFileW
  • GetFileAttributesW
  • MoveFileW / CopyFileW など

UTF-8からUTF-16への変換は必要サイズを先に取得し、固定長 MAX_PATH 配列へ依存しない。不正なUTF-8は変換時に検出し、呼び出し元へエラーを返す。

POSIX系では、内部UTF-8のバイト列を原則としてそのまま fopenstat などへ渡す。ただし、POSIXファイル名は仕様上UTF-8に限定されないため、非UTF-8ファイル名の扱いは設計判断事項とする。

4.3 通常ファイルI/Oと仮想ファイルI/Oを分離する

次の3層に責務を分ける。

  1. UTF-8共通パス操作
    • basename、dirname、extension、join、絶対パス判定など
  2. UTF-8通常ファイルI/O
    • OSファイルのopen、create、stat、copyなど
  3. 仮想ファイルI/O
    • FilePack / dpmread によるDPM、埋め込み、暗号化、メモリファイルの解決

CMemBuf は通常ファイルI/O層を利用する。FilePack は仮想ファイル内で対象が見つからなかった場合に通常ファイルI/O層を利用する。hspcmp のソース読み込みは、DPM内ソースを正式にサポートしない限り dpmread を直接利用しない。

この分離は FilePack に関しては現状でも部分的に実現している。FilePack::pack_fopen はパック内に対象が見つからない場合 hsp3_fopen へフォールバックし(filepack.cpp)、pack_flengthpack_fread も同様に hsp3_flength/hsp3_rawload 経由でフォールバックする。つまり FilePack は既に hsp3utfcnv 側の通常ファイルI/Oを利用しており、CMemBuf のように素の fopen を直接呼んではいない。移行手順(9章)ではこの既存の依存関係を踏まえ、FilePack 側は主に下位API自体のUTF-8対応(6章)に追従させる作業として位置づけられる。

4.4 既存実装を下位層へ整理する

hsp3utfcnvhsp3_fopen と同じ変換処理を CMemBuf に複製しない。純粋なUTF-8 OSファイルI/Oを共通の下位APIとして抽出し、hsp3_fopenCMemBuf、必要なツールから利用する。

概念上のAPI例を次に示す。正式な名前と配置は設計判断事項とする。

FILE *hsp_fopen_utf8(const char *path, const char *mode);
int64_t hsp_filesize_utf8(const char *path);
bool hsp_file_exists_utf8(const char *path);

既存の hsp3_fopen はHSPTV検索やモバイル固有処理などを保持し、その内部から必要に応じて下位APIを呼ぶ。

5. パス操作仕様

5.1 字句的なパス分解

UTF-8パスのbasename、dirname、拡張子を求めるだけであれば、Unicodeコードポイント単位の走査は不要である。/\. はASCIIであり、有効なUTF-8の多バイト文字の後続バイトには現れないため、バイト列として検索できる。

ただし、区切り文字の規則は対象プラットフォームによって異なる。

  • Windowsでは /\ を区切りとして扱う。
  • POSIXでは / のみを区切りとし、\ は通常の文字として扱う。
  • Windowsのドライブ名、UNC、デバイスパスを扱う場合は専用の規則を追加する。

パス全体を strcase などで小文字化しない。WindowsでもUnicodeの大文字小文字規則を単純なバイト変換で再現できず、表示用・実アクセス用パスを破壊する可能性がある。

5.2 パス連結

パス連結は容量を管理できる共通関数または文字列型を使用し、strcpy / strcat の連鎖を廃止する。

連結関数は少なくとも次を定義する。

  • 空のベースパス
  • ベース末尾または子パス先頭に区切りがある場合
  • 絶対パスを子として渡した場合
  • ... を正規化するか否か
  • Windowsのドライブ相対パス C:foo
  • UNCおよびデバイスパス

セキュリティ境界として利用する場合を除き、字句的正規化と実ファイルのcanonical化を混同しない。シンボリックリンクを解決するcanonical化は、ファイルの存在を要求し、I/Oや権限エラーも発生する別操作とする。

5.3 Unicode正規化

内部パスに対してNFCまたはNFDへの自動変換は行わないことを初期方針とする。ファイルシステムごとの正規化動作が異なり、入力文字列の変更によって既存ファイルへ到達できなくなる可能性があるためである。

同一視や重複検査が必要な箇所では、実アクセス用パスと比較用キーを分けて検討する。

6. 入力境界の仕様

6.1 Windowsコマンドライン

主ソースファイル名を含むコマンドライン引数は、可能であれば wmain または GetCommandLineW / CommandLineToArgvW でUTF-16として取得し、入口でUTF-8へ変換する。

main(int, char **) のANSI引数をUTF-8と仮定してはならない。互換性のためANSI引数経路を残す場合は、CP_ACPからUTF-8へ変換する明示的な互換入口とする。

この方式には既にランタイム側で前例がある。src/hsp3/win32gui/main.cppWinMainHSPDEBUG かつ HSPUTF8 の条件下で CommandLineToArgvW(GetCommandLineW(), &nArgs) を使い、argv[1] 相当をUTF-16からUTF-8へ変換して渡している。一方 src/hspcmp/main.cppint main(int argc, char *argv[]) は素のANSI引数をそのまま使っており、この対応が入っていない。6.1の提案は新規方式の導入というより、ランタイム側の既存パターンをコンパイラ側にも広げる作業と位置づけられる。ただし win32gui/main.cpp の実装自体も変換先バッファが char utf8filename[4096] の固定長であり、3.5節で指摘した固定バッファ問題を内包している点は流用時に修正が必要である。

6.2 ソースコード

ソースの文字コード判定は既存オプションおよびBOM等の仕様に従う。パスとして使用される文字列は、ソース文字コードに従ってデコードし、内部UTF-8へ変換する。

主ソースのOSパスと、ソース中の #include 文字列を同じ未変換 char * として扱わない。

6.3 WindowsエディタおよびDLL API

エディタからコンパイラDLLへ渡される既存APIのパス文字コード契約は、当面CP932のまま維持する。対象には hsc_inihsc_refnamehsc_objnamehsc_compath など、現行エディタが利用する char * 入力の公開関数を含む。

前述の通り、現行の hspcmp Windows DLL(win32dll/hspcmp.vcxproj)は HSPUTF8 を有効化できないビルド構成であり、配布版 hspcmp_64.dll も公開関数で char * を受け取り、ANSI版Windows APIを使用している。したがって、既存APIをUTF-8またはUTF-16へ暗黙に意味変更しない。

DLL内部をUTF-8パスへ移行する際は、既存APIの入口でCP932からUTF-8へ明示的に変換し、それ以降を共通の内部実装へ渡す。CP932で表現できないパスは、エディタとDLLの新しい受け渡し方式が導入されるまで、既存APIの制約として扱う。

Unicodeパスを損失なく受け取る将来の方式として、UTF-8専用関数、Windows向けUTF-16専用関数、初期化時の文字コードフラグ、またはバージョン付き設定APIを候補とする。採用方式は、内部UTF-8化の準備状況とエディタ側のUnicode対応設計・開発時期を合わせて決定する。

初期化フラグ方式は公開関数を増やさずに済む一方、グローバル状態、呼び出し順、モード指定忘れに依存する。別関数方式はAPI数が増える一方、文字コード契約が関数単位で明確になり、旧エディタと新エディタを同じDLLで扱いやすい。

7. packfileおよび #packopt name

内部では #packopt name の値もUTF-8で保持する。

ただし、packfileは複数の実装やツールが読み書きする交換形式であるため、文字コードを一方的に変更しない。次のいずれを採用するか決定したうえで、生成側と全読込側を同時に対応させる。

  • packfile全体をUTF-8と定義する。
  • バージョンまたは明示的なエンコーディング指定を追加する。
  • 既存形式をCP932のまま維持し、新形式を別に用意する。
  • name 等のメタ情報のみエスケープまたはUTF-8化する。

出力ファイルを作るWindows側処理は、packfileを読み取った後にUTF-16へ変換してワイド文字APIを使用する。プリプロセッサでCP932へ変換する方式では、CP932に存在しない文字を扱えないため、完全なUnicode対応にはならない。

現状の実装を #packopt name から出力EXE生成まで追跡すると、CToken::PP_PackOptAddPackfile によるpackfile内;!name=行への書き込み → CHsc3::GetPackfileOption(hsc3.cpp)による読み出し → win32dll/hspcmp3.cppFilePack::MakeEXEFile(filepack_write.cpp)の hsp3_fopenwrite という経路全体で、エンコーディング変換は一切行われていない。加えて6.3節の通りこのDLLビルドは HSPUTF8 を有効化できないため、hsp3_fopenwrite も実際にはナロー文字APIに解決される。つまり本節で提案するワイド文字API経由の変換は「未実装の設計目標」であり、現状はCP932前提のまま素通りしている経路として扱う必要がある。

8. エラー処理

少なくとも次のエラーを区別できるようにする。

  • 入力パスが不正なUTF-8である。
  • OS用文字列への変換に失敗した。
  • パスが長すぎる、または内部上限を超えた。
  • ファイルが存在しない。
  • アクセス権限がない。
  • ファイルではなくディレクトリである。
  • 読み込みまたは書き込みが途中で失敗した。

既存公開APIが -1NULL しか返せない場合でも、内部では詳細なエラーを保持し、コンパイラの診断メッセージで可能な範囲を表示する。診断に表示するパスも内部UTF-8から出力先に適した文字コードへ変換する。

9. 移行手順

段階的に次の順で移行する。

  1. UTF-8通常ファイルI/Oの共通下位APIを定義する。
  2. UTF-8、UTF-16、CP932間の変換APIとエラー規則を整理する。
  3. CMemBuf::PutFile / SaveFile をUTF-8パス契約へ移行する。
  4. hspcmp のコマンドライン、主ソース、#include、検索パスを入口でUTF-8へ正規化する。
  5. getpath のコンパイラ利用箇所をUTF-8共通パス操作へ移行する。
  6. hsp3_fopen を共通下位APIの利用へ移行し、既存の追加検索機能を維持する。
  7. FilePack / dpmread の通常ファイルフォールバックを確認する。
  8. 既存コンパイラDLL入口のCP932契約を維持し、入口でCP932から内部UTF-8へ変換する。
  9. packfile交換形式と #packopt name の仕様を決定し、全読込側を対応させる。
  10. エディタ側のUnicode対応と歩調を合わせ、DLLへUnicodeパスを渡す新しいAPIまたはモードを決定する。
  11. エディタ、コンパイラDLL、packagerなどの境界を、決定した方式で段階的に移行する。
  12. 直接 fopen 等を呼ぶ残存箇所を調査し、用途に応じて共通APIへ移行する。

移行中も既存DLL APIはCP932契約として固定し、ビルドフラグによって入力文字コードを変えない。将来UTF-8またはUTF-16契約のAPIを追加する場合は、関数名、型またはAPIバージョンによって従来APIと明確に区別する。

10. テスト仕様

10.1 共通テストケース

  • ASCIIのみの相対パスと絶対パス
  • 日本語を含むファイル名とディレクトリ名
  • など、CP932で2バイト目が区切り文字等と衝突し得る文字
  • CP932に存在しないBMP文字
  • 絵文字などの補助平面文字
  • 空白、複数のドット、先頭ドット、末尾ドットを含む名前
  • / を含むパス
  • プラットフォームに応じた \ の扱い
  • 長いパス
  • 不正なUTF-8
  • 存在しないファイルとアクセス不能ファイル

10.2 コンパイラテスト

  • 日本語名の主ソースをコマンドラインからコンパイルできる。
  • UTF-8ソースから日本語名および補助平面文字を含む .as#include できる。
  • CP932ソースから従来の日本語名 .as#include できる。
  • 主ソースとincludeファイルが別ディレクトリにある。
  • common_pathsearch_path の各検索順が維持される。
  • __file__、エラーメッセージ、デバッグ情報にパスが正しく現れる。
  • #pack#epack などでUnicode名のファイルを扱える。
  • #packopt name にUnicode名を指定し、意図した名前で成果物を作成できる。

10.3 プラットフォーム別テスト

  • Windows 32bit / 64bit、CP932ランタイム、UTF-8ランタイム
  • Linux
  • macOS
  • Emscripten
  • Android
  • iOS

各プラットフォームで機能しない概念、例えばEmscripten上のホストファイルシステム操作については、対象外または代替動作を明示する。

11. 決めるべき事項

以下は利点と欠点があり、本仕様だけでは一意に決められない。

11.1 共通APIの配置

  • supio に置く
    • 利点: 既存のプラットフォーム補助層を利用できる。
    • 欠点: supio は実装がプラットフォームごとに分散し、文字列・パス・OS操作の責務が広い。
  • hsp3utfcnv を一般化する
    • 利点: UTF-8からUTF-16への変換とファイルI/Oの既存実装を再利用できる。
    • 欠点: HSP文字列変換、HSPTV検索、モバイル固有処理などとの結合がある。
  • 独立したUTF-8パス/ファイルI/Oモジュールを作る
    • 利点: 入力UTF-8という契約と責務を明確にでき、コンパイラとランタイムで共有しやすい。
    • 欠点: ビルド対象の追加と既存コードの段階的移行が必要になる。

11.2 std::filesystem の採用範囲

  • 採用する
    • 利点: パス連結、分解、ファイル状態取得を標準APIへ集約できる。
    • 欠点: C++標準バージョン、コンパイラ、プラットフォームごとの path の文字コード動作を確認する必要がある。C++20では u8stringchar8_t の扱いも変わる。
  • 採用しない、または限定利用する
    • 利点: 古いツールチェーンとC形式APIとの互換性を維持しやすい。
    • 欠点: パス操作、容量管理、OS差異を独自に保守する必要がある。

採用する場合も、外部APIが受け取る文字コードを std::filesystem の暗黙変換へ任せず、UTF-8との変換境界を明文化する。

11.3 パス表現の型

  • std::string をUTF-8と規約で定義する。
  • Utf8Path などのラッパー型を導入する。
  • 公開C APIは const char * のまま、内部C++層のみ型を分ける。

ラッパー型は誤ってCP932文字列を渡す事故を減らせるが、既存APIからの移行量が増える。

11.4 POSIX上の非UTF-8ファイル名

  • 対象外として拒否する。
  • バイト列として透過的に保持する例外を設ける。
  • 表示用UTF-8とOS用バイト列を別に保持する。

内部UTF-8統一は単純で移植性が高い一方、POSIXで作成可能な任意バイト列のファイル名すべては表現できない。

11.5 Windowsの長いパス

  • \\?\ 形式を内部で付与するか。
  • アプリケーションマニフェストによるlong path awareを前提とするか。
  • 従来の HSP_MAX_PATH を維持するか、動的文字列へ移行するか。

単に _wfopen へ変更するだけでは、すべての長いパス問題が解決するとは限らない。

11.6 大文字小文字とパス比較

  • パック内ファイル名の比較規則を既存どおり維持するか。
  • Windowsの通常ファイルと同様の大文字小文字非依存を模倣するか。
  • Unicodeのcase foldingを導入するか。

暗号化パックではパス文字列がCRCや暗号キーに使われるため、比較や正規化規則の変更が既存パックとの互換性を壊す可能性がある。

11.7 packfileの文字コードと互換性

  • 現行packfileの実際の文字コード契約
  • UTF-8化した場合のバージョニング
  • 旧ツールが新packfileを読んだ場合の動作
  • name 以外の文字列オプションの扱い
  • パック内パス、暗号キー、重複判定への影響

11.8 UTF-8エラー時の互換動作

  • 不正UTF-8を即座にエラーにする。
  • WindowsではCP932として再解釈する互換フォールバックを設ける。
  • 呼び出し元が文字コードを明示した場合だけCP932変換を許可する。

自動フォールバックは既存資産を読みやすい一方、同じバイト列が環境や内容によって異なるパスになるため、再現性と安全性を損なう。

11.9 エディタからDLLへUnicodeパスを渡す方式

既存DLL APIのCP932契約を当面維持することは決定済みである。内部UTF-8化とエディタ側開発の準備が整った後に、次の方式から移行方法を決定する。

  • UTF-8専用関数を追加する。
    • 利点: マルチプラットフォームな内部表現と一致し、文字コード契約が明確になる。
    • 欠点: パスを受け取る既存関数ごとに新APIが必要になる可能性がある。
  • UTF-16専用関数を追加する。
    • 利点: WindowsエディタがOSから取得したパスを損失なく直接渡せる。
    • 欠点: Windows固有APIとなり、DLL内部でUTF-8への変換が必要になる。
  • 初期化時にUTF-8モードを指定する。
    • 利点: 後続の既存関数群を共用しやすい。
    • 欠点: グローバル状態と呼び出し順に依存し、個々の引数の文字コードをAPIから判別できない。
  • バージョン付き設定APIを追加する。
    • 利点: パス群と文字コード契約をまとめて更新でき、将来の拡張に向く。
    • 欠点: エディタ側とDLL側の変更が大きく、従来のHSPプラグイン呼び出し規約との調整が必要になる。

新方式を導入する場合は、旧エディタと新DLL、新エディタと旧DLLの組み合わせを考慮し、エクスポート関数の存在確認やAPIバージョンによる能力検出を定義する。新エディタが旧DLLへフォールバックする際、パスをCP932へ損失なく変換できない場合は、誤ったファイルを開かず明示的な非対応エラーとする。

12. このリポジトリ内で追加調査が必要な事項

  • [方針決定・2026-08-07] 既存のエディタ向けコンパイラDLL APIは当面CP932契約を維持し、DLL内部のUTF-8化では入口変換を行う。Unicodeパスを直接渡す新方式はエディタ側開発と合わせて決定する。ランタイムおよびその他のツール間の契約は引き続き調査する。
  • [調査済み・2026-08-07] HSPUTF8HSPWIN などのビルド構成ごとにリンクされる supiohsp3utfcnv の組み合わせ — win32/hspcmp.vcxprojwin32dll/hspcmp.vcxproj はいずれも HSPUTF8 未定義・CharacterSet=MultiBytesupio_win.cpp(非Unicode版)固定。win32gui/hsp3.vcxproj(ランタイムGUI)は HSPUTF8 定義済み。非Windows GCC系は hsp3config.h により常時強制ON。残る調査事項: 他のWindows向けプロジェクト(hsp3debug、各種プラグイン等)の設定分布。
  • hsp3_fopen のHSPTV検索およびモバイル固有処理を下位APIから分離できるか
  • CMemBuf の複製実装と派生コピーがどのターゲットで使用されるか — src/hspcmp/membuf.cpp 以外に src/tools/win32/hsed3f/membuf.hhsed3_footy2/membuf.hsrc/plugins/win32/hspda/membuf.hhspsw/membuf.h に構造的に同一の複製が存在することを確認。各コピー間の差分の有無は未調査。
  • getpath の全呼び出し箇所と、各呼び出しが期待する区切り・小文字化・拡張子規則 — WindowsのUTF8ビルド版 getpath(supio_win_unicode.cpp)も mbstowcs/_wsplitpath を使用しロケール依存であることを確認。一方Linux/iOS/NDK/Emscripten版の getpath は独自実装の _splitpath を使い、strchr2 によるバイト列走査のみでワイド文字変換を経由しない。
  • [調査済み・2026-08-07] strcase がパスへ適用される箇所と、パック内検索・重複判定への影響 — FilePack::StrCase/SearchFileObject(filepack.cpp)がパック内ファイル名・フォルダ名を\/変換込みで小文字化して大文字小文字非依存の比較を行っている。加えて supio_win.cppSecurityCheck(HSP3IMP限定)がWindowsディレクトリ判定に strcase 適用済みパスの部分一致を用いている。
  • [調査済み・2026-08-07] #packopt name を読むすべてのツールおよび最終的に成果物名を決定する処理 — CToken::PP_PackOptAddPackfileCHsc3::GetPackfileOption(hsc3.cpp)→ win32dll/hspcmp3.cppFilePack::MakeEXEFile(filepack_write.cpp)→ hsp3_fopenwrite の経路を確認。全経路でエンコーディング変換なし。
  • packfile、DPM、暗号化パックのパスバイト列が互換性や暗号キーに与える影響
  • Android、iOS、Emscriptenのストレージパス変換とUTF-8契約
  • 既存テストおよびCIで利用できるWindows、Linux、macOS環境
  • サポート対象の最小C++標準、Visual C++、GCC、Clangのバージョン
  • 公開SDKやプラグインAPIに char * パスが露出している箇所

13. このリポジトリだけでは完結しない可能性がある調査

  • 配布版HSP Script EditorがコンパイラDLLの各公開関数を呼ぶ箇所、および将来UTF-8・UTF-16パスを渡せるようにするための変更可能範囲
  • 外部または旧版のpackager、DPM作成ツールが想定するpackfileの文字コード
  • OpenHSPリポジトリ外で保守されるランチャー、IDE、ビルド支援ツールとの互換性
  • 各配布対象OSでサポートすべき最小バージョンとファイルシステム
  • macOSファイルシステム上のUnicode正規化差異をどこまで保証するか
  • Linux等で非UTF-8ロケールおよび非UTF-8ファイル名を正式サポートするか
  • Windowsのlong path aware設定を配布実行ファイルへ適用できるか
  • 既存DLL入口のCP932契約を将来廃止するか、恒久的な互換APIとして維持するか

これらは、外部ツールのソースまたは仕様、配布パッケージ、対応OS上での実動作を確認してから決定する。

14. 完了条件

次を満たした時点で、本対応を完了とみなす。

  • 内部UTF-8パスの契約がAPIまたは型として明文化されている。
  • Windowsの通常ファイルアクセスがUTF-16 API経由で行われる。
  • POSIX系でUTF-8パスが変換によって破壊されない。
  • CMemBufhsp3utfcnv にUTF-8・UTF-16変換処理が重複していない。
  • supio、通常ファイルI/O、FilePack / dpmread の責務が分離されている。
  • UTF-8およびCP932ソースからUnicode名のincludeを処理できる。
  • Unicode名の主ソース、パック対象ファイルおよび出力ファイルを扱える。
  • packfileおよび既存パックとの互換性方針が決定され、テストされている。
  • 対象プラットフォームのテストが追加されている。
  • 不正UTF-8、変換失敗、長いパスに対して安全にエラーを返す。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment