Initial public source release

Split reusable rendering and format support into vectorail-core and vectorail-gc.
This commit is contained in:
2026-08-02 17:05:27 +02:00
commit 831d96e562
109 changed files with 20558 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include <cstdint>
namespace openroller::psp {
constexpr char kSongCatalogMagic[4] = {'O', 'R', 'P', 'C'};
constexpr std::uint16_t kSongCatalogVersion = 1;
constexpr std::uint32_t kMaximumCatalogSongs = 64;
struct SongCatalogHeader {
char magic[4]{};
std::uint16_t version = 0;
std::uint16_t headerSize = 0;
std::uint32_t fileSize = 0;
std::uint32_t songCount = 0;
std::uint32_t recordSize = 0;
std::uint32_t reserved[3]{};
};
struct SongCatalogRecord {
char key[32]{};
char title[64]{};
char artist[48]{};
char duration[8]{};
char bpm[16]{};
std::uint8_t genre = 0;
std::uint8_t availableMask = 0;
std::uint8_t ratings[4]{};
std::uint8_t reserved[2]{};
};
constexpr char kJacketMagic[4] = {'O', 'R', 'P', 'J'};
constexpr std::uint16_t kJacketVersion = 1;
struct JacketHeader {
char magic[4]{};
std::uint16_t version = 0;
std::uint16_t width = 0;
std::uint16_t height = 0;
std::uint16_t pixelFormat = 0; // 0 = PSP GU_PSM_4444
std::uint32_t dataSize = 0;
};
static_assert(sizeof(SongCatalogHeader) == 32);
static_assert(sizeof(SongCatalogRecord) == 176);
static_assert(sizeof(JacketHeader) == 16);
} // namespace openroller::psp
+242
View File
@@ -0,0 +1,242 @@
#pragma once
#include <cstdint>
namespace openroller::psp {
constexpr char kStagePackageMagic[4] = {'O', 'R', 'P', 'S'};
constexpr std::uint16_t kStagePackageVersion = 4;
enum class PackageDifficulty : std::uint32_t {
Easy = 0,
Normal = 1,
Hard = 2,
Extra = 3,
};
constexpr std::uint32_t kStageDifficultyMask = 0x3u;
constexpr std::uint32_t kMaximumPackageBackgroundObjects = 1024u;
struct PackageSection {
std::uint32_t offset = 0;
std::uint32_t count = 0;
};
struct PackageRange {
std::uint32_t first = 0;
std::uint32_t count = 0;
};
// All package data is little-endian. Every section begins at a 16-byte
// boundary so it can be read directly into PSP-friendly arrays.
struct StagePackageHeader {
char magic[4]{};
std::uint16_t version = 0;
std::uint16_t headerSize = 0;
std::uint32_t fileSize = 0;
std::uint32_t flags = 0;
std::uint32_t durationMs = 0;
std::uint32_t audioOffsetRaw = 0;
float visualOffset = 0.0f;
float greatMinimumTimeMs = 32.0f;
float scratchEnableTimeMs = 250.0f;
float beatEnableTimeMs = 200.0f;
float backwardsDrawDistance = 0.0f;
float forwardDrawDistance = 0.0f;
std::uint32_t trackAheadRgba = 0;
std::uint32_t trackBehindRgba = 0;
PackageSection track;
PackageSection notes;
PackageSection cameras;
PackageSection drawDistances;
PackageSection backgroundColors;
PackageSection backgroundModels;
PackageSection backgroundVertices;
PackageSection backgroundObjects;
PackageSection visibilityKeys;
PackageSection transformKeys;
PackageSection objectColorKeys;
PackageSection particles;
PackageSection visualizer;
PackageSection bpmChanges;
};
struct PackageTrackPoint {
std::uint32_t timeMs = 0;
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
};
enum PackageNoteFlags : std::uint8_t {
kNoteTypeOverride = 1u << 0,
kNoteFlag24 = 1u << 1,
kNoteFlag37 = 1u << 2,
kNoteFlag38 = 1u << 3,
};
// This is the original note payload normalized to little-endian and with its
// four single-byte flags packed into one byte. Unknown values are retained so
// later reverse-engineering does not require rebuilding the source parser.
struct PackageNote {
std::uint32_t timeMs = 0;
std::uint8_t type = 0;
std::uint8_t flags = 0;
std::int16_t params16[9]{};
float params25[3]{};
float params39[4]{};
std::uint32_t params55[3]{};
float param67 = 0.0f;
std::uint32_t param71 = 0;
float params75[5]{};
std::uint32_t param95 = 0;
// Values produced by the original BuildTimingDataSub path. Keeping these
// in the host-built package avoids duplicating chart/config resolution on
// the PSP and makes its runtime consume the same semantics as desktop.
std::uint8_t effectiveType = 0;
std::uint8_t reserved = 0;
std::int16_t markEffectId = -1;
float appearTimeMs = 0.0f;
float endTimeMs = 0.0f;
float directionVector[3]{};
float beatDurationMs = 500.0f;
float earlyTimingMs = 250.0f;
float lateTimingMs = 250.0f;
float missTimingMs = 250.0f;
float muteTimingMs = 0.0f;
float markerFadeEndTimeMs = 0.0f;
std::uint32_t packedColor = 0xffffffffu;
std::uint32_t merryCount = 0;
};
struct PackageCameraPoint {
std::uint32_t timeMs = 0;
std::uint8_t aMode = 0;
std::uint8_t fMode = 0;
std::uint8_t projectionType = 0;
std::uint8_t reserved = 0;
float distance = 0.0f;
float rotationA[2]{};
float originOffset[3]{};
float fieldFar[3]{};
float fieldNear[3]{};
float rotationB = 0.0f;
};
struct PackageDrawDistancePoint {
std::uint32_t timeMs = 0;
float distance = 0.0f;
};
struct PackageBackgroundColorPoint {
std::uint32_t timeMs = 0;
std::uint32_t topRightRgba = 0;
std::uint32_t topLeftRgba = 0;
std::uint32_t bottomRightRgba = 0;
std::uint32_t bottomLeftRgba = 0;
std::uint32_t flags = 0;
};
struct PackageBackgroundModel {
PackageRange triangles;
PackageRange solidLines;
PackageRange wireframeLines;
};
struct PackageBackgroundVertex {
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
};
enum PackageBackgroundObjectFlags : std::uint32_t {
kBackgroundObjectWireframe = 1u << 0,
kBackgroundObjectFlashing = 1u << 1,
kBackgroundObjectUnknown = 1u << 2,
};
struct PackageBackgroundObject {
std::uint32_t model = 0;
std::int32_t parentIndex = -1;
std::uint32_t flags = 0;
std::uint32_t fragmentShader = 0;
float position[3]{};
float scale[3]{1.0f, 1.0f, 1.0f};
float rotation[3]{};
float color[4]{1.0f, 1.0f, 1.0f, 1.0f};
PackageRange visibility;
PackageRange movement;
PackageRange scaling;
PackageRange rotations;
PackageRange colorChanges;
};
enum PackageObjectKeyFlags : std::uint8_t {
kObjectKeyLoop = 1u << 0,
kObjectKeyInterpolate = 1u << 1,
};
struct PackageVisibilityKey {
std::uint32_t timeMs = 0;
std::uint8_t flags = 0;
std::uint8_t visible = 0;
std::uint16_t reserved = 0;
};
struct PackageTransformKey {
std::uint32_t timeMs = 0;
std::uint8_t flags = 0;
std::uint8_t reserved[3]{};
float value[3]{};
};
struct PackageObjectColorKey {
std::uint32_t timeMs = 0;
std::uint8_t flags = 0;
std::uint8_t reserved[3]{};
std::uint32_t rgba = 0;
};
struct PackageParticlePoint {
std::uint32_t timeMs = 0;
std::uint32_t enabled = 0;
std::uint32_t shape = 0;
std::uint32_t texture = 0;
std::uint32_t rgba = 0;
float velocity[3]{};
float repeatMeasure = 0.0f;
float lifespanMeasure = 0.0f;
std::uint32_t groupShapeSize = 0;
};
struct PackageVisualizerPoint {
std::uint32_t timeMs = 0;
std::uint32_t type = 0;
std::uint32_t rgba = 0;
};
struct PackageBpmPoint {
std::uint32_t timeMs = 0;
std::uint32_t bpm = 120;
};
static_assert(sizeof(PackageSection) == 8);
static_assert(sizeof(PackageRange) == 8);
static_assert(sizeof(StagePackageHeader) == 168);
static_assert(sizeof(PackageTrackPoint) == 16);
static_assert(sizeof(PackageNote) == 152);
static_assert(sizeof(PackageCameraPoint) == 60);
static_assert(sizeof(PackageDrawDistancePoint) == 8);
static_assert(sizeof(PackageBackgroundColorPoint) == 24);
static_assert(sizeof(PackageBackgroundModel) == 24);
static_assert(sizeof(PackageBackgroundVertex) == 12);
static_assert(sizeof(PackageBackgroundObject) == 108);
static_assert(sizeof(PackageVisibilityKey) == 8);
static_assert(sizeof(PackageTransformKey) == 20);
static_assert(sizeof(PackageObjectColorKey) == 12);
static_assert(sizeof(PackageParticlePoint) == 44);
static_assert(sizeof(PackageVisualizerPoint) == 12);
static_assert(sizeof(PackageBpmPoint) == 8);
} // namespace openroller::psp