This commit is contained in:
vetusmagnus
2026-10-13 20:04:31 -04:00
commit 28916eaf7d
208 changed files with 23516 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+73
View File
@@ -0,0 +1,73 @@
/* Movie playback (RAC1 Wrapper).
*
* The game plays its movies (the intro, the cut-scenes) with Sony's sample
* player (src/game/movie: disc streaming, the IPU, EE threads, the SPU),
* entered through func_0023B670 (host/overrides). On the PC the whole call is
* replaced: the movie's sectors are read from the disc image, the MPEG-2
* video is decoded with pl_mpeg2.h and shown at the movie's frame rate with
* its sound (PS2 ADPCM, the language track the game asks for). A skip key
* ends it early, like a pad button on the PS2. Shared with the UYA Wrapper. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PL_MPEG_IMPLEMENTATION
#include "pl_mpeg2.h"
#include "pss.h"
#include "uya_runtime.h"
int uya_movie_play_track(uint32_t lsn, uint32_t bytes, int track)
{
uint32_t sectors = (bytes + 2047) / 2048;
uint8_t *ps = malloc((size_t)sectors * 2048), *es = 0;
int skipped = 0, frames = 0;
if (!ps || uya_disc_read(lsn, sectors, ps) != 0) { uya_log("movie at LSN %u: disc read failed", lsn); free(ps); return 0; }
es = malloc((size_t)sectors * 2048);
size_t n = pss_extract_video(ps, (size_t)sectors * 2048, es, (size_t)sectors * 2048);
int16_t *pcm = 0;
size_t pcm_frames = 0;
int rate = 0;
{
uint8_t *au = malloc((size_t)sectors * 2048);
size_t an = au ? pss_extract_audio(ps, (size_t)sectors * 2048, track, au, (size_t)sectors * 2048) : 0;
if (!an && track && au) an = pss_extract_audio(ps, (size_t)sectors * 2048, 0, au, (size_t)sectors * 2048);
if (an) pcm_frames = pss_decode_audio(au, an, &pcm, &rate);
free(au);
}
free(ps);
plm_video_t *v = plm_video_create_with_buffer(plm_buffer_create_with_memory(es, n, 0), 1);
int w = plm_video_get_width(v), h = plm_video_get_height(v);
double fps = plm_video_get_framerate(v);
if (w <= 0 || fps <= 0) { uya_log("movie at LSN %u: no video", lsn); plm_video_destroy(v); free(es); return 0; }
uya_log("movie at LSN %u: %ux%u at %.2f fps, %u KB; sound track %d: %s%.1f s at %d Hz", lsn, w, h, fps, (unsigned)(n / 1024), track,
pcm_frames ? "" : "none ", pcm_frames && rate ? (double)pcm_frames / rate : 0.0, rate);
uint32_t *px = malloc((size_t)w * h * 4);
char tag[32];
snprintf(tag, sizeof tag, "movie_%u", lsn);
if (pcm_frames) plat_audio_play(pcm, pcm_frames, rate, tag);
plat_movie_started();
uint64_t t0 = plat_time_us();
int nowait = getenv("UYA_NOWAIT") != 0, dropped = 0;
plm_frame_t *f;
while ((f = plm_video_decode(v))) {
uint64_t due = t0 + (uint64_t)(frames * 1000000.0 / fps), now = plat_time_us();
int late = !nowait && now > due + (uint64_t)(1000000.0 / fps); /* a frame behind the sound: not shown, to catch up */
if (due > now && !nowait) plat_sleep_us(due - now);
if (!late) {
plm_frame_to_bgra(f, (uint8_t *)px, w * 4);
{ /* GL mode: the GS thread draws it into the window */
extern int uya_gs_movie_frame(const uint32_t *px, int w, int h);
int shown = uya_gs_movie_frame(px, w, h);
if (!plat_present(shown ? 0 : px, w, h)) { uya_log("window closed"); exit(0); }
}
} else dropped++;
frames++;
if (plat_key_skip()) { skipped = 1; break; }
}
plat_audio_stop();
free(pcm);
uya_log("movie at LSN %u: %d frames shown%s (%d dropped to keep up with the sound)", lsn, frames - dropped, skipped ? " (skipped)" : "", dropped);
free(px);
plm_video_destroy(v);
free(es);
return skipped;
}
+111
View File
@@ -0,0 +1,111 @@
/* pss.h: MPEG-2 program stream (PS2 .pss) demuxer: pulls the video elementary
* stream (stream id 0xE0) out of pack/PES framing. */
#ifndef UYA_PSS_H
#define UYA_PSS_H
#include <stdint.h>
#include <stddef.h>
/* Appends the payload of every video PES packet in `ps` to `out` (capacity
* `cap`); returns the number of bytes written. */
static size_t pss_extract_video(const uint8_t *ps, size_t n, uint8_t *out, size_t cap)
{
size_t i = 0, w = 0;
while (i + 4 <= n) {
if (ps[i] != 0 || ps[i + 1] != 0 || ps[i + 2] != 1) { i++; continue; }
uint8_t id = ps[i + 3];
if (id == 0xBA) { /* pack header */
if ((ps[i + 4] & 0xC0) == 0x40) i += 14 + (ps[i + 13] & 7); /* MPEG-2 */
else i += 12; /* MPEG-1 */
continue;
}
if (id == 0xB9) { i += 4; continue; } /* program end */
if (id < 0xBB) { i++; continue; } /* not a PES/system packet */
if (i + 6 > n) break;
size_t len = ((size_t)ps[i + 4] << 8) | ps[i + 5];
size_t end = i + 6 + len;
if (end > n) end = n;
if (id == 0xE0 && (ps[i + 6] & 0xC0) == 0x80) { /* MPEG-2 PES header */
size_t p = i + 9 + ps[i + 8];
if (p < end && w + (end - p) <= cap) { memcpy(out + w, ps + p, end - p); w += end - p; }
}
i = end;
}
return w;
}
/* Audio: private stream 1 (0xBD). Each payload starts with a 4-byte substream
* header (ff a1 00 0N, N = the track; the movies carry several language
* tracks). The track data is an "SShd" header then "SSbd" and PS2 ADPCM.
* Appends track `track`'s bytes to `out`; returns the count. */
static size_t pss_extract_audio(const uint8_t *ps, size_t n, int track, uint8_t *out, size_t cap)
{
size_t i = 0, w = 0;
while (i + 4 <= n) {
if (ps[i] != 0 || ps[i + 1] != 0 || ps[i + 2] != 1) { i++; continue; }
uint8_t id = ps[i + 3];
if (id == 0xBA) { i += ((ps[i + 4] & 0xC0) == 0x40) ? 14 + (ps[i + 13] & 7) : 12; continue; }
if (id == 0xB9) { i += 4; continue; }
if (id < 0xBB) { i++; continue; }
if (i + 6 > n) break;
size_t len = ((size_t)ps[i + 4] << 8) | ps[i + 5];
size_t end = i + 6 + len;
if (end > n) end = n;
if (id == 0xBD && (ps[i + 6] & 0xC0) == 0x80) {
size_t p = i + 9 + ps[i + 8];
if (p + 4 <= end && ps[p] == 0xFF && ps[p + 3] == track) {
p += 4;
if (w + (end - p) <= cap) { memcpy(out + w, ps + p, end - p); w += end - p; }
}
}
i = end;
}
return w;
}
static uint32_t pss_le32(const uint8_t *p) { return p[0] | (p[1] << 8) | (p[2] << 16) | ((uint32_t)p[3] << 24); }
/* Decodes an SShd/SSbd track (PS2 ADPCM, channels interleaved in blocks of
* `interleave` bytes) into interleaved 16-bit stereo. Returns frames, or 0. */
static size_t pss_decode_audio(const uint8_t *a, size_t n, int16_t **out, int *rate)
{
static const int fk[5][2] = {{0, 0}, {60, 0}, {115, -52}, {98, -55}, {122, -60}};
if (n < 0x28 || memcmp(a, "SShd", 4) || pss_le32(a + 8) != 0x10) return 0;
uint32_t hsz = pss_le32(a + 4), ch = pss_le32(a + 16), il = pss_le32(a + 20);
*rate = (int)pss_le32(a + 12);
const uint8_t *b = a + 8 + hsz;
if (b + 8 > a + n || memcmp(b, "SSbd", 4) || ch < 1 || ch > 2 || il < 16 || il % 16) return 0;
size_t len = pss_le32(b + 4);
b += 8;
if (len > (size_t)(a + n - b)) len = (size_t)(a + n - b);
size_t blocks = len / (il * ch), per = il / 16 * 28;
size_t frames = blocks * per;
int16_t *o = (int16_t *)malloc(frames * 4 + 4);
if (!o) return 0;
int hist[2][2] = {{0, 0}, {0, 0}};
for (size_t k = 0; k < blocks; k++)
for (uint32_t c = 0; c < ch; c++) {
const uint8_t *blk = b + (k * ch + c) * il;
for (uint32_t f = 0; f < il / 16; f++) {
const uint8_t *fr = blk + f * 16;
int sh = fr[0] & 15, flt = (fr[0] >> 4) & 7;
if (flt > 4) flt = 0;
if (sh > 12) sh = 9;
for (int s = 0; s < 28; s++) {
int nib = (fr[2 + s / 2] >> ((s & 1) * 4)) & 15;
if (nib >= 8) nib -= 16;
int v = (nib << 12) >> sh;
v += (hist[c][0] * fk[flt][0] + hist[c][1] * fk[flt][1] + 32) >> 6;
if (v > 32767) v = 32767;
if (v < -32768) v = -32768;
hist[c][1] = hist[c][0];
hist[c][0] = v;
size_t idx = (k * per + f * 28 + s) * 2;
o[idx + c] = (int16_t)v;
if (ch == 1) o[idx + 1] = (int16_t)v;
}
}
}
*out = o;
return frames;
}
#endif