# API設計書

## 概要

本ドキュメントは、K-Shoot MANIA（HSP製Windows用音楽ゲーム）が外部サーバーと行うAPI通信の設計を記載する。本アプリケーションはデスクトップクライアントであり、APIサーバーを公開する側ではなく、外部APIを利用（消費）する側である。

主な通信先は以下の3つである：
1. **IR（インターネットランキング）サーバー** (`ir.kshootmania.com`) — ログイン認証、スコア送信、譜面登録
2. **Twitter Web Intent** — ツイート投稿（ブラウザ経由）
3. **HTTPオーディオストリーミング** — BASS_StreamCreateURLによる楽曲のHTTPストリーミング再生

> **注意**: ソースコード公開にあたり、IR通信およびTwitter連携のコア実装は削除されている（README.mdに明記）。本ドキュメントではスタブ関数のインターフェースと残存するコードから推測される仕様を記載する。

## 共通仕様

### ベースURL

```
https://ir.kshootmania.com/
```

### 認証方式

IRサーバーへの認証は `ir_login` 関数により行われる。認証成功時に `session_id`（文字列）と `ksm_id`（整数）がグローバル変数として設定される。以降のAPI通信ではこれらの認証情報が使用される。

> **注意**: `ir_login` の実装は削除されており、詳細な認証プロトコル（Bearer Token、API Key、Session Cookie等）は不明。

### ネットワークライブラリ

| ライブラリ | 用途 |
| --- | --- |
| hspinet.dll (HSPインターネット拡張) | HTTP通信、JSON解析、MD5計算、URLエンコード |
| bass.dll (BASS Audio Library) | HTTPオーディオストリーミング |

### hspinet.dll 提供関数

| 関数名 | 説明 |
| --- | --- |
| `netinit` | ネットワーク初期化 |
| `neturl` | リクエスト先URL設定 |
| `netrequest_get` | HTTP GETリクエスト送信 |
| `netrequest_post` | HTTP POSTリクエスト送信 |
| `netexec` | リクエスト実行・応答待機 |
| `netgetv` | レスポンスデータ取得 |
| `netproxy` | プロキシ設定 |
| `varmd5` | MD5ハッシュ計算 |
| `urlencode` / `urldecode` | URLエンコード・デコード |
| `jsonopen` / `jsongeti` / `jsongets` / `jsonclose` | JSONレスポンス解析 |

### 共通ヘッダー

| ヘッダー名 | 必須 | 説明 |
| --- | --- | --- |
| Content-Type | ○ | application/x-www-form-urlencoded（推定: hspinet.dllのPOST標準形式） |

### プロキシ対応

アプリケーション設定ファイルで `proxy_host` と `proxy_port` が指定されている場合、`netproxy` 関数により全通信にプロキシが適用される（`kshootmania.hsp` 833行目）。

### 共通エラーレスポンス

| ステータスコード | 説明 |
| --- | --- |
| 接続失敗 | ダイアログ "Connection failed." を表示（`ir_login` 内） |
| スコア送信失敗 | `ir_send_score` が `-1` を返却 |
| 譜面登録失敗 | `ir_send_chart` が `-1` を返却 |

## API一覧

| カテゴリ | エンドポイント | メソッド | 説明 |
| --- | --- | --- | --- |
| IR認証 | (実装削除) | POST（推定） | IRサーバーへのログイン認証 |
| IRスコア送信 | (実装削除) | POST（推定） | プレイ結果のスコアをIRサーバーへ送信 |
| IR譜面登録 | (実装削除) | POST（推定） | 未登録譜面をIRサーバーに登録 |
| IRページ表示 | /md5/{hash} | GET（ブラウザ） | 譜面のIRランキングページをブラウザで開く |
| Twitterツイート | twitter.com/intent/tweet | GET（ブラウザ） | リザルト画面からツイートをブラウザで投稿 |
| オーディオストリーミング | (任意のHTTP/HTTPS URL) | GET | BASS_StreamCreateURLによる楽曲ストリーミング |

## 各APIエンドポイント定義

### IR（インターネットランキング）

#### 1. IRログイン

IRサーバーに対してユーザー認証を行い、セッション情報を取得する。タイトル画面の「IR Sign In」メニューから呼び出される。

**基本情報**

| 項目 | 内容 |
| --- | --- |
| 関数名 | `ir_login` |
| 定義元 | `src/ir.hsp` 30-36行目 |
| 呼び出し元 | `kshootmania.hsp` 1144行目（SCENE_IR_SIGN_IN内） |
| 認証 | 不要（認証を行うAPI自体） |
| 実装状態 | **削除済み**（スタブのみ） |

**リクエスト（推定）**

hspinet.dllの `neturl` → `netrequest_post` → `netexec` のフローで通信が行われていたと推定される。

| パラメータ名 | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| (不明) | - | - | ユーザー名・パスワード等の認証情報（実装削除のため詳細不明） |

**レスポンス（推定）**

認証成功時に以下のグローバル変数が設定される：

| 変数名 | 型 | 説明 |
| --- | --- | --- |
| `session_id` | string | セッションID（空文字の場合はオフライン動作） |
| `ksm_id` | int | ユーザーID（0の場合はオフライン動作） |

**現在の動作**

```
// ir_is_logged_in は常に 0 を返す
#defcfunc ir_is_logged_in
    return 0

// ir_login はスタブ（何もしない）
#deffunc ir_login
    // Note: ソースコード公開用に削除
```

**エラー処理**

| ラベル | 処理 |
| --- | --- |
| `*ir_login_error` | ダイアログ "Connection failed." を表示 |
| `*ir_login_success` | 正常終了（return） |

---

#### 2. IRスコア送信

プレイ結果のスコアをIRサーバーへ送信する。リザルト画面で自動的に呼び出される。

**基本情報**

| 項目 | 内容 |
| --- | --- |
| 関数名 | `ir_send_score` |
| 定義元 | `src/ir.hsp` 38-40行目 |
| 呼び出し元 | `src/scene/result.hsp` 590行目、786行目 |
| 認証 | 必要（`ksm_id` が非0であること） |
| 実装状態 | **削除済み**（スタブのみ、常に `-1` を返却） |

**呼び出し条件**

```
if((auto=0)&(adjmode=0)&(iscmdline=0)&(shortmode+longmode+analogmode=0)&(tochu_esc=0)){
    if(ksm_id){
        if(no_hstype_c=1){
            ir_send_score
```

| 条件 | 説明 |
| --- | --- |
| `auto=0` | オートプレイでないこと |
| `adjmode=0` | 調整モードでないこと |
| `iscmdline=0` | コマンドライン起動でないこと |
| `shortmode+longmode+analogmode=0` | 全ノートモードが通常であること |
| `tochu_esc=0` | 途中ESCでの中断でないこと |
| `ksm_id≠0` | IRにログイン済みであること |
| `no_hstype_c=1` | HiSPEEDタイプが"C"でないこと |

**リクエスト（推定）**

送信データとして以下の情報が含まれていたと推定される：

| フィールド名 | 型 | 説明 |
| --- | --- | --- |
| chart_hash | string | 譜面のMD5ハッシュ（`getChartIRHash`で計算） |
| session_id | string | 認証セッションID |
| score | int | 総合スコア |
| critical | int | CRITICAL判定数 |
| near | int | NEAR判定数 |
| error | int | ERROR判定数 |

**レスポンス**

| 戻り値 | 型 | 説明 |
| --- | --- | --- |
| stat | int | `-1`: 失敗、`0`: 未登録譜面、`1`: 登録済み・送信成功 |
| `output@ksm_ir` | string | サーバーからのレスポンスメッセージ |

**レスポンス表示**

| ステータス | ポップアップ表示 |
| --- | --- |
| stat=0（未登録譜面） | "Unregistered chart\nHold FX-L+R to register this chart" |
| stat=1（成功） | サーバーからのメッセージ（`output@ksm_ir`） |
| HiSPEEDタイプC使用時 | "Score was not sent\n(HiSPEED type \"C\" was used)" |

---

#### 3. IR譜面登録

未登録の譜面をIRサーバーに新規登録する。リザルト画面でFX-L+Rボタンの500ms長押しで呼び出される。

**基本情報**

| 項目 | 内容 |
| --- | --- |
| 関数名 | `ir_send_chart` |
| 定義元 | `src/ir.hsp` 42-44行目 |
| 呼び出し元 | `src/scene/result.hsp` 785行目 |
| 認証 | 必要（`ksm_id` が非0であること） |
| 実装状態 | **削除済み**（スタブのみ、常に `-1` を返却） |

**呼び出し条件**

```
if((hold_fxlr_time>=500)&(hold_fxlr_time<50000)&(ir_regchart_wait=1)){
    ir_send_chart
    ir_send_score
```

| 条件 | 説明 |
| --- | --- |
| `hold_fxlr_time>=500` | FX-L+Rボタンを500ms以上長押し |
| `ir_regchart_wait=1` | 譜面が未登録状態 |

**リクエスト（推定）**

| フィールド名 | 型 | 説明 |
| --- | --- | --- |
| chart_data | string | 譜面データ（KSH形式） |
| chart_hash | string | 譜面のMD5ハッシュ |
| session_id | string | 認証セッションID |

**レスポンス**

| 戻り値 | 型 | 説明 |
| --- | --- | --- |
| stat | int | `-1`: 失敗（現在のスタブの戻り値） |

> **注意**: `ir_send_chart` の後に `ir_send_score` が連続して呼び出される。譜面登録後にスコアも送信するフローになっている。

---

#### 4. IRハッシュ計算（getChartIRHash）

譜面データからIR用のMD5ハッシュ値を計算するローカル関数。IR通信のキーとなる譜面識別子を生成する。（この関数はAPIエンドポイントではなく、API通信の前処理として使用される）

**基本情報**

| 項目 | 内容 |
| --- | --- |
| 関数名 | `getChartIRHash` |
| 定義元 | `src/ir.hsp` 51-68行目 |
| 実装状態 | **実装あり**（削除されていない） |

**処理概要**

1. KSH形式の譜面ファイル内容を受け取る
2. メタデータ行（title=、artist=、level=等）をフィルタリングして除外
3. 譜面の本体データのみでMD5ハッシュを計算
4. 32文字のMD5ハッシュ文字列を返す

**除外対象メタデータキー**

```
"//","title=","title_img=","artist=","artist_img=","effect=","illustrator=",
"jacket=","level=","difficulty=","m=","o=","v=","vo=","mvol=","bg=","layer=",
"po=","plength=","pfiltergain=","filtertype=","chokkakuautovol=","chokkakuvol=",
"chokkakuse=","icon=","ver=","fx-l=","fx-r=","#define","information=",
"ver_compat=","title_translit=","artist_translit="
```

**入出力**

| パラメータ | 型 | 方向 | 説明 |
| --- | --- | --- | --- |
| p1 | var (string) | 入力 | KSH譜面ファイルの全内容 |
| 戻り値 | string | 出力 | 32文字のMD5ハッシュ文字列 |

---

#### 5. IRランキングページ表示

選曲画面でCtrl+Iキー押下時に、選択中の譜面のIRランキングページをデフォルトブラウザで開く。

**基本情報**

| 項目 | 内容 |
| --- | --- |
| エンドポイント | `GET https://ir.kshootmania.com/md5/{hash}` |
| 呼び出し元 | `src/scene/select.hsp` 308行目 |
| 認証 | 不要（公開Webページ） |
| 実装状態 | **実装あり** |

**パスパラメータ**

| パラメータ名 | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| hash | string | ○ | `getChartIRHash` で計算された譜面のMD5ハッシュ値（32文字） |

**呼び出し条件**

| 条件 | 説明 |
| --- | --- |
| Ctrl+I同時押し | キーボードショートカット |
| 通常の譜面を選択中 | `minfo.selnow.0.0 = 0`（コースモード等でないこと） |
| 譜面ファイルが存在 | `fileExists(filePath)` がtrue |

**処理フロー**

```
1. Ctrl+Iキー押下を検出
2. 選択中の譜面ファイルを読み込み（noteload）
3. UTF-8 BOMがあれば除去（DeleteUTF8BOM）
4. getChartIRHash(buf) でMD5ハッシュを計算
5. exec "https://ir.kshootmania.com/md5/" + hash, 16 でブラウザ起動
```

**レスポンス**

デフォルトブラウザでIRランキングページが表示される（アプリケーション内でのレスポンス処理なし）。

---

### Twitterツイート

#### 6. ツイート投稿（Web Intent）

リザルト画面からプレイ結果をTwitterに投稿する。ブラウザのWeb Intentを使用する方式。

**基本情報**

| 項目 | 内容 |
| --- | --- |
| エンドポイント | `GET https://twitter.com/intent/tweet?text={urlencoded_text}` |
| 呼び出し元（リザルト） | `src/scene/result.hsp` 839-840行目 |
| 呼び出し元（コースリザルト） | `src/scene/course_result.hsp` 623-624行目 |
| 認証 | 不要（ブラウザで認証） |
| 実装状態 | **実装あり** |

**クエリパラメータ**

| パラメータ名 | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| text | string | ○ | URLエンコード済みのツイートテキスト |

**ツイートテキストの構成**

ツイートテキストには以下の情報が含まれる：

| 要素 | 説明 |
| --- | --- |
| プレイ結果 | スコア、判定数等 |
| アプリ名・バージョン | "K-Shoot MANIA v1.75b" |
| 公式サイトURL | `https://www.kshootmania.com/` |

**処理フロー**

```
1. F11キー押下またはツイートボタンクリックを検出
2. 画面キャプチャをクリップボードにコピー（ClipB_SetBmp）
3. ダイアログで通知
4. urlencode tweet_text_urlencoded, tweet_text でURLエンコード
5. exec "https://twitter.com/intent/tweet?text=" + tweet_text_urlencoded, 16 でブラウザ起動
```

**レスポンス**

デフォルトブラウザでTwitterのツイート投稿画面が表示される（アプリケーション内でのレスポンス処理なし）。

**補足: Twitter API Key**

`kshlib.dll` に `KSHLib_GetTwitterAPIKey` 関数と `KSHLib_GetHMACSHA1` 関数が定義されている。これはブラウザ経由のWeb Intent方式とは別に、アプリ内から直接Twitter APIを呼び出す機能が存在していた（または計画されていた）ことを示唆する。

| 関数名 | 説明 |
| --- | --- |
| `KSHLib_GetTwitterAPIKey` | Twitter APIキーの取得 |
| `KSHLib_GetHMACSHA1` | HMAC-SHA1署名計算（OAuth認証用） |
| `KSHLib_GetTwitterStringLength` | ツイート文字数計算（短縮URL等考慮） |

---

### オーディオストリーミング

#### 7. HTTP楽曲ストリーミング

楽曲のソースがHTTP/HTTPS URLの場合、BASSライブラリのストリーミング機能で再生する。

**基本情報**

| 項目 | 内容 |
| --- | --- |
| 関数名 | `BASS_StreamCreateURL` |
| 定義元 | `src/bass.as` 17行目 |
| 呼び出し元（選曲プレビュー） | `src/scene/select.hsp` 2478行目 |
| 呼び出し元（プレイ画面） | `src/scene/play/play_chart_load.hsp` 539行目 |
| 認証 | 不要 |
| 実装状態 | **実装あり**（BASS.dll経由） |

**関数シグネチャ**

```
BASS_StreamCreateURL(url, offset, flags, proc, user, reserved)
```

**パラメータ**

| パラメータ名 | 型 | 必須 | 説明 |
| --- | --- | --- | --- |
| url | sptr (string) | ○ | 楽曲ファイルのHTTP/HTTPS URL |
| offset | int | ○ | オフセット（通常0） |
| flags | int | ○ | フラグ（0x20000 = BASS_STREAM_DECODE等） |
| proc | int | - | コールバック関数（未使用時0） |
| user | int | - | ユーザーデータ（未使用時0） |
| reserved | int | - | 予約（未使用時0） |

**URL判定条件**

```hsp
// 選曲画面でのURL判定
if (strmid(minfostr.selnow.levmn2.7, 0, 5) = "http:") {
    mpid = BASS_StreamCreateURL(minfostr.selnow.levmn2.7, 0, 0x20000, 0, 0)

// プレイ画面でのURL判定
if ((strmid(ccsongfile, 0, 5) = "http:") | (strmid(ccsongfile, 0, 6) = "https:")) {
    mid.cnt = BASS_StreamCreateURL(ccsongfile, 0, 0x20000, 0, 0, 0)
```

**レスポンス**

| 戻り値 | 型 | 説明 |
| --- | --- | --- |
| ストリームハンドル | int | 成功時: 正の整数（BASSストリームハンドル）、失敗時: 0 |

---

### 補助API（URLエンコード・デコード）

#### 8. URLエンコード

UTF-8変換とURLエンコードを行うユーティリティ関数群。IR通信パラメータの準備に使用される。

**基本情報**

| 項目 | 内容 |
| --- | --- |
| モジュール | `ksm_ir` |
| 定義元 | `src/ir.hsp` 119-142行目 |
| 実装状態 | **実装あり** |

**関数一覧**

| 関数名 | 入力型 | 出力型 | 説明 |
| --- | --- | --- | --- |
| `url_utf8_enc(src)` | str | str | UTF-8変換 + URLエンコード |
| `urlenc(src)` | str | str | URLエンコード（`-` `.` はエンコードしない） |
| `urldec(src)` | str | str | URLデコード |
| `utf8enc_auto(src)` | str | str | UTF-8 BOM付きならBOM除去、それ以外はUTF-8変換 |

**urlenc の特殊処理**

```hsp
urlencode@ url_string, var_src_string
strrep url_string, "%2D", "-"    // ハイフンはエンコードしない
strrep url_string, "%2E", "."    // ピリオドはエンコードしない
```

## 備考

### ソースコード公開時の削除内容

README.mdに記載の通り、以下の機能のコア実装は削除されている：

| 機能 | 削除状態 | スタブの有無 |
| --- | --- | --- |
| INPUT GATE（ゲーム内譜面ダウンローダー） | 完全削除 | 不明 |
| Twitter連携機能 | 部分削除（Web Intentは残存） | - |
| インターネットランキング | コア通信部分削除 | あり（`ir_login`, `ir_send_score`, `ir_send_chart`） |

### IR通信のアーキテクチャ

```
K-Shoot MANIA (HSPクライアント)
    │
    ├── hspinet.dll（HSPインターネット拡張）
    │     ├── netinit → ネットワーク初期化
    │     ├── neturl → URL設定
    │     ├── netrequest_get/post → リクエスト送信
    │     ├── netexec → 応答待機
    │     ├── netgetv → レスポンス取得
    │     ├── jsonopen/jsongets/jsongeti/jsonclose → JSON解析
    │     ├── varmd5 → MD5計算
    │     └── urlencode/urldecode → URLエンコード
    │
    └── ir.kshootmania.com（IRサーバー）
          ├── ログイン認証
          ├── スコア送信
          ├── 譜面登録
          └── /md5/{hash} → ランキングページ（Webブラウザ）
```

### ネットワーク初期化フロー

```
1. kshootmania.hsp 763行目: hspinet.dll の存在確認
2. kshootmania.hsp 832行目: netinit（ネットワーク初期化）
3. kshootmania.hsp 833行目: netproxy（プロキシ設定、設定がある場合のみ）
4. kshootmania.hsp 834行目: 接続関連のコード（削除済み）
```

### バージョン情報

| 項目 | 値 |
| --- | --- |
| アプリ名 | K-Shoot MANIA |
| バージョン | v1.75b |
| 内部バージョン | 175b |
| 公式サイト | https://www.kshootmania.com/ |
