# BCACHE 離線備援最終計畫

日期：2026-04-26
專案：HBSTK (GTWVT Console Mode)

---

## 一、問題根源

不同電腦的螢幕解析度/DPI 不同 → GTWVT cell 尺寸不同 → BCACHE 不匹配 → 造字消失或品質下降。

## 二、解決方案

### 核心設計：多套 BCACHE + .24M 直接生成

```
84 個 .24M 字形 → ScaleGlyph1bit 即時生成 → 任何尺寸完美品質
47 個動態字形 → 預生成 10 套不同解析度的 BCACHE → 自動選擇最佳套組
13 個 ASCFONT  → 同上，預生成 10 套
```

### 10 套 BCACHE 解析度對照表

| #  | 螢幕解析度  | USRFONT (全寬×高) | ASCFONT (半寬×高) | 每套大小 |
|----|------------|-------------------|-------------------|---------|
| 1  | 1024×768   | 24×29             | 12×29             | 36 KB   |
| 2  | 1280×720   | 32×27             | 16×27             | 45 KB   |
| 3  | 1280×1024  | 32×39             | 16×39             | 65 KB   |
| 4  | 1366×768   | 34×29             | 17×29             | 51 KB   |
| 5  | 1440×900   | 36×34             | 18×34             | 64 KB   |
| 6  | 1536×864   | 38×32             | 19×32             | 63 KB   |
| 7  | 1600×900   | 40×34             | 20×34             | 71 KB   |
| 8  | 1920×1080  | 48×41             | 24×41             | 102 KB  |
| 9  | 1920×1200  | 48×46             | 24×46             | 115 KB  |
| 10 | 2560×1440  | 64×56             | 32×56             | 187 KB  |
|    | **合計**   |                   |                   | **803 KB** |

計算公式（80×25 模式，標題列約 40px）：
```
cellW = 螢幕寬 / 80
cellH = (螢幕高 - 40) / 25
USRFONT fullW = cellW × 2
ASCFONT halfW = cellW
```

---

## 三、System.ini 設定與流程

### System.ini 設定項

```ini
# =====================================================
# 字型尺寸決定優先順序：
#   1. width>0 AND height>0 → 固定模式（使用指定值）
#   2. FontSize>0           → 固定模式（FontSize/2, FontSize）
#   3. FontSize=0           → 自動模式（依螢幕解析度計算）
# =====================================================
FontName=DFKai-SB
FontSize=0
width=0
height=0

# =====================================================
# BCacheMode = scale / regen / multi
#   scale = 從最接近的 BCACHE 縮放（離線，不需要華康字型）
#   regen = 用華康字型重新生成（最佳品質，需要字型）
#   multi = 多套 BCACHE 自動選擇（最佳方案）
# =====================================================
BCacheMode=multi
```

### 啟動流程與套用時機

```
程式啟動
  │
  ▼
1. LoadIni(System.ini)
  │
  ▼
2. 讀取 FontSize, width, height
  │
  ▼
3. 判斷模式
  │
  ├─── FontSize=0 AND width=0 AND height=0 ──────────────┐
  │    【自動模式】                                        │
  │    nFontH = (螢幕高-100) / Rows                       │
  │    nFontW = nFontH / 2                                │
  │    → cell 尺寸依螢幕變化                               │
  │    → BCacheMode 生效（scale/multi）                    │
  │                                                       │
  ├─── FontSize>0 ────────────────────────────────────────┤
  │    【FontSize 固定模式】                               │
  │    nFontH = FontSize                                  │
  │    nFontW = FontSize / 2                              │
  │    → cell 尺寸固定                                    │
  │    → BCacheMode 生效                                  │
  │                                                       │
  └─── width>0 AND height>0 ──────────────────────────────┤
       【width/height 固定模式（最高優先）】                │
       nFontW = width                                     │
       nFontH = height                                    │
       → cell 尺寸固定                                    │
       → BCacheMode 生效                                  │
                                                          │
  ◄───────────────────────────────────────────────────────┘
  │
  ▼
4. hb_gtInfo(HB_GTI_FONTNAME, cFontName)
   hb_gtInfo(HB_GTI_FONTSIZE, nFontH)
   hb_gtInfo(HB_GTI_FONTWIDTH, nFontW)
  │
  ▼
5. SetMode(nRows, nCols)
   → 注意：RESIZEMODE=FONT 可能覆蓋字型尺寸
   → 固定模式建議用 RESIZEMODE=ROWS 防止覆蓋
  │
  ▼
6. FONTSIZE.log 記錄實際使用的 width/height
  │
  ▼
7. STK_EXTRACT_RES() — 解壓 EXE 資源
   → BCACHE overwrite=0（存在就不覆寫）
   → 多套 BCACHE 全部解壓到子目錄
  │
  ▼
8. UsrFontInit("USRFONT.24M")
   → USRFONT_LOAD: 載入 .24M 字型
   → [FIX-1] 不清 USRFONT alpha cache
   → [FIX-4] 不清 ASCFONT cache
   → HOOKCDP, HOOKGT, HOOKWND
  │
  ▼
9. 第一次 WM_PAINT → ScanAndDrawUda
  │
  ▼
10. LoadUsrFontUmap(nCellW*2, nCellH)
    │
    ├─ BCacheMode=multi 時：
    │   → 偵測實際 cell 尺寸
    │   → 搜尋 10 套 BCACHE，找完全符合的
    │   → 完全符合 → 直接載入（品質 100%）
    │   → 接近符合 → 從最接近的縮放（品質 95%+）
    │   → 只載入動態字形（s_aBig5Lookup[idx]>=0 的跳過）
    │
    ├─ BCacheMode=scale 時：
    │   → 從單一 BCACHE 縮放（現有行為）
    │
    └─ BCacheMode=regen 時：
        → 清空 cache，用華康字型重新生成
  │
  ▼
11. EnsureUfntAlpha(idx)
    │
    ├─ s_aUfntAlpha[idx] 已有 → 直接使用（從 BCACHE 載入的）
    ├─ s_aBig5Lookup[idx] >= 0 → ScaleGlyph1bit 從 .24M 生成（完美品質）
    └─ s_aDynUnicode[idx] != 0 → CaptureGlyphAlpha（需要華康字型）
```

---

## 四、BCACHE 保護機制（已完成）

| 修正 | 位置 | 說明 |
|------|------|------|
| [FIX-1] | USRFONT_LOAD | 不清 USRFONT alpha cache |
| [FIX-2] | LoadUsrFontUmap | 先驗證再清 + 不符就縮放 |
| [FIX-3] | SaveUsrFontUmap | 新數量 < 舊數量時不覆寫 |
| [FIX-4] | USRFONT_LOAD | 不清 ASCFONT cache |
| [FIX-5] | LoadAscFontUmap | 不符時從 BCACHE 縮放 |
| overwrite=0 | STK_EXTRACT_RES | BCACHE 存在就不從 EXE 覆寫 |

---

## 五、字形品質策略

| 字形類型 | 數量 | 來源 | 品質 |
|---------|------|------|------|
| .24M 字形 | 84 | ScaleGlyph1bit 即時生成 | 任何尺寸 100% |
| 動態字形 | 47 | 多套 BCACHE 自動選擇 | 符合時 100%，縮放時 95%+ |
| ASCFONT | 13 | 多套 BCACHE 自動選擇 | 同上 |

### LoadUsrFontUmap 載入邏輯

```c
// 載入 BCACHE 時，跳過有 .24M 原始資料的字形
if( s_aBig5Lookup[(int)wIdx] >= 0 )
    continue;  // 有 .24M → 讓 EnsureUfntAlpha 用 ScaleGlyph1bit
// 只載入動態字形（沒有 .24M 的）
s_aUfntAlpha[(int)wIdx] = pAlpha;
```

---

## 六、EXE 打包結構

### stk32.rc 資源定義

```rc
// 原始字型資料（所有電腦相同）
101 RCDATA ".\USRFONT.24M"
102 RCDATA ".\USRFONT.UMAP"
104 RCDATA ".\ASCFONT.15"
105 RCDATA ".\ASCFONT.UMAP"

// 多套 BCACHE（依解析度分組，只存動態字形）
201 RCDATA ".\bcache\USRFONT_1024x768.BCACHE"
202 RCDATA ".\bcache\USRFONT_1280x720.BCACHE"
203 RCDATA ".\bcache\USRFONT_1280x1024.BCACHE"
204 RCDATA ".\bcache\USRFONT_1366x768.BCACHE"
205 RCDATA ".\bcache\USRFONT_1440x900.BCACHE"
206 RCDATA ".\bcache\USRFONT_1536x864.BCACHE"
207 RCDATA ".\bcache\USRFONT_1600x900.BCACHE"
208 RCDATA ".\bcache\USRFONT_1920x1080.BCACHE"
209 RCDATA ".\bcache\USRFONT_1920x1200.BCACHE"
210 RCDATA ".\bcache\USRFONT_2560x1440.BCACHE"

// ASCFONT 同理
301 RCDATA ".\bcache\ASCFONT_1024x768.BCACHE"
...

// 設定檔（不覆寫）
501 RCDATA ".\stk.ini"
502 RCDATA ".\system.ini"
```

---

## 七、自動選擇演算法

```c
// 在 LoadUsrFontUmap 或 ScanAndDrawUda 中：
// 1. 取得實際 cell 尺寸 (nCellW, nCellH)
// 2. 掃描所有 BCACHE 套組
// 3. 找完全符合的 → 直接載入
// 4. 找最接近的 → 縮放載入

int bestMatch = -1;
int bestDist = 999999;
for( i = 0; i < nBCacheSets; i++ )
{
    int dist = abs(sets[i].w - nCellW*2) + abs(sets[i].h - nCellH);
    if( dist == 0 ) { bestMatch = i; break; }  // 完全符合
    if( dist < bestDist ) { bestDist = dist; bestMatch = i; }
}
// bestMatch → 載入該套 BCACHE
```

---

## 八、生成工具

在開發機（有華康字型）執行，依序生成 10 套 BCACHE：

```
gen_bcache.py:
  for each resolution:
    1. 設定 GTWVT 視窗大小
    2. 啟動 STK32.exe
    3. 觸發所有動態字形渲染
    4. 收集生成的 BCACHE
    5. 重命名為 USRFONT_{resolution}.BCACHE
```

---

## 九、實作順序

| 順序 | 項目 | 說明 |
|------|------|------|
| 1 | .24M 跳過邏輯 | LoadUsrFontUmap 跳過 .24M 字形 |
| 2 | BCACHE 瘦身 | SaveUsrFontUmap 只存動態字形 |
| 3 | 多套打包結構 | stk32.rc + STK_EXTRACT_RES 修改 |
| 4 | 自動選擇演算法 | 偵測 cell 尺寸 → 選最佳套組 |
| 5 | 生成工具 | gen_bcache.py 批次生成 10 套 |
| 6 | ASCFONT 同步處理 | ASCFONT 也做多套 |
| 7 | 測試 | 各解析度驗證品質 |
