From d6bae78fdc65fa465a8c5b4aa31ae87984908c15 Mon Sep 17 00:00:00 2001 From: Roberto Remedio Date: Wed, 10 Jun 2026 09:26:11 -0300 Subject: [PATCH 01/12] Add "Show Compiled Track" view with bit-exact gp2cc module Add a toggleable editor view (View menu + toolbar "C" button) that draws GP2's exact compiled track geometry and cc-line (racing line), with no pitlane/kerbs/objects. When active it recompiles from the live track on each repaint, so it updates as cc-line sectors are edited, and mirrors the other cc-line views: a small perpendicular tick where each cc-line sector starts, plus a highlight on the selected sector. Drawing goes through a new standalone C module (gp2cc.c/.h/gp2cc_tables.c): a bit-exact port of GP2's geometry compiler + cc-line (UACalcBestLine) and its helpers, validated to the byte against GP2Lap dumps on F1CT01/02a/03/ 09/11/16: - geometry: heading, startAngle, road widths, side vectors - exact integer position warp (Bresenham gap distribution, sub_76D39) - cc-line bestLine / angle18 t_Sinus and t_ArithTab1 are embedded in gp2cc_tables.c so no GP2.EXE is needed at runtime. --- GPTrack.cpp | 109 ++++++++ GPTrack.h | 3 + TrackEditor.rc | 9 +- TrackEditor.vcxproj | 9 + TrackEditorScrollView.cpp | 19 ++ TrackEditorScrollView.h | 4 + gp2cc.c | 532 ++++++++++++++++++++++++++++++++++++++ gp2cc.h | 113 ++++++++ gp2cc_tables.c | 404 +++++++++++++++++++++++++++++ res/bmp00076.bmp | Bin 4086 -> 4214 bytes resource.h | 3 +- 11 files changed, 1201 insertions(+), 4 deletions(-) mode change 100755 => 100644 GPTrack.cpp mode change 100755 => 100644 GPTrack.h mode change 100755 => 100644 TrackEditor.rc mode change 100755 => 100644 TrackEditorScrollView.cpp mode change 100755 => 100644 TrackEditorScrollView.h create mode 100644 gp2cc.c create mode 100644 gp2cc.h create mode 100644 gp2cc_tables.c mode change 100755 => 100644 resource.h diff --git a/GPTrack.cpp b/GPTrack.cpp old mode 100755 new mode 100644 index 9456e2b..e85077a --- a/GPTrack.cpp +++ b/GPTrack.cpp @@ -11,6 +11,7 @@ #include "TrackSection.h" #include "TrackObjectDefinition.h" #include "CCLineSection.h" +#include "gp2cc.h" // bit-exact compiled track + cc-line #include "TrackCmd.h" #include "InternalObject.h" #include "JamFileEditor.h" @@ -144,6 +145,7 @@ void GPTrack::Create() PROFILE(showPitLane, TRUE) PROFILE(showObjects, FALSE) PROFILE(showCCLine, TRUE) + PROFILE(showComputed, FALSE) PROFILE(showHiddenAsGray, TRUE) PROFILE(showTrackPie, FALSE) @@ -412,6 +414,7 @@ GPTrack::~GPTrack() WR_PROFILE(showPitLane) WR_PROFILE(showObjects) WR_PROFILE(showCCLine) + WR_PROFILE(showComputed) WR_PROFILE(showHiddenAsGray) WR_PROFILE(showTrackPie) @@ -4030,6 +4033,112 @@ void GPTrack::drawCCLine(Display *g) } } +// --------------------------------------------------------------------------- +// drawComputed: the bit-exact "compiled track" view. Rebuilds the .dat image +// in trackdata[] from the current in-memory track (RecreateData), runs the +// gp2cc compiler + cc-line on it, and draws the road EDGES + the racing line +// in the editor's own coordinate frame. No pitlane/kerbs/objects. +// +// Coordinate map: gp2cc positions are in 1/8-world-units (X8/Y8); the editor +// integrates 1 unit/segment while GP2 steps 128 world-units (=1024 X8) per +// segment, so scale = 1/1024, anchored at track section 0's start, same +// heading orientation. (If the shape comes out mirrored/wrong-scale, adjust +// S / the axis pairing here.) +// --------------------------------------------------------------------------- +void GPTrack::drawComputed(Display *g) +{ + if (TrackSections == NULL || TrackSections->size() == 0) return; + + // rebuild trackdata[] (the .dat image) from the live track, then compile it + RecreateData(); + + static gp2cc_track gt; + static gp2cc_ccgeo cg; + static short bl[GP2CC_MAXSEG], a18[GP2CC_MAXSEG]; + + if (gp2cc_compile_geometry_buf(trackdata, 65535, >) != 0) return; + int n = gt.n; + if (n <= 1) return; + + // cc-line on the compiled geometry (cx8 pairs with world Y=Y8, cy8 with X=X8) + cg.n = n; + for (int i = 0; i < n; i++) { + cg.segAngle[i] = gt.angle[i]; + cg.cx8[i] = gt.Y8[i]; + cg.cy8[i] = gt.X8[i]; + cg.f14[i] = gt.f14[i]; + } + static int cmdSeg[GP2CC_MAXSEG]; // first segment of each cc-command (=sector) + int numCmds = 0; + gp2cc_compute_ccline(&cg, trackdata, 65535, gt.ccoff, bl, a18, cmdSeg, &numCmds); + + // editor frame: world units -> editor units = /128 (128 world-u/seg = 1 editor-u), + // anchored at section 0's start. Edges + cc-line = centre + perpendicular(heading)* + // (offset/8 world units), exactly like the validated preview (plot_ccline.py). + TrackSection *s0 = (TrackSection*)TrackSections->elementAt(0); + double ox = s0->getStartX(), oy = s0->getStartY(); + double bwx = gt.X8[0] / 8.0, bwy = gt.Y8[0] / 8.0; + const double ES = 1.0 / 128.0; + + static double lex[GP2CC_MAXSEG], ley[GP2CC_MAXSEG]; // left edge (editor coords) + static double rex[GP2CC_MAXSEG], rey[GP2CC_MAXSEG]; // right edge + static double ccx[GP2CC_MAXSEG], ccy[GP2CC_MAXSEG]; // cc-line + for (int i = 0; i < n; i++) { + double cosh = gp2cc_gv(gt.angle[i]) / 16384.0; // cos(heading) + double sinh = gp2cc_gv(0x4000 - gt.angle[i]) / 16384.0; // sin(heading) + double pvx = -sinh, pvy = cosh; // unit perpendicular (worldX, worldY) + double px = gt.X8[i] / 8.0, py = gt.Y8[i] / 8.0; // segment centre (world) + double wl = gt.widthL[i] / 8.0, wr = gt.widthR[i] / 8.0; // half-widths (world) + double cc = bl[i] / 8.0; // cc-line offset (world) + lex[i] = ox + (px + pvx * wl - bwx) * ES; ley[i] = oy + (py + pvy * wl - bwy) * ES; + rex[i] = ox + (px - pvx * wr - bwx) * ES; rey[i] = oy + (py - pvy * wr - bwy) * ES; + ccx[i] = ox + (px + pvx * cc - bwx) * ES; ccy[i] = oy + (py + pvy * cc - bwy) * ES; + } + + g->setColor(0); // road edges (dark) + for (int i = 0; i < n; i++) { + int j = (i + 1) % n; + g->drawLine(lex[i], ley[i], lex[j], ley[j]); + g->drawLine(rex[i], rey[i], rex[j], rey[j]); + } + g->setColor(RED_PEN); // cc-line (racing line) + for (int i = 0; i < n; i++) { + int j = (i + 1) % n; + g->drawLine(ccx[i], ccy[i], ccx[j], ccy[j]); + } + + // --- per-sector indicators + selected-sector highlight --- + // each CCLineSection maps 1:1 to a cc-command (cmdSeg[c] = its first segment). + int nSec = (CCLineSections != NULL) ? CCLineSections->size() : 0; + int mSec = (numCmds < nSec) ? numCmds : nSec; + for (int c = 0; c < mSec; c++) { + int s = cmdSeg[c]; + if (s < 0 || s >= n) continue; + CCLineSection *sec = (CCLineSection *)CCLineSections->elementAt(c); + BOOL sel = (sec != NULL) && sec->isSelected(); + + // highlight the selected sector's cc-line segments (over-draw in colour 4) + if (sel) { + int len = (sec != NULL) ? (int)sec->getLength() : 0; + g->setColor(4); + for (int k = 0; k < len; k++) { + int i = (s + k) % n, j = (i + 1) % n; + g->drawLine(ccx[i], ccy[i], ccx[j], ccy[j]); + } + } + + // small perpendicular tick on the cc-line where the sector starts. The editor + // transform is uniform scale + translate (no rotation), so the world-space unit + // perpendicular doubles as the editor-space direction; tlen is in editor units. + double cosh = gp2cc_gv(gt.angle[s]) / 16384.0; + double sinh = gp2cc_gv(0x4000 - gt.angle[s]) / 16384.0; + double tlen = 3.0; // editor units + double tx = -sinh * tlen, ty = cosh * tlen; + g->setColor(sel ? 4 : BLUE_PEN); + g->drawLine(ccx[s] - tx, ccy[s] - ty, ccx[s] + tx, ccy[s] + ty); + } +} + void GPTrack::drawPitlane(Display *g) { if (TrackSections == NULL || TrackSections->size() == 0) return; diff --git a/GPTrack.h b/GPTrack.h old mode 100755 new mode 100644 index 7946d64..93d873f --- a/GPTrack.h +++ b/GPTrack.h @@ -144,6 +144,8 @@ class GPTrack : public CWnd drawPitlane(Display *g); void drawCCLine(Display *g); + void + drawComputed(Display *g); // bit-exact compiled track + cc-line (gp2cc) void drawCameras(Display *g); void @@ -243,6 +245,7 @@ class GPTrack : public CWnd BOOL showPitLane; BOOL showObjects; BOOL showCCLine; + BOOL showComputed; // the bit-exact compiled track + cc-line view BOOL showHiddenAsGray; BOOL showTrackPie; BOOL showCameras; diff --git a/TrackEditor.rc b/TrackEditor.rc old mode 100755 new mode 100644 index 580f88f..cc9263c --- a/TrackEditor.rc +++ b/TrackEditor.rc @@ -274,6 +274,8 @@ BEGIN BUTTON ID_AIRVIEW BUTTON ID_DRAWAXIS BUTTON ID_VIEWCAMERAS + SEPARATOR + BUTTON ID_VIEW_COMPILED END IDR_OBJECT TOOLBAR 16, 15 @@ -472,6 +474,7 @@ BEGIN MENUITEM "Show Track", VIEW_TRACK MENUITEM "Show Pitlane", VIEW_PITLANE MENUITEM "Show CC Line", VIEW_CCLINE + MENUITEM "Show Compiled Track", ID_VIEW_COMPILED MENUITEM "Show Objects", VIEW_OBJECTS MENUITEM "Show Fences", VIEW_WALLS MENUITEM "Show Pit Fences", ID_SHOW_SHOWPITFENCES @@ -825,7 +828,7 @@ CAPTION "About TrackEditor" FONT 8, "MS Sans Serif", 0, 0, 0x0 BEGIN LTEXT "TrackEditor Version",IDC_STATIC,147,7,68,8,SS_NOPREFIX - LTEXT "Copyright © Paul Hoad 1997-2020",IDC_STATIC,147,19,119,8 + LTEXT "Copyright � Paul Hoad 1997-2020",IDC_STATIC,147,19,119,8 DEFPUSHBUTTON "I Accept",IDOK,119,192,54,14,WS_GROUP LTEXT "TrackEditor is a Shareware product. You are free to use TrackEditor on a trial basis only. If you like or intend to continue using the Track Editor in any way then you are required to register your copy with the author: (See README)",IDC_LEGAL1,7,59,303,27 LTEXT "You must NOT distribute this editor by any means other than via email or internet download without the authors prior agreement. You are not permitted to include this TrackEditor on any Media for distribution without the authors prior written consent.",IDC_LEGAL2,7,86,303,27 @@ -1390,7 +1393,7 @@ BEGIN VALUE "FileDescription", "TRACKEDITOR MFC Application" VALUE "FileVersion", "0, 0, 0, 0" VALUE "InternalName", "TRACKEDITOR" - VALUE "LegalCopyright", "Copyright © Paul Hoad 1997-1998" + VALUE "LegalCopyright", "Copyright � Paul Hoad 1997-1998" VALUE "OriginalFilename", "TRACKEDITOR.EXE" VALUE "ProductName", "TRACKEDITOR Application" VALUE "ProductVersion", "000.000.000.000" @@ -2339,7 +2342,7 @@ BEGIN LTEXT "Info on Track Format",IDC_STATIC,14,115,67,8 LTEXT "Thank you to the Following....",IDC_STATIC,61,38,94,8 GROUPBOX "Credits",IDC_STATIC,7,7,212,212 - LTEXT "Rob Buis, Michele Merlino, Ponk, Marcel Borsboom, Trevor Kellaway,Andrzej Barganski,John Verheijen,""Swervin' Irvin'"",Mike Wallace, Michaël 'NRG' Hompus, Bob Culver.",IDC_STATIC,96,145,109,49,0,WS_EX_RIGHT + LTEXT "Rob Buis, Michele Merlino, Ponk, Marcel Borsboom, Trevor Kellaway,Andrzej Barganski,John Verheijen,""Swervin' Irvin'"",Mike Wallace, Micha�l 'NRG' Hompus, Bob Culver.",IDC_STATIC,96,145,109,49,0,WS_EX_RIGHT LTEXT "Additional Thanks:",IDC_STATIC,13,145,60,8 LTEXT "Mirror Sites:",IDC_STATIC,14,71,50,8 LTEXT "All you Mirroring sites",IDC_STATIC,137,71,68,11 diff --git a/TrackEditor.vcxproj b/TrackEditor.vcxproj index 8096677..d9f2376 100755 --- a/TrackEditor.vcxproj +++ b/TrackEditor.vcxproj @@ -174,6 +174,14 @@ + + stdc11 + NotUsing + + + stdc11 + NotUsing + @@ -279,6 +287,7 @@ + diff --git a/TrackEditorScrollView.cpp b/TrackEditorScrollView.cpp old mode 100755 new mode 100644 index 5812c42..898a957 --- a/TrackEditorScrollView.cpp +++ b/TrackEditorScrollView.cpp @@ -89,6 +89,8 @@ ON_COMMAND(ID_TRACK_TOOL, OnTrackTool) ON_COMMAND(ID_ZOOMTOOL, OnZoomtool) ON_UPDATE_COMMAND_UI(VIEW_CCLINE, OnUpdateCcline) ON_COMMAND(VIEW_CCLINE, OnCcline) +ON_UPDATE_COMMAND_UI(ID_VIEW_COMPILED, OnUpdateViewCompiled) +ON_COMMAND(ID_VIEW_COMPILED, OnViewCompiled) ON_UPDATE_COMMAND_UI(ID_VIEW_OBJ_BITMAPS, OnUpdateViewObjBitmaps) ON_UPDATE_COMMAND_UI(ID_ZOOMTOOL, OnUpdateZoomtool) ON_UPDATE_COMMAND_UI(ID_POINTER, OnUpdatePointer) @@ -456,6 +458,10 @@ void TrackEditorScrollView::OnMyDraw(CDC* pDC) track->drawGrid(display); track->drawTrack(display, TRUE); track->drawCCLines(display); + } else if (track->showComputed) { + // bit-exact compiled-track + cc-line view (replaces the normal draws) + track->drawGrid(display); + track->drawComputed(display); } else { track->drawGrid(display); track->drawTrack(display); @@ -1026,6 +1032,19 @@ void TrackEditorScrollView::OnCcline() OnDrivingLine(); } +void TrackEditorScrollView::OnUpdateViewCompiled(CCmdUI* pCmdUI) +{ + UPDATE_TRACK(showComputed); +} + +void TrackEditorScrollView::OnViewCompiled() +{ + CTrackEditorDoc* pDoc = GetDocument(); + GPTrack* mytrack = pDoc->getTrack(); + mytrack->showComputed = !mytrack->showComputed; + repaint(); +} + void TrackEditorScrollView::OnUpdateViewObjBitmaps(CCmdUI* pCmdUI) { // TODO: Add your command update UI handler code here diff --git a/TrackEditorScrollView.h b/TrackEditorScrollView.h old mode 100755 new mode 100644 index f4e3b0f..4fbd0d6 --- a/TrackEditorScrollView.h +++ b/TrackEditorScrollView.h @@ -142,6 +142,10 @@ class TrackEditorScrollView : public CScrollView OnUpdateCcline(CCmdUI* pCmdUI); afx_msg void OnCcline(); + afx_msg void + OnUpdateViewCompiled(CCmdUI* pCmdUI); + afx_msg void + OnViewCompiled(); afx_msg void OnUpdateViewObjBitmaps(CCmdUI* pCmdUI); afx_msg void diff --git a/gp2cc.c b/gp2cc.c new file mode 100644 index 0000000..c297e28 --- /dev/null +++ b/gp2cc.c @@ -0,0 +1,532 @@ +/* gp2cc — geometry compiler (see gp2cc.h). Ported verbatim from datparse.py, + * which is bit-validated against the GP2Lap dumps. */ +#include "gp2cc.h" +#include +#include +#include + +/* The game's trig tables are EMBEDDED (gp2cc_tables.c, generated from GP2.EXE) so + * gp2cc needs no GP2.EXE at runtime — important for embedding in the editor. + * COS = t_Sinus (amp 0x4000, one entry/8 angle units; gv interp + raw) + * ATAN1 = t_ArithTab1 (atan, indexed by the curved-branch atan2 / finer cos) */ +extern const short gp2cc_COS[]; +extern const short gp2cc_ATAN1[]; +static const short *const COS = gp2cc_COS; +static const short *const ATAN1 = gp2cc_ATAN1; + +/* Tables are embedded; nothing to load. Kept for API compatibility (the exe path + * is ignored). Always succeeds. */ +int gp2cc_load_tables(const char *gp2exe_path) { + (void)gp2exe_path; + return 0; +} + +static inline int s16(int v) { v &= 0xFFFF; return (v & 0x8000) ? v - 0x10000 : v; } + +/* GetSinusVal (0x104B9): cos, interpolated. */ +int gp2cc_gv(int ax) { + ax = s16(ax); + if (ax < 0) ax = (-ax) & 0xFFFF; + int frac = ax & 7; + int widx = ((ax >> 2) & 0xFFFE) >> 1; + int base = COS[widx], nxt = COS[widx + 1]; + int d = s16(nxt - base); + return s16(base + ((d * frac) >> 3)); +} +/* raw (non-interpolated) cosine for the position step (InitTrackSegs 0x79A9F). */ +static int scos(int a) { + a = s16(a); + if (a < 0) a = -a; + return COS[((a >> 2) & 0xFFFE) >> 1]; +} + +/* ---- track-command arg sizes (datparse TRK_ARGSZ). opcode 0x45 is conditional + * on w_C5Code bit9 (handled inline). -1 = unknown (desync). */ +static const int TRK_ARGSZ[0x66] = { + 2,2,2,0,0,4,0,0,2,2, 10,10,2,2,4,4,2,2,2,2, 2,2,0,0,2,2,4,0,0,0, + 0,0,0,0,0,0,0,0,4,4, 0,2,6,4,8,4,2,4,2,2, 2,2,4,4,2,2,26,2,4,8, + 14,4,4,4,2,2,4,4,6,14, 2,4,14,16,8,8,4,6,2,2, 4,0,2,2,0,2,0,0,0,2, + 2,0,2,4,6,6,2,0,0,0, 0,0 /* 0x64,0x65 */ +}; + +/* read little-endian words from a buffer */ +static int rd16 (const uint8_t *d, int o) { return d[o] | (d[o+1] << 8); } +static int rds16(const uint8_t *d, int o) { return s16(rd16(d, o)); } +static int32_t rd32 (const uint8_t *d, int o) { return (int32_t)(d[o] | (d[o+1]<<8) | (d[o+2]<<16) | (d[o+3]<<24)); } + +/* Buffer-based core: compile geometry from a .dat image already in memory (the + * caller owns `d`; no allocation/free here). The editor calls this with the + * track it serialized via WriteTrack. Returns 0 on success. */ +int gp2cc_compile_geometry_buf(const uint8_t *d, int len, gp2cc_track *t) { + (void)len; + int base = 0x1020 + rd32(d, 0x1010); /* MainTrackData */ + t->hdr_angle = rds16(d, base + 0); + int hdr_climb = rds16(d, base + 2); + t->hdr_Y = rds16(d, base + 4); + t->hdr_X = rds16(d, base + 8); + t->hdr_width = rd16(d, base + 0xA); + t->hdr_c5 = rd16(d, base + 0xE); + int unknownNumber = rd16(d, base + 0x12); + + int o = base + 0x14; /* Handle4Unknowns */ + for (int i = 0; i < unknownNumber; i++) { + int ax = rd16(d, o); o += 2; + if (ax != 0x1F) o += 2; + } + + /* ---- walk the command stream, integrating heading/startAngle (Q16) ---- */ + int32_t TA = (t->hdr_angle & 0xFFFF) << 16; /* d_TrkStrtAngle */ + int32_t SA = TA; /* d_StartAngle */ + int c5b12 = (t->hdr_c5 & 0x1000) != 0; + int c5b9 = (t->hdr_c5 & 0x200) != 0; + int n = 0, ccoff = -1; + + /* ---- road-width state (tseg+0x60 left / +0x62 right), with transition ramps. + * Width cmds: op 0x05 both (TrkWidthChange), 0x34 left (sub_75ACA), 0x35 right + * (sub_75BCD); each: arg1=transition length, arg2=target. len==0 = instant; + * len>0 = ramp over `len` segs, delta=(target-cur)/len (trunc). Per-seg: + * store cur THEN ramp (cur+=delta; len--). Init both = header width. */ + int curL = t->hdr_width, curR = t->hdr_width; + int deltaL = 0, deltaR = 0, transitL = 0, transitR = 0; + + for (;;) { + int w = rd16(d, o); + if (w == 0xFFFF) { ccoff = o + 2; break; } + if (w & 0x8000) { + int op = (w >> 8) & 0x7F; + int szc; + if (op == 0x45) szc = c5b9 ? 14 : 12; /* C5sub_75D97 conditional */ + else if (op < 0x66) szc = TRK_ARGSZ[op]; + else szc = -1; + if (szc < 0) { return -2; } /* desync / unknown opcode */ + if (op == 0x34 || op == 0x05) { /* left (or both) width change */ + int len = rds16(d, o + 2), target = rds16(d, o + 4); + if (len == 0) { curL = target; deltaL = 0; transitL = 0; } + else { transitL = len; deltaL = (target - curL) / len; } + } + if (op == 0x35 || op == 0x05) { /* right (or both) width change */ + int len = rds16(d, o + 2), target = rds16(d, o + 4); + if (len == 0) { curR = target; deltaR = 0; transitR = 0; } + else { transitR = len; deltaR = (target - curR) / len; } + } + o += 2 + szc; + continue; + } + /* geometry command: w = N segments */ + int N = w; + int d1w = rds16(d, o + 2); /* d_1stArg per-seg angle word */ + int32_t d1 = d1w << 16; /* ...as Q16 for the integration */ + /* tseg+0x14 (θ' tilt source) = (d1word * 0x6488) >> 14, 0x6488=round(pi/2*2^14); + * constant for every seg in this command. Validated bit-exact vs CCREP f14. */ + int32_t f14 = ((int32_t)d1w * 0x6488) >> 14; + o += 10; + /* half-step (sub_79166: TrkStrtAngle += d1/2). The bit12 gate is `xor ax,ax`, + * which zeros ONLY the low 16 bits of the 32-bit half-step (NOT the whole + * value): d1=d1w<<16, so the integer part of d1/2 survives when bit12 clear. + * Earlier `:0` wrongly killed the whole half-step -> command-boundary stalls + * on bit12-clear tracks (e.g. f1ct02a, c5=0xD80). */ + int32_t half_a = d1 >> 1; + if (!c5b12) half_a &= ~0xFFFF; + for (int k = 0; k < N; k++) { + if (n >= GP2CC_MAXSEG) { return -3; } + if (k == 0) { SA = TA; TA += half_a; } + else { TA += d1; SA += d1; } + t->fAngle[n] = TA; + t->angle[n] = (int16_t)(TA >> 16); + t->startAngle[n] = (int16_t)(SA >> 16); + t->f14[n] = f14; + t->widthL[n] = curL; /* store width, THEN ramp (sub_77AD0) */ + t->widthR[n] = curR; + if (transitL > 0) { curL += deltaL; transitL--; } + if (transitR > 0) { curR += deltaR; transitR--; } + n++; + } + TA += half_a; /* command-end half-step */ + } + /* the walk over-produces by 1 (a wrap seg the game doesn't count: + * w_NumTrackSegs = w_ActSegCount-1). Drop it so the warp uses the real N. */ + n -= 1; + t->n = n; + t->ccoff = ccoff; + + /* ---- side vectors (tseg+0x0C/0E right, +0x4C/4E left): one component = + * ((scos(startAngle)*width)>>17)<<6, matching InitTrackSegs 0x796C3/0x79735. */ + for (int i = 0; i < n; i++) { + int sa = s16(t->startAngle[i]); + int csa = scos(sa), ssa = scos(0x4000 - sa); /* cos/sin via raw t_Sinus */ + #define SIDEVEC(cosv, wid) ((int16_t)(((int16_t)((int)(cosv)*(int)(wid) >> 16) >> 1) << 6)) + t->sideRX[i] = SIDEVEC(csa, t->widthR[i]); + t->sideRY[i] = SIDEVEC(ssa, t->widthR[i]); + t->sideLX[i] = SIDEVEC(csa, t->widthL[i]); + t->sideLY[i] = SIDEVEC(ssa, t->widthL[i]); + #undef SIDEVEC + } + + /* ---- exact integer position (t_Sinus accumulation) + linear end-warp ---- */ + int32_t WX = t->hdr_X << 3, WY = t->hdr_Y << 3; + int32_t *rx8 = t->rawX8, *ry8 = t->rawY8; + for (int i = 0; i < n; i++) { + int h = t->fAngle[i] >> 16; + rx8[i] = WX; ry8[i] = WY; + WX += (scos(h) * 0x400) >> 14; /* X step = cos(h)*128 */ + WY += (scos(0x4000-h) * 0x400) >> 14; /* Y step = sin(h)*128 */ + } + int32_t gx8 = WX - rx8[0], gy8 = WY - ry8[0]; /* closure gap (1/8 units) */ + t->gapX8 = gx8; t->gapY8 = gy8; + /* EXACT integer warp (sub_0_76D39): a Bresenham gap-distributor. Verbatim + * behaviour: the routine SKIPS segment 0 (loop entry jumps to edi+=0x6C first), + * so segment i has accumulated |gap| exactly i times -> correction = + * sign(gap)*floor(i*|gap|/M), with M = w_ActSegCount = n+1 (the over-produced + * wrap seg the game counts but we drop). Validated bit-exact (full 1/8-unit incl + * sub-bits) vs the BESTLN dumps on F1CT01/02a/03/09/11/16. Subtract to close the + * loop. Earlier float `gap*i/n` was wrong on both M (n vs n+1) and the truncate- + * vs-floor of the negative quotient -> ~1u error that the cc-line amplified. */ + { + int64_t M = (int64_t)n + 1; + int64_t agx = gx8 < 0 ? -(int64_t)gx8 : gx8; + int64_t agy = gy8 < 0 ? -(int64_t)gy8 : gy8; + for (int i = 0; i < n; i++) { + int32_t sx = (int32_t)((agx * i) / M); /* floor, operand >= 0 */ + int32_t sy = (int32_t)((agy * i) / M); + t->X8[i] = rx8[i] - (gx8 < 0 ? -sx : sx); + t->Y8[i] = ry8[i] - (gy8 < 0 ? -sy : sy); + } + } + (void)hdr_climb; + return 0; +} + +/* File wrapper: read the whole .dat into a buffer, then compile from it. */ +int gp2cc_compile_geometry(const char *dat_path, gp2cc_track *t) { + FILE *f = fopen(dat_path, "rb"); + if (!f) return -1; + fseek(f, 0, SEEK_END); long sz = ftell(f); fseek(f, 0, SEEK_SET); + uint8_t *d = (uint8_t*)malloc(sz); + if (!d) { fclose(f); return -1; } + if (fread(d, 1, sz, f) != (size_t)sz) { fclose(f); free(d); return -1; } + fclose(f); + int rc = gp2cc_compile_geometry_buf(d, (int)sz, t); + free(d); + return rc; +} + +/* ======================================================================== * + * cc-line (racing line) — verbatim asm->C port of UACalcBestLine (0x78EB2) + * and its helper set. The cc-line is a chaotic feedback system; every step + * is transcribed instruction-faithfully so it is bit-exact by construction. + * IDA addrs cited per routine; disasm in new-export-from-selection-annotated.lst. + * ======================================================================== */ + +#define ONE60 (((int64_t)1) << 60) +#define CBB0C 20861 /* data const @0xCBB0C, segment-frame θ' tilt */ + +static int abs16(int v) { v = s16(v); return v < 0 ? -v : v; } + +/* sub_10240 (0x10240): integer sqrt of a 32-bit value via a FIXED 3-iteration + * 16-bit Newton (NOT exact floor — must be replicated bit-for-bit). */ +static uint32_t sqrt32_10240(uint32_t V) { + if (V == 0) return 0; + int shift = 0; + while (V < 0x40000000u) { shift++; V <<= 2; if (V == 0) return 0; } + uint32_t half = V >> 1; /* bx:cx */ + uint32_t di = ((V >> 17) + 0x8000) & 0xFFFF; /* 16-bit seed */ + for (int it = 0; it < 3; it++) { + uint32_t hi = half >> 16, q; + if (hi >= di) q = half & 0xFFFF; /* overflow guard (div skipped) */ + else q = half / di; /* 32/16 -> 16-bit */ + di = ((di >> 1) + q) & 0xFFFF; + } + return (di >> shift) & 0xFFFF; +} + +/* sub_102F0 (0x102F0): integer sqrt of a 64-bit value via a FIXED 5-iteration + * Newton (exact division inside, but fixed iters -> lands floor±1; NOT math.isqrt). + * Bit-exactness of the whole cc-line hinges on this matching the game exactly. */ +static uint32_t sqrt64_102F0(uint64_t V) { + if (V == 0) return 0; + if ((uint32_t)(V >> 32) == 0) return sqrt32_10240((uint32_t)V); + int shift = 0; + while ((V >> 32) < 0x40000000ull) { shift++; V <<= 2; } + uint64_t half = V >> 1; + uint32_t est = (uint32_t)((half >> 32) + 0x80000000ull); /* 32-bit seed */ + for (int it = 0; it < 5; it++) { + uint32_t q = (uint32_t)(half / est); /* exact unsigned div */ + est = (est >> 1) + q; /* 32-bit, wraps as asm */ + } + return est >> shift; +} + +/* sub_10502 (0x10502): cos(|angle|) in Q30 (amp 0x40000000) via the cosine + * table with high-precision interp. result32 = (COS[widx]<<16)+(delta*frac)<<13. + * Sign is carried by the extended COS table (negative for widx>2048). */ +static int32_t cos30(int angle) { + int a = abs16(angle); + int widx = a >> 3, frac = a & 7; + int base = COS[widx]; + int delta = COS[widx + 1] - COS[widx]; + return ((int32_t)base << 16) + ((int32_t)(delta * frac) << 13); +} + +/* sub_7827D (0x7827D): Q30 cos/sin of `angle`. Larger-magnitude component via + * sqrt(ONE60-other^2), smaller via the cos table; decided by coarse |sin|>=0x2000. + * Signs re-applied from the coarse table values. */ +static void q30_cossin(int angle, int32_t *pcos, int32_t *psin) { + int a = s16(angle); + int coarse_sin = COS[abs16(s16(0x4000 - a)) >> 3]; /* COS[..]=sin(a) coarse */ + int coarse_sin_abs = coarse_sin < 0 ? -coarse_sin : coarse_sin; + int32_t c30, s30; + if (coarse_sin_abs >= 0x2000) { /* sin large: cos via table, sin via sqrt */ + c30 = cos30(a); + int64_t smag = (int64_t)sqrt64_102F0((uint64_t)(ONE60 - (int64_t)c30 * c30)); + s30 = coarse_sin < 0 ? (int32_t)(-smag) : (int32_t)smag; + } else { /* cos large: sin via table, cos via sqrt */ + s30 = cos30(s16(0x4000 - a)); + int64_t cmag = (int64_t)sqrt64_102F0((uint64_t)(ONE60 - (int64_t)s30 * s30)); + int coarse_cos = COS[abs16(a) >> 3]; + c30 = coarse_cos < 0 ? (int32_t)(-cmag) : (int32_t)cmag; + } + *pcos = c30; *psin = s30; +} + +/* sub_10798 (0x10798): atan2(y,x) in 0x10000 units (full circle). 16-bit args. */ +static int atan2_u(int y, int x) { + y = s16(y); x = s16(x); + int dy = y < 0 ? -y : y; + int dx = x < 0 ? -x : x; + int signs_differ = (y < 0) != (x < 0); + int a; + if (dy >= dx) { + if (dy == 0) return 0; /* both zero */ + int idx = (dx << 11) / dy; /* <=2048 since dx<=dy */ + a = 0x4000 - ATAN1[idx]; + } else { + int idx = (dy << 11) / dx; + a = ATAN1[idx]; + } + if (signs_differ) a = -a; + if (x < 0) a += 0x8000; + return s16(a); +} + +/* sub_78492 (0x78492): build the world arc-centre point W=(C94BC,C94C0) for the + * first segment of a command, from (seg, P, H, arg2, arg1mul4). 1/8 world units. + * latP = (P*f14)>>15 ; base = rotate(P,latP) by PLAIN segAngle + segCentre + * + arg1mul4 along H ; + |arg2| along perp(H) (Q30 arc offset) when arg2!=0. */ +static void build_centre(const gp2cc_ccgeo *g, int idx, int P, int H, + int32_t arg2, int arg1mul4, int32_t *pWx, int32_t *pWy) { + int seg = idx; + int f14 = (int)g->f14[seg]; + int latP = (int)(((int32_t)P * f14) >> 15); /* (P*f14)<<2 then >>16 then >>1 */ + int ang = s16(g->segAngle[seg]); + int cosA = gp2cc_gv(ang); /* gv(segAngle) */ + int sinA = gp2cc_gv(s16(0x4000 - ang)); /* gv(0x4000-seg) */ + /* RX = P*cos + latP*sin ; RY = latP*cos - P*sin (each >>14, s16) */ + int32_t RX = (int16_t)(((int32_t)P * cosA + (int32_t)latP * sinA) >> 14); + int32_t RY = (int16_t)(((int32_t)latP * cosA - (int32_t)P * sinA) >> 14); + RX += g->cx8[seg]; + RY += g->cy8[seg]; + /* arg1mul4 component along H: gv(0x4000-H)=sin(H), gv(H)=cos(H) */ + int sinH = gp2cc_gv(s16(0x4000 - H)); + int cosH = gp2cc_gv(s16(H)); + int16_t a1m4 = (int16_t)arg1mul4; + RX += (int16_t)(((int32_t)sinH * a1m4) >> 14); + RY += (int16_t)(((int32_t)cosH * a1m4) >> 14); + int32_t Wx = RX, Wy = RY; + if (arg2 != 0) { + int32_t mag = arg2 < 0 ? -arg2 : arg2; + int perp = (arg2 < 0) ? s16(H - 0x4000) : s16(H + 0x4000); + int32_t c32, s32; q30_cossin(perp, &c32, &s32); + Wx += (int32_t)(((int64_t)s32 * mag) >> 30); /* x gets sin */ + Wy += (int32_t)(((int64_t)c32 * mag) >> 30); /* y gets cos */ + } + *pWx = Wx; *pWy = Wy; +} + +/* sub_787E7 (0x787E7): reproject world point W onto seg `idx` -> P (and H on + * curves). Uses the θ' tilt and reads the sub-bit-exact segment centre. */ +static void reproject(const gp2cc_ccgeo *g, int idx, int32_t Wx, int32_t Wy, + int32_t arg2, int *pP, int *pH) { + int seg = idx; + int f14 = (int)g->f14[seg]; + int ang = s16(g->segAngle[seg]); + /* θ' = segAngle - ((f14/2 * CBB0C)>>15) */ + int thetaP = s16(ang - (int)((((int32_t)(f14 >> 1) * CBB0C) << 1) >> 16)); + int32_t relx = Wx - g->cx8[seg]; + int32_t rely = Wy - g->cy8[seg]; + int32_t c32, s32; q30_cossin(thetaP, &c32, &s32); + /* lat = cos*relx - sin*rely ; lon = sin*relx + cos*rely (Q30, >>30) */ + int64_t lat64 = (int64_t)c32 * relx - (int64_t)s32 * rely; + int64_t lon64 = (int64_t)s32 * relx + (int64_t)c32 * rely; + int32_t lat = (int32_t)(lat64 >> 30); + int32_t lon = (int32_t)(lon64 >> 30); + if (arg2 == 0) { + /* straight (loc_78A54): P = lat - lon*tan(H-θ'). The asm uses sub_10502 + * (raw cos table, NOT the sqrt-normalised q30 pair): sn=cos30(0x4000-d), + * cs=cos30(d); tanterm = (sn*lon)/cs (signed 64-bit divide). H unchanged. */ + int d = s16(*pH - thetaP); + int32_t sn = cos30(s16(0x4000 - d)); /* sub_10502(0x4000-d) = sin(d) */ + int32_t cs = cos30(d); /* sub_10502(d) = cos(d) */ + int32_t tanterm = 0; + if (cs != 0) tanterm = (int32_t)(((int64_t)sn * lon) / cs); + *pP = s16(lat - tanterm); + return; + } + int64_t a2 = arg2; + int64_t disc = a2 * a2 - (int64_t)lon * lon; + int64_t T = (int64_t)sqrt64_102F0((uint64_t)disc); + int64_t Tsigned = (arg2 < 0) ? -T : T; + *pP = s16((int32_t)(lat - (int32_t)Tsigned)); + /* heading: scale (Tsigned, lon, |arg2|) down until |arg2|<0x7F00, then atan2 */ + int32_t Ts = (int32_t)Tsigned, lonv = lon; + uint32_t aa = (uint32_t)(arg2 < 0 ? -arg2 : arg2); + while (((aa >> 16) > 0) || ((aa >> 16) == 0 && (aa & 0xFFFF) >= 0x7F00)) { + Ts >>= 1; lonv >>= 1; aa >>= 1; + } + int at = atan2_u((int)(int16_t)(Ts & 0xFFFF), (int)(int16_t)(lonv & 0xFFFF)); + at = (arg2 < 0) ? s16(at + 0x4000) : s16(at - 0x4000); + *pH = s16(at + thetaP); +} + +/* sub_78CEC (0x78CEC): per-seg curvature-ramp nudge of W (slope!=0 commands). + * a = H ± 0x4000 (sign by arg2) ; C94BC += sin(a)*slope>>14, C94C0 += cos(a)*slope>>14. */ +static void nudge(int32_t *pWx, int32_t *pWy, int H, int32_t arg2, int32_t slope) { + int32_t ebp = slope; + int a; + if (arg2 < 0) { ebp = -ebp; a = s16(H - 0x4000); } + else a = s16(H + 0x4000); + int sinA = gp2cc_gv(s16(0x4000 - a)); + int cosA = gp2cc_gv(s16(a)); + *pWx += (int32_t)(((int64_t)sinA * ebp) >> 14); + *pWy += (int32_t)(((int64_t)cosA * ebp) >> 14); +} + +/* GetSingleCmdArgs (0x78D4C): parse one cc command from cc[*po]. */ +typedef struct { + int end; /* word==0 (last command) */ + int word; /* the command word (bp) */ + int N; /* word & 0x7FF (wActCCLCookedCmd) */ + int a1, a2; /* wCCL1stCmdArg1/2 (bit15 header words) */ + int32_t arg2; /* dActCCLineArg2 (×8 unless bit12, dword if bit14) */ + int32_t slope; /* dArg3mArg2divLen = (arg3-arg2)/N */ + int arg1mul4; /* wActCCLArg1mul4 (arg1<<2 when arg2!=0) */ + int arg1if2is0; /* wActCLArg1If2is0 (arg1 when arg2==0) */ +} cc_cmd; + +static int rdw(const uint8_t *d, int o) { return d[o] | (d[o + 1] << 8); } + +static void parse_cmd(const uint8_t *cc, int *po, cc_cmd *c) { + int o = *po; + memset(c, 0, sizeof *c); + int word = rdw(cc, o); o += 2; + c->word = word; + c->N = word & 0x7FF; + if (word == 0) { c->end = 1; *po = o; return; } + if (word & 0x8000) { c->a1 = rdw(cc, o); o += 2; c->a2 = rdw(cc, o); o += 2; } + int cx = rdw(cc, o); o += 2; /* arg1 */ + int32_t arg2 = (int32_t)(int16_t)rdw(cc, o); o += 2; + if (word & 0x4000) { arg2 = (int32_t)((uint32_t)arg2 << 16) | rdw(cc, o); o += 2; } + if (!(word & 0x1000)) arg2 <<= 3; + c->arg2 = arg2; + if (word & 0x2000) { /* arg3 → slope */ + int32_t arg3 = (int32_t)(int16_t)rdw(cc, o); o += 2; + if (word & 0x4000) { arg3 = (int32_t)((uint32_t)arg3 << 16) | rdw(cc, o); o += 2; } + if (!(word & 0x1000)) arg3 <<= 3; + int32_t diff = arg3 - arg2; + c->slope = diff / c->N; /* idiv (trunc toward zero) */ + } + if (arg2 != 0) { c->arg1if2is0 = 0; c->arg1mul4 = (int16_t)(cx << 2); } + else { c->arg1mul4 = 0; c->arg1if2is0 = cx; } + *po = o; +} + +int gp2cc_reproject_dbg(int segAngle, int32_t cx8, int32_t cy8, int f14, + int32_t Wx, int32_t Wy, int32_t arg2, int Hin, int *pH) { + gp2cc_ccgeo g; g.n = 1; + g.segAngle[0] = (int16_t)segAngle; g.cx8[0] = cx8; g.cy8[0] = cy8; g.f14[0] = f14; + int P = 0, H = Hin; + reproject(&g, 0, Wx, Wy, arg2, &P, &H); + if (pH) *pH = H; + return P; +} + +/* Front half of sub_787E7 exposed for stage validation: θ', q30 cos/sin, lat, lon. */ +void gp2cc_reproject_stages(int segAngle, int32_t cx8, int32_t cy8, int f14, + int32_t Wx, int32_t Wy, + int32_t *pCos32, int32_t *pSin32, + int32_t *pLat, int32_t *pLon, int *pTheta) { + int ang = s16(segAngle); + int thetaP = s16(ang - (int)((((int32_t)(f14 >> 1) * CBB0C) << 1) >> 16)); + int32_t relx = Wx - cx8; + int32_t rely = Wy - cy8; + int32_t c32, s32; q30_cossin(thetaP, &c32, &s32); + int64_t lat64 = (int64_t)c32 * relx - (int64_t)s32 * rely; + int64_t lon64 = (int64_t)s32 * relx + (int64_t)c32 * rely; + if (pCos32) *pCos32 = c32; + if (pSin32) *pSin32 = s32; + if (pLat) *pLat = (int32_t)(lat64 >> 30); + if (pLon) *pLon = (int32_t)(lon64 >> 30); + if (pTheta) *pTheta = thetaP; +} + +void gp2cc_build_dbg(int segAngle, int32_t cx8, int32_t cy8, int f14, + int P, int H, int32_t arg2, int arg1mul4, + int32_t *pWx, int32_t *pWy) { + gp2cc_ccgeo g; g.n = 1; + g.segAngle[0] = (int16_t)segAngle; g.cx8[0] = cx8; g.cy8[0] = cy8; g.f14[0] = f14; + build_centre(&g, 0, P, H, arg2, arg1mul4, pWx, pWy); +} + +int gp2cc_compute_ccline_dbg(const gp2cc_ccgeo *g, const uint8_t *cc, int cc_len, + int cc_off, int16_t *bestLine, int16_t *angle18, + int32_t *capWx, int32_t *capWy, int32_t *capArg2, + int *cmdStartSeg, int *pNumCmds) { + int n = g->n; + if (n <= 0) return -1; + int cur = 0, cmdIdx = 0; + /* seed (0x78EB2): H = segAngle[0]; P = ccdata[cc_off+2] if bit15 && !bit11 */ + int H = s16(g->segAngle[0]); + int P = 0; + int w0 = rdw(cc, cc_off); + /* seed segPosX_0A (=P) is a SIGNED 16-bit word ([ebx+2], used by a signed + * imul in sub_78492); sign-extend or build_centre blows up on negative seeds. */ + if (!(w0 & 0x800) && (w0 & 0x8000)) P = s16(rdw(cc, cc_off + 2)); + int o = cc_off; + for (;;) { + cc_cmd c; + parse_cmd(cc, &o, &c); + if (c.end) break; + if (o > cc_len) return -2; + if ((c.word & 0x800) && (c.word & 0x8000)) { P = s16(c.a1); H = s16(c.a2); } + if (c.arg2 == 0) H = s16(H + s16(c.arg1if2is0)); + if (cmdStartSeg) cmdStartSeg[cmdIdx] = cur; /* this command's first segment */ + cmdIdx++; + int32_t Wx, Wy; + build_centre(g, cur, P, H, c.arg2, c.arg1mul4, &Wx, &Wy); + int32_t arg2 = c.arg2; + int left = c.N; + while (left > 0) { + angle18[cur] = (int16_t)s16(H - s16(g->segAngle[cur])); + bestLine[cur] = (int16_t)s16(P); + if (capWx) capWx[cur] = Wx; + if (capWy) capWy[cur] = Wy; + if (capArg2) capArg2[cur] = arg2; + cur = (cur + 1) % n; + reproject(g, cur, Wx, Wy, arg2, &P, &H); + left--; + if (left == 0) break; + if (arg2 != 0 && c.slope != 0) { + arg2 += c.slope; + nudge(&Wx, &Wy, H, arg2, c.slope); + } + } + } + if (pNumCmds) *pNumCmds = cmdIdx; + return 0; +} + +int gp2cc_compute_ccline(const gp2cc_ccgeo *g, const uint8_t *cc, int cc_len, + int cc_off, int16_t *bestLine, int16_t *angle18, + int *cmdStartSeg, int *pNumCmds) { + return gp2cc_compute_ccline_dbg(g, cc, cc_len, cc_off, bestLine, angle18, + 0, 0, 0, cmdStartSeg, pNumCmds); +} diff --git a/gp2cc.h b/gp2cc.h new file mode 100644 index 0000000..c3f7aeb --- /dev/null +++ b/gp2cc.h @@ -0,0 +1,113 @@ +/* + * gp2cc — standalone bit-exact reproduction of GP2's compiled track geometry + * and cc-line (racing line), ported verbatim from the disassembly. + * + * Goal: given a track .dat (and the t_Sinus table from GP2.EXE), reproduce the + * game's per-segment compiled geometry + cc-line bit-for-bit, validated against + * the GP2Lap BESTLNN.TXT dumps. The editor's new compiled-track view will call + * this module and just draw the arrays — it never touches the fixed point. + * + * Status: geometry compiler ported + validated (heading/startAngle exact, + * position via integer t_Sinus accumulation + warp). cc-line (build/reproject/ + * nudge + the Q30/atan2 helpers) to follow. + */ +#ifndef GP2CC_H +#define GP2CC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define GP2CC_MAXSEG 4096 + +typedef struct { + int n; /* number of track segments */ + int32_t fAngle[GP2CC_MAXSEG]; /* d_TrkStrtAngle per seg, Q16 (full precision) */ + int16_t angle[GP2CC_MAXSEG]; /* heading = fAngle>>16 (tseg+0) */ + int16_t startAngle[GP2CC_MAXSEG]; /* d_StartAngle>>16 (tseg+0xA, side-vec) */ + int32_t f14[GP2CC_MAXSEG]; /* tseg+0x14 = (cmd d1 * 0x6488)>>14 (θ' tilt) */ + int32_t widthL[GP2CC_MAXSEG]; /* tseg+0x60 left road width (w_TrkBeginWidth) */ + int32_t widthR[GP2CC_MAXSEG]; /* tseg+0x62 right road width (w_TrkBeginWidth2) */ + /* side vectors (tseg+0x4C/0x4E left, +0x0C/0x0E right): edge = centre ± side/64. + * side = ((scos(startAngle)*width)>>17)<<6 components. (sideRX/RY validated == dump xSide/ySide.) */ + int16_t sideLX[GP2CC_MAXSEG], sideLY[GP2CC_MAXSEG]; + int16_t sideRX[GP2CC_MAXSEG], sideRY[GP2CC_MAXSEG]; + /* exact 1/8-world-unit position (the game's w_TrkStrtX/Y, warped) */ + int32_t X8[GP2CC_MAXSEG]; /* world X (= dump col4) * 8, warped */ + int32_t Y8[GP2CC_MAXSEG]; /* world Y (= dump col3) * 8, warped */ + int32_t rawX8[GP2CC_MAXSEG]; /* world X * 8, BEFORE warp (raw integration) */ + int32_t rawY8[GP2CC_MAXSEG]; /* world Y * 8, BEFORE warp */ + int32_t gapX8, gapY8; /* raw closure gap (rawEnd - start), 1/8 units */ + int ccoff; /* file offset of the cc-line command stream */ + /* header */ + int16_t hdr_angle, hdr_X, hdr_Y; + uint16_t hdr_width, hdr_c5; +} gp2cc_track; + +/* Per-segment geometry the cc-line compiler consumes. The cc-line internal + * convention pairs C94BC with tseg+4 (=dump col3 =world Y) and C94C0 with tseg+8 + * (=dump col4 =world X), so cx8/cy8 follow that pairing (NOT the X8/Y8 naming). */ +typedef struct { + int n; + int16_t segAngle[GP2CC_MAXSEG]; /* tseg+0 heading */ + int32_t cx8[GP2CC_MAXSEG]; /* tseg+4 full 1/8-unit (C94BC-paired) */ + int32_t cy8[GP2CC_MAXSEG]; /* tseg+8 full 1/8-unit (C94C0-paired) */ + int32_t f14[GP2CC_MAXSEG]; /* tseg+0x14 (width-derived, drives θ' tilt) */ +} gp2cc_ccgeo; + +/* Load the t_Sinus cosine table + t_ArithTab1 atan table from GP2.EXE + * (file offs 0x1DDAEC / 0x1DFAF0). Returns 0 ok. */ +int gp2cc_load_tables(const char *gp2exe_path); + +/* GetSinusVal (0x104B9): cosine, amplitude 0x4000, 8-unit table + lerp. */ +int gp2cc_gv(int ax); + +/* Compile the track geometry from a .dat file into `t`. Returns 0 on success. */ +int gp2cc_compile_geometry(const char *dat_path, gp2cc_track *t); + +/* Same, from a .dat image already in memory (caller owns `dat`). For the editor, + * which serializes its in-memory track to a .dat buffer. Returns 0 on success. */ +int gp2cc_compile_geometry_buf(const uint8_t *dat, int len, gp2cc_track *t); + +/* Run UACalcBestLine (0x78EB2) over the cc-command stream `cc` (the raw .dat + * bytes) starting at byte offset `cc_off`, using geometry `g`. Writes per-seg + * bestLine (tseg+0x16) and angle18 (tseg+0x18). Returns 0 on success. + * Verbatim asm->C port of the cc-line helpers (bit-exact by construction). */ +/* Run the cc-line. `cmdStartSeg` (NULL ok) is filled with each cc-command's first + * segment index (cmdStartSeg[0..*pNumCmds-1]); the editor's CCLineSections map 1:1 + * to cc-commands in order, so this places the per-sector indicators. */ +int gp2cc_compute_ccline(const gp2cc_ccgeo *g, const uint8_t *cc, int cc_len, + int cc_off, int16_t *bestLine, int16_t *angle18, + int *cmdStartSeg, int *pNumCmds); + +/* Same, but also captures the carried world point W (=C94BC/C94C0) and arg2 + * per segment (for stage-by-stage validation vs CCREP). Any out ptr may be NULL. */ +int gp2cc_compute_ccline_dbg(const gp2cc_ccgeo *g, const uint8_t *cc, int cc_len, + int cc_off, int16_t *bestLine, int16_t *angle18, + int32_t *capWx, int32_t *capWy, int32_t *capArg2, + int *cmdStartSeg, int *pNumCmds); + +/* Debug: reproject a SINGLE world point W onto one segment (isolates sub_787E7 + * from the carried-W feedback). Returns P; writes *pH (curve heading). */ +int gp2cc_reproject_dbg(int segAngle, int32_t cx8, int32_t cy8, int f14, + int32_t Wx, int32_t Wy, int32_t arg2, int Hin, int *pH); + +/* Debug: build the world point W for one segment (isolates sub_78492). */ +void gp2cc_build_dbg(int segAngle, int32_t cx8, int32_t cy8, int f14, + int P, int H, int32_t arg2, int arg1mul4, + int32_t *pWx, int32_t *pWy); + +/* Debug: expose the reproject intermediates (q30 cos/sin of θ', and lat/lon) + * for stage-by-stage validation against the extended CCREP dump. */ +void gp2cc_reproject_stages(int segAngle, int32_t cx8, int32_t cy8, int f14, + int32_t Wx, int32_t Wy, + int32_t *pCos32, int32_t *pSin32, + int32_t *pLat, int32_t *pLon, int *pTheta); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gp2cc_tables.c b/gp2cc_tables.c new file mode 100644 index 0000000..8f84007 --- /dev/null +++ b/gp2cc_tables.c @@ -0,0 +1,404 @@ +/* gp2cc_tables.c — GENERATED from GP2.EXE. The game's exact trig tables + * embedded so gp2cc needs no GP2.EXE at runtime (e.g. inside the editor). + * gp2cc_COS = t_Sinus (file 0x1DDAEC), amp 0x4000, 4200 entries + * gp2cc_ATAN1 = t_ArithTab1 (file 0x1DFAF0), 2050 entries + * Regenerate: extract those two ranges from GP2.EXE. */ + +const short gp2cc_COS[4200] = { + 16384,16384,16384,16384,16384,16384,16384,16384,16384,16384,16384,16383,16383,16383,16383,16383, + 16383,16383,16382,16382,16382,16382,16382,16381,16381,16381,16381,16380,16380,16380,16380,16379, + 16379,16379,16378,16378,16378,16377,16377,16377,16376,16376,16375,16375,16375,16374,16374,16373, + 16373,16372,16372,16371,16371,16370,16370,16369,16369,16368,16368,16367,16367,16366,16365,16365, + 16364,16364,16363,16362,16362,16361,16360,16360,16359,16358,16358,16357,16356,16355,16355,16354, + 16353,16352,16352,16351,16350,16349,16348,16348,16347,16346,16345,16344,16343,16342,16341,16341, + 16340,16339,16338,16337,16336,16335,16334,16333,16332,16331,16330,16329,16328,16327,16326,16325, + 16324,16323,16321,16320,16319,16318,16317,16316,16315,16313,16312,16311,16310,16309,16308,16306, + 16305,16304,16303,16301,16300,16299,16298,16296,16295,16294,16292,16291,16290,16288,16287,16286, + 16284,16283,16281,16280,16279,16277,16276,16274,16273,16271,16270,16268,16267,16265,16264,16262, + 16261,16259,16258,16256,16255,16253,16251,16250,16248,16247,16245,16243,16242,16240,16238,16237, + 16235,16233,16232,16230,16228,16226,16225,16223,16221,16219,16218,16216,16214,16212,16210,16209, + 16207,16205,16203,16201,16199,16197,16195,16194,16192,16190,16188,16186,16184,16182,16180,16178, + 16176,16174,16172,16170,16168,16166,16164,16162,16160,16158,16156,16153,16151,16149,16147,16145, + 16143,16141,16138,16136,16134,16132,16130,16128,16125,16123,16121,16119,16116,16114,16112,16109, + 16107,16105,16103,16100,16098,16096,16093,16091,16088,16086,16084,16081,16079,16076,16074,16072, + 16069,16067,16064,16062,16059,16057,16054,16052,16049,16047,16044,16042,16039,16037,16034,16031, + 16029,16026,16024,16021,16018,16016,16013,16010,16008,16005,16002,16000,15997,15994,15991,15989, + 15986,15983,15980,15978,15975,15972,15969,15966,15964,15961,15958,15955,15952,15949,15946,15944, + 15941,15938,15935,15932,15929,15926,15923,15920,15917,15914,15911,15908,15905,15902,15899,15896, + 15893,15890,15887,15884,15881,15878,15875,15871,15868,15865,15862,15859,15856,15853,15849,15846, + 15843,15840,15837,15833,15830,15827,15824,15820,15817,15814,15810,15807,15804,15801,15797,15794, + 15791,15787,15784,15780,15777,15774,15770,15767,15763,15760,15757,15753,15750,15746,15743,15739, + 15736,15732,15729,15725,15722,15718,15715,15711,15707,15704,15700,15697,15693,15689,15686,15682, + 15679,15675,15671,15668,15664,15660,15656,15653,15649,15645,15642,15638,15634,15630,15627,15623, + 15619,15615,15611,15608,15604,15600,15596,15592,15588,15584,15581,15577,15573,15569,15565,15561, + 15557,15553,15549,15545,15541,15537,15533,15529,15525,15521,15517,15513,15509,15505,15501,15497, + 15493,15489,15485,15481,15476,15472,15468,15464,15460,15456,15451,15447,15443,15439,15435,15430, + 15426,15422,15418,15414,15409,15405,15401,15396,15392,15388,15383,15379,15375,15370,15366,15362, + 15357,15353,15349,15344,15340,15335,15331,15326,15322,15318,15313,15309,15304,15300,15295,15291, + 15286,15282,15277,15273,15268,15263,15259,15254,15250,15245,15240,15236,15231,15227,15222,15217, + 15213,15208,15203,15199,15194,15189,15184,15180,15175,15170,15166,15161,15156,15151,15146,15142, + 15137,15132,15127,15122,15118,15113,15108,15103,15098,15093,15088,15083,15078,15074,15069,15064, + 15059,15054,15049,15044,15039,15034,15029,15024,15019,15014,15009,15004,14999,14994,14989,14984, + 14978,14973,14968,14963,14958,14953,14948,14943,14937,14932,14927,14922,14917,14911,14906,14901, + 14896,14891,14885,14880,14875,14870,14864,14859,14854,14848,14843,14838,14832,14827,14822,14816, + 14811,14806,14800,14795,14789,14784,14779,14773,14768,14762,14757,14751,14746,14740,14735,14729, + 14724,14718,14713,14707,14702,14696,14691,14685,14680,14674,14668,14663,14657,14651,14646,14640, + 14635,14629,14623,14618,14612,14606,14601,14595,14589,14583,14578,14572,14566,14560,14555,14549, + 14543,14537,14531,14526,14520,14514,14508,14502,14497,14491,14485,14479,14473,14467,14461,14455, + 14449,14443,14438,14432,14426,14420,14414,14408,14402,14396,14390,14384,14378,14372,14366,14360, + 14354,14347,14341,14335,14329,14323,14317,14311,14305,14299,14293,14286,14280,14274,14268,14262, + 14256,14249,14243,14237,14231,14224,14218,14212,14206,14199,14193,14187,14181,14174,14168,14162, + 14155,14149,14143,14136,14130,14124,14117,14111,14104,14098,14092,14085,14079,14072,14066,14059, + 14053,14047,14040,14034,14027,14021,14014,14008,14001,13995,13988,13981,13975,13968,13962,13955, + 13949,13942,13935,13929,13922,13916,13909,13902,13896,13889,13882,13876,13869,13862,13856,13849, + 13842,13835,13829,13822,13815,13808,13802,13795,13788,13781,13774,13768,13761,13754,13747,13740, + 13733,13727,13720,13713,13706,13699,13692,13685,13678,13671,13665,13658,13651,13644,13637,13630, + 13623,13616,13609,13602,13595,13588,13581,13574,13567,13560,13553,13546,13538,13531,13524,13517, + 13510,13503,13496,13489,13482,13474,13467,13460,13453,13446,13439,13431,13424,13417,13410,13403, + 13395,13388,13381,13374,13366,13359,13352,13344,13337,13330,13323,13315,13308,13301,13293,13286, + 13279,13271,13264,13256,13249,13242,13234,13227,13219,13212,13205,13197,13190,13182,13175,13167, + 13160,13152,13145,13137,13130,13122,13115,13107,13100,13092,13085,13077,13069,13062,13054,13047, + 13039,13031,13024,13016,13008,13001,12993,12986,12978,12970,12963,12955,12947,12939,12932,12924, + 12916,12909,12901,12893,12885,12878,12870,12862,12854,12846,12839,12831,12823,12815,12807,12799, + 12792,12784,12776,12768,12760,12752,12744,12736,12729,12721,12713,12705,12697,12689,12681,12673, + 12665,12657,12649,12641,12633,12625,12617,12609,12601,12593,12585,12577,12569,12561,12553,12545, + 12537,12528,12520,12512,12504,12496,12488,12480,12472,12463,12455,12447,12439,12431,12423,12414, + 12406,12398,12390,12381,12373,12365,12357,12348,12340,12332,12324,12315,12307,12299,12290,12282, + 12274,12266,12257,12249,12240,12232,12224,12215,12207,12199,12190,12182,12173,12165,12157,12148, + 12140,12131,12123,12114,12106,12097,12089,12080,12072,12064,12055,12046,12038,12029,12021,12012, + 12004,11995,11987,11978,11970,11961,11952,11944,11935,11927,11918,11909,11901,11892,11883,11875, + 11866,11857,11849,11840,11831,11823,11814,11805,11797,11788,11779,11770,11762,11753,11744,11735, + 11727,11718,11709,11700,11691,11683,11674,11665,11656,11647,11638,11630,11621,11612,11603,11594, + 11585,11576,11567,11559,11550,11541,11532,11523,11514,11505,11496,11487,11478,11469,11460,11451, + 11442,11433,11424,11415,11406,11397,11388,11379,11370,11361,11352,11343,11334,11325,11316,11307, + 11297,11288,11279,11270,11261,11252,11243,11234,11224,11215,11206,11197,11188,11179,11169,11160, + 11151,11142,11133,11123,11114,11105,11096,11086,11077,11068,11059,11049,11040,11031,11021,11012, + 11003,10994,10984,10975,10966,10956,10947,10937,10928,10919,10909,10900,10891,10881,10872,10862, + 10853,10844,10834,10825,10815,10806,10796,10787,10778,10768,10759,10749,10740,10730,10721,10711, + 10702,10692,10683,10673,10663,10654,10644,10635,10625,10616,10606,10597,10587,10577,10568,10558, + 10549,10539,10529,10520,10510,10500,10491,10481,10471,10462,10452,10442,10433,10423,10413,10404, + 10394,10384,10374,10365,10355,10345,10336,10326,10316,10306,10296,10287,10277,10267,10257,10248, + 10238,10228,10218,10208,10198,10189,10179,10169,10159,10149,10139,10129,10120,10110,10100,10090, + 10080,10070,10060,10050,10040,10030,10020,10010,10001,9991,9981,9971,9961,9951,9941,9931, + 9921,9911,9901,9891,9881,9871,9861,9851,9841,9830,9820,9810,9800,9790,9780,9770, + 9760,9750,9740,9730,9720,9709,9699,9689,9679,9669,9659,9649,9638,9628,9618,9608, + 9598,9588,9577,9567,9557,9547,9537,9526,9516,9506,9496,9485,9475,9465,9455,9444, + 9434,9424,9413,9403,9393,9383,9372,9362,9352,9341,9331,9321,9310,9300,9290,9279, + 9269,9259,9248,9238,9227,9217,9207,9196,9186,9175,9165,9155,9144,9134,9123,9113, + 9102,9092,9082,9071,9061,9050,9040,9029,9019,9008,8998,8987,8977,8966,8956,8945, + 8935,8924,8914,8903,8892,8882,8871,8861,8850,8840,8829,8818,8808,8797,8787,8776, + 8765,8755,8744,8734,8723,8712,8702,8691,8680,8670,8659,8648,8638,8627,8616,8606, + 8595,8584,8573,8563,8552,8541,8531,8520,8509,8498,8488,8477,8466,8455,8445,8434, + 8423,8412,8401,8391,8380,8369,8358,8347,8337,8326,8315,8304,8293,8283,8272,8261, + 8250,8239,8228,8217,8207,8196,8185,8174,8163,8152,8141,8130,8119,8108,8098,8087, + 8076,8065,8054,8043,8032,8021,8010,7999,7988,7977,7966,7955,7944,7933,7922,7911, + 7900,7889,7878,7867,7856,7845,7834,7823,7812,7801,7790,7779,7768,7757,7746,7734, + 7723,7712,7701,7690,7679,7668,7657,7646,7635,7623,7612,7601,7590,7579,7568,7557, + 7545,7534,7523,7512,7501,7490,7478,7467,7456,7445,7434,7423,7411,7400,7389,7378, + 7366,7355,7344,7333,7321,7310,7299,7288,7276,7265,7254,7243,7231,7220,7209,7198, + 7186,7175,7164,7152,7141,7130,7118,7107,7096,7084,7073,7062,7050,7039,7028,7016, + 7005,6994,6982,6971,6960,6948,6937,6925,6914,6903,6891,6880,6868,6857,6846,6834, + 6823,6811,6800,6788,6777,6766,6754,6743,6731,6720,6708,6697,6685,6674,6662,6651, + 6639,6628,6616,6605,6593,6582,6570,6559,6547,6536,6524,6513,6501,6490,6478,6467, + 6455,6444,6432,6420,6409,6397,6386,6374,6363,6351,6339,6328,6316,6305,6293,6281, + 6270,6258,6247,6235,6223,6212,6200,6189,6177,6165,6154,6142,6130,6119,6107,6095, + 6084,6072,6060,6049,6037,6025,6014,6002,5990,5979,5967,5955,5943,5932,5920,5908, + 5897,5885,5873,5861,5850,5838,5826,5814,5803,5791,5779,5767,5756,5744,5732,5720, + 5708,5697,5685,5673,5661,5650,5638,5626,5614,5602,5591,5579,5567,5555,5543,5531, + 5520,5508,5496,5484,5472,5460,5449,5437,5425,5413,5401,5389,5377,5366,5354,5342, + 5330,5318,5306,5294,5282,5270,5259,5247,5235,5223,5211,5199,5187,5175,5163,5151, + 5139,5127,5115,5104,5092,5080,5068,5056,5044,5032,5020,5008,4996,4984,4972,4960, + 4948,4936,4924,4912,4900,4888,4876,4864,4852,4840,4828,4816,4804,4792,4780,4768, + 4756,4744,4732,4720,4708,4696,4684,4672,4660,4648,4636,4624,4612,4599,4587,4575, + 4563,4551,4539,4527,4515,4503,4491,4479,4467,4455,4442,4430,4418,4406,4394,4382, + 4370,4358,4346,4333,4321,4309,4297,4285,4273,4261,4249,4236,4224,4212,4200,4188, + 4176,4164,4151,4139,4127,4115,4103,4091,4078,4066,4054,4042,4030,4018,4005,3993, + 3981,3969,3957,3944,3932,3920,3908,3896,3883,3871,3859,3847,3835,3822,3810,3798, + 3786,3773,3761,3749,3737,3724,3712,3700,3688,3676,3663,3651,3639,3627,3614,3602, + 3590,3577,3565,3553,3541,3528,3516,3504,3492,3479,3467,3455,3442,3430,3418,3406, + 3393,3381,3369,3356,3344,3332,3320,3307,3295,3283,3270,3258,3246,3233,3221,3209, + 3196,3184,3172,3159,3147,3135,3122,3110,3098,3085,3073,3061,3048,3036,3024,3011, + 2999,2987,2974,2962,2949,2937,2925,2912,2900,2888,2875,2863,2851,2838,2826,2813, + 2801,2789,2776,2764,2752,2739,2727,2714,2702,2690,2677,2665,2652,2640,2628,2615, + 2603,2590,2578,2566,2553,2541,2528,2516,2503,2491,2479,2466,2454,2441,2429,2416, + 2404,2392,2379,2367,2354,2342,2329,2317,2305,2292,2280,2267,2255,2242,2230,2217, + 2205,2193,2180,2168,2155,2143,2130,2118,2105,2093,2080,2068,2055,2043,2031,2018, + 2006,1993,1981,1968,1956,1943,1931,1918,1906,1893,1881,1868,1856,1843,1831,1818, + 1806,1793,1781,1768,1756,1743,1731,1718,1706,1693,1681,1668,1656,1643,1631,1618, + 1606,1593,1581,1568,1556,1543,1531,1518,1506,1493,1481,1468,1456,1443,1431,1418, + 1406,1393,1381,1368,1356,1343,1331,1318,1306,1293,1280,1268,1255,1243,1230,1218, + 1205,1193,1180,1168,1155,1143,1130,1118,1105,1092,1080,1067,1055,1042,1030,1017, + 1005,992,980,967,955,942,929,917,904,892,879,867,854,842,829,816, + 804,791,779,766,754,741,729,716,703,691,678,666,653,641,628,616, + 603,590,578,565,553,540,528,515,503,490,477,465,452,440,427,415, + 402,390,377,364,352,339,327,314,302,289,276,264,251,239,226,214, + 201,188,176,163,151,138,126,113,101,88,75,63,50,38,25,13, + 0,-13,-25,-38,-50,-63,-75,-88,-101,-113,-126,-138,-151,-163,-176,-188, + -201,-214,-226,-239,-251,-264,-276,-289,-302,-314,-327,-339,-352,-364,-377,-390, + -402,-415,-427,-440,-452,-465,-477,-490,-503,-515,-528,-540,-553,-565,-578,-590, + -603,-616,-628,-641,-653,-666,-678,-691,-704,-716,-729,-741,-754,-766,-779,-791, + -804,-816,-829,-842,-854,-867,-879,-892,-904,-917,-929,-942,-955,-967,-980,-992, + -1005,-1017,-1030,-1042,-1055,-1067,-1080,-1092,-1105,-1118,-1130,-1143,-1155,-1168,-1180,-1193, + -1205,-1218,-1230,-1243,-1255,-1268,-1280,-1293,-1306,-1318,-1331,-1343,-1356,-1368,-1381,-1393, + -1406,-1418,-1431,-1443,-1456,-1468,-1481,-1493,-1506,-1518,-1531,-1543,-1556,-1568,-1581,-1593, + -1606,-1618,-1631,-1643,-1656,-1668,-1681,-1693,-1706,-1718,-1731,-1743,-1756,-1768,-1781,-1793, + -1806,-1818,-1831,-1843,-1856,-1868,-1881,-1893,-1906,-1918,-1931,-1943,-1956,-1968,-1981,-1993, + -2006,-2018,-2031,-2043,-2055,-2068,-2080,-2093,-2105,-2118,-2130,-2143,-2155,-2168,-2180,-2193, + -2205,-2217,-2230,-2242,-2255,-2267,-2280,-2292,-2305,-2317,-2329,-2342,-2354,-2367,-2379,-2392, + -2404,-2416,-2429,-2441,-2454,-2466,-2479,-2491,-2503,-2516,-2528,-2541,-2553,-2566,-2578,-2590, + -2603,-2615,-2628,-2640,-2652,-2665,-2677,-2690,-2702,-2714,-2727,-2739,-2752,-2764,-2776,-2789, + -2801,-2813,-2826,-2838,-2851,-2863,-2875,-2888,-2900,-2912,-2925,-2937,-2949,-2962,-2974,-2987, + -2999,-3011,-3024,-3036,-3048,-3061,-3073,-3085,-3098,-3110,-3122,-3135,-3147,-3159,-3172,-3184, + -3196,-3209,-3221,-3233,-3246,-3258,-3270,-3283,-3295,-3307,-3320,-3332,-3344,-3356,-3369,-3381, + -3393,-3406,-3418,-3430,-3442,-3455,-3467,-3479,-3492,-3504,-3516,-3528,-3541,-3553,-3565,-3577, + -3590,-3602,-3614,-3627,-3639,-3651,-3663,-3676,-3688,-3700,-3712,-3724,-3737,-3749,-3761,-3773, + -3786,-3798,-3810,-3822,-3835,-3847,-3859,-3871,-3883,-3896,-3908,-3920,-3932,-3944,-3957,-3969, + -3981,-3993,-4005,-4018,-4030,-4042,-4054,-4066,-4078,-4091,-4103,-4115,-4127,-4139,-4151,-4164, + -4176,-4188,-4200,-4212,-4224,-4236,-4249,-4261,-4273,-4285,-4297,-4309,-4321,-4333,-4346,-4358, + -4370,-4382,-4394,-4406,-4418,-4430,-4442,-4455,-4467,-4479,-4491,-4503,-4515,-4527,-4539,-4551, + -4563,-4575,-4587,-4599,-4612,-4624,-4636,-4648,-4660,-4672,-4684,-4696,-4708,-4720,-4732,-4744, + -4756,-4768,-4780,-4792,-4804,-4816,-4828,-4840,-4852,-4864,-4876,-4888,-4900,-4912,-4924,-4936, + -4948,-4960,-4972,-4984,-4996,-5008,-5020,-5032,-5044,-5056,-5068,-5080,-5092,-5104,-5115,-5127, + -5139,-5151,-5163,-5175,-5187,-5199,-5211,-5223,-5235,-5247,-5259,-5270,-5282,-5294,-5306,-5318, + -5330,-5342,-5354,-5366,-5377,-5389,-5401,-5413,-5425,-5437,-5449,-5460,-5472,-5484,-5496,-5508, + -5520,-5531,-5543,-5555,-5567,-5579,-5591,-5602,-5614,-5626,-5638,-5650,-5661,-5673,-5685,-5697, + -5708,-5720,-5732,-5744,-5756,-5767,-5779,-5791,-5803,-5814,-5826,-5838,-5850,-5861,-5873,-5885, + -5897,-5908,-5920,-5932,-5943,-5955,-5967,-5979,-5990,-6002,-6014,-6025,-6037,-6049,-6060,-6072, + -6084,-6095,-6107,-6119,-6130,-6142,-6154,-6165,-6177,-6189,-6200,-6212,-6223,-6235,-6247,-6258, + -6270,-6281,-6293,-6305,-6316,-6328,-6339,-6351,-6363,-6374,-6386,-6397,-6409,-6420,-6432,-6444, + -6455,-6467,-6478,-6490,-6501,-6513,-6524,-6536,-6547,-6559,-6570,-6582,-6593,-6605,-6616,-6628, + -6639,-6651,-6662,-6674,-6685,-6697,-6708,-6720,-6731,-6743,-6754,-6766,-6777,-6788,-6800,-6811, + -6823,-6834,-6846,-6857,-6868,-6880,-6891,-6903,-6914,-6925,-6937,-6948,-6960,-6971,-6982,-6994, + -7005,-7016,-7028,-7039,-7050,-7062,-7073,-7084,-7096,-7107,-7118,-7130,-7141,-7152,-7164,-7175, + -7186,-7198,-7209,-7220,-7231,-7243,-7254,-7265,-7276,-7288,-7299,-7310,-7321,-7333,-7344,-7355, + -7366,-7378,-7389,-7400,-7411,-7423,-7434,-7445,-7456,-7467,-7478,-7490,-7501,-7512,-7523,-7534, + -7545,-7557,-7568,-7579,-7590,-7601,-7612,-7623,-7635,-7646,-7657,-7668,-7679,-7690,-7701,-7712, + -7723,-7734,-7746,-7757,-7768,-7779,-7790,-7801,-7812,-7823,-7834,-7845,-7856,-7867,-7878,-7889, + -7900,-7911,-7922,-7933,-7944,-7955,-7966,-7977,-7988,-7999,-8010,-8021,-8032,-8043,-8054,-8065, + -8076,-8087,-8098,-8108,-8119,-8130,-8141,-8152,-8163,-8174,-8185,-8196,-8207,-8217,-8228,-8239, + -8250,-8261,-8272,-8283,-8293,-8304,-8315,-8326,-8337,-8347,-8358,-8369,-8380,-8391,-8401,-8412, + -8423,-8434,-8445,-8455,-8466,-8477,-8488,-8498,-8509,-8520,-8531,-8541,-8552,-8563,-8573,-8584, + -8595,-8606,-8616,-8627,-8638,-8648,-8659,-8670,-8680,-8691,-8702,-8712,-8723,-8734,-8744,-8755, + -8765,-8776,-8787,-8797,-8808,-8818,-8829,-8840,-8850,-8861,-8871,-8882,-8892,-8903,-8914,-8924, + -8935,-8945,-8956,-8966,-8977,-8987,-8998,-9008,-9019,-9029,-9040,-9050,-9061,-9071,-9082,-9092, + -9102,-9113,-9123,-9134,-9144,-9155,-9165,-9175,-9186,-9196,-9207,-9217,-9227,-9238,-9248,-9259, + -9269,-9279,-9290,-9300,-9310,-9321,-9331,-9341,-9352,-9362,-9372,-9383,-9393,-9403,-9413,-9424, + -9434,-9444,-9455,-9465,-9475,-9485,-9496,-9506,-9516,-9526,-9537,-9547,-9557,-9567,-9577,-9588, + -9598,-9608,-9618,-9628,-9638,-9649,-9659,-9669,-9679,-9689,-9699,-9709,-9720,-9730,-9740,-9750, + -9760,-9770,-9780,-9790,-9800,-9810,-9820,-9830,-9841,-9851,-9861,-9871,-9881,-9891,-9901,-9911, + -9921,-9931,-9941,-9951,-9961,-9971,-9981,-9991,-10001,-10010,-10020,-10030,-10040,-10050,-10060,-10070, + -10080,-10090,-10100,-10110,-10120,-10129,-10139,-10149,-10159,-10169,-10179,-10189,-10198,-10208,-10218,-10228, + -10238,-10248,-10257,-10267,-10277,-10287,-10296,-10306,-10316,-10326,-10336,-10345,-10355,-10365,-10374,-10384, + -10394,-10404,-10413,-10423,-10433,-10442,-10452,-10462,-10471,-10481,-10491,-10500,-10510,-10520,-10529,-10539, + -10549,-10558,-10568,-10577,-10587,-10597,-10606,-10616,-10625,-10635,-10644,-10654,-10663,-10673,-10683,-10692, + -10702,-10711,-10721,-10730,-10740,-10749,-10759,-10768,-10778,-10787,-10796,-10806,-10815,-10825,-10834,-10844, + -10853,-10862,-10872,-10881,-10891,-10900,-10909,-10919,-10928,-10937,-10947,-10956,-10966,-10975,-10984,-10994, + -11003,-11012,-11021,-11031,-11040,-11049,-11059,-11068,-11077,-11086,-11096,-11105,-11114,-11123,-11133,-11142, + -11151,-11160,-11169,-11179,-11188,-11197,-11206,-11215,-11224,-11234,-11243,-11252,-11261,-11270,-11279,-11288, + -11297,-11307,-11316,-11325,-11334,-11343,-11352,-11361,-11370,-11379,-11388,-11397,-11406,-11415,-11424,-11433, + -11442,-11451,-11460,-11469,-11478,-11487,-11496,-11505,-11514,-11523,-11532,-11541,-11550,-11559,-11567,-11576, + -11585,-11594,-11603,-11612,-11621,-11630,-11638,-11647,-11656,-11665,-11674,-11683,-11691,-11700,-11709,-11718, + -11727,-11735,-11744,-11753,-11762,-11770,-11779,-11788,-11797,-11805,-11814,-11823,-11831,-11840,-11849,-11857, + -11866,-11875,-11883,-11892,-11901,-11909,-11918,-11927,-11935,-11944,-11952,-11961,-11970,-11978,-11987,-11995, + -12004,-12012,-12021,-12029,-12038,-12046,-12055,-12064,-12072,-12080,-12089,-12097,-12106,-12114,-12123,-12131, + -12140,-12148,-12157,-12165,-12173,-12182,-12190,-12199,-12207,-12215,-12224,-12232,-12240,-12249,-12257,-12266, + -12274,-12282,-12290,-12299,-12307,-12315,-12324,-12332,-12340,-12348,-12357,-12365,-12373,-12381,-12390,-12398, + -12406,-12414,-12423,-12431,-12439,-12447,-12455,-12463,-12472,-12480,-12488,-12496,-12504,-12512,-12520,-12528, + -12537,-12545,-12553,-12561,-12569,-12577,-12585,-12593,-12601,-12609,-12617,-12625,-12633,-12641,-12649,-12657, + -12665,-12673,-12681,-12689,-12697,-12705,-12713,-12721,-12729,-12736,-12744,-12752,-12760,-12768,-12776,-12784, + -12792,-12799,-12807,-12815,-12823,-12831,-12839,-12846,-12854,-12862,-12870,-12878,-12885,-12893,-12901,-12909, + -12916,-12924,-12932,-12939,-12947,-12955,-12963,-12970,-12978,-12986,-12993,-13001,-13008,-13016,-13024,-13031, + -13039,-13047,-13054,-13062,-13069,-13077,-13085,-13092,-13100,-13107,-13115,-13122,-13130,-13137,-13145,-13152, + -13160,-13167,-13175,-13182,-13190,-13197,-13205,-13212,-13219,-13227,-13234,-13242,-13249,-13256,-13264,-13271, + -13279,-13286,-13293,-13301,-13308,-13315,-13323,-13330,-13337,-13344,-13352,-13359,-13366,-13374,-13381,-13388, + -13395,-13403,-13410,-13417,-13424,-13431,-13439,-13446,-13453,-13460,-13467,-13474,-13482,-13489,-13496,-13503, + -13510,-13517,-13524,-13531,-13538,-13546,-13553,-13560,-13567,-13574,-13581,-13588,-13595,-13602,-13609,-13616, + -13623,-13630,-13637,-13644,-13651,-13658,-13665,-13671,-13678,-13685,-13692,-13699,-13706,-13713,-13720,-13727, + -13733,-13740,-13747,-13754,-13761,-13768,-13774,-13781,-13788,-13795,-13802,-13808,-13815,-13822,-13829,-13835, + -13842,-13849,-13856,-13862,-13869,-13876,-13882,-13889,-13896,-13902,-13909,-13916,-13922,-13929,-13935,-13942, + -13949,-13955,-13962,-13968,-13975,-13981,-13988,-13995,-14001,-14008,-14014,-14021,-14027,-14034,-14040,-14047, + -14053,-14059,-14066,-14072,-14079,-14085,-14092,-14098,-14104,-14111,-14117,-14124,-14130,-14136,-14143,-14149, + -14155,-14162,-14168,-14174,-14181,-14187,-14193,-14199,-14206,-14212,-14218,-14224,-14231,-14237,-14243,-14249, + -14256,-14262,-14268,-14274,-14280,-14286,-14293,-14299,-14305,-14311,-14317,-14323,-14329,-14335,-14341,-14347, + -14354,-14360,-14366,-14372,-14378,-14384,-14390,-14396,-14402,-14408,-14414,-14420,-14426,-14432,-14438,-14443, + -14449,-14455,-14461,-14467,-14473,-14479,-14485,-14491,-14497,-14502,-14508,-14514,-14520,-14526,-14531,-14537, + -14543,-14549,-14555,-14560,-14566,-14572,-14578,-14583,-14589,-14595,-14601,-14606,-14612,-14618,-14623,-14629, + -14635,-14640,-14646,-14651,-14657,-14663,-14668,-14674,-14680,-14685,-14691,-14696,-14702,-14707,-14713,-14718, + -14724,-14729,-14735,-14740,-14746,-14751,-14757,-14762,-14768,-14773,-14779,-14784,-14789,-14795,-14800,-14806, + -14811,-14816,-14822,-14827,-14832,-14838,-14843,-14848,-14854,-14859,-14864,-14870,-14875,-14880,-14885,-14891, + -14896,-14901,-14906,-14911,-14917,-14922,-14927,-14932,-14937,-14943,-14948,-14953,-14958,-14963,-14968,-14973, + -14978,-14984,-14989,-14994,-14999,-15004,-15009,-15014,-15019,-15024,-15029,-15034,-15039,-15044,-15049,-15054, + -15059,-15064,-15069,-15074,-15078,-15083,-15088,-15093,-15098,-15103,-15108,-15113,-15118,-15122,-15127,-15132, + -15137,-15142,-15146,-15151,-15156,-15161,-15166,-15170,-15175,-15180,-15184,-15189,-15194,-15199,-15203,-15208, + -15213,-15217,-15222,-15227,-15231,-15236,-15240,-15245,-15250,-15254,-15259,-15263,-15268,-15273,-15277,-15282, + -15286,-15291,-15295,-15300,-15304,-15309,-15313,-15318,-15322,-15326,-15331,-15335,-15340,-15344,-15349,-15353, + -15357,-15362,-15366,-15370,-15375,-15379,-15383,-15388,-15392,-15396,-15401,-15405,-15409,-15414,-15418,-15422, + -15426,-15430,-15435,-15439,-15443,-15447,-15451,-15456,-15460,-15464,-15468,-15472,-15476,-15481,-15485,-15489, + -15493,-15497,-15501,-15505,-15509,-15513,-15517,-15521,-15525,-15529,-15533,-15537,-15541,-15545,-15549,-15553, + -15557,-15561,-15565,-15569,-15573,-15577,-15581,-15584,-15588,-15592,-15596,-15600,-15604,-15608,-15611,-15615, + -15619,-15623,-15627,-15630,-15634,-15638,-15642,-15645,-15649,-15653,-15656,-15660,-15664,-15668,-15671,-15675, + -15679,-15682,-15686,-15689,-15693,-15697,-15700,-15704,-15707,-15711,-15715,-15718,-15722,-15725,-15729,-15732, + -15736,-15739,-15743,-15746,-15750,-15753,-15757,-15760,-15763,-15767,-15770,-15774,-15777,-15780,-15784,-15787, + -15791,-15794,-15797,-15801,-15804,-15807,-15810,-15814,-15817,-15820,-15824,-15827,-15830,-15833,-15837,-15840, + -15843,-15846,-15849,-15853,-15856,-15859,-15862,-15865,-15868,-15871,-15875,-15878,-15881,-15884,-15887,-15890, + -15893,-15896,-15899,-15902,-15905,-15908,-15911,-15914,-15917,-15920,-15923,-15926,-15929,-15932,-15935,-15938, + -15941,-15944,-15946,-15949,-15952,-15955,-15958,-15961,-15964,-15966,-15969,-15972,-15975,-15978,-15980,-15983, + -15986,-15989,-15991,-15994,-15997,-16000,-16002,-16005,-16008,-16010,-16013,-16016,-16018,-16021,-16024,-16026, + -16029,-16031,-16034,-16037,-16039,-16042,-16044,-16047,-16049,-16052,-16054,-16057,-16059,-16062,-16064,-16067, + -16069,-16072,-16074,-16076,-16079,-16081,-16084,-16086,-16088,-16091,-16093,-16096,-16098,-16100,-16103,-16105, + -16107,-16109,-16112,-16114,-16116,-16119,-16121,-16123,-16125,-16128,-16130,-16132,-16134,-16136,-16138,-16141, + -16143,-16145,-16147,-16149,-16151,-16153,-16156,-16158,-16160,-16162,-16164,-16166,-16168,-16170,-16172,-16174, + -16176,-16178,-16180,-16182,-16184,-16186,-16188,-16190,-16192,-16194,-16195,-16197,-16199,-16201,-16203,-16205, + -16207,-16209,-16210,-16212,-16214,-16216,-16218,-16219,-16221,-16223,-16225,-16226,-16228,-16230,-16232,-16233, + -16235,-16237,-16238,-16240,-16242,-16243,-16245,-16247,-16248,-16250,-16251,-16253,-16255,-16256,-16258,-16259, + -16261,-16262,-16264,-16265,-16267,-16268,-16270,-16271,-16273,-16274,-16276,-16277,-16279,-16280,-16281,-16283, + -16284,-16286,-16287,-16288,-16290,-16291,-16292,-16294,-16295,-16296,-16298,-16299,-16300,-16301,-16303,-16304, + -16305,-16306,-16308,-16309,-16310,-16311,-16312,-16313,-16315,-16316,-16317,-16318,-16319,-16320,-16321,-16323, + -16324,-16325,-16326,-16327,-16328,-16329,-16330,-16331,-16332,-16333,-16334,-16335,-16336,-16337,-16338,-16339, + -16340,-16341,-16341,-16342,-16343,-16344,-16345,-16346,-16347,-16348,-16348,-16349,-16350,-16351,-16352,-16352, + -16353,-16354,-16355,-16355,-16356,-16357,-16358,-16358,-16359,-16360,-16360,-16361,-16362,-16362,-16363,-16364, + -16364,-16365,-16365,-16366,-16367,-16367,-16368,-16368,-16369,-16369,-16370,-16370,-16371,-16371,-16372,-16372, + -16373,-16373,-16374,-16374,-16375,-16375,-16375,-16376,-16376,-16377,-16377,-16377,-16378,-16378,-16378,-16379, + -16379,-16379,-16380,-16380,-16380,-16380,-16381,-16381,-16381,-16381,-16382,-16382,-16382,-16382,-16382,-16383, + -16383,-16383,-16383,-16383,-16383,-16383,-16384,-16384,-16384,-16384,-16384,-16384,-16384,-16384,-16384,-16384, + -16384,-16384,0,5,10,15,20,25,31,36,41,46,51,56,61,66, + 71,76,81,87,92,97,102,107,112,117,122,127,132,138,143,148, + 153,158,163,168,173,178,183,188,194,199,204,209,214,219,224,229, + 234,239,244,250,255,260,265,270,275,280,285,290,295,300,305,311, + 316,321,326,331,336,341,346,351,356,361,367,372,377,382,387,392, + 397,402,407,412,417,422,428,433,438,443,448,453,458,463,468,473, + 478,483,489,494,499,504,509,514, +}; + +const short gp2cc_ATAN1[2050] = { + 0,5,10,15,20,25,31,36,41,46,51,56,61,66,71,76, + 81,87,92,97,102,107,112,117,122,127,132,138,143,148,153,158, + 163,168,173,178,183,188,194,199,204,209,214,219,224,229,234,239, + 244,250,255,260,265,270,275,280,285,290,295,300,305,311,316,321, + 326,331,336,341,346,351,356,361,367,372,377,382,387,392,397,402, + 407,412,417,422,428,433,438,443,448,453,458,463,468,473,478,483, + 489,494,499,504,509,514,519,524,529,534,539,544,550,555,560,565, + 570,575,580,585,590,595,600,605,610,616,621,626,631,636,641,646, + 651,656,661,666,671,676,681,687,692,697,702,707,712,717,722,727, + 732,737,742,747,752,758,763,768,773,778,783,788,793,798,803,808, + 813,818,823,828,833,839,844,849,854,859,864,869,874,879,884,889, + 894,899,904,909,914,919,924,930,935,940,945,950,955,960,965,970, + 975,980,985,990,995,1000,1005,1010,1015,1020,1025,1031,1036,1041,1046,1051, + 1056,1061,1066,1071,1076,1081,1086,1091,1096,1101,1106,1111,1116,1121,1126,1131, + 1136,1141,1146,1151,1156,1161,1166,1172,1177,1182,1187,1192,1197,1202,1207,1212, + 1217,1222,1227,1232,1237,1242,1247,1252,1257,1262,1267,1272,1277,1282,1287,1292, + 1297,1302,1307,1312,1317,1322,1327,1332,1337,1342,1347,1352,1357,1362,1367,1372, + 1377,1382,1387,1392,1397,1402,1407,1412,1417,1422,1427,1432,1437,1442,1447,1452, + 1457,1462,1467,1472,1477,1482,1487,1492,1497,1502,1507,1512,1517,1522,1527,1532, + 1537,1542,1547,1552,1557,1562,1567,1572,1577,1582,1587,1592,1597,1602,1607,1612, + 1617,1622,1627,1632,1637,1642,1646,1651,1656,1661,1666,1671,1676,1681,1686,1691, + 1696,1701,1706,1711,1716,1721,1726,1731,1736,1741,1746,1751,1756,1761,1765,1770, + 1775,1780,1785,1790,1795,1800,1805,1810,1815,1820,1825,1830,1835,1840,1845,1849, + 1854,1859,1864,1869,1874,1879,1884,1889,1894,1899,1904,1909,1914,1918,1923,1928, + 1933,1938,1943,1948,1953,1958,1963,1968,1973,1977,1982,1987,1992,1997,2002,2007, + 2012,2017,2022,2027,2031,2036,2041,2046,2051,2056,2061,2066,2071,2076,2080,2085, + 2090,2095,2100,2105,2110,2115,2120,2124,2129,2134,2139,2144,2149,2154,2159,2163, + 2168,2173,2178,2183,2188,2193,2198,2202,2207,2212,2217,2222,2227,2232,2237,2241, + 2246,2251,2256,2261,2266,2271,2275,2280,2285,2290,2295,2300,2305,2309,2314,2319, + 2324,2329,2334,2338,2343,2348,2353,2358,2363,2367,2372,2377,2382,2387,2392,2396, + 2401,2406,2411,2416,2421,2425,2430,2435,2440,2445,2450,2454,2459,2464,2469,2474, + 2478,2483,2488,2493,2498,2502,2507,2512,2517,2522,2526,2531,2536,2541,2546,2550, + 2555,2560,2565,2570,2574,2579,2584,2589,2594,2598,2603,2608,2613,2617,2622,2627, + 2632,2637,2641,2646,2651,2656,2660,2665,2670,2675,2679,2684,2689,2694,2699,2703, + 2708,2713,2718,2722,2727,2732,2737,2741,2746,2751,2756,2760,2765,2770,2775,2779, + 2784,2789,2793,2798,2803,2808,2812,2817,2822,2827,2831,2836,2841,2846,2850,2855, + 2860,2864,2869,2874,2879,2883,2888,2893,2897,2902,2907,2912,2916,2921,2926,2930, + 2935,2940,2944,2949,2954,2959,2963,2968,2973,2977,2982,2987,2991,2996,3001,3005, + 3010,3015,3019,3024,3029,3033,3038,3043,3047,3052,3057,3061,3066,3071,3075,3080, + 3085,3089,3094,3099,3103,3108,3113,3117,3122,3127,3131,3136,3141,3145,3150,3155, + 3159,3164,3168,3173,3178,3182,3187,3192,3196,3201,3206,3210,3215,3219,3224,3229, + 3233,3238,3243,3247,3252,3256,3261,3266,3270,3275,3279,3284,3289,3293,3298,3302, + 3307,3312,3316,3321,3325,3330,3335,3339,3344,3348,3353,3358,3362,3367,3371,3376, + 3380,3385,3390,3394,3399,3403,3408,3412,3417,3422,3426,3431,3435,3440,3444,3449, + 3453,3458,3463,3467,3472,3476,3481,3485,3490,3494,3499,3503,3508,3513,3517,3522, + 3526,3531,3535,3540,3544,3549,3553,3558,3562,3567,3571,3576,3580,3585,3589,3594, + 3599,3603,3608,3612,3617,3621,3626,3630,3635,3639,3644,3648,3653,3657,3662,3666, + 3670,3675,3679,3684,3688,3693,3697,3702,3706,3711,3715,3720,3724,3729,3733,3738, + 3742,3747,3751,3756,3760,3764,3769,3773,3778,3782,3787,3791,3796,3800,3804,3809, + 3813,3818,3822,3827,3831,3836,3840,3844,3849,3853,3858,3862,3867,3871,3875,3880, + 3884,3889,3893,3898,3902,3906,3911,3915,3920,3924,3928,3933,3937,3942,3946,3950, + 3955,3959,3964,3968,3972,3977,3981,3985,3990,3994,3999,4003,4007,4012,4016,4021, + 4025,4029,4034,4038,4042,4047,4051,4055,4060,4064,4069,4073,4077,4082,4086,4090, + 4095,4099,4103,4108,4112,4116,4121,4125,4129,4134,4138,4142,4147,4151,4155,4160, + 4164,4168,4173,4177,4181,4186,4190,4194,4199,4203,4207,4211,4216,4220,4224,4229, + 4233,4237,4242,4246,4250,4254,4259,4263,4267,4272,4276,4280,4284,4289,4293,4297, + 4302,4306,4310,4314,4319,4323,4327,4331,4336,4340,4344,4349,4353,4357,4361,4366, + 4370,4374,4378,4383,4387,4391,4395,4400,4404,4408,4412,4416,4421,4425,4429,4433, + 4438,4442,4446,4450,4454,4459,4463,4467,4471,4476,4480,4484,4488,4492,4497,4501, + 4505,4509,4513,4518,4522,4526,4530,4534,4539,4543,4547,4551,4555,4559,4564,4568, + 4572,4576,4580,4585,4589,4593,4597,4601,4605,4610,4614,4618,4622,4626,4630,4634, + 4639,4643,4647,4651,4655,4659,4663,4668,4672,4676,4680,4684,4688,4692,4697,4701, + 4705,4709,4713,4717,4721,4725,4730,4734,4738,4742,4746,4750,4754,4758,4762,4767, + 4771,4775,4779,4783,4787,4791,4795,4799,4803,4807,4812,4816,4820,4824,4828,4832, + 4836,4840,4844,4848,4852,4856,4860,4865,4869,4873,4877,4881,4885,4889,4893,4897, + 4901,4905,4909,4913,4917,4921,4925,4929,4933,4937,4941,4945,4949,4954,4958,4962, + 4966,4970,4974,4978,4982,4986,4990,4994,4998,5002,5006,5010,5014,5018,5022,5026, + 5030,5034,5038,5042,5046,5050,5054,5058,5062,5066,5070,5074,5078,5082,5086,5090, + 5094,5097,5101,5105,5109,5113,5117,5121,5125,5129,5133,5137,5141,5145,5149,5153, + 5157,5161,5165,5169,5173,5177,5181,5184,5188,5192,5196,5200,5204,5208,5212,5216, + 5220,5224,5228,5232,5235,5239,5243,5247,5251,5255,5259,5263,5267,5271,5275,5278, + 5282,5286,5290,5294,5298,5302,5306,5310,5313,5317,5321,5325,5329,5333,5337,5341, + 5344,5348,5352,5356,5360,5364,5368,5371,5375,5379,5383,5387,5391,5395,5398,5402, + 5406,5410,5414,5418,5421,5425,5429,5433,5437,5441,5444,5448,5452,5456,5460,5464, + 5467,5471,5475,5479,5483,5486,5490,5494,5498,5502,5505,5509,5513,5517,5521,5524, + 5528,5532,5536,5540,5543,5547,5551,5555,5559,5562,5566,5570,5574,5577,5581,5585, + 5589,5592,5596,5600,5604,5608,5611,5615,5619,5623,5626,5630,5634,5638,5641,5645, + 5649,5652,5656,5660,5664,5667,5671,5675,5679,5682,5686,5690,5694,5697,5701,5705, + 5708,5712,5716,5720,5723,5727,5731,5734,5738,5742,5745,5749,5753,5757,5760,5764, + 5768,5771,5775,5779,5782,5786,5790,5793,5797,5801,5804,5808,5812,5815,5819,5823, + 5826,5830,5834,5837,5841,5845,5848,5852,5856,5859,5863,5867,5870,5874,5878,5881, + 5885,5888,5892,5896,5899,5903,5907,5910,5914,5917,5921,5925,5928,5932,5936,5939, + 5943,5946,5950,5954,5957,5961,5964,5968,5972,5975,5979,5982,5986,5990,5993,5997, + 6000,6004,6008,6011,6015,6018,6022,6025,6029,6033,6036,6040,6043,6047,6050,6054, + 6058,6061,6065,6068,6072,6075,6079,6082,6086,6089,6093,6097,6100,6104,6107,6111, + 6114,6118,6121,6125,6128,6132,6135,6139,6142,6146,6150,6153,6157,6160,6164,6167, + 6171,6174,6178,6181,6185,6188,6192,6195,6199,6202,6206,6209,6213,6216,6220,6223, + 6227,6230,6234,6237,6240,6244,6247,6251,6254,6258,6261,6265,6268,6272,6275,6279, + 6282,6286,6289,6292,6296,6299,6303,6306,6310,6313,6317,6320,6323,6327,6330,6334, + 6337,6341,6344,6348,6351,6354,6358,6361,6365,6368,6371,6375,6378,6382,6385,6389, + 6392,6395,6399,6402,6406,6409,6412,6416,6419,6423,6426,6429,6433,6436,6440,6443, + 6446,6450,6453,6456,6460,6463,6467,6470,6473,6477,6480,6483,6487,6490,6493,6497, + 6500,6504,6507,6510,6514,6517,6520,6524,6527,6530,6534,6537,6540,6544,6547,6550, + 6554,6557,6560,6564,6567,6570,6574,6577,6580,6584,6587,6590,6594,6597,6600,6604, + 6607,6610,6613,6617,6620,6623,6627,6630,6633,6637,6640,6643,6646,6650,6653,6656, + 6660,6663,6666,6669,6673,6676,6679,6683,6686,6689,6692,6696,6699,6702,6705,6709, + 6712,6715,6718,6722,6725,6728,6731,6735,6738,6741,6744,6748,6751,6754,6757,6761, + 6764,6767,6770,6774,6777,6780,6783,6787,6790,6793,6796,6799,6803,6806,6809,6812, + 6815,6819,6822,6825,6828,6832,6835,6838,6841,6844,6848,6851,6854,6857,6860,6863, + 6867,6870,6873,6876,6879,6883,6886,6889,6892,6895,6898,6902,6905,6908,6911,6914, + 6917,6921,6924,6927,6930,6933,6936,6940,6943,6946,6949,6952,6955,6958,6962,6965, + 6968,6971,6974,6977,6980,6984,6987,6990,6993,6996,6999,7002,7005,7009,7012,7015, + 7018,7021,7024,7027,7030,7033,7037,7040,7043,7046,7049,7052,7055,7058,7061,7064, + 7068,7071,7074,7077,7080,7083,7086,7089,7092,7095,7098,7101,7105,7108,7111,7114, + 7117,7120,7123,7126,7129,7132,7135,7138,7141,7144,7147,7150,7154,7157,7160,7163, + 7166,7169,7172,7175,7178,7181,7184,7187,7190,7193,7196,7199,7202,7205,7208,7211, + 7214,7217,7220,7223,7226,7229,7232,7235,7238,7241,7244,7247,7250,7253,7256,7259, + 7262,7265,7268,7271,7274,7277,7280,7283,7286,7289,7292,7295,7298,7301,7304,7307, + 7310,7313,7316,7319,7322,7325,7328,7331,7334,7337,7340,7343,7346,7349,7352,7355, + 7358,7361,7363,7366,7369,7372,7375,7378,7381,7384,7387,7390,7393,7396,7399,7402, + 7405,7408,7411,7413,7416,7419,7422,7425,7428,7431,7434,7437,7440,7443,7446,7448, + 7451,7454,7457,7460,7463,7466,7469,7472,7475,7477,7480,7483,7486,7489,7492,7495, + 7498,7501,7503,7506,7509,7512,7515,7518,7521,7524,7526,7529,7532,7535,7538,7541, + 7544,7547,7549,7552,7555,7558,7561,7564,7566,7569,7572,7575,7578,7581,7584,7586, + 7589,7592,7595,7598,7601,7603,7606,7609,7612,7615,7618,7620,7623,7626,7629,7632, + 7635,7637,7640,7643,7646,7649,7651,7654,7657,7660,7663,7665,7668,7671,7674,7677, + 7679,7682,7685,7688,7691,7693,7696,7699,7702,7705,7707,7710,7713,7716,7718,7721, + 7724,7727,7730,7732,7735,7738,7741,7743,7746,7749,7752,7754,7757,7760,7763,7765, + 7768,7771,7774,7776,7779,7782,7785,7787,7790,7793,7796,7798,7801,7804,7807,7809, + 7812,7815,7818,7820,7823,7826,7828,7831,7834,7837,7839,7842,7845,7848,7850,7853, + 7856,7858,7861,7864,7866,7869,7872,7875,7877,7880,7883,7885,7888,7891,7893,7896, + 7899,7902,7904,7907,7910,7912,7915,7918,7920,7923,7926,7928,7931,7934,7936,7939, + 7942,7944,7947,7950,7952,7955,7958,7960,7963,7966,7968,7971,7974,7976,7979,7982, + 7984,7987,7990,7992,7995,7997,8000,8003,8005,8008,8011,8013,8016,8019,8021,8024, + 8026,8029,8032,8034,8037,8040,8042,8045,8047,8050,8053,8055,8058,8060,8063,8066, + 8068,8071,8074,8076,8079,8081,8084,8087,8089,8092,8094,8097,8100,8102,8105,8107, + 8110,8112,8115,8118,8120,8123,8125,8128,8131,8133,8136,8138,8141,8143,8146,8149, + 8151,8154,8156,8159,8161,8164,8166,8169,8172,8174,8177,8179,8182,8184,8187,8189, + 8192,0, +}; + diff --git a/res/bmp00076.bmp b/res/bmp00076.bmp index b4a794b2be5a21e7f392e1d93352a8452927cae4..bc6585ffbf7c676e4ac88cd356f178983a87846f 100755 GIT binary patch delta 264 zcmew+|4l)~$+t{^0SwB3qy`ZGV`N|e2{AITfW;XEHmZ0qPQJj$JGqeYGiNyrOipF` zj>L;)M&^aGfOrKA3?S8$eOW;~hVlY-5YLqj$OAD!l9O%Op**lkQw|^xtO+Ei%ZbcW z00^c2=5EL*>Zsb#$ti*q5;$2Qg1^`{e BS&aYy delta 148 zcmeyS@J(LD$@d#S0~nM6Nev)=%gDeW0HhfiSis^9{2Ns~7#RyDmok0_kxXEc8BDT( zNmek)1}53TBnOz}1e07~k{e9&fJxr(Oa%;+6&a-_PvE-)Vt(XPnLLaC5|9Z14`DAe diff --git a/resource.h b/resource.h old mode 100755 new mode 100644 index 295ad6b..9e2f70a --- a/resource.h +++ b/resource.h @@ -885,6 +885,7 @@ #define ID_HELP_QUICKKEYREMINDER 33097 #define ID_SHOW_SHOWSCENERYARMS 33098 #define ID_SHOW_USESWIVELANGLES 33099 +#define ID_VIEW_COMPILED 33100 #define ID_INDICATOR_MODEL 59142 // Next default values for new objects @@ -893,7 +894,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_3D_CONTROLS 1 #define _APS_NEXT_RESOURCE_VALUE 266 -#define _APS_NEXT_COMMAND_VALUE 33100 +#define _APS_NEXT_COMMAND_VALUE 33101 #define _APS_NEXT_CONTROL_VALUE 1244 #define _APS_NEXT_SYMED_VALUE 115 #endif From 66741a8222bd85e115e2d0825bd4ac745f932904 Mon Sep 17 00:00:00 2001 From: Roberto Remedio Date: Wed, 10 Jun 2026 10:32:01 -0300 Subject: [PATCH 02/12] Compiled view: section dividers, numbers, and start/finish line Make the existing "Track Section Numbers" and "Show Finish Line" toggles work in the Show Compiled Track view: - showFinishLine: thick line across the road at segment 0 + checkered flag. - showTrackNumbers: a perpendicular divider across the road at each track section boundary + the section index drawn mid-section. Section->segment mapping uses cumulative TrackSection::getLength() (= the geometry-command word gp2cc consumes), so the markers land bit-exactly on the compiled road. GPTrack.cpp only; no gp2cc change. --- GPTrack.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/GPTrack.cpp b/GPTrack.cpp index e85077a..facd69e 100644 --- a/GPTrack.cpp +++ b/GPTrack.cpp @@ -4137,6 +4137,52 @@ void GPTrack::drawComputed(Display *g) g->setColor(sel ? 4 : BLUE_PEN); g->drawLine(ccx[s] - tx, ccy[s] - ty, ccx[s] + tx, ccy[s] + ty); } + + // --- track-section dividers + numbers (gated on showTrackNumbers) --- + // Each TrackSection serializes to ONE geometry command whose word N = getLength() + // (TrackSection::write writes lengthData as the command word) and gp2cc emits exactly + // N segments per command, so the running sum of getLength() over the sections (skipping + // the index==-99 ones that emit no command, as WriteTrack does) gives each section's + // first compiled segment — bit-exact with the road drawn above. + if (showTrackNumbers) { + int nsec = TrackSections->size(); + CPen *divPen = new CPen(PS_SOLID, 1, RGB(110, 110, 110)); // section dividers (gray) + g->SelectObject(divPen); + int seg = 0; + for (int k = 0; k < nsec; k++) { + TrackSection *t = (TrackSection *)TrackSections->elementAt(k); + if (t->index == -99) continue; // emits no geometry command (see WriteTrack) + int s = seg; // this section's first compiled segment + int len = (int)t->getLength(); + seg += len; + if (s < 0 || s >= n) continue; + + // divider: perpendicular line across the road at the section boundary + g->drawLine(lex[s], ley[s], rex[s], rey[s]); + + // section number placed mid-section on the centre line (like the default view) + int mid = s + len / 2; + if (mid >= n) mid = s; + char buf[12]; + wsprintf(buf, "%d", k); + g->drawText(ox + (gt.X8[mid] / 8.0 - bwx) * ES, + oy + (gt.Y8[mid] / 8.0 - bwy) * ES, buf); + } + g->setColor(0); // deselect divPen before deleting it + delete divPen; + } + + // --- start/finish line (gated on showFinishLine) --- + // Thick line across the road at segment 0 + checkered flag, mirroring + // TrackSection::drawStartingLine in the default view. + if (showFinishLine) { + CPen *sfPen = new CPen(PS_SOLID, 3, RGB(0, 0, 0)); + g->SelectObject(sfPen); + g->drawLine(lex[0], ley[0], rex[0], rey[0]); + g->setColor(0); // deselect sfPen before deleting it + delete sfPen; + g->drawBitmap(IDB_CHECKED_FLAG, lex[0], ley[0] - 16); + } } void GPTrack::drawPitlane(Display *g) From a389e98b44a4244cea5d09d1be8da087e62b06d7 Mon Sep 17 00:00:00 2001 From: Roberto Remedio Date: Thu, 11 Jun 2026 21:49:17 -0300 Subject: [PATCH 03/12] Semantic C++ track-geometry compiler (gp2geom), wired into the compiled view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrites GP2's geometry compilation as the geometric process it actually is — a fixed-point "turtle" with named steps (turn, advance one fixed chord, midpoint half-step, ramp width, compute side vectors, close the loop) — instead of a transcription of the disassembly. Every fixed-point op is a named helper whose comment derives the constant (0x6488 = round(pi/2*2^14); the half-step = midpoint integration; the warp = DDA error diffusion; the chord = a fixed 128-unit step). Same exact integers: validated BYTE-IDENTICAL to the original gp2cc compiler on F1CT01/02a/03/09/11/16 (every field) and the cc-line on it is bit-exact. The cosine it uses comes from gp2cos.hpp: the table expressed as its formula (round(16384*cos(i*pi/4096)) + the one documented COS[1992]=703 quirk), generated at compile time and static_assert'd against GP2's extracted bytes. If a toolchain struggles with the constexpr generation, define GP2GEOM_EMBEDDED_COS to use the existing table (bit-identical); MSVC also gets a raised /constexpr:steps budget. New files only — the original gp2cc.c (incl. the chaotic cc-line kernel, kept as-is) is untouched. drawComputed now calls gp2geom::compileGeometry; the cc-line still runs through the existing kernel. Note: ATAN1's formula header (gp2atan.hpp) is proven but not wired in yet — it belongs with a later cc-line-kernel cleanup. CMakeLists isn't on this branch (predates the upstream CMake merge); when this lands on dev, add gp2cc.c, gp2cc_tables.c and gp2geom.cpp to its SOURCES. --- GPTrack.cpp | 5 +- TrackEditor.vcxproj | 11 ++ gp2cos.hpp | 100 ++++++++++++++++ gp2cos_reference.hpp | 263 +++++++++++++++++++++++++++++++++++++++++++ gp2geom.cpp | 261 ++++++++++++++++++++++++++++++++++++++++++ gp2geom.hpp | 23 ++++ 6 files changed, 661 insertions(+), 2 deletions(-) create mode 100644 gp2cos.hpp create mode 100644 gp2cos_reference.hpp create mode 100644 gp2geom.cpp create mode 100644 gp2geom.hpp diff --git a/GPTrack.cpp b/GPTrack.cpp index facd69e..31dda76 100644 --- a/GPTrack.cpp +++ b/GPTrack.cpp @@ -11,7 +11,8 @@ #include "TrackSection.h" #include "TrackObjectDefinition.h" #include "CCLineSection.h" -#include "gp2cc.h" // bit-exact compiled track + cc-line +#include "gp2cc.h" // bit-exact cc-line kernel (chaotic; kept as-is) +#include "gp2geom.hpp" // semantic C++ track-geometry compiler (drop-in for gp2cc_compile_geometry_buf) #include "TrackCmd.h" #include "InternalObject.h" #include "JamFileEditor.h" @@ -4056,7 +4057,7 @@ void GPTrack::drawComputed(Display *g) static gp2cc_ccgeo cg; static short bl[GP2CC_MAXSEG], a18[GP2CC_MAXSEG]; - if (gp2cc_compile_geometry_buf(trackdata, 65535, >) != 0) return; + if (gp2geom::compileGeometry(trackdata, 65535, gt) != 0) return; int n = gt.n; if (n <= 1) return; diff --git a/TrackEditor.vcxproj b/TrackEditor.vcxproj index d9f2376..1798ef7 100755 --- a/TrackEditor.vcxproj +++ b/TrackEditor.vcxproj @@ -182,6 +182,14 @@ stdc11 NotUsing + + stdcpp17 + NotUsing + + /constexpr:steps4000000 %(AdditionalOptions) + @@ -288,6 +296,9 @@ + + + diff --git a/gp2cos.hpp b/gp2cos.hpp new file mode 100644 index 0000000..cf5cbf5 --- /dev/null +++ b/gp2cos.hpp @@ -0,0 +1,100 @@ +#pragma once +#include +#include +#include "gp2cos_reference.hpp" // golden oracle: GP2's extracted t_Sinus bytes + +// --------------------------------------------------------------------------- +// GP2 geometry — the cosine table expressed as its generating formula. +// +// GP2 represents angles in "binary radians": 65536 units per full turn, so a +// quarter turn is 0x4000. The engine keeps a cosine table of amplitude 0x4000 +// (16384), sampled every 8 units, and for the position step it does a RAW, +// non-interpolated lookup COS[angle >> 3]. That lookup is therefore the cosine +// of the angle quantised to the 8-unit grid: +// +// COS[i] = round( 16384 * cos( i * pi / 4096 ) ) (i = angle>>3) +// +// ...with exactly ONE exception. At i = 1992 the exact value is 703.5004 — +// dead on a .5 rounding boundary — and GP2's own table generator resolved it +// to 703 (round() gives 704). That single 1-unit entry is load-bearing: the +// racing-line solver is a chaotic feedback system, and a track whose heading +// passes through this angle (e.g. F1CT09) drops from 100% to 29% correct +// without it. So we override that one entry and PROVE the whole table equals +// GP2's bytes at compile time (see the static_assert at the bottom). +// +// Why a formula instead of an embedded blob: the formula documents intent +// (this IS a cosine), and computing it at build time keeps the runtime fully +// deterministic (no per-call float). The float boundary entries aren't +// portable, so we don't trust them blindly — the static_assert is the +// guardrail: if any toolchain rounds a boundary entry differently, the BUILD +// fails loudly instead of the racing line drifting silently. +// --------------------------------------------------------------------------- + +namespace gp2geom { + +inline constexpr int kGrid = 4098; // indices 0..4097 (cos over [0, pi]) +inline constexpr short kAmplitude = 16384; // 0x4000 +inline constexpr int kQuirkIndex = 1992; // the lone .5-boundary entry GP2 rounds down +inline constexpr short kQuirkValue = 703; +inline constexpr double kPi = 3.14159265358979323846; + +// constexpr cosine (std::cos isn't portably constexpr before C++26): fold the +// argument into [0, pi/2] via cos(x) = -cos(pi - x), then a Taylor series. +// Accurate to ~1e-15 over [0, pi] — far inside the rounding margin (the closest +// non-quirk entry sits >1e-4 from a .5 boundary). +constexpr double cosine(double x) { + bool negate = false; + if (x > kPi * 0.5) { x = kPi - x; negate = true; } + double term = 1.0, sum = 1.0, x2 = x * x; + for (int n = 1; n < 16; ++n) { + term *= -x2 / static_cast((2 * n - 1) * (2 * n)); + sum += term; + } + return negate ? -sum : sum; +} + +// round half away from zero, matching the game's table. +constexpr short roundToShort(double v) { + long r = (v >= 0.0) ? static_cast(v + 0.5) : static_cast(v - 0.5); + return static_cast(r); +} + +// Build the table at compile time from the formula + the one documented quirk. +constexpr std::array makeCosineTable() { + std::array t{}; + for (int i = 0; i < kGrid; ++i) { + t[i] = (i == kQuirkIndex) + ? kQuirkValue + : roundToShort(kAmplitude * cosine(i * (kPi / 4096.0))); + } + return t; +} + +inline constexpr std::array kCosine = makeCosineTable(); + +// Raw (non-interpolated) cosine of an angle in binary-radian units: cos is +// even, so fold the sign, then index the 8-unit grid. This is the readable +// equivalent of GP2's COS[((a>>2)&0xFFFE)>>1]. +constexpr short scos(int angle) { + int a = static_cast(angle & 0xFFFF); // wrap to signed 16-bit + if (a < 0) a = -a; + return kCosine[a >> 3]; +} + +// ---- compile-time guardrails ------------------------------------------------- +constexpr bool matchesReference() { + for (int i = 0; i < kGrid; ++i) + if (kCosine[i] != kCosRef[i]) return false; + return true; +} +static_assert(matchesReference(), + "gp2geom cosine table != GP2 reference bytes: a float rounding boundary " + "flipped on this toolchain. Inspect the diff and add a documented override " + "before shipping — do NOT relax this assert."); + +static_assert(kCosine[0] == 16384, "cos(0)"); +static_assert(kCosine[2048] == 0, "cos(pi/2)"); +static_assert(kCosine[4096] == -16384, "cos(pi)"); +static_assert(kCosine[1992] == 703, "GP2 .5-boundary quirk"); + +} // namespace gp2geom diff --git a/gp2cos_reference.hpp b/gp2cos_reference.hpp new file mode 100644 index 0000000..36322ed --- /dev/null +++ b/gp2cos_reference.hpp @@ -0,0 +1,263 @@ +#pragma once +#include +// Golden reference: GP2 t_Sinus bytes (file 0x1DDAEC), indices 0..4097. +// Used ONLY as the compile-time oracle for the formula in gp2cos.hpp. +namespace gp2geom { inline constexpr short kCosRef[4098] = { + 16384,16384,16384,16384,16384,16384,16384,16384,16384,16384,16384,16383,16383,16383,16383,16383, + 16383,16383,16382,16382,16382,16382,16382,16381,16381,16381,16381,16380,16380,16380,16380,16379, + 16379,16379,16378,16378,16378,16377,16377,16377,16376,16376,16375,16375,16375,16374,16374,16373, + 16373,16372,16372,16371,16371,16370,16370,16369,16369,16368,16368,16367,16367,16366,16365,16365, + 16364,16364,16363,16362,16362,16361,16360,16360,16359,16358,16358,16357,16356,16355,16355,16354, + 16353,16352,16352,16351,16350,16349,16348,16348,16347,16346,16345,16344,16343,16342,16341,16341, + 16340,16339,16338,16337,16336,16335,16334,16333,16332,16331,16330,16329,16328,16327,16326,16325, + 16324,16323,16321,16320,16319,16318,16317,16316,16315,16313,16312,16311,16310,16309,16308,16306, + 16305,16304,16303,16301,16300,16299,16298,16296,16295,16294,16292,16291,16290,16288,16287,16286, + 16284,16283,16281,16280,16279,16277,16276,16274,16273,16271,16270,16268,16267,16265,16264,16262, + 16261,16259,16258,16256,16255,16253,16251,16250,16248,16247,16245,16243,16242,16240,16238,16237, + 16235,16233,16232,16230,16228,16226,16225,16223,16221,16219,16218,16216,16214,16212,16210,16209, + 16207,16205,16203,16201,16199,16197,16195,16194,16192,16190,16188,16186,16184,16182,16180,16178, + 16176,16174,16172,16170,16168,16166,16164,16162,16160,16158,16156,16153,16151,16149,16147,16145, + 16143,16141,16138,16136,16134,16132,16130,16128,16125,16123,16121,16119,16116,16114,16112,16109, + 16107,16105,16103,16100,16098,16096,16093,16091,16088,16086,16084,16081,16079,16076,16074,16072, + 16069,16067,16064,16062,16059,16057,16054,16052,16049,16047,16044,16042,16039,16037,16034,16031, + 16029,16026,16024,16021,16018,16016,16013,16010,16008,16005,16002,16000,15997,15994,15991,15989, + 15986,15983,15980,15978,15975,15972,15969,15966,15964,15961,15958,15955,15952,15949,15946,15944, + 15941,15938,15935,15932,15929,15926,15923,15920,15917,15914,15911,15908,15905,15902,15899,15896, + 15893,15890,15887,15884,15881,15878,15875,15871,15868,15865,15862,15859,15856,15853,15849,15846, + 15843,15840,15837,15833,15830,15827,15824,15820,15817,15814,15810,15807,15804,15801,15797,15794, + 15791,15787,15784,15780,15777,15774,15770,15767,15763,15760,15757,15753,15750,15746,15743,15739, + 15736,15732,15729,15725,15722,15718,15715,15711,15707,15704,15700,15697,15693,15689,15686,15682, + 15679,15675,15671,15668,15664,15660,15656,15653,15649,15645,15642,15638,15634,15630,15627,15623, + 15619,15615,15611,15608,15604,15600,15596,15592,15588,15584,15581,15577,15573,15569,15565,15561, + 15557,15553,15549,15545,15541,15537,15533,15529,15525,15521,15517,15513,15509,15505,15501,15497, + 15493,15489,15485,15481,15476,15472,15468,15464,15460,15456,15451,15447,15443,15439,15435,15430, + 15426,15422,15418,15414,15409,15405,15401,15396,15392,15388,15383,15379,15375,15370,15366,15362, + 15357,15353,15349,15344,15340,15335,15331,15326,15322,15318,15313,15309,15304,15300,15295,15291, + 15286,15282,15277,15273,15268,15263,15259,15254,15250,15245,15240,15236,15231,15227,15222,15217, + 15213,15208,15203,15199,15194,15189,15184,15180,15175,15170,15166,15161,15156,15151,15146,15142, + 15137,15132,15127,15122,15118,15113,15108,15103,15098,15093,15088,15083,15078,15074,15069,15064, + 15059,15054,15049,15044,15039,15034,15029,15024,15019,15014,15009,15004,14999,14994,14989,14984, + 14978,14973,14968,14963,14958,14953,14948,14943,14937,14932,14927,14922,14917,14911,14906,14901, + 14896,14891,14885,14880,14875,14870,14864,14859,14854,14848,14843,14838,14832,14827,14822,14816, + 14811,14806,14800,14795,14789,14784,14779,14773,14768,14762,14757,14751,14746,14740,14735,14729, + 14724,14718,14713,14707,14702,14696,14691,14685,14680,14674,14668,14663,14657,14651,14646,14640, + 14635,14629,14623,14618,14612,14606,14601,14595,14589,14583,14578,14572,14566,14560,14555,14549, + 14543,14537,14531,14526,14520,14514,14508,14502,14497,14491,14485,14479,14473,14467,14461,14455, + 14449,14443,14438,14432,14426,14420,14414,14408,14402,14396,14390,14384,14378,14372,14366,14360, + 14354,14347,14341,14335,14329,14323,14317,14311,14305,14299,14293,14286,14280,14274,14268,14262, + 14256,14249,14243,14237,14231,14224,14218,14212,14206,14199,14193,14187,14181,14174,14168,14162, + 14155,14149,14143,14136,14130,14124,14117,14111,14104,14098,14092,14085,14079,14072,14066,14059, + 14053,14047,14040,14034,14027,14021,14014,14008,14001,13995,13988,13981,13975,13968,13962,13955, + 13949,13942,13935,13929,13922,13916,13909,13902,13896,13889,13882,13876,13869,13862,13856,13849, + 13842,13835,13829,13822,13815,13808,13802,13795,13788,13781,13774,13768,13761,13754,13747,13740, + 13733,13727,13720,13713,13706,13699,13692,13685,13678,13671,13665,13658,13651,13644,13637,13630, + 13623,13616,13609,13602,13595,13588,13581,13574,13567,13560,13553,13546,13538,13531,13524,13517, + 13510,13503,13496,13489,13482,13474,13467,13460,13453,13446,13439,13431,13424,13417,13410,13403, + 13395,13388,13381,13374,13366,13359,13352,13344,13337,13330,13323,13315,13308,13301,13293,13286, + 13279,13271,13264,13256,13249,13242,13234,13227,13219,13212,13205,13197,13190,13182,13175,13167, + 13160,13152,13145,13137,13130,13122,13115,13107,13100,13092,13085,13077,13069,13062,13054,13047, + 13039,13031,13024,13016,13008,13001,12993,12986,12978,12970,12963,12955,12947,12939,12932,12924, + 12916,12909,12901,12893,12885,12878,12870,12862,12854,12846,12839,12831,12823,12815,12807,12799, + 12792,12784,12776,12768,12760,12752,12744,12736,12729,12721,12713,12705,12697,12689,12681,12673, + 12665,12657,12649,12641,12633,12625,12617,12609,12601,12593,12585,12577,12569,12561,12553,12545, + 12537,12528,12520,12512,12504,12496,12488,12480,12472,12463,12455,12447,12439,12431,12423,12414, + 12406,12398,12390,12381,12373,12365,12357,12348,12340,12332,12324,12315,12307,12299,12290,12282, + 12274,12266,12257,12249,12240,12232,12224,12215,12207,12199,12190,12182,12173,12165,12157,12148, + 12140,12131,12123,12114,12106,12097,12089,12080,12072,12064,12055,12046,12038,12029,12021,12012, + 12004,11995,11987,11978,11970,11961,11952,11944,11935,11927,11918,11909,11901,11892,11883,11875, + 11866,11857,11849,11840,11831,11823,11814,11805,11797,11788,11779,11770,11762,11753,11744,11735, + 11727,11718,11709,11700,11691,11683,11674,11665,11656,11647,11638,11630,11621,11612,11603,11594, + 11585,11576,11567,11559,11550,11541,11532,11523,11514,11505,11496,11487,11478,11469,11460,11451, + 11442,11433,11424,11415,11406,11397,11388,11379,11370,11361,11352,11343,11334,11325,11316,11307, + 11297,11288,11279,11270,11261,11252,11243,11234,11224,11215,11206,11197,11188,11179,11169,11160, + 11151,11142,11133,11123,11114,11105,11096,11086,11077,11068,11059,11049,11040,11031,11021,11012, + 11003,10994,10984,10975,10966,10956,10947,10937,10928,10919,10909,10900,10891,10881,10872,10862, + 10853,10844,10834,10825,10815,10806,10796,10787,10778,10768,10759,10749,10740,10730,10721,10711, + 10702,10692,10683,10673,10663,10654,10644,10635,10625,10616,10606,10597,10587,10577,10568,10558, + 10549,10539,10529,10520,10510,10500,10491,10481,10471,10462,10452,10442,10433,10423,10413,10404, + 10394,10384,10374,10365,10355,10345,10336,10326,10316,10306,10296,10287,10277,10267,10257,10248, + 10238,10228,10218,10208,10198,10189,10179,10169,10159,10149,10139,10129,10120,10110,10100,10090, + 10080,10070,10060,10050,10040,10030,10020,10010,10001,9991,9981,9971,9961,9951,9941,9931, + 9921,9911,9901,9891,9881,9871,9861,9851,9841,9830,9820,9810,9800,9790,9780,9770, + 9760,9750,9740,9730,9720,9709,9699,9689,9679,9669,9659,9649,9638,9628,9618,9608, + 9598,9588,9577,9567,9557,9547,9537,9526,9516,9506,9496,9485,9475,9465,9455,9444, + 9434,9424,9413,9403,9393,9383,9372,9362,9352,9341,9331,9321,9310,9300,9290,9279, + 9269,9259,9248,9238,9227,9217,9207,9196,9186,9175,9165,9155,9144,9134,9123,9113, + 9102,9092,9082,9071,9061,9050,9040,9029,9019,9008,8998,8987,8977,8966,8956,8945, + 8935,8924,8914,8903,8892,8882,8871,8861,8850,8840,8829,8818,8808,8797,8787,8776, + 8765,8755,8744,8734,8723,8712,8702,8691,8680,8670,8659,8648,8638,8627,8616,8606, + 8595,8584,8573,8563,8552,8541,8531,8520,8509,8498,8488,8477,8466,8455,8445,8434, + 8423,8412,8401,8391,8380,8369,8358,8347,8337,8326,8315,8304,8293,8283,8272,8261, + 8250,8239,8228,8217,8207,8196,8185,8174,8163,8152,8141,8130,8119,8108,8098,8087, + 8076,8065,8054,8043,8032,8021,8010,7999,7988,7977,7966,7955,7944,7933,7922,7911, + 7900,7889,7878,7867,7856,7845,7834,7823,7812,7801,7790,7779,7768,7757,7746,7734, + 7723,7712,7701,7690,7679,7668,7657,7646,7635,7623,7612,7601,7590,7579,7568,7557, + 7545,7534,7523,7512,7501,7490,7478,7467,7456,7445,7434,7423,7411,7400,7389,7378, + 7366,7355,7344,7333,7321,7310,7299,7288,7276,7265,7254,7243,7231,7220,7209,7198, + 7186,7175,7164,7152,7141,7130,7118,7107,7096,7084,7073,7062,7050,7039,7028,7016, + 7005,6994,6982,6971,6960,6948,6937,6925,6914,6903,6891,6880,6868,6857,6846,6834, + 6823,6811,6800,6788,6777,6766,6754,6743,6731,6720,6708,6697,6685,6674,6662,6651, + 6639,6628,6616,6605,6593,6582,6570,6559,6547,6536,6524,6513,6501,6490,6478,6467, + 6455,6444,6432,6420,6409,6397,6386,6374,6363,6351,6339,6328,6316,6305,6293,6281, + 6270,6258,6247,6235,6223,6212,6200,6189,6177,6165,6154,6142,6130,6119,6107,6095, + 6084,6072,6060,6049,6037,6025,6014,6002,5990,5979,5967,5955,5943,5932,5920,5908, + 5897,5885,5873,5861,5850,5838,5826,5814,5803,5791,5779,5767,5756,5744,5732,5720, + 5708,5697,5685,5673,5661,5650,5638,5626,5614,5602,5591,5579,5567,5555,5543,5531, + 5520,5508,5496,5484,5472,5460,5449,5437,5425,5413,5401,5389,5377,5366,5354,5342, + 5330,5318,5306,5294,5282,5270,5259,5247,5235,5223,5211,5199,5187,5175,5163,5151, + 5139,5127,5115,5104,5092,5080,5068,5056,5044,5032,5020,5008,4996,4984,4972,4960, + 4948,4936,4924,4912,4900,4888,4876,4864,4852,4840,4828,4816,4804,4792,4780,4768, + 4756,4744,4732,4720,4708,4696,4684,4672,4660,4648,4636,4624,4612,4599,4587,4575, + 4563,4551,4539,4527,4515,4503,4491,4479,4467,4455,4442,4430,4418,4406,4394,4382, + 4370,4358,4346,4333,4321,4309,4297,4285,4273,4261,4249,4236,4224,4212,4200,4188, + 4176,4164,4151,4139,4127,4115,4103,4091,4078,4066,4054,4042,4030,4018,4005,3993, + 3981,3969,3957,3944,3932,3920,3908,3896,3883,3871,3859,3847,3835,3822,3810,3798, + 3786,3773,3761,3749,3737,3724,3712,3700,3688,3676,3663,3651,3639,3627,3614,3602, + 3590,3577,3565,3553,3541,3528,3516,3504,3492,3479,3467,3455,3442,3430,3418,3406, + 3393,3381,3369,3356,3344,3332,3320,3307,3295,3283,3270,3258,3246,3233,3221,3209, + 3196,3184,3172,3159,3147,3135,3122,3110,3098,3085,3073,3061,3048,3036,3024,3011, + 2999,2987,2974,2962,2949,2937,2925,2912,2900,2888,2875,2863,2851,2838,2826,2813, + 2801,2789,2776,2764,2752,2739,2727,2714,2702,2690,2677,2665,2652,2640,2628,2615, + 2603,2590,2578,2566,2553,2541,2528,2516,2503,2491,2479,2466,2454,2441,2429,2416, + 2404,2392,2379,2367,2354,2342,2329,2317,2305,2292,2280,2267,2255,2242,2230,2217, + 2205,2193,2180,2168,2155,2143,2130,2118,2105,2093,2080,2068,2055,2043,2031,2018, + 2006,1993,1981,1968,1956,1943,1931,1918,1906,1893,1881,1868,1856,1843,1831,1818, + 1806,1793,1781,1768,1756,1743,1731,1718,1706,1693,1681,1668,1656,1643,1631,1618, + 1606,1593,1581,1568,1556,1543,1531,1518,1506,1493,1481,1468,1456,1443,1431,1418, + 1406,1393,1381,1368,1356,1343,1331,1318,1306,1293,1280,1268,1255,1243,1230,1218, + 1205,1193,1180,1168,1155,1143,1130,1118,1105,1092,1080,1067,1055,1042,1030,1017, + 1005,992,980,967,955,942,929,917,904,892,879,867,854,842,829,816, + 804,791,779,766,754,741,729,716,703,691,678,666,653,641,628,616, + 603,590,578,565,553,540,528,515,503,490,477,465,452,440,427,415, + 402,390,377,364,352,339,327,314,302,289,276,264,251,239,226,214, + 201,188,176,163,151,138,126,113,101,88,75,63,50,38,25,13, + 0,-13,-25,-38,-50,-63,-75,-88,-101,-113,-126,-138,-151,-163,-176,-188, + -201,-214,-226,-239,-251,-264,-276,-289,-302,-314,-327,-339,-352,-364,-377,-390, + -402,-415,-427,-440,-452,-465,-477,-490,-503,-515,-528,-540,-553,-565,-578,-590, + -603,-616,-628,-641,-653,-666,-678,-691,-704,-716,-729,-741,-754,-766,-779,-791, + -804,-816,-829,-842,-854,-867,-879,-892,-904,-917,-929,-942,-955,-967,-980,-992, + -1005,-1017,-1030,-1042,-1055,-1067,-1080,-1092,-1105,-1118,-1130,-1143,-1155,-1168,-1180,-1193, + -1205,-1218,-1230,-1243,-1255,-1268,-1280,-1293,-1306,-1318,-1331,-1343,-1356,-1368,-1381,-1393, + -1406,-1418,-1431,-1443,-1456,-1468,-1481,-1493,-1506,-1518,-1531,-1543,-1556,-1568,-1581,-1593, + -1606,-1618,-1631,-1643,-1656,-1668,-1681,-1693,-1706,-1718,-1731,-1743,-1756,-1768,-1781,-1793, + -1806,-1818,-1831,-1843,-1856,-1868,-1881,-1893,-1906,-1918,-1931,-1943,-1956,-1968,-1981,-1993, + -2006,-2018,-2031,-2043,-2055,-2068,-2080,-2093,-2105,-2118,-2130,-2143,-2155,-2168,-2180,-2193, + -2205,-2217,-2230,-2242,-2255,-2267,-2280,-2292,-2305,-2317,-2329,-2342,-2354,-2367,-2379,-2392, + -2404,-2416,-2429,-2441,-2454,-2466,-2479,-2491,-2503,-2516,-2528,-2541,-2553,-2566,-2578,-2590, + -2603,-2615,-2628,-2640,-2652,-2665,-2677,-2690,-2702,-2714,-2727,-2739,-2752,-2764,-2776,-2789, + -2801,-2813,-2826,-2838,-2851,-2863,-2875,-2888,-2900,-2912,-2925,-2937,-2949,-2962,-2974,-2987, + -2999,-3011,-3024,-3036,-3048,-3061,-3073,-3085,-3098,-3110,-3122,-3135,-3147,-3159,-3172,-3184, + -3196,-3209,-3221,-3233,-3246,-3258,-3270,-3283,-3295,-3307,-3320,-3332,-3344,-3356,-3369,-3381, + -3393,-3406,-3418,-3430,-3442,-3455,-3467,-3479,-3492,-3504,-3516,-3528,-3541,-3553,-3565,-3577, + -3590,-3602,-3614,-3627,-3639,-3651,-3663,-3676,-3688,-3700,-3712,-3724,-3737,-3749,-3761,-3773, + -3786,-3798,-3810,-3822,-3835,-3847,-3859,-3871,-3883,-3896,-3908,-3920,-3932,-3944,-3957,-3969, + -3981,-3993,-4005,-4018,-4030,-4042,-4054,-4066,-4078,-4091,-4103,-4115,-4127,-4139,-4151,-4164, + -4176,-4188,-4200,-4212,-4224,-4236,-4249,-4261,-4273,-4285,-4297,-4309,-4321,-4333,-4346,-4358, + -4370,-4382,-4394,-4406,-4418,-4430,-4442,-4455,-4467,-4479,-4491,-4503,-4515,-4527,-4539,-4551, + -4563,-4575,-4587,-4599,-4612,-4624,-4636,-4648,-4660,-4672,-4684,-4696,-4708,-4720,-4732,-4744, + -4756,-4768,-4780,-4792,-4804,-4816,-4828,-4840,-4852,-4864,-4876,-4888,-4900,-4912,-4924,-4936, + -4948,-4960,-4972,-4984,-4996,-5008,-5020,-5032,-5044,-5056,-5068,-5080,-5092,-5104,-5115,-5127, + -5139,-5151,-5163,-5175,-5187,-5199,-5211,-5223,-5235,-5247,-5259,-5270,-5282,-5294,-5306,-5318, + -5330,-5342,-5354,-5366,-5377,-5389,-5401,-5413,-5425,-5437,-5449,-5460,-5472,-5484,-5496,-5508, + -5520,-5531,-5543,-5555,-5567,-5579,-5591,-5602,-5614,-5626,-5638,-5650,-5661,-5673,-5685,-5697, + -5708,-5720,-5732,-5744,-5756,-5767,-5779,-5791,-5803,-5814,-5826,-5838,-5850,-5861,-5873,-5885, + -5897,-5908,-5920,-5932,-5943,-5955,-5967,-5979,-5990,-6002,-6014,-6025,-6037,-6049,-6060,-6072, + -6084,-6095,-6107,-6119,-6130,-6142,-6154,-6165,-6177,-6189,-6200,-6212,-6223,-6235,-6247,-6258, + -6270,-6281,-6293,-6305,-6316,-6328,-6339,-6351,-6363,-6374,-6386,-6397,-6409,-6420,-6432,-6444, + -6455,-6467,-6478,-6490,-6501,-6513,-6524,-6536,-6547,-6559,-6570,-6582,-6593,-6605,-6616,-6628, + -6639,-6651,-6662,-6674,-6685,-6697,-6708,-6720,-6731,-6743,-6754,-6766,-6777,-6788,-6800,-6811, + -6823,-6834,-6846,-6857,-6868,-6880,-6891,-6903,-6914,-6925,-6937,-6948,-6960,-6971,-6982,-6994, + -7005,-7016,-7028,-7039,-7050,-7062,-7073,-7084,-7096,-7107,-7118,-7130,-7141,-7152,-7164,-7175, + -7186,-7198,-7209,-7220,-7231,-7243,-7254,-7265,-7276,-7288,-7299,-7310,-7321,-7333,-7344,-7355, + -7366,-7378,-7389,-7400,-7411,-7423,-7434,-7445,-7456,-7467,-7478,-7490,-7501,-7512,-7523,-7534, + -7545,-7557,-7568,-7579,-7590,-7601,-7612,-7623,-7635,-7646,-7657,-7668,-7679,-7690,-7701,-7712, + -7723,-7734,-7746,-7757,-7768,-7779,-7790,-7801,-7812,-7823,-7834,-7845,-7856,-7867,-7878,-7889, + -7900,-7911,-7922,-7933,-7944,-7955,-7966,-7977,-7988,-7999,-8010,-8021,-8032,-8043,-8054,-8065, + -8076,-8087,-8098,-8108,-8119,-8130,-8141,-8152,-8163,-8174,-8185,-8196,-8207,-8217,-8228,-8239, + -8250,-8261,-8272,-8283,-8293,-8304,-8315,-8326,-8337,-8347,-8358,-8369,-8380,-8391,-8401,-8412, + -8423,-8434,-8445,-8455,-8466,-8477,-8488,-8498,-8509,-8520,-8531,-8541,-8552,-8563,-8573,-8584, + -8595,-8606,-8616,-8627,-8638,-8648,-8659,-8670,-8680,-8691,-8702,-8712,-8723,-8734,-8744,-8755, + -8765,-8776,-8787,-8797,-8808,-8818,-8829,-8840,-8850,-8861,-8871,-8882,-8892,-8903,-8914,-8924, + -8935,-8945,-8956,-8966,-8977,-8987,-8998,-9008,-9019,-9029,-9040,-9050,-9061,-9071,-9082,-9092, + -9102,-9113,-9123,-9134,-9144,-9155,-9165,-9175,-9186,-9196,-9207,-9217,-9227,-9238,-9248,-9259, + -9269,-9279,-9290,-9300,-9310,-9321,-9331,-9341,-9352,-9362,-9372,-9383,-9393,-9403,-9413,-9424, + -9434,-9444,-9455,-9465,-9475,-9485,-9496,-9506,-9516,-9526,-9537,-9547,-9557,-9567,-9577,-9588, + -9598,-9608,-9618,-9628,-9638,-9649,-9659,-9669,-9679,-9689,-9699,-9709,-9720,-9730,-9740,-9750, + -9760,-9770,-9780,-9790,-9800,-9810,-9820,-9830,-9841,-9851,-9861,-9871,-9881,-9891,-9901,-9911, + -9921,-9931,-9941,-9951,-9961,-9971,-9981,-9991,-10001,-10010,-10020,-10030,-10040,-10050,-10060,-10070, + -10080,-10090,-10100,-10110,-10120,-10129,-10139,-10149,-10159,-10169,-10179,-10189,-10198,-10208,-10218,-10228, + -10238,-10248,-10257,-10267,-10277,-10287,-10296,-10306,-10316,-10326,-10336,-10345,-10355,-10365,-10374,-10384, + -10394,-10404,-10413,-10423,-10433,-10442,-10452,-10462,-10471,-10481,-10491,-10500,-10510,-10520,-10529,-10539, + -10549,-10558,-10568,-10577,-10587,-10597,-10606,-10616,-10625,-10635,-10644,-10654,-10663,-10673,-10683,-10692, + -10702,-10711,-10721,-10730,-10740,-10749,-10759,-10768,-10778,-10787,-10796,-10806,-10815,-10825,-10834,-10844, + -10853,-10862,-10872,-10881,-10891,-10900,-10909,-10919,-10928,-10937,-10947,-10956,-10966,-10975,-10984,-10994, + -11003,-11012,-11021,-11031,-11040,-11049,-11059,-11068,-11077,-11086,-11096,-11105,-11114,-11123,-11133,-11142, + -11151,-11160,-11169,-11179,-11188,-11197,-11206,-11215,-11224,-11234,-11243,-11252,-11261,-11270,-11279,-11288, + -11297,-11307,-11316,-11325,-11334,-11343,-11352,-11361,-11370,-11379,-11388,-11397,-11406,-11415,-11424,-11433, + -11442,-11451,-11460,-11469,-11478,-11487,-11496,-11505,-11514,-11523,-11532,-11541,-11550,-11559,-11567,-11576, + -11585,-11594,-11603,-11612,-11621,-11630,-11638,-11647,-11656,-11665,-11674,-11683,-11691,-11700,-11709,-11718, + -11727,-11735,-11744,-11753,-11762,-11770,-11779,-11788,-11797,-11805,-11814,-11823,-11831,-11840,-11849,-11857, + -11866,-11875,-11883,-11892,-11901,-11909,-11918,-11927,-11935,-11944,-11952,-11961,-11970,-11978,-11987,-11995, + -12004,-12012,-12021,-12029,-12038,-12046,-12055,-12064,-12072,-12080,-12089,-12097,-12106,-12114,-12123,-12131, + -12140,-12148,-12157,-12165,-12173,-12182,-12190,-12199,-12207,-12215,-12224,-12232,-12240,-12249,-12257,-12266, + -12274,-12282,-12290,-12299,-12307,-12315,-12324,-12332,-12340,-12348,-12357,-12365,-12373,-12381,-12390,-12398, + -12406,-12414,-12423,-12431,-12439,-12447,-12455,-12463,-12472,-12480,-12488,-12496,-12504,-12512,-12520,-12528, + -12537,-12545,-12553,-12561,-12569,-12577,-12585,-12593,-12601,-12609,-12617,-12625,-12633,-12641,-12649,-12657, + -12665,-12673,-12681,-12689,-12697,-12705,-12713,-12721,-12729,-12736,-12744,-12752,-12760,-12768,-12776,-12784, + -12792,-12799,-12807,-12815,-12823,-12831,-12839,-12846,-12854,-12862,-12870,-12878,-12885,-12893,-12901,-12909, + -12916,-12924,-12932,-12939,-12947,-12955,-12963,-12970,-12978,-12986,-12993,-13001,-13008,-13016,-13024,-13031, + -13039,-13047,-13054,-13062,-13069,-13077,-13085,-13092,-13100,-13107,-13115,-13122,-13130,-13137,-13145,-13152, + -13160,-13167,-13175,-13182,-13190,-13197,-13205,-13212,-13219,-13227,-13234,-13242,-13249,-13256,-13264,-13271, + -13279,-13286,-13293,-13301,-13308,-13315,-13323,-13330,-13337,-13344,-13352,-13359,-13366,-13374,-13381,-13388, + -13395,-13403,-13410,-13417,-13424,-13431,-13439,-13446,-13453,-13460,-13467,-13474,-13482,-13489,-13496,-13503, + -13510,-13517,-13524,-13531,-13538,-13546,-13553,-13560,-13567,-13574,-13581,-13588,-13595,-13602,-13609,-13616, + -13623,-13630,-13637,-13644,-13651,-13658,-13665,-13671,-13678,-13685,-13692,-13699,-13706,-13713,-13720,-13727, + -13733,-13740,-13747,-13754,-13761,-13768,-13774,-13781,-13788,-13795,-13802,-13808,-13815,-13822,-13829,-13835, + -13842,-13849,-13856,-13862,-13869,-13876,-13882,-13889,-13896,-13902,-13909,-13916,-13922,-13929,-13935,-13942, + -13949,-13955,-13962,-13968,-13975,-13981,-13988,-13995,-14001,-14008,-14014,-14021,-14027,-14034,-14040,-14047, + -14053,-14059,-14066,-14072,-14079,-14085,-14092,-14098,-14104,-14111,-14117,-14124,-14130,-14136,-14143,-14149, + -14155,-14162,-14168,-14174,-14181,-14187,-14193,-14199,-14206,-14212,-14218,-14224,-14231,-14237,-14243,-14249, + -14256,-14262,-14268,-14274,-14280,-14286,-14293,-14299,-14305,-14311,-14317,-14323,-14329,-14335,-14341,-14347, + -14354,-14360,-14366,-14372,-14378,-14384,-14390,-14396,-14402,-14408,-14414,-14420,-14426,-14432,-14438,-14443, + -14449,-14455,-14461,-14467,-14473,-14479,-14485,-14491,-14497,-14502,-14508,-14514,-14520,-14526,-14531,-14537, + -14543,-14549,-14555,-14560,-14566,-14572,-14578,-14583,-14589,-14595,-14601,-14606,-14612,-14618,-14623,-14629, + -14635,-14640,-14646,-14651,-14657,-14663,-14668,-14674,-14680,-14685,-14691,-14696,-14702,-14707,-14713,-14718, + -14724,-14729,-14735,-14740,-14746,-14751,-14757,-14762,-14768,-14773,-14779,-14784,-14789,-14795,-14800,-14806, + -14811,-14816,-14822,-14827,-14832,-14838,-14843,-14848,-14854,-14859,-14864,-14870,-14875,-14880,-14885,-14891, + -14896,-14901,-14906,-14911,-14917,-14922,-14927,-14932,-14937,-14943,-14948,-14953,-14958,-14963,-14968,-14973, + -14978,-14984,-14989,-14994,-14999,-15004,-15009,-15014,-15019,-15024,-15029,-15034,-15039,-15044,-15049,-15054, + -15059,-15064,-15069,-15074,-15078,-15083,-15088,-15093,-15098,-15103,-15108,-15113,-15118,-15122,-15127,-15132, + -15137,-15142,-15146,-15151,-15156,-15161,-15166,-15170,-15175,-15180,-15184,-15189,-15194,-15199,-15203,-15208, + -15213,-15217,-15222,-15227,-15231,-15236,-15240,-15245,-15250,-15254,-15259,-15263,-15268,-15273,-15277,-15282, + -15286,-15291,-15295,-15300,-15304,-15309,-15313,-15318,-15322,-15326,-15331,-15335,-15340,-15344,-15349,-15353, + -15357,-15362,-15366,-15370,-15375,-15379,-15383,-15388,-15392,-15396,-15401,-15405,-15409,-15414,-15418,-15422, + -15426,-15430,-15435,-15439,-15443,-15447,-15451,-15456,-15460,-15464,-15468,-15472,-15476,-15481,-15485,-15489, + -15493,-15497,-15501,-15505,-15509,-15513,-15517,-15521,-15525,-15529,-15533,-15537,-15541,-15545,-15549,-15553, + -15557,-15561,-15565,-15569,-15573,-15577,-15581,-15584,-15588,-15592,-15596,-15600,-15604,-15608,-15611,-15615, + -15619,-15623,-15627,-15630,-15634,-15638,-15642,-15645,-15649,-15653,-15656,-15660,-15664,-15668,-15671,-15675, + -15679,-15682,-15686,-15689,-15693,-15697,-15700,-15704,-15707,-15711,-15715,-15718,-15722,-15725,-15729,-15732, + -15736,-15739,-15743,-15746,-15750,-15753,-15757,-15760,-15763,-15767,-15770,-15774,-15777,-15780,-15784,-15787, + -15791,-15794,-15797,-15801,-15804,-15807,-15810,-15814,-15817,-15820,-15824,-15827,-15830,-15833,-15837,-15840, + -15843,-15846,-15849,-15853,-15856,-15859,-15862,-15865,-15868,-15871,-15875,-15878,-15881,-15884,-15887,-15890, + -15893,-15896,-15899,-15902,-15905,-15908,-15911,-15914,-15917,-15920,-15923,-15926,-15929,-15932,-15935,-15938, + -15941,-15944,-15946,-15949,-15952,-15955,-15958,-15961,-15964,-15966,-15969,-15972,-15975,-15978,-15980,-15983, + -15986,-15989,-15991,-15994,-15997,-16000,-16002,-16005,-16008,-16010,-16013,-16016,-16018,-16021,-16024,-16026, + -16029,-16031,-16034,-16037,-16039,-16042,-16044,-16047,-16049,-16052,-16054,-16057,-16059,-16062,-16064,-16067, + -16069,-16072,-16074,-16076,-16079,-16081,-16084,-16086,-16088,-16091,-16093,-16096,-16098,-16100,-16103,-16105, + -16107,-16109,-16112,-16114,-16116,-16119,-16121,-16123,-16125,-16128,-16130,-16132,-16134,-16136,-16138,-16141, + -16143,-16145,-16147,-16149,-16151,-16153,-16156,-16158,-16160,-16162,-16164,-16166,-16168,-16170,-16172,-16174, + -16176,-16178,-16180,-16182,-16184,-16186,-16188,-16190,-16192,-16194,-16195,-16197,-16199,-16201,-16203,-16205, + -16207,-16209,-16210,-16212,-16214,-16216,-16218,-16219,-16221,-16223,-16225,-16226,-16228,-16230,-16232,-16233, + -16235,-16237,-16238,-16240,-16242,-16243,-16245,-16247,-16248,-16250,-16251,-16253,-16255,-16256,-16258,-16259, + -16261,-16262,-16264,-16265,-16267,-16268,-16270,-16271,-16273,-16274,-16276,-16277,-16279,-16280,-16281,-16283, + -16284,-16286,-16287,-16288,-16290,-16291,-16292,-16294,-16295,-16296,-16298,-16299,-16300,-16301,-16303,-16304, + -16305,-16306,-16308,-16309,-16310,-16311,-16312,-16313,-16315,-16316,-16317,-16318,-16319,-16320,-16321,-16323, + -16324,-16325,-16326,-16327,-16328,-16329,-16330,-16331,-16332,-16333,-16334,-16335,-16336,-16337,-16338,-16339, + -16340,-16341,-16341,-16342,-16343,-16344,-16345,-16346,-16347,-16348,-16348,-16349,-16350,-16351,-16352,-16352, + -16353,-16354,-16355,-16355,-16356,-16357,-16358,-16358,-16359,-16360,-16360,-16361,-16362,-16362,-16363,-16364, + -16364,-16365,-16365,-16366,-16367,-16367,-16368,-16368,-16369,-16369,-16370,-16370,-16371,-16371,-16372,-16372, + -16373,-16373,-16374,-16374,-16375,-16375,-16375,-16376,-16376,-16377,-16377,-16377,-16378,-16378,-16378,-16379, + -16379,-16379,-16380,-16380,-16380,-16380,-16381,-16381,-16381,-16381,-16382,-16382,-16382,-16382,-16382,-16383, + -16383,-16383,-16383,-16383,-16383,-16383,-16384,-16384,-16384,-16384,-16384,-16384,-16384,-16384,-16384,-16384, + -16384,-16384, +};} diff --git a/gp2geom.cpp b/gp2geom.cpp new file mode 100644 index 0000000..2f896e7 --- /dev/null +++ b/gp2geom.cpp @@ -0,0 +1,261 @@ +#include "gp2geom.hpp" + +// Cosine source. Default: the build-time-verified formula table (gp2cos.hpp), +// generated at compile time and static_assert'd against GP2's bytes. If a +// toolchain struggles with the constexpr generation, define GP2GEOM_EMBEDDED_COS +// to use the existing extracted table instead — the two are bit-identical +// (verified on all 65536 angles), so the result is unchanged either way. +#ifndef GP2GEOM_EMBEDDED_COS +# include "gp2cos.hpp" +#else +extern "C" const short gp2cc_COS[4200]; +#endif + +namespace gp2geom { +namespace { + +// ============================== units & model =============================== +// +// ANGLES are binary radians: 65536 units per full turn, so a quarter turn is +// 0x4000. Wrapping is automatic in 16 bits and trig is a direct table lookup +// with no range reduction. The heading is integrated in Q16 fixed point — the +// whole-unit angle is the top 16 bits of a 32-bit accumulator. +constexpr int kQuarterTurn = 0x4000; // 90 degrees +constexpr int kQ16 = 16; // heading fractional bits + +// Each segment is a FIXED-LENGTH straight chord. The engine steps 128 world +// units per segment; in its 1/8-world-unit grid that is 1024. +constexpr int kChordEighths = 0x400; // 1024 = 128 world units, in 1/8 units +constexpr int kChordShift = 14; // chord = (cos * kChordEighths) >> 14 + +// The cc-line's per-segment "theta-prime" tilt (tseg+0x14) is the section's +// per-segment turn scaled by round(pi/2 * 2^14). Produced by the geometry pass, +// consumed later by the racing-line solver. +constexpr int kHalfPiQ14 = 0x6488; // round( (pi/2) * 2^14 ) = 25736 + +// side vector = unit perpendicular to the start heading, scaled by road width; +// one component = ((trig * width) >> 17) << 6 (InitTrackSegs 0x796C3/0x79735). + +// signed 16-bit wrap (binary-radian angles live in 16 bits). +constexpr int wrap16(int v) { v &= 0xFFFF; return (v & 0x8000) ? v - 0x10000 : v; } + +// raw, grid-sampled cosine (amplitude 0x4000) of a binary-radian angle. +inline int rawCos(int a) { +#ifndef GP2GEOM_EMBEDDED_COS + return scos(a); // formula table (gp2cos.hpp) +#else + a = wrap16(a); if (a < 0) a = -a; // existing extracted table + return gp2cc_COS[((a >> 2) & 0xFFFE) >> 1]; +#endif +} +inline int cosine(int angle) { return rawCos(angle); } +inline int sine (int angle) { return rawCos(kQuarterTurn - angle); } // sin(x) = cos(90-x) + +// little-endian readers. +inline int rdU16(const std::uint8_t* d, int o) { return d[o] | (d[o + 1] << 8); } +inline int rdS16(const std::uint8_t* d, int o) { return wrap16(rdU16(d, o)); } +inline std::int32_t rdS32(const std::uint8_t* d, int o) { + return (std::int32_t)(d[o] | (d[o+1] << 8) | (d[o+2] << 16) | (d[o+3] << 24)); +} + +// Track-command argument sizes (datparse). Opcode 0x45 is conditional on a c5 +// bit and is handled separately; -1 means an unknown opcode (stream desync). +constexpr int kArgSize[0x66] = { + 2,2,2,0,0,4,0,0,2,2, 10,10,2,2,4,4,2,2,2,2, 2,2,0,0,2,2,4,0,0,0, + 0,0,0,0,0,0,0,0,4,4, 0,2,6,4,8,4,2,4,2,2, 2,2,4,4,2,2,26,2,4,8, + 14,4,4,4,2,2,4,4,6,14, 2,4,14,16,8,8,4,6,2,2, 4,0,2,2,0,2,0,0,0,2, + 2,0,2,4,6,6,2,0,0,0, 0,0 /* 0x64, 0x65 */ +}; + +enum Side { Left, Right }; + +// ============================== the turtle ================================== +class TrackBuilder { +public: + TrackBuilder(const std::uint8_t* dat, gp2cc_track& out) : d(dat), t(out) {} + + int run() { + readHeader(); + if (!walkSections()) return -2; // turn + half-step + width, per segment + t.n = segCount - 1; // drop the over-produced wrap segment + computeSideVectors(); // road-edge perpendiculars + integratePositions(); // advance one fixed chord per segment + distributeClosureGap(); // DDA warp so the lap closes exactly + return 0; + } + +private: + const std::uint8_t* d; + gp2cc_track& t; + + // turtle state during the section walk + std::int32_t heading = 0; // d_TrkStrtAngle, Q16 binary radians + std::int32_t sectionStartHeading = 0; // d_StartAngle, the side-vector reference + int widthL = 0, widthR = 0; // current road width (left/right halves) + int dWidthL = 0, dWidthR = 0; // per-segment width ramp deltas + int rampL = 0, rampR = 0; // segments left in each width transition + bool halfStepFull = false; // c5 bit12: full-precision half-step + bool c5bit9 = false; // c5 bit9: opcode-0x45 arg size + int segCount = 0; + int headerEnd = 0; + + // -- header: start pose, widths, flags; cursor lands at the command stream -- + void readHeader() { + const int base = 0x1020 + rdS32(d, 0x1010); // MainTrackData + t.hdr_angle = (std::int16_t)rdS16(d, base + 0); + t.hdr_Y = (std::int16_t)rdS16(d, base + 4); + t.hdr_X = (std::int16_t)rdS16(d, base + 8); + t.hdr_width = (std::uint16_t)rdU16(d, base + 0xA); + t.hdr_c5 = (std::uint16_t)rdU16(d, base + 0xE); + halfStepFull = (t.hdr_c5 & 0x1000) != 0; + c5bit9 = (t.hdr_c5 & 0x0200) != 0; + const int unknownCount = rdU16(d, base + 0x12); + + heading = (std::int32_t)((t.hdr_angle & 0xFFFF) << kQ16); + sectionStartHeading = heading; + widthL = widthR = (int)t.hdr_width; + + int o = base + 0x14; // skip the "4 unknowns" block + for (int i = 0; i < unknownCount; ++i) { int ax = rdU16(d, o); o += 2; if (ax != 0x1F) o += 2; } + headerEnd = o; + } + + // -- walk the command stream: op commands tweak width; geometry commands are + // sections the turtle drives through. 0xFFFF ends it (cc-line stream next). -- + bool walkSections() { + int o = headerEnd; + for (;;) { + const int w = rdU16(d, o); + if (w == 0xFFFF) { t.ccoff = o + 2; return true; } + if (w & 0x8000) { o = applyOpCommand(o, w); if (o < 0) return false; } + else { o = buildSection(o, w); if (o < 0) return false; } + } + } + + int applyOpCommand(int o, int w) { + const int op = (w >> 8) & 0x7F; + const int sz = (op == 0x45) ? (c5bit9 ? 14 : 12) + : (op < 0x66) ? kArgSize[op] + : -1; + if (sz < 0) return -1; // unknown opcode -> desync + if (op == 0x34 || op == 0x05) setWidthTransition(Left, rdS16(d, o + 2), rdS16(d, o + 4)); + if (op == 0x35 || op == 0x05) setWidthTransition(Right, rdS16(d, o + 2), rdS16(d, o + 4)); + return o + 2 + sz; + } + + // -- one geometry command = one section of `segments` fixed chords, all + // turning by the same per-segment amount. -- + int buildSection(int o, int segments) { + const int perSegTurn = rdS16(d, o + 2); // d1word + const std::int32_t turn = (std::int32_t)perSegTurn << kQ16; + // theta'-tilt the cc-line will need later (computed from this command). + const std::int32_t f14 = ((std::int32_t)perSegTurn * kHalfPiQ14) >> 14; + o += 10; + + // MIDPOINT INTEGRATION RULE: the heading is sampled at each segment's + // midpoint, so a section starts and ends with a HALF turn and takes full + // turns in between. This centres each straight chord on the underlying arc + // (no corner-cutting) and makes curvature continuous across section joins: + // a section's end-half plus the next section's start-half = one full turn. + std::int32_t halfTurn = turn >> 1; + // bit12-clear tracks zero only the LOW 16 bits of the half-step (the engine's + // `xor ax,ax`), i.e. they floor d1/2 to a whole binary-radian unit. + if (!halfStepFull) halfTurn &= ~0xFFFF; + + for (int k = 0; k < segments; ++k) { + if (segCount >= GP2CC_MAXSEG) return -1; + if (k == 0) { sectionStartHeading = heading; heading += halfTurn; } // start half-step + else { heading += turn; sectionStartHeading += turn; } // full turn + emitSegment(f14); + } + heading += halfTurn; // end half-step (pairs with the next start half) + return o; + } + + void emitSegment(std::int32_t f14) { + const int i = segCount++; + t.fAngle[i] = heading; + t.angle[i] = (std::int16_t)(heading >> kQ16); + t.startAngle[i] = (std::int16_t)(sectionStartHeading >> kQ16); + t.f14[i] = f14; + t.widthL[i] = widthL; // store current width, THEN advance the ramp + t.widthR[i] = widthR; + rampWidthOneStep(); + } + + // -- width transitions: len==0 sets it instantly, len>0 ramps linearly. -- + void setWidthTransition(Side s, int len, int target) { + int& cur = (s == Left) ? widthL : widthR; + int& delta = (s == Left) ? dWidthL : dWidthR; + int& ramp = (s == Left) ? rampL : rampR; + if (len == 0) { cur = target; delta = 0; ramp = 0; } + else { ramp = len; delta = (target - cur) / len; } // truncating step + } + void rampWidthOneStep() { + if (rampL > 0) { widthL += dWidthL; --rampL; } + if (rampR > 0) { widthR += dWidthR; --rampR; } + } + + // -- road edges = centre +/- sideVector; sideVector is perpendicular to the + // start heading, scaled by width. -- + void computeSideVectors() { + const int n = t.n; + for (int i = 0; i < n; ++i) { + const int sa = wrap16(t.startAngle[i]); + const int cs = cosine(sa), sn = sine(sa); + t.sideRX[i] = component(cs, t.widthR[i]); + t.sideRY[i] = component(sn, t.widthR[i]); + t.sideLX[i] = component(cs, t.widthL[i]); + t.sideLY[i] = component(sn, t.widthL[i]); + } + } + static std::int16_t component(int trig, int width) { + // ((trig*width) >> 16) keeps the perpendicular unit*width, >>1 halves it + // and <<6 puts it in the engine's edge units. Folded through int16 like + // the engine's word ops. + return (std::int16_t)(((std::int16_t)((int)trig * width >> 16) >> 1) << 6); + } + + // -- advance one fixed 128-unit chord per segment along the heading (forward + // Euler), accumulating the raw, not-yet-closed position. -- + void integratePositions() { + std::int32_t x = (std::int32_t)t.hdr_X << 3; // start position, 1/8 world units + std::int32_t y = (std::int32_t)t.hdr_Y << 3; + const int n = t.n; + for (int i = 0; i < n; ++i) { + const int h = t.fAngle[i] >> kQ16; + t.rawX8[i] = x; t.rawY8[i] = y; + x += (cosine(h) * kChordEighths) >> kChordShift; // X step = cos(h) * 128 + y += (sine(h) * kChordEighths) >> kChordShift; // Y step = sin(h) * 128 + } + t.gapX8 = x - t.rawX8[0]; // raw integration doesn't close the loop exactly + t.gapY8 = y - t.rawY8[0]; + } + + // -- close the loop: the chord rounding leaves a gap between end and start. + // Spread it backward with a DDA sweep so segment i carries + // sign(gap)*floor(i*|gap|/M), M = n+1 (the engine counts the wrap segment + // we dropped). First segment ~0, last ~the whole gap -> the lap closes. -- + void distributeClosureGap() { + const int n = t.n; + const std::int64_t M = (std::int64_t)n + 1; + const std::int64_t absX = t.gapX8 < 0 ? -(std::int64_t)t.gapX8 : t.gapX8; + const std::int64_t absY = t.gapY8 < 0 ? -(std::int64_t)t.gapY8 : t.gapY8; + for (int i = 0; i < n; ++i) { + const std::int32_t sx = (std::int32_t)((absX * i) / M); + const std::int32_t sy = (std::int32_t)((absY * i) / M); + t.X8[i] = t.rawX8[i] - (t.gapX8 < 0 ? -sx : sx); + t.Y8[i] = t.rawY8[i] - (t.gapY8 < 0 ? -sy : sy); + } + } +}; + +} // anonymous namespace + +int compileGeometry(const std::uint8_t* dat, int len, gp2cc_track& out) { + (void)len; + TrackBuilder builder(dat, out); + return builder.run(); +} + +} // namespace gp2geom diff --git a/gp2geom.hpp b/gp2geom.hpp new file mode 100644 index 0000000..fb4bde5 --- /dev/null +++ b/gp2geom.hpp @@ -0,0 +1,23 @@ +#pragma once +#include +#include "gp2cc.h" // shared output format: gp2cc_track + GP2CC_MAXSEG + +// --------------------------------------------------------------------------- +// gp2geom — GP2 track-geometry compiler, written as the geometric process it +// actually is (a fixed-point "turtle" that integrates a curve from its +// curvature), rather than as a transcription of the disassembly. +// +// Same bit-exact integers as the engine, same output struct as the original +// gp2cc compiler (so it is a drop-in for gp2cc_compile_geometry_buf), but the +// steps are named — turn, advance one chord, midpoint half-step, ramp width, +// close the loop — and every "magic" constant is derived in a comment. +// +// The cosine it uses is the build-time-verified table from gp2cos.hpp. +// --------------------------------------------------------------------------- +namespace gp2geom { + +// Compile a track .dat image already in memory into `out`. Returns 0 on success, +// negative on a command-stream desync. Bit-exact with the game. +int compileGeometry(const std::uint8_t* dat, int len, gp2cc_track& out); + +} // namespace gp2geom From 23d5b0c144d4d908e16fb6c9d6f73d8d5f356558 Mon Sep 17 00:00:00 2001 From: Roberto Remedio Date: Mon, 15 Jun 2026 09:22:34 -0300 Subject: [PATCH 04/12] Wire the cc-line solver to C++ + the formula atan table (gp2ccline) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports GP2's racing-line kernel (UACalcBestLine + helpers: reproject, build_centre, nudge, q30 trig, the fixed-iteration integer sqrts, atan2) to C++, fed by the formula-generated tables (gp2cos.hpp cosine + gp2atan.hpp arctan). It is a faithful transcription of the chaotic fixed-point math, not a re-derivation — validated BIT-IDENTICAL to the original gp2cc_compute_ccline (bestLine, angle18, cmdStartSeg, numCmds) on F1CT01/02a/03/09/11/16. drawComputed now runs entirely on the C++/formula path: gp2geom::compileGeometry for the geometry and gp2geom::computeCcLine for the racing line. gp2cc.c/.h/_tables.c stay compiled (fallback, the shared structs, and gp2cc_gv which the view's drawing still uses) until the new files are tested in the editor. gp2atan.hpp now wired in (was sandbox-only). vcxproj builds gp2ccline.cpp (stdcpp17, NotUsing PCH, raised /constexpr:steps). View drawing unchanged. --- GPTrack.cpp | 7 +- TrackEditor.vcxproj | 9 ++ gp2atan.hpp | 78 +++++++++++++ gp2atan_reference.hpp | 135 +++++++++++++++++++++ gp2ccline.cpp | 266 ++++++++++++++++++++++++++++++++++++++++++ gp2ccline.hpp | 24 ++++ 6 files changed, 516 insertions(+), 3 deletions(-) create mode 100644 gp2atan.hpp create mode 100644 gp2atan_reference.hpp create mode 100644 gp2ccline.cpp create mode 100644 gp2ccline.hpp diff --git a/GPTrack.cpp b/GPTrack.cpp index 31dda76..69eed3c 100644 --- a/GPTrack.cpp +++ b/GPTrack.cpp @@ -11,8 +11,9 @@ #include "TrackSection.h" #include "TrackObjectDefinition.h" #include "CCLineSection.h" -#include "gp2cc.h" // bit-exact cc-line kernel (chaotic; kept as-is) -#include "gp2geom.hpp" // semantic C++ track-geometry compiler (drop-in for gp2cc_compile_geometry_buf) +#include "gp2cc.h" // shared structs + the original C kernel (kept as fallback) +#include "gp2geom.hpp" // semantic C++ track-geometry compiler (drop-in for gp2cc_compile_geometry_buf) +#include "gp2ccline.hpp" // C++ cc-line solver on the formula tables (drop-in for gp2cc_compute_ccline) #include "TrackCmd.h" #include "InternalObject.h" #include "JamFileEditor.h" @@ -4071,7 +4072,7 @@ void GPTrack::drawComputed(Display *g) } static int cmdSeg[GP2CC_MAXSEG]; // first segment of each cc-command (=sector) int numCmds = 0; - gp2cc_compute_ccline(&cg, trackdata, 65535, gt.ccoff, bl, a18, cmdSeg, &numCmds); + gp2geom::computeCcLine(cg, trackdata, 65535, gt.ccoff, bl, a18, cmdSeg, &numCmds); // editor frame: world units -> editor units = /128 (128 world-u/seg = 1 editor-u), // anchored at section 0's start. Edges + cc-line = centre + perpendicular(heading)* diff --git a/TrackEditor.vcxproj b/TrackEditor.vcxproj index 1798ef7..bcea40b 100755 --- a/TrackEditor.vcxproj +++ b/TrackEditor.vcxproj @@ -190,6 +190,12 @@ define GP2GEOM_EMBEDDED_COS to use the extracted table (bit-identical). --> /constexpr:steps4000000 %(AdditionalOptions) + + stdcpp17 + NotUsing + + /constexpr:steps4000000 %(AdditionalOptions) + @@ -299,6 +305,9 @@ + + + diff --git a/gp2atan.hpp b/gp2atan.hpp new file mode 100644 index 0000000..11813f3 --- /dev/null +++ b/gp2atan.hpp @@ -0,0 +1,78 @@ +#pragma once +#include +#include +#include "gp2atan_reference.hpp" // golden oracle: GP2's extracted t_ArithTab1 bytes + +// --------------------------------------------------------------------------- +// GP2 geometry — the arctangent table expressed as its generating formula. +// +// GP2's t_ArithTab1 maps a ratio i/2048 (i = 0..2048, so the ratio runs 0..1) +// to an angle in binary-radian units (65536 per full turn). It is exactly: +// +// ATAN1[i] = round( atan(i / 2048) * 65536 / (2*pi) ) +// +// Unlike the cosine table, this one is CLEAN: every real entry matches the +// formula with no hand-tweaked exceptions (verified 2049/2049). Index 2049 is +// unused padding (0). It is also far less rounding-fragile than COS — only a +// single entry sits within 1e-3 of a .5 boundary, and it rounds correctly. +// +// So this is the poster child for replacing an opaque blob with a formula: +// one line of intent, no override. We still keep the compile-time guardrail +// (static_assert against GP2's bytes) as free insurance against an exotic +// toolchain ever flipping that one boundary entry. +// --------------------------------------------------------------------------- + +namespace gp2geom { + +inline constexpr int kAtanGrid = 2050; // 0..2048 real, 2049 = padding +inline constexpr int kAtanReal = 2048; // last real index +inline constexpr double kPiAtan = 3.14159265358979323846; + +// constexpr sqrt (Newton) and atan, since neither is portably constexpr before +// C++26. atan is range-reduced toward 0 via atan(x)=2*atan(x/(1+sqrt(1+x^2))) +// then a Taylor series on the small remainder; accurate to ~1e-15 on [0,1]. +constexpr double atanSqrt(double v) { + if (v <= 0.0) return 0.0; + double g = v; + for (int i = 0; i < 80; ++i) g = 0.5 * (g + v / g); + return g; +} +constexpr double arctan(double x) { + int k = 0; + while (x > 0.05) { x = x / (1.0 + atanSqrt(1.0 + x * x)); ++k; } + double sum = 0.0, t = x, x2 = x * x; + for (int n = 0; n < 12; ++n) { sum += t / (2 * n + 1); t *= -x2; } + for (int i = 0; i < k; ++i) sum *= 2.0; + return sum; +} + +constexpr short roundToShortA(double v) { + long r = (v >= 0.0) ? static_cast(v + 0.5) : static_cast(v - 0.5); + return static_cast(r); +} + +constexpr std::array makeAtanTable() { + std::array t{}; + for (int i = 0; i <= kAtanReal; ++i) + t[i] = roundToShortA(arctan(static_cast(i) / 2048.0) + * 65536.0 / (2.0 * kPiAtan)); + t[2049] = 0; // unused padding, matches GP2 + return t; +} + +inline constexpr std::array kArctan = makeAtanTable(); + +// ---- compile-time guardrail (no override needed for this table) ------------- +constexpr bool atanMatchesReference() { + for (int i = 0; i < kAtanGrid; ++i) + if (kArctan[i] != kAtanRef[i]) return false; + return true; +} +static_assert(atanMatchesReference(), + "gp2geom arctan table != GP2 reference bytes: a float rounding boundary " + "flipped on this toolchain. Inspect the diff before shipping."); + +static_assert(kArctan[0] == 0, "atan(0)"); +static_assert(kArctan[2048] == 8192, "atan(1) = pi/4 = 0x2000"); + +} // namespace gp2geom diff --git a/gp2atan_reference.hpp b/gp2atan_reference.hpp new file mode 100644 index 0000000..e36a07a --- /dev/null +++ b/gp2atan_reference.hpp @@ -0,0 +1,135 @@ +#pragma once +#include +// Golden reference: GP2 t_ArithTab1 bytes (file 0x1DFAF0), 2050 entries +// (indices 0..2048 are real; 2049 is unused padding = 0). +namespace gp2geom { inline constexpr short kAtanRef[2050] = { + 0,5,10,15,20,25,31,36,41,46,51,56,61,66,71,76, + 81,87,92,97,102,107,112,117,122,127,132,138,143,148,153,158, + 163,168,173,178,183,188,194,199,204,209,214,219,224,229,234,239, + 244,250,255,260,265,270,275,280,285,290,295,300,305,311,316,321, + 326,331,336,341,346,351,356,361,367,372,377,382,387,392,397,402, + 407,412,417,422,428,433,438,443,448,453,458,463,468,473,478,483, + 489,494,499,504,509,514,519,524,529,534,539,544,550,555,560,565, + 570,575,580,585,590,595,600,605,610,616,621,626,631,636,641,646, + 651,656,661,666,671,676,681,687,692,697,702,707,712,717,722,727, + 732,737,742,747,752,758,763,768,773,778,783,788,793,798,803,808, + 813,818,823,828,833,839,844,849,854,859,864,869,874,879,884,889, + 894,899,904,909,914,919,924,930,935,940,945,950,955,960,965,970, + 975,980,985,990,995,1000,1005,1010,1015,1020,1025,1031,1036,1041,1046,1051, + 1056,1061,1066,1071,1076,1081,1086,1091,1096,1101,1106,1111,1116,1121,1126,1131, + 1136,1141,1146,1151,1156,1161,1166,1172,1177,1182,1187,1192,1197,1202,1207,1212, + 1217,1222,1227,1232,1237,1242,1247,1252,1257,1262,1267,1272,1277,1282,1287,1292, + 1297,1302,1307,1312,1317,1322,1327,1332,1337,1342,1347,1352,1357,1362,1367,1372, + 1377,1382,1387,1392,1397,1402,1407,1412,1417,1422,1427,1432,1437,1442,1447,1452, + 1457,1462,1467,1472,1477,1482,1487,1492,1497,1502,1507,1512,1517,1522,1527,1532, + 1537,1542,1547,1552,1557,1562,1567,1572,1577,1582,1587,1592,1597,1602,1607,1612, + 1617,1622,1627,1632,1637,1642,1646,1651,1656,1661,1666,1671,1676,1681,1686,1691, + 1696,1701,1706,1711,1716,1721,1726,1731,1736,1741,1746,1751,1756,1761,1765,1770, + 1775,1780,1785,1790,1795,1800,1805,1810,1815,1820,1825,1830,1835,1840,1845,1849, + 1854,1859,1864,1869,1874,1879,1884,1889,1894,1899,1904,1909,1914,1918,1923,1928, + 1933,1938,1943,1948,1953,1958,1963,1968,1973,1977,1982,1987,1992,1997,2002,2007, + 2012,2017,2022,2027,2031,2036,2041,2046,2051,2056,2061,2066,2071,2076,2080,2085, + 2090,2095,2100,2105,2110,2115,2120,2124,2129,2134,2139,2144,2149,2154,2159,2163, + 2168,2173,2178,2183,2188,2193,2198,2202,2207,2212,2217,2222,2227,2232,2237,2241, + 2246,2251,2256,2261,2266,2271,2275,2280,2285,2290,2295,2300,2305,2309,2314,2319, + 2324,2329,2334,2338,2343,2348,2353,2358,2363,2367,2372,2377,2382,2387,2392,2396, + 2401,2406,2411,2416,2421,2425,2430,2435,2440,2445,2450,2454,2459,2464,2469,2474, + 2478,2483,2488,2493,2498,2502,2507,2512,2517,2522,2526,2531,2536,2541,2546,2550, + 2555,2560,2565,2570,2574,2579,2584,2589,2594,2598,2603,2608,2613,2617,2622,2627, + 2632,2637,2641,2646,2651,2656,2660,2665,2670,2675,2679,2684,2689,2694,2699,2703, + 2708,2713,2718,2722,2727,2732,2737,2741,2746,2751,2756,2760,2765,2770,2775,2779, + 2784,2789,2793,2798,2803,2808,2812,2817,2822,2827,2831,2836,2841,2846,2850,2855, + 2860,2864,2869,2874,2879,2883,2888,2893,2897,2902,2907,2912,2916,2921,2926,2930, + 2935,2940,2944,2949,2954,2959,2963,2968,2973,2977,2982,2987,2991,2996,3001,3005, + 3010,3015,3019,3024,3029,3033,3038,3043,3047,3052,3057,3061,3066,3071,3075,3080, + 3085,3089,3094,3099,3103,3108,3113,3117,3122,3127,3131,3136,3141,3145,3150,3155, + 3159,3164,3168,3173,3178,3182,3187,3192,3196,3201,3206,3210,3215,3219,3224,3229, + 3233,3238,3243,3247,3252,3256,3261,3266,3270,3275,3279,3284,3289,3293,3298,3302, + 3307,3312,3316,3321,3325,3330,3335,3339,3344,3348,3353,3358,3362,3367,3371,3376, + 3380,3385,3390,3394,3399,3403,3408,3412,3417,3422,3426,3431,3435,3440,3444,3449, + 3453,3458,3463,3467,3472,3476,3481,3485,3490,3494,3499,3503,3508,3513,3517,3522, + 3526,3531,3535,3540,3544,3549,3553,3558,3562,3567,3571,3576,3580,3585,3589,3594, + 3599,3603,3608,3612,3617,3621,3626,3630,3635,3639,3644,3648,3653,3657,3662,3666, + 3670,3675,3679,3684,3688,3693,3697,3702,3706,3711,3715,3720,3724,3729,3733,3738, + 3742,3747,3751,3756,3760,3764,3769,3773,3778,3782,3787,3791,3796,3800,3804,3809, + 3813,3818,3822,3827,3831,3836,3840,3844,3849,3853,3858,3862,3867,3871,3875,3880, + 3884,3889,3893,3898,3902,3906,3911,3915,3920,3924,3928,3933,3937,3942,3946,3950, + 3955,3959,3964,3968,3972,3977,3981,3985,3990,3994,3999,4003,4007,4012,4016,4021, + 4025,4029,4034,4038,4042,4047,4051,4055,4060,4064,4069,4073,4077,4082,4086,4090, + 4095,4099,4103,4108,4112,4116,4121,4125,4129,4134,4138,4142,4147,4151,4155,4160, + 4164,4168,4173,4177,4181,4186,4190,4194,4199,4203,4207,4211,4216,4220,4224,4229, + 4233,4237,4242,4246,4250,4254,4259,4263,4267,4272,4276,4280,4284,4289,4293,4297, + 4302,4306,4310,4314,4319,4323,4327,4331,4336,4340,4344,4349,4353,4357,4361,4366, + 4370,4374,4378,4383,4387,4391,4395,4400,4404,4408,4412,4416,4421,4425,4429,4433, + 4438,4442,4446,4450,4454,4459,4463,4467,4471,4476,4480,4484,4488,4492,4497,4501, + 4505,4509,4513,4518,4522,4526,4530,4534,4539,4543,4547,4551,4555,4559,4564,4568, + 4572,4576,4580,4585,4589,4593,4597,4601,4605,4610,4614,4618,4622,4626,4630,4634, + 4639,4643,4647,4651,4655,4659,4663,4668,4672,4676,4680,4684,4688,4692,4697,4701, + 4705,4709,4713,4717,4721,4725,4730,4734,4738,4742,4746,4750,4754,4758,4762,4767, + 4771,4775,4779,4783,4787,4791,4795,4799,4803,4807,4812,4816,4820,4824,4828,4832, + 4836,4840,4844,4848,4852,4856,4860,4865,4869,4873,4877,4881,4885,4889,4893,4897, + 4901,4905,4909,4913,4917,4921,4925,4929,4933,4937,4941,4945,4949,4954,4958,4962, + 4966,4970,4974,4978,4982,4986,4990,4994,4998,5002,5006,5010,5014,5018,5022,5026, + 5030,5034,5038,5042,5046,5050,5054,5058,5062,5066,5070,5074,5078,5082,5086,5090, + 5094,5097,5101,5105,5109,5113,5117,5121,5125,5129,5133,5137,5141,5145,5149,5153, + 5157,5161,5165,5169,5173,5177,5181,5184,5188,5192,5196,5200,5204,5208,5212,5216, + 5220,5224,5228,5232,5235,5239,5243,5247,5251,5255,5259,5263,5267,5271,5275,5278, + 5282,5286,5290,5294,5298,5302,5306,5310,5313,5317,5321,5325,5329,5333,5337,5341, + 5344,5348,5352,5356,5360,5364,5368,5371,5375,5379,5383,5387,5391,5395,5398,5402, + 5406,5410,5414,5418,5421,5425,5429,5433,5437,5441,5444,5448,5452,5456,5460,5464, + 5467,5471,5475,5479,5483,5486,5490,5494,5498,5502,5505,5509,5513,5517,5521,5524, + 5528,5532,5536,5540,5543,5547,5551,5555,5559,5562,5566,5570,5574,5577,5581,5585, + 5589,5592,5596,5600,5604,5608,5611,5615,5619,5623,5626,5630,5634,5638,5641,5645, + 5649,5652,5656,5660,5664,5667,5671,5675,5679,5682,5686,5690,5694,5697,5701,5705, + 5708,5712,5716,5720,5723,5727,5731,5734,5738,5742,5745,5749,5753,5757,5760,5764, + 5768,5771,5775,5779,5782,5786,5790,5793,5797,5801,5804,5808,5812,5815,5819,5823, + 5826,5830,5834,5837,5841,5845,5848,5852,5856,5859,5863,5867,5870,5874,5878,5881, + 5885,5888,5892,5896,5899,5903,5907,5910,5914,5917,5921,5925,5928,5932,5936,5939, + 5943,5946,5950,5954,5957,5961,5964,5968,5972,5975,5979,5982,5986,5990,5993,5997, + 6000,6004,6008,6011,6015,6018,6022,6025,6029,6033,6036,6040,6043,6047,6050,6054, + 6058,6061,6065,6068,6072,6075,6079,6082,6086,6089,6093,6097,6100,6104,6107,6111, + 6114,6118,6121,6125,6128,6132,6135,6139,6142,6146,6150,6153,6157,6160,6164,6167, + 6171,6174,6178,6181,6185,6188,6192,6195,6199,6202,6206,6209,6213,6216,6220,6223, + 6227,6230,6234,6237,6240,6244,6247,6251,6254,6258,6261,6265,6268,6272,6275,6279, + 6282,6286,6289,6292,6296,6299,6303,6306,6310,6313,6317,6320,6323,6327,6330,6334, + 6337,6341,6344,6348,6351,6354,6358,6361,6365,6368,6371,6375,6378,6382,6385,6389, + 6392,6395,6399,6402,6406,6409,6412,6416,6419,6423,6426,6429,6433,6436,6440,6443, + 6446,6450,6453,6456,6460,6463,6467,6470,6473,6477,6480,6483,6487,6490,6493,6497, + 6500,6504,6507,6510,6514,6517,6520,6524,6527,6530,6534,6537,6540,6544,6547,6550, + 6554,6557,6560,6564,6567,6570,6574,6577,6580,6584,6587,6590,6594,6597,6600,6604, + 6607,6610,6613,6617,6620,6623,6627,6630,6633,6637,6640,6643,6646,6650,6653,6656, + 6660,6663,6666,6669,6673,6676,6679,6683,6686,6689,6692,6696,6699,6702,6705,6709, + 6712,6715,6718,6722,6725,6728,6731,6735,6738,6741,6744,6748,6751,6754,6757,6761, + 6764,6767,6770,6774,6777,6780,6783,6787,6790,6793,6796,6799,6803,6806,6809,6812, + 6815,6819,6822,6825,6828,6832,6835,6838,6841,6844,6848,6851,6854,6857,6860,6863, + 6867,6870,6873,6876,6879,6883,6886,6889,6892,6895,6898,6902,6905,6908,6911,6914, + 6917,6921,6924,6927,6930,6933,6936,6940,6943,6946,6949,6952,6955,6958,6962,6965, + 6968,6971,6974,6977,6980,6984,6987,6990,6993,6996,6999,7002,7005,7009,7012,7015, + 7018,7021,7024,7027,7030,7033,7037,7040,7043,7046,7049,7052,7055,7058,7061,7064, + 7068,7071,7074,7077,7080,7083,7086,7089,7092,7095,7098,7101,7105,7108,7111,7114, + 7117,7120,7123,7126,7129,7132,7135,7138,7141,7144,7147,7150,7154,7157,7160,7163, + 7166,7169,7172,7175,7178,7181,7184,7187,7190,7193,7196,7199,7202,7205,7208,7211, + 7214,7217,7220,7223,7226,7229,7232,7235,7238,7241,7244,7247,7250,7253,7256,7259, + 7262,7265,7268,7271,7274,7277,7280,7283,7286,7289,7292,7295,7298,7301,7304,7307, + 7310,7313,7316,7319,7322,7325,7328,7331,7334,7337,7340,7343,7346,7349,7352,7355, + 7358,7361,7363,7366,7369,7372,7375,7378,7381,7384,7387,7390,7393,7396,7399,7402, + 7405,7408,7411,7413,7416,7419,7422,7425,7428,7431,7434,7437,7440,7443,7446,7448, + 7451,7454,7457,7460,7463,7466,7469,7472,7475,7477,7480,7483,7486,7489,7492,7495, + 7498,7501,7503,7506,7509,7512,7515,7518,7521,7524,7526,7529,7532,7535,7538,7541, + 7544,7547,7549,7552,7555,7558,7561,7564,7566,7569,7572,7575,7578,7581,7584,7586, + 7589,7592,7595,7598,7601,7603,7606,7609,7612,7615,7618,7620,7623,7626,7629,7632, + 7635,7637,7640,7643,7646,7649,7651,7654,7657,7660,7663,7665,7668,7671,7674,7677, + 7679,7682,7685,7688,7691,7693,7696,7699,7702,7705,7707,7710,7713,7716,7718,7721, + 7724,7727,7730,7732,7735,7738,7741,7743,7746,7749,7752,7754,7757,7760,7763,7765, + 7768,7771,7774,7776,7779,7782,7785,7787,7790,7793,7796,7798,7801,7804,7807,7809, + 7812,7815,7818,7820,7823,7826,7828,7831,7834,7837,7839,7842,7845,7848,7850,7853, + 7856,7858,7861,7864,7866,7869,7872,7875,7877,7880,7883,7885,7888,7891,7893,7896, + 7899,7902,7904,7907,7910,7912,7915,7918,7920,7923,7926,7928,7931,7934,7936,7939, + 7942,7944,7947,7950,7952,7955,7958,7960,7963,7966,7968,7971,7974,7976,7979,7982, + 7984,7987,7990,7992,7995,7997,8000,8003,8005,8008,8011,8013,8016,8019,8021,8024, + 8026,8029,8032,8034,8037,8040,8042,8045,8047,8050,8053,8055,8058,8060,8063,8066, + 8068,8071,8074,8076,8079,8081,8084,8087,8089,8092,8094,8097,8100,8102,8105,8107, + 8110,8112,8115,8118,8120,8123,8125,8128,8131,8133,8136,8138,8141,8143,8146,8149, + 8151,8154,8156,8159,8161,8164,8166,8169,8172,8174,8177,8179,8182,8184,8187,8189, + 8192,0, +};} diff --git a/gp2ccline.cpp b/gp2ccline.cpp new file mode 100644 index 0000000..3b5269f --- /dev/null +++ b/gp2ccline.cpp @@ -0,0 +1,266 @@ +#include "gp2ccline.hpp" +#include "gp2cos.hpp" // gp2geom::kCosine — the verified cosine table (COS) +#include "gp2atan.hpp" // gp2geom::kArctan — the verified atan table (ATAN1) +#include + +namespace gp2geom { +namespace { + +// Table aliases so the transcription below reads like the original kernel. +inline int COS (int i) { return kCosine[i]; } // amplitude 0x4000 cosine table +inline int ATAN(int i) { return kArctan[i]; } // atan table, 0x10000/turn + +#define ONE60 (((int64_t)1) << 60) +constexpr int CBB0C = 20861; // data const @0xCBB0C: segment-frame theta' tilt + +inline int s16(int v) { v &= 0xFFFF; return (v & 0x8000) ? v - 0x10000 : v; } +inline int abs16(int v) { v = s16(v); return v < 0 ? -v : v; } +inline int rdw(const std::uint8_t* d, int o) { return d[o] | (d[o + 1] << 8); } + +// GetSinusVal (0x104B9): interpolated cosine, amplitude 0x4000. +inline int gv(int ax) { + ax = s16(ax); + if (ax < 0) ax = (-ax) & 0xFFFF; + int frac = ax & 7; + int widx = ((ax >> 2) & 0xFFFE) >> 1; + int base = COS(widx), nxt = COS(widx + 1); + int d = s16(nxt - base); + return s16(base + ((d * frac) >> 3)); +} + +// sub_10240: 32-bit integer sqrt, FIXED 3-iteration 16-bit Newton (not floor). +std::uint32_t sqrt32(std::uint32_t V) { + if (V == 0) return 0; + int shift = 0; + while (V < 0x40000000u) { shift++; V <<= 2; if (V == 0) return 0; } + std::uint32_t half = V >> 1; + std::uint32_t di = ((V >> 17) + 0x8000) & 0xFFFF; + for (int it = 0; it < 3; it++) { + std::uint32_t hi = half >> 16, q; + if (hi >= di) q = half & 0xFFFF; + else q = half / di; + di = ((di >> 1) + q) & 0xFFFF; + } + return (di >> shift) & 0xFFFF; +} + +// sub_102F0: 64-bit integer sqrt, FIXED 5-iteration Newton. +std::uint32_t sqrt64(std::uint64_t V) { + if (V == 0) return 0; + if ((std::uint32_t)(V >> 32) == 0) return sqrt32((std::uint32_t)V); + int shift = 0; + while ((V >> 32) < 0x40000000ull) { shift++; V <<= 2; } + std::uint64_t half = V >> 1; + std::uint32_t est = (std::uint32_t)((half >> 32) + 0x80000000ull); + for (int it = 0; it < 5; it++) { + std::uint32_t q = (std::uint32_t)(half / est); + est = (est >> 1) + q; + } + return est >> shift; +} + +// sub_10502: cos(|angle|) in Q30 (amp 0x40000000) via the cos table + interp. +std::int32_t cos30(int angle) { + int a = abs16(angle); + int widx = a >> 3, frac = a & 7; + int base = COS(widx); + int delta = COS(widx + 1) - COS(widx); + return ((std::int32_t)base << 16) + ((std::int32_t)(delta * frac) << 13); +} + +// sub_7827D: Q30 cos/sin; larger component via sqrt(ONE60-other^2), smaller via table. +void q30_cossin(int angle, std::int32_t* pcos, std::int32_t* psin) { + int a = s16(angle); + int coarse_sin = COS(abs16(s16(0x4000 - a)) >> 3); + int coarse_sin_abs = coarse_sin < 0 ? -coarse_sin : coarse_sin; + std::int32_t c30, s30; + if (coarse_sin_abs >= 0x2000) { + c30 = cos30(a); + std::int64_t smag = (std::int64_t)sqrt64((std::uint64_t)(ONE60 - (std::int64_t)c30 * c30)); + s30 = coarse_sin < 0 ? (std::int32_t)(-smag) : (std::int32_t)smag; + } else { + s30 = cos30(s16(0x4000 - a)); + std::int64_t cmag = (std::int64_t)sqrt64((std::uint64_t)(ONE60 - (std::int64_t)s30 * s30)); + int coarse_cos = COS(abs16(a) >> 3); + c30 = coarse_cos < 0 ? (std::int32_t)(-cmag) : (std::int32_t)cmag; + } + *pcos = c30; *psin = s30; +} + +// sub_10798: atan2(y,x) in 0x10000 units (full circle), 16-bit args. +int atan2u(int y, int x) { + y = s16(y); x = s16(x); + int dy = y < 0 ? -y : y; + int dx = x < 0 ? -x : x; + int signs_differ = (y < 0) != (x < 0); + int a; + if (dy >= dx) { + if (dy == 0) return 0; + int idx = (dx << 11) / dy; + a = 0x4000 - ATAN(idx); + } else { + int idx = (dy << 11) / dx; + a = ATAN(idx); + } + if (signs_differ) a = -a; + if (x < 0) a += 0x8000; + return s16(a); +} + +// sub_78492: build the world arc-centre W for the first segment of a command. +void build_centre(const gp2cc_ccgeo* g, int idx, int P, int H, + std::int32_t arg2, int arg1mul4, std::int32_t* pWx, std::int32_t* pWy) { + int seg = idx; + int f14 = (int)g->f14[seg]; + int latP = (int)(((std::int32_t)P * f14) >> 15); + int ang = s16(g->segAngle[seg]); + int cosA = gv(ang); + int sinA = gv(s16(0x4000 - ang)); + std::int32_t RX = (std::int16_t)(((std::int32_t)P * cosA + (std::int32_t)latP * sinA) >> 14); + std::int32_t RY = (std::int16_t)(((std::int32_t)latP * cosA - (std::int32_t)P * sinA) >> 14); + RX += g->cx8[seg]; + RY += g->cy8[seg]; + int sinH = gv(s16(0x4000 - H)); + int cosH = gv(s16(H)); + std::int16_t a1m4 = (std::int16_t)arg1mul4; + RX += (std::int16_t)(((std::int32_t)sinH * a1m4) >> 14); + RY += (std::int16_t)(((std::int32_t)cosH * a1m4) >> 14); + std::int32_t Wx = RX, Wy = RY; + if (arg2 != 0) { + std::int32_t mag = arg2 < 0 ? -arg2 : arg2; + int perp = (arg2 < 0) ? s16(H - 0x4000) : s16(H + 0x4000); + std::int32_t c32, s32; q30_cossin(perp, &c32, &s32); + Wx += (std::int32_t)(((std::int64_t)s32 * mag) >> 30); + Wy += (std::int32_t)(((std::int64_t)c32 * mag) >> 30); + } + *pWx = Wx; *pWy = Wy; +} + +// sub_787E7: reproject world point W onto seg idx -> P (and H on curves). +void reproject(const gp2cc_ccgeo* g, int idx, std::int32_t Wx, std::int32_t Wy, + std::int32_t arg2, int* pP, int* pH) { + int seg = idx; + int f14 = (int)g->f14[seg]; + int ang = s16(g->segAngle[seg]); + int thetaP = s16(ang - (int)((((std::int32_t)(f14 >> 1) * CBB0C) << 1) >> 16)); + std::int32_t relx = Wx - g->cx8[seg]; + std::int32_t rely = Wy - g->cy8[seg]; + std::int32_t c32, s32; q30_cossin(thetaP, &c32, &s32); + std::int64_t lat64 = (std::int64_t)c32 * relx - (std::int64_t)s32 * rely; + std::int64_t lon64 = (std::int64_t)s32 * relx + (std::int64_t)c32 * rely; + std::int32_t lat = (std::int32_t)(lat64 >> 30); + std::int32_t lon = (std::int32_t)(lon64 >> 30); + if (arg2 == 0) { + int d = s16(*pH - thetaP); + std::int32_t sn = cos30(s16(0x4000 - d)); + std::int32_t cs = cos30(d); + std::int32_t tanterm = 0; + if (cs != 0) tanterm = (std::int32_t)(((std::int64_t)sn * lon) / cs); + *pP = s16(lat - tanterm); + return; + } + std::int64_t a2 = arg2; + std::int64_t disc = a2 * a2 - (std::int64_t)lon * lon; + std::int64_t T = (std::int64_t)sqrt64((std::uint64_t)disc); + std::int64_t Tsigned = (arg2 < 0) ? -T : T; + *pP = s16((std::int32_t)(lat - (std::int32_t)Tsigned)); + std::int32_t Ts = (std::int32_t)Tsigned, lonv = lon; + std::uint32_t aa = (std::uint32_t)(arg2 < 0 ? -arg2 : arg2); + while (((aa >> 16) > 0) || ((aa >> 16) == 0 && (aa & 0xFFFF) >= 0x7F00)) { + Ts >>= 1; lonv >>= 1; aa >>= 1; + } + int at = atan2u((int)(std::int16_t)(Ts & 0xFFFF), (int)(std::int16_t)(lonv & 0xFFFF)); + at = (arg2 < 0) ? s16(at + 0x4000) : s16(at - 0x4000); + *pH = s16(at + thetaP); +} + +// sub_78CEC: per-seg curvature-ramp nudge of W (slope!=0 commands). +void nudge(std::int32_t* pWx, std::int32_t* pWy, int H, std::int32_t arg2, std::int32_t slope) { + std::int32_t ebp = slope; + int a; + if (arg2 < 0) { ebp = -ebp; a = s16(H - 0x4000); } + else a = s16(H + 0x4000); + int sinA = gv(s16(0x4000 - a)); + int cosA = gv(s16(a)); + *pWx += (std::int32_t)(((std::int64_t)sinA * ebp) >> 14); + *pWy += (std::int32_t)(((std::int64_t)cosA * ebp) >> 14); +} + +// GetSingleCmdArgs (0x78D4C): parse one cc command. +struct cc_cmd { + int end, word, N, a1, a2; + std::int32_t arg2, slope; + int arg1mul4, arg1if2is0; +}; +void parse_cmd(const std::uint8_t* cc, int* po, cc_cmd* c) { + int o = *po; + std::memset(c, 0, sizeof *c); + int word = rdw(cc, o); o += 2; + c->word = word; + c->N = word & 0x7FF; + if (word == 0) { c->end = 1; *po = o; return; } + if (word & 0x8000) { c->a1 = rdw(cc, o); o += 2; c->a2 = rdw(cc, o); o += 2; } + int cx = rdw(cc, o); o += 2; + std::int32_t arg2 = (std::int32_t)(std::int16_t)rdw(cc, o); o += 2; + if (word & 0x4000) { arg2 = (std::int32_t)((std::uint32_t)arg2 << 16) | rdw(cc, o); o += 2; } + if (!(word & 0x1000)) arg2 <<= 3; + c->arg2 = arg2; + if (word & 0x2000) { + std::int32_t arg3 = (std::int32_t)(std::int16_t)rdw(cc, o); o += 2; + if (word & 0x4000) { arg3 = (std::int32_t)((std::uint32_t)arg3 << 16) | rdw(cc, o); o += 2; } + if (!(word & 0x1000)) arg3 <<= 3; + c->slope = (arg3 - arg2) / c->N; + } + if (arg2 != 0) { c->arg1if2is0 = 0; c->arg1mul4 = (std::int16_t)(cx << 2); } + else { c->arg1mul4 = 0; c->arg1if2is0 = cx; } + *po = o; +} + +} // anonymous namespace + +// UACalcBestLine (0x78EB2): walk the command stream; seed P/H; per segment store +// bestLine/angle18 then reproject the carried world point; nudge on ramps. +int computeCcLine(const gp2cc_ccgeo& gref, const std::uint8_t* cc, int cc_len, int cc_off, + std::int16_t* bestLine, std::int16_t* angle18, + int* cmdStartSeg, int* pNumCmds) { + const gp2cc_ccgeo* g = &gref; + int n = g->n; + if (n <= 0) return -1; + int cur = 0, cmdIdx = 0; + int H = s16(g->segAngle[0]); + int P = 0; + int w0 = rdw(cc, cc_off); + // seed P (segPosX_0A) is a SIGNED 16-bit word; sign-extend it. + if (!(w0 & 0x800) && (w0 & 0x8000)) P = s16(rdw(cc, cc_off + 2)); + int o = cc_off; + for (;;) { + cc_cmd c; + parse_cmd(cc, &o, &c); + if (c.end) break; + if (o > cc_len) return -2; + if ((c.word & 0x800) && (c.word & 0x8000)) { P = s16(c.a1); H = s16(c.a2); } + if (c.arg2 == 0) H = s16(H + s16(c.arg1if2is0)); + if (cmdStartSeg) cmdStartSeg[cmdIdx] = cur; + cmdIdx++; + std::int32_t Wx, Wy; + build_centre(g, cur, P, H, c.arg2, c.arg1mul4, &Wx, &Wy); + std::int32_t arg2 = c.arg2; + int left = c.N; + while (left > 0) { + angle18[cur] = (std::int16_t)s16(H - s16(g->segAngle[cur])); + bestLine[cur] = (std::int16_t)s16(P); + cur = (cur + 1) % n; + reproject(g, cur, Wx, Wy, arg2, &P, &H); + left--; + if (left == 0) break; + if (arg2 != 0 && c.slope != 0) { + arg2 += c.slope; + nudge(&Wx, &Wy, H, arg2, c.slope); + } + } + } + if (pNumCmds) *pNumCmds = cmdIdx; + return 0; +} + +} // namespace gp2geom diff --git a/gp2ccline.hpp b/gp2ccline.hpp new file mode 100644 index 0000000..1d37688 --- /dev/null +++ b/gp2ccline.hpp @@ -0,0 +1,24 @@ +#pragma once +#include +#include "gp2cc.h" // gp2cc_ccgeo input struct + +// --------------------------------------------------------------------------- +// gp2ccline — GP2's racing-line (cc-line) solver, ported to C++ and fed by the +// formula-generated cosine/atan tables (gp2cos.hpp / gp2atan.hpp). +// +// This is the CHAOTIC kernel: a reproject/nudge feedback loop whose output +// diverges on any rounding difference. It is therefore a FAITHFUL transcription +// of the engine's fixed-point math (UACalcBestLine 0x78EB2 + helpers), not a +// re-derivation — kept legible with comments, but the integers are load-bearing +// and must not be "cleaned up". Validated bit-exact vs the original C kernel. +// --------------------------------------------------------------------------- +namespace gp2geom { + +// Drop-in for gp2cc_compute_ccline. Writes per-segment bestLine (tseg+0x16) and +// angle18 (tseg+0x18); cmdStartSeg/pNumCmds (may be null) record each cc-command's +// first segment. Returns 0 on success. +int computeCcLine(const gp2cc_ccgeo& g, const std::uint8_t* cc, int cc_len, int cc_off, + std::int16_t* bestLine, std::int16_t* angle18, + int* cmdStartSeg, int* pNumCmds); + +} // namespace gp2geom From 4146c1e5b764079d69d24f8d80e3393ba7e82ec6 Mon Sep 17 00:00:00 2001 From: Roberto Remedio Date: Mon, 15 Jun 2026 10:44:06 -0300 Subject: [PATCH 05/12] Compiled track + cc-line as independent overlays on the normal view Scrap the standalone compiled-track view (which replaced the normal view). The two compiled features are now independent, toggleable overlays drawn on top of the normal editor view: - Compiled Track overlay (showComputed, reuses ID_VIEW_COMPILED): bit-exact road edges + per-section dividers, BLUE. - Compiled CC-Line overlay (new showComputedCCLine / ID_VIEW_COMPILED_CCLINE): bit-exact racing line + per-sector start ticks + selected-sector highlight, ORANGE / bright YELLOW. Both compile once per repaint (buildCompiledOverlay) and share the result. Fix the selected-sector off-by-one: CCLineSection[c] starts at cmdSeg[c+1] (cmdSeg[0] is the seed/start command = the editor's CCLineStart header, not a user sector), so selecting sector N now highlights N. drawComputed() is split into buildCompiledOverlay + drawCompiledTrack + drawCompiledCcLine; OnDraw's standalone branch is removed and the overlays are appended to the normal-view branch. Menu: "Show Compiled Track" -> "Compiled Track Overlay" + new "Compiled CC-Line Overlay". Toolbar: ID_VIEW_COMPILED button re-tinted blue (track-tool icon); new ID_VIEW_COMPILED_CCLINE button (ccline-tool icon, orange via repurposed palette idx 5). gp2cc.c untouched. View-only. --- GPTrack.cpp | 263 ++++++++++++++++++++------------------ GPTrack.h | 12 +- TrackEditor.rc | 4 +- TrackEditorScrollView.cpp | 27 +++- TrackEditorScrollView.h | 4 + res/bmp00076.bmp | Bin 4214 -> 4342 bytes resource.h | 3 +- 7 files changed, 180 insertions(+), 133 deletions(-) diff --git a/GPTrack.cpp b/GPTrack.cpp index 69eed3c..53da8f0 100644 --- a/GPTrack.cpp +++ b/GPTrack.cpp @@ -148,6 +148,7 @@ void GPTrack::Create() PROFILE(showObjects, FALSE) PROFILE(showCCLine, TRUE) PROFILE(showComputed, FALSE) + PROFILE(showComputedCCLine, FALSE) PROFILE(showHiddenAsGray, TRUE) PROFILE(showTrackPie, FALSE) @@ -417,6 +418,7 @@ GPTrack::~GPTrack() WR_PROFILE(showObjects) WR_PROFILE(showCCLine) WR_PROFILE(showComputed) + WR_PROFILE(showComputedCCLine) WR_PROFILE(showHiddenAsGray) WR_PROFILE(showTrackPie) @@ -4036,155 +4038,168 @@ void GPTrack::drawCCLine(Display *g) } // --------------------------------------------------------------------------- -// drawComputed: the bit-exact "compiled track" view. Rebuilds the .dat image -// in trackdata[] from the current in-memory track (RecreateData), runs the -// gp2cc compiler + cc-line on it, and draws the road EDGES + the racing line -// in the editor's own coordinate frame. No pitlane/kerbs/objects. +// Compiled overlays (bit-exact, gp2geom) drawn ON TOP of the normal view. +// buildCompiledOverlay() rebuilds the .dat from the live track (RecreateData), +// runs the gp2geom geometry compiler (and the cc-line only if that overlay is +// on), and projects every segment into the editor's frame. The two draw +// functions then render the compiled road (edges + section dividers, BLUE) +// and the compiled racing line (line + sector ticks, ORANGE; selected sector +// bright YELLOW), each toggled independently. // -// Coordinate map: gp2cc positions are in 1/8-world-units (X8/Y8); the editor -// integrates 1 unit/segment while GP2 steps 128 world-units (=1024 X8) per -// segment, so scale = 1/1024, anchored at track section 0's start, same -// heading orientation. (If the shape comes out mirrored/wrong-scale, adjust -// S / the axis pairing here.) +// Coordinate map: gp2geom positions are 1/8-world-units (X8/Y8); GP2 steps 128 +// world-units (=1024 X8) per segment while the editor uses 1 unit/segment, so +// scale = 1/128, anchored at track section 0's start, same heading orientation. +// Each point = centre + perpendicular(heading) * (offset / 8 world units). // --------------------------------------------------------------------------- -void GPTrack::drawComputed(Display *g) -{ - if (TrackSections == NULL || TrackSections->size() == 0) return; +namespace { + struct CompiledOverlay { + bool valid; + bool hasCC; + int n; + gp2cc_track gt; // compiled geometry + short bl[GP2CC_MAXSEG], a18[GP2CC_MAXSEG]; // cc-line bestLine / angle18 + int cmdSeg[GP2CC_MAXSEG], numCmds; // first segment of each cc-command + double lex[GP2CC_MAXSEG], ley[GP2CC_MAXSEG]; // left edge (editor coords) + double rex[GP2CC_MAXSEG], rey[GP2CC_MAXSEG]; // right edge + double ccx[GP2CC_MAXSEG], ccy[GP2CC_MAXSEG]; // racing line + }; + static CompiledOverlay ov; // shared between buildCompiledOverlay + the draw fns +} + +bool GPTrack::buildCompiledOverlay() +{ + ov.valid = false; ov.hasCC = false; + if (TrackSections == NULL || TrackSections->size() == 0) return false; + + RecreateData(); // live track -> trackdata[] + if (gp2geom::compileGeometry(trackdata, 65535, ov.gt) != 0) return false; + int n = ov.gt.n; + if (n <= 1) return false; + ov.n = n; + + // editor frame: scale 1/128, anchored at section 0's start. + TrackSection *s0 = (TrackSection *)TrackSections->elementAt(0); + double ox = s0->getStartX(), oy = s0->getStartY(); + double bwx = ov.gt.X8[0] / 8.0, bwy = ov.gt.Y8[0] / 8.0; + const double ES = 1.0 / 128.0; - // rebuild trackdata[] (the .dat image) from the live track, then compile it - RecreateData(); + for (int i = 0; i < n; i++) { + double cosh = gp2cc_gv(ov.gt.angle[i]) / 16384.0; + double sinh = gp2cc_gv(0x4000 - ov.gt.angle[i]) / 16384.0; + double pvx = -sinh, pvy = cosh; // unit perpendicular (worldX, worldY) + double px = ov.gt.X8[i] / 8.0, py = ov.gt.Y8[i] / 8.0; + double wl = ov.gt.widthL[i] / 8.0, wr = ov.gt.widthR[i] / 8.0; + ov.lex[i] = ox + (px + pvx * wl - bwx) * ES; ov.ley[i] = oy + (py + pvy * wl - bwy) * ES; + ov.rex[i] = ox + (px - pvx * wr - bwx) * ES; ov.rey[i] = oy + (py - pvy * wr - bwy) * ES; + } + + if (showComputedCCLine) { + static gp2cc_ccgeo cg; + cg.n = n; + for (int i = 0; i < n; i++) { + cg.segAngle[i] = ov.gt.angle[i]; + cg.cx8[i] = ov.gt.Y8[i]; + cg.cy8[i] = ov.gt.X8[i]; + cg.f14[i] = ov.gt.f14[i]; + } + ov.numCmds = 0; + gp2geom::computeCcLine(cg, trackdata, 65535, ov.gt.ccoff, ov.bl, ov.a18, ov.cmdSeg, &ov.numCmds); + for (int i = 0; i < n; i++) { + double cosh = gp2cc_gv(ov.gt.angle[i]) / 16384.0; + double sinh = gp2cc_gv(0x4000 - ov.gt.angle[i]) / 16384.0; + double pvx = -sinh, pvy = cosh; + double px = ov.gt.X8[i] / 8.0, py = ov.gt.Y8[i] / 8.0; + double cc = ov.bl[i] / 8.0; + ov.ccx[i] = ox + (px + pvx * cc - bwx) * ES; ov.ccy[i] = oy + (py + pvy * cc - bwy) * ES; + } + ov.hasCC = true; + } - static gp2cc_track gt; - static gp2cc_ccgeo cg; - static short bl[GP2CC_MAXSEG], a18[GP2CC_MAXSEG]; + ov.valid = true; + return true; +} - if (gp2geom::compileGeometry(trackdata, 65535, gt) != 0) return; - int n = gt.n; - if (n <= 1) return; +// Compiled-track overlay: bit-exact road edges + a perpendicular divider at each +// TrackSection boundary, in BLUE. Numbers / start-finish stay with the normal view. +void GPTrack::drawCompiledTrack(Display *g) +{ + if (!ov.valid) return; + int n = ov.n; - // cc-line on the compiled geometry (cx8 pairs with world Y=Y8, cy8 with X=X8) - cg.n = n; - for (int i = 0; i < n; i++) { - cg.segAngle[i] = gt.angle[i]; - cg.cx8[i] = gt.Y8[i]; - cg.cy8[i] = gt.X8[i]; - cg.f14[i] = gt.f14[i]; - } - static int cmdSeg[GP2CC_MAXSEG]; // first segment of each cc-command (=sector) - int numCmds = 0; - gp2geom::computeCcLine(cg, trackdata, 65535, gt.ccoff, bl, a18, cmdSeg, &numCmds); - - // editor frame: world units -> editor units = /128 (128 world-u/seg = 1 editor-u), - // anchored at section 0's start. Edges + cc-line = centre + perpendicular(heading)* - // (offset/8 world units), exactly like the validated preview (plot_ccline.py). - TrackSection *s0 = (TrackSection*)TrackSections->elementAt(0); - double ox = s0->getStartX(), oy = s0->getStartY(); - double bwx = gt.X8[0] / 8.0, bwy = gt.Y8[0] / 8.0; - const double ES = 1.0 / 128.0; + CPen *trackPen = new CPen(PS_SOLID, 1, RGB(0, 96, 255)); // blue + g->SelectObject(trackPen); - static double lex[GP2CC_MAXSEG], ley[GP2CC_MAXSEG]; // left edge (editor coords) - static double rex[GP2CC_MAXSEG], rey[GP2CC_MAXSEG]; // right edge - static double ccx[GP2CC_MAXSEG], ccy[GP2CC_MAXSEG]; // cc-line - for (int i = 0; i < n; i++) { - double cosh = gp2cc_gv(gt.angle[i]) / 16384.0; // cos(heading) - double sinh = gp2cc_gv(0x4000 - gt.angle[i]) / 16384.0; // sin(heading) - double pvx = -sinh, pvy = cosh; // unit perpendicular (worldX, worldY) - double px = gt.X8[i] / 8.0, py = gt.Y8[i] / 8.0; // segment centre (world) - double wl = gt.widthL[i] / 8.0, wr = gt.widthR[i] / 8.0; // half-widths (world) - double cc = bl[i] / 8.0; // cc-line offset (world) - lex[i] = ox + (px + pvx * wl - bwx) * ES; ley[i] = oy + (py + pvy * wl - bwy) * ES; - rex[i] = ox + (px - pvx * wr - bwx) * ES; rey[i] = oy + (py - pvy * wr - bwy) * ES; - ccx[i] = ox + (px + pvx * cc - bwx) * ES; ccy[i] = oy + (py + pvy * cc - bwy) * ES; - } - - g->setColor(0); // road edges (dark) - for (int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { // road edges int j = (i + 1) % n; - g->drawLine(lex[i], ley[i], lex[j], ley[j]); - g->drawLine(rex[i], rey[i], rex[j], rey[j]); + g->drawLine(ov.lex[i], ov.ley[i], ov.lex[j], ov.ley[j]); + g->drawLine(ov.rex[i], ov.rey[i], ov.rex[j], ov.rey[j]); + } + + // section dividers: cumulative getLength() gives each section's first segment + // (skip index==-99 sections that emit no geometry command, as WriteTrack does). + int nsec = TrackSections->size(); + int seg = 0; + for (int k = 0; k < nsec; k++) { + TrackSection *t = (TrackSection *)TrackSections->elementAt(k); + if (t->index == -99) continue; + int s = seg; + seg += (int)t->getLength(); + if (s < 0 || s >= n) continue; + g->drawLine(ov.lex[s], ov.ley[s], ov.rex[s], ov.rey[s]); } - g->setColor(RED_PEN); // cc-line (racing line) + + g->setColor(0); // deselect before delete + delete trackPen; +} + +// Compiled-cc-line overlay: bit-exact racing line + a perpendicular tick at each +// cc-sector start, in ORANGE; the SELECTED sector over-drawn in bright YELLOW. +// CCLineSection[c] starts at cmdSeg[c+1] -- cmdSeg[0] is the seed/start command +// (the editor's CCLineStart header), not a user-editable sector. +void GPTrack::drawCompiledCcLine(Display *g) +{ + if (!ov.valid || !ov.hasCC) return; + int n = ov.n; + + CPen *linePen = new CPen(PS_SOLID, 1, RGB(255, 140, 0)); // orange + g->SelectObject(linePen); for (int i = 0; i < n; i++) { int j = (i + 1) % n; - g->drawLine(ccx[i], ccy[i], ccx[j], ccy[j]); + g->drawLine(ov.ccx[i], ov.ccy[i], ov.ccx[j], ov.ccy[j]); } + g->setColor(0); + delete linePen; - // --- per-sector indicators + selected-sector highlight --- - // each CCLineSection maps 1:1 to a cc-command (cmdSeg[c] = its first segment). int nSec = (CCLineSections != NULL) ? CCLineSections->size() : 0; - int mSec = (numCmds < nSec) ? numCmds : nSec; - for (int c = 0; c < mSec; c++) { - int s = cmdSeg[c]; + CPen *tickPen = new CPen(PS_SOLID, 1, RGB(255, 140, 0)); // orange tick + CPen *selPen = new CPen(PS_SOLID, 2, RGB(255, 255, 0)); // bright yellow (selected) + for (int c = 0; c < nSec; c++) { + int ci = c + 1; // +1: skip the seed command + if (ci >= ov.numCmds) break; + int s = ov.cmdSeg[ci]; if (s < 0 || s >= n) continue; CCLineSection *sec = (CCLineSection *)CCLineSections->elementAt(c); BOOL sel = (sec != NULL) && sec->isSelected(); - // highlight the selected sector's cc-line segments (over-draw in colour 4) - if (sel) { - int len = (sec != NULL) ? (int)sec->getLength() : 0; - g->setColor(4); + if (sel) { // highlight the selected sector + int len = (int)sec->getLength(); + g->SelectObject(selPen); for (int k = 0; k < len; k++) { int i = (s + k) % n, j = (i + 1) % n; - g->drawLine(ccx[i], ccy[i], ccx[j], ccy[j]); + g->drawLine(ov.ccx[i], ov.ccy[i], ov.ccx[j], ov.ccy[j]); } } - // small perpendicular tick on the cc-line where the sector starts. The editor - // transform is uniform scale + translate (no rotation), so the world-space unit - // perpendicular doubles as the editor-space direction; tlen is in editor units. - double cosh = gp2cc_gv(gt.angle[s]) / 16384.0; - double sinh = gp2cc_gv(0x4000 - gt.angle[s]) / 16384.0; - double tlen = 3.0; // editor units + double cosh = gp2cc_gv(ov.gt.angle[s]) / 16384.0; // tick perpendicular + double sinh = gp2cc_gv(0x4000 - ov.gt.angle[s]) / 16384.0; + double tlen = 3.0; double tx = -sinh * tlen, ty = cosh * tlen; - g->setColor(sel ? 4 : BLUE_PEN); - g->drawLine(ccx[s] - tx, ccy[s] - ty, ccx[s] + tx, ccy[s] + ty); - } - - // --- track-section dividers + numbers (gated on showTrackNumbers) --- - // Each TrackSection serializes to ONE geometry command whose word N = getLength() - // (TrackSection::write writes lengthData as the command word) and gp2cc emits exactly - // N segments per command, so the running sum of getLength() over the sections (skipping - // the index==-99 ones that emit no command, as WriteTrack does) gives each section's - // first compiled segment — bit-exact with the road drawn above. - if (showTrackNumbers) { - int nsec = TrackSections->size(); - CPen *divPen = new CPen(PS_SOLID, 1, RGB(110, 110, 110)); // section dividers (gray) - g->SelectObject(divPen); - int seg = 0; - for (int k = 0; k < nsec; k++) { - TrackSection *t = (TrackSection *)TrackSections->elementAt(k); - if (t->index == -99) continue; // emits no geometry command (see WriteTrack) - int s = seg; // this section's first compiled segment - int len = (int)t->getLength(); - seg += len; - if (s < 0 || s >= n) continue; - - // divider: perpendicular line across the road at the section boundary - g->drawLine(lex[s], ley[s], rex[s], rey[s]); - - // section number placed mid-section on the centre line (like the default view) - int mid = s + len / 2; - if (mid >= n) mid = s; - char buf[12]; - wsprintf(buf, "%d", k); - g->drawText(ox + (gt.X8[mid] / 8.0 - bwx) * ES, - oy + (gt.Y8[mid] / 8.0 - bwy) * ES, buf); - } - g->setColor(0); // deselect divPen before deleting it - delete divPen; - } - - // --- start/finish line (gated on showFinishLine) --- - // Thick line across the road at segment 0 + checkered flag, mirroring - // TrackSection::drawStartingLine in the default view. - if (showFinishLine) { - CPen *sfPen = new CPen(PS_SOLID, 3, RGB(0, 0, 0)); - g->SelectObject(sfPen); - g->drawLine(lex[0], ley[0], rex[0], rey[0]); - g->setColor(0); // deselect sfPen before deleting it - delete sfPen; - g->drawBitmap(IDB_CHECKED_FLAG, lex[0], ley[0] - 16); + g->SelectObject(sel ? selPen : tickPen); + g->drawLine(ov.ccx[s] - tx, ov.ccy[s] - ty, ov.ccx[s] + tx, ov.ccy[s] + ty); } + g->setColor(0); // deselect before delete + delete tickPen; + delete selPen; } void GPTrack::drawPitlane(Display *g) diff --git a/GPTrack.h b/GPTrack.h index 93d873f..5a6d2b3 100644 --- a/GPTrack.h +++ b/GPTrack.h @@ -144,8 +144,13 @@ class GPTrack : public CWnd drawPitlane(Display *g); void drawCCLine(Display *g); - void - drawComputed(Display *g); // bit-exact compiled track + cc-line (gp2cc) + // Compiled overlays (bit-exact, gp2geom). buildCompiledOverlay() recompiles the + // track once per repaint and fills the shared overlay arrays; the two draw + // functions render the track (edges + dividers) and the cc-line (racing line + + // sector marks) on top of the normal view. Returns false if compile failed. + bool buildCompiledOverlay(); + void drawCompiledTrack(Display *g); + void drawCompiledCcLine(Display *g); void drawCameras(Display *g); void @@ -245,7 +250,8 @@ class GPTrack : public CWnd BOOL showPitLane; BOOL showObjects; BOOL showCCLine; - BOOL showComputed; // the bit-exact compiled track + cc-line view + BOOL showComputed; // compiled-track overlay (bit-exact road edges + dividers) + BOOL showComputedCCLine; // compiled-cc-line overlay (bit-exact racing line + sector marks) BOOL showHiddenAsGray; BOOL showTrackPie; BOOL showCameras; diff --git a/TrackEditor.rc b/TrackEditor.rc index cc9263c..b729d5c 100644 --- a/TrackEditor.rc +++ b/TrackEditor.rc @@ -276,6 +276,7 @@ BEGIN BUTTON ID_VIEWCAMERAS SEPARATOR BUTTON ID_VIEW_COMPILED + BUTTON ID_VIEW_COMPILED_CCLINE END IDR_OBJECT TOOLBAR 16, 15 @@ -474,7 +475,8 @@ BEGIN MENUITEM "Show Track", VIEW_TRACK MENUITEM "Show Pitlane", VIEW_PITLANE MENUITEM "Show CC Line", VIEW_CCLINE - MENUITEM "Show Compiled Track", ID_VIEW_COMPILED + MENUITEM "Compiled Track Overlay", ID_VIEW_COMPILED + MENUITEM "Compiled CC-Line Overlay", ID_VIEW_COMPILED_CCLINE MENUITEM "Show Objects", VIEW_OBJECTS MENUITEM "Show Fences", VIEW_WALLS MENUITEM "Show Pit Fences", ID_SHOW_SHOWPITFENCES diff --git a/TrackEditorScrollView.cpp b/TrackEditorScrollView.cpp index 898a957..043ef13 100644 --- a/TrackEditorScrollView.cpp +++ b/TrackEditorScrollView.cpp @@ -91,6 +91,8 @@ ON_UPDATE_COMMAND_UI(VIEW_CCLINE, OnUpdateCcline) ON_COMMAND(VIEW_CCLINE, OnCcline) ON_UPDATE_COMMAND_UI(ID_VIEW_COMPILED, OnUpdateViewCompiled) ON_COMMAND(ID_VIEW_COMPILED, OnViewCompiled) +ON_UPDATE_COMMAND_UI(ID_VIEW_COMPILED_CCLINE, OnUpdateViewCompiledCCLine) +ON_COMMAND(ID_VIEW_COMPILED_CCLINE, OnViewCompiledCCLine) ON_UPDATE_COMMAND_UI(ID_VIEW_OBJ_BITMAPS, OnUpdateViewObjBitmaps) ON_UPDATE_COMMAND_UI(ID_ZOOMTOOL, OnUpdateZoomtool) ON_UPDATE_COMMAND_UI(ID_POINTER, OnUpdatePointer) @@ -458,10 +460,6 @@ void TrackEditorScrollView::OnMyDraw(CDC* pDC) track->drawGrid(display); track->drawTrack(display, TRUE); track->drawCCLines(display); - } else if (track->showComputed) { - // bit-exact compiled-track + cc-line view (replaces the normal draws) - track->drawGrid(display); - track->drawComputed(display); } else { track->drawGrid(display); track->drawTrack(display); @@ -470,6 +468,14 @@ void TrackEditorScrollView::OnMyDraw(CDC* pDC) track->drawCameras(display); track->drawBlackFlags(display); track->DrawCCDataLog(display); + // bit-exact compiled overlays drawn ON TOP of the normal view, each + // toggled independently. Compile once, both overlays share the result. + if (track->showComputed || track->showComputedCCLine) { + if (track->buildCompiledOverlay()) { + if (track->showComputed) track->drawCompiledTrack(display); + if (track->showComputedCCLine) track->drawCompiledCcLine(display); + } + } } TrackSection* t = track->getTrackSelection(); @@ -1045,6 +1051,19 @@ void TrackEditorScrollView::OnViewCompiled() repaint(); } +void TrackEditorScrollView::OnUpdateViewCompiledCCLine(CCmdUI* pCmdUI) +{ + UPDATE_TRACK(showComputedCCLine); +} + +void TrackEditorScrollView::OnViewCompiledCCLine() +{ + CTrackEditorDoc* pDoc = GetDocument(); + GPTrack* mytrack = pDoc->getTrack(); + mytrack->showComputedCCLine = !mytrack->showComputedCCLine; + repaint(); +} + void TrackEditorScrollView::OnUpdateViewObjBitmaps(CCmdUI* pCmdUI) { // TODO: Add your command update UI handler code here diff --git a/TrackEditorScrollView.h b/TrackEditorScrollView.h index 4fbd0d6..fba18d1 100644 --- a/TrackEditorScrollView.h +++ b/TrackEditorScrollView.h @@ -146,6 +146,10 @@ class TrackEditorScrollView : public CScrollView OnUpdateViewCompiled(CCmdUI* pCmdUI); afx_msg void OnViewCompiled(); + afx_msg void + OnUpdateViewCompiledCCLine(CCmdUI* pCmdUI); + afx_msg void + OnViewCompiledCCLine(); afx_msg void OnUpdateViewObjBitmaps(CCmdUI* pCmdUI); afx_msg void diff --git a/res/bmp00076.bmp b/res/bmp00076.bmp index bc6585ffbf7c676e4ac88cd356f178983a87846f..00670856040700ea2d54add4fc92b32f58039f76 100755 GIT binary patch delta 374 zcmZY5Jr05}7zW^0Q1OqS)k%VhjGn=rvEdpnr3-@xaL}-M3s2$F8yFAa!jFr-UkMW9 zm$XeD-n<3MRT)ORr1Y7Y410{_4HzTh%r<^FM;nVB-`6DO#g;pKUu68{YOVSK4H_@k z`u1a1<7rHf$l0}zT(43AhqZ64i^>!@s)9C2DwM!L$5qfqp;AbN4eESVNhwk&HB36C zsKBe4%cg%y4Kq@O3#pN^iW=rn+@52ZS{%>?loFU$Q)tMS#mRjH-Pg+0JV(pZT^ASc H-u}D~;4-Qv delta 253 zcmeyS_)S6C$+t{^0SwB3qy`ZGV`N|e2{AITfW;XmDyuU$Ff?rR)nH^~m|VyBi>(|4 z7$&zd{bUC-f$TnJ1bZ6G4~_x`28g10tUovy$_qdW8741d`_2hsg5($`uVaVsAPTo} zeCGt~0n6>8`+d=Jl{Ei210GP#|z}aEmvfen#6yX6J!AhFicM5SD7p% IaGQ|<0HEztHvj+t diff --git a/resource.h b/resource.h index 9e2f70a..5ebe9c4 100644 --- a/resource.h +++ b/resource.h @@ -886,6 +886,7 @@ #define ID_SHOW_SHOWSCENERYARMS 33098 #define ID_SHOW_USESWIVELANGLES 33099 #define ID_VIEW_COMPILED 33100 +#define ID_VIEW_COMPILED_CCLINE 33101 #define ID_INDICATOR_MODEL 59142 // Next default values for new objects @@ -894,7 +895,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_3D_CONTROLS 1 #define _APS_NEXT_RESOURCE_VALUE 266 -#define _APS_NEXT_COMMAND_VALUE 33101 +#define _APS_NEXT_COMMAND_VALUE 33102 #define _APS_NEXT_CONTROL_VALUE 1244 #define _APS_NEXT_SYMED_VALUE 115 #endif From 69ae677003afd1d45d287a83ec9d035b7f4b72e3 Mon Sep 17 00:00:00 2001 From: Roberto Remedio Date: Mon, 15 Jun 2026 11:03:49 -0300 Subject: [PATCH 06/12] Nicer overlay toolbar icons + tooltips Replace the compiled-track and compiled-CC-line toolbar icons with recolored copies of the existing Show Track / Show CC Line glyphs (track in blue, CC line in orange) and add status-bar/tooltip strings for both buttons. --- TrackEditor.rc | 2 ++ res/bmp00076.bmp | Bin 4342 -> 4342 bytes 2 files changed, 2 insertions(+) diff --git a/TrackEditor.rc b/TrackEditor.rc index b729d5c..50ea0ce 100644 --- a/TrackEditor.rc +++ b/TrackEditor.rc @@ -1751,6 +1751,8 @@ BEGIN VIEW_PITLANE "Show Pitlane data\nShow Pit lane" VIEW_TRACK "Show track data\nShow Track" VIEW_CCLINE "Show Driving Line\nDriving Line" + ID_VIEW_COMPILED "Toggle compiled track overlay\nCompiled Track Overlay" + ID_VIEW_COMPILED_CCLINE "Toggle compiled CC-line overlay\nCompiled CC-Line Overlay" VIEW_OBJECTS "Show trackside objects\nShow Objects" END diff --git a/res/bmp00076.bmp b/res/bmp00076.bmp index 00670856040700ea2d54add4fc92b32f58039f76..958f5ff364973a5c4717f9a42a05d0b1d92c5e6c 100755 GIT binary patch delta 260 zcmXxey$ZrG5C`x}tRM9&Q4r_8fiF{hjV>i3f`bnrc&n?UAc&*TL2!wqV>jKMB#Kb) zQq6E&e%$|(XY`DA6DiMTKb2JxBn5SgN+250wuVlM%b@5c0P4fnm0dh`jN4> zP0i^9SZM|2JZ|ewX8;dUMvA8f4-Kb_QrSpjgU8URnWbl-{zp7DU)sz+lrJge!Ar|& rV=U!Mv-gOT{sW4Fk?CaYXjUQ?0 delta 274 zcmX|+Jqp4=5QRxhH2x;Bv}GQ9zbwfTT4L*mUcp#wX{s<8NzC#GnCQL4nc~}Lz}cV zfuMm!myu0cBSQpaC(teGp+*Gd;`g!bS0aF`c}zZCTEdP2J*Se^TqVl{YDKRZL#{3O jxQ#rhp4*g7PAycJQ|7IXah=N7M5x+3r%k-3W%B+58qSQ^ From 19b8fe4e35dbe177e1b700ae7546e8d69a40e44a Mon Sep 17 00:00:00 2001 From: Roberto Remedio Date: Mon, 15 Jun 2026 11:29:45 -0300 Subject: [PATCH 07/12] Register new geometry/cc-line sources in CMakeLists.txt Add gp2cc.c, gp2cc_tables.c, gp2ccline.cpp and gp2geom.cpp to the CMake SOURCES list so the CMake build matches the .vcxproj. --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e503343..0dddf7f 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,11 @@ set(SOURCES FileVersion.cpp FindCmd.cpp GeneralDataEnry.cpp + gp2cc.c + gp2cc_tables.c + gp2ccline.cpp gp2exe.cpp + gp2geom.cpp GPPitDraw.cpp GPTrack.cpp GPTrackDraw.cpp From 14d4d4965b880d31fd1f08bb1523daa25f3a9606 Mon Sep 17 00:00:00 2001 From: Roberto Remedio Date: Mon, 15 Jun 2026 11:44:06 -0300 Subject: [PATCH 08/12] Restore Latin-1 copyright/credit characters in TrackEditor.rc An earlier edit had re-saved the file as UTF-8 and mangled the three non-ASCII characters (the two (c) copyright signs and the e-umlaut in 'Michael') into U+FFFD. Restore the original ISO-8859-1 bytes so those lines match upstream byte-for-byte. --- TrackEditor.rc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) mode change 100644 => 100755 TrackEditor.rc diff --git a/TrackEditor.rc b/TrackEditor.rc old mode 100644 new mode 100755 index 50ea0ce..3e2db8f --- a/TrackEditor.rc +++ b/TrackEditor.rc @@ -830,7 +830,7 @@ CAPTION "About TrackEditor" FONT 8, "MS Sans Serif", 0, 0, 0x0 BEGIN LTEXT "TrackEditor Version",IDC_STATIC,147,7,68,8,SS_NOPREFIX - LTEXT "Copyright � Paul Hoad 1997-2020",IDC_STATIC,147,19,119,8 + LTEXT "Copyright © Paul Hoad 1997-2020",IDC_STATIC,147,19,119,8 DEFPUSHBUTTON "I Accept",IDOK,119,192,54,14,WS_GROUP LTEXT "TrackEditor is a Shareware product. You are free to use TrackEditor on a trial basis only. If you like or intend to continue using the Track Editor in any way then you are required to register your copy with the author: (See README)",IDC_LEGAL1,7,59,303,27 LTEXT "You must NOT distribute this editor by any means other than via email or internet download without the authors prior agreement. You are not permitted to include this TrackEditor on any Media for distribution without the authors prior written consent.",IDC_LEGAL2,7,86,303,27 @@ -1395,7 +1395,7 @@ BEGIN VALUE "FileDescription", "TRACKEDITOR MFC Application" VALUE "FileVersion", "0, 0, 0, 0" VALUE "InternalName", "TRACKEDITOR" - VALUE "LegalCopyright", "Copyright � Paul Hoad 1997-1998" + VALUE "LegalCopyright", "Copyright © Paul Hoad 1997-1998" VALUE "OriginalFilename", "TRACKEDITOR.EXE" VALUE "ProductName", "TRACKEDITOR Application" VALUE "ProductVersion", "000.000.000.000" @@ -2346,7 +2346,7 @@ BEGIN LTEXT "Info on Track Format",IDC_STATIC,14,115,67,8 LTEXT "Thank you to the Following....",IDC_STATIC,61,38,94,8 GROUPBOX "Credits",IDC_STATIC,7,7,212,212 - LTEXT "Rob Buis, Michele Merlino, Ponk, Marcel Borsboom, Trevor Kellaway,Andrzej Barganski,John Verheijen,""Swervin' Irvin'"",Mike Wallace, Micha�l 'NRG' Hompus, Bob Culver.",IDC_STATIC,96,145,109,49,0,WS_EX_RIGHT + LTEXT "Rob Buis, Michele Merlino, Ponk, Marcel Borsboom, Trevor Kellaway,Andrzej Barganski,John Verheijen,""Swervin' Irvin'"",Mike Wallace, Michaël 'NRG' Hompus, Bob Culver.",IDC_STATIC,96,145,109,49,0,WS_EX_RIGHT LTEXT "Additional Thanks:",IDC_STATIC,13,145,60,8 LTEXT "Mirror Sites:",IDC_STATIC,14,71,50,8 LTEXT "All you Mirroring sites",IDC_STATIC,137,71,68,11 From 204e839d94754c0454dd9ad04ce37c990efaa786 Mon Sep 17 00:00:00 2001 From: Roberto Remedio Date: Mon, 15 Jun 2026 11:44:06 -0300 Subject: [PATCH 09/12] Remove the legacy C track-compiler (gp2cc.c / gp2cc_tables.c) The C++ modules gp2geom (geometry) and gp2ccline (cc-line) are the live compilers now; the old C kernel was dead except for one cosine helper (gp2cc_gv) used to draw the overlay. Move that into gp2geom::gv (the interpolated GetSinusVal, bit-identical over all 65536 angles, verified against the old table) and delete both C files. gp2cc.h is trimmed to just the shared output structs (gp2cc_track / gp2cc_ccgeo). gp2geom now always uses the build-time formula cosine table (gp2cos.hpp); the GP2GEOM_EMBEDDED_COS fallback is dropped since its table source is gone. Removed from the .vcxproj and CMakeLists SOURCES. --- CMakeLists.txt | 2 - GPTrack.cpp | 18 +- TrackEditor.vcxproj | 11 +- gp2cc.c | 532 -------------------------------------------- gp2cc.h | 73 +----- gp2cc_tables.c | 404 --------------------------------- gp2ccline.hpp | 4 +- gp2geom.cpp | 30 +-- gp2geom.hpp | 13 +- 9 files changed, 42 insertions(+), 1045 deletions(-) delete mode 100644 gp2cc.c delete mode 100644 gp2cc_tables.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0dddf7f..9a52154 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,8 +38,6 @@ set(SOURCES FileVersion.cpp FindCmd.cpp GeneralDataEnry.cpp - gp2cc.c - gp2cc_tables.c gp2ccline.cpp gp2exe.cpp gp2geom.cpp diff --git a/GPTrack.cpp b/GPTrack.cpp index 53da8f0..43d1083 100644 --- a/GPTrack.cpp +++ b/GPTrack.cpp @@ -11,9 +11,9 @@ #include "TrackSection.h" #include "TrackObjectDefinition.h" #include "CCLineSection.h" -#include "gp2cc.h" // shared structs + the original C kernel (kept as fallback) -#include "gp2geom.hpp" // semantic C++ track-geometry compiler (drop-in for gp2cc_compile_geometry_buf) -#include "gp2ccline.hpp" // C++ cc-line solver on the formula tables (drop-in for gp2cc_compute_ccline) +#include "gp2cc.h" // shared compiled-track data structs (gp2cc_track / gp2cc_ccgeo) +#include "gp2geom.hpp" // semantic C++ track-geometry compiler + the gv() cosine helper +#include "gp2ccline.hpp" // C++ cc-line (racing-line) solver on the formula tables #include "TrackCmd.h" #include "InternalObject.h" #include "JamFileEditor.h" @@ -4084,8 +4084,8 @@ bool GPTrack::buildCompiledOverlay() const double ES = 1.0 / 128.0; for (int i = 0; i < n; i++) { - double cosh = gp2cc_gv(ov.gt.angle[i]) / 16384.0; - double sinh = gp2cc_gv(0x4000 - ov.gt.angle[i]) / 16384.0; + double cosh = gp2geom::gv(ov.gt.angle[i]) / 16384.0; + double sinh = gp2geom::gv(0x4000 - ov.gt.angle[i]) / 16384.0; double pvx = -sinh, pvy = cosh; // unit perpendicular (worldX, worldY) double px = ov.gt.X8[i] / 8.0, py = ov.gt.Y8[i] / 8.0; double wl = ov.gt.widthL[i] / 8.0, wr = ov.gt.widthR[i] / 8.0; @@ -4105,8 +4105,8 @@ bool GPTrack::buildCompiledOverlay() ov.numCmds = 0; gp2geom::computeCcLine(cg, trackdata, 65535, ov.gt.ccoff, ov.bl, ov.a18, ov.cmdSeg, &ov.numCmds); for (int i = 0; i < n; i++) { - double cosh = gp2cc_gv(ov.gt.angle[i]) / 16384.0; - double sinh = gp2cc_gv(0x4000 - ov.gt.angle[i]) / 16384.0; + double cosh = gp2geom::gv(ov.gt.angle[i]) / 16384.0; + double sinh = gp2geom::gv(0x4000 - ov.gt.angle[i]) / 16384.0; double pvx = -sinh, pvy = cosh; double px = ov.gt.X8[i] / 8.0, py = ov.gt.Y8[i] / 8.0; double cc = ov.bl[i] / 8.0; @@ -4190,8 +4190,8 @@ void GPTrack::drawCompiledCcLine(Display *g) } } - double cosh = gp2cc_gv(ov.gt.angle[s]) / 16384.0; // tick perpendicular - double sinh = gp2cc_gv(0x4000 - ov.gt.angle[s]) / 16384.0; + double cosh = gp2geom::gv(ov.gt.angle[s]) / 16384.0; // tick perpendicular + double sinh = gp2geom::gv(0x4000 - ov.gt.angle[s]) / 16384.0; double tlen = 3.0; double tx = -sinh * tlen, ty = cosh * tlen; g->SelectObject(sel ? selPen : tickPen); diff --git a/TrackEditor.vcxproj b/TrackEditor.vcxproj index bcea40b..932df8c 100755 --- a/TrackEditor.vcxproj +++ b/TrackEditor.vcxproj @@ -174,20 +174,11 @@ - - stdc11 - NotUsing - - - stdc11 - NotUsing - stdcpp17 NotUsing + step budget so the 4098-entry build fits. --> /constexpr:steps4000000 %(AdditionalOptions) diff --git a/gp2cc.c b/gp2cc.c deleted file mode 100644 index c297e28..0000000 --- a/gp2cc.c +++ /dev/null @@ -1,532 +0,0 @@ -/* gp2cc — geometry compiler (see gp2cc.h). Ported verbatim from datparse.py, - * which is bit-validated against the GP2Lap dumps. */ -#include "gp2cc.h" -#include -#include -#include - -/* The game's trig tables are EMBEDDED (gp2cc_tables.c, generated from GP2.EXE) so - * gp2cc needs no GP2.EXE at runtime — important for embedding in the editor. - * COS = t_Sinus (amp 0x4000, one entry/8 angle units; gv interp + raw) - * ATAN1 = t_ArithTab1 (atan, indexed by the curved-branch atan2 / finer cos) */ -extern const short gp2cc_COS[]; -extern const short gp2cc_ATAN1[]; -static const short *const COS = gp2cc_COS; -static const short *const ATAN1 = gp2cc_ATAN1; - -/* Tables are embedded; nothing to load. Kept for API compatibility (the exe path - * is ignored). Always succeeds. */ -int gp2cc_load_tables(const char *gp2exe_path) { - (void)gp2exe_path; - return 0; -} - -static inline int s16(int v) { v &= 0xFFFF; return (v & 0x8000) ? v - 0x10000 : v; } - -/* GetSinusVal (0x104B9): cos, interpolated. */ -int gp2cc_gv(int ax) { - ax = s16(ax); - if (ax < 0) ax = (-ax) & 0xFFFF; - int frac = ax & 7; - int widx = ((ax >> 2) & 0xFFFE) >> 1; - int base = COS[widx], nxt = COS[widx + 1]; - int d = s16(nxt - base); - return s16(base + ((d * frac) >> 3)); -} -/* raw (non-interpolated) cosine for the position step (InitTrackSegs 0x79A9F). */ -static int scos(int a) { - a = s16(a); - if (a < 0) a = -a; - return COS[((a >> 2) & 0xFFFE) >> 1]; -} - -/* ---- track-command arg sizes (datparse TRK_ARGSZ). opcode 0x45 is conditional - * on w_C5Code bit9 (handled inline). -1 = unknown (desync). */ -static const int TRK_ARGSZ[0x66] = { - 2,2,2,0,0,4,0,0,2,2, 10,10,2,2,4,4,2,2,2,2, 2,2,0,0,2,2,4,0,0,0, - 0,0,0,0,0,0,0,0,4,4, 0,2,6,4,8,4,2,4,2,2, 2,2,4,4,2,2,26,2,4,8, - 14,4,4,4,2,2,4,4,6,14, 2,4,14,16,8,8,4,6,2,2, 4,0,2,2,0,2,0,0,0,2, - 2,0,2,4,6,6,2,0,0,0, 0,0 /* 0x64,0x65 */ -}; - -/* read little-endian words from a buffer */ -static int rd16 (const uint8_t *d, int o) { return d[o] | (d[o+1] << 8); } -static int rds16(const uint8_t *d, int o) { return s16(rd16(d, o)); } -static int32_t rd32 (const uint8_t *d, int o) { return (int32_t)(d[o] | (d[o+1]<<8) | (d[o+2]<<16) | (d[o+3]<<24)); } - -/* Buffer-based core: compile geometry from a .dat image already in memory (the - * caller owns `d`; no allocation/free here). The editor calls this with the - * track it serialized via WriteTrack. Returns 0 on success. */ -int gp2cc_compile_geometry_buf(const uint8_t *d, int len, gp2cc_track *t) { - (void)len; - int base = 0x1020 + rd32(d, 0x1010); /* MainTrackData */ - t->hdr_angle = rds16(d, base + 0); - int hdr_climb = rds16(d, base + 2); - t->hdr_Y = rds16(d, base + 4); - t->hdr_X = rds16(d, base + 8); - t->hdr_width = rd16(d, base + 0xA); - t->hdr_c5 = rd16(d, base + 0xE); - int unknownNumber = rd16(d, base + 0x12); - - int o = base + 0x14; /* Handle4Unknowns */ - for (int i = 0; i < unknownNumber; i++) { - int ax = rd16(d, o); o += 2; - if (ax != 0x1F) o += 2; - } - - /* ---- walk the command stream, integrating heading/startAngle (Q16) ---- */ - int32_t TA = (t->hdr_angle & 0xFFFF) << 16; /* d_TrkStrtAngle */ - int32_t SA = TA; /* d_StartAngle */ - int c5b12 = (t->hdr_c5 & 0x1000) != 0; - int c5b9 = (t->hdr_c5 & 0x200) != 0; - int n = 0, ccoff = -1; - - /* ---- road-width state (tseg+0x60 left / +0x62 right), with transition ramps. - * Width cmds: op 0x05 both (TrkWidthChange), 0x34 left (sub_75ACA), 0x35 right - * (sub_75BCD); each: arg1=transition length, arg2=target. len==0 = instant; - * len>0 = ramp over `len` segs, delta=(target-cur)/len (trunc). Per-seg: - * store cur THEN ramp (cur+=delta; len--). Init both = header width. */ - int curL = t->hdr_width, curR = t->hdr_width; - int deltaL = 0, deltaR = 0, transitL = 0, transitR = 0; - - for (;;) { - int w = rd16(d, o); - if (w == 0xFFFF) { ccoff = o + 2; break; } - if (w & 0x8000) { - int op = (w >> 8) & 0x7F; - int szc; - if (op == 0x45) szc = c5b9 ? 14 : 12; /* C5sub_75D97 conditional */ - else if (op < 0x66) szc = TRK_ARGSZ[op]; - else szc = -1; - if (szc < 0) { return -2; } /* desync / unknown opcode */ - if (op == 0x34 || op == 0x05) { /* left (or both) width change */ - int len = rds16(d, o + 2), target = rds16(d, o + 4); - if (len == 0) { curL = target; deltaL = 0; transitL = 0; } - else { transitL = len; deltaL = (target - curL) / len; } - } - if (op == 0x35 || op == 0x05) { /* right (or both) width change */ - int len = rds16(d, o + 2), target = rds16(d, o + 4); - if (len == 0) { curR = target; deltaR = 0; transitR = 0; } - else { transitR = len; deltaR = (target - curR) / len; } - } - o += 2 + szc; - continue; - } - /* geometry command: w = N segments */ - int N = w; - int d1w = rds16(d, o + 2); /* d_1stArg per-seg angle word */ - int32_t d1 = d1w << 16; /* ...as Q16 for the integration */ - /* tseg+0x14 (θ' tilt source) = (d1word * 0x6488) >> 14, 0x6488=round(pi/2*2^14); - * constant for every seg in this command. Validated bit-exact vs CCREP f14. */ - int32_t f14 = ((int32_t)d1w * 0x6488) >> 14; - o += 10; - /* half-step (sub_79166: TrkStrtAngle += d1/2). The bit12 gate is `xor ax,ax`, - * which zeros ONLY the low 16 bits of the 32-bit half-step (NOT the whole - * value): d1=d1w<<16, so the integer part of d1/2 survives when bit12 clear. - * Earlier `:0` wrongly killed the whole half-step -> command-boundary stalls - * on bit12-clear tracks (e.g. f1ct02a, c5=0xD80). */ - int32_t half_a = d1 >> 1; - if (!c5b12) half_a &= ~0xFFFF; - for (int k = 0; k < N; k++) { - if (n >= GP2CC_MAXSEG) { return -3; } - if (k == 0) { SA = TA; TA += half_a; } - else { TA += d1; SA += d1; } - t->fAngle[n] = TA; - t->angle[n] = (int16_t)(TA >> 16); - t->startAngle[n] = (int16_t)(SA >> 16); - t->f14[n] = f14; - t->widthL[n] = curL; /* store width, THEN ramp (sub_77AD0) */ - t->widthR[n] = curR; - if (transitL > 0) { curL += deltaL; transitL--; } - if (transitR > 0) { curR += deltaR; transitR--; } - n++; - } - TA += half_a; /* command-end half-step */ - } - /* the walk over-produces by 1 (a wrap seg the game doesn't count: - * w_NumTrackSegs = w_ActSegCount-1). Drop it so the warp uses the real N. */ - n -= 1; - t->n = n; - t->ccoff = ccoff; - - /* ---- side vectors (tseg+0x0C/0E right, +0x4C/4E left): one component = - * ((scos(startAngle)*width)>>17)<<6, matching InitTrackSegs 0x796C3/0x79735. */ - for (int i = 0; i < n; i++) { - int sa = s16(t->startAngle[i]); - int csa = scos(sa), ssa = scos(0x4000 - sa); /* cos/sin via raw t_Sinus */ - #define SIDEVEC(cosv, wid) ((int16_t)(((int16_t)((int)(cosv)*(int)(wid) >> 16) >> 1) << 6)) - t->sideRX[i] = SIDEVEC(csa, t->widthR[i]); - t->sideRY[i] = SIDEVEC(ssa, t->widthR[i]); - t->sideLX[i] = SIDEVEC(csa, t->widthL[i]); - t->sideLY[i] = SIDEVEC(ssa, t->widthL[i]); - #undef SIDEVEC - } - - /* ---- exact integer position (t_Sinus accumulation) + linear end-warp ---- */ - int32_t WX = t->hdr_X << 3, WY = t->hdr_Y << 3; - int32_t *rx8 = t->rawX8, *ry8 = t->rawY8; - for (int i = 0; i < n; i++) { - int h = t->fAngle[i] >> 16; - rx8[i] = WX; ry8[i] = WY; - WX += (scos(h) * 0x400) >> 14; /* X step = cos(h)*128 */ - WY += (scos(0x4000-h) * 0x400) >> 14; /* Y step = sin(h)*128 */ - } - int32_t gx8 = WX - rx8[0], gy8 = WY - ry8[0]; /* closure gap (1/8 units) */ - t->gapX8 = gx8; t->gapY8 = gy8; - /* EXACT integer warp (sub_0_76D39): a Bresenham gap-distributor. Verbatim - * behaviour: the routine SKIPS segment 0 (loop entry jumps to edi+=0x6C first), - * so segment i has accumulated |gap| exactly i times -> correction = - * sign(gap)*floor(i*|gap|/M), with M = w_ActSegCount = n+1 (the over-produced - * wrap seg the game counts but we drop). Validated bit-exact (full 1/8-unit incl - * sub-bits) vs the BESTLN dumps on F1CT01/02a/03/09/11/16. Subtract to close the - * loop. Earlier float `gap*i/n` was wrong on both M (n vs n+1) and the truncate- - * vs-floor of the negative quotient -> ~1u error that the cc-line amplified. */ - { - int64_t M = (int64_t)n + 1; - int64_t agx = gx8 < 0 ? -(int64_t)gx8 : gx8; - int64_t agy = gy8 < 0 ? -(int64_t)gy8 : gy8; - for (int i = 0; i < n; i++) { - int32_t sx = (int32_t)((agx * i) / M); /* floor, operand >= 0 */ - int32_t sy = (int32_t)((agy * i) / M); - t->X8[i] = rx8[i] - (gx8 < 0 ? -sx : sx); - t->Y8[i] = ry8[i] - (gy8 < 0 ? -sy : sy); - } - } - (void)hdr_climb; - return 0; -} - -/* File wrapper: read the whole .dat into a buffer, then compile from it. */ -int gp2cc_compile_geometry(const char *dat_path, gp2cc_track *t) { - FILE *f = fopen(dat_path, "rb"); - if (!f) return -1; - fseek(f, 0, SEEK_END); long sz = ftell(f); fseek(f, 0, SEEK_SET); - uint8_t *d = (uint8_t*)malloc(sz); - if (!d) { fclose(f); return -1; } - if (fread(d, 1, sz, f) != (size_t)sz) { fclose(f); free(d); return -1; } - fclose(f); - int rc = gp2cc_compile_geometry_buf(d, (int)sz, t); - free(d); - return rc; -} - -/* ======================================================================== * - * cc-line (racing line) — verbatim asm->C port of UACalcBestLine (0x78EB2) - * and its helper set. The cc-line is a chaotic feedback system; every step - * is transcribed instruction-faithfully so it is bit-exact by construction. - * IDA addrs cited per routine; disasm in new-export-from-selection-annotated.lst. - * ======================================================================== */ - -#define ONE60 (((int64_t)1) << 60) -#define CBB0C 20861 /* data const @0xCBB0C, segment-frame θ' tilt */ - -static int abs16(int v) { v = s16(v); return v < 0 ? -v : v; } - -/* sub_10240 (0x10240): integer sqrt of a 32-bit value via a FIXED 3-iteration - * 16-bit Newton (NOT exact floor — must be replicated bit-for-bit). */ -static uint32_t sqrt32_10240(uint32_t V) { - if (V == 0) return 0; - int shift = 0; - while (V < 0x40000000u) { shift++; V <<= 2; if (V == 0) return 0; } - uint32_t half = V >> 1; /* bx:cx */ - uint32_t di = ((V >> 17) + 0x8000) & 0xFFFF; /* 16-bit seed */ - for (int it = 0; it < 3; it++) { - uint32_t hi = half >> 16, q; - if (hi >= di) q = half & 0xFFFF; /* overflow guard (div skipped) */ - else q = half / di; /* 32/16 -> 16-bit */ - di = ((di >> 1) + q) & 0xFFFF; - } - return (di >> shift) & 0xFFFF; -} - -/* sub_102F0 (0x102F0): integer sqrt of a 64-bit value via a FIXED 5-iteration - * Newton (exact division inside, but fixed iters -> lands floor±1; NOT math.isqrt). - * Bit-exactness of the whole cc-line hinges on this matching the game exactly. */ -static uint32_t sqrt64_102F0(uint64_t V) { - if (V == 0) return 0; - if ((uint32_t)(V >> 32) == 0) return sqrt32_10240((uint32_t)V); - int shift = 0; - while ((V >> 32) < 0x40000000ull) { shift++; V <<= 2; } - uint64_t half = V >> 1; - uint32_t est = (uint32_t)((half >> 32) + 0x80000000ull); /* 32-bit seed */ - for (int it = 0; it < 5; it++) { - uint32_t q = (uint32_t)(half / est); /* exact unsigned div */ - est = (est >> 1) + q; /* 32-bit, wraps as asm */ - } - return est >> shift; -} - -/* sub_10502 (0x10502): cos(|angle|) in Q30 (amp 0x40000000) via the cosine - * table with high-precision interp. result32 = (COS[widx]<<16)+(delta*frac)<<13. - * Sign is carried by the extended COS table (negative for widx>2048). */ -static int32_t cos30(int angle) { - int a = abs16(angle); - int widx = a >> 3, frac = a & 7; - int base = COS[widx]; - int delta = COS[widx + 1] - COS[widx]; - return ((int32_t)base << 16) + ((int32_t)(delta * frac) << 13); -} - -/* sub_7827D (0x7827D): Q30 cos/sin of `angle`. Larger-magnitude component via - * sqrt(ONE60-other^2), smaller via the cos table; decided by coarse |sin|>=0x2000. - * Signs re-applied from the coarse table values. */ -static void q30_cossin(int angle, int32_t *pcos, int32_t *psin) { - int a = s16(angle); - int coarse_sin = COS[abs16(s16(0x4000 - a)) >> 3]; /* COS[..]=sin(a) coarse */ - int coarse_sin_abs = coarse_sin < 0 ? -coarse_sin : coarse_sin; - int32_t c30, s30; - if (coarse_sin_abs >= 0x2000) { /* sin large: cos via table, sin via sqrt */ - c30 = cos30(a); - int64_t smag = (int64_t)sqrt64_102F0((uint64_t)(ONE60 - (int64_t)c30 * c30)); - s30 = coarse_sin < 0 ? (int32_t)(-smag) : (int32_t)smag; - } else { /* cos large: sin via table, cos via sqrt */ - s30 = cos30(s16(0x4000 - a)); - int64_t cmag = (int64_t)sqrt64_102F0((uint64_t)(ONE60 - (int64_t)s30 * s30)); - int coarse_cos = COS[abs16(a) >> 3]; - c30 = coarse_cos < 0 ? (int32_t)(-cmag) : (int32_t)cmag; - } - *pcos = c30; *psin = s30; -} - -/* sub_10798 (0x10798): atan2(y,x) in 0x10000 units (full circle). 16-bit args. */ -static int atan2_u(int y, int x) { - y = s16(y); x = s16(x); - int dy = y < 0 ? -y : y; - int dx = x < 0 ? -x : x; - int signs_differ = (y < 0) != (x < 0); - int a; - if (dy >= dx) { - if (dy == 0) return 0; /* both zero */ - int idx = (dx << 11) / dy; /* <=2048 since dx<=dy */ - a = 0x4000 - ATAN1[idx]; - } else { - int idx = (dy << 11) / dx; - a = ATAN1[idx]; - } - if (signs_differ) a = -a; - if (x < 0) a += 0x8000; - return s16(a); -} - -/* sub_78492 (0x78492): build the world arc-centre point W=(C94BC,C94C0) for the - * first segment of a command, from (seg, P, H, arg2, arg1mul4). 1/8 world units. - * latP = (P*f14)>>15 ; base = rotate(P,latP) by PLAIN segAngle + segCentre - * + arg1mul4 along H ; + |arg2| along perp(H) (Q30 arc offset) when arg2!=0. */ -static void build_centre(const gp2cc_ccgeo *g, int idx, int P, int H, - int32_t arg2, int arg1mul4, int32_t *pWx, int32_t *pWy) { - int seg = idx; - int f14 = (int)g->f14[seg]; - int latP = (int)(((int32_t)P * f14) >> 15); /* (P*f14)<<2 then >>16 then >>1 */ - int ang = s16(g->segAngle[seg]); - int cosA = gp2cc_gv(ang); /* gv(segAngle) */ - int sinA = gp2cc_gv(s16(0x4000 - ang)); /* gv(0x4000-seg) */ - /* RX = P*cos + latP*sin ; RY = latP*cos - P*sin (each >>14, s16) */ - int32_t RX = (int16_t)(((int32_t)P * cosA + (int32_t)latP * sinA) >> 14); - int32_t RY = (int16_t)(((int32_t)latP * cosA - (int32_t)P * sinA) >> 14); - RX += g->cx8[seg]; - RY += g->cy8[seg]; - /* arg1mul4 component along H: gv(0x4000-H)=sin(H), gv(H)=cos(H) */ - int sinH = gp2cc_gv(s16(0x4000 - H)); - int cosH = gp2cc_gv(s16(H)); - int16_t a1m4 = (int16_t)arg1mul4; - RX += (int16_t)(((int32_t)sinH * a1m4) >> 14); - RY += (int16_t)(((int32_t)cosH * a1m4) >> 14); - int32_t Wx = RX, Wy = RY; - if (arg2 != 0) { - int32_t mag = arg2 < 0 ? -arg2 : arg2; - int perp = (arg2 < 0) ? s16(H - 0x4000) : s16(H + 0x4000); - int32_t c32, s32; q30_cossin(perp, &c32, &s32); - Wx += (int32_t)(((int64_t)s32 * mag) >> 30); /* x gets sin */ - Wy += (int32_t)(((int64_t)c32 * mag) >> 30); /* y gets cos */ - } - *pWx = Wx; *pWy = Wy; -} - -/* sub_787E7 (0x787E7): reproject world point W onto seg `idx` -> P (and H on - * curves). Uses the θ' tilt and reads the sub-bit-exact segment centre. */ -static void reproject(const gp2cc_ccgeo *g, int idx, int32_t Wx, int32_t Wy, - int32_t arg2, int *pP, int *pH) { - int seg = idx; - int f14 = (int)g->f14[seg]; - int ang = s16(g->segAngle[seg]); - /* θ' = segAngle - ((f14/2 * CBB0C)>>15) */ - int thetaP = s16(ang - (int)((((int32_t)(f14 >> 1) * CBB0C) << 1) >> 16)); - int32_t relx = Wx - g->cx8[seg]; - int32_t rely = Wy - g->cy8[seg]; - int32_t c32, s32; q30_cossin(thetaP, &c32, &s32); - /* lat = cos*relx - sin*rely ; lon = sin*relx + cos*rely (Q30, >>30) */ - int64_t lat64 = (int64_t)c32 * relx - (int64_t)s32 * rely; - int64_t lon64 = (int64_t)s32 * relx + (int64_t)c32 * rely; - int32_t lat = (int32_t)(lat64 >> 30); - int32_t lon = (int32_t)(lon64 >> 30); - if (arg2 == 0) { - /* straight (loc_78A54): P = lat - lon*tan(H-θ'). The asm uses sub_10502 - * (raw cos table, NOT the sqrt-normalised q30 pair): sn=cos30(0x4000-d), - * cs=cos30(d); tanterm = (sn*lon)/cs (signed 64-bit divide). H unchanged. */ - int d = s16(*pH - thetaP); - int32_t sn = cos30(s16(0x4000 - d)); /* sub_10502(0x4000-d) = sin(d) */ - int32_t cs = cos30(d); /* sub_10502(d) = cos(d) */ - int32_t tanterm = 0; - if (cs != 0) tanterm = (int32_t)(((int64_t)sn * lon) / cs); - *pP = s16(lat - tanterm); - return; - } - int64_t a2 = arg2; - int64_t disc = a2 * a2 - (int64_t)lon * lon; - int64_t T = (int64_t)sqrt64_102F0((uint64_t)disc); - int64_t Tsigned = (arg2 < 0) ? -T : T; - *pP = s16((int32_t)(lat - (int32_t)Tsigned)); - /* heading: scale (Tsigned, lon, |arg2|) down until |arg2|<0x7F00, then atan2 */ - int32_t Ts = (int32_t)Tsigned, lonv = lon; - uint32_t aa = (uint32_t)(arg2 < 0 ? -arg2 : arg2); - while (((aa >> 16) > 0) || ((aa >> 16) == 0 && (aa & 0xFFFF) >= 0x7F00)) { - Ts >>= 1; lonv >>= 1; aa >>= 1; - } - int at = atan2_u((int)(int16_t)(Ts & 0xFFFF), (int)(int16_t)(lonv & 0xFFFF)); - at = (arg2 < 0) ? s16(at + 0x4000) : s16(at - 0x4000); - *pH = s16(at + thetaP); -} - -/* sub_78CEC (0x78CEC): per-seg curvature-ramp nudge of W (slope!=0 commands). - * a = H ± 0x4000 (sign by arg2) ; C94BC += sin(a)*slope>>14, C94C0 += cos(a)*slope>>14. */ -static void nudge(int32_t *pWx, int32_t *pWy, int H, int32_t arg2, int32_t slope) { - int32_t ebp = slope; - int a; - if (arg2 < 0) { ebp = -ebp; a = s16(H - 0x4000); } - else a = s16(H + 0x4000); - int sinA = gp2cc_gv(s16(0x4000 - a)); - int cosA = gp2cc_gv(s16(a)); - *pWx += (int32_t)(((int64_t)sinA * ebp) >> 14); - *pWy += (int32_t)(((int64_t)cosA * ebp) >> 14); -} - -/* GetSingleCmdArgs (0x78D4C): parse one cc command from cc[*po]. */ -typedef struct { - int end; /* word==0 (last command) */ - int word; /* the command word (bp) */ - int N; /* word & 0x7FF (wActCCLCookedCmd) */ - int a1, a2; /* wCCL1stCmdArg1/2 (bit15 header words) */ - int32_t arg2; /* dActCCLineArg2 (×8 unless bit12, dword if bit14) */ - int32_t slope; /* dArg3mArg2divLen = (arg3-arg2)/N */ - int arg1mul4; /* wActCCLArg1mul4 (arg1<<2 when arg2!=0) */ - int arg1if2is0; /* wActCLArg1If2is0 (arg1 when arg2==0) */ -} cc_cmd; - -static int rdw(const uint8_t *d, int o) { return d[o] | (d[o + 1] << 8); } - -static void parse_cmd(const uint8_t *cc, int *po, cc_cmd *c) { - int o = *po; - memset(c, 0, sizeof *c); - int word = rdw(cc, o); o += 2; - c->word = word; - c->N = word & 0x7FF; - if (word == 0) { c->end = 1; *po = o; return; } - if (word & 0x8000) { c->a1 = rdw(cc, o); o += 2; c->a2 = rdw(cc, o); o += 2; } - int cx = rdw(cc, o); o += 2; /* arg1 */ - int32_t arg2 = (int32_t)(int16_t)rdw(cc, o); o += 2; - if (word & 0x4000) { arg2 = (int32_t)((uint32_t)arg2 << 16) | rdw(cc, o); o += 2; } - if (!(word & 0x1000)) arg2 <<= 3; - c->arg2 = arg2; - if (word & 0x2000) { /* arg3 → slope */ - int32_t arg3 = (int32_t)(int16_t)rdw(cc, o); o += 2; - if (word & 0x4000) { arg3 = (int32_t)((uint32_t)arg3 << 16) | rdw(cc, o); o += 2; } - if (!(word & 0x1000)) arg3 <<= 3; - int32_t diff = arg3 - arg2; - c->slope = diff / c->N; /* idiv (trunc toward zero) */ - } - if (arg2 != 0) { c->arg1if2is0 = 0; c->arg1mul4 = (int16_t)(cx << 2); } - else { c->arg1mul4 = 0; c->arg1if2is0 = cx; } - *po = o; -} - -int gp2cc_reproject_dbg(int segAngle, int32_t cx8, int32_t cy8, int f14, - int32_t Wx, int32_t Wy, int32_t arg2, int Hin, int *pH) { - gp2cc_ccgeo g; g.n = 1; - g.segAngle[0] = (int16_t)segAngle; g.cx8[0] = cx8; g.cy8[0] = cy8; g.f14[0] = f14; - int P = 0, H = Hin; - reproject(&g, 0, Wx, Wy, arg2, &P, &H); - if (pH) *pH = H; - return P; -} - -/* Front half of sub_787E7 exposed for stage validation: θ', q30 cos/sin, lat, lon. */ -void gp2cc_reproject_stages(int segAngle, int32_t cx8, int32_t cy8, int f14, - int32_t Wx, int32_t Wy, - int32_t *pCos32, int32_t *pSin32, - int32_t *pLat, int32_t *pLon, int *pTheta) { - int ang = s16(segAngle); - int thetaP = s16(ang - (int)((((int32_t)(f14 >> 1) * CBB0C) << 1) >> 16)); - int32_t relx = Wx - cx8; - int32_t rely = Wy - cy8; - int32_t c32, s32; q30_cossin(thetaP, &c32, &s32); - int64_t lat64 = (int64_t)c32 * relx - (int64_t)s32 * rely; - int64_t lon64 = (int64_t)s32 * relx + (int64_t)c32 * rely; - if (pCos32) *pCos32 = c32; - if (pSin32) *pSin32 = s32; - if (pLat) *pLat = (int32_t)(lat64 >> 30); - if (pLon) *pLon = (int32_t)(lon64 >> 30); - if (pTheta) *pTheta = thetaP; -} - -void gp2cc_build_dbg(int segAngle, int32_t cx8, int32_t cy8, int f14, - int P, int H, int32_t arg2, int arg1mul4, - int32_t *pWx, int32_t *pWy) { - gp2cc_ccgeo g; g.n = 1; - g.segAngle[0] = (int16_t)segAngle; g.cx8[0] = cx8; g.cy8[0] = cy8; g.f14[0] = f14; - build_centre(&g, 0, P, H, arg2, arg1mul4, pWx, pWy); -} - -int gp2cc_compute_ccline_dbg(const gp2cc_ccgeo *g, const uint8_t *cc, int cc_len, - int cc_off, int16_t *bestLine, int16_t *angle18, - int32_t *capWx, int32_t *capWy, int32_t *capArg2, - int *cmdStartSeg, int *pNumCmds) { - int n = g->n; - if (n <= 0) return -1; - int cur = 0, cmdIdx = 0; - /* seed (0x78EB2): H = segAngle[0]; P = ccdata[cc_off+2] if bit15 && !bit11 */ - int H = s16(g->segAngle[0]); - int P = 0; - int w0 = rdw(cc, cc_off); - /* seed segPosX_0A (=P) is a SIGNED 16-bit word ([ebx+2], used by a signed - * imul in sub_78492); sign-extend or build_centre blows up on negative seeds. */ - if (!(w0 & 0x800) && (w0 & 0x8000)) P = s16(rdw(cc, cc_off + 2)); - int o = cc_off; - for (;;) { - cc_cmd c; - parse_cmd(cc, &o, &c); - if (c.end) break; - if (o > cc_len) return -2; - if ((c.word & 0x800) && (c.word & 0x8000)) { P = s16(c.a1); H = s16(c.a2); } - if (c.arg2 == 0) H = s16(H + s16(c.arg1if2is0)); - if (cmdStartSeg) cmdStartSeg[cmdIdx] = cur; /* this command's first segment */ - cmdIdx++; - int32_t Wx, Wy; - build_centre(g, cur, P, H, c.arg2, c.arg1mul4, &Wx, &Wy); - int32_t arg2 = c.arg2; - int left = c.N; - while (left > 0) { - angle18[cur] = (int16_t)s16(H - s16(g->segAngle[cur])); - bestLine[cur] = (int16_t)s16(P); - if (capWx) capWx[cur] = Wx; - if (capWy) capWy[cur] = Wy; - if (capArg2) capArg2[cur] = arg2; - cur = (cur + 1) % n; - reproject(g, cur, Wx, Wy, arg2, &P, &H); - left--; - if (left == 0) break; - if (arg2 != 0 && c.slope != 0) { - arg2 += c.slope; - nudge(&Wx, &Wy, H, arg2, c.slope); - } - } - } - if (pNumCmds) *pNumCmds = cmdIdx; - return 0; -} - -int gp2cc_compute_ccline(const gp2cc_ccgeo *g, const uint8_t *cc, int cc_len, - int cc_off, int16_t *bestLine, int16_t *angle18, - int *cmdStartSeg, int *pNumCmds) { - return gp2cc_compute_ccline_dbg(g, cc, cc_len, cc_off, bestLine, angle18, - 0, 0, 0, cmdStartSeg, pNumCmds); -} diff --git a/gp2cc.h b/gp2cc.h index c3f7aeb..eb63fb2 100644 --- a/gp2cc.h +++ b/gp2cc.h @@ -1,25 +1,17 @@ /* - * gp2cc — standalone bit-exact reproduction of GP2's compiled track geometry - * and cc-line (racing line), ported verbatim from the disassembly. + * gp2cc.h — the shared compiled-track data format. * - * Goal: given a track .dat (and the t_Sinus table from GP2.EXE), reproduce the - * game's per-segment compiled geometry + cc-line bit-for-bit, validated against - * the GP2Lap BESTLNN.TXT dumps. The editor's new compiled-track view will call - * this module and just draw the arrays — it never touches the fixed point. - * - * Status: geometry compiler ported + validated (heading/startAngle exact, - * position via integer t_Sinus accumulation + warp). cc-line (build/reproject/ - * nudge + the Q30/atan2 helpers) to follow. + * These two structs are the bit-exact output of GP2's track-geometry + + * cc-line (racing-line) compilation: gp2cc_track is filled by + * gp2geom::compileGeometry, and gp2cc_ccgeo feeds gp2ccline::computeCcLine. + * The editor's overlay just draws the arrays — it never touches the fixed + * point. Field offsets/units reference the original engine layout (tseg+N). */ #ifndef GP2CC_H #define GP2CC_H #include -#ifdef __cplusplus -extern "C" { -#endif - #define GP2CC_MAXSEG 4096 typedef struct { @@ -57,57 +49,4 @@ typedef struct { int32_t f14[GP2CC_MAXSEG]; /* tseg+0x14 (width-derived, drives θ' tilt) */ } gp2cc_ccgeo; -/* Load the t_Sinus cosine table + t_ArithTab1 atan table from GP2.EXE - * (file offs 0x1DDAEC / 0x1DFAF0). Returns 0 ok. */ -int gp2cc_load_tables(const char *gp2exe_path); - -/* GetSinusVal (0x104B9): cosine, amplitude 0x4000, 8-unit table + lerp. */ -int gp2cc_gv(int ax); - -/* Compile the track geometry from a .dat file into `t`. Returns 0 on success. */ -int gp2cc_compile_geometry(const char *dat_path, gp2cc_track *t); - -/* Same, from a .dat image already in memory (caller owns `dat`). For the editor, - * which serializes its in-memory track to a .dat buffer. Returns 0 on success. */ -int gp2cc_compile_geometry_buf(const uint8_t *dat, int len, gp2cc_track *t); - -/* Run UACalcBestLine (0x78EB2) over the cc-command stream `cc` (the raw .dat - * bytes) starting at byte offset `cc_off`, using geometry `g`. Writes per-seg - * bestLine (tseg+0x16) and angle18 (tseg+0x18). Returns 0 on success. - * Verbatim asm->C port of the cc-line helpers (bit-exact by construction). */ -/* Run the cc-line. `cmdStartSeg` (NULL ok) is filled with each cc-command's first - * segment index (cmdStartSeg[0..*pNumCmds-1]); the editor's CCLineSections map 1:1 - * to cc-commands in order, so this places the per-sector indicators. */ -int gp2cc_compute_ccline(const gp2cc_ccgeo *g, const uint8_t *cc, int cc_len, - int cc_off, int16_t *bestLine, int16_t *angle18, - int *cmdStartSeg, int *pNumCmds); - -/* Same, but also captures the carried world point W (=C94BC/C94C0) and arg2 - * per segment (for stage-by-stage validation vs CCREP). Any out ptr may be NULL. */ -int gp2cc_compute_ccline_dbg(const gp2cc_ccgeo *g, const uint8_t *cc, int cc_len, - int cc_off, int16_t *bestLine, int16_t *angle18, - int32_t *capWx, int32_t *capWy, int32_t *capArg2, - int *cmdStartSeg, int *pNumCmds); - -/* Debug: reproject a SINGLE world point W onto one segment (isolates sub_787E7 - * from the carried-W feedback). Returns P; writes *pH (curve heading). */ -int gp2cc_reproject_dbg(int segAngle, int32_t cx8, int32_t cy8, int f14, - int32_t Wx, int32_t Wy, int32_t arg2, int Hin, int *pH); - -/* Debug: build the world point W for one segment (isolates sub_78492). */ -void gp2cc_build_dbg(int segAngle, int32_t cx8, int32_t cy8, int f14, - int P, int H, int32_t arg2, int arg1mul4, - int32_t *pWx, int32_t *pWy); - -/* Debug: expose the reproject intermediates (q30 cos/sin of θ', and lat/lon) - * for stage-by-stage validation against the extended CCREP dump. */ -void gp2cc_reproject_stages(int segAngle, int32_t cx8, int32_t cy8, int f14, - int32_t Wx, int32_t Wy, - int32_t *pCos32, int32_t *pSin32, - int32_t *pLat, int32_t *pLon, int *pTheta); - -#ifdef __cplusplus -} -#endif - #endif diff --git a/gp2cc_tables.c b/gp2cc_tables.c deleted file mode 100644 index 8f84007..0000000 --- a/gp2cc_tables.c +++ /dev/null @@ -1,404 +0,0 @@ -/* gp2cc_tables.c — GENERATED from GP2.EXE. The game's exact trig tables - * embedded so gp2cc needs no GP2.EXE at runtime (e.g. inside the editor). - * gp2cc_COS = t_Sinus (file 0x1DDAEC), amp 0x4000, 4200 entries - * gp2cc_ATAN1 = t_ArithTab1 (file 0x1DFAF0), 2050 entries - * Regenerate: extract those two ranges from GP2.EXE. */ - -const short gp2cc_COS[4200] = { - 16384,16384,16384,16384,16384,16384,16384,16384,16384,16384,16384,16383,16383,16383,16383,16383, - 16383,16383,16382,16382,16382,16382,16382,16381,16381,16381,16381,16380,16380,16380,16380,16379, - 16379,16379,16378,16378,16378,16377,16377,16377,16376,16376,16375,16375,16375,16374,16374,16373, - 16373,16372,16372,16371,16371,16370,16370,16369,16369,16368,16368,16367,16367,16366,16365,16365, - 16364,16364,16363,16362,16362,16361,16360,16360,16359,16358,16358,16357,16356,16355,16355,16354, - 16353,16352,16352,16351,16350,16349,16348,16348,16347,16346,16345,16344,16343,16342,16341,16341, - 16340,16339,16338,16337,16336,16335,16334,16333,16332,16331,16330,16329,16328,16327,16326,16325, - 16324,16323,16321,16320,16319,16318,16317,16316,16315,16313,16312,16311,16310,16309,16308,16306, - 16305,16304,16303,16301,16300,16299,16298,16296,16295,16294,16292,16291,16290,16288,16287,16286, - 16284,16283,16281,16280,16279,16277,16276,16274,16273,16271,16270,16268,16267,16265,16264,16262, - 16261,16259,16258,16256,16255,16253,16251,16250,16248,16247,16245,16243,16242,16240,16238,16237, - 16235,16233,16232,16230,16228,16226,16225,16223,16221,16219,16218,16216,16214,16212,16210,16209, - 16207,16205,16203,16201,16199,16197,16195,16194,16192,16190,16188,16186,16184,16182,16180,16178, - 16176,16174,16172,16170,16168,16166,16164,16162,16160,16158,16156,16153,16151,16149,16147,16145, - 16143,16141,16138,16136,16134,16132,16130,16128,16125,16123,16121,16119,16116,16114,16112,16109, - 16107,16105,16103,16100,16098,16096,16093,16091,16088,16086,16084,16081,16079,16076,16074,16072, - 16069,16067,16064,16062,16059,16057,16054,16052,16049,16047,16044,16042,16039,16037,16034,16031, - 16029,16026,16024,16021,16018,16016,16013,16010,16008,16005,16002,16000,15997,15994,15991,15989, - 15986,15983,15980,15978,15975,15972,15969,15966,15964,15961,15958,15955,15952,15949,15946,15944, - 15941,15938,15935,15932,15929,15926,15923,15920,15917,15914,15911,15908,15905,15902,15899,15896, - 15893,15890,15887,15884,15881,15878,15875,15871,15868,15865,15862,15859,15856,15853,15849,15846, - 15843,15840,15837,15833,15830,15827,15824,15820,15817,15814,15810,15807,15804,15801,15797,15794, - 15791,15787,15784,15780,15777,15774,15770,15767,15763,15760,15757,15753,15750,15746,15743,15739, - 15736,15732,15729,15725,15722,15718,15715,15711,15707,15704,15700,15697,15693,15689,15686,15682, - 15679,15675,15671,15668,15664,15660,15656,15653,15649,15645,15642,15638,15634,15630,15627,15623, - 15619,15615,15611,15608,15604,15600,15596,15592,15588,15584,15581,15577,15573,15569,15565,15561, - 15557,15553,15549,15545,15541,15537,15533,15529,15525,15521,15517,15513,15509,15505,15501,15497, - 15493,15489,15485,15481,15476,15472,15468,15464,15460,15456,15451,15447,15443,15439,15435,15430, - 15426,15422,15418,15414,15409,15405,15401,15396,15392,15388,15383,15379,15375,15370,15366,15362, - 15357,15353,15349,15344,15340,15335,15331,15326,15322,15318,15313,15309,15304,15300,15295,15291, - 15286,15282,15277,15273,15268,15263,15259,15254,15250,15245,15240,15236,15231,15227,15222,15217, - 15213,15208,15203,15199,15194,15189,15184,15180,15175,15170,15166,15161,15156,15151,15146,15142, - 15137,15132,15127,15122,15118,15113,15108,15103,15098,15093,15088,15083,15078,15074,15069,15064, - 15059,15054,15049,15044,15039,15034,15029,15024,15019,15014,15009,15004,14999,14994,14989,14984, - 14978,14973,14968,14963,14958,14953,14948,14943,14937,14932,14927,14922,14917,14911,14906,14901, - 14896,14891,14885,14880,14875,14870,14864,14859,14854,14848,14843,14838,14832,14827,14822,14816, - 14811,14806,14800,14795,14789,14784,14779,14773,14768,14762,14757,14751,14746,14740,14735,14729, - 14724,14718,14713,14707,14702,14696,14691,14685,14680,14674,14668,14663,14657,14651,14646,14640, - 14635,14629,14623,14618,14612,14606,14601,14595,14589,14583,14578,14572,14566,14560,14555,14549, - 14543,14537,14531,14526,14520,14514,14508,14502,14497,14491,14485,14479,14473,14467,14461,14455, - 14449,14443,14438,14432,14426,14420,14414,14408,14402,14396,14390,14384,14378,14372,14366,14360, - 14354,14347,14341,14335,14329,14323,14317,14311,14305,14299,14293,14286,14280,14274,14268,14262, - 14256,14249,14243,14237,14231,14224,14218,14212,14206,14199,14193,14187,14181,14174,14168,14162, - 14155,14149,14143,14136,14130,14124,14117,14111,14104,14098,14092,14085,14079,14072,14066,14059, - 14053,14047,14040,14034,14027,14021,14014,14008,14001,13995,13988,13981,13975,13968,13962,13955, - 13949,13942,13935,13929,13922,13916,13909,13902,13896,13889,13882,13876,13869,13862,13856,13849, - 13842,13835,13829,13822,13815,13808,13802,13795,13788,13781,13774,13768,13761,13754,13747,13740, - 13733,13727,13720,13713,13706,13699,13692,13685,13678,13671,13665,13658,13651,13644,13637,13630, - 13623,13616,13609,13602,13595,13588,13581,13574,13567,13560,13553,13546,13538,13531,13524,13517, - 13510,13503,13496,13489,13482,13474,13467,13460,13453,13446,13439,13431,13424,13417,13410,13403, - 13395,13388,13381,13374,13366,13359,13352,13344,13337,13330,13323,13315,13308,13301,13293,13286, - 13279,13271,13264,13256,13249,13242,13234,13227,13219,13212,13205,13197,13190,13182,13175,13167, - 13160,13152,13145,13137,13130,13122,13115,13107,13100,13092,13085,13077,13069,13062,13054,13047, - 13039,13031,13024,13016,13008,13001,12993,12986,12978,12970,12963,12955,12947,12939,12932,12924, - 12916,12909,12901,12893,12885,12878,12870,12862,12854,12846,12839,12831,12823,12815,12807,12799, - 12792,12784,12776,12768,12760,12752,12744,12736,12729,12721,12713,12705,12697,12689,12681,12673, - 12665,12657,12649,12641,12633,12625,12617,12609,12601,12593,12585,12577,12569,12561,12553,12545, - 12537,12528,12520,12512,12504,12496,12488,12480,12472,12463,12455,12447,12439,12431,12423,12414, - 12406,12398,12390,12381,12373,12365,12357,12348,12340,12332,12324,12315,12307,12299,12290,12282, - 12274,12266,12257,12249,12240,12232,12224,12215,12207,12199,12190,12182,12173,12165,12157,12148, - 12140,12131,12123,12114,12106,12097,12089,12080,12072,12064,12055,12046,12038,12029,12021,12012, - 12004,11995,11987,11978,11970,11961,11952,11944,11935,11927,11918,11909,11901,11892,11883,11875, - 11866,11857,11849,11840,11831,11823,11814,11805,11797,11788,11779,11770,11762,11753,11744,11735, - 11727,11718,11709,11700,11691,11683,11674,11665,11656,11647,11638,11630,11621,11612,11603,11594, - 11585,11576,11567,11559,11550,11541,11532,11523,11514,11505,11496,11487,11478,11469,11460,11451, - 11442,11433,11424,11415,11406,11397,11388,11379,11370,11361,11352,11343,11334,11325,11316,11307, - 11297,11288,11279,11270,11261,11252,11243,11234,11224,11215,11206,11197,11188,11179,11169,11160, - 11151,11142,11133,11123,11114,11105,11096,11086,11077,11068,11059,11049,11040,11031,11021,11012, - 11003,10994,10984,10975,10966,10956,10947,10937,10928,10919,10909,10900,10891,10881,10872,10862, - 10853,10844,10834,10825,10815,10806,10796,10787,10778,10768,10759,10749,10740,10730,10721,10711, - 10702,10692,10683,10673,10663,10654,10644,10635,10625,10616,10606,10597,10587,10577,10568,10558, - 10549,10539,10529,10520,10510,10500,10491,10481,10471,10462,10452,10442,10433,10423,10413,10404, - 10394,10384,10374,10365,10355,10345,10336,10326,10316,10306,10296,10287,10277,10267,10257,10248, - 10238,10228,10218,10208,10198,10189,10179,10169,10159,10149,10139,10129,10120,10110,10100,10090, - 10080,10070,10060,10050,10040,10030,10020,10010,10001,9991,9981,9971,9961,9951,9941,9931, - 9921,9911,9901,9891,9881,9871,9861,9851,9841,9830,9820,9810,9800,9790,9780,9770, - 9760,9750,9740,9730,9720,9709,9699,9689,9679,9669,9659,9649,9638,9628,9618,9608, - 9598,9588,9577,9567,9557,9547,9537,9526,9516,9506,9496,9485,9475,9465,9455,9444, - 9434,9424,9413,9403,9393,9383,9372,9362,9352,9341,9331,9321,9310,9300,9290,9279, - 9269,9259,9248,9238,9227,9217,9207,9196,9186,9175,9165,9155,9144,9134,9123,9113, - 9102,9092,9082,9071,9061,9050,9040,9029,9019,9008,8998,8987,8977,8966,8956,8945, - 8935,8924,8914,8903,8892,8882,8871,8861,8850,8840,8829,8818,8808,8797,8787,8776, - 8765,8755,8744,8734,8723,8712,8702,8691,8680,8670,8659,8648,8638,8627,8616,8606, - 8595,8584,8573,8563,8552,8541,8531,8520,8509,8498,8488,8477,8466,8455,8445,8434, - 8423,8412,8401,8391,8380,8369,8358,8347,8337,8326,8315,8304,8293,8283,8272,8261, - 8250,8239,8228,8217,8207,8196,8185,8174,8163,8152,8141,8130,8119,8108,8098,8087, - 8076,8065,8054,8043,8032,8021,8010,7999,7988,7977,7966,7955,7944,7933,7922,7911, - 7900,7889,7878,7867,7856,7845,7834,7823,7812,7801,7790,7779,7768,7757,7746,7734, - 7723,7712,7701,7690,7679,7668,7657,7646,7635,7623,7612,7601,7590,7579,7568,7557, - 7545,7534,7523,7512,7501,7490,7478,7467,7456,7445,7434,7423,7411,7400,7389,7378, - 7366,7355,7344,7333,7321,7310,7299,7288,7276,7265,7254,7243,7231,7220,7209,7198, - 7186,7175,7164,7152,7141,7130,7118,7107,7096,7084,7073,7062,7050,7039,7028,7016, - 7005,6994,6982,6971,6960,6948,6937,6925,6914,6903,6891,6880,6868,6857,6846,6834, - 6823,6811,6800,6788,6777,6766,6754,6743,6731,6720,6708,6697,6685,6674,6662,6651, - 6639,6628,6616,6605,6593,6582,6570,6559,6547,6536,6524,6513,6501,6490,6478,6467, - 6455,6444,6432,6420,6409,6397,6386,6374,6363,6351,6339,6328,6316,6305,6293,6281, - 6270,6258,6247,6235,6223,6212,6200,6189,6177,6165,6154,6142,6130,6119,6107,6095, - 6084,6072,6060,6049,6037,6025,6014,6002,5990,5979,5967,5955,5943,5932,5920,5908, - 5897,5885,5873,5861,5850,5838,5826,5814,5803,5791,5779,5767,5756,5744,5732,5720, - 5708,5697,5685,5673,5661,5650,5638,5626,5614,5602,5591,5579,5567,5555,5543,5531, - 5520,5508,5496,5484,5472,5460,5449,5437,5425,5413,5401,5389,5377,5366,5354,5342, - 5330,5318,5306,5294,5282,5270,5259,5247,5235,5223,5211,5199,5187,5175,5163,5151, - 5139,5127,5115,5104,5092,5080,5068,5056,5044,5032,5020,5008,4996,4984,4972,4960, - 4948,4936,4924,4912,4900,4888,4876,4864,4852,4840,4828,4816,4804,4792,4780,4768, - 4756,4744,4732,4720,4708,4696,4684,4672,4660,4648,4636,4624,4612,4599,4587,4575, - 4563,4551,4539,4527,4515,4503,4491,4479,4467,4455,4442,4430,4418,4406,4394,4382, - 4370,4358,4346,4333,4321,4309,4297,4285,4273,4261,4249,4236,4224,4212,4200,4188, - 4176,4164,4151,4139,4127,4115,4103,4091,4078,4066,4054,4042,4030,4018,4005,3993, - 3981,3969,3957,3944,3932,3920,3908,3896,3883,3871,3859,3847,3835,3822,3810,3798, - 3786,3773,3761,3749,3737,3724,3712,3700,3688,3676,3663,3651,3639,3627,3614,3602, - 3590,3577,3565,3553,3541,3528,3516,3504,3492,3479,3467,3455,3442,3430,3418,3406, - 3393,3381,3369,3356,3344,3332,3320,3307,3295,3283,3270,3258,3246,3233,3221,3209, - 3196,3184,3172,3159,3147,3135,3122,3110,3098,3085,3073,3061,3048,3036,3024,3011, - 2999,2987,2974,2962,2949,2937,2925,2912,2900,2888,2875,2863,2851,2838,2826,2813, - 2801,2789,2776,2764,2752,2739,2727,2714,2702,2690,2677,2665,2652,2640,2628,2615, - 2603,2590,2578,2566,2553,2541,2528,2516,2503,2491,2479,2466,2454,2441,2429,2416, - 2404,2392,2379,2367,2354,2342,2329,2317,2305,2292,2280,2267,2255,2242,2230,2217, - 2205,2193,2180,2168,2155,2143,2130,2118,2105,2093,2080,2068,2055,2043,2031,2018, - 2006,1993,1981,1968,1956,1943,1931,1918,1906,1893,1881,1868,1856,1843,1831,1818, - 1806,1793,1781,1768,1756,1743,1731,1718,1706,1693,1681,1668,1656,1643,1631,1618, - 1606,1593,1581,1568,1556,1543,1531,1518,1506,1493,1481,1468,1456,1443,1431,1418, - 1406,1393,1381,1368,1356,1343,1331,1318,1306,1293,1280,1268,1255,1243,1230,1218, - 1205,1193,1180,1168,1155,1143,1130,1118,1105,1092,1080,1067,1055,1042,1030,1017, - 1005,992,980,967,955,942,929,917,904,892,879,867,854,842,829,816, - 804,791,779,766,754,741,729,716,703,691,678,666,653,641,628,616, - 603,590,578,565,553,540,528,515,503,490,477,465,452,440,427,415, - 402,390,377,364,352,339,327,314,302,289,276,264,251,239,226,214, - 201,188,176,163,151,138,126,113,101,88,75,63,50,38,25,13, - 0,-13,-25,-38,-50,-63,-75,-88,-101,-113,-126,-138,-151,-163,-176,-188, - -201,-214,-226,-239,-251,-264,-276,-289,-302,-314,-327,-339,-352,-364,-377,-390, - -402,-415,-427,-440,-452,-465,-477,-490,-503,-515,-528,-540,-553,-565,-578,-590, - -603,-616,-628,-641,-653,-666,-678,-691,-704,-716,-729,-741,-754,-766,-779,-791, - -804,-816,-829,-842,-854,-867,-879,-892,-904,-917,-929,-942,-955,-967,-980,-992, - -1005,-1017,-1030,-1042,-1055,-1067,-1080,-1092,-1105,-1118,-1130,-1143,-1155,-1168,-1180,-1193, - -1205,-1218,-1230,-1243,-1255,-1268,-1280,-1293,-1306,-1318,-1331,-1343,-1356,-1368,-1381,-1393, - -1406,-1418,-1431,-1443,-1456,-1468,-1481,-1493,-1506,-1518,-1531,-1543,-1556,-1568,-1581,-1593, - -1606,-1618,-1631,-1643,-1656,-1668,-1681,-1693,-1706,-1718,-1731,-1743,-1756,-1768,-1781,-1793, - -1806,-1818,-1831,-1843,-1856,-1868,-1881,-1893,-1906,-1918,-1931,-1943,-1956,-1968,-1981,-1993, - -2006,-2018,-2031,-2043,-2055,-2068,-2080,-2093,-2105,-2118,-2130,-2143,-2155,-2168,-2180,-2193, - -2205,-2217,-2230,-2242,-2255,-2267,-2280,-2292,-2305,-2317,-2329,-2342,-2354,-2367,-2379,-2392, - -2404,-2416,-2429,-2441,-2454,-2466,-2479,-2491,-2503,-2516,-2528,-2541,-2553,-2566,-2578,-2590, - -2603,-2615,-2628,-2640,-2652,-2665,-2677,-2690,-2702,-2714,-2727,-2739,-2752,-2764,-2776,-2789, - -2801,-2813,-2826,-2838,-2851,-2863,-2875,-2888,-2900,-2912,-2925,-2937,-2949,-2962,-2974,-2987, - -2999,-3011,-3024,-3036,-3048,-3061,-3073,-3085,-3098,-3110,-3122,-3135,-3147,-3159,-3172,-3184, - -3196,-3209,-3221,-3233,-3246,-3258,-3270,-3283,-3295,-3307,-3320,-3332,-3344,-3356,-3369,-3381, - -3393,-3406,-3418,-3430,-3442,-3455,-3467,-3479,-3492,-3504,-3516,-3528,-3541,-3553,-3565,-3577, - -3590,-3602,-3614,-3627,-3639,-3651,-3663,-3676,-3688,-3700,-3712,-3724,-3737,-3749,-3761,-3773, - -3786,-3798,-3810,-3822,-3835,-3847,-3859,-3871,-3883,-3896,-3908,-3920,-3932,-3944,-3957,-3969, - -3981,-3993,-4005,-4018,-4030,-4042,-4054,-4066,-4078,-4091,-4103,-4115,-4127,-4139,-4151,-4164, - -4176,-4188,-4200,-4212,-4224,-4236,-4249,-4261,-4273,-4285,-4297,-4309,-4321,-4333,-4346,-4358, - -4370,-4382,-4394,-4406,-4418,-4430,-4442,-4455,-4467,-4479,-4491,-4503,-4515,-4527,-4539,-4551, - -4563,-4575,-4587,-4599,-4612,-4624,-4636,-4648,-4660,-4672,-4684,-4696,-4708,-4720,-4732,-4744, - -4756,-4768,-4780,-4792,-4804,-4816,-4828,-4840,-4852,-4864,-4876,-4888,-4900,-4912,-4924,-4936, - -4948,-4960,-4972,-4984,-4996,-5008,-5020,-5032,-5044,-5056,-5068,-5080,-5092,-5104,-5115,-5127, - -5139,-5151,-5163,-5175,-5187,-5199,-5211,-5223,-5235,-5247,-5259,-5270,-5282,-5294,-5306,-5318, - -5330,-5342,-5354,-5366,-5377,-5389,-5401,-5413,-5425,-5437,-5449,-5460,-5472,-5484,-5496,-5508, - -5520,-5531,-5543,-5555,-5567,-5579,-5591,-5602,-5614,-5626,-5638,-5650,-5661,-5673,-5685,-5697, - -5708,-5720,-5732,-5744,-5756,-5767,-5779,-5791,-5803,-5814,-5826,-5838,-5850,-5861,-5873,-5885, - -5897,-5908,-5920,-5932,-5943,-5955,-5967,-5979,-5990,-6002,-6014,-6025,-6037,-6049,-6060,-6072, - -6084,-6095,-6107,-6119,-6130,-6142,-6154,-6165,-6177,-6189,-6200,-6212,-6223,-6235,-6247,-6258, - -6270,-6281,-6293,-6305,-6316,-6328,-6339,-6351,-6363,-6374,-6386,-6397,-6409,-6420,-6432,-6444, - -6455,-6467,-6478,-6490,-6501,-6513,-6524,-6536,-6547,-6559,-6570,-6582,-6593,-6605,-6616,-6628, - -6639,-6651,-6662,-6674,-6685,-6697,-6708,-6720,-6731,-6743,-6754,-6766,-6777,-6788,-6800,-6811, - -6823,-6834,-6846,-6857,-6868,-6880,-6891,-6903,-6914,-6925,-6937,-6948,-6960,-6971,-6982,-6994, - -7005,-7016,-7028,-7039,-7050,-7062,-7073,-7084,-7096,-7107,-7118,-7130,-7141,-7152,-7164,-7175, - -7186,-7198,-7209,-7220,-7231,-7243,-7254,-7265,-7276,-7288,-7299,-7310,-7321,-7333,-7344,-7355, - -7366,-7378,-7389,-7400,-7411,-7423,-7434,-7445,-7456,-7467,-7478,-7490,-7501,-7512,-7523,-7534, - -7545,-7557,-7568,-7579,-7590,-7601,-7612,-7623,-7635,-7646,-7657,-7668,-7679,-7690,-7701,-7712, - -7723,-7734,-7746,-7757,-7768,-7779,-7790,-7801,-7812,-7823,-7834,-7845,-7856,-7867,-7878,-7889, - -7900,-7911,-7922,-7933,-7944,-7955,-7966,-7977,-7988,-7999,-8010,-8021,-8032,-8043,-8054,-8065, - -8076,-8087,-8098,-8108,-8119,-8130,-8141,-8152,-8163,-8174,-8185,-8196,-8207,-8217,-8228,-8239, - -8250,-8261,-8272,-8283,-8293,-8304,-8315,-8326,-8337,-8347,-8358,-8369,-8380,-8391,-8401,-8412, - -8423,-8434,-8445,-8455,-8466,-8477,-8488,-8498,-8509,-8520,-8531,-8541,-8552,-8563,-8573,-8584, - -8595,-8606,-8616,-8627,-8638,-8648,-8659,-8670,-8680,-8691,-8702,-8712,-8723,-8734,-8744,-8755, - -8765,-8776,-8787,-8797,-8808,-8818,-8829,-8840,-8850,-8861,-8871,-8882,-8892,-8903,-8914,-8924, - -8935,-8945,-8956,-8966,-8977,-8987,-8998,-9008,-9019,-9029,-9040,-9050,-9061,-9071,-9082,-9092, - -9102,-9113,-9123,-9134,-9144,-9155,-9165,-9175,-9186,-9196,-9207,-9217,-9227,-9238,-9248,-9259, - -9269,-9279,-9290,-9300,-9310,-9321,-9331,-9341,-9352,-9362,-9372,-9383,-9393,-9403,-9413,-9424, - -9434,-9444,-9455,-9465,-9475,-9485,-9496,-9506,-9516,-9526,-9537,-9547,-9557,-9567,-9577,-9588, - -9598,-9608,-9618,-9628,-9638,-9649,-9659,-9669,-9679,-9689,-9699,-9709,-9720,-9730,-9740,-9750, - -9760,-9770,-9780,-9790,-9800,-9810,-9820,-9830,-9841,-9851,-9861,-9871,-9881,-9891,-9901,-9911, - -9921,-9931,-9941,-9951,-9961,-9971,-9981,-9991,-10001,-10010,-10020,-10030,-10040,-10050,-10060,-10070, - -10080,-10090,-10100,-10110,-10120,-10129,-10139,-10149,-10159,-10169,-10179,-10189,-10198,-10208,-10218,-10228, - -10238,-10248,-10257,-10267,-10277,-10287,-10296,-10306,-10316,-10326,-10336,-10345,-10355,-10365,-10374,-10384, - -10394,-10404,-10413,-10423,-10433,-10442,-10452,-10462,-10471,-10481,-10491,-10500,-10510,-10520,-10529,-10539, - -10549,-10558,-10568,-10577,-10587,-10597,-10606,-10616,-10625,-10635,-10644,-10654,-10663,-10673,-10683,-10692, - -10702,-10711,-10721,-10730,-10740,-10749,-10759,-10768,-10778,-10787,-10796,-10806,-10815,-10825,-10834,-10844, - -10853,-10862,-10872,-10881,-10891,-10900,-10909,-10919,-10928,-10937,-10947,-10956,-10966,-10975,-10984,-10994, - -11003,-11012,-11021,-11031,-11040,-11049,-11059,-11068,-11077,-11086,-11096,-11105,-11114,-11123,-11133,-11142, - -11151,-11160,-11169,-11179,-11188,-11197,-11206,-11215,-11224,-11234,-11243,-11252,-11261,-11270,-11279,-11288, - -11297,-11307,-11316,-11325,-11334,-11343,-11352,-11361,-11370,-11379,-11388,-11397,-11406,-11415,-11424,-11433, - -11442,-11451,-11460,-11469,-11478,-11487,-11496,-11505,-11514,-11523,-11532,-11541,-11550,-11559,-11567,-11576, - -11585,-11594,-11603,-11612,-11621,-11630,-11638,-11647,-11656,-11665,-11674,-11683,-11691,-11700,-11709,-11718, - -11727,-11735,-11744,-11753,-11762,-11770,-11779,-11788,-11797,-11805,-11814,-11823,-11831,-11840,-11849,-11857, - -11866,-11875,-11883,-11892,-11901,-11909,-11918,-11927,-11935,-11944,-11952,-11961,-11970,-11978,-11987,-11995, - -12004,-12012,-12021,-12029,-12038,-12046,-12055,-12064,-12072,-12080,-12089,-12097,-12106,-12114,-12123,-12131, - -12140,-12148,-12157,-12165,-12173,-12182,-12190,-12199,-12207,-12215,-12224,-12232,-12240,-12249,-12257,-12266, - -12274,-12282,-12290,-12299,-12307,-12315,-12324,-12332,-12340,-12348,-12357,-12365,-12373,-12381,-12390,-12398, - -12406,-12414,-12423,-12431,-12439,-12447,-12455,-12463,-12472,-12480,-12488,-12496,-12504,-12512,-12520,-12528, - -12537,-12545,-12553,-12561,-12569,-12577,-12585,-12593,-12601,-12609,-12617,-12625,-12633,-12641,-12649,-12657, - -12665,-12673,-12681,-12689,-12697,-12705,-12713,-12721,-12729,-12736,-12744,-12752,-12760,-12768,-12776,-12784, - -12792,-12799,-12807,-12815,-12823,-12831,-12839,-12846,-12854,-12862,-12870,-12878,-12885,-12893,-12901,-12909, - -12916,-12924,-12932,-12939,-12947,-12955,-12963,-12970,-12978,-12986,-12993,-13001,-13008,-13016,-13024,-13031, - -13039,-13047,-13054,-13062,-13069,-13077,-13085,-13092,-13100,-13107,-13115,-13122,-13130,-13137,-13145,-13152, - -13160,-13167,-13175,-13182,-13190,-13197,-13205,-13212,-13219,-13227,-13234,-13242,-13249,-13256,-13264,-13271, - -13279,-13286,-13293,-13301,-13308,-13315,-13323,-13330,-13337,-13344,-13352,-13359,-13366,-13374,-13381,-13388, - -13395,-13403,-13410,-13417,-13424,-13431,-13439,-13446,-13453,-13460,-13467,-13474,-13482,-13489,-13496,-13503, - -13510,-13517,-13524,-13531,-13538,-13546,-13553,-13560,-13567,-13574,-13581,-13588,-13595,-13602,-13609,-13616, - -13623,-13630,-13637,-13644,-13651,-13658,-13665,-13671,-13678,-13685,-13692,-13699,-13706,-13713,-13720,-13727, - -13733,-13740,-13747,-13754,-13761,-13768,-13774,-13781,-13788,-13795,-13802,-13808,-13815,-13822,-13829,-13835, - -13842,-13849,-13856,-13862,-13869,-13876,-13882,-13889,-13896,-13902,-13909,-13916,-13922,-13929,-13935,-13942, - -13949,-13955,-13962,-13968,-13975,-13981,-13988,-13995,-14001,-14008,-14014,-14021,-14027,-14034,-14040,-14047, - -14053,-14059,-14066,-14072,-14079,-14085,-14092,-14098,-14104,-14111,-14117,-14124,-14130,-14136,-14143,-14149, - -14155,-14162,-14168,-14174,-14181,-14187,-14193,-14199,-14206,-14212,-14218,-14224,-14231,-14237,-14243,-14249, - -14256,-14262,-14268,-14274,-14280,-14286,-14293,-14299,-14305,-14311,-14317,-14323,-14329,-14335,-14341,-14347, - -14354,-14360,-14366,-14372,-14378,-14384,-14390,-14396,-14402,-14408,-14414,-14420,-14426,-14432,-14438,-14443, - -14449,-14455,-14461,-14467,-14473,-14479,-14485,-14491,-14497,-14502,-14508,-14514,-14520,-14526,-14531,-14537, - -14543,-14549,-14555,-14560,-14566,-14572,-14578,-14583,-14589,-14595,-14601,-14606,-14612,-14618,-14623,-14629, - -14635,-14640,-14646,-14651,-14657,-14663,-14668,-14674,-14680,-14685,-14691,-14696,-14702,-14707,-14713,-14718, - -14724,-14729,-14735,-14740,-14746,-14751,-14757,-14762,-14768,-14773,-14779,-14784,-14789,-14795,-14800,-14806, - -14811,-14816,-14822,-14827,-14832,-14838,-14843,-14848,-14854,-14859,-14864,-14870,-14875,-14880,-14885,-14891, - -14896,-14901,-14906,-14911,-14917,-14922,-14927,-14932,-14937,-14943,-14948,-14953,-14958,-14963,-14968,-14973, - -14978,-14984,-14989,-14994,-14999,-15004,-15009,-15014,-15019,-15024,-15029,-15034,-15039,-15044,-15049,-15054, - -15059,-15064,-15069,-15074,-15078,-15083,-15088,-15093,-15098,-15103,-15108,-15113,-15118,-15122,-15127,-15132, - -15137,-15142,-15146,-15151,-15156,-15161,-15166,-15170,-15175,-15180,-15184,-15189,-15194,-15199,-15203,-15208, - -15213,-15217,-15222,-15227,-15231,-15236,-15240,-15245,-15250,-15254,-15259,-15263,-15268,-15273,-15277,-15282, - -15286,-15291,-15295,-15300,-15304,-15309,-15313,-15318,-15322,-15326,-15331,-15335,-15340,-15344,-15349,-15353, - -15357,-15362,-15366,-15370,-15375,-15379,-15383,-15388,-15392,-15396,-15401,-15405,-15409,-15414,-15418,-15422, - -15426,-15430,-15435,-15439,-15443,-15447,-15451,-15456,-15460,-15464,-15468,-15472,-15476,-15481,-15485,-15489, - -15493,-15497,-15501,-15505,-15509,-15513,-15517,-15521,-15525,-15529,-15533,-15537,-15541,-15545,-15549,-15553, - -15557,-15561,-15565,-15569,-15573,-15577,-15581,-15584,-15588,-15592,-15596,-15600,-15604,-15608,-15611,-15615, - -15619,-15623,-15627,-15630,-15634,-15638,-15642,-15645,-15649,-15653,-15656,-15660,-15664,-15668,-15671,-15675, - -15679,-15682,-15686,-15689,-15693,-15697,-15700,-15704,-15707,-15711,-15715,-15718,-15722,-15725,-15729,-15732, - -15736,-15739,-15743,-15746,-15750,-15753,-15757,-15760,-15763,-15767,-15770,-15774,-15777,-15780,-15784,-15787, - -15791,-15794,-15797,-15801,-15804,-15807,-15810,-15814,-15817,-15820,-15824,-15827,-15830,-15833,-15837,-15840, - -15843,-15846,-15849,-15853,-15856,-15859,-15862,-15865,-15868,-15871,-15875,-15878,-15881,-15884,-15887,-15890, - -15893,-15896,-15899,-15902,-15905,-15908,-15911,-15914,-15917,-15920,-15923,-15926,-15929,-15932,-15935,-15938, - -15941,-15944,-15946,-15949,-15952,-15955,-15958,-15961,-15964,-15966,-15969,-15972,-15975,-15978,-15980,-15983, - -15986,-15989,-15991,-15994,-15997,-16000,-16002,-16005,-16008,-16010,-16013,-16016,-16018,-16021,-16024,-16026, - -16029,-16031,-16034,-16037,-16039,-16042,-16044,-16047,-16049,-16052,-16054,-16057,-16059,-16062,-16064,-16067, - -16069,-16072,-16074,-16076,-16079,-16081,-16084,-16086,-16088,-16091,-16093,-16096,-16098,-16100,-16103,-16105, - -16107,-16109,-16112,-16114,-16116,-16119,-16121,-16123,-16125,-16128,-16130,-16132,-16134,-16136,-16138,-16141, - -16143,-16145,-16147,-16149,-16151,-16153,-16156,-16158,-16160,-16162,-16164,-16166,-16168,-16170,-16172,-16174, - -16176,-16178,-16180,-16182,-16184,-16186,-16188,-16190,-16192,-16194,-16195,-16197,-16199,-16201,-16203,-16205, - -16207,-16209,-16210,-16212,-16214,-16216,-16218,-16219,-16221,-16223,-16225,-16226,-16228,-16230,-16232,-16233, - -16235,-16237,-16238,-16240,-16242,-16243,-16245,-16247,-16248,-16250,-16251,-16253,-16255,-16256,-16258,-16259, - -16261,-16262,-16264,-16265,-16267,-16268,-16270,-16271,-16273,-16274,-16276,-16277,-16279,-16280,-16281,-16283, - -16284,-16286,-16287,-16288,-16290,-16291,-16292,-16294,-16295,-16296,-16298,-16299,-16300,-16301,-16303,-16304, - -16305,-16306,-16308,-16309,-16310,-16311,-16312,-16313,-16315,-16316,-16317,-16318,-16319,-16320,-16321,-16323, - -16324,-16325,-16326,-16327,-16328,-16329,-16330,-16331,-16332,-16333,-16334,-16335,-16336,-16337,-16338,-16339, - -16340,-16341,-16341,-16342,-16343,-16344,-16345,-16346,-16347,-16348,-16348,-16349,-16350,-16351,-16352,-16352, - -16353,-16354,-16355,-16355,-16356,-16357,-16358,-16358,-16359,-16360,-16360,-16361,-16362,-16362,-16363,-16364, - -16364,-16365,-16365,-16366,-16367,-16367,-16368,-16368,-16369,-16369,-16370,-16370,-16371,-16371,-16372,-16372, - -16373,-16373,-16374,-16374,-16375,-16375,-16375,-16376,-16376,-16377,-16377,-16377,-16378,-16378,-16378,-16379, - -16379,-16379,-16380,-16380,-16380,-16380,-16381,-16381,-16381,-16381,-16382,-16382,-16382,-16382,-16382,-16383, - -16383,-16383,-16383,-16383,-16383,-16383,-16384,-16384,-16384,-16384,-16384,-16384,-16384,-16384,-16384,-16384, - -16384,-16384,0,5,10,15,20,25,31,36,41,46,51,56,61,66, - 71,76,81,87,92,97,102,107,112,117,122,127,132,138,143,148, - 153,158,163,168,173,178,183,188,194,199,204,209,214,219,224,229, - 234,239,244,250,255,260,265,270,275,280,285,290,295,300,305,311, - 316,321,326,331,336,341,346,351,356,361,367,372,377,382,387,392, - 397,402,407,412,417,422,428,433,438,443,448,453,458,463,468,473, - 478,483,489,494,499,504,509,514, -}; - -const short gp2cc_ATAN1[2050] = { - 0,5,10,15,20,25,31,36,41,46,51,56,61,66,71,76, - 81,87,92,97,102,107,112,117,122,127,132,138,143,148,153,158, - 163,168,173,178,183,188,194,199,204,209,214,219,224,229,234,239, - 244,250,255,260,265,270,275,280,285,290,295,300,305,311,316,321, - 326,331,336,341,346,351,356,361,367,372,377,382,387,392,397,402, - 407,412,417,422,428,433,438,443,448,453,458,463,468,473,478,483, - 489,494,499,504,509,514,519,524,529,534,539,544,550,555,560,565, - 570,575,580,585,590,595,600,605,610,616,621,626,631,636,641,646, - 651,656,661,666,671,676,681,687,692,697,702,707,712,717,722,727, - 732,737,742,747,752,758,763,768,773,778,783,788,793,798,803,808, - 813,818,823,828,833,839,844,849,854,859,864,869,874,879,884,889, - 894,899,904,909,914,919,924,930,935,940,945,950,955,960,965,970, - 975,980,985,990,995,1000,1005,1010,1015,1020,1025,1031,1036,1041,1046,1051, - 1056,1061,1066,1071,1076,1081,1086,1091,1096,1101,1106,1111,1116,1121,1126,1131, - 1136,1141,1146,1151,1156,1161,1166,1172,1177,1182,1187,1192,1197,1202,1207,1212, - 1217,1222,1227,1232,1237,1242,1247,1252,1257,1262,1267,1272,1277,1282,1287,1292, - 1297,1302,1307,1312,1317,1322,1327,1332,1337,1342,1347,1352,1357,1362,1367,1372, - 1377,1382,1387,1392,1397,1402,1407,1412,1417,1422,1427,1432,1437,1442,1447,1452, - 1457,1462,1467,1472,1477,1482,1487,1492,1497,1502,1507,1512,1517,1522,1527,1532, - 1537,1542,1547,1552,1557,1562,1567,1572,1577,1582,1587,1592,1597,1602,1607,1612, - 1617,1622,1627,1632,1637,1642,1646,1651,1656,1661,1666,1671,1676,1681,1686,1691, - 1696,1701,1706,1711,1716,1721,1726,1731,1736,1741,1746,1751,1756,1761,1765,1770, - 1775,1780,1785,1790,1795,1800,1805,1810,1815,1820,1825,1830,1835,1840,1845,1849, - 1854,1859,1864,1869,1874,1879,1884,1889,1894,1899,1904,1909,1914,1918,1923,1928, - 1933,1938,1943,1948,1953,1958,1963,1968,1973,1977,1982,1987,1992,1997,2002,2007, - 2012,2017,2022,2027,2031,2036,2041,2046,2051,2056,2061,2066,2071,2076,2080,2085, - 2090,2095,2100,2105,2110,2115,2120,2124,2129,2134,2139,2144,2149,2154,2159,2163, - 2168,2173,2178,2183,2188,2193,2198,2202,2207,2212,2217,2222,2227,2232,2237,2241, - 2246,2251,2256,2261,2266,2271,2275,2280,2285,2290,2295,2300,2305,2309,2314,2319, - 2324,2329,2334,2338,2343,2348,2353,2358,2363,2367,2372,2377,2382,2387,2392,2396, - 2401,2406,2411,2416,2421,2425,2430,2435,2440,2445,2450,2454,2459,2464,2469,2474, - 2478,2483,2488,2493,2498,2502,2507,2512,2517,2522,2526,2531,2536,2541,2546,2550, - 2555,2560,2565,2570,2574,2579,2584,2589,2594,2598,2603,2608,2613,2617,2622,2627, - 2632,2637,2641,2646,2651,2656,2660,2665,2670,2675,2679,2684,2689,2694,2699,2703, - 2708,2713,2718,2722,2727,2732,2737,2741,2746,2751,2756,2760,2765,2770,2775,2779, - 2784,2789,2793,2798,2803,2808,2812,2817,2822,2827,2831,2836,2841,2846,2850,2855, - 2860,2864,2869,2874,2879,2883,2888,2893,2897,2902,2907,2912,2916,2921,2926,2930, - 2935,2940,2944,2949,2954,2959,2963,2968,2973,2977,2982,2987,2991,2996,3001,3005, - 3010,3015,3019,3024,3029,3033,3038,3043,3047,3052,3057,3061,3066,3071,3075,3080, - 3085,3089,3094,3099,3103,3108,3113,3117,3122,3127,3131,3136,3141,3145,3150,3155, - 3159,3164,3168,3173,3178,3182,3187,3192,3196,3201,3206,3210,3215,3219,3224,3229, - 3233,3238,3243,3247,3252,3256,3261,3266,3270,3275,3279,3284,3289,3293,3298,3302, - 3307,3312,3316,3321,3325,3330,3335,3339,3344,3348,3353,3358,3362,3367,3371,3376, - 3380,3385,3390,3394,3399,3403,3408,3412,3417,3422,3426,3431,3435,3440,3444,3449, - 3453,3458,3463,3467,3472,3476,3481,3485,3490,3494,3499,3503,3508,3513,3517,3522, - 3526,3531,3535,3540,3544,3549,3553,3558,3562,3567,3571,3576,3580,3585,3589,3594, - 3599,3603,3608,3612,3617,3621,3626,3630,3635,3639,3644,3648,3653,3657,3662,3666, - 3670,3675,3679,3684,3688,3693,3697,3702,3706,3711,3715,3720,3724,3729,3733,3738, - 3742,3747,3751,3756,3760,3764,3769,3773,3778,3782,3787,3791,3796,3800,3804,3809, - 3813,3818,3822,3827,3831,3836,3840,3844,3849,3853,3858,3862,3867,3871,3875,3880, - 3884,3889,3893,3898,3902,3906,3911,3915,3920,3924,3928,3933,3937,3942,3946,3950, - 3955,3959,3964,3968,3972,3977,3981,3985,3990,3994,3999,4003,4007,4012,4016,4021, - 4025,4029,4034,4038,4042,4047,4051,4055,4060,4064,4069,4073,4077,4082,4086,4090, - 4095,4099,4103,4108,4112,4116,4121,4125,4129,4134,4138,4142,4147,4151,4155,4160, - 4164,4168,4173,4177,4181,4186,4190,4194,4199,4203,4207,4211,4216,4220,4224,4229, - 4233,4237,4242,4246,4250,4254,4259,4263,4267,4272,4276,4280,4284,4289,4293,4297, - 4302,4306,4310,4314,4319,4323,4327,4331,4336,4340,4344,4349,4353,4357,4361,4366, - 4370,4374,4378,4383,4387,4391,4395,4400,4404,4408,4412,4416,4421,4425,4429,4433, - 4438,4442,4446,4450,4454,4459,4463,4467,4471,4476,4480,4484,4488,4492,4497,4501, - 4505,4509,4513,4518,4522,4526,4530,4534,4539,4543,4547,4551,4555,4559,4564,4568, - 4572,4576,4580,4585,4589,4593,4597,4601,4605,4610,4614,4618,4622,4626,4630,4634, - 4639,4643,4647,4651,4655,4659,4663,4668,4672,4676,4680,4684,4688,4692,4697,4701, - 4705,4709,4713,4717,4721,4725,4730,4734,4738,4742,4746,4750,4754,4758,4762,4767, - 4771,4775,4779,4783,4787,4791,4795,4799,4803,4807,4812,4816,4820,4824,4828,4832, - 4836,4840,4844,4848,4852,4856,4860,4865,4869,4873,4877,4881,4885,4889,4893,4897, - 4901,4905,4909,4913,4917,4921,4925,4929,4933,4937,4941,4945,4949,4954,4958,4962, - 4966,4970,4974,4978,4982,4986,4990,4994,4998,5002,5006,5010,5014,5018,5022,5026, - 5030,5034,5038,5042,5046,5050,5054,5058,5062,5066,5070,5074,5078,5082,5086,5090, - 5094,5097,5101,5105,5109,5113,5117,5121,5125,5129,5133,5137,5141,5145,5149,5153, - 5157,5161,5165,5169,5173,5177,5181,5184,5188,5192,5196,5200,5204,5208,5212,5216, - 5220,5224,5228,5232,5235,5239,5243,5247,5251,5255,5259,5263,5267,5271,5275,5278, - 5282,5286,5290,5294,5298,5302,5306,5310,5313,5317,5321,5325,5329,5333,5337,5341, - 5344,5348,5352,5356,5360,5364,5368,5371,5375,5379,5383,5387,5391,5395,5398,5402, - 5406,5410,5414,5418,5421,5425,5429,5433,5437,5441,5444,5448,5452,5456,5460,5464, - 5467,5471,5475,5479,5483,5486,5490,5494,5498,5502,5505,5509,5513,5517,5521,5524, - 5528,5532,5536,5540,5543,5547,5551,5555,5559,5562,5566,5570,5574,5577,5581,5585, - 5589,5592,5596,5600,5604,5608,5611,5615,5619,5623,5626,5630,5634,5638,5641,5645, - 5649,5652,5656,5660,5664,5667,5671,5675,5679,5682,5686,5690,5694,5697,5701,5705, - 5708,5712,5716,5720,5723,5727,5731,5734,5738,5742,5745,5749,5753,5757,5760,5764, - 5768,5771,5775,5779,5782,5786,5790,5793,5797,5801,5804,5808,5812,5815,5819,5823, - 5826,5830,5834,5837,5841,5845,5848,5852,5856,5859,5863,5867,5870,5874,5878,5881, - 5885,5888,5892,5896,5899,5903,5907,5910,5914,5917,5921,5925,5928,5932,5936,5939, - 5943,5946,5950,5954,5957,5961,5964,5968,5972,5975,5979,5982,5986,5990,5993,5997, - 6000,6004,6008,6011,6015,6018,6022,6025,6029,6033,6036,6040,6043,6047,6050,6054, - 6058,6061,6065,6068,6072,6075,6079,6082,6086,6089,6093,6097,6100,6104,6107,6111, - 6114,6118,6121,6125,6128,6132,6135,6139,6142,6146,6150,6153,6157,6160,6164,6167, - 6171,6174,6178,6181,6185,6188,6192,6195,6199,6202,6206,6209,6213,6216,6220,6223, - 6227,6230,6234,6237,6240,6244,6247,6251,6254,6258,6261,6265,6268,6272,6275,6279, - 6282,6286,6289,6292,6296,6299,6303,6306,6310,6313,6317,6320,6323,6327,6330,6334, - 6337,6341,6344,6348,6351,6354,6358,6361,6365,6368,6371,6375,6378,6382,6385,6389, - 6392,6395,6399,6402,6406,6409,6412,6416,6419,6423,6426,6429,6433,6436,6440,6443, - 6446,6450,6453,6456,6460,6463,6467,6470,6473,6477,6480,6483,6487,6490,6493,6497, - 6500,6504,6507,6510,6514,6517,6520,6524,6527,6530,6534,6537,6540,6544,6547,6550, - 6554,6557,6560,6564,6567,6570,6574,6577,6580,6584,6587,6590,6594,6597,6600,6604, - 6607,6610,6613,6617,6620,6623,6627,6630,6633,6637,6640,6643,6646,6650,6653,6656, - 6660,6663,6666,6669,6673,6676,6679,6683,6686,6689,6692,6696,6699,6702,6705,6709, - 6712,6715,6718,6722,6725,6728,6731,6735,6738,6741,6744,6748,6751,6754,6757,6761, - 6764,6767,6770,6774,6777,6780,6783,6787,6790,6793,6796,6799,6803,6806,6809,6812, - 6815,6819,6822,6825,6828,6832,6835,6838,6841,6844,6848,6851,6854,6857,6860,6863, - 6867,6870,6873,6876,6879,6883,6886,6889,6892,6895,6898,6902,6905,6908,6911,6914, - 6917,6921,6924,6927,6930,6933,6936,6940,6943,6946,6949,6952,6955,6958,6962,6965, - 6968,6971,6974,6977,6980,6984,6987,6990,6993,6996,6999,7002,7005,7009,7012,7015, - 7018,7021,7024,7027,7030,7033,7037,7040,7043,7046,7049,7052,7055,7058,7061,7064, - 7068,7071,7074,7077,7080,7083,7086,7089,7092,7095,7098,7101,7105,7108,7111,7114, - 7117,7120,7123,7126,7129,7132,7135,7138,7141,7144,7147,7150,7154,7157,7160,7163, - 7166,7169,7172,7175,7178,7181,7184,7187,7190,7193,7196,7199,7202,7205,7208,7211, - 7214,7217,7220,7223,7226,7229,7232,7235,7238,7241,7244,7247,7250,7253,7256,7259, - 7262,7265,7268,7271,7274,7277,7280,7283,7286,7289,7292,7295,7298,7301,7304,7307, - 7310,7313,7316,7319,7322,7325,7328,7331,7334,7337,7340,7343,7346,7349,7352,7355, - 7358,7361,7363,7366,7369,7372,7375,7378,7381,7384,7387,7390,7393,7396,7399,7402, - 7405,7408,7411,7413,7416,7419,7422,7425,7428,7431,7434,7437,7440,7443,7446,7448, - 7451,7454,7457,7460,7463,7466,7469,7472,7475,7477,7480,7483,7486,7489,7492,7495, - 7498,7501,7503,7506,7509,7512,7515,7518,7521,7524,7526,7529,7532,7535,7538,7541, - 7544,7547,7549,7552,7555,7558,7561,7564,7566,7569,7572,7575,7578,7581,7584,7586, - 7589,7592,7595,7598,7601,7603,7606,7609,7612,7615,7618,7620,7623,7626,7629,7632, - 7635,7637,7640,7643,7646,7649,7651,7654,7657,7660,7663,7665,7668,7671,7674,7677, - 7679,7682,7685,7688,7691,7693,7696,7699,7702,7705,7707,7710,7713,7716,7718,7721, - 7724,7727,7730,7732,7735,7738,7741,7743,7746,7749,7752,7754,7757,7760,7763,7765, - 7768,7771,7774,7776,7779,7782,7785,7787,7790,7793,7796,7798,7801,7804,7807,7809, - 7812,7815,7818,7820,7823,7826,7828,7831,7834,7837,7839,7842,7845,7848,7850,7853, - 7856,7858,7861,7864,7866,7869,7872,7875,7877,7880,7883,7885,7888,7891,7893,7896, - 7899,7902,7904,7907,7910,7912,7915,7918,7920,7923,7926,7928,7931,7934,7936,7939, - 7942,7944,7947,7950,7952,7955,7958,7960,7963,7966,7968,7971,7974,7976,7979,7982, - 7984,7987,7990,7992,7995,7997,8000,8003,8005,8008,8011,8013,8016,8019,8021,8024, - 8026,8029,8032,8034,8037,8040,8042,8045,8047,8050,8053,8055,8058,8060,8063,8066, - 8068,8071,8074,8076,8079,8081,8084,8087,8089,8092,8094,8097,8100,8102,8105,8107, - 8110,8112,8115,8118,8120,8123,8125,8128,8131,8133,8136,8138,8141,8143,8146,8149, - 8151,8154,8156,8159,8161,8164,8166,8169,8172,8174,8177,8179,8182,8184,8187,8189, - 8192,0, -}; - diff --git a/gp2ccline.hpp b/gp2ccline.hpp index 1d37688..c40b512 100644 --- a/gp2ccline.hpp +++ b/gp2ccline.hpp @@ -10,11 +10,11 @@ // diverges on any rounding difference. It is therefore a FAITHFUL transcription // of the engine's fixed-point math (UACalcBestLine 0x78EB2 + helpers), not a // re-derivation — kept legible with comments, but the integers are load-bearing -// and must not be "cleaned up". Validated bit-exact vs the original C kernel. +// and must not be "cleaned up". Validated bit-exact against the GP2Lap dumps. // --------------------------------------------------------------------------- namespace gp2geom { -// Drop-in for gp2cc_compute_ccline. Writes per-segment bestLine (tseg+0x16) and +// Run the cc-line pass. Writes per-segment bestLine (tseg+0x16) and // angle18 (tseg+0x18); cmdStartSeg/pNumCmds (may be null) record each cc-command's // first segment. Returns 0 on success. int computeCcLine(const gp2cc_ccgeo& g, const std::uint8_t* cc, int cc_len, int cc_off, diff --git a/gp2geom.cpp b/gp2geom.cpp index 2f896e7..6772356 100644 --- a/gp2geom.cpp +++ b/gp2geom.cpp @@ -1,15 +1,8 @@ #include "gp2geom.hpp" -// Cosine source. Default: the build-time-verified formula table (gp2cos.hpp), -// generated at compile time and static_assert'd against GP2's bytes. If a -// toolchain struggles with the constexpr generation, define GP2GEOM_EMBEDDED_COS -// to use the existing extracted table instead — the two are bit-identical -// (verified on all 65536 angles), so the result is unchanged either way. -#ifndef GP2GEOM_EMBEDDED_COS -# include "gp2cos.hpp" -#else -extern "C" const short gp2cc_COS[4200]; -#endif +// Cosine source: the build-time-verified formula table (gp2cos.hpp), generated +// at compile time and static_assert'd against GP2's bytes. +#include "gp2cos.hpp" namespace gp2geom { namespace { @@ -41,12 +34,7 @@ constexpr int wrap16(int v) { v &= 0xFFFF; return (v & 0x8000) ? v - 0x10000 : v // raw, grid-sampled cosine (amplitude 0x4000) of a binary-radian angle. inline int rawCos(int a) { -#ifndef GP2GEOM_EMBEDDED_COS return scos(a); // formula table (gp2cos.hpp) -#else - a = wrap16(a); if (a < 0) a = -a; // existing extracted table - return gp2cc_COS[((a >> 2) & 0xFFFE) >> 1]; -#endif } inline int cosine(int angle) { return rawCos(angle); } inline int sine (int angle) { return rawCos(kQuarterTurn - angle); } // sin(x) = cos(90-x) @@ -258,4 +246,16 @@ int compileGeometry(const std::uint8_t* dat, int len, gp2cc_track& out) { return builder.run(); } +// GP2's GetSinusVal (0x104B9): fold the sign, sample the 8-unit cosine grid, and +// linearly interpolate over the low 3 bits. Bit-identical to the old C gp2cc_gv. +int gv(int angle) { + int ax = wrap16(angle); + if (ax < 0) ax = (-ax) & 0xFFFF; + int frac = ax & 7; + int widx = ((ax >> 2) & 0xFFFE) >> 1; // == ax >> 3 + int base = kCosine[widx]; + int d = wrap16(kCosine[widx + 1] - base); + return wrap16(base + ((d * frac) >> 3)); +} + } // namespace gp2geom diff --git a/gp2geom.hpp b/gp2geom.hpp index fb4bde5..3707341 100644 --- a/gp2geom.hpp +++ b/gp2geom.hpp @@ -7,10 +7,9 @@ // actually is (a fixed-point "turtle" that integrates a curve from its // curvature), rather than as a transcription of the disassembly. // -// Same bit-exact integers as the engine, same output struct as the original -// gp2cc compiler (so it is a drop-in for gp2cc_compile_geometry_buf), but the -// steps are named — turn, advance one chord, midpoint half-step, ramp width, -// close the loop — and every "magic" constant is derived in a comment. +// Same bit-exact integers as the engine, but the steps are named — turn, +// advance one chord, midpoint half-step, ramp width, close the loop — and every +// "magic" constant is derived in a comment. // // The cosine it uses is the build-time-verified table from gp2cos.hpp. // --------------------------------------------------------------------------- @@ -20,4 +19,10 @@ namespace gp2geom { // negative on a command-stream desync. Bit-exact with the game. int compileGeometry(const std::uint8_t* dat, int len, gp2cc_track& out); +// Interpolated cosine (amplitude 0x4000) of a binary-radian angle, matching +// GP2's GetSinusVal (0x104B9): an 8-unit grid lookup with a linear lerp over the +// low 3 bits. The editor uses this only to draw perpendiculars/ticks for the +// overlay; sin(x) is gv(0x4000 - x). +int gv(int angle); + } // namespace gp2geom From 40778e61bb53332009c1b53386366c89f32ed83a Mon Sep 17 00:00:00 2001 From: Roberto Remedio Date: Mon, 15 Jun 2026 12:00:01 -0300 Subject: [PATCH 10/12] Drop disassembly names from gp2ccline comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the GP2 sub_/address/field references in the cc-line solver's comments with plain descriptions of what each step does, rename the register-named 'ebp' local to 'step', and name the 0xCBB0C constant kInvPiQ16 (round(2^16/pi)) with a derivation. Pure comment/identifier changes — the fixed-point math is unchanged (still bit-exact). --- gp2ccline.cpp | 61 +++++++++++++++++++++++++++++++-------------------- gp2ccline.hpp | 14 ++++++------ 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/gp2ccline.cpp b/gp2ccline.cpp index 3b5269f..c2c831a 100644 --- a/gp2ccline.cpp +++ b/gp2ccline.cpp @@ -1,23 +1,27 @@ #include "gp2ccline.hpp" -#include "gp2cos.hpp" // gp2geom::kCosine — the verified cosine table (COS) -#include "gp2atan.hpp" // gp2geom::kArctan — the verified atan table (ATAN1) +#include "gp2cos.hpp" // gp2geom::kCosine — the verified cosine table +#include "gp2atan.hpp" // gp2geom::kArctan — the verified arctangent table #include namespace gp2geom { namespace { -// Table aliases so the transcription below reads like the original kernel. -inline int COS (int i) { return kCosine[i]; } // amplitude 0x4000 cosine table -inline int ATAN(int i) { return kArctan[i]; } // atan table, 0x10000/turn +// Short accessors for the two trig tables used throughout the math below. +inline int COS (int i) { return kCosine[i]; } // cosine, amplitude 0x4000 +inline int ATAN(int i) { return kArctan[i]; } // arctangent, 0x10000 per turn #define ONE60 (((int64_t)1) << 60) -constexpr int CBB0C = 20861; // data const @0xCBB0C: segment-frame theta' tilt + +// Converts a segment's theta-prime tilt back into a binary-radian heading +// offset: this is round(2^16 / pi), the inverse of the (pi/2)-scaled tilt that +// the geometry pass stores in f14. +constexpr int kInvPiQ16 = 20861; // round(65536 / pi) inline int s16(int v) { v &= 0xFFFF; return (v & 0x8000) ? v - 0x10000 : v; } inline int abs16(int v) { v = s16(v); return v < 0 ? -v : v; } inline int rdw(const std::uint8_t* d, int o) { return d[o] | (d[o + 1] << 8); } -// GetSinusVal (0x104B9): interpolated cosine, amplitude 0x4000. +// Interpolated cosine, amplitude 0x4000 (8-unit grid lookup + linear lerp). inline int gv(int ax) { ax = s16(ax); if (ax < 0) ax = (-ax) & 0xFFFF; @@ -28,7 +32,9 @@ inline int gv(int ax) { return s16(base + ((d * frac) >> 3)); } -// sub_10240: 32-bit integer sqrt, FIXED 3-iteration 16-bit Newton (not floor). +// 32-bit integer square root: a FIXED 3-iteration 16-bit Newton step. This is +// deliberately NOT a true floor-sqrt — the fixed iteration count reproduces the +// engine's exact (slightly-off) result, which the chaotic loop below depends on. std::uint32_t sqrt32(std::uint32_t V) { if (V == 0) return 0; int shift = 0; @@ -44,7 +50,8 @@ std::uint32_t sqrt32(std::uint32_t V) { return (di >> shift) & 0xFFFF; } -// sub_102F0: 64-bit integer sqrt, FIXED 5-iteration Newton. +// 64-bit integer square root: a FIXED 5-iteration Newton step (same caveat as +// sqrt32 — the iteration count is part of the result, not an approximation). std::uint32_t sqrt64(std::uint64_t V) { if (V == 0) return 0; if ((std::uint32_t)(V >> 32) == 0) return sqrt32((std::uint32_t)V); @@ -59,7 +66,7 @@ std::uint32_t sqrt64(std::uint64_t V) { return est >> shift; } -// sub_10502: cos(|angle|) in Q30 (amp 0x40000000) via the cos table + interp. +// cos(|angle|) in Q30 (amplitude 0x40000000) from the cosine table + interpolation. std::int32_t cos30(int angle) { int a = abs16(angle); int widx = a >> 3, frac = a & 7; @@ -68,7 +75,9 @@ std::int32_t cos30(int angle) { return ((std::int32_t)base << 16) + ((std::int32_t)(delta * frac) << 13); } -// sub_7827D: Q30 cos/sin; larger component via sqrt(ONE60-other^2), smaller via table. +// Q30 cosine and sine of an angle. To keep precision, the larger-magnitude of +// the two is recovered from the smaller via sqrt(1 - x^2) (computed in Q60), +// and the smaller-magnitude one is read straight from the table. void q30_cossin(int angle, std::int32_t* pcos, std::int32_t* psin) { int a = s16(angle); int coarse_sin = COS(abs16(s16(0x4000 - a)) >> 3); @@ -87,7 +96,7 @@ void q30_cossin(int angle, std::int32_t* pcos, std::int32_t* psin) { *pcos = c30; *psin = s30; } -// sub_10798: atan2(y,x) in 0x10000 units (full circle), 16-bit args. +// atan2(y, x) in binary radians (0x10000 = a full turn), 16-bit arguments. int atan2u(int y, int x) { y = s16(y); x = s16(x); int dy = y < 0 ? -y : y; @@ -107,7 +116,7 @@ int atan2u(int y, int x) { return s16(a); } -// sub_78492: build the world arc-centre W for the first segment of a command. +// Build the world arc-centre point W for the first segment of a command. void build_centre(const gp2cc_ccgeo* g, int idx, int P, int H, std::int32_t arg2, int arg1mul4, std::int32_t* pWx, std::int32_t* pWy) { int seg = idx; @@ -136,13 +145,14 @@ void build_centre(const gp2cc_ccgeo* g, int idx, int P, int H, *pWx = Wx; *pWy = Wy; } -// sub_787E7: reproject world point W onto seg idx -> P (and H on curves). +// Reproject the carried world point W onto segment `idx`, producing the lateral +// offset P (and, on curved commands, the updated heading H). void reproject(const gp2cc_ccgeo* g, int idx, std::int32_t Wx, std::int32_t Wy, std::int32_t arg2, int* pP, int* pH) { int seg = idx; int f14 = (int)g->f14[seg]; int ang = s16(g->segAngle[seg]); - int thetaP = s16(ang - (int)((((std::int32_t)(f14 >> 1) * CBB0C) << 1) >> 16)); + int thetaP = s16(ang - (int)((((std::int32_t)(f14 >> 1) * kInvPiQ16) << 1) >> 16)); std::int32_t relx = Wx - g->cx8[seg]; std::int32_t rely = Wy - g->cy8[seg]; std::int32_t c32, s32; q30_cossin(thetaP, &c32, &s32); @@ -174,19 +184,20 @@ void reproject(const gp2cc_ccgeo* g, int idx, std::int32_t Wx, std::int32_t Wy, *pH = s16(at + thetaP); } -// sub_78CEC: per-seg curvature-ramp nudge of W (slope!=0 commands). +// Per-segment nudge of the world point W along a curvature ramp (commands whose +// radius changes from segment to segment, i.e. a non-zero slope). void nudge(std::int32_t* pWx, std::int32_t* pWy, int H, std::int32_t arg2, std::int32_t slope) { - std::int32_t ebp = slope; + std::int32_t step = slope; int a; - if (arg2 < 0) { ebp = -ebp; a = s16(H - 0x4000); } + if (arg2 < 0) { step = -step; a = s16(H - 0x4000); } else a = s16(H + 0x4000); int sinA = gv(s16(0x4000 - a)); int cosA = gv(s16(a)); - *pWx += (std::int32_t)(((std::int64_t)sinA * ebp) >> 14); - *pWy += (std::int32_t)(((std::int64_t)cosA * ebp) >> 14); + *pWx += (std::int32_t)(((std::int64_t)sinA * step) >> 14); + *pWy += (std::int32_t)(((std::int64_t)cosA * step) >> 14); } -// GetSingleCmdArgs (0x78D4C): parse one cc command. +// Parse one command from the cc-line command stream. struct cc_cmd { int end, word, N, a1, a2; std::int32_t arg2, slope; @@ -218,8 +229,10 @@ void parse_cmd(const std::uint8_t* cc, int* po, cc_cmd* c) { } // anonymous namespace -// UACalcBestLine (0x78EB2): walk the command stream; seed P/H; per segment store -// bestLine/angle18 then reproject the carried world point; nudge on ramps. +// Walk the cc-line command stream: seed the lateral offset P and heading H, then +// for each segment store its racing-line offset (bestLine) and heading delta +// (angle18) before reprojecting the carried world point onto the next segment, +// nudging it along on curvature ramps. int computeCcLine(const gp2cc_ccgeo& gref, const std::uint8_t* cc, int cc_len, int cc_off, std::int16_t* bestLine, std::int16_t* angle18, int* cmdStartSeg, int* pNumCmds) { @@ -230,7 +243,7 @@ int computeCcLine(const gp2cc_ccgeo& gref, const std::uint8_t* cc, int cc_len, i int H = s16(g->segAngle[0]); int P = 0; int w0 = rdw(cc, cc_off); - // seed P (segPosX_0A) is a SIGNED 16-bit word; sign-extend it. + // the initial lateral offset P is a SIGNED 16-bit word; sign-extend it. if (!(w0 & 0x800) && (w0 & 0x8000)) P = s16(rdw(cc, cc_off + 2)); int o = cc_off; for (;;) { diff --git a/gp2ccline.hpp b/gp2ccline.hpp index c40b512..30fe4ad 100644 --- a/gp2ccline.hpp +++ b/gp2ccline.hpp @@ -7,16 +7,16 @@ // formula-generated cosine/atan tables (gp2cos.hpp / gp2atan.hpp). // // This is the CHAOTIC kernel: a reproject/nudge feedback loop whose output -// diverges on any rounding difference. It is therefore a FAITHFUL transcription -// of the engine's fixed-point math (UACalcBestLine 0x78EB2 + helpers), not a -// re-derivation — kept legible with comments, but the integers are load-bearing -// and must not be "cleaned up". Validated bit-exact against the GP2Lap dumps. +// diverges on any rounding difference. It therefore reproduces the engine's +// fixed-point math exactly, rather than re-deriving it — kept legible with +// comments and meaningful names, but every integer operation is load-bearing +// and must not be "simplified". Validated bit-exact against the GP2Lap dumps. // --------------------------------------------------------------------------- namespace gp2geom { -// Run the cc-line pass. Writes per-segment bestLine (tseg+0x16) and -// angle18 (tseg+0x18); cmdStartSeg/pNumCmds (may be null) record each cc-command's -// first segment. Returns 0 on success. +// Run the cc-line pass. Writes the per-segment racing-line offset (bestLine) and +// heading delta (angle18); cmdStartSeg/pNumCmds (may be null) record each +// cc-command's first segment. Returns 0 on success. int computeCcLine(const gp2cc_ccgeo& g, const std::uint8_t* cc, int cc_len, int cc_off, std::int16_t* bestLine, std::int16_t* angle18, int* cmdStartSeg, int* pNumCmds); From b17df67e0c8a8ab6fc0169f265df00c47e68bd78 Mon Sep 17 00:00:00 2001 From: Roberto Remedio Date: Mon, 15 Jun 2026 12:12:00 -0300 Subject: [PATCH 11/12] Drop the embedded GP2 reference tables and full-table guards The cosine/atan tables are generated from their formulas at compile time; gp2cos_reference.hpp and gp2atan_reference.hpp existed only to hold GP2's extracted bytes for a static_assert that proved the generated tables matched. Remove both headers and the matchesReference/atanMatchesReference guards (and their .vcxproj entries) so no extracted GP2 data ships in the editor. In their place, each table header now warns that the entries come from compile-time float rounding: a different compiler/std-lib could round a .5-boundary entry the other way, and a drifting compiled track or cc-line is the symptom to watch for. The single load-bearing boundary (cos index 1992) is still pinned by a one-line spot-check static_assert. Verified the generated tables still match GP2's bytes bit-for-bit on the current toolchain. --- TrackEditor.vcxproj | 2 - gp2atan.hpp | 24 ++-- gp2atan_reference.hpp | 135 ---------------------- gp2cos.hpp | 32 +++-- gp2cos_reference.hpp | 263 ------------------------------------------ 5 files changed, 23 insertions(+), 433 deletions(-) delete mode 100644 gp2atan_reference.hpp delete mode 100644 gp2cos_reference.hpp diff --git a/TrackEditor.vcxproj b/TrackEditor.vcxproj index 932df8c..8ae3b2e 100755 --- a/TrackEditor.vcxproj +++ b/TrackEditor.vcxproj @@ -295,10 +295,8 @@ - - diff --git a/gp2atan.hpp b/gp2atan.hpp index 11813f3..f381b82 100644 --- a/gp2atan.hpp +++ b/gp2atan.hpp @@ -1,7 +1,6 @@ #pragma once #include #include -#include "gp2atan_reference.hpp" // golden oracle: GP2's extracted t_ArithTab1 bytes // --------------------------------------------------------------------------- // GP2 geometry — the arctangent table expressed as its generating formula. @@ -17,9 +16,15 @@ // single entry sits within 1e-3 of a .5 boundary, and it rounds correctly. // // So this is the poster child for replacing an opaque blob with a formula: -// one line of intent, no override. We still keep the compile-time guardrail -// (static_assert against GP2's bytes) as free insurance against an exotic -// toolchain ever flipping that one boundary entry. +// one line of intent, no override. +// +// WARNING: the entries come from compile-time floating-point rounding, so a +// different compiler or standard library could round an entry that lands on a +// .5 boundary the other way. This table is far less fragile than the cosine one +// (only a single entry is anywhere near a boundary, and it rounds correctly), +// but if the compiled track or cc-line ever start to DRIFT, a rounding +// disagreement here — or in gp2cos.hpp — is the first thing to suspect: +// regenerate the table and diff it against GP2's t_ArithTab1 bytes. // --------------------------------------------------------------------------- namespace gp2geom { @@ -62,16 +67,7 @@ constexpr std::array makeAtanTable() { inline constexpr std::array kArctan = makeAtanTable(); -// ---- compile-time guardrail (no override needed for this table) ------------- -constexpr bool atanMatchesReference() { - for (int i = 0; i < kAtanGrid; ++i) - if (kArctan[i] != kAtanRef[i]) return false; - return true; -} -static_assert(atanMatchesReference(), - "gp2geom arctan table != GP2 reference bytes: a float rounding boundary " - "flipped on this toolchain. Inspect the diff before shipping."); - +// Cheap spot-checks of the two exact endpoints (no embedded reference table). static_assert(kArctan[0] == 0, "atan(0)"); static_assert(kArctan[2048] == 8192, "atan(1) = pi/4 = 0x2000"); diff --git a/gp2atan_reference.hpp b/gp2atan_reference.hpp deleted file mode 100644 index e36a07a..0000000 --- a/gp2atan_reference.hpp +++ /dev/null @@ -1,135 +0,0 @@ -#pragma once -#include -// Golden reference: GP2 t_ArithTab1 bytes (file 0x1DFAF0), 2050 entries -// (indices 0..2048 are real; 2049 is unused padding = 0). -namespace gp2geom { inline constexpr short kAtanRef[2050] = { - 0,5,10,15,20,25,31,36,41,46,51,56,61,66,71,76, - 81,87,92,97,102,107,112,117,122,127,132,138,143,148,153,158, - 163,168,173,178,183,188,194,199,204,209,214,219,224,229,234,239, - 244,250,255,260,265,270,275,280,285,290,295,300,305,311,316,321, - 326,331,336,341,346,351,356,361,367,372,377,382,387,392,397,402, - 407,412,417,422,428,433,438,443,448,453,458,463,468,473,478,483, - 489,494,499,504,509,514,519,524,529,534,539,544,550,555,560,565, - 570,575,580,585,590,595,600,605,610,616,621,626,631,636,641,646, - 651,656,661,666,671,676,681,687,692,697,702,707,712,717,722,727, - 732,737,742,747,752,758,763,768,773,778,783,788,793,798,803,808, - 813,818,823,828,833,839,844,849,854,859,864,869,874,879,884,889, - 894,899,904,909,914,919,924,930,935,940,945,950,955,960,965,970, - 975,980,985,990,995,1000,1005,1010,1015,1020,1025,1031,1036,1041,1046,1051, - 1056,1061,1066,1071,1076,1081,1086,1091,1096,1101,1106,1111,1116,1121,1126,1131, - 1136,1141,1146,1151,1156,1161,1166,1172,1177,1182,1187,1192,1197,1202,1207,1212, - 1217,1222,1227,1232,1237,1242,1247,1252,1257,1262,1267,1272,1277,1282,1287,1292, - 1297,1302,1307,1312,1317,1322,1327,1332,1337,1342,1347,1352,1357,1362,1367,1372, - 1377,1382,1387,1392,1397,1402,1407,1412,1417,1422,1427,1432,1437,1442,1447,1452, - 1457,1462,1467,1472,1477,1482,1487,1492,1497,1502,1507,1512,1517,1522,1527,1532, - 1537,1542,1547,1552,1557,1562,1567,1572,1577,1582,1587,1592,1597,1602,1607,1612, - 1617,1622,1627,1632,1637,1642,1646,1651,1656,1661,1666,1671,1676,1681,1686,1691, - 1696,1701,1706,1711,1716,1721,1726,1731,1736,1741,1746,1751,1756,1761,1765,1770, - 1775,1780,1785,1790,1795,1800,1805,1810,1815,1820,1825,1830,1835,1840,1845,1849, - 1854,1859,1864,1869,1874,1879,1884,1889,1894,1899,1904,1909,1914,1918,1923,1928, - 1933,1938,1943,1948,1953,1958,1963,1968,1973,1977,1982,1987,1992,1997,2002,2007, - 2012,2017,2022,2027,2031,2036,2041,2046,2051,2056,2061,2066,2071,2076,2080,2085, - 2090,2095,2100,2105,2110,2115,2120,2124,2129,2134,2139,2144,2149,2154,2159,2163, - 2168,2173,2178,2183,2188,2193,2198,2202,2207,2212,2217,2222,2227,2232,2237,2241, - 2246,2251,2256,2261,2266,2271,2275,2280,2285,2290,2295,2300,2305,2309,2314,2319, - 2324,2329,2334,2338,2343,2348,2353,2358,2363,2367,2372,2377,2382,2387,2392,2396, - 2401,2406,2411,2416,2421,2425,2430,2435,2440,2445,2450,2454,2459,2464,2469,2474, - 2478,2483,2488,2493,2498,2502,2507,2512,2517,2522,2526,2531,2536,2541,2546,2550, - 2555,2560,2565,2570,2574,2579,2584,2589,2594,2598,2603,2608,2613,2617,2622,2627, - 2632,2637,2641,2646,2651,2656,2660,2665,2670,2675,2679,2684,2689,2694,2699,2703, - 2708,2713,2718,2722,2727,2732,2737,2741,2746,2751,2756,2760,2765,2770,2775,2779, - 2784,2789,2793,2798,2803,2808,2812,2817,2822,2827,2831,2836,2841,2846,2850,2855, - 2860,2864,2869,2874,2879,2883,2888,2893,2897,2902,2907,2912,2916,2921,2926,2930, - 2935,2940,2944,2949,2954,2959,2963,2968,2973,2977,2982,2987,2991,2996,3001,3005, - 3010,3015,3019,3024,3029,3033,3038,3043,3047,3052,3057,3061,3066,3071,3075,3080, - 3085,3089,3094,3099,3103,3108,3113,3117,3122,3127,3131,3136,3141,3145,3150,3155, - 3159,3164,3168,3173,3178,3182,3187,3192,3196,3201,3206,3210,3215,3219,3224,3229, - 3233,3238,3243,3247,3252,3256,3261,3266,3270,3275,3279,3284,3289,3293,3298,3302, - 3307,3312,3316,3321,3325,3330,3335,3339,3344,3348,3353,3358,3362,3367,3371,3376, - 3380,3385,3390,3394,3399,3403,3408,3412,3417,3422,3426,3431,3435,3440,3444,3449, - 3453,3458,3463,3467,3472,3476,3481,3485,3490,3494,3499,3503,3508,3513,3517,3522, - 3526,3531,3535,3540,3544,3549,3553,3558,3562,3567,3571,3576,3580,3585,3589,3594, - 3599,3603,3608,3612,3617,3621,3626,3630,3635,3639,3644,3648,3653,3657,3662,3666, - 3670,3675,3679,3684,3688,3693,3697,3702,3706,3711,3715,3720,3724,3729,3733,3738, - 3742,3747,3751,3756,3760,3764,3769,3773,3778,3782,3787,3791,3796,3800,3804,3809, - 3813,3818,3822,3827,3831,3836,3840,3844,3849,3853,3858,3862,3867,3871,3875,3880, - 3884,3889,3893,3898,3902,3906,3911,3915,3920,3924,3928,3933,3937,3942,3946,3950, - 3955,3959,3964,3968,3972,3977,3981,3985,3990,3994,3999,4003,4007,4012,4016,4021, - 4025,4029,4034,4038,4042,4047,4051,4055,4060,4064,4069,4073,4077,4082,4086,4090, - 4095,4099,4103,4108,4112,4116,4121,4125,4129,4134,4138,4142,4147,4151,4155,4160, - 4164,4168,4173,4177,4181,4186,4190,4194,4199,4203,4207,4211,4216,4220,4224,4229, - 4233,4237,4242,4246,4250,4254,4259,4263,4267,4272,4276,4280,4284,4289,4293,4297, - 4302,4306,4310,4314,4319,4323,4327,4331,4336,4340,4344,4349,4353,4357,4361,4366, - 4370,4374,4378,4383,4387,4391,4395,4400,4404,4408,4412,4416,4421,4425,4429,4433, - 4438,4442,4446,4450,4454,4459,4463,4467,4471,4476,4480,4484,4488,4492,4497,4501, - 4505,4509,4513,4518,4522,4526,4530,4534,4539,4543,4547,4551,4555,4559,4564,4568, - 4572,4576,4580,4585,4589,4593,4597,4601,4605,4610,4614,4618,4622,4626,4630,4634, - 4639,4643,4647,4651,4655,4659,4663,4668,4672,4676,4680,4684,4688,4692,4697,4701, - 4705,4709,4713,4717,4721,4725,4730,4734,4738,4742,4746,4750,4754,4758,4762,4767, - 4771,4775,4779,4783,4787,4791,4795,4799,4803,4807,4812,4816,4820,4824,4828,4832, - 4836,4840,4844,4848,4852,4856,4860,4865,4869,4873,4877,4881,4885,4889,4893,4897, - 4901,4905,4909,4913,4917,4921,4925,4929,4933,4937,4941,4945,4949,4954,4958,4962, - 4966,4970,4974,4978,4982,4986,4990,4994,4998,5002,5006,5010,5014,5018,5022,5026, - 5030,5034,5038,5042,5046,5050,5054,5058,5062,5066,5070,5074,5078,5082,5086,5090, - 5094,5097,5101,5105,5109,5113,5117,5121,5125,5129,5133,5137,5141,5145,5149,5153, - 5157,5161,5165,5169,5173,5177,5181,5184,5188,5192,5196,5200,5204,5208,5212,5216, - 5220,5224,5228,5232,5235,5239,5243,5247,5251,5255,5259,5263,5267,5271,5275,5278, - 5282,5286,5290,5294,5298,5302,5306,5310,5313,5317,5321,5325,5329,5333,5337,5341, - 5344,5348,5352,5356,5360,5364,5368,5371,5375,5379,5383,5387,5391,5395,5398,5402, - 5406,5410,5414,5418,5421,5425,5429,5433,5437,5441,5444,5448,5452,5456,5460,5464, - 5467,5471,5475,5479,5483,5486,5490,5494,5498,5502,5505,5509,5513,5517,5521,5524, - 5528,5532,5536,5540,5543,5547,5551,5555,5559,5562,5566,5570,5574,5577,5581,5585, - 5589,5592,5596,5600,5604,5608,5611,5615,5619,5623,5626,5630,5634,5638,5641,5645, - 5649,5652,5656,5660,5664,5667,5671,5675,5679,5682,5686,5690,5694,5697,5701,5705, - 5708,5712,5716,5720,5723,5727,5731,5734,5738,5742,5745,5749,5753,5757,5760,5764, - 5768,5771,5775,5779,5782,5786,5790,5793,5797,5801,5804,5808,5812,5815,5819,5823, - 5826,5830,5834,5837,5841,5845,5848,5852,5856,5859,5863,5867,5870,5874,5878,5881, - 5885,5888,5892,5896,5899,5903,5907,5910,5914,5917,5921,5925,5928,5932,5936,5939, - 5943,5946,5950,5954,5957,5961,5964,5968,5972,5975,5979,5982,5986,5990,5993,5997, - 6000,6004,6008,6011,6015,6018,6022,6025,6029,6033,6036,6040,6043,6047,6050,6054, - 6058,6061,6065,6068,6072,6075,6079,6082,6086,6089,6093,6097,6100,6104,6107,6111, - 6114,6118,6121,6125,6128,6132,6135,6139,6142,6146,6150,6153,6157,6160,6164,6167, - 6171,6174,6178,6181,6185,6188,6192,6195,6199,6202,6206,6209,6213,6216,6220,6223, - 6227,6230,6234,6237,6240,6244,6247,6251,6254,6258,6261,6265,6268,6272,6275,6279, - 6282,6286,6289,6292,6296,6299,6303,6306,6310,6313,6317,6320,6323,6327,6330,6334, - 6337,6341,6344,6348,6351,6354,6358,6361,6365,6368,6371,6375,6378,6382,6385,6389, - 6392,6395,6399,6402,6406,6409,6412,6416,6419,6423,6426,6429,6433,6436,6440,6443, - 6446,6450,6453,6456,6460,6463,6467,6470,6473,6477,6480,6483,6487,6490,6493,6497, - 6500,6504,6507,6510,6514,6517,6520,6524,6527,6530,6534,6537,6540,6544,6547,6550, - 6554,6557,6560,6564,6567,6570,6574,6577,6580,6584,6587,6590,6594,6597,6600,6604, - 6607,6610,6613,6617,6620,6623,6627,6630,6633,6637,6640,6643,6646,6650,6653,6656, - 6660,6663,6666,6669,6673,6676,6679,6683,6686,6689,6692,6696,6699,6702,6705,6709, - 6712,6715,6718,6722,6725,6728,6731,6735,6738,6741,6744,6748,6751,6754,6757,6761, - 6764,6767,6770,6774,6777,6780,6783,6787,6790,6793,6796,6799,6803,6806,6809,6812, - 6815,6819,6822,6825,6828,6832,6835,6838,6841,6844,6848,6851,6854,6857,6860,6863, - 6867,6870,6873,6876,6879,6883,6886,6889,6892,6895,6898,6902,6905,6908,6911,6914, - 6917,6921,6924,6927,6930,6933,6936,6940,6943,6946,6949,6952,6955,6958,6962,6965, - 6968,6971,6974,6977,6980,6984,6987,6990,6993,6996,6999,7002,7005,7009,7012,7015, - 7018,7021,7024,7027,7030,7033,7037,7040,7043,7046,7049,7052,7055,7058,7061,7064, - 7068,7071,7074,7077,7080,7083,7086,7089,7092,7095,7098,7101,7105,7108,7111,7114, - 7117,7120,7123,7126,7129,7132,7135,7138,7141,7144,7147,7150,7154,7157,7160,7163, - 7166,7169,7172,7175,7178,7181,7184,7187,7190,7193,7196,7199,7202,7205,7208,7211, - 7214,7217,7220,7223,7226,7229,7232,7235,7238,7241,7244,7247,7250,7253,7256,7259, - 7262,7265,7268,7271,7274,7277,7280,7283,7286,7289,7292,7295,7298,7301,7304,7307, - 7310,7313,7316,7319,7322,7325,7328,7331,7334,7337,7340,7343,7346,7349,7352,7355, - 7358,7361,7363,7366,7369,7372,7375,7378,7381,7384,7387,7390,7393,7396,7399,7402, - 7405,7408,7411,7413,7416,7419,7422,7425,7428,7431,7434,7437,7440,7443,7446,7448, - 7451,7454,7457,7460,7463,7466,7469,7472,7475,7477,7480,7483,7486,7489,7492,7495, - 7498,7501,7503,7506,7509,7512,7515,7518,7521,7524,7526,7529,7532,7535,7538,7541, - 7544,7547,7549,7552,7555,7558,7561,7564,7566,7569,7572,7575,7578,7581,7584,7586, - 7589,7592,7595,7598,7601,7603,7606,7609,7612,7615,7618,7620,7623,7626,7629,7632, - 7635,7637,7640,7643,7646,7649,7651,7654,7657,7660,7663,7665,7668,7671,7674,7677, - 7679,7682,7685,7688,7691,7693,7696,7699,7702,7705,7707,7710,7713,7716,7718,7721, - 7724,7727,7730,7732,7735,7738,7741,7743,7746,7749,7752,7754,7757,7760,7763,7765, - 7768,7771,7774,7776,7779,7782,7785,7787,7790,7793,7796,7798,7801,7804,7807,7809, - 7812,7815,7818,7820,7823,7826,7828,7831,7834,7837,7839,7842,7845,7848,7850,7853, - 7856,7858,7861,7864,7866,7869,7872,7875,7877,7880,7883,7885,7888,7891,7893,7896, - 7899,7902,7904,7907,7910,7912,7915,7918,7920,7923,7926,7928,7931,7934,7936,7939, - 7942,7944,7947,7950,7952,7955,7958,7960,7963,7966,7968,7971,7974,7976,7979,7982, - 7984,7987,7990,7992,7995,7997,8000,8003,8005,8008,8011,8013,8016,8019,8021,8024, - 8026,8029,8032,8034,8037,8040,8042,8045,8047,8050,8053,8055,8058,8060,8063,8066, - 8068,8071,8074,8076,8079,8081,8084,8087,8089,8092,8094,8097,8100,8102,8105,8107, - 8110,8112,8115,8118,8120,8123,8125,8128,8131,8133,8136,8138,8141,8143,8146,8149, - 8151,8154,8156,8159,8161,8164,8166,8169,8172,8174,8177,8179,8182,8184,8187,8189, - 8192,0, -};} diff --git a/gp2cos.hpp b/gp2cos.hpp index cf5cbf5..cbd48a9 100644 --- a/gp2cos.hpp +++ b/gp2cos.hpp @@ -1,7 +1,6 @@ #pragma once #include #include -#include "gp2cos_reference.hpp" // golden oracle: GP2's extracted t_Sinus bytes // --------------------------------------------------------------------------- // GP2 geometry — the cosine table expressed as its generating formula. @@ -19,15 +18,19 @@ // to 703 (round() gives 704). That single 1-unit entry is load-bearing: the // racing-line solver is a chaotic feedback system, and a track whose heading // passes through this angle (e.g. F1CT09) drops from 100% to 29% correct -// without it. So we override that one entry and PROVE the whole table equals -// GP2's bytes at compile time (see the static_assert at the bottom). +// without it. So we override that one entry explicitly (kQuirkIndex below). // // Why a formula instead of an embedded blob: the formula documents intent -// (this IS a cosine), and computing it at build time keeps the runtime fully -// deterministic (no per-call float). The float boundary entries aren't -// portable, so we don't trust them blindly — the static_assert is the -// guardrail: if any toolchain rounds a boundary entry differently, the BUILD -// fails loudly instead of the racing line drifting silently. +// (this IS a cosine) and computing it at build time keeps the runtime fully +// deterministic (no per-call float). +// +// WARNING: the entries come from compile-time floating-point rounding, so a +// different compiler or standard library could round an entry that lands on a +// .5 boundary the other way. We force only the one known boundary case +// (i = 1992); any other toolchain disagreement would be silent. If the compiled +// track or cc-line ever start to DRIFT, suspect a rounding mismatch in this +// table (or in gp2atan.hpp) first: regenerate it and diff against GP2's t_Sinus +// bytes. // --------------------------------------------------------------------------- namespace gp2geom { @@ -81,17 +84,8 @@ constexpr short scos(int angle) { return kCosine[a >> 3]; } -// ---- compile-time guardrails ------------------------------------------------- -constexpr bool matchesReference() { - for (int i = 0; i < kGrid; ++i) - if (kCosine[i] != kCosRef[i]) return false; - return true; -} -static_assert(matchesReference(), - "gp2geom cosine table != GP2 reference bytes: a float rounding boundary " - "flipped on this toolchain. Inspect the diff and add a documented override " - "before shipping — do NOT relax this assert."); - +// Cheap spot-checks (no embedded reference table). The 1992 entry is the one +// load-bearing rounding boundary, so we still pin it at compile time. static_assert(kCosine[0] == 16384, "cos(0)"); static_assert(kCosine[2048] == 0, "cos(pi/2)"); static_assert(kCosine[4096] == -16384, "cos(pi)"); diff --git a/gp2cos_reference.hpp b/gp2cos_reference.hpp deleted file mode 100644 index 36322ed..0000000 --- a/gp2cos_reference.hpp +++ /dev/null @@ -1,263 +0,0 @@ -#pragma once -#include -// Golden reference: GP2 t_Sinus bytes (file 0x1DDAEC), indices 0..4097. -// Used ONLY as the compile-time oracle for the formula in gp2cos.hpp. -namespace gp2geom { inline constexpr short kCosRef[4098] = { - 16384,16384,16384,16384,16384,16384,16384,16384,16384,16384,16384,16383,16383,16383,16383,16383, - 16383,16383,16382,16382,16382,16382,16382,16381,16381,16381,16381,16380,16380,16380,16380,16379, - 16379,16379,16378,16378,16378,16377,16377,16377,16376,16376,16375,16375,16375,16374,16374,16373, - 16373,16372,16372,16371,16371,16370,16370,16369,16369,16368,16368,16367,16367,16366,16365,16365, - 16364,16364,16363,16362,16362,16361,16360,16360,16359,16358,16358,16357,16356,16355,16355,16354, - 16353,16352,16352,16351,16350,16349,16348,16348,16347,16346,16345,16344,16343,16342,16341,16341, - 16340,16339,16338,16337,16336,16335,16334,16333,16332,16331,16330,16329,16328,16327,16326,16325, - 16324,16323,16321,16320,16319,16318,16317,16316,16315,16313,16312,16311,16310,16309,16308,16306, - 16305,16304,16303,16301,16300,16299,16298,16296,16295,16294,16292,16291,16290,16288,16287,16286, - 16284,16283,16281,16280,16279,16277,16276,16274,16273,16271,16270,16268,16267,16265,16264,16262, - 16261,16259,16258,16256,16255,16253,16251,16250,16248,16247,16245,16243,16242,16240,16238,16237, - 16235,16233,16232,16230,16228,16226,16225,16223,16221,16219,16218,16216,16214,16212,16210,16209, - 16207,16205,16203,16201,16199,16197,16195,16194,16192,16190,16188,16186,16184,16182,16180,16178, - 16176,16174,16172,16170,16168,16166,16164,16162,16160,16158,16156,16153,16151,16149,16147,16145, - 16143,16141,16138,16136,16134,16132,16130,16128,16125,16123,16121,16119,16116,16114,16112,16109, - 16107,16105,16103,16100,16098,16096,16093,16091,16088,16086,16084,16081,16079,16076,16074,16072, - 16069,16067,16064,16062,16059,16057,16054,16052,16049,16047,16044,16042,16039,16037,16034,16031, - 16029,16026,16024,16021,16018,16016,16013,16010,16008,16005,16002,16000,15997,15994,15991,15989, - 15986,15983,15980,15978,15975,15972,15969,15966,15964,15961,15958,15955,15952,15949,15946,15944, - 15941,15938,15935,15932,15929,15926,15923,15920,15917,15914,15911,15908,15905,15902,15899,15896, - 15893,15890,15887,15884,15881,15878,15875,15871,15868,15865,15862,15859,15856,15853,15849,15846, - 15843,15840,15837,15833,15830,15827,15824,15820,15817,15814,15810,15807,15804,15801,15797,15794, - 15791,15787,15784,15780,15777,15774,15770,15767,15763,15760,15757,15753,15750,15746,15743,15739, - 15736,15732,15729,15725,15722,15718,15715,15711,15707,15704,15700,15697,15693,15689,15686,15682, - 15679,15675,15671,15668,15664,15660,15656,15653,15649,15645,15642,15638,15634,15630,15627,15623, - 15619,15615,15611,15608,15604,15600,15596,15592,15588,15584,15581,15577,15573,15569,15565,15561, - 15557,15553,15549,15545,15541,15537,15533,15529,15525,15521,15517,15513,15509,15505,15501,15497, - 15493,15489,15485,15481,15476,15472,15468,15464,15460,15456,15451,15447,15443,15439,15435,15430, - 15426,15422,15418,15414,15409,15405,15401,15396,15392,15388,15383,15379,15375,15370,15366,15362, - 15357,15353,15349,15344,15340,15335,15331,15326,15322,15318,15313,15309,15304,15300,15295,15291, - 15286,15282,15277,15273,15268,15263,15259,15254,15250,15245,15240,15236,15231,15227,15222,15217, - 15213,15208,15203,15199,15194,15189,15184,15180,15175,15170,15166,15161,15156,15151,15146,15142, - 15137,15132,15127,15122,15118,15113,15108,15103,15098,15093,15088,15083,15078,15074,15069,15064, - 15059,15054,15049,15044,15039,15034,15029,15024,15019,15014,15009,15004,14999,14994,14989,14984, - 14978,14973,14968,14963,14958,14953,14948,14943,14937,14932,14927,14922,14917,14911,14906,14901, - 14896,14891,14885,14880,14875,14870,14864,14859,14854,14848,14843,14838,14832,14827,14822,14816, - 14811,14806,14800,14795,14789,14784,14779,14773,14768,14762,14757,14751,14746,14740,14735,14729, - 14724,14718,14713,14707,14702,14696,14691,14685,14680,14674,14668,14663,14657,14651,14646,14640, - 14635,14629,14623,14618,14612,14606,14601,14595,14589,14583,14578,14572,14566,14560,14555,14549, - 14543,14537,14531,14526,14520,14514,14508,14502,14497,14491,14485,14479,14473,14467,14461,14455, - 14449,14443,14438,14432,14426,14420,14414,14408,14402,14396,14390,14384,14378,14372,14366,14360, - 14354,14347,14341,14335,14329,14323,14317,14311,14305,14299,14293,14286,14280,14274,14268,14262, - 14256,14249,14243,14237,14231,14224,14218,14212,14206,14199,14193,14187,14181,14174,14168,14162, - 14155,14149,14143,14136,14130,14124,14117,14111,14104,14098,14092,14085,14079,14072,14066,14059, - 14053,14047,14040,14034,14027,14021,14014,14008,14001,13995,13988,13981,13975,13968,13962,13955, - 13949,13942,13935,13929,13922,13916,13909,13902,13896,13889,13882,13876,13869,13862,13856,13849, - 13842,13835,13829,13822,13815,13808,13802,13795,13788,13781,13774,13768,13761,13754,13747,13740, - 13733,13727,13720,13713,13706,13699,13692,13685,13678,13671,13665,13658,13651,13644,13637,13630, - 13623,13616,13609,13602,13595,13588,13581,13574,13567,13560,13553,13546,13538,13531,13524,13517, - 13510,13503,13496,13489,13482,13474,13467,13460,13453,13446,13439,13431,13424,13417,13410,13403, - 13395,13388,13381,13374,13366,13359,13352,13344,13337,13330,13323,13315,13308,13301,13293,13286, - 13279,13271,13264,13256,13249,13242,13234,13227,13219,13212,13205,13197,13190,13182,13175,13167, - 13160,13152,13145,13137,13130,13122,13115,13107,13100,13092,13085,13077,13069,13062,13054,13047, - 13039,13031,13024,13016,13008,13001,12993,12986,12978,12970,12963,12955,12947,12939,12932,12924, - 12916,12909,12901,12893,12885,12878,12870,12862,12854,12846,12839,12831,12823,12815,12807,12799, - 12792,12784,12776,12768,12760,12752,12744,12736,12729,12721,12713,12705,12697,12689,12681,12673, - 12665,12657,12649,12641,12633,12625,12617,12609,12601,12593,12585,12577,12569,12561,12553,12545, - 12537,12528,12520,12512,12504,12496,12488,12480,12472,12463,12455,12447,12439,12431,12423,12414, - 12406,12398,12390,12381,12373,12365,12357,12348,12340,12332,12324,12315,12307,12299,12290,12282, - 12274,12266,12257,12249,12240,12232,12224,12215,12207,12199,12190,12182,12173,12165,12157,12148, - 12140,12131,12123,12114,12106,12097,12089,12080,12072,12064,12055,12046,12038,12029,12021,12012, - 12004,11995,11987,11978,11970,11961,11952,11944,11935,11927,11918,11909,11901,11892,11883,11875, - 11866,11857,11849,11840,11831,11823,11814,11805,11797,11788,11779,11770,11762,11753,11744,11735, - 11727,11718,11709,11700,11691,11683,11674,11665,11656,11647,11638,11630,11621,11612,11603,11594, - 11585,11576,11567,11559,11550,11541,11532,11523,11514,11505,11496,11487,11478,11469,11460,11451, - 11442,11433,11424,11415,11406,11397,11388,11379,11370,11361,11352,11343,11334,11325,11316,11307, - 11297,11288,11279,11270,11261,11252,11243,11234,11224,11215,11206,11197,11188,11179,11169,11160, - 11151,11142,11133,11123,11114,11105,11096,11086,11077,11068,11059,11049,11040,11031,11021,11012, - 11003,10994,10984,10975,10966,10956,10947,10937,10928,10919,10909,10900,10891,10881,10872,10862, - 10853,10844,10834,10825,10815,10806,10796,10787,10778,10768,10759,10749,10740,10730,10721,10711, - 10702,10692,10683,10673,10663,10654,10644,10635,10625,10616,10606,10597,10587,10577,10568,10558, - 10549,10539,10529,10520,10510,10500,10491,10481,10471,10462,10452,10442,10433,10423,10413,10404, - 10394,10384,10374,10365,10355,10345,10336,10326,10316,10306,10296,10287,10277,10267,10257,10248, - 10238,10228,10218,10208,10198,10189,10179,10169,10159,10149,10139,10129,10120,10110,10100,10090, - 10080,10070,10060,10050,10040,10030,10020,10010,10001,9991,9981,9971,9961,9951,9941,9931, - 9921,9911,9901,9891,9881,9871,9861,9851,9841,9830,9820,9810,9800,9790,9780,9770, - 9760,9750,9740,9730,9720,9709,9699,9689,9679,9669,9659,9649,9638,9628,9618,9608, - 9598,9588,9577,9567,9557,9547,9537,9526,9516,9506,9496,9485,9475,9465,9455,9444, - 9434,9424,9413,9403,9393,9383,9372,9362,9352,9341,9331,9321,9310,9300,9290,9279, - 9269,9259,9248,9238,9227,9217,9207,9196,9186,9175,9165,9155,9144,9134,9123,9113, - 9102,9092,9082,9071,9061,9050,9040,9029,9019,9008,8998,8987,8977,8966,8956,8945, - 8935,8924,8914,8903,8892,8882,8871,8861,8850,8840,8829,8818,8808,8797,8787,8776, - 8765,8755,8744,8734,8723,8712,8702,8691,8680,8670,8659,8648,8638,8627,8616,8606, - 8595,8584,8573,8563,8552,8541,8531,8520,8509,8498,8488,8477,8466,8455,8445,8434, - 8423,8412,8401,8391,8380,8369,8358,8347,8337,8326,8315,8304,8293,8283,8272,8261, - 8250,8239,8228,8217,8207,8196,8185,8174,8163,8152,8141,8130,8119,8108,8098,8087, - 8076,8065,8054,8043,8032,8021,8010,7999,7988,7977,7966,7955,7944,7933,7922,7911, - 7900,7889,7878,7867,7856,7845,7834,7823,7812,7801,7790,7779,7768,7757,7746,7734, - 7723,7712,7701,7690,7679,7668,7657,7646,7635,7623,7612,7601,7590,7579,7568,7557, - 7545,7534,7523,7512,7501,7490,7478,7467,7456,7445,7434,7423,7411,7400,7389,7378, - 7366,7355,7344,7333,7321,7310,7299,7288,7276,7265,7254,7243,7231,7220,7209,7198, - 7186,7175,7164,7152,7141,7130,7118,7107,7096,7084,7073,7062,7050,7039,7028,7016, - 7005,6994,6982,6971,6960,6948,6937,6925,6914,6903,6891,6880,6868,6857,6846,6834, - 6823,6811,6800,6788,6777,6766,6754,6743,6731,6720,6708,6697,6685,6674,6662,6651, - 6639,6628,6616,6605,6593,6582,6570,6559,6547,6536,6524,6513,6501,6490,6478,6467, - 6455,6444,6432,6420,6409,6397,6386,6374,6363,6351,6339,6328,6316,6305,6293,6281, - 6270,6258,6247,6235,6223,6212,6200,6189,6177,6165,6154,6142,6130,6119,6107,6095, - 6084,6072,6060,6049,6037,6025,6014,6002,5990,5979,5967,5955,5943,5932,5920,5908, - 5897,5885,5873,5861,5850,5838,5826,5814,5803,5791,5779,5767,5756,5744,5732,5720, - 5708,5697,5685,5673,5661,5650,5638,5626,5614,5602,5591,5579,5567,5555,5543,5531, - 5520,5508,5496,5484,5472,5460,5449,5437,5425,5413,5401,5389,5377,5366,5354,5342, - 5330,5318,5306,5294,5282,5270,5259,5247,5235,5223,5211,5199,5187,5175,5163,5151, - 5139,5127,5115,5104,5092,5080,5068,5056,5044,5032,5020,5008,4996,4984,4972,4960, - 4948,4936,4924,4912,4900,4888,4876,4864,4852,4840,4828,4816,4804,4792,4780,4768, - 4756,4744,4732,4720,4708,4696,4684,4672,4660,4648,4636,4624,4612,4599,4587,4575, - 4563,4551,4539,4527,4515,4503,4491,4479,4467,4455,4442,4430,4418,4406,4394,4382, - 4370,4358,4346,4333,4321,4309,4297,4285,4273,4261,4249,4236,4224,4212,4200,4188, - 4176,4164,4151,4139,4127,4115,4103,4091,4078,4066,4054,4042,4030,4018,4005,3993, - 3981,3969,3957,3944,3932,3920,3908,3896,3883,3871,3859,3847,3835,3822,3810,3798, - 3786,3773,3761,3749,3737,3724,3712,3700,3688,3676,3663,3651,3639,3627,3614,3602, - 3590,3577,3565,3553,3541,3528,3516,3504,3492,3479,3467,3455,3442,3430,3418,3406, - 3393,3381,3369,3356,3344,3332,3320,3307,3295,3283,3270,3258,3246,3233,3221,3209, - 3196,3184,3172,3159,3147,3135,3122,3110,3098,3085,3073,3061,3048,3036,3024,3011, - 2999,2987,2974,2962,2949,2937,2925,2912,2900,2888,2875,2863,2851,2838,2826,2813, - 2801,2789,2776,2764,2752,2739,2727,2714,2702,2690,2677,2665,2652,2640,2628,2615, - 2603,2590,2578,2566,2553,2541,2528,2516,2503,2491,2479,2466,2454,2441,2429,2416, - 2404,2392,2379,2367,2354,2342,2329,2317,2305,2292,2280,2267,2255,2242,2230,2217, - 2205,2193,2180,2168,2155,2143,2130,2118,2105,2093,2080,2068,2055,2043,2031,2018, - 2006,1993,1981,1968,1956,1943,1931,1918,1906,1893,1881,1868,1856,1843,1831,1818, - 1806,1793,1781,1768,1756,1743,1731,1718,1706,1693,1681,1668,1656,1643,1631,1618, - 1606,1593,1581,1568,1556,1543,1531,1518,1506,1493,1481,1468,1456,1443,1431,1418, - 1406,1393,1381,1368,1356,1343,1331,1318,1306,1293,1280,1268,1255,1243,1230,1218, - 1205,1193,1180,1168,1155,1143,1130,1118,1105,1092,1080,1067,1055,1042,1030,1017, - 1005,992,980,967,955,942,929,917,904,892,879,867,854,842,829,816, - 804,791,779,766,754,741,729,716,703,691,678,666,653,641,628,616, - 603,590,578,565,553,540,528,515,503,490,477,465,452,440,427,415, - 402,390,377,364,352,339,327,314,302,289,276,264,251,239,226,214, - 201,188,176,163,151,138,126,113,101,88,75,63,50,38,25,13, - 0,-13,-25,-38,-50,-63,-75,-88,-101,-113,-126,-138,-151,-163,-176,-188, - -201,-214,-226,-239,-251,-264,-276,-289,-302,-314,-327,-339,-352,-364,-377,-390, - -402,-415,-427,-440,-452,-465,-477,-490,-503,-515,-528,-540,-553,-565,-578,-590, - -603,-616,-628,-641,-653,-666,-678,-691,-704,-716,-729,-741,-754,-766,-779,-791, - -804,-816,-829,-842,-854,-867,-879,-892,-904,-917,-929,-942,-955,-967,-980,-992, - -1005,-1017,-1030,-1042,-1055,-1067,-1080,-1092,-1105,-1118,-1130,-1143,-1155,-1168,-1180,-1193, - -1205,-1218,-1230,-1243,-1255,-1268,-1280,-1293,-1306,-1318,-1331,-1343,-1356,-1368,-1381,-1393, - -1406,-1418,-1431,-1443,-1456,-1468,-1481,-1493,-1506,-1518,-1531,-1543,-1556,-1568,-1581,-1593, - -1606,-1618,-1631,-1643,-1656,-1668,-1681,-1693,-1706,-1718,-1731,-1743,-1756,-1768,-1781,-1793, - -1806,-1818,-1831,-1843,-1856,-1868,-1881,-1893,-1906,-1918,-1931,-1943,-1956,-1968,-1981,-1993, - -2006,-2018,-2031,-2043,-2055,-2068,-2080,-2093,-2105,-2118,-2130,-2143,-2155,-2168,-2180,-2193, - -2205,-2217,-2230,-2242,-2255,-2267,-2280,-2292,-2305,-2317,-2329,-2342,-2354,-2367,-2379,-2392, - -2404,-2416,-2429,-2441,-2454,-2466,-2479,-2491,-2503,-2516,-2528,-2541,-2553,-2566,-2578,-2590, - -2603,-2615,-2628,-2640,-2652,-2665,-2677,-2690,-2702,-2714,-2727,-2739,-2752,-2764,-2776,-2789, - -2801,-2813,-2826,-2838,-2851,-2863,-2875,-2888,-2900,-2912,-2925,-2937,-2949,-2962,-2974,-2987, - -2999,-3011,-3024,-3036,-3048,-3061,-3073,-3085,-3098,-3110,-3122,-3135,-3147,-3159,-3172,-3184, - -3196,-3209,-3221,-3233,-3246,-3258,-3270,-3283,-3295,-3307,-3320,-3332,-3344,-3356,-3369,-3381, - -3393,-3406,-3418,-3430,-3442,-3455,-3467,-3479,-3492,-3504,-3516,-3528,-3541,-3553,-3565,-3577, - -3590,-3602,-3614,-3627,-3639,-3651,-3663,-3676,-3688,-3700,-3712,-3724,-3737,-3749,-3761,-3773, - -3786,-3798,-3810,-3822,-3835,-3847,-3859,-3871,-3883,-3896,-3908,-3920,-3932,-3944,-3957,-3969, - -3981,-3993,-4005,-4018,-4030,-4042,-4054,-4066,-4078,-4091,-4103,-4115,-4127,-4139,-4151,-4164, - -4176,-4188,-4200,-4212,-4224,-4236,-4249,-4261,-4273,-4285,-4297,-4309,-4321,-4333,-4346,-4358, - -4370,-4382,-4394,-4406,-4418,-4430,-4442,-4455,-4467,-4479,-4491,-4503,-4515,-4527,-4539,-4551, - -4563,-4575,-4587,-4599,-4612,-4624,-4636,-4648,-4660,-4672,-4684,-4696,-4708,-4720,-4732,-4744, - -4756,-4768,-4780,-4792,-4804,-4816,-4828,-4840,-4852,-4864,-4876,-4888,-4900,-4912,-4924,-4936, - -4948,-4960,-4972,-4984,-4996,-5008,-5020,-5032,-5044,-5056,-5068,-5080,-5092,-5104,-5115,-5127, - -5139,-5151,-5163,-5175,-5187,-5199,-5211,-5223,-5235,-5247,-5259,-5270,-5282,-5294,-5306,-5318, - -5330,-5342,-5354,-5366,-5377,-5389,-5401,-5413,-5425,-5437,-5449,-5460,-5472,-5484,-5496,-5508, - -5520,-5531,-5543,-5555,-5567,-5579,-5591,-5602,-5614,-5626,-5638,-5650,-5661,-5673,-5685,-5697, - -5708,-5720,-5732,-5744,-5756,-5767,-5779,-5791,-5803,-5814,-5826,-5838,-5850,-5861,-5873,-5885, - -5897,-5908,-5920,-5932,-5943,-5955,-5967,-5979,-5990,-6002,-6014,-6025,-6037,-6049,-6060,-6072, - -6084,-6095,-6107,-6119,-6130,-6142,-6154,-6165,-6177,-6189,-6200,-6212,-6223,-6235,-6247,-6258, - -6270,-6281,-6293,-6305,-6316,-6328,-6339,-6351,-6363,-6374,-6386,-6397,-6409,-6420,-6432,-6444, - -6455,-6467,-6478,-6490,-6501,-6513,-6524,-6536,-6547,-6559,-6570,-6582,-6593,-6605,-6616,-6628, - -6639,-6651,-6662,-6674,-6685,-6697,-6708,-6720,-6731,-6743,-6754,-6766,-6777,-6788,-6800,-6811, - -6823,-6834,-6846,-6857,-6868,-6880,-6891,-6903,-6914,-6925,-6937,-6948,-6960,-6971,-6982,-6994, - -7005,-7016,-7028,-7039,-7050,-7062,-7073,-7084,-7096,-7107,-7118,-7130,-7141,-7152,-7164,-7175, - -7186,-7198,-7209,-7220,-7231,-7243,-7254,-7265,-7276,-7288,-7299,-7310,-7321,-7333,-7344,-7355, - -7366,-7378,-7389,-7400,-7411,-7423,-7434,-7445,-7456,-7467,-7478,-7490,-7501,-7512,-7523,-7534, - -7545,-7557,-7568,-7579,-7590,-7601,-7612,-7623,-7635,-7646,-7657,-7668,-7679,-7690,-7701,-7712, - -7723,-7734,-7746,-7757,-7768,-7779,-7790,-7801,-7812,-7823,-7834,-7845,-7856,-7867,-7878,-7889, - -7900,-7911,-7922,-7933,-7944,-7955,-7966,-7977,-7988,-7999,-8010,-8021,-8032,-8043,-8054,-8065, - -8076,-8087,-8098,-8108,-8119,-8130,-8141,-8152,-8163,-8174,-8185,-8196,-8207,-8217,-8228,-8239, - -8250,-8261,-8272,-8283,-8293,-8304,-8315,-8326,-8337,-8347,-8358,-8369,-8380,-8391,-8401,-8412, - -8423,-8434,-8445,-8455,-8466,-8477,-8488,-8498,-8509,-8520,-8531,-8541,-8552,-8563,-8573,-8584, - -8595,-8606,-8616,-8627,-8638,-8648,-8659,-8670,-8680,-8691,-8702,-8712,-8723,-8734,-8744,-8755, - -8765,-8776,-8787,-8797,-8808,-8818,-8829,-8840,-8850,-8861,-8871,-8882,-8892,-8903,-8914,-8924, - -8935,-8945,-8956,-8966,-8977,-8987,-8998,-9008,-9019,-9029,-9040,-9050,-9061,-9071,-9082,-9092, - -9102,-9113,-9123,-9134,-9144,-9155,-9165,-9175,-9186,-9196,-9207,-9217,-9227,-9238,-9248,-9259, - -9269,-9279,-9290,-9300,-9310,-9321,-9331,-9341,-9352,-9362,-9372,-9383,-9393,-9403,-9413,-9424, - -9434,-9444,-9455,-9465,-9475,-9485,-9496,-9506,-9516,-9526,-9537,-9547,-9557,-9567,-9577,-9588, - -9598,-9608,-9618,-9628,-9638,-9649,-9659,-9669,-9679,-9689,-9699,-9709,-9720,-9730,-9740,-9750, - -9760,-9770,-9780,-9790,-9800,-9810,-9820,-9830,-9841,-9851,-9861,-9871,-9881,-9891,-9901,-9911, - -9921,-9931,-9941,-9951,-9961,-9971,-9981,-9991,-10001,-10010,-10020,-10030,-10040,-10050,-10060,-10070, - -10080,-10090,-10100,-10110,-10120,-10129,-10139,-10149,-10159,-10169,-10179,-10189,-10198,-10208,-10218,-10228, - -10238,-10248,-10257,-10267,-10277,-10287,-10296,-10306,-10316,-10326,-10336,-10345,-10355,-10365,-10374,-10384, - -10394,-10404,-10413,-10423,-10433,-10442,-10452,-10462,-10471,-10481,-10491,-10500,-10510,-10520,-10529,-10539, - -10549,-10558,-10568,-10577,-10587,-10597,-10606,-10616,-10625,-10635,-10644,-10654,-10663,-10673,-10683,-10692, - -10702,-10711,-10721,-10730,-10740,-10749,-10759,-10768,-10778,-10787,-10796,-10806,-10815,-10825,-10834,-10844, - -10853,-10862,-10872,-10881,-10891,-10900,-10909,-10919,-10928,-10937,-10947,-10956,-10966,-10975,-10984,-10994, - -11003,-11012,-11021,-11031,-11040,-11049,-11059,-11068,-11077,-11086,-11096,-11105,-11114,-11123,-11133,-11142, - -11151,-11160,-11169,-11179,-11188,-11197,-11206,-11215,-11224,-11234,-11243,-11252,-11261,-11270,-11279,-11288, - -11297,-11307,-11316,-11325,-11334,-11343,-11352,-11361,-11370,-11379,-11388,-11397,-11406,-11415,-11424,-11433, - -11442,-11451,-11460,-11469,-11478,-11487,-11496,-11505,-11514,-11523,-11532,-11541,-11550,-11559,-11567,-11576, - -11585,-11594,-11603,-11612,-11621,-11630,-11638,-11647,-11656,-11665,-11674,-11683,-11691,-11700,-11709,-11718, - -11727,-11735,-11744,-11753,-11762,-11770,-11779,-11788,-11797,-11805,-11814,-11823,-11831,-11840,-11849,-11857, - -11866,-11875,-11883,-11892,-11901,-11909,-11918,-11927,-11935,-11944,-11952,-11961,-11970,-11978,-11987,-11995, - -12004,-12012,-12021,-12029,-12038,-12046,-12055,-12064,-12072,-12080,-12089,-12097,-12106,-12114,-12123,-12131, - -12140,-12148,-12157,-12165,-12173,-12182,-12190,-12199,-12207,-12215,-12224,-12232,-12240,-12249,-12257,-12266, - -12274,-12282,-12290,-12299,-12307,-12315,-12324,-12332,-12340,-12348,-12357,-12365,-12373,-12381,-12390,-12398, - -12406,-12414,-12423,-12431,-12439,-12447,-12455,-12463,-12472,-12480,-12488,-12496,-12504,-12512,-12520,-12528, - -12537,-12545,-12553,-12561,-12569,-12577,-12585,-12593,-12601,-12609,-12617,-12625,-12633,-12641,-12649,-12657, - -12665,-12673,-12681,-12689,-12697,-12705,-12713,-12721,-12729,-12736,-12744,-12752,-12760,-12768,-12776,-12784, - -12792,-12799,-12807,-12815,-12823,-12831,-12839,-12846,-12854,-12862,-12870,-12878,-12885,-12893,-12901,-12909, - -12916,-12924,-12932,-12939,-12947,-12955,-12963,-12970,-12978,-12986,-12993,-13001,-13008,-13016,-13024,-13031, - -13039,-13047,-13054,-13062,-13069,-13077,-13085,-13092,-13100,-13107,-13115,-13122,-13130,-13137,-13145,-13152, - -13160,-13167,-13175,-13182,-13190,-13197,-13205,-13212,-13219,-13227,-13234,-13242,-13249,-13256,-13264,-13271, - -13279,-13286,-13293,-13301,-13308,-13315,-13323,-13330,-13337,-13344,-13352,-13359,-13366,-13374,-13381,-13388, - -13395,-13403,-13410,-13417,-13424,-13431,-13439,-13446,-13453,-13460,-13467,-13474,-13482,-13489,-13496,-13503, - -13510,-13517,-13524,-13531,-13538,-13546,-13553,-13560,-13567,-13574,-13581,-13588,-13595,-13602,-13609,-13616, - -13623,-13630,-13637,-13644,-13651,-13658,-13665,-13671,-13678,-13685,-13692,-13699,-13706,-13713,-13720,-13727, - -13733,-13740,-13747,-13754,-13761,-13768,-13774,-13781,-13788,-13795,-13802,-13808,-13815,-13822,-13829,-13835, - -13842,-13849,-13856,-13862,-13869,-13876,-13882,-13889,-13896,-13902,-13909,-13916,-13922,-13929,-13935,-13942, - -13949,-13955,-13962,-13968,-13975,-13981,-13988,-13995,-14001,-14008,-14014,-14021,-14027,-14034,-14040,-14047, - -14053,-14059,-14066,-14072,-14079,-14085,-14092,-14098,-14104,-14111,-14117,-14124,-14130,-14136,-14143,-14149, - -14155,-14162,-14168,-14174,-14181,-14187,-14193,-14199,-14206,-14212,-14218,-14224,-14231,-14237,-14243,-14249, - -14256,-14262,-14268,-14274,-14280,-14286,-14293,-14299,-14305,-14311,-14317,-14323,-14329,-14335,-14341,-14347, - -14354,-14360,-14366,-14372,-14378,-14384,-14390,-14396,-14402,-14408,-14414,-14420,-14426,-14432,-14438,-14443, - -14449,-14455,-14461,-14467,-14473,-14479,-14485,-14491,-14497,-14502,-14508,-14514,-14520,-14526,-14531,-14537, - -14543,-14549,-14555,-14560,-14566,-14572,-14578,-14583,-14589,-14595,-14601,-14606,-14612,-14618,-14623,-14629, - -14635,-14640,-14646,-14651,-14657,-14663,-14668,-14674,-14680,-14685,-14691,-14696,-14702,-14707,-14713,-14718, - -14724,-14729,-14735,-14740,-14746,-14751,-14757,-14762,-14768,-14773,-14779,-14784,-14789,-14795,-14800,-14806, - -14811,-14816,-14822,-14827,-14832,-14838,-14843,-14848,-14854,-14859,-14864,-14870,-14875,-14880,-14885,-14891, - -14896,-14901,-14906,-14911,-14917,-14922,-14927,-14932,-14937,-14943,-14948,-14953,-14958,-14963,-14968,-14973, - -14978,-14984,-14989,-14994,-14999,-15004,-15009,-15014,-15019,-15024,-15029,-15034,-15039,-15044,-15049,-15054, - -15059,-15064,-15069,-15074,-15078,-15083,-15088,-15093,-15098,-15103,-15108,-15113,-15118,-15122,-15127,-15132, - -15137,-15142,-15146,-15151,-15156,-15161,-15166,-15170,-15175,-15180,-15184,-15189,-15194,-15199,-15203,-15208, - -15213,-15217,-15222,-15227,-15231,-15236,-15240,-15245,-15250,-15254,-15259,-15263,-15268,-15273,-15277,-15282, - -15286,-15291,-15295,-15300,-15304,-15309,-15313,-15318,-15322,-15326,-15331,-15335,-15340,-15344,-15349,-15353, - -15357,-15362,-15366,-15370,-15375,-15379,-15383,-15388,-15392,-15396,-15401,-15405,-15409,-15414,-15418,-15422, - -15426,-15430,-15435,-15439,-15443,-15447,-15451,-15456,-15460,-15464,-15468,-15472,-15476,-15481,-15485,-15489, - -15493,-15497,-15501,-15505,-15509,-15513,-15517,-15521,-15525,-15529,-15533,-15537,-15541,-15545,-15549,-15553, - -15557,-15561,-15565,-15569,-15573,-15577,-15581,-15584,-15588,-15592,-15596,-15600,-15604,-15608,-15611,-15615, - -15619,-15623,-15627,-15630,-15634,-15638,-15642,-15645,-15649,-15653,-15656,-15660,-15664,-15668,-15671,-15675, - -15679,-15682,-15686,-15689,-15693,-15697,-15700,-15704,-15707,-15711,-15715,-15718,-15722,-15725,-15729,-15732, - -15736,-15739,-15743,-15746,-15750,-15753,-15757,-15760,-15763,-15767,-15770,-15774,-15777,-15780,-15784,-15787, - -15791,-15794,-15797,-15801,-15804,-15807,-15810,-15814,-15817,-15820,-15824,-15827,-15830,-15833,-15837,-15840, - -15843,-15846,-15849,-15853,-15856,-15859,-15862,-15865,-15868,-15871,-15875,-15878,-15881,-15884,-15887,-15890, - -15893,-15896,-15899,-15902,-15905,-15908,-15911,-15914,-15917,-15920,-15923,-15926,-15929,-15932,-15935,-15938, - -15941,-15944,-15946,-15949,-15952,-15955,-15958,-15961,-15964,-15966,-15969,-15972,-15975,-15978,-15980,-15983, - -15986,-15989,-15991,-15994,-15997,-16000,-16002,-16005,-16008,-16010,-16013,-16016,-16018,-16021,-16024,-16026, - -16029,-16031,-16034,-16037,-16039,-16042,-16044,-16047,-16049,-16052,-16054,-16057,-16059,-16062,-16064,-16067, - -16069,-16072,-16074,-16076,-16079,-16081,-16084,-16086,-16088,-16091,-16093,-16096,-16098,-16100,-16103,-16105, - -16107,-16109,-16112,-16114,-16116,-16119,-16121,-16123,-16125,-16128,-16130,-16132,-16134,-16136,-16138,-16141, - -16143,-16145,-16147,-16149,-16151,-16153,-16156,-16158,-16160,-16162,-16164,-16166,-16168,-16170,-16172,-16174, - -16176,-16178,-16180,-16182,-16184,-16186,-16188,-16190,-16192,-16194,-16195,-16197,-16199,-16201,-16203,-16205, - -16207,-16209,-16210,-16212,-16214,-16216,-16218,-16219,-16221,-16223,-16225,-16226,-16228,-16230,-16232,-16233, - -16235,-16237,-16238,-16240,-16242,-16243,-16245,-16247,-16248,-16250,-16251,-16253,-16255,-16256,-16258,-16259, - -16261,-16262,-16264,-16265,-16267,-16268,-16270,-16271,-16273,-16274,-16276,-16277,-16279,-16280,-16281,-16283, - -16284,-16286,-16287,-16288,-16290,-16291,-16292,-16294,-16295,-16296,-16298,-16299,-16300,-16301,-16303,-16304, - -16305,-16306,-16308,-16309,-16310,-16311,-16312,-16313,-16315,-16316,-16317,-16318,-16319,-16320,-16321,-16323, - -16324,-16325,-16326,-16327,-16328,-16329,-16330,-16331,-16332,-16333,-16334,-16335,-16336,-16337,-16338,-16339, - -16340,-16341,-16341,-16342,-16343,-16344,-16345,-16346,-16347,-16348,-16348,-16349,-16350,-16351,-16352,-16352, - -16353,-16354,-16355,-16355,-16356,-16357,-16358,-16358,-16359,-16360,-16360,-16361,-16362,-16362,-16363,-16364, - -16364,-16365,-16365,-16366,-16367,-16367,-16368,-16368,-16369,-16369,-16370,-16370,-16371,-16371,-16372,-16372, - -16373,-16373,-16374,-16374,-16375,-16375,-16375,-16376,-16376,-16377,-16377,-16377,-16378,-16378,-16378,-16379, - -16379,-16379,-16380,-16380,-16380,-16380,-16381,-16381,-16381,-16381,-16382,-16382,-16382,-16382,-16382,-16383, - -16383,-16383,-16383,-16383,-16383,-16383,-16384,-16384,-16384,-16384,-16384,-16384,-16384,-16384,-16384,-16384, - -16384,-16384, -};} From f5816f9e75d65a574d0d90bd7698998a6a2808e4 Mon Sep 17 00:00:00 2001 From: Roberto Remedio Date: Mon, 29 Jun 2026 00:26:22 -0300 Subject: [PATCH 12/12] Fix mirrored left/right road edges in the compiled overlay buildCompiledOverlay drew widthL/widthR with the perpendicular sign opposite to the normal view's getLeftSide (angle-90) / getRightSide (angle+90), so a Track Width Change Right (0xB5) command moved the on-screen LEFT edge and vice-versa. Flip the edge signs so widthL uses -pv (left) and widthR uses +pv (right), matching the normal view and the game. --- GPTrack.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/GPTrack.cpp b/GPTrack.cpp index 43d1083..972be67 100644 --- a/GPTrack.cpp +++ b/GPTrack.cpp @@ -4089,8 +4089,10 @@ bool GPTrack::buildCompiledOverlay() double pvx = -sinh, pvy = cosh; // unit perpendicular (worldX, worldY) double px = ov.gt.X8[i] / 8.0, py = ov.gt.Y8[i] / 8.0; double wl = ov.gt.widthL[i] / 8.0, wr = ov.gt.widthR[i] / 8.0; - ov.lex[i] = ox + (px + pvx * wl - bwx) * ES; ov.ley[i] = oy + (py + pvy * wl - bwy) * ES; - ov.rex[i] = ox + (px - pvx * wr - bwx) * ES; ov.rey[i] = oy + (py - pvy * wr - bwy) * ES; + // edge sign matches the normal view's getLeftSide (a-90 => -pv) / getRightSide + // (a+90 => +pv); otherwise widthL/widthR render on the mirror-opposite side. + ov.lex[i] = ox + (px - pvx * wl - bwx) * ES; ov.ley[i] = oy + (py - pvy * wl - bwy) * ES; + ov.rex[i] = ox + (px + pvx * wr - bwx) * ES; ov.rey[i] = oy + (py + pvy * wr - bwy) * ES; } if (showComputedCCLine) {