遊戲年代 GOAL GOAL

 找回密碼
 立即註冊
搜索
查看: 371|回復: 14

[PC版模擬器] [DC模擬器] Flycast 2.6 繁/簡/英文版

[複製鏈接]

14

主題

126

帖子

3156

積分

銀牌會員

Rank: 5Rank: 5

積分
3156
發表於 2026-7-24 20:09:49 | 顯示全部樓層 |閱讀模式
本帖最後由 good7 於 2026-8-7 14:55 編輯

Flycast 2.6 繁、簡、英文版(正式版)

以下這份內容是依據「貼文一樓」的修復內容,叫 ai 總結寫的,用一般玩家看得懂的內容方式撰寫,
不是專業術語,這樣才好理解用這個版本的好處在哪裡與原版的區別在哪裡。

================================================================================
                Flycast 專案介紹(依實際修復內容撰寫)
================================================================================

【Flycast 是什麼?】
Flycast 是免費、開源的 Dreamcast 家用主機模擬器,也支援 Naomi / Naomi 2
街機遊戲。作用就是讓你在電腦上玩 DC / 街機遊戲。

這份文件記錄的,是我們這一陣子對這個模擬器做的「實際修復」與「實際強化」,
每一項都有對應的程式檔案與行為。我們用玩家聽得懂的方式,逐項說明這些
修復修了什麼、壞了會發生什麼事。


================================================================================
一、遊戲讀不到/讀到一半出錯(光碟映像檔解析)
================================================================================
Flycast 靠「光碟映像檔」來讀遊戲,常見格式有 CHD、GDI、CDI、CUE。這幾個
檔案的讀取程式原本存在多處錯誤,會造成以下實際狀況:

• CHD 檔案內附的說明文字(metadata)如果特別長,會直接「溢寫」到程式
  記憶體裡(緩衝區溢位),遊戲可能因此當掉或出現怪行為。
  → 已限制讀入長度,不會再溢寫。

• GDI 檔案如果標記了奇怪的軌道數(例如軌道數寫成 0 或超過 100),
  程式會一直狂分配記憶體直到當機(記憶體耗盡攻擊)。
  → 已加上數量上限檢查。

• 某些光碟映像檔的引號(")沒結束時,解析程式會「無限迴圈」,
  整個模擬器卡在讀取畫面不動。
  → 已改成遇到檔案結尾就報錯跳出,不再卡住。

• 讀到一半檔案就截斷(不完整)時,舊程式只發個警告就繼續用「壞掉的
  半份資料」解析,之後就會出現各種怪錯。 → 改為直接報錯,不再硬撐。

• 磁軌長度、偏移量等數值有多處會「算錯」(整數溢位、負值),導致
  讀取到錯誤的資料位置。 → 已改用更大的數值範圍並加上檢查。

(以上涉及檔案:core/imgread/chd.cpp、gdi.cpp、cdi.cpp、cue.cpp 等)


================================================================================
二、玩到一半卡死/當機(死結、無限迴圈)
================================================================================
這幾項是「遊戲玩到一半整個模擬器不動了」的最常見原因:

• 垂直同步等待機制有「lost-wakeup」設計缺陷:當螢幕更新頻率和遊戲
  不同步時,模擬器會永久卡住(死結)。 → 已重寫等待機制。

• 渲染(繪圖)過程一旦出錯,舊程式不會通知「這幀結束了」,下一幀
  就會一直等、永遠卡住。 → 出錯時也會正確釋放等待。

• 光碟機 DMA(大批資料搬移)有處判斷條件寫錯,導致「永遠判斷 DMA
  沒完成」,遊戲就會一直等待 → 已修正判斷條件。

• SH4 CPU 動態重編譯(把主機程式碼轉成 PC 可執行的機制)在每次編譯
  後不釋放暫存空間,長時間遊玩記憶體越吃越多(約多佔 165MB)。
  → 已加上編譯後釋放。


================================================================================
三、存檔、金手指相關
================================================================================

• 讀取存檔時,某段「記憶體區塊大小」沒驗證就照著用,被塞進壞資料時
  會直接讀爆。 → 已加上長度驗證,讀到異常會報錯而不會崩壞。

• 存檔的 RAM 大小記錄方式,在「家用機 vs 街機」兩種平台間不一致,
  若存檔在不同平台間交換使用,後面的資料全部會錯位(讀出亂資料)。
  → 已統一記錄格式。

• 讀取存檔的過程,原本跳過了 9 個必要的初始化步驟(例如貼圖快取、
  CPU 快取、記憶體保護重置等),可能造成「讀檔後畫面/行為怪怪的」。
  → 已改走完整流程。

• 金手指(作弊碼)相關:行數太多時記憶體會被塞爆、位址計算可能越界
  寫到不該寫的地方。 → 已加上行數上限與位址邊界檢查。


================================================================================
四、畫面顯示錯誤
================================================================================

• VMU(記憶卡上的小螢幕)的 LCD 顯示,原本「顯示」時讀的是「儲存資料
  (flash)」而不是「目前畫面(LCD)」,導致 VMU 畫面顯示錯誤內容。
  → 已改讀正確的資料來源。

• 紋理(貼圖)處理有一處數值「繞回成巨大值」的錯誤,可能造成畫面的
  記憶體被寫越界(破圖或當機)。 → 已修正計算方式。

• 繪圖排序(哪個三角形先畫)有一條路徑沒檢查「特殊標記」,可能把
  不存在的點拿去畫。 → 已加上檢查,與另一條路徑邏輯一致。

• 幀數掃描線的初始值算錯,造成某些時間相關的效果(如 CRT 掃描線
  效果)差了 1 條線。 → 已修正。


================================================================================
五、線上連線(Naomi 網路對戰等)
================================================================================

• 網路封包解析有多處「越界讀取」:DNS 回應、DHCP 選項、UDP 訊息等,
  遇到惡意或壞掉的封包會讀到不該讀的記憶體。
  → 全部加上範圍檢查。

• GGPO(網路對戰框架)接收資料時沒驗證長度,壞資料可能造成當機。
  → 已加長度驗證。

• 透過 HTTP 下載檔案(例如更新)沒有大小限制,可能被塞爆。
  → 已加上 10MB 上限。

• 程式內建的除錯服務(GDB Server)原本「綁定所有網路介面」,等於把
  你的電腦開了一個口讓外面連進來。 → 改為只綁定本機(loopback)。


================================================================================
六、安全性(這些修復對玩家是「防止壞事」)
================================================================================

• 遊戲可執行的「Lua 腳本」原本開了全部功能(包含可讀寫檔案、執行
  指令的權限),等於給遊戲腳本一把全權鑰匙。 → 改為只開放基本功能
  (運算、字串、表格、數學、UTF-8),拿掉檔案/系統指令權限。

• 幾個「檔案路徑」處理沒檢查「..」這種跳出路徑的寫法,惡意檔案路徑
  可以讓你指定的資料被讀寫到別的目錄(路徑遍歷)。
  → 全部加上路徑檢查。

• 多處陣列/緩衝區的「越界」風險(遊戲提供數值來索引陣列但沒檢查
  範圍)都已補上檢查。

• 程式編譯時加回了「堆疊防護」(stack canary / stack protector),
  這是防止緩衝區溢位被利用的標準防護機制。
  → 同時,我們把原本關閉的安全警告也重新打開,之後程式碼若用不安全
    函式,會在開發階段就先被提醒。


================================================================================
七、執行速度(效能最佳化)
================================================================================

• 光碟讀取原本一次只讀 16 個磁區(16 次系統呼叫),改為一次讀完
  32KB 的批次方式,I/O 開銷大幅下降。

• 讀取「原始格式 = 目標格式」的資料時,原本會先複製到暫存再搬,
  中間那一次複製其實是白做的 → 已移除(fast path)。

• 畫面像素格式轉換(16 位元 RGB → 顯示用格式)改用 SIMD 高速指令,
  一次處理 4 個像素,CPU 負擔下降。

• 繪圖資料搬進顯示記憶體時改用「非時態寫入」(不污染 CPU 快取的
  寫法),避免拖慢其他程式的處理速度。

• 一堆每幀都重複做的準備工作(如 uniform 設定、向量預留空間)改成
  只做一次或減少次數。


================================================================================
八、穩定性:多執行緒資料打架(Race Condition)
================================================================================
模擬器同時有好幾個執行緒在跑(模擬主機 CPU、繪圖、輸入等)。原本有
幾處「兩個執行緒同時讀寫同一個資料」卻沒做同步,這種問題最難抓:
不一定當機,但可能偶爾畫面閃一下、偶爾卡一下、存檔偶爾壞掉。

• 模擬器狀態、幀數計數器改用「原子變數」(讀寫保證不會打架)。
• 繪圖上下文列表、軟重置等處加上鎖,防止同時存取。


================================================================================
附註
================================================================================
以上所有修復都有對應的原始碼位置與「修改前後對照」,屬於模擬器專案
內部持續進行的品質翻修。目的只有一個:讓玩家手上的 Flycast 更穩、更準、
跑得更順。
================================================================================


這是正式版本了,如上所示,修復了很多問題,有添加的功能如下。

sshot-2026-08-07-[13-12-38]-1.png

sshot-2026-08-07-[13-14-42]-1.png

sshot-2026-08-07-[13-14-15]-1.png

如上,硬體重置功能,原版沒有做在介面上讓使用者使用,(對自己寫的程式真有信心)
這是如果遇到在遊戲進行中如果遇到死當的重啟方式(預設值快速鍵 Ctrl + R)
在遊戲中按 tab 鍵也可以叫出上面的視窗。

隱藏重複遊戲選項,不用多說了,而如果是檔名不同,但遊戲相同好像也不會隱藏。

「直接到遊戲選單」的功能這個是要設定熱鍵才可以使用。
我的設計用意是遊戲載入後到遊戲的選單時,按下熱鍵(按一次後這個熱鍵就失效了,這是避免重按,而洗掉原本的設定。)設定節點後。
有勾選這個項目的話,以後進入遊戲就是直接跳到這個選單畫面,直接操作來玩了,這個儲存是與存檔分開的,是不同的地方。

如下畫面所示,紅框中的項目可自由搭配來進入遊戲的行為,但是「直接到遊戲選單」是與其它特定的功能「互斥」的,不能同時用的。

sshot-2026-08-07-[13-13-23]-3.png


這次沒有塞一些特效進來,因為 reshade 可用,所以不放了,喜歡效果的人,可自行下載來套用。

sshot-2026-08-07-[13-51-23].png

sshot-2026-08-07-[13-13-34]-1.png

字型檔我已直接內嵌至 .exe 內了,就算這個 .exe 拿到純英文的環境
,顯示介面還是能正確顯示繁、簡體字型的。(之前做的,有的我也
有放進去。)

說完了,下載吧。
https://u.pcloud.link/publink/sh ... qj3QC0zmVNwtXKHixNX







評分

參與人數 3威望 +1 金錢 +45 貢獻 +1 收起 理由
egg606231 + 10
SAM-123 + 5 精品文章
ili789 + 1 + 30 + 1 感謝分享

查看全部評分

回復

使用道具 舉報

14

主題

126

帖子

3156

積分

銀牌會員

Rank: 5Rank: 5

積分
3156
 樓主| 發表於 2026-7-24 20:42:41 | 顯示全部樓層
本帖最後由 good7 於 2026-8-7 14:38 編輯

================================================================================
                Flycast 專案最佳化、安全性漏洞及其它記錄
================================================================================
一、安全性修復
================================================================================
------------------------------------------------------------------------------
1.1 Critical(即時危及安全性/可利用性)
------------------------------------------------------------------------------

[C-01] CHD sscanf 堆疊緩衝區溢位
  檔案:core/imgread/chd.cpp:198,203,218
  修改:三處 sscanf 的格式字串中 %s → %15s,限制寫入寬度
  說明:type[16] 等四個 16 byte 緩衝區不再被超長元資料溢位
  狀態:已修復

[C-02] ISO9660 目錄記錄越界讀取
  檔案:core/imgread/isofs.cpp:76
  修改:if (index >= data.size()) → if (index + 34 > data.size())
  說明:確保剩餘空間足夠容納完整的目錄記錄結構(至少 34 byte)
  狀態:已修復

[C-03] RetroArch 存檔 MEM 區段大小未驗證
  檔案:core/serialize.cpp:117-122
  修改:加入 if (sectionSize > limit) throw Exception(...)
  說明:MEM 區段的 sectionSize 現在會與剩餘資料長度比較
  狀態:已修復

[C-05/06] JIT 代碼緩衝區 RWX(W^X 違規)+ jit_set_exec 空操作
  檔案:core/windows/win_vmem.cpp:188,254-256
  修改:mem_alloc 初始分配 PAGE_EXECUTE_READWRITE → PAGE_READWRITE;
        實作 jit_set_exec(VirtualProtect 切換 PAGE_READWRITE / PAGE_EXECUTE_READ)
  說明:初始分配改為 RW,編譯完成後 jit_set_exec(true) 切為 RX,實作 W^X 防護
  狀態:已修復

[C-08] Lua 無沙箱 — 任意程式碼執行
  檔案:core/lua/lua.cpp:622-623
  修改:luaL_openlibs(L) 替換為白名單載入(base/table/string/math/utf8)
  說明:移除 os/io/package/debug 函式庫,消除任意命令執行能力
  狀態:已修復

[C-10] getSubPath 路徑遍歷漏洞
  檔案:core/oslib/storage.cpp:151-155
  修改:加入 .. 和絕對路徑檢查,不合法時 throw StorageException
  說明:所有 36+ 處呼叫端現在都受到路徑遍歷保護
  狀態:已修復

[C-1] SH4 動態重編譯器記憶體洩漏
  問題:block cache 累積 ~165 MB 未釋放的 oplist 記憶體
  原因:std::vector 每次 compile 完不釋放 capacity
  影響:boot/gameplay 期間記憶體持續上升
  修復:rdv_CompilePC 回傳後插入 std::vector<shil_opcode>().swap(rbi->oplist)
  狀態:已修復

[C-2] 垂直同步堵塞(lost-wakeup 死結)
  問題:renderEnd.Wait() 和 frame_finished.Wait() lost-wakeup 設計缺陷
  影響:Emu thread 永久阻塞,模擬器死結
  修復:cResetEvent::Wait() → predicate 版 + render() 提早返回時補 renderEnd.Set()
  狀態:已修復

------------------------------------------------------------------------------
1.2 High(越界/溢位/注入等可利用性高)
------------------------------------------------------------------------------

[C1] rec-x86\x86_ops.cpp:326-327 邏輯錯位(stack push 順序顛倒)
  說明:interpreter_fallback(Sh4Context*,u16,OpCallFP*,u32) x86 __fastcall
        stack push 順序反了:oph 與 pc 對調。函式從 stack 讀到錯誤的
        函式指標→呼叫非法位址→崩潰。影響所有 x86 MMU 啟用時的路徑。
  修復:交換兩個 push 指令的順序(__fastcall 參數由右至左 push:先 pc 再 oph)
  狀態:已修復

[C6] pvr_sb_regs.cpp:67-76 陣列越界(無邊界檢查)
  說明:calculate_start_link_addr() 用 SB_SDDIV 直接索引
        ((u16*)base)[SB_SDDIV] / ((u32*)base)[SB_SDDIV],無 bounds check,
        guest 可設 SB_SDDIV 任意值→OOB read。
  修復:拆分 offset/base 變數,計算 elemSize(2 或 4),
        加入 SB_SDDIV >= (RAM_SIZE - offset) / elemSize 溢位安全檢查,
        越界時回傳 2(sort DMA 迴圈終止值)
  狀態:已修復

[C11] TexCache.cpp:1183-1184 Unsigned wraparound → 緩衝區溢寫
  說明:padding + (width - xclip.max - 1) * bpp
        當 xclip.max >= width 時,width - xclip.max - 1 作為 u32 wrap
        成巨大值→pixWriter.advance() 指標跳躍→VRAM 寫越界。
  修復:(width - xclip.max - 1) 改為 (width - clipWidth);
        clipWidth = min(width, xclip.max+1),保證不 wraparound。
        正常路徑數學等價,越界時為 0
  狀態:已修復

[C12] sorter.cpp:67-73 陣列越界(無 primitive restart 檢查)
  說明:sortedTriangles path 直接 rendContext.idx[tri.first + i],
        未檢查 (u32)-1 primitive restart marker(非 sorted path 有檢查)。
        若 index buffer 含 -1,存取 verts[0xFFFFFFFF]。
  修復:提取 idx0/idx2 變數,檢查 (u32)-1 後才 dereference,
        與非 sorted path 邏輯一致
  狀態:已修復

[H-01] CDI fread 回傳值未檢查
  檔案:core/deps/chdpsr/cdipsr.cpp:47-103
  修改:CDI_read_track 中 13 處 fread 全部加入 != 1 return false 檢查
  狀態:已修復

[H-02] GDI 軌道計數無上限 → OOM DoS
  檔案:core/imgread/gdi.cpp:34
  修改:iso_tc == 0 → iso_tc == 0 || iso_tc > 100
  狀態:已修復

[H-03] GDI EndFAD 整數下溢
  檔案:core/imgread/gdi.cpp:99-100
  修改:加入 OFFSET 範圍檢查(< 0 或 > fileInfo.size 時 throw)
  狀態:已修復

[H-04] CDI fseek 偏移量整數溢位
  檔案:core/imgread/cdi.cpp:183
  修改:track.total_length * track.sector_size → (long long)... * ...
  狀態:已修復

[H-05] CHD 數值欄位無範圍驗證
  檔案:core/imgread/chd.cpp:222
  修改:加入 frames/padframes/pregap/postgap 範圍檢查
  狀態:已修復

[H-06] DNS 回應解析無邊界 strlen
  檔案:core/network/dns.cpp:130-137
  修改:加入最小長度檢查;strlen → strnlen;加 reader > end 檢查
  狀態:已修復

[H-07] DHCP 選項越界讀取
  檔案:core/network/dcnet.cpp:709
  修改:while 條件 options - frame < len → options - frame + 2 < len
  狀態:已修復

[H-08] GGPO VF4Card 缺少長度驗證
  檔案:core/network/ggpo.cpp:468
  修改:加入 len >= sizeof(type) + sizeof(card) 檢查
  狀態:已修復

[H-09] registerHandler 使用 assert(Release 被移除)
  檔案:core/hw/mem/addrspace.cpp:236
  修改:assert(rv < HANDLER_COUNT) → verify(rv < HANDLER_COUNT)
  狀態:已修復

[H-10] 處理器 ID 索引無邊界檢查
  檔案:core/hw/mem/addrspace.cpp:123,168
  修改:readt/writet 中加入 verify(id < HANDLER_COUNT)
  狀態:已修復

[H-11] init() 中分配無錯誤檢查
  檔案:core/windows/win_vmem.cpp:67,72
  修改:加入 verify(mem_handle != NULL) 和 verify(base_alloc != NULL)
  狀態:已修復

[H-13] UAF 風險:Stale Block 與代碼快取重用
  檔案:core/hw/sh4/dyna/blockmanager.cpp:316
  修改:bm_ResetCache 中加入 del_blocks.clear()
  狀態:已修復

[H-14] cheats 計數無上限 → DoS/OOM
  檔案:core/cheats.cpp:401
  修改:加入 count 範圍檢查 (0-10000)
  狀態:已修復

[H-15] cheat.size 位元移位 UB
  檔案:core/cheats.cpp:417
  修改:1 << → 1u <<,限制 memSize 範圍 [0,5]
  狀態:已修復

[H-16] apply() 位址遞增無邊界
  檔案:core/cheats.cpp:765,785
  修改:copy 迴圈加邊界條件;setValue 迴圈加 address >= RAM_SIZE break
  狀態:已修復

[H-17] INI 格式注入
  檔案:core/cfg/ini.cpp:282-296
  修改:加入 sanitizeIniValue() 過濾 \n \r;save() 兩處 fprintf 套用
  狀態:已修復

[H-20] GDB stack 命令堆疊緩衝區溢位
  檔案:core/debug/gdb_server.cpp:587-605
  修改:固定/VLA 緩衝區改為 std::string 動態分配
  狀態:已修復

[H-22] ELF 載入器越界讀取 + 整數下溢
  檔案:core/reios/reios_elf.cpp:55-66
  修改:加入 offset+len <= filesize 和 memSize >= len 檢查
  狀態:已修復

[H-23] readdir 非執行緒安全
  檔案:core/oslib/directory.cpp:52
  修改:static dirent d → thread_local dirent d
  狀態:已修復

[H-24] UDP AP 發現越界讀取
  檔案:core/network/dcnet.cpp:470
  修改:while 條件改為 p + 5 <= recvbuf + len
  狀態:已修復

[H-25] GDB 繫結所有介面
  檔案:core/debug/gdb_server.cpp:140
  修改:tcp::v4() → address_v4::loopback()
  狀態:已修復

[H-26] getArcadeFlashPath 路徑遍歷
  檔案:core/oslib/oslib.cpp:103
  修改:加入 fileName 淨化(拒絕 .. / \)
  狀態:已修復

[H15] maple_jvs.cpp:1831-1837 邊界缺失
  說明:handle_86_subcommand case 0x27 用 dma_buffer_in[idx] 無檢查
        cmd_count 與 len 是否超出剩餘資料。
  修復:迴圈內加入兩個 break 條件:
        if (idx + 2 >= dma_count_in) break;
        if (idx + 2 + len > dma_count_in) break;
  狀態:已修復

[H17] TexCache.cpp:1228 Null pointer dereference
  說明:invalidate() 直接 libCore_vramlock_Unlock_block_wb(lock_block)
        未檢查 lock_block 是否已經為 nullptr。
  修復:加入 if (lock_block != nullptr) 檢查後才呼叫並設為 nullptr
  狀態:已修復

[H18] texconv.cpp:34-43(oslib.h) UB(__builtin_clz(0))
  說明:bitscanrev(0) 在 GCC/Clang 呼叫 __builtin_clz(0) 是 UB,
        若 texture width/height=0 時觸發。
  修復:bitscanrev() 函式開頭加入 if (v == 0) return 0;
  狀態:已修復

[H19] texconv.cpp:391-392,496-497 OOB 存取
  說明:detwiddle[2][11][1024] 用 bitscanrev(width/height) 索引,
        若值 >10 時越界,未驗證 texture dimension。
  修復:4 處 detwiddle 存取改為 std::min(bitscanrev(...), 10u)
  狀態:已修復

------------------------------------------------------------------------------
1.3 Medium(中等風險/需條件觸發)
------------------------------------------------------------------------------

[M-12] GGPO Deserializer 限制不一致
  檔案:core/network/ggpo.cpp:448
  修改:1_MB → settings.platform.isNaomi() ? 20_MB : 10_MB
  狀態:已修復

[M-13] CDI 負軌道長度僅警告
  檔案:core/imgread/cdi.cpp:166
  修改:track.length < 0 WARN → track.length <= 0 throw
  狀態:已修復

[M-14b] repeatCount 無上限
  檔案:core/cheats.cpp:419
  修改:限制 repeatCount ≤ 65536
  狀態:已修復

[M-15] INI get_int/64 strstr("0x") 邏輯缺陷
  檔案:core/cfg/ini.cpp:15-25, 27-33
  修改:strstr → 字首檢查 value[0]=='0' && value[1]=='x'/'X'
  狀態:已修復

[M-16] option.h 路徑列表越界存取
  檔案:core/cfg/option.h:212,220
  修改:加入 if (start >= paths.size()) break 邊界檢查
  狀態:已修復

[M-24] saveScreenshot 路徑遍歷
  檔案:core/oslib/oslib.cpp:301
  修改:加入 name 淨化(拒絕 .. / \)
  狀態:已修復

[M-25] descrambl 非執行緒安全全域狀態
  檔案:core/reios/descrambl.cpp:16,33
  修改:static → thread_local(seed 和 idx[])
  狀態:已修復

[M-27] stdclass 路徑拼接無驗證
  檔案:core/stdclass.cpp:67-81,100-114
  修改:get_writable_config_path/data_path 加入 filename 淨化
  狀態:已修復

[M-29] HTTP 回應無大小限制
  檔案:core/oslib/http_client.cpp:62,233
  修改:加入 10MB 上限檢查(Windows + curl 兩處)
  狀態:已修復

[M-31] GDB unpack 無輸入驗證
  檔案:core/debug/gdb_server.cpp:38-46
  修改:加入嚴格 hex 字元範圍驗證,非 hex 回傳 0
  狀態:已修復


================================================================================
二、系統級防護強化(CMAKE/建置層,3 項)
================================================================================

[CMAKE-01] Stack Canary 恢復
  檔案:CMakeLists.txt:102
  修改前:target_compile_options(${PROJECT_NAME} PRIVATE /GR /GS-)
  修改後:target_compile_options(${PROJECT_NAME} PRIVATE /GR /GS)
  說明:/GS- 明確關閉 stack canary,改為 /GS 恢復保護
  狀態:已修復

[CMAKE-02] GCC Stack Protector 啟用
  檔案:CMakeLists.txt:106
  修改:加入 -fstack-protector-strong 編譯選項
  說明:GCC build 加入 stack protector,與 MSVC 的 /GS 對等
  狀態:已修復

[CMAKE-03] 移除 _CRT_SECURE_NO_WARNINGS
  檔案:CMakeLists.txt:112
  修改:移除 $<$<BOOL{MSVC}>:_CRT_SECURE_NO_WARNINGS> 定義
  說明:恢復 MSVC 對不安全 CRT 函數的編譯警告
  狀態:已修復


================================================================================
三、穩定性/正確性修復
================================================================================
------------------------------------------------------------------------------
3.1 執行緒安全/競態條件
------------------------------------------------------------------------------

[C7/H2] emulator.h:207 + 多處 資料競賽(state 非 atomic)
  說明:State state = Uninitialized 非 std::atomic<>,跨 thread 無同步存取:
        read(795,942,961,965,1061)、write(514,683,757,788,954,969);
        [H2] state = Error 在 run() catch block 中未受 mutex 保護,由本項一併解決。
  修復:宣告為 std::atomic<State>
  實際修改:
    emulator.h:207 State state → std::atomic<State> state{Uninitialized}
    emulator.h:217 getState() 改用 state.load()(移除 mutex)
    emulator.h:218 setState() 改用 state.store(s)(移除 mutex)
    emulator.cpp:966 WARN_LOG 中 state 改為 state.load()
  狀態:已修復

[#4] ctx_list 跨執行緒無鎖存取
  目標:core\hw\pvr\ta_ctx.cpp
  問題:ctx_list 在 Emu 執行緒與主執行緒之間無鎖存取,可能導致迭代器失效
  對策:新增 static std::recursive_mutex mtx_list;在 5 個存取點加入 lock_guard
  狀態:已修復

[#6] FrameCount 非原子跨執行緒讀寫
  目標:core\hw\pvr\Renderer_if.h:7, Renderer_if.cpp:22, ta_ctx.cpp:87, spg.cpp:184
  問題:FrameCount 為普通 u32,多執行緒讀寫 → C++ data race (UB)
  對策:將 u32 FrameCount 改為 std::atomic<u32> FrameCount
  狀態:已修復

[M-10] NaomiNetwork 競態條件(未修復,需架構調整)
  檔案:core/network/naomi_network.h
  說明:_startNow 需改為 atomic
  狀態:未修復(需外部配合)

[H22] CustomTexture.cpp:141-160 Memory ordering
  說明:custom_image_data 非 atomic 寫在 atomic decrement 之後,
        缺 release/acquire ordering,其他 thread 可能看到 decrement=0
        但 custom_image_data 尚未 visible。
  修復:custom_load_in_progress-- 前加入
        std::atomic_thread_fence(std::memory_order_release)
  狀態:已修復

[#21] dc_reset 軟重設與 unloadGame 可能並行
  目標:core\emulator.cpp:720-726
  問題:dc_reset(false) 不在 Emulator::mutex 內,可能與 unloadGame 並行修改
  對策:在 dc_reset 呼叫外包裹 std::lock_guard<std::mutex>
  狀態:已修復

[H10] decoder.cpp:565-615 邊界缺失
  說明:MatchDiv32() 在迴圈中 IReadMem16(v_pc) 最多 32 次(128 bytes)
        超出目前 block 範圍無檢查。
  修復:迴圈開頭加入 if ((v_pc >> 24) != (pc >> 24)) break;
        確保 v_pc 不跨越 16MB 區域邊界
  狀態:已修復

------------------------------------------------------------------------------
3.2 死結/掛起
------------------------------------------------------------------------------

[#2] Renderer 例外導致死結
  目標:core\hw\pvr\Renderer_if.cpp:194-215
  問題:renderer->rocess/Render 拋出例外時,FinishRender 不執行 → rqueue 不清空
        → frame_finished 不 Set → 下一幀 QueueRender 永久阻塞 → 模擬器死結
  對策:用 try/catch 包裝 Process+Render+present 區段;catch 中執行
        renderEnd.Set() + rend_allow_rollback() 確保渲染狀態一致
  狀態:已修復

[C-2] 垂直同步堵塞(lost-wakeup)— 詳見「一、安全性 1.1  [C-2]」
  狀態:已修復

------------------------------------------------------------------------------
3.3 邏輯錯誤/狀態不一致
------------------------------------------------------------------------------

[C3] nullDC.cpp:468 狀態不一致(跳過關鍵重設步驟)
  說明:dc_bookmarkLoad() 直接呼叫 dc_deserialize() 而非 emu.loadstate(),
        跳過:custom_texture preload、aica::arm::recompiler::flush()、
        mmu_flush_table()、bm_Reset()、memwatch::unprotect/reset、
        mmu_set_state()、getSh4Executor()->ResetCache() 等。
  修復:dc_deserialize(deser) → emu.loadstate(deser)
        (loadstate 內部呼叫 dc_deserialize 並執行前後 9 個關鍵步驟)
  狀態:已修復

[C8] cfg/option.h:134-139 邏輯錯誤(per-game 設定被靜默刪除)
  說明:save() 呼叫 doLoad(section,name) 傳入 value 當預設值,
        若 global config 無該選項,doLoad() 回傳 value,
        value==doLoad() 永遠 true→cfgDeleteEntry 刪除 per-game 條目,
        使用者對預設值的修改遺失。
  修復:新增 3 參數 doLoad(section, name, defValue) 重載(if constexpr),
        save() 改為 doLoad(section, name, defaultValue)
  狀態:已修復

[C9] maple_devs.cpp:601 資料錯位(錯誤的資料來源)
  說明:VMU LCD block read 使用 wptr(flash_data,192),
        應使用 wptr(lcd_data,192) 回傳 LCD 顯示內容而非 flash 儲存資料。
  修復:flash_data → lcd_data(與 write path line 694 rptr(lcd_data,192) 一致)
  狀態:已修復

[H1] emulator.cpp:489-493 狀態不一致
  說明:init() 在 Error 狀態時 verify(state==Init) assert→crash,
        無從 Error 恢復到 Init 的路徑。
  修復:init() 加入 Error 狀態恢復:if (state == Error) { term(); state = Uninitialized; }
        term() 條件改為 if (state == Init || state == Error)
  狀態:已修復

[H8] serialize.cpp:139,151-154 狀態不一致
  說明:ram_size 僅在 isConsole() 時序列化。若 save 在 console 建立、
        load 在非 console 平台(或反之),後續所有欄位偏移→stream desync。
  修復:Deserializer 與 Serializer 皆改為無條件 serialize(ram_size)
        (if(_version >= V42)),確保所有平台 stream 佈局一致
  狀態:已修復

[H12] ta_vtx.cpp:956-965 邏輯錯位
  說明:CurrentPPlist==nullptr(wldkickspw hack)時 return,
        但 CurrentPP 在此之前已指向可能失效的 context。
  修復:將 if (CurrentPPlist == nullptr) return 移至函式開頭
  狀態:已修復

[H13] maple_jvs.cpp:1044-1053,1123-1133 順序相依(side effect coupling)
  說明:read_joystick_x() 修改成員 axis_y,read_joystick_y() 依賴此值,
        若呼叫順序變更→Y axis 回傳 0。
  修復:read_joystick_y() 改為獨立讀取 mapleInputState,
        呼叫 limit_joystick_magnitude 後回傳,不依賴 read_joystick_x() 副作用
  狀態:已修復

[H14] gdromv3.cpp:1309 狀態不一致
  說明:SB_GDLEN==0 視為 0x02000000(line 1234),
        SB_GDLEND 用 & 0x01ffffe0 mask wrap 在 0x01ffffe0,
        與 SB_GDLEN(0) 永遠不相等→DMA 永不完成。
  修復:mask 改為 0x01ffffff,允許 SB_GDLEND 達到 0x01ffffff
  狀態:已修復

[H26] nullDC.cpp:134 狀態不一致
  說明:dc_savestate() 在 save 成功前就 clear lastStateFile,
        若 save 失敗,lastStateFile 已清空但檔案可能不完整。
  修復:移除 line 134 的 lastStateFile.clear(),
        改在 save 成功路徑(free(data) 後)加入
  狀態:已修復

[H3] stdclass.cpp:27 邏輯錯誤
  說明:file_exists() 使用 access(filename, R_OK) 而非 F_OK,
        存在但不可讀時回傳 false。
  修復:R_OK → F_OK;oslib/directory.h 新增 #define F_OK 0
  狀態:已修復

[M1] sh4_cycles.cpp:443,479 邏輯錯誤(log 誤植)
  說明:write function 內 log 寫 "Invalid read size" / "Invalid read to area 2",
        應為 "Invalid write"。
  修復:line 443 → "aica: Invalid write size";line 479 → "Invalid write to area 2"
  狀態:已修復

[H21] vulkan_renderer.cpp:119-123 順序顛倒
  說明:framebufferTexIndex %= GetSwapChainSize() 在 vector resize 前計算,
        若 swap chain 變小,index 可能超出 vector 大小。
  修復:將 framebufferTexIndex 計算移至 resize 之後
  狀態:已修復

[H24] holly_intc.cpp:268,294 不一致
  說明:Write_SB_ISTNRM<true> 無 &,Write_SB_ISTNRM<false> 有 &,
        template argument deduction 可能選擇不同 overload。
  修復:line 294 &Write_SB_ISTNRM<false> → Write_SB_ISTNRM<false>(統一無 &)
  狀態:已修復

[M6] spg.cpp:20 初始化
  說明:prv_cur_scanline = -1 (0xFFFFFFFF) sentinel 值,
        首次 getNextSpgInterrupt() 計算差 1 scanline。
  修復:prv_cur_scanline = -1 → prv_cur_scanline = 0
  狀態:已修復

[H11] elan.cpp:1614,1627 未初始化
  說明:schedId=-1(default),若 executeCommand() 在 init() 前被呼叫
        (via write_elancmd memory write handler),sh4_sched_request(-1)。
  修復:兩處 sh4_sched_request(schedId, 512) 前加入 if (schedId != -1)
  狀態:已修復

[C2] emulator.cpp:842 UB(字串自我賦值 via c_str())
  說明:settings.content.gameId = settings.content.gameId.c_str()
        C++17 operator=(const char*) 可能在 realloc 時 invalidate 來源
        指標→編譯器優化後產出錯誤結果。
  修復:改為 std::string(settings.content.gameId.c_str()),
        建立獨立暫存物件消除 aliasing UB,語意保持(截斷至首個 null)
  狀態:已修復

[M5] ta_vtx.cpp:1041-1062 除零
  說明:CaclulateSpritePlane 中 k3 = (AC_x * AB_y - AC_y * AB_x),
        若 k3==0 或 AB_x==0 && AB_y==0 時除零→NaN。
  修復:k3==0 時 k1/k2 設為 0;AB_x==0 && AB_y==0 時跳過(退化邊)
  狀態:已修復

[M7] spg.cpp:105 除零
  說明:(prv_cur_scanline + 1) % pvr_numscanlines,若 pvr_numscanlines==0 時除零。
  修復:加入 if (pvr_numscanlines > 0) 保護
  狀態:已修復

[M8] maple_devs.cpp:1075-1078 符號混淆
  說明:s16 INC = (VIBSET>>24) & 0xFF,0-255 存入 s16 128-255 變負。
  修復:u8 inc_raw = (VIBSET>>24) & 0xFF;INH 時 INC = -(s16)inc_raw;
        非 INH 且非 EXH 時 INC = inc_raw
  狀態:已修復

[H20] gl4/gles.cpp:326-333 缺少功能
  說明:Two-volume mode area 1 的 palette texture 跳過 palettePixel 解碼
        (只在 area1=false 分支呼叫),直接取 texcol。
  修復:area1 分支改為依 pp_Palette 值選擇 texture/palettePixel/palettePixelBilinear,
        使用 tex1 + vec3(vtx_uv1, 1.0) 或 vec3(vtx_uv1.xy, vtx_uv.z)
  狀態:已修復

------------------------------------------------------------------------------
3.4 資源管理/錯誤處理
------------------------------------------------------------------------------

[H4] stdclass.cpp:241-247 邏輯錯誤
  說明:RamRegion::free() 未重置 ownsMemory=false,重複呼叫時
        freeAligned(nullptr)(若 data=nullptr)。
  修復:free() 函式中 data=nullptr 後加入 ownsMemory = false
  狀態:已修復

[H5] stdclass.cpp:234-239 邏輯錯誤
  說明:RamRegion::alloc() 未檢查 allocAligned() 回傳值是否為 nullptr,
        失敗時 data=nullptr 但 ownsMemory=true。
  修復:allocAligned() 後加入 if (data == nullptr) throw std::bad_alloc()
  狀態:已修復

[H7] nullDC.cpp:280 錯誤處理(catch 太窄)
  說明:dc_loadstate() 只 catch Deserializer::Exception,
        其他例外(bad_alloc、runtime_error)未處理→free(data) 不執行。
  修復:改為 catch(const std::exception&)
        (Deserializer::Exception 繼承 std::runtime_error,涵蓋於 std::exception)
  狀態:已修復

[H25] nullDC.cpp:327-333 資源洩漏
  說明:pngData.resize(header.pngSize) 若 throw bad_alloc,
        fclose(f) 不執行→FILE* leak。
  修復:fread + resize 區塊包入 try-catch,catch 中 fclose(f) 後 re-throw
  狀態:已修復

[M10] emulator.cpp:945-946 錯誤處理
  說明:getSh4Executor()->Start() 不在 try-catch 內,若 throw 則 state 停留在 Running。
  修復:Start() 呼叫移入 try block 內,在 runInternal() 之前
  狀態:已修復

------------------------------------------------------------------------------
3.5 型別/運算安全
------------------------------------------------------------------------------

[M3] ta_vtx.cpp:1235-1237 窄化轉換
  說明:(int)vd_rc.render_passes.back().op_count 從 u32 轉 int,
        若 >INT_MAX 變負數,與 size()(size_t)比較時轉為巨大值。
  修復:移除 3 處 (int) cast,直接比較 u32 與 size()
  狀態:已修復

[M2] ta_vtx.cpp:31 型別混用
  說明:constexpr u32 ListType_None = -1,
        -1 作為 u32 為 0xFFFFFFFF,比較語意不明確。
  修復:改為 constexpr u32 ListType_None = 0xFFFFFFFF 並註明意圖
  狀態:已修復

[H16] TexCache.cpp:876 UB(strict aliasing)
  說明:(u8 *&)dest 從 u16* 轉 u8*& 違反 strict aliasing。
  修復:內部存 u8*,讀取時 cast 為 u16*:
        (u8 *&)dest += bytes → dest = reinterpret_cast<u16*>(reinterpret_cast<u8*>(dest) + bytes)
  狀態:已修復

[M21] rec-x64\rec_x64.cpp:338-341 語意錯誤
  說明:shop_mul_s64(有號 64-bit 乘法)使用 mul(無號)非 imul(有號),
        目前因取 lower 64 bits 結果相同而正確,語意錯誤。
  修復:mul(rcx) → imul(rcx)(lower 64 bits 結果相同,語意正確)
  狀態:已修復

[M12] pvr_regs.cpp:91-107 執行緒安全
  說明:regName() 回傳 static char[32] 指標,非 thread-safe。
  修復:static char regName[32] → static thread_local char regName[32]
  狀態:已修復


================================================================================
四、效能最佳化
================================================================================

------------------------------------------------------------------------------
4.1 檔案讀取/載入(I/O 路徑)
------------------------------------------------------------------------------

[M-4] 檔案讀取系統呼叫過多
  問題:GDROM DMA 讀取 16 磁區產生 16 次 fread 系統呼叫
  影響:16 次 syscall 替代 1 次,提高 I/O 開銷
  修復:RawTrackFile::Read 新增 batch 版本,一次 fread(32KB)
  狀態:已修復

[M-5] 檔案讀取中繼緩衝區
  問題:原始格式 == 目標格式時,temp buffer memcpy 完全多餘
  影響:每 DMA 額外 3-5μs
  修復:Disc::ReadSectors fast path,skip temp buffer
  狀態:已修復

[L-1] elan.cpp cache 污染
  問題:Naomi2 紋理 DMA 使用普通 memcpy 污染 L2/L3 cache
  影響:SH4 執行緒 cache 效率大幅下降
  修復:改用 non-temporal store (_mm_stream_si128)
  狀態:已修復

------------------------------------------------------------------------------
4.2 渲染/TA 管線
------------------------------------------------------------------------------

【最佳化 1】ta_thd_data32_i() — TA 資料接收瓶頸
  目標:core/hw/pvr/ta.cpp:545-588
  方法:非時態寫入 + 預取 + 批次展開 + PCW 讀取合併
  修改:
    1. 新增 simd256_t::stream_copy() 方法(非時態寫入)
    2. 新增 #include <emmintrin.h>(x86 SSE2 標頭)
    3. ta_thd_data32_i():使用 dst->stream_copy(data)、__builtin_prefetch、PCW 讀取合併
    4. ta_vtx_data():批次展開從 4 次增加到 8 次
  狀態:已修復

【最佳化 2】ReadFramebuffer() — 像素格式轉換 SIMD 化
  目標:core/rend/TexCache.cpp:634-757
  方法:SSE2 SIMD 加速 fbde_0555 和 fbde_565 格式的像素轉換
  修改:
    1. 新增 #include <emmintrin.h>、#include <type_traits>
    2. fbde_0555 格式 SIMD 路徑:每次處理 4 個 16 位元像素
    3. fbde_565 格式 SIMD 路徑:每次處理 4 個 16 位元像素
    4. 標量回退路徑:保留原始程式碼處理剩餘像素
    5. 條件編譯:#if HOST_CPU == CPU_X86
  狀態:已修復

【最佳化 3】GLES Shader Uniform 迴圈 — 版本計數器延遲更新
  目標:core/rend/gles/gles.cpp:1185-1190
  方法:版本計數器機制,消除每幀 N 次 UseProgram + Set
  修改:
    1. PipelineShader 新增 u32 uniformVersion = 0
    2. ShaderUniforms_t 新增 u32 version = 0
    3. renderFrame 中 6 行迴圈取代為 ShaderUniforms.version++
    4. SetGPState 中 UseProgram 後加延遲檢查
  狀態:已修復

【最佳化 4】makeIndex / makePrimRestartIndex — reserve + is_vertex_inf 位元級最佳化
  目標:core/hw/pvr/ta_util.cpp:520, 438, 29
  方法:is_vertex_inf 位元級檢查 + idx 向量 reserve
  修改:
    1. is_vertex_inf:使用 float_bits() 輔助函式,6 浮點運算 → 2 整數 AND+比較
    2. makeIndex/makePrimRestartIndex:函式開頭加 reserve(size + totalVerts*2)
  狀態:已修復

【最佳化 5】AppendPolyVertex — verts/idx 初始 reserve 提升
  目標:core/hw/pvr/ta_ctx.h:359-360
  修改:rend.verts.reserve(32768) → 65536、rend.idx.reserve(32768) → 65536
  理由:遊戲實測 vtx 最大 34,046、idx 最大 47,178,原始 32768 不足
  狀態:已修復

【最佳化 6】Provoking Vertex 修正 — memcpy 合併
  目標:core/rend/sorter.cpp:23
  方法:將 4 次 4B memcpy 合併為 2 次 8B memcpy
  修改:sizeof(vertex.col) + sizeof(vertex.spc) 合併複製
  狀態:已修復

[#6] GLES u32→u16 索引轉換 — resize + 自動向量化
  目標:core/rend/gles/gles.cpp:1090(upload_vertex_indices)
  修改:
    1. resize 替代 clear+reserve:單一呼叫設定大小
    2. 直接賦值替代 push_back:消除每元素容量檢查分支
    3. 緊密迴圈 + const 指標:GCC 13 可自動向量化(SSE2 pack 指令)
  狀態:已修復

------------------------------------------------------------------------------
4.3 GPU/SwapChain/Vulkan
------------------------------------------------------------------------------

[M-1] vulkan buffer 膨脹
  問題:imageCount 因 swapInterval 高乘數而過度配置 (240Hz: 12張)
  影響:GPU 視訊記憶體浪費,延遲增加
  修復:固定 imageCount = max(3, minImageCount) (120Hz: 3張, 240Hz: 3張)
  狀態:已修復

[M-2] vblank polling 堵塞
  問題:vblank 內 network IO polling loop 堵塞 100ms
  影響:幀率崩跌至 ~10fps
  修復:移除 100ms polling loop,使用單次 non-blocking 呼叫
  狀態:已修復

[M-3] swapchain 資源重複配置
  問題:CreateSwapChain 每次重建都從頭配置,oldSwapchain 未傳入
  影響:重建延遲 + 記憶體峰值
  修復:修改 createInfo.oldSwapchain = swapChain.get()
  狀態:已修復

[L-2] sortTriangles 可能觸發 realloc
  問題:static vector 在 trigCount 波動時觸發 realloc
  影響:僅場景切換時有分配,但可能影響
  修復:添加 capacity 檢查後再 resize
  狀態:已修復

[L-3] n2uniforms 每幀 resize
  問題:Naomi2 每幀 uniform buffer 分配
  影響:uniform 記憶體頻繁分配
  修復:static vector + reserve
  狀態:已修復


================================================================================
五、程式碼品質/可維護性
================================================================================

[C-3] 紋理濾波重複邏輯
  問題:5 處後端重複的采樣器濾波 if-else 邏輯
  影響:程式碼冗餘,單點維護困難
  修復:提取為 static inline isNearestFilter() 共用函式
  狀態:已修復

[M14] TexCache.cpp:97-103 Dead code
  說明:invalidate() 將 lock(auto&)設為 nullptr(via vramlock_list_remove),
        if(lock!=nullptr) 永遠 false。
  修復:移除 invalidate() 後的 if(lock!=nullptr) + ERROR_LOG + die 區塊
  狀態:已修復

[M1] sh4_cycles.cpp:443,479 log 誤植 — 詳見「三、穩定性 3.3」
  狀態:已修復


================================================================================
六、載入檔案相關.實質對策整理
================================================================================
------------------------------------------------------------------------------
6.1 高優先(掛起/錯誤資料/溢位)
------------------------------------------------------------------------------

[1] 引號解析無限迴圈 → 程式掛起 gdi.cpp / cue.cpp
  位置:gdi.cpp L68-70、cue.cpp L148-150
  問題:引號內遇到 EOF 時 for(;;) 永遠不結束,程式整個掛起。
  對策:加入 EOF 檢查 `if (!(gdi >> last))
        throw FlycastException(...)`,改為拋出異常。
  狀態:已修復

[2] fread 截斷仍繼續解析 gdi.cpp / cue.cpp
  位置:gdi.cpp L26-30、cue.cpp L70-74
  問題:讀檔不完整時僅發 WARNING,卻繼續用截斷資料解析,造成後續錯誤。
  對策:改為 throw FlycastException,不再沿用不完整資料。
  狀態:已修復

[3] RawTrackFile 整數溢位(u32*u32) common.h
  位置:common.h L242-251
  問題:`first_fad * secfmt` 用 u32*u32 可能溢出,產生錯誤偏移量。
  對策:改用 u64 運算後再轉 s32,並加 `verify(product <= (u64)UINT32_MAX)`。
  狀態:已修復

[4] CDITrackFile 整數溢位 cdi.cpp
  位置:cdi.cpp L16-23
  問題:`first_fad * secfmt` u32*u32 可能溢出。
  對策:改用 u64 運算後轉 s32,加 verify 哨兵檢查。
  狀態:已修復

[5] Texture DMA base+size 越界 elan.cpp
  位置:elan.cpp L1628 與 L1644
  問題:原僅檢查 `link->size > VRAM_SIZE` 限總量,未檢查 `base+size` 越界;
        寫入 VRAM 與 ELAN_RAM 可能超出容量。
  對策:L1628 新增 `(vramAddress & VRAM_MASK) + size > VRAM_SIZE`;
        L1644 再增 `(offset & ELAN_RAM_MASK) + size > ERAM_SIZE_MAX`。
  狀態:已修復

[8] strict aliasing violation(未定義行為) common.cpp
  位置:common.cpp L200-203
  問題:`return *(u32 *)p;` 把 u8[4] 強轉為 u32*,違反 C++ 標準。
  對策:改用 memcpy 避免 strict aliasing violation。
  狀態:已修復

[10] DMA 32-bit path len 非 4 倍數造成無限迴圈 dmac.cpp(hw/sh4)
  位置:dmac.cpp L88
  問題:32-bit DMA 路徑 `while(len>0){ len-=4 }` 若 SB_C2DLEN 非 4 倍數
        會無限迴圈(或大量反覆執行)。
  對策:進入迴圈前 `len &= ~3u;` 向下對齊 4 的倍數,等同真實硬體忽略低位。
  狀態:已修復

------------------------------------------------------------------------------
6.2 中優先(越界/邊界/型別安全性)
------------------------------------------------------------------------------

[6] 整數 <= 0 檢查位置錯誤 cdi.cpp
  位置:cdi.cpp L173-178
  問題:track.length <= 0 檢查在 Track 已建立並 push_back 之後才做,
        不完整 track 會被加入磁碟結構。
  對策:將檢查移到 push_back 之前。
  狀態:已修復

[7] truncated track 處理邏輯錯誤 cdi.cpp
  位置:cdi.cpp L180-189
  問題:truncated track 時 SEEK_CUR 加上 track.total_length 卻沒乘
        track.sector_size(非 truncated 時有乘),導致檔案定位錯誤。
  對策:truncated 分支同樣乘以 sector_size。
  狀態:已修復

[9] GDI SSIZE=0 產 null track gdi.cpp
  位置:gdi.cpp L91-95
  問題:SSIZE==0 時 t.file 為 nullptr 仍被 push_back,讀取時可能
        解引用錯誤。
  對策:SSIZE==0 時 throw FlycastException。
  狀態:已修復

[11] DreamPicoPort words OOB 上限 dreampicoport.cpp
  位置:dreampicoport.cpp L481-485
  問題:msg.data 固定 1024byte、僅容 256 u32;原迴圈無上限保護,
        第 257 個 word 寫入 dst[1024] 越界 1byte。
  對策:`u32 dataWords = std::min(words.size()-1, sizeof(msg.data)/4);` 鎖住上限。
  狀態:已修復

[12] isofs offset >= len 下溢 offset isofs.cpp
  位置:isofs.cpp L131
  問題:caller 以 offset > len 呼叫時 `len - offset` 下運算成 OOB 值。
  對策:min() 前先 `if (offset >= len) return 0;`
  狀態:已修復

[13] m1cartridge buffer_actual_size < 2 m1cartridge.cpp
  位置:m1cartridge.cpp L37-38
  問題:直接 `hist[0] = buffer[buffer_actual_size - 2];`
        buffer_actual_size < 2 時負索引 / 越界。
  對策:改為 `if (buffer_actual_size >= 2)` 先檢查。
  狀態:已修復

[14] CHD cached_hunk 初值少一個 0?(哨兵差異) chd.cpp
  位置:chd.cpp L23
  問題:`u32 cached_hunk[2] = { 0xFFFFFFF, 0xFFFFFFF }` 少一個 F,
        可能造成極端 false hit。
  對策:改為 { 0xFFFFFFFF, 0xFFFFFFFF }。
  狀態:已修復

[15] CHD C 式向下轉型 chd.cpp
  位置:chd.cpp L276
  問題:`((CHDTrack*)tracks.back().file)` 用 C 風格轉型,型別不安全。
  對策:改用 static_cast<CHDTrack*>。
  狀態:已修復

[16] GDI OFFSET 驗證順序 gdi.cpp
  位置:gdi.cpp L106-112
  問題:RawTrackFile 在 OFFSET 驗證之前建立,物件生命週期管理不佳。
  對策:先驗證 OFFSET 再建立 RawTrackFile。
  狀態:已修復


================================================================================
七、後端移植/平台建置
================================================================================

------------------------------------------------------------------------------
7.1 GLES/GL4 後端移植至新 core API
------------------------------------------------------------------------------
對策日期:2026-07-22
目標:core/rend/gles/ + core/rend/gl4/(整個 OpenGL/GLES 後端)

修改檔案(10 個):
  CMakeLists.txt:     USE_OPENGL 區塊加入 gles/gl4 原始碼 + 編譯定義 + opengl32
  11.bat:             USE_OPENGL=OFF → ON
  wsi/context.h:      加 virtual void swap() {}
  wsi/sdl.h:          swap() → swap() override
  gles/gles.h:        TransformMatrix → TransformMatrix<COORD_OPENGL>
  gles/gles.cpp:      FlycastException×3, GLGraphicsContext cast, 移除swapInterval
  gles/gldraw.cpp:    setTileClip用GetTileClip, Rect→int[4], writeFramebufferToVRAM重寫
  gles/gltex.cpp:     fbClip→fb_X/Y_CLIP, WriteTextureToVRam去除clip參數
  gl4/gldraw.cpp:     FlycastException, setTileClip新API, Rect→int[4]
  gl4/gles.cpp:       getBaseScissor新API

解決的 12 個不相容點:
  1.  TransformMatrix 模板化
  2.  Rect 型別消除(改用 int clip_rect[4])
  3.  getTileClip → GetTileClip 自由函式
  4.  getBaseScissor → GetScissorMatrix + fb_X/Y_CLIP 計算
  5.  GraphicsContext 版本方法
  6.  swap() → GraphicsContext 加虛擬方法
  7.  setSwapInterval → 移除
  8.  globClip → ta_GLOB_TILE_CLIP 計算
  9.  fbClip → fb_X_CLIP/fb_Y_CLIP
  10. swapInterval → 移除
  11. RendererException → FlycastException
  12. getWriteFBToVramParams/writeFramebufferToVRAM → 重寫

------------------------------------------------------------------------------
7.2 UploadToGPUGl4 immutable storage 尺寸/格式變化檢查缺失修復
------------------------------------------------------------------------------
目標:core/rend/gles/gltex.cpp:97-105
問題:UploadToGPUGl4 使用 glTexStorage2D 建立不可變紋理,但只在 texID == 0 時建立,
      後續紋理更新時不檢查尺寸/格式/mipmapLevels 是否一致
修改:
  1. gles.h — TextureCacheData 加入 4 個 storage 追蹤成員變數
  2. gltex.cpp — UploadToGPUGl4 加入屬性變化檢查 + 刪舊建新邏輯


================================================================================
八、未修復項目(需外部配合)
================================================================================

[M-08] 未加密 WebSocket (ice.cpp:234, ws:// → wss://)
  原因:需伺服器端(lobby.flyca.st)支援 wss://

[M-09] 硬編碼 TURN 憑證 (ice.cpp:436)
  原因:需伺服器端 REST API 發放短期憑證

[M-10] NaomiNetwork 競態條件 (naomi_network.h)
  原因:需架構調整(_startNow 改為 atomic)


================================================================================
附註
================================================================================
- 原始記錄修復日期:2026-07-24、2026-07-28、2026-07-29
- 已修復項目標記「狀態:已修復」;未修復者明確標示原因。
- 總計整理約 100 餘項。
================================================================================

回復

使用道具 舉報

0

主題

4984

帖子

4485

積分

金牌會員

Rank: 6Rank: 6

積分
4485
發表於 2026-7-24 23:36:46 | 顯示全部樓層
感謝大大的分享
回復

使用道具 舉報

7

主題

5599

帖子

5237

積分

金牌會員

Rank: 6Rank: 6

積分
5237
發表於 2026-7-25 00:43:20 | 顯示全部樓層
感謝大大無私熱心提供的分享遊戲資訊
回復

使用道具 舉報

0

主題

67

帖子

133

積分

初級會員

Rank: 2

積分
133
發表於 2026-7-25 08:43:40 | 顯示全部樓層
謝謝大大的用心分享
回復

使用道具 舉報

5

主題

511

帖子

856

積分

高級會員

Rank: 4

積分
856
發表於 2026-7-25 12:12:14 | 顯示全部樓層
謝謝分享~~
回復

使用道具 舉報

0

主題

2874

帖子

2521

積分

銀牌會員

Rank: 5Rank: 5

積分
2521
發表於 2026-7-25 18:20:37 | 顯示全部樓層
感谢分享
回復

使用道具 舉報

0

主題

5

帖子

66

積分

初級會員

Rank: 2

積分
66
發表於 2026-7-25 18:42:17 | 顯示全部樓層
能不能也给READREAM制作汉化?
回復

使用道具 舉報

14

主題

126

帖子

3156

積分

銀牌會員

Rank: 5Rank: 5

積分
3156
 樓主| 發表於 2026-7-25 19:43:37 | 顯示全部樓層
M.K. 發表於 2026-7-25 18:42
能不能也给READREAM制作汉化?

那東西好像很久不更新了。

回復

使用道具 舉報

0

主題

139

帖子

398

積分

中級會員

Rank: 3Rank: 3

積分
398
發表於 2026-7-26 12:26:37 | 顯示全部樓層
要寫一個模擬器好浪費時間....也很辛苦   如果寫一個微軟系統內建模擬器 可以通用安卓 蘋果 各式掌機  筆電  電視機 平板  桌上型 這種模擬器應該是更加難寫....
回復

使用道具 舉報

您需要登錄後才可以回帖 登錄 | 立即註冊

本版積分規則

Archiver|小黑屋|遊戲年代 GOAL GOAL

GMT+8, 2026-8-8 03:30 , Processed in 0.029882 second(s), 24 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回復 返回頂部 返回列表