mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
v0.5.0 playtest fold-in: fog and near-plane clip, crash fixes, polish (D540-D564)
Deck/Windows playtest fixes: fog rework (D540) and RSP-style CPU near-plane clip with per-vertex N64 fog (D543), with fog recomputed on near-plane-clipped vertices as the RSP does, so the haze around the player stays steady (D553); colour-combiner clamp for banded walls (D548); rocket and thrown-item stack overflows (D545, D549); menu double-trigger mitigation (D541); Dam ending first-shot camera swivel restored by removing an old port look-at workaround (D552); settings standardisation (D546), live All unlocked (D547), F10 mouse (D544); static window title and the project mark (the in-game crosshair shape, site gold) as window/taskbar icon and favicon (D550). Reference frames re-based on Windows, Linux and Steam Deck (A/B-confirmed); capture_p7.sh runs on Windows. Opt-in update check, off by default (D551: one HTTPS request to GitHub per launch when enabled; winhttp.dll loaded only then; the tag CI job checks the tag against GE007_VERSION). Release notes, ROADMAP, internals and security docs updated; tree-hygiene pass. Refs: D540 D541 D543 D544 D545 D546 D547 D548 D549 D550 D551 D552 D553 D554 D555 D556 D557 D558 D559 D560 D561 D562 D563 D564
This commit is contained in:
1 parent
d7b9371afb
commit
4b710728bf
243 files changed
+2908
-614
No files matched your search
@@ -0,0 +1,46 @@
|
||||
/* Generated by tools_pc/make_icon.py -- do not edit by hand.
|
||||
* The project mark (a generic version of the GE default aim
|
||||
* crosshair) as a 32x32 RGBA array, row-major, plain R,G,B,A
|
||||
* byte order per pixel; used by gfx_sdl2.cpp for
|
||||
* SDL_SetWindowIcon. Regenerate: python tools_pc/make_icon.py */
|
||||
#ifndef GE007_ICON_H
|
||||
#define GE007_ICON_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define GE007_ICON_SIZE 32
|
||||
static const uint8_t ge007_icon_rgba[GE007_ICON_SIZE][GE007_ICON_SIZE][4] = {
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 6, 7, 7,255},{ 6, 7, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 2, 4, 6,255},{ 2, 4, 6,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 6, 6, 7,255},{ 17, 14, 10,255},{ 17, 14, 10,255},{ 6, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 7, 7,255},{ 2, 4, 6,255},{ 16, 14, 10,255},{211,147, 53,255},{211,147, 53,255},{ 16, 14, 10,255},{ 2, 4, 6,255},{ 5, 7, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 8, 7, 7,255},{ 5, 5, 7,255},{ 21, 17, 11,255},{226,157, 57,255},{226,157, 57,255},{ 21, 17, 11,255},{ 5, 5, 7,255},{ 8, 7, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 8, 7, 8,255},{ 9, 8, 8,255},{ 13, 11, 9,255},{ 10, 9, 8,255},{ 17, 14, 9,255},{222,154, 56,255},{222,154, 56,255},{ 17, 14, 9,255},{ 10, 9, 8,255},{ 13, 11, 9,255},{ 9, 8, 8,255},{ 8, 7, 8,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 6, 7, 7,255},{ 9, 8, 8,255},{ 10, 9, 8,255},{ 10, 9, 8,255},{ 0, 1, 5,255},{ 0, 0, 3,255},{ 29, 22, 12,255},{224,156, 56,255},{224,156, 56,255},{ 29, 22, 12,255},{ 0, 0, 3,255},{ 0, 1, 5,255},{ 10, 9, 8,255},{ 10, 9, 8,255},{ 9, 8, 8,255},{ 6, 7, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 6, 7, 7,255},{ 9, 8, 8,255},{ 12, 10, 9,255},{ 6, 6, 7,255},{ 3, 3, 6,255},{ 70, 50, 22,255},{130, 91, 35,255},{216,150, 54,255},{233,162, 58,255},{233,162, 58,255},{216,150, 54,255},{130, 91, 35,255},{ 70, 50, 22,255},{ 3, 3, 6,255},{ 6, 6, 7,255},{ 12, 10, 9,255},{ 9, 8, 8,255},{ 6, 7, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 6, 7, 7,255},{ 9, 8, 8,255},{ 10, 9, 8,255},{ 1, 2, 6,255},{ 20, 15, 10,255},{162,113, 42,255},{249,173, 62,255},{255,184, 66,255},{226,157, 57,255},{231,160, 58,255},{231,160, 58,255},{226,157, 57,255},{255,184, 66,255},{249,173, 62,255},{162,113, 42,255},{ 20, 15, 10,255},{ 1, 2, 6,255},{ 10, 9, 8,255},{ 9, 8, 8,255},{ 6, 7, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 9, 8, 8,255},{ 12, 10, 9,255},{ 5, 4, 6,255},{ 74, 52, 23,255},{222,154, 56,255},{251,174, 62,255},{218,151, 55,255},{112, 78, 31,255},{ 35, 25, 14,255},{223,155, 56,255},{223,155, 56,255},{ 35, 25, 14,255},{112, 78, 31,255},{218,151, 55,255},{251,174, 62,255},{222,154, 56,255},{ 74, 52, 23,255},{ 5, 4, 6,255},{ 12, 10, 9,255},{ 9, 8, 8,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 8, 7, 8,255},{ 11, 9, 8,255},{ 6, 6, 7,255},{ 17, 13, 10,255},{223,155, 56,255},{255,180, 64,255},{164,114, 43,255},{ 20, 15, 10,255},{ 0, 0, 3,255},{ 16, 13, 10,255},{222,155, 56,255},{222,155, 56,255},{ 16, 13, 10,255},{ 0, 0, 3,255},{ 20, 15, 10,255},{164,114, 43,255},{255,180, 64,255},{223,155, 56,255},{ 17, 13, 10,255},{ 6, 6, 7,255},{ 11, 9, 8,255},{ 8, 7, 8,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 9, 8, 8,255},{ 10, 9, 8,255},{ 3, 3, 6,255},{161,112, 42,255},{251,174, 62,255},{165,115, 43,255},{ 8, 6, 7,255},{ 5, 5, 7,255},{ 10, 9, 8,255},{ 21, 17, 10,255},{226,157, 57,255},{226,157, 57,255},{ 21, 17, 10,255},{ 10, 9, 8,255},{ 5, 5, 7,255},{ 8, 6, 7,255},{165,115, 43,255},{251,174, 62,255},{161,112, 42,255},{ 3, 3, 6,255},{ 10, 9, 8,255},{ 9, 8, 8,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 6, 7, 7,255},{ 8, 7, 7,255},{ 13, 11, 9,255},{ 3, 3, 5,255},{ 70, 50, 22,255},{248,173, 62,255},{218,152, 55,255},{ 17, 13, 10,255},{ 6, 6, 7,255},{ 14, 11, 9,255},{ 6, 6, 7,255},{ 19, 16, 10,255},{212,147, 53,255},{212,147, 53,255},{ 19, 16, 10,255},{ 6, 6, 7,255},{ 14, 11, 9,255},{ 6, 6, 7,255},{ 17, 13, 10,255},{218,152, 55,255},{248,173, 62,255},{ 70, 50, 22,255},{ 3, 3, 5,255},{ 13, 11, 9,255},{ 8, 7, 7,255},{ 6, 7, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 2, 4, 6,255},{ 5, 5, 7,255},{ 10, 9, 8,255},{ 0, 0, 3,255},{130, 91, 35,255},{254,184, 65,255},{112, 79, 31,255},{ 0, 0, 4,255},{ 10, 9, 8,255},{ 6, 6, 7,255},{ 9, 8, 8,255},{ 10, 8, 8,255},{ 20, 16, 11,255},{ 20, 16, 11,255},{ 10, 8, 8,255},{ 9, 8, 8,255},{ 6, 6, 7,255},{ 10, 9, 8,255},{ 0, 0, 4,255},{112, 79, 31,255},{254,184, 65,255},{130, 91, 35,255},{ 0, 0, 3,255},{ 10, 9, 8,255},{ 5, 5, 7,255},{ 2, 4, 6,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 6, 6, 7,255},{ 16, 13, 9,255},{ 21, 16, 11,255},{ 17, 14, 10,255},{ 30, 22, 12,255},{216,150, 54,255},{225,157, 57,255},{ 34, 26, 13,255},{ 17, 13, 9,255},{ 21, 17, 11,255},{ 20, 15, 10,255},{ 10, 8, 8,255},{ 9, 8, 8,255},{ 6, 6, 7,255},{ 6, 6, 7,255},{ 9, 8, 8,255},{ 10, 8, 8,255},{ 20, 15, 10,255},{ 21, 17, 11,255},{ 17, 13, 9,255},{ 34, 26, 13,255},{225,157, 57,255},{216,150, 54,255},{ 30, 22, 12,255},{ 17, 14, 10,255},{ 21, 16, 11,255},{ 16, 13, 9,255},{ 6, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 6, 7, 7,255},{ 2, 4, 6,255},{ 17, 14, 10,255},{212,147, 53,255},{226,157, 57,255},{222,154, 56,255},{224,156, 56,255},{233,162, 58,255},{231,160, 58,255},{223,155, 56,255},{223,154, 56,255},{226,157, 57,255},{212,147, 53,255},{ 21, 16, 11,255},{ 6, 6, 7,255},{ 11, 10, 8,255},{ 11, 10, 8,255},{ 6, 6, 7,255},{ 21, 16, 11,255},{212,147, 53,255},{226,157, 57,255},{223,154, 56,255},{223,155, 56,255},{231,160, 58,255},{233,162, 58,255},{224,156, 56,255},{222,154, 56,255},{226,157, 57,255},{212,147, 53,255},{ 17, 14, 10,255},{ 2, 4, 6,255},{ 6, 7, 7,255}},
|
||||
{{ 6, 7, 7,255},{ 2, 4, 6,255},{ 17, 14, 10,255},{212,147, 53,255},{226,157, 57,255},{222,154, 56,255},{224,156, 56,255},{233,162, 58,255},{231,160, 58,255},{223,155, 56,255},{223,154, 56,255},{226,157, 57,255},{212,147, 53,255},{ 21, 16, 11,255},{ 6, 6, 7,255},{ 11, 10, 8,255},{ 11, 10, 8,255},{ 6, 6, 7,255},{ 21, 16, 11,255},{212,147, 53,255},{226,157, 57,255},{223,154, 56,255},{223,155, 56,255},{231,160, 58,255},{233,162, 58,255},{224,156, 56,255},{222,154, 56,255},{226,157, 57,255},{212,147, 53,255},{ 17, 14, 10,255},{ 2, 4, 6,255},{ 6, 7, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 6, 6, 7,255},{ 16, 13, 9,255},{ 21, 16, 11,255},{ 17, 14, 10,255},{ 30, 22, 12,255},{216,150, 54,255},{225,157, 57,255},{ 34, 26, 13,255},{ 17, 13, 9,255},{ 21, 17, 11,255},{ 20, 15, 10,255},{ 10, 8, 8,255},{ 9, 8, 8,255},{ 6, 6, 7,255},{ 6, 6, 7,255},{ 9, 8, 8,255},{ 10, 8, 8,255},{ 20, 15, 10,255},{ 21, 17, 11,255},{ 17, 13, 9,255},{ 34, 26, 13,255},{225,157, 57,255},{216,150, 54,255},{ 30, 22, 12,255},{ 17, 14, 10,255},{ 21, 16, 11,255},{ 16, 13, 9,255},{ 6, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 2, 4, 6,255},{ 5, 5, 7,255},{ 10, 9, 8,255},{ 0, 0, 3,255},{130, 91, 35,255},{254,184, 65,255},{112, 79, 31,255},{ 0, 0, 4,255},{ 10, 9, 8,255},{ 6, 6, 7,255},{ 9, 8, 8,255},{ 10, 8, 8,255},{ 20, 16, 11,255},{ 20, 16, 11,255},{ 10, 8, 8,255},{ 9, 8, 8,255},{ 6, 6, 7,255},{ 10, 9, 8,255},{ 0, 0, 4,255},{112, 79, 31,255},{254,184, 65,255},{130, 91, 35,255},{ 0, 0, 3,255},{ 10, 9, 8,255},{ 5, 5, 7,255},{ 2, 4, 6,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 6, 7, 7,255},{ 8, 7, 7,255},{ 13, 11, 9,255},{ 3, 3, 5,255},{ 70, 50, 22,255},{248,173, 62,255},{218,152, 55,255},{ 17, 13, 10,255},{ 6, 6, 7,255},{ 14, 11, 9,255},{ 6, 6, 7,255},{ 19, 16, 10,255},{212,147, 53,255},{212,147, 53,255},{ 19, 16, 10,255},{ 6, 6, 7,255},{ 14, 11, 9,255},{ 6, 6, 7,255},{ 17, 13, 10,255},{218,152, 55,255},{248,173, 62,255},{ 70, 50, 22,255},{ 3, 3, 5,255},{ 13, 11, 9,255},{ 8, 7, 7,255},{ 6, 7, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 9, 8, 8,255},{ 10, 9, 8,255},{ 3, 3, 6,255},{161,112, 42,255},{251,174, 62,255},{165,115, 43,255},{ 8, 6, 7,255},{ 5, 5, 7,255},{ 10, 9, 8,255},{ 21, 17, 10,255},{226,157, 57,255},{226,157, 57,255},{ 21, 17, 10,255},{ 10, 9, 8,255},{ 5, 5, 7,255},{ 8, 6, 7,255},{165,115, 43,255},{251,174, 62,255},{161,112, 42,255},{ 3, 3, 6,255},{ 10, 9, 8,255},{ 9, 8, 8,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 8, 7, 8,255},{ 11, 9, 8,255},{ 6, 6, 7,255},{ 17, 13, 10,255},{223,155, 56,255},{255,180, 64,255},{164,114, 43,255},{ 20, 15, 10,255},{ 0, 0, 3,255},{ 16, 13, 10,255},{222,155, 56,255},{222,155, 56,255},{ 16, 13, 10,255},{ 0, 0, 3,255},{ 20, 15, 10,255},{164,114, 43,255},{255,180, 64,255},{223,155, 56,255},{ 17, 13, 10,255},{ 6, 6, 7,255},{ 11, 9, 8,255},{ 8, 7, 8,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 9, 8, 8,255},{ 12, 10, 9,255},{ 5, 4, 6,255},{ 74, 52, 23,255},{222,154, 56,255},{251,174, 62,255},{218,151, 55,255},{112, 78, 31,255},{ 35, 25, 14,255},{223,155, 56,255},{223,155, 56,255},{ 35, 25, 14,255},{112, 78, 31,255},{218,151, 55,255},{251,174, 62,255},{222,154, 56,255},{ 74, 52, 23,255},{ 5, 4, 6,255},{ 12, 10, 9,255},{ 9, 8, 8,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 6, 7, 7,255},{ 9, 8, 8,255},{ 10, 9, 8,255},{ 1, 2, 6,255},{ 20, 15, 10,255},{162,113, 42,255},{249,173, 62,255},{255,184, 66,255},{226,157, 57,255},{231,160, 58,255},{231,160, 58,255},{226,157, 57,255},{255,184, 66,255},{249,173, 62,255},{162,113, 42,255},{ 20, 15, 10,255},{ 1, 2, 6,255},{ 10, 9, 8,255},{ 9, 8, 8,255},{ 6, 7, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 6, 7, 7,255},{ 9, 8, 8,255},{ 12, 10, 9,255},{ 6, 6, 7,255},{ 3, 3, 6,255},{ 70, 50, 22,255},{130, 91, 35,255},{216,150, 54,255},{233,162, 58,255},{233,162, 58,255},{216,150, 54,255},{130, 91, 35,255},{ 70, 50, 22,255},{ 3, 3, 6,255},{ 6, 6, 7,255},{ 12, 10, 9,255},{ 9, 8, 8,255},{ 6, 7, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 6, 7, 7,255},{ 9, 8, 8,255},{ 10, 9, 8,255},{ 10, 9, 8,255},{ 0, 1, 5,255},{ 0, 0, 3,255},{ 29, 22, 12,255},{224,156, 56,255},{224,156, 56,255},{ 29, 22, 12,255},{ 0, 0, 3,255},{ 0, 1, 5,255},{ 10, 9, 8,255},{ 10, 9, 8,255},{ 9, 8, 8,255},{ 6, 7, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 8, 7, 8,255},{ 9, 8, 8,255},{ 13, 11, 9,255},{ 10, 9, 8,255},{ 17, 14, 9,255},{222,154, 56,255},{222,154, 56,255},{ 17, 14, 9,255},{ 10, 9, 8,255},{ 13, 11, 9,255},{ 9, 8, 8,255},{ 8, 7, 8,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 8, 7, 7,255},{ 5, 5, 7,255},{ 21, 17, 11,255},{226,157, 57,255},{226,157, 57,255},{ 21, 17, 11,255},{ 5, 5, 7,255},{ 8, 7, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 7, 7,255},{ 2, 4, 6,255},{ 16, 14, 10,255},{211,147, 53,255},{211,147, 53,255},{ 16, 14, 10,255},{ 2, 4, 6,255},{ 5, 7, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 6, 6, 7,255},{ 17, 14, 10,255},{ 17, 14, 10,255},{ 6, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 2, 4, 6,255},{ 2, 4, 6,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}},
|
||||
{{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 6, 7, 7,255},{ 6, 7, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255},{ 5, 6, 7,255}}
|
||||
};
|
||||
#endif /* GE007_ICON_H */
|
||||
+60
-14
@@ -63,6 +63,8 @@ static int gl_glsl_version = 130;
|
||||
static char gl_glsl_version_str[16] = "130";
|
||||
static GLenum gl_mirror_clamp = GL_MIRROR_CLAMP_TO_EDGE;
|
||||
static bool gl_es = false;
|
||||
extern int gfx_rdp_affine; /* D540: defined in gfx_pc.cpp */
|
||||
extern int gfx_fog_vertex; /* D540: defined in gfx_pc.cpp */
|
||||
static bool gl_core_profile = false;
|
||||
|
||||
static int gfx_opengl_get_max_texture_size() {
|
||||
@@ -149,13 +151,13 @@ static const char* shader_item_to_str(uint32_t item, bool with_alpha, bool only_
|
||||
case SHADER_1:
|
||||
return with_alpha ? "vec4(1.0, 1.0, 1.0, 1.0)" : "vec3(1.0, 1.0, 1.0)";
|
||||
case SHADER_INPUT_1:
|
||||
return with_alpha || !inputs_have_alpha ? "vInput1" : "vInput1.rgb";
|
||||
return with_alpha || !inputs_have_alpha ? "cInput1" : "cInput1.rgb";
|
||||
case SHADER_INPUT_2:
|
||||
return with_alpha || !inputs_have_alpha ? "vInput2" : "vInput2.rgb";
|
||||
return with_alpha || !inputs_have_alpha ? "cInput2" : "cInput2.rgb";
|
||||
case SHADER_INPUT_3:
|
||||
return with_alpha || !inputs_have_alpha ? "vInput3" : "vInput3.rgb";
|
||||
return with_alpha || !inputs_have_alpha ? "cInput3" : "cInput3.rgb";
|
||||
case SHADER_INPUT_4:
|
||||
return with_alpha || !inputs_have_alpha ? "vInput4" : "vInput4.rgb";
|
||||
return with_alpha || !inputs_have_alpha ? "cInput4" : "cInput4.rgb";
|
||||
case SHADER_TEXEL0:
|
||||
return with_alpha ? "texVal0" : "texVal0.rgb";
|
||||
case SHADER_TEXEL0A:
|
||||
@@ -181,13 +183,13 @@ static const char* shader_item_to_str(uint32_t item, bool with_alpha, bool only_
|
||||
case SHADER_1:
|
||||
return "1.0";
|
||||
case SHADER_INPUT_1:
|
||||
return "vInput1.a";
|
||||
return "cInput1.a";
|
||||
case SHADER_INPUT_2:
|
||||
return "vInput2.a";
|
||||
return "cInput2.a";
|
||||
case SHADER_INPUT_3:
|
||||
return "vInput3.a";
|
||||
return "cInput3.a";
|
||||
case SHADER_INPUT_4:
|
||||
return "vInput4.a";
|
||||
return "cInput4.a";
|
||||
case SHADER_TEXEL0:
|
||||
return "texVal0.a";
|
||||
case SHADER_TEXEL0A:
|
||||
@@ -277,9 +279,16 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
|
||||
}
|
||||
}
|
||||
}
|
||||
/* D540: RDP-style screen-affine shade colour (see gfx_opengl_init).
|
||||
* D543: fog is the per-vertex RSP value by default, which the RDP also
|
||||
* interpolates screen-affine (it rides in shade alpha), so it shares the
|
||||
* shade qualifier. GE_FOGPIXEL=1 (D540 per-pixel) needs a perspective
|
||||
* attribute: it carries fog * w, divided by w in the shader. */
|
||||
const char *shadeq = gfx_rdp_affine ? "noperspective " : "";
|
||||
const char *fogq = gfx_fog_vertex ? shadeq : "";
|
||||
if (cc_features.opt_fog) {
|
||||
append_line(vs_buf, &vs_len, "INPUT vec4 aFog;");
|
||||
append_line(vs_buf, &vs_len, "OUTPUT vec4 vFog;");
|
||||
vs_len += sprintf(vs_buf + vs_len, "%sOUTPUT vec4 vFog;\n", fogq);
|
||||
num_floats += 4;
|
||||
}
|
||||
|
||||
@@ -291,7 +300,7 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
|
||||
|
||||
for (int i = 0; i < cc_features.num_inputs; i++) {
|
||||
vs_len += sprintf(vs_buf + vs_len, "INPUT vec%d aInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1);
|
||||
vs_len += sprintf(vs_buf + vs_len, "OUTPUT vec%d vInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1);
|
||||
vs_len += sprintf(vs_buf + vs_len, "%sOUTPUT vec%d vInput%d;\n", shadeq, cc_features.opt_alpha ? 4 : 3, i + 1);
|
||||
num_floats += cc_features.opt_alpha ? 4 : 3;
|
||||
}
|
||||
|
||||
@@ -360,13 +369,13 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
|
||||
}
|
||||
}
|
||||
if (cc_features.opt_fog) {
|
||||
append_line(fs_buf, &fs_len, "INPUT vec4 vFog;");
|
||||
fs_len += sprintf(fs_buf + fs_len, "%sINPUT vec4 vFog;\n", fogq);
|
||||
}
|
||||
if (cc_features.opt_grayscale) {
|
||||
append_line(fs_buf, &fs_len, "INPUT vec4 vGrayscaleColor;");
|
||||
}
|
||||
for (int i = 0; i < cc_features.num_inputs; i++) {
|
||||
fs_len += sprintf(fs_buf + fs_len, "INPUT vec%d vInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1);
|
||||
fs_len += sprintf(fs_buf + fs_len, "%sINPUT vec%d vInput%d;\n", shadeq, cc_features.opt_alpha ? 4 : 3, i + 1);
|
||||
}
|
||||
|
||||
if (cc_features.used_textures[0]) {
|
||||
@@ -441,6 +450,15 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
|
||||
}
|
||||
|
||||
append_line(fs_buf, &fs_len, "void main() {");
|
||||
/* D548: combiner inputs are 8-bit on the N64 (shade, prim, env), so never
|
||||
* above 1.0. Under the D540 noperspective shade, GPU clipping of a big
|
||||
* near-camera triangle can extrapolate them past 1.0; the CC wrap
|
||||
* emulation below then turned walls into black/white/yellow/blue bands
|
||||
* (Aztec, near rocket fire). Clamp to the hardware range first. */
|
||||
for (int i = 0; i < cc_features.num_inputs; i++) {
|
||||
fs_len += sprintf(fs_buf + fs_len, " vec%d cInput%d = clamp(vInput%d, 0.0, 1.0);\n",
|
||||
cc_features.opt_alpha ? 4 : 3, i + 1, i + 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (cc_features.used_textures[i]) {
|
||||
@@ -503,10 +521,22 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
|
||||
append_line(fs_buf, &fs_len, " texel = clamp(texel, 0.0, 1.0);");
|
||||
// TODO discard if alpha is 0?
|
||||
if (cc_features.opt_fog) {
|
||||
/* D543 default: vFog.a is the per-vertex RSP value, used as-is
|
||||
* (N64). GE_FOGPIXEL=1 (D540): vFog.a = fog * w, divided by w here =
|
||||
* exact per-pixel fog from the pixel's own depth. */
|
||||
append_line(fs_buf, &fs_len, gfx_fog_vertex ? " float fogA = clamp(vFog.a, 0.0, 1.0);"
|
||||
: " float fogA = clamp(vFog.a * gl_FragCoord.w, 0.0, 1.0);");
|
||||
/* D540: the D503 snap is OFF by default -- its hard 97% edge swept
|
||||
* across the ground in tile-shaped chunks as the player walked
|
||||
* (Deck playtest; maintainer-confirmed with it off). GE_FOGSNAP=1
|
||||
* restores it. */
|
||||
static int fogSnap = -1;
|
||||
if (fogSnap < 0) { const char *e = getenv("GE_FOGSNAP"); fogSnap = (e && e[0] == '1'); }
|
||||
if (fogSnap) append_line(fs_buf, &fs_len, " if (fogA >= 247.0 / 255.0) fogA = 1.0;");
|
||||
if (cc_features.opt_alpha) {
|
||||
append_line(fs_buf, &fs_len, " texel = vec4(mix(texel.rgb, vFog.rgb, vFog.a), texel.a);");
|
||||
append_line(fs_buf, &fs_len, " texel = vec4(mix(texel.rgb, vFog.rgb, fogA), texel.a);");
|
||||
} else {
|
||||
append_line(fs_buf, &fs_len, " texel = mix(texel, vFog.rgb, vFog.a);");
|
||||
append_line(fs_buf, &fs_len, " texel = mix(texel, vFog.rgb, fogA);");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1103,6 +1133,22 @@ static void gfx_opengl_init(void) {
|
||||
}
|
||||
sysLogPrintf(LOG_NOTE, "GL: using GLSL version %s", gl_glsl_version_str);
|
||||
|
||||
/* D540: the RDP interpolates shade colour affinely in screen space; the
|
||||
* GPU default is perspective-correct, which squeezed the sky's horizon
|
||||
* fade into a thin band. noperspective needs desktop GLSL 1.30+ (not
|
||||
* GLSL ES). GE_SHADEPERSP=1 restores perspective-correct shade (A/B).
|
||||
* Fog (D543): per-vertex RSP fog by default, as the N64. Its old
|
||||
* light/dark cycling at the player's feet came from GPU near-clipping of
|
||||
* behind-camera corners; gfx_sp_tri1 now near-clips on the CPU like the
|
||||
* RSP. GE_FOGPIXEL=1 = the D540 exact per-pixel fog (A/B). */
|
||||
{
|
||||
const char *sp = getenv("GE_SHADEPERSP"), *fp = getenv("GE_FOGPIXEL");
|
||||
gfx_rdp_affine = (!gl_es && gl_glsl_version >= 130 && !(sp && sp[0] == '1')) ? 1 : 0;
|
||||
gfx_fog_vertex = (fp && fp[0] == '1') ? 0 : 1; /* D543: per-vertex RSP fog by default */
|
||||
}
|
||||
sysLogPrintf(LOG_NOTE, "GL: shade %s, fog %s", gfx_rdp_affine ? "affine (RDP)" : "perspective",
|
||||
gfx_fog_vertex ? "per-vertex" : "per-pixel");
|
||||
|
||||
glGenBuffers(1, &opengl_vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, opengl_vbo);
|
||||
|
||||
|
||||
+125
-16
@@ -81,12 +81,19 @@ struct NormalColor {
|
||||
};
|
||||
};
|
||||
|
||||
/* D540: set by the GL backend at init. gfx_rdp_affine: RDP-style
|
||||
* screen-affine shade colour. gfx_fog_vertex: 1 = per-vertex RSP fog
|
||||
* (default, D543), 0 = the D540 exact per-pixel fog (GE_FOGPIXEL=1). */
|
||||
int gfx_rdp_affine = 0;
|
||||
int gfx_fog_vertex = 0;
|
||||
|
||||
struct LoadedVertex {
|
||||
float x, y, z, w;
|
||||
float u, v;
|
||||
struct RGBA color;
|
||||
uint8_t fog;
|
||||
uint8_t clip_rej;
|
||||
float fog_n; /* D540: fog sent to the GPU (0..255): the RSP value (default), or fog * w (GE_FOGPIXEL) */
|
||||
};
|
||||
|
||||
static struct {
|
||||
@@ -2261,19 +2268,27 @@ static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx* verti
|
||||
}
|
||||
|
||||
float winv = 1.0f / w;
|
||||
if (winv < 0.0f) {
|
||||
/* D540: the RSP divides by a negative w as-is: a vertex behind
|
||||
* the camera gets z/w at/beyond the far end of the range (full
|
||||
* fog), continuously. The old override (winv < 0 -> +32767)
|
||||
* flipped the sign, so a corner crossing behind the player
|
||||
* jumped no-fog <-> full-fog and big ground triangles popped
|
||||
* one at a time (Deck playtest). */
|
||||
if (!gfx_fog_vertex && winv < 0.0f) {
|
||||
winv = std::numeric_limits<int16_t>::max();
|
||||
}
|
||||
|
||||
float fog_z = z * winv * rsp.fog_mul + rsp.fog_offset;
|
||||
d->fog = clampf(fog_z, 0.f, 255.f);
|
||||
/* D503: the N64's 16-bit framebuffer hid objects ~97% fogged;
|
||||
* 32-bit output reveals them as silhouettes. Snap to full fog. */
|
||||
if (d->fog >= 247.f) {
|
||||
d->fog = 255.f;
|
||||
}
|
||||
/* D543 default: the per-vertex RSP value (as the N64; D543's CPU
|
||||
* near clip lerps it for clip-created vertices like the RSP).
|
||||
* GE_FOGPIXEL=1 (D540): the GPU gets fog * w (z*mul + w*off,
|
||||
* linear in clip space) and the shader divides by w -- exact
|
||||
* per-pixel fog. The D503 full-fog snap is opt-in (GE_FOGSNAP=1). */
|
||||
d->fog_n = gfx_fog_vertex ? d->fog : d->z * rsp.fog_mul + d->w * rsp.fog_offset;
|
||||
} else {
|
||||
d->fog = rdp.fog_color.a;
|
||||
d->fog_n = gfx_fog_vertex ? d->fog : d->fog * d->w;
|
||||
}
|
||||
|
||||
d->color.a = vcn->a; // can be required for SHADE_ALPHA even if fog is enabled
|
||||
@@ -2858,14 +2873,106 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
|
||||
d526_note_emit(videoGetFrameCount(), v_arr, use_alpha ? 1 : 0, use_modulate ? 1 : 0); // D526 census
|
||||
d75d_note_emit_z(videoGetFrameCount(), v_arr, (used_textures[0] || used_textures[1]) ? 1 : 0, comb->used_textures[0] ? 1 : 0);
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
float z = v_arr[i]->z, w = v_arr[i]->w;
|
||||
/* D543: RSP-style near-plane clipping on the CPU. The GPU clips the
|
||||
* triangle itself against the near plane, but it interpolates the
|
||||
* noperspective shade colour and the fog value in screen space (D548
|
||||
* bands, D540 light/dark cycling, D543 bright lit area at Bond's feet),
|
||||
* so a big ground triangle that crosses the camera plane extrapolates
|
||||
* them. The RSP instead clips in clip space and lerps every vertex
|
||||
* attribute linearly there (t = dA / (dA - dB)); do the same, so GL
|
||||
* receives only fully-in-front triangles and never clips. Skipped for
|
||||
* rects, G_NO_CLIPPING_EXT, and GE_NEARCLIP=0 (A/B). Fog on a vertex
|
||||
* the clip creates is re-derived from its own z/w (as gfx_sp_vertex
|
||||
* does), like the RSP, whose clipper runs new vertices through the same
|
||||
* vertex-finish code: at the near plane that is a constant, so the haze
|
||||
* at the player's feet stays static (D553; maintainer compared against
|
||||
* LLE mupen64plus, cxd4 + angrylion). GE_NEARCLIPFOG=lerp restores the
|
||||
* old interpolation from the parents (whose behind-the-eye corner flips
|
||||
* between 0 and 255 fog as w crosses 0 -> the dark/bright popping) for
|
||||
* A/B. The new vertices live on the stack: rsp.loaded_vertices is
|
||||
* game-visible. */
|
||||
static int nearclip_on = -1, nearclip_fog_recompute = -1;
|
||||
if (nearclip_on < 0) {
|
||||
const char* e = getenv("GE_NEARCLIP");
|
||||
nearclip_on = !(e && e[0] == '0');
|
||||
const char* f = getenv("GE_NEARCLIPFOG");
|
||||
nearclip_fog_recompute = (f && strcmp(f, "lerp") == 0) ? 0 : 1; /* D553: recompute by default */
|
||||
}
|
||||
struct LoadedVertex* emit_v[6] = { v1, v2, v3, NULL, NULL, NULL };
|
||||
int n_out_tris = 1;
|
||||
struct LoadedVertex nc_vtx[2];
|
||||
if (nearclip_on && !is_rect && (rsp.extra_geometry_mode & G_NO_CLIPPING_EXT) == 0) {
|
||||
const float NEAR_EPS = 1e-4f;
|
||||
float d[3];
|
||||
int n_in = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
d[i] = v_arr[i]->z + v_arr[i]->w - NEAR_EPS;
|
||||
if (d[i] > 0.0f) n_in++;
|
||||
}
|
||||
if (n_in == 0) {
|
||||
return; // entirely behind the near plane
|
||||
}
|
||||
if (n_in < 3) {
|
||||
struct LoadedVertex* poly[4];
|
||||
int n_poly = 0, n_new = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int j = (i + 1) % 3;
|
||||
struct LoadedVertex* a = v_arr[i];
|
||||
struct LoadedVertex* b = v_arr[j];
|
||||
if (d[i] > 0.0f) poly[n_poly++] = a;
|
||||
if ((d[i] > 0.0f) != (d[j] > 0.0f)) {
|
||||
float t = d[i] / (d[i] - d[j]);
|
||||
struct LoadedVertex* o = &nc_vtx[n_new++];
|
||||
o->x = a->x + (b->x - a->x) * t;
|
||||
o->y = a->y + (b->y - a->y) * t;
|
||||
o->z = a->z + (b->z - a->z) * t;
|
||||
o->w = a->w + (b->w - a->w) * t;
|
||||
o->u = a->u + (b->u - a->u) * t;
|
||||
o->v = a->v + (b->v - a->v) * t;
|
||||
o->color.r = (uint8_t)clampf(floorf(a->color.r + (b->color.r - a->color.r) * t + 0.5f), 0.f, 255.f);
|
||||
o->color.g = (uint8_t)clampf(floorf(a->color.g + (b->color.g - a->color.g) * t + 0.5f), 0.f, 255.f);
|
||||
o->color.b = (uint8_t)clampf(floorf(a->color.b + (b->color.b - a->color.b) * t + 0.5f), 0.f, 255.f);
|
||||
o->color.a = (uint8_t)clampf(floorf(a->color.a + (b->color.a - a->color.a) * t + 0.5f), 0.f, 255.f);
|
||||
o->clip_rej = 0;
|
||||
o->fog_n = a->fog_n + (b->fog_n - a->fog_n) * t;
|
||||
if (nearclip_fog_recompute) {
|
||||
if (rsp.geometry_mode & G_FOG) {
|
||||
float fw = o->w;
|
||||
if (fabsf(fw) < 0.001f) fw = 0.001f;
|
||||
float winv = 1.0f / fw;
|
||||
if (!gfx_fog_vertex && winv < 0.0f) winv = std::numeric_limits<int16_t>::max();
|
||||
o->fog = clampf(o->z * winv * rsp.fog_mul + rsp.fog_offset, 0.f, 255.f);
|
||||
} else {
|
||||
o->fog = a->fog;
|
||||
}
|
||||
} else {
|
||||
o->fog = (uint8_t)clampf(floorf(a->fog + ((float)b->fog - (float)a->fog) * t + 0.5f), 0.f, 255.f);
|
||||
}
|
||||
if (gfx_fog_vertex) o->fog_n = o->fog; // as gfx_sp_vertex
|
||||
poly[n_poly++] = o;
|
||||
}
|
||||
}
|
||||
// n_poly is 3 or 4 here (one or two vertices inside)
|
||||
emit_v[0] = poly[0]; emit_v[1] = poly[1]; emit_v[2] = poly[2];
|
||||
if (n_poly == 4) {
|
||||
emit_v[3] = poly[0]; emit_v[4] = poly[2]; emit_v[5] = poly[3];
|
||||
n_out_tris = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
const int n_emit_verts = n_out_tris * 3;
|
||||
if (buf_vbo_num_tris + n_out_tris > MAX_BUFFERED) {
|
||||
gfx_flush(); // buf_vbo holds MAX_BUFFERED triangles; make room for 1-2
|
||||
}
|
||||
for (int i = 0; i < n_emit_verts; i++) {
|
||||
struct LoadedVertex* vi = emit_v[i];
|
||||
float z = vi->z, w = vi->w;
|
||||
if (clip_parameters.z_is_from_0_to_1) {
|
||||
z = (z + w) / 2.0f;
|
||||
}
|
||||
|
||||
buf_vbo[buf_vbo_len++] = v_arr[i]->x;
|
||||
buf_vbo[buf_vbo_len++] = clip_parameters.invert_y ? -v_arr[i]->y : v_arr[i]->y;
|
||||
buf_vbo[buf_vbo_len++] = vi->x;
|
||||
buf_vbo[buf_vbo_len++] = clip_parameters.invert_y ? -vi->y : vi->y;
|
||||
buf_vbo[buf_vbo_len++] = z;
|
||||
buf_vbo[buf_vbo_len++] = w;
|
||||
|
||||
@@ -2877,8 +2984,8 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
|
||||
// TODO: fix this; for now just ignore smaller mips
|
||||
const uint32_t tile = gfx_lod_tile_offset(t);
|
||||
|
||||
float u = v_arr[i]->u / 32.0f;
|
||||
float v = v_arr[i]->v / 32.0f;
|
||||
float u = vi->u / 32.0f;
|
||||
float v = vi->v / 32.0f;
|
||||
|
||||
int shifts = rdp.texture_tile[rdp.first_tile_index + tile].shifts;
|
||||
int shiftt = rdp.texture_tile[rdp.first_tile_index + tile].shiftt;
|
||||
@@ -2955,7 +3062,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
|
||||
buf_vbo[buf_vbo_len++] = rdp.fog_color.r / 255.0f;
|
||||
buf_vbo[buf_vbo_len++] = rdp.fog_color.g / 255.0f;
|
||||
buf_vbo[buf_vbo_len++] = rdp.fog_color.b / 255.0f;
|
||||
buf_vbo[buf_vbo_len++] = v_arr[i]->fog / 255.0f; // fog factor
|
||||
buf_vbo[buf_vbo_len++] = vi->fog_n / 255.0f; // D540: see LoadedVertex.fog_n
|
||||
}
|
||||
|
||||
if (use_grayscale) {
|
||||
@@ -2976,10 +3083,10 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
|
||||
color = &rdp.prim_color;
|
||||
break;
|
||||
case G_CCMUX_SHADE:
|
||||
color = &v_arr[i]->color;
|
||||
color = &vi->color;
|
||||
break;
|
||||
case G_CCMUX_SHADE_ALPHA:
|
||||
tmp.r = tmp.g = tmp.b = v_arr[i]->color.a;
|
||||
tmp.r = tmp.g = tmp.b = vi->color.a;
|
||||
color = &tmp;
|
||||
break;
|
||||
case G_CCMUX_ENVIRONMENT:
|
||||
@@ -3032,7 +3139,8 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
|
||||
}
|
||||
}
|
||||
|
||||
if (++buf_vbo_num_tris == MAX_BUFFERED) {
|
||||
buf_vbo_num_tris += n_out_tris;
|
||||
if (buf_vbo_num_tris >= MAX_BUFFERED) {
|
||||
gfx_flush();
|
||||
}
|
||||
}
|
||||
@@ -3943,6 +4051,7 @@ static void gfx_sp_vertex_float(size_t n_vertices, size_t dest_index, const Port
|
||||
d->z = z;
|
||||
d->w = w;
|
||||
d->fog = rdp.fog_color.a;
|
||||
d->fog_n = gfx_fog_vertex ? d->fog : d->fog * w;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "gfx_window_manager_api.h"
|
||||
#include "gfx_screen_config.h"
|
||||
#include "input.h"
|
||||
#include "ge007_icon.h"
|
||||
|
||||
extern "C" void videoRequestQuit(const char *why); // port/src/video.c (D344)
|
||||
extern "C" void inputRequestRescan(void); // port/src/input.c (D450)
|
||||
@@ -228,6 +229,32 @@ static void gfx_sdl_init(const struct GfxWindowInitSettings *set) {
|
||||
SDL_GL_MakeCurrent(wnd, ctx);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
|
||||
// D550: window / taskbar icon = the project mark (generated by
|
||||
// tools_pc/make_icon.py into ge007_icon.h; R,G,B,A byte order per
|
||||
// pixel, portable -- packed here against the surface's masks).
|
||||
{
|
||||
const Uint32 rmask = 0x000000ff, gmask = 0x0000ff00,
|
||||
bmask = 0x00ff0000, amask = 0xff000000;
|
||||
SDL_Surface *icon = SDL_CreateRGBSurface(
|
||||
0, GE007_ICON_SIZE, GE007_ICON_SIZE, 32, rmask, gmask, bmask, amask);
|
||||
if (icon) {
|
||||
for (int y = 0; y < GE007_ICON_SIZE; ++y) {
|
||||
for (int x = 0; x < GE007_ICON_SIZE; ++x) {
|
||||
const uint8_t *p = ge007_icon_rgba[y][x];
|
||||
// Shift each channel into its mask's byte lane before
|
||||
// the & (an unshifted p[1] & 0x0000ff00 is always 0):
|
||||
const Uint32 v = ((Uint32)p[0] & rmask) | ((Uint32)(p[1] << 8) & gmask) |
|
||||
((Uint32)(p[2] << 16) & bmask) | ((Uint32)(p[3] << 24) & amask);
|
||||
*(Uint32 *)((Uint8 *)icon->pixels + y * icon->pitch + x * 4) = v;
|
||||
}
|
||||
}
|
||||
SDL_SetWindowIcon(wnd, icon);
|
||||
SDL_FreeSurface(icon);
|
||||
} else {
|
||||
sysLogPrintf(LOG_NOTE, "SDL: could not create the window icon surface: %s", SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
SDL_ShowWindow(wnd);
|
||||
|
||||
qpc_freq = SDL_GetPerformanceFrequency();
|
||||
|
||||
@@ -59,6 +59,8 @@ int inputReleaseCapture(void);
|
||||
/* F10 options overlay: force the OS cursor free + visible while the overlay
|
||||
* owns the mouse. Safe to call every poll. */
|
||||
void inputSuspendForOverlay(void);
|
||||
int inputFrontEndCursorUiFrac(double *fx, double *fy, double *xscale); /* D555 */
|
||||
int inputCrosshairCursorOn(void); /* D555: Input.CrosshairCursor (overlay draws the game crosshair as the pointer) */
|
||||
|
||||
/* Raw gamepad state for controller idx (the F10 options overlay's gamepad
|
||||
* navigation). Unaffected by the overlay's pad-swallow, which happens at our
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef PORT_UPDATECHECK_H
|
||||
#define PORT_UPDATECHECK_H
|
||||
|
||||
/*
|
||||
* Opt-in "check for updates" (Game.CheckUpdates, default OFF).
|
||||
*
|
||||
* updateCheckStart() does nothing at all unless the ini value is 1 -- no
|
||||
* thread, no DNS, no library init. When enabled it spawns one detached
|
||||
* background thread that makes a single HTTPS GET to GitHub's releases API.
|
||||
*/
|
||||
void updateCheckStart(void); /* call once, after configLoad() */
|
||||
const char *updateCheckAvailable(void); /* newer tag (e.g. "vX.Y.Z") or NULL */
|
||||
void updateCheckOpenReleases(void); /* SDL_OpenURL on the releases page */
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,7 @@
|
||||
/* Generated by CMake from versioninfo.h.in — do not edit. */
|
||||
|
||||
#define GE007_VERSION_HASH "@VERSION_HASH@"
|
||||
#define GE007_VERSION "@GE007_VERSION@"
|
||||
#define GE007_VERSION_CODENAME "@VERSION_CODENAME@"
|
||||
#define GE007_ORIGIN_URL "@ORIGIN_URL@"
|
||||
#define GE007_TARGET_PLATFORM "@TARGET_PLATFORM@"
|
||||
|
||||
+72
-7
@@ -122,7 +122,7 @@ extern void gunRequestHandWeaponChange(enum GUNHAND hand, s32 nextWeapon, s32 cy
|
||||
#define GE_CONT_F 0x0001 /* C-right */
|
||||
|
||||
#define MAX_PADS 4
|
||||
#define STICK_DEADZONE 7000
|
||||
#define STICK_DEADZONE 7500 /* 25 %; D546 (was 7000 = 23 %) */
|
||||
#define STICK_MAX 80
|
||||
|
||||
/* Front-end pointer mode: GE's menu cursor ("crosshair") is stick-driven
|
||||
@@ -791,6 +791,14 @@ static void mouseRequestCursor(int show)
|
||||
SDL_AtomicUnlock(&s_mouseReqLock);
|
||||
}
|
||||
|
||||
/* D555 (reworked 2026-10-06): Input.CrosshairCursor -- while the F10 overlay /
|
||||
* PC Options is open the OS cursor is HIDDEN and optionsoverlay.c draws the
|
||||
* game's own crosshair sprite at the mouse position, last in its display list
|
||||
* (so it is above the card). Off: the OS arrow over the overlay, as before. */
|
||||
static int cfgCrosshairCursor = 1;
|
||||
int inputCrosshairCursorOn(void) { return cfgCrosshairCursor; }
|
||||
static int s_cursorShown = -1; /* host thread only: last SDL_ShowCursor state applied */
|
||||
|
||||
/* Host thread only (videoPumpEvents, inputInit). */
|
||||
void inputApplyMouseRequests(void)
|
||||
{
|
||||
@@ -806,9 +814,15 @@ void inputApplyMouseRequests(void)
|
||||
SDL_GetRelativeMouseState(NULL, NULL); /* drain the accumulated jump */
|
||||
}
|
||||
}
|
||||
if (cur >= 0 && cur != s_appCursor) {
|
||||
s_appCursor = cur;
|
||||
SDL_ShowCursor(cur ? SDL_ENABLE : SDL_DISABLE);
|
||||
if (cur >= 0) s_appCursor = cur;
|
||||
if (s_appCursor >= 0) {
|
||||
/* D555: with the crosshair pointer on, the overlay draws its own
|
||||
* pointer, so the OS cursor stays hidden while it is open. */
|
||||
const int show = s_appCursor == 1 && !(cfgCrosshairCursor && optionsOverlayIsOpen());
|
||||
if (show != s_cursorShown) {
|
||||
s_cursorShown = show;
|
||||
SDL_ShowCursor(show ? SDL_ENABLE : SDL_DISABLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1029,6 +1043,7 @@ static int padSouthpaw = 0; /* 1 = swap fire (G) / grenade (R)
|
||||
static int padLookSmooth = 0; /* right-stick look low-pass strength, 0-10 (0 = off, 10 = max) */
|
||||
static int padTriggerPct = 25; /* trigger press point, % of travel (~30*256) */
|
||||
static int padLookInvertY = 0; /* 1 = invert right-stick (look) Y */
|
||||
static int inputDefaultsRev = 0; /* D546: 1 once the deadzone default migration ran */
|
||||
/* Per-pad look-smoothing EMA state (Wave A item 5), one X/Y pair per pad. Reset
|
||||
* on hot-unplug (inputRescanPads) so a smoothed value can't stick after the
|
||||
* stick snaps back; the low-pass otherwise recentres it toward 0 on its own. */
|
||||
@@ -1516,7 +1531,7 @@ static void inputMigrateBinds(void); /* D380; after configLoad */
|
||||
static void inputBindingProbe(void); /* D383/D384; opt-in, restores ini state */
|
||||
|
||||
static int bindsVersion = 0; /* D380/D386: versioned migration of effective ini binds */
|
||||
static int crouchMode = 0; /* 0 = hold, 1 = toggle (latched crouch input) */
|
||||
static int crouchMode = 1; /* 0 = hold, 1 = toggle (latched crouch input); D556: toggle by default (existing ini values kept) */
|
||||
|
||||
static void padMapSelfTest(void);
|
||||
int inputInit(void)
|
||||
@@ -1553,6 +1568,14 @@ int inputInit(void)
|
||||
* Runs before any configSave, so the saved L/R values are always valid. */
|
||||
if (padDeadzoneL == PAD_DZ_UNSET) padDeadzoneL = padDeadzone;
|
||||
if (padDeadzoneR == PAD_DZ_UNSET) padDeadzoneR = padDeadzone;
|
||||
/* D546: one-time move of the untouched old deadzone default (7000 = 23 %)
|
||||
* to the standardized 25 % (7500); a value the player set is kept. */
|
||||
if (inputDefaultsRev < 1) {
|
||||
if (padDeadzone == 7000) padDeadzone = STICK_DEADZONE;
|
||||
if (padDeadzoneL == 7000) padDeadzoneL = STICK_DEADZONE;
|
||||
if (padDeadzoneR == 7000) padDeadzoneR = STICK_DEADZONE;
|
||||
inputDefaultsRev = 1;
|
||||
}
|
||||
if (getenv("GE_BINDPROBE")) inputBindingProbe();
|
||||
|
||||
sysLogPrintf(LOG_INFO, "input: PC bindings, crouch mode %s",
|
||||
@@ -2172,8 +2195,9 @@ static unsigned inputComputePadSlot(int idx, signed char *stick_x, signed char *
|
||||
}
|
||||
optionsOverlayHandleInput();
|
||||
if (mouseEnabled && current_menu != GE_MENU_RUN_STAGE && current_menu != GE_MENU_INVALID) {
|
||||
overlayFrontEndCrosshair(); /* D519: crosshair stays the pointer over the overlay */
|
||||
mouseRequestCursor(0); /* ...so hide the OS cursor (one pointer, not two) */
|
||||
overlayFrontEndCrosshair(); /* D519: the game cursor tracks the mouse (D555: the overlay pointer is drawn exactly over it) */
|
||||
/* D544: the OS cursor stays visible (inputSuspendForOverlay) -- the crosshair
|
||||
* draws under the overlay box, so it alone could not point at F10 rows. */
|
||||
}
|
||||
if (stick_x) *stick_x = 0;
|
||||
if (stick_y) *stick_y = 0;
|
||||
@@ -2897,6 +2921,23 @@ static unsigned inputComputePadSlot(int idx, signed char *stick_x, signed char *
|
||||
}
|
||||
}
|
||||
|
||||
/* D541: front-end release hold-off. One physical press can reach the menu
|
||||
* through two OR'd sources a few ms apart -- on a Steam Deck in Desktop
|
||||
* Mode, Steam's desktop layout types Enter for A (Escape for B) while SDL
|
||||
* also reads the raw pad -- and a gap between them is a second edge, so
|
||||
* one A press advanced two screens. A menu button only counts as released
|
||||
* after ~70 ms up; press edges are untouched. Menus only. */
|
||||
if (idx >= 0 && idx < MAX_PADS &&
|
||||
current_menu != GE_MENU_RUN_STAGE && current_menu != GE_MENU_INVALID) {
|
||||
static Uint32 s_menuBtnDownAt[MAX_PADS][3];
|
||||
static const u16 kHoldBits[3] = { GE_CONT_A, GE_CONT_B, GE_CONT_START };
|
||||
Uint32 now = SDL_GetTicks();
|
||||
for (int k = 0; k < 3; k++) {
|
||||
if (button & kHoldBits[k]) s_menuBtnDownAt[idx][k] = now;
|
||||
else if (s_menuBtnDownAt[idx][k] && now - s_menuBtnDownAt[idx][k] < 70) button |= kHoldBits[k];
|
||||
}
|
||||
}
|
||||
|
||||
if (idx == 0 && scriptIsActive(0)) {
|
||||
/* D263: apply BEFORE the stick is written out -- scripted stick
|
||||
* tokens (SUP/SDOWN/SLEFT/SRIGHT) used to be discarded. */
|
||||
@@ -3134,6 +3175,25 @@ static void overlayFrontEndCrosshair(void)
|
||||
cursor_v_pos = (float)(v < loV ? loV : v > hiV ? hiV : v);
|
||||
}
|
||||
|
||||
/* D555 rework: INVERSE of overlayFrontEndCrosshair's mapping. Where the game's
|
||||
* front-end cursor (cursor_h_pos/cursor_v_pos, which frontDrawCursor draws at)
|
||||
* lands, as fractions of the UI screen rect (the rect the overlay's full-canvas
|
||||
* DL maps to), plus the x scale of the front-end canvas inside it (D335
|
||||
* native-widescreen pillarbox: vis, else 1). 0 = not available. */
|
||||
int inputFrontEndCursorUiFrac(double *fx, double *fy, double *xscale)
|
||||
{
|
||||
double sw = getPlayer_c_screenwidth(), sh = getPlayer_c_screenheight();
|
||||
double sl = getPlayer_c_screenleft(), st = getPlayer_c_screentop();
|
||||
if (!(sw > 200.0 && sw < 2000.0 && sh > 150.0 && sh < 2000.0)) return 0;
|
||||
double x = ((double)cursor_h_pos - sl) / sw, y = ((double)cursor_v_pos - st) / sh;
|
||||
double vis = 1.0;
|
||||
extern f32 portNativeAspect(void);
|
||||
f32 na = portNativeAspect();
|
||||
if (na > (4.0f / 3.0f)) { vis = (4.0 / 3.0) / (double)na; x = x * vis + (1.0 - vis) * 0.5; }
|
||||
*fx = x; *fy = y; *xscale = vis;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void inputSuspendForOverlay(void)
|
||||
{
|
||||
if (mouseGrabbed) {
|
||||
@@ -3141,6 +3201,9 @@ void inputSuspendForOverlay(void)
|
||||
mouseRequestRelative(0);
|
||||
mouseDX = mouseDY = 0.0;
|
||||
}
|
||||
/* D544: the OS cursor is always shown while the overlay is open, so the
|
||||
* pointer draws OVER the menu (maintainer). The front-end path no longer
|
||||
* hides it, which also removes the show/hide race that flickered. */
|
||||
mouseRequestCursor(1);
|
||||
}
|
||||
|
||||
@@ -3834,6 +3897,7 @@ PD_CONSTRUCTOR static void inputConfigInit(void)
|
||||
}
|
||||
configRegisterInt("Input.MPMode", &mpInputMode, 0, 2); /* D416: 0 Auto, 1 PadsOnly, 2 KbmP1 */
|
||||
configRegisterInt("Input.MouseEnabled", &mouseEnabled, 0, 1);
|
||||
configRegisterInt("Input.CrosshairCursor", &cfgCrosshairCursor, 0, 1); /* D555 */
|
||||
configRegisterInt("Input.MouseAimSpeed", &mouseAimSpeed, 1, 500);
|
||||
configRegisterInt("Input.PdMouseAim", &pdMouseAim, 0, 1); /* legacy -> AimStyle 1 */
|
||||
configRegisterInt("Input.AimStyle", &aimStyleLegacy, 0, 2); /* D333 legacy -> AimMode */
|
||||
@@ -3876,6 +3940,7 @@ PD_CONSTRUCTOR static void inputConfigInit(void)
|
||||
configRegisterInt("Input.PadDeadzone", &padDeadzone, 0, 30000); /* legacy; Wave A migrates to L/R */
|
||||
configRegisterInt("Input.PadDeadzoneL", &padDeadzoneL, 0, 30000); /* Wave A: left (movement) stick */
|
||||
configRegisterInt("Input.PadDeadzoneR", &padDeadzoneR, 0, 30000); /* Wave A: right (look) stick */
|
||||
configRegisterInt("Input.DefaultsRev", &inputDefaultsRev, 0, 1); /* D546 */
|
||||
configRegisterInt("Input.PadLookSensX", &padLookSensX, 25, 200); /* Wave A: look horizontal sensitivity (%) */
|
||||
configRegisterInt("Input.PadLookSensY", &padLookSensY, 25, 200); /* Wave A: look vertical (pitch) sensitivity (%) */
|
||||
configRegisterInt("Input.PadSouthpaw", &padSouthpaw, 0, 1); /* Wave A: swap fire/grenade triggers */
|
||||
|
||||
+30
-13
@@ -24,6 +24,7 @@
|
||||
#include "platform.h"
|
||||
#include "system.h"
|
||||
#include "config.h"
|
||||
#include "updatecheck.h"
|
||||
#include "fs.h"
|
||||
#include "romdata.h"
|
||||
#include "dram.h"
|
||||
@@ -95,6 +96,33 @@ static void portAtExit(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* D257/D442: Game.AllUnlocked applies live. Called at boot and on every F10
|
||||
* row change. On seeds the game's own RAM unlock flags (mission select offers
|
||||
* every solo level at every difficulty plus 007 mode); Off clears only the
|
||||
* flags this setting seeded, so GE_UNLOCK_ALL / debug-menu unlocks survive.
|
||||
* Port-layer memory writes only (AGENTS rule 2); the cheat menu and COMPLETED
|
||||
* checks come from D442's query-time hooks in file2.c, which read
|
||||
* portAllUnlocked live. The debug flags exist only with LEFTOVERDEBUG (not
|
||||
* PAL); the file2.c hooks cover All unlocked everywhere. */
|
||||
void portAllUnlockedApply(void)
|
||||
{
|
||||
#if defined(LEFTOVERDEBUG)
|
||||
extern s32 portAllUnlocked; /* port/src/video.c */
|
||||
extern s32 debug_enable_all_levels_flag; /* src/game/debugmenu_handler.c */
|
||||
extern s32 debug_007_unlock_flag; /* ditto */
|
||||
static int s_seededLevels, s_seeded007;
|
||||
if (portAllUnlocked) {
|
||||
if (!debug_enable_all_levels_flag) { debug_enable_all_levels_flag = 1; s_seededLevels = 1; }
|
||||
if (!debug_007_unlock_flag) { debug_007_unlock_flag = 1; s_seeded007 = 1; }
|
||||
} else {
|
||||
if (s_seededLevels) { debug_enable_all_levels_flag = 0; s_seededLevels = 0; }
|
||||
if (s_seeded007) { debug_007_unlock_flag = 0; s_seeded007 = 0; }
|
||||
}
|
||||
sysLogPrintf(LOG_INFO, "all-unlocked: Game.AllUnlocked=%d (levels flag %d, 007 flag %d)",
|
||||
(int)portAllUnlocked, (int)debug_enable_all_levels_flag, (int)debug_007_unlock_flag);
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
sysSetArgs(argc, argv);
|
||||
@@ -134,6 +162,7 @@ int main(int argc, char **argv)
|
||||
* skips it for good. */
|
||||
configLoad();
|
||||
videoApplySteamDeckPreset();
|
||||
updateCheckStart(); /* opt-in (Game.CheckUpdates, default off); no-op otherwise */
|
||||
atexit(portAtExit); /* persist config + window geometry on clean exit */
|
||||
|
||||
/* 1a. D257: Game.AllUnlocked (default OFF; F10 'All unlocked' enables)
|
||||
@@ -151,19 +180,7 @@ int main(int argc, char **argv)
|
||||
* COMPLETED check come from the query-time hooks in file2.c
|
||||
* (fileGetIsCheatUnlocked, fileIsStageUnlockedAtDifficulty); the
|
||||
* save is never patched. */
|
||||
#if defined(LEFTOVERDEBUG) /* the debug unlock flags only exist with LEFTOVERDEBUG (not PAL); D442's query-time hooks cover All unlocked everywhere */
|
||||
{
|
||||
extern s32 portAllUnlocked; /* port/src/video.c */
|
||||
extern s32 debug_enable_all_levels_flag; /* src/game/debugmenu_handler.c */
|
||||
extern s32 debug_007_unlock_flag; /* ditto */
|
||||
if (portAllUnlocked) {
|
||||
debug_enable_all_levels_flag = 1;
|
||||
debug_007_unlock_flag = 1;
|
||||
sysLogPrintf(LOG_INFO, "all-unlocked: RAM unlock flags seeded "
|
||||
"(Game.AllUnlocked=1)");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
portAllUnlockedApply();
|
||||
|
||||
/* 2. Load the ROM and map segments. */
|
||||
if (romdataInit() != 0) {
|
||||
|
||||
+539
-221
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Opt-in update check. See port/include/updatecheck.h.
|
||||
*
|
||||
* Off by default (Game.CheckUpdates = 0): the ONLY entry point that can
|
||||
* reach the network, updateCheckStart(), returns before creating a thread
|
||||
* when the setting is 0. When on: one HTTPS GET per launch, on a detached
|
||||
* thread, ~5 s timeout, silent on any failure (one log line). Windows uses
|
||||
* WinHTTP, LoadLibrary'd at runtime (never linked); elsewhere `curl` is run
|
||||
* through popen() (skipped if missing).
|
||||
* No telemetry: the request carries only a fixed User-Agent.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "platform.h"
|
||||
#include "system.h"
|
||||
#include "config.h"
|
||||
#include "versioninfo.h"
|
||||
#include "updatecheck.h"
|
||||
|
||||
#define UC_HOST "api.github.com"
|
||||
/* /releases/latest deliberately excludes drafts and pre-releases (maintainer
|
||||
* decision): a pre-release never prompts anyone to update. */
|
||||
#define UC_PATH "/repos/jkdansereau/goldeneye-pc-port/releases/latest"
|
||||
#define UC_URL "https://" UC_HOST UC_PATH
|
||||
#define UC_RELEASES "https://github.com/jkdansereau/goldeneye-pc-port/releases"
|
||||
#define UC_UA "ge007-pc-port"
|
||||
#define UC_BODY_MAX (64 * 1024)
|
||||
|
||||
static int cfgCheckUpdates = 0; /* Game.CheckUpdates, default OFF */
|
||||
static SDL_atomic_t s_ready; /* 1 once s_tag is published */
|
||||
static char s_tag[32];
|
||||
|
||||
PD_CONSTRUCTOR static void updateCheckConfigInit(void)
|
||||
{
|
||||
configRegisterInt("Game.CheckUpdates", &cfgCheckUpdates, 0, 1);
|
||||
}
|
||||
|
||||
/* Parse "vMAJOR.MINOR.PATCH" (missing parts = 0, suffix ignored). */
|
||||
static int parseVer(const char *s, int v[3])
|
||||
{
|
||||
v[0] = v[1] = v[2] = 0;
|
||||
if (*s == 'v' || *s == 'V') s++;
|
||||
if (*s < '0' || *s > '9') return 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (*s < '0' || *s > '9') break;
|
||||
v[i] = (int)strtol(s, (char **)&s, 10);
|
||||
if (*s != '.') break;
|
||||
s++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int versionNewer(const char *tag, const char *running)
|
||||
{
|
||||
int a[3], b[3];
|
||||
if (!parseVer(tag, a) || !parseVer(running, b)) return 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (a[i] != b[i]) return a[i] > b[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Minimal scan for "tag_name": "<value>". */
|
||||
static int extractTag(const char *body, char *out, size_t n)
|
||||
{
|
||||
const char *p = strstr(body, "\"tag_name\"");
|
||||
if (!p) return 0;
|
||||
p += 10;
|
||||
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p++;
|
||||
if (*p != ':') return 0;
|
||||
p++;
|
||||
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p++;
|
||||
if (*p != '"') return 0;
|
||||
p++;
|
||||
size_t i = 0;
|
||||
/* Tag characters only (no control/ANSI bytes reach the log). */
|
||||
while (*p && *p != '"' && i + 1 < n) {
|
||||
char c = *p++;
|
||||
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
||||
c == '.' || c == '_' || c == '-')) return 0;
|
||||
out[i++] = c;
|
||||
}
|
||||
if (*p != '"' || i == 0) return 0;
|
||||
out[i] = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
/* winhttp.dll is loaded at runtime, only here (after the Game.CheckUpdates
|
||||
* gate), so the exe's import table never lists it and nothing networking-
|
||||
* related is mapped into the process while the setting is off. */
|
||||
typedef LPVOID HINT_;
|
||||
typedef HINT_ (WINAPI *PFN_Open)(LPCWSTR, DWORD, LPCWSTR, LPCWSTR, DWORD);
|
||||
typedef int (WINAPI *PFN_SetTimeouts)(HINT_, int, int, int, int);
|
||||
typedef HINT_ (WINAPI *PFN_Connect)(HINT_, LPCWSTR, WORD, DWORD);
|
||||
typedef HINT_ (WINAPI *PFN_OpenRequest)(HINT_, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR *, DWORD);
|
||||
typedef int (WINAPI *PFN_Send)(HINT_, LPCWSTR, DWORD, LPVOID, DWORD, DWORD, DWORD_PTR);
|
||||
typedef int (WINAPI *PFN_Recv)(HINT_, LPVOID);
|
||||
typedef int (WINAPI *PFN_Query)(HINT_, DWORD, LPCWSTR, LPVOID, LPDWORD, LPDWORD);
|
||||
typedef int (WINAPI *PFN_Read)(HINT_, LPVOID, DWORD, LPDWORD);
|
||||
typedef int (WINAPI *PFN_Close)(HINT_);
|
||||
#define UC_WH_ACCESS_DEFAULT_PROXY 0
|
||||
#define UC_WH_FLAG_SECURE 0x00800000
|
||||
#define UC_WH_QUERY_STATUS_CODE 19
|
||||
#define UC_WH_QUERY_FLAG_NUMBER 0x20000000
|
||||
#endif
|
||||
|
||||
/* Fetch the response body into buf (NUL-terminated). Returns 1 on success. */
|
||||
static int fetchLatest(char *buf, size_t cap)
|
||||
{
|
||||
size_t len = 0;
|
||||
#ifdef _WIN32
|
||||
int ok = 0;
|
||||
/* System32 only: never a winhttp.dll planted next to the exe (D551 review). */
|
||||
#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32
|
||||
#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
|
||||
#endif
|
||||
HMODULE dll = LoadLibraryExW(L"winhttp.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
if (!dll) return 0;
|
||||
PFN_Open pOpen = (PFN_Open)(void *)GetProcAddress(dll, "WinHttpOpen");
|
||||
PFN_SetTimeouts pTimeout = (PFN_SetTimeouts)(void *)GetProcAddress(dll, "WinHttpSetTimeouts");
|
||||
PFN_Connect pConnect = (PFN_Connect)(void *)GetProcAddress(dll, "WinHttpConnect");
|
||||
PFN_OpenRequest pOpenReq = (PFN_OpenRequest)(void *)GetProcAddress(dll, "WinHttpOpenRequest");
|
||||
PFN_Send pSend = (PFN_Send)(void *)GetProcAddress(dll, "WinHttpSendRequest");
|
||||
PFN_Recv pRecv = (PFN_Recv)(void *)GetProcAddress(dll, "WinHttpReceiveResponse");
|
||||
PFN_Query pQuery = (PFN_Query)(void *)GetProcAddress(dll, "WinHttpQueryHeaders");
|
||||
PFN_Read pRead = (PFN_Read)(void *)GetProcAddress(dll, "WinHttpReadData");
|
||||
PFN_Close pClose = (PFN_Close)(void *)GetProcAddress(dll, "WinHttpCloseHandle");
|
||||
if (!pOpen || !pTimeout || !pConnect || !pOpenReq || !pSend || !pRecv ||
|
||||
!pQuery || !pRead || !pClose) {
|
||||
FreeLibrary(dll);
|
||||
return 0;
|
||||
}
|
||||
HINT_ ses = pOpen(L"" UC_UA, UC_WH_ACCESS_DEFAULT_PROXY, NULL, NULL, 0);
|
||||
if (!ses) { FreeLibrary(dll); return 0; }
|
||||
/* resolve, connect, send, receive: 5 s each */
|
||||
pTimeout(ses, 5000, 5000, 5000, 5000);
|
||||
HINT_ con = pConnect(ses, L"" UC_HOST, 443, 0);
|
||||
HINT_ req = con ? pOpenReq(con, L"GET", L"" UC_PATH, NULL, NULL, NULL,
|
||||
UC_WH_FLAG_SECURE) : NULL;
|
||||
if (req &&
|
||||
pSend(req, L"Accept: application/vnd.github+json\r\n", (DWORD)-1L, NULL, 0, 0, 0) &&
|
||||
pRecv(req, NULL)) {
|
||||
DWORD status = 0, sz = sizeof(status);
|
||||
if (pQuery(req, UC_WH_QUERY_STATUS_CODE | UC_WH_QUERY_FLAG_NUMBER, NULL,
|
||||
&status, &sz, NULL) && status == 200) {
|
||||
for (;;) {
|
||||
DWORD got = 0;
|
||||
if (len + 1 >= cap) break;
|
||||
if (!pRead(req, buf + len, (DWORD)(cap - 1 - len), &got) || got == 0)
|
||||
break;
|
||||
len += got;
|
||||
}
|
||||
ok = len > 0;
|
||||
}
|
||||
}
|
||||
if (req) pClose(req);
|
||||
if (con) pClose(con);
|
||||
pClose(ses);
|
||||
FreeLibrary(dll);
|
||||
buf[len] = 0;
|
||||
return ok;
|
||||
#else
|
||||
FILE *f = popen("curl -q -fsS --proto =https --max-time 5 -A '" UC_UA "' '" UC_URL "' 2>/dev/null", "r");
|
||||
if (!f) return 0;
|
||||
size_t got;
|
||||
while (len + 1 < cap && (got = fread(buf + len, 1, cap - 1 - len, f)) > 0) len += got;
|
||||
int rc = pclose(f); /* nonzero if curl is missing (127) or failed */
|
||||
buf[len] = 0;
|
||||
return rc == 0 && len > 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int updateCheckThread(void *unused)
|
||||
{
|
||||
(void)unused;
|
||||
static char body[UC_BODY_MAX]; /* the thread runs at most once per launch */
|
||||
char tag[32];
|
||||
if (fetchLatest(body, UC_BODY_MAX) && extractTag(body, tag, sizeof(tag))) {
|
||||
if (versionNewer(tag, GE007_VERSION)) {
|
||||
snprintf(s_tag, sizeof(s_tag), "%s", tag);
|
||||
SDL_AtomicSet(&s_ready, 1); /* full barrier: s_tag visible first */
|
||||
sysLogPrintf(LOG_INFO, "update check: %s is available (running v%s); %s",
|
||||
tag, GE007_VERSION, UC_RELEASES);
|
||||
} else {
|
||||
sysLogPrintf(LOG_INFO, "update check: up to date (latest %s, running v%s)",
|
||||
tag, GE007_VERSION);
|
||||
}
|
||||
} else {
|
||||
sysLogPrintf(LOG_INFO, "update check: no result (offline or unavailable)");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void updateCheckStart(void)
|
||||
{
|
||||
if (!cfgCheckUpdates) return; /* OFF by default: no thread, no network, no init */
|
||||
SDL_Thread *t = SDL_CreateThread(updateCheckThread, "ge007-updatecheck", NULL);
|
||||
if (t) SDL_DetachThread(t);
|
||||
else sysLogPrintf(LOG_INFO, "update check: could not start thread");
|
||||
}
|
||||
|
||||
const char *updateCheckAvailable(void)
|
||||
{
|
||||
return SDL_AtomicGet(&s_ready) ? s_tag : NULL;
|
||||
}
|
||||
|
||||
void updateCheckOpenReleases(void)
|
||||
{
|
||||
if (SDL_OpenURL(UC_RELEASES) != 0)
|
||||
sysLogPrintf(LOG_INFO, "update check: could not open browser: %s", SDL_GetError());
|
||||
}
|
||||
+46
-26
@@ -63,13 +63,14 @@ static int cfgTexFilter = 1; /* 0 = nearest, 1 = bilinear (default), 2 = N
|
||||
static int cfgFixMipTex = 1; /* RC2: clip mip-contaminated texture uploads to base height */
|
||||
static int cfgDetailBaseTile = 1; /* D236: TEXTURETYPE_DETAIL -> sample the base image, not the detail tile */
|
||||
static int cfgWrapFix = 0; /* D74 sub-tile UV pre-wrap + RC3/D167 non-PoT mask-period wrap (opt-in; GE_WRAPFIX env overrides) */
|
||||
static int cfgFovScale = 100; /* D211: percent of the original vertical FOV; 100 = unchanged (byte-identical) */
|
||||
static float cfgFovScale = 100.0f; /* D211: percent of the original vertical FOV; 100 = unchanged (byte-identical). D546: float so the 5-degree UI steps are exact */
|
||||
static int cfgWidescreenAuto = 1; /* WIDESCREEN-FOV-PLAN Phase 4: auto-scale vertical FOV by window aspect ratio; on by default, no-op at 4:3 */
|
||||
static int cfgNativeWidescreen = 1; /* D334 (WIDESCREEN-FOV-PLAN Phase 2): project the world at the real window aspect (Hor+); no-op at 4:3 */
|
||||
static int cfgHudScale = 100; /* D226: HUD text/ammo scale %, 100 = original (no emission) */
|
||||
static int cfgDrawDistance = 250; /* % of authored far clip; 250% is the UI's 50/100 midpoint */
|
||||
static int cfgDrawDistance = 200; /* % of authored far clip; D546: 2.0x default (was 2.5x) */
|
||||
static int cfgFogDistance = 100; /* D540: % of the authored fog distances; 100 = N64 */
|
||||
static int cfgDrawDistanceAutoFov = 0; /* legacy ini option, no longer exposed in the menu */
|
||||
static int cfgLodDistance = 250; /* % of authored geometry LOD distance; 50/100 in the UI */
|
||||
static int cfgLodDistance = 200; /* % of authored geometry LOD distance; D546: 2.0x default (was 2.5x) */
|
||||
static int cfgLodDistanceAutoFov = 0; /* legacy ini option, no longer exposed in the menu */
|
||||
static int cfgAniso = 4; /* D212: anisotropic filtering samples; 4 = the value fast3d already applied (no visual delta at default) */
|
||||
static int cfgSafeAreaCrop = 1; /* crop the N64 TV-overscan safe-area margin (visible as black top/bottom bars on PC) instead of showing it; on by default */
|
||||
@@ -78,6 +79,7 @@ static int cfgFullscreen = 0; /* 0 = windowed, 1 = borderless fullscreen
|
||||
static int cfgFullscreenMode = 0; /* D511: fullscreen flavour, 0 = borderless desktop, 1 = exclusive */
|
||||
static int cfgDeckPresetApplied = 0; /* D283: 1 once the Steam Deck preset has been considered */
|
||||
static int cfgLowEndConsidered = 0; /* D482: 1 once the low-end GPU defaults have been considered */
|
||||
static int cfgDefaultsRev = 0; /* D546: 1 once the 2.5x -> 2.0x draw/LOD default migration ran */
|
||||
|
||||
/*
|
||||
* [Window] persistence. W/H = 0 -> auto (gfx_sdl2 fits a 4:3 window into ~85%
|
||||
@@ -208,14 +210,14 @@ void portCrosshairPreview(s32 *r, s32 *g, s32 *b)
|
||||
}
|
||||
|
||||
/* D257: Game.AllUnlocked — everything-unlocked goodie, OFF by default
|
||||
* (faithful N64 progression: levels unlock as you complete them). Consumed
|
||||
* once at startup by main.c, which sets the game's own RAM unlock flags
|
||||
* (faithful N64 progression: levels unlock as you complete them). Applied
|
||||
* by portAllUnlockedApply (main.c) at startup and on every F10 change: it
|
||||
* sets the game's own RAM unlock flags
|
||||
* (debug_enable_all_levels_flag / debug_007_unlock_flag in
|
||||
* src/game/debugmenu_handler.c, live because the PC build defines
|
||||
* LEFTOVERDEBUG) — port-layer memory writes only, no game-code edits.
|
||||
* 1 = every solo level selectable at every difficulty plus 007 mode from
|
||||
* the first launch. F10 'All unlocked' row toggles it; takes effect next
|
||||
* run. Cheats are unlocked at query time via fileGetIsCheatUnlocked
|
||||
* the first launch. F10 'All unlocked' row toggles it live. Cheats are unlocked at query time via fileGetIsCheatUnlocked
|
||||
* (src/game/file2.c, D442) -- the save file is never patched. */
|
||||
s32 portAllUnlocked = 0;
|
||||
|
||||
@@ -398,6 +400,15 @@ f32 portDrawDistanceMultiplier(void)
|
||||
return mult;
|
||||
}
|
||||
|
||||
/* D540: Video.FogDistance -- multiplier on the level's authored fog start/end
|
||||
* DISTANCES, independent of Video.DrawDistance (which moves the far clip).
|
||||
* bgfog.c caps the fog at the far clip, so the fog always hides the edge of
|
||||
* the drawn world. 1.0f (100) = the N64's own fog. */
|
||||
f32 portFogDistanceMultiplier(void)
|
||||
{
|
||||
return (f32)cfgFogDistance / 100.0f;
|
||||
}
|
||||
|
||||
/* D249: Video.LodDistance -- multiplier on the *distance* term
|
||||
* modelUpdateDistanceRelations() (src/game/model.c) tests against each LOD
|
||||
* node's MinDistance/MaxDistance, composed on top of the game's own
|
||||
@@ -531,11 +542,12 @@ PD_CONSTRUCTOR static void videoConfigInit(void)
|
||||
configRegisterInt("Video.FixMipTextures", &cfgFixMipTex, 0, 1);
|
||||
configRegisterInt("Video.DetailBaseTile", &cfgDetailBaseTile, 0, 1);
|
||||
configRegisterInt("Video.WrapFix", &cfgWrapFix, 0, 1);
|
||||
configRegisterInt("Video.FovScale", &cfgFovScale, 50, 150);
|
||||
configRegisterFloat("Video.FovScale", &cfgFovScale, 50.f, 150.f);
|
||||
configRegisterInt("Video.WidescreenAuto", &cfgWidescreenAuto, 0, 1);
|
||||
configRegisterInt("Video.NativeWidescreen", &cfgNativeWidescreen, 0, 1); /* D334 */
|
||||
configRegisterInt("Game.HudScale", &cfgHudScale, 75, 150); /* D226: capped at 150 (user: little benefit above) */
|
||||
configRegisterInt("Video.DrawDistance", &cfgDrawDistance, 100, 800);
|
||||
configRegisterInt("Video.FogDistance", &cfgFogDistance, 50, 800); /* D540 */
|
||||
configRegisterInt("Video.DrawDistanceAutoFov", &cfgDrawDistanceAutoFov, 0, 1);
|
||||
configRegisterInt("Video.LodDistance", &cfgLodDistance, 25, 800);
|
||||
configRegisterInt("Video.LodDistanceAutoFov", &cfgLodDistanceAutoFov, 0, 1);
|
||||
@@ -551,6 +563,7 @@ PD_CONSTRUCTOR static void videoConfigInit(void)
|
||||
configRegisterInt("Window.Maximized", &cfgWinMax, 0, 1);
|
||||
configRegisterInt("Video.DeckPresetApplied", &cfgDeckPresetApplied, 0, 1); /* D283 */
|
||||
configRegisterInt("Video.LowEndConsidered", &cfgLowEndConsidered, 0, 1); /* D482 */
|
||||
configRegisterInt("Video.DefaultsRev", &cfgDefaultsRev, 0, 1); /* D546 */
|
||||
}
|
||||
|
||||
/* D283: Steam Deck preset. Called from main() right AFTER configLoad(), so
|
||||
@@ -686,6 +699,20 @@ static int videoIsLowEndRenderer(const char *r, const char **why)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* D546: one-time move of untouched old defaults to the standardized ones
|
||||
* (draw/LOD distance 2.5x -> 2.0x). A value the player changed is kept; same
|
||||
* "considered" flag pattern as D482. */
|
||||
static void videoMigrateDefaults(void)
|
||||
{
|
||||
int changed = 0;
|
||||
if (cfgDefaultsRev >= 1) return;
|
||||
if (cfgDrawDistance == 250) { cfgDrawDistance = 200; changed++; }
|
||||
if (cfgLodDistance == 250) { cfgLodDistance = 200; changed++; }
|
||||
sysLogPrintf(LOG_INFO, "video: defaults rev 1 (D546): %d value(s) moved to 2.0x", changed);
|
||||
cfgDefaultsRev = 1;
|
||||
configSave();
|
||||
}
|
||||
|
||||
static void videoApplyLowEndDefaults(void)
|
||||
{
|
||||
const char *r = gfx_opengl_renderer_string();
|
||||
@@ -702,8 +729,8 @@ static void videoApplyLowEndDefaults(void)
|
||||
}
|
||||
if (low) {
|
||||
int changed = 0;
|
||||
if (cfgDrawDistance == 250) { cfgDrawDistance = 100; changed++; }
|
||||
if (cfgLodDistance == 250) { cfgLodDistance = 100; changed++; }
|
||||
if (cfgDrawDistance == 200) { cfgDrawDistance = 100; changed++; } /* D546: the default is 2.0x */
|
||||
if (cfgLodDistance == 200) { cfgLodDistance = 100; changed++; }
|
||||
if (cfgMSAA == 2) { cfgMSAA = 1; gfx_msaa_level = 1; changed++; }
|
||||
sysLogPrintf(LOG_INFO, "video: low-end renderer \"%s\" (%s); lowered %d default(s): DrawDistance=%d LodDistance=%d MSAA=%d",
|
||||
r, why, changed, cfgDrawDistance, cfgLodDistance, cfgMSAA);
|
||||
@@ -734,8 +761,9 @@ static const struct { const char *key; double n64, port; } kVideoPresets[] = {
|
||||
{ "Video.WidescreenAuto", 0, 1 }, /* stock vertical FOV at any window aspect */
|
||||
{ "Video.SafeAreaCrop", 0, 1 }, /* show the VI frame as output, borders included */
|
||||
{ "Video.AspectMode", 1, 0 }, /* D447: exact console aspect, bars around it */
|
||||
{ "Video.DrawDistance", 100, 250 }, /* authored far clip / fog (D218) */
|
||||
{ "Video.LodDistance", 100, 250 }, /* authored LOD switch distances (D249) */
|
||||
{ "Video.DrawDistance", 100, 200 }, /* authored far clip (D218); D546 2.0x */
|
||||
{ "Video.FogDistance", 100, 100 }, /* D540: N64 fog in both presets */
|
||||
{ "Video.LodDistance", 100, 200 }, /* authored LOD switch distances (D249); D546 2.0x */
|
||||
{ "Video.DrawDistanceAutoFov", 0, 0 }, /* legacy coupling: off = identity */
|
||||
{ "Video.LodDistanceAutoFov", 0, 0 },
|
||||
{ "Video.FovScale", 100, 100 }, /* stock FOV (D211) */
|
||||
@@ -794,7 +822,7 @@ static SDL_atomic_t liveCfgDirty;
|
||||
* plain float the game re-reads each frame; anisotropy goes to fast3d. */
|
||||
static void videoApplyImageOptions(void)
|
||||
{
|
||||
portFovScale = (f32)cfgFovScale / 100.0f;
|
||||
portFovScale = cfgFovScale / 100.0f;
|
||||
gfx_set_anisotropy_level(cfgAniso);
|
||||
gfx_set_safe_area_crop(cfgSafeAreaCrop);
|
||||
}
|
||||
@@ -1017,6 +1045,7 @@ int videoInit(void)
|
||||
};
|
||||
|
||||
gfx_init(&set);
|
||||
videoMigrateDefaults(); /* D546: before D482, which compares against the new defaults */
|
||||
videoApplyLowEndDefaults(); /* D482: needs GL_RENDERER; before the first frame */
|
||||
|
||||
/* VSync + optional fps cap; fast3d paces the window itself. */
|
||||
@@ -1190,11 +1219,11 @@ void videoStartFrame(void)
|
||||
if (dirty & VCFG_VSYNC) wmAPI->set_swap_interval(cfgVSync ? 1 : 0);
|
||||
if (dirty & VCFG_FPS) gfx_set_target_fps(cfgFpsCap);
|
||||
if (dirty & VCFG_FILTER) videoApplyTexFilter();
|
||||
if (dirty & VCFG_FOV) portFovScale = (f32)cfgFovScale / 100.0f;
|
||||
if (dirty & VCFG_FOV) portFovScale = cfgFovScale / 100.0f;
|
||||
if (dirty & VCFG_ANISO) gfx_set_anisotropy_level(cfgAniso);
|
||||
if (dirty & VCFG_CROP) gfx_set_safe_area_crop(cfgSafeAreaCrop);
|
||||
sysLogPrintf(LOG_INFO, "video: live config applied mask=%02x "
|
||||
"(vsync=%d fpscap=%d texfilter=%d fov=%d aniso=%d)",
|
||||
"(vsync=%d fpscap=%d texfilter=%d fov=%.1f aniso=%d)",
|
||||
dirty, cfgVSync, cfgFpsCap, cfgTexFilter, cfgFovScale, cfgAniso);
|
||||
}
|
||||
|
||||
@@ -1299,17 +1328,8 @@ void videoPumpEvents(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Refresh the window title with the live FPS about once a second. */
|
||||
if (wmAPI && wmAPI->set_window_title) {
|
||||
static double lastTitle = 0.0;
|
||||
double now = wmAPI->get_time();
|
||||
if (now - lastTitle >= 1.0) {
|
||||
lastTitle = now;
|
||||
char title[64];
|
||||
snprintf(title, sizeof(title), "GoldenEye 007 - %.0f fps", vidAvgFPS);
|
||||
wmAPI->set_window_title(title);
|
||||
}
|
||||
}
|
||||
/* D550: static title bar -- the "GoldenEye 007 - NN fps" per-second
|
||||
* refresh used to live here (maintainer request 2026-10-17). */
|
||||
|
||||
/* D287: apply anything the events above (click-to-lock, focus) queued. */
|
||||
inputApplyMouseRequests();
|
||||
|
||||
@@ -28,6 +28,7 @@ extern u16 get_mTrack2Vol(void);
|
||||
extern void fileBuildWriteNewSave(u32 folder);
|
||||
extern void set_cur_player_look_vertical_inverted(u32 value);
|
||||
extern void cur_player_set_aim_control(u32 value);
|
||||
extern void cur_player_set_lookahead(u32 value);
|
||||
extern void cur_player_set_sight_onscreen_control(u32 value);
|
||||
extern void cur_player_set_ammo_onscreen_setting(u32 value);
|
||||
|
||||
@@ -532,6 +533,32 @@ static void applyValue(enum WatchSettingField field, int value)
|
||||
}
|
||||
}
|
||||
|
||||
/* D559 (maintainer-approved, port layer only): D557 turned Look ahead off in
|
||||
* DEFAULT_OPTIONS, but folders created before it hold the OLD factory options.
|
||||
* When a folder is started to play (the stage becomes active; the stage load's
|
||||
* fileLoadSettingsForFolder then reads the saved options), clear OPTION_LOOKAHEAD
|
||||
* iff the folder is untouched: no stage time at any difficulty (times[] all
|
||||
* zero), no 007 flag, no unlocked cheats, and options EXACTLY the old factory
|
||||
* default with control-type bits 0. Any progress or any other option bit
|
||||
* (including a deliberately-set Look ahead on a played folder) is never touched.
|
||||
* Persists through fileWriteSave, as saveFrontField does. Game thread only.
|
||||
* Returns 1 when it changed the save. */
|
||||
#define OLD_FACTORY_OPTIONS (OPTION_AUTOAIM | OPTION_SIGHTONSCREEN | OPTION_LOOKAHEAD | OPTION_DISPLAYAMMO)
|
||||
static int migrateUntouchedLookAhead(int folder)
|
||||
{
|
||||
save_data *save = validSave(folder);
|
||||
if (!save || save->options != OLD_FACTORY_OPTIONS) return 0;
|
||||
if (save->flag_007 || save->unlocked_cheats_1 || save->unlocked_cheats_2 || save->unlocked_cheats_3) return 0;
|
||||
for (size_t i = 0; i < sizeof(save->times); i++)
|
||||
if (save->times[i]) return 0;
|
||||
if (!fileGamePakProbe()) return 0;
|
||||
save->options = (u16)(save->options & ~OPTION_LOOKAHEAD);
|
||||
fileWriteSave(save);
|
||||
sysLogPrintf(LOG_INFO, "watchsettings: Bond file %d is untouched and has the old factory options; Look ahead set off (D559)", folder + 1);
|
||||
return 1;
|
||||
}
|
||||
static int wsLookAheadChecked = 0; /* once per stage activation */
|
||||
|
||||
void watchSettingsGameTick(void)
|
||||
{
|
||||
/* Drain F10 front-end commits on the game thread. This MUST run before
|
||||
@@ -566,7 +593,9 @@ void watchSettingsGameTick(void)
|
||||
{
|
||||
static int wsResetProbePhase = 0;
|
||||
static int wsProbeFrontTicks = 0, wsProbeStageTicks = 0;
|
||||
if (getenv("GE_WSPROBE_RESET")) {
|
||||
static int wsResetProbeOn = -1; /* cached: this runs every tick (D250/D302 class) */
|
||||
if (wsResetProbeOn < 0) wsResetProbeOn = getenv("GE_WSPROBE_RESET") != NULL;
|
||||
if (wsResetProbeOn) {
|
||||
if (!stageActive()) {
|
||||
if (wsResetProbePhase == 0 && ++wsProbeFrontTicks == 120) {
|
||||
optionsResetProbePrepare();
|
||||
@@ -594,8 +623,22 @@ void watchSettingsGameTick(void)
|
||||
* during a level/menu transition. Each command's folder is re-checked
|
||||
* against selected_folder_num at apply time, so a stale cross-file edit
|
||||
* is still dropped, never misapplied. */
|
||||
/* D564: the game's own "Look up/down" option is not exposed (the port's
|
||||
* Mouse / Controller "Invert look" rows are the inversion controls). Pin it
|
||||
* to its factory value (0, as a blank profile has it) so a value saved by an
|
||||
* older build or set via the in-game watch menu cannot silently flip the
|
||||
* pad stick on the game's pitch path. RAM only; one int compare per tick. */
|
||||
if (stageActive() && g_CurrentPlayer && get_cur_player_look_vertical_inverted() != 0)
|
||||
set_cur_player_look_vertical_inverted(0);
|
||||
|
||||
if (!stageActive() || !g_CurrentPlayer) {
|
||||
wsStageWasActive = 0;
|
||||
if (!stageActive()) wsLookAheadChecked = 0;
|
||||
else if (!wsLookAheadChecked) { /* D559: stage starting, before the stage load reads the save */
|
||||
wsLookAheadChecked = 1;
|
||||
if (migrateUntouchedLookAhead(selected_folder_num) && getPlayerCount() == 1)
|
||||
cur_player_set_lookahead(0); /* harmless if the stage load re-reads the save after this */
|
||||
}
|
||||
/* D354: one-shot fallback -- if not a single folder holds a valid
|
||||
* save (wiped/corrupt/fresh eeprom), build the game's own blank save for
|
||||
* file 1 (game thread, so the EEPROM write is on the owning thread)
|
||||
@@ -649,6 +692,11 @@ void watchSettingsGameTick(void)
|
||||
* init_watch re-applies the saved music value to the X track; resync the
|
||||
* BGM (track 1) to the same live value once per activation so the player's
|
||||
* music setting survives level starts, not just live F10 edits. */
|
||||
if (!wsLookAheadChecked) { /* D559: no tick saw the stage before its load: fix the live setting too */
|
||||
wsLookAheadChecked = 1;
|
||||
if (migrateUntouchedLookAhead(selected_folder_num) && getPlayerCount() == 1)
|
||||
cur_player_set_lookahead(0);
|
||||
}
|
||||
if (!wsStageWasActive && musicTrack1GetVolume() != get_mTrack2Vol())
|
||||
musicTrack1ApplySeqpVol(get_mTrack2Vol());
|
||||
wsStageWasActive = 1;
|
||||
|
||||
Reference in new issue
Block a user