56230dd340
- util/ref.c: refUnlock's assert(count >= 0) on an unsigned count was tautological, so a double-unlock silently underflowed to UINT32_MAX instead of asserting. Now asserts count > 0 before decrementing. - duskpsp/time/timepsp.c: timeGetRealTimeZonePSP returned hours while every other platform (and timeepoch.c's math) expects seconds. - thread.c: threadHandler reset threadId outside the mutex, after signaling STOPPED, letting a caller's immediate threadStart() race threadStartRequest()'s "thread id not 0" assert. threadId is now reset inside the same locked section. - tools/color.py: whole-number CSV channels (0, 1) produced invalid C float literals (0f, 1f) for the generated COLOR_*_3F/_4F macros. Route through float() so they always stringify with a decimal point. Also adds .claude/code-check.md: a full review pass over every subsystem in src/dusk/, with the above (plus several other findings not yet acted on) written up in detail. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
85 lines
2.9 KiB
Python
85 lines
2.9 KiB
Python
import argparse
|
|
import csv
|
|
import os
|
|
|
|
parser = argparse.ArgumentParser(description="Color CSV to .h defines")
|
|
parser.add_argument("--csv", required=True, help="Path to color CSV file")
|
|
parser.add_argument("--output", required=True, help="Path to output .h file")
|
|
args = parser.parse_args()
|
|
|
|
# Load colors
|
|
colors = {}
|
|
with open(args.csv, newline="", encoding="utf-8") as f:
|
|
reader = csv.DictReader(f)
|
|
if "name" not in reader.fieldnames:
|
|
raise ValueError("CSV must have a 'name' column")
|
|
for row in reader:
|
|
name = row["name"]
|
|
r, g, b = row["r"], row["g"], row["b"]
|
|
a = row["a"] if "a" in row and row["a"] != "" else "1.0"
|
|
for ch in (r, g, b, a):
|
|
if not (0.0 <= float(ch) <= 1.0):
|
|
raise ValueError(f"Color channel {ch!r} for '{name}' out of range [0.0, 1.0]")
|
|
colors[name] = (r, g, b, a)
|
|
|
|
# Build output
|
|
out = [
|
|
"#pragma once",
|
|
'#include "dusk.h"',
|
|
"",
|
|
"typedef float_t colorchannelf_t;",
|
|
"typedef uint8_t colorchannel8_t;",
|
|
"",
|
|
"typedef struct { colorchannelf_t r, g, b; } color3f_t;",
|
|
"typedef struct { colorchannelf_t r, g, b, a; } color4f_t;",
|
|
"typedef struct { colorchannel8_t r, g, b; } color3b_t;",
|
|
"typedef struct { colorchannel8_t r, g, b, a; } color4b_t;",
|
|
"typedef color4b_t color_t;",
|
|
"",
|
|
"#define color3f(r, g, b) ((color3f_t){ r, g, b })",
|
|
"#define color4f(r, g, b, a) ((color4f_t){ r, g, b, a })",
|
|
"#define color3b(r, g, b) ((color3b_t){ r, g, b })",
|
|
"#define color4b(r, g, b, a) ((color4b_t){ r, g, b, a })",
|
|
"#define color(r, g, b, a) color4b(r, g, b, a)",
|
|
"#define colorHex(hex) color4b(((hex >> 24) & 0xFF), ((hex >> 16) & 0xFF), ((hex >> 8) & 0xFF), (hex & 0xFF))",
|
|
"",
|
|
]
|
|
|
|
js = []
|
|
for name, (r, g, b, a) in colors.items():
|
|
r8, g8, b8, a8 = (int(float(ch) * 255) for ch in (r, g, b, a))
|
|
# Route through float() rather than the raw CSV string -- a whole-number
|
|
# channel like "0" or "1" has no decimal point, and "0f"/"1f" aren't
|
|
# valid C float literals (float() always stringifies with one, e.g.
|
|
# "0.0"/"1.0").
|
|
rf, gf, bf, af = (float(ch) for ch in (r, g, b, a))
|
|
macro = "COLOR_" + name.upper()
|
|
camel = "".join(p[0].upper() + p[1:].lower() for p in name.split("_"))
|
|
|
|
out += [
|
|
f"// {name}",
|
|
f"#define {macro}_4B color4b({r8}, {g8}, {b8}, {a8})",
|
|
f"#define {macro}_3B color3b({r8}, {g8}, {b8})",
|
|
f"#define {macro}_3F color3f({rf}f, {gf}f, {bf}f)",
|
|
f"#define {macro}_4F color4f({rf}f, {gf}f, {bf}f, {af}f)",
|
|
f"#define {macro} {macro}_4B",
|
|
"",
|
|
]
|
|
js += [
|
|
f"Color.{name.lower()} = function() {{",
|
|
f" return new Color({r8}, {g8}, {b8}, {a8});",
|
|
"};",
|
|
"",
|
|
]
|
|
|
|
out.append("// JS color functions")
|
|
out.append("#define COLOR_SCRIPT \\")
|
|
for line in "\n".join(js).rstrip().splitlines():
|
|
out.append(f' "{line}\\n" \\')
|
|
out[-1] = out[-1].rstrip(" \\")
|
|
out.append("")
|
|
|
|
os.makedirs(os.path.dirname(args.output), exist_ok=True)
|
|
with open(args.output, "w", encoding="utf-8") as f:
|
|
f.write("\n".join(out))
|