mirror of
https://github.com/luksamuk/engine-psx.git
synced 2025-04-28 13:28:02 +03:00
Remove any traces of FMVs, .STR playback and MDEC usage
This commit is contained in:
parent
43fa3cd3fe
commit
5a5ca5ce62
18 changed files with 3 additions and 614 deletions
7
assets/fmv/.gitignore
vendored
7
assets/fmv/.gitignore
vendored
|
@ -1,7 +0,0 @@
|
||||||
*.avi
|
|
||||||
*.AVI
|
|
||||||
*.wav
|
|
||||||
*.WAV
|
|
||||||
*.xa
|
|
||||||
*.XA
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,6 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
for f in *.mp4; do
|
|
||||||
psxavenc -t str2 -f 37800 -b 4 -c 2 -s 320x240 -r 15 -x 2 "$f" "${f%%.mp4}.STR"
|
|
||||||
done
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PLAYBACK_XA,
|
PLAYBACK_XA,
|
||||||
PLAYBACK_STR,
|
|
||||||
} PlaybackType;
|
} PlaybackType;
|
||||||
|
|
||||||
void cd_set_callbacks(PlaybackType type);
|
void cd_set_callbacks(PlaybackType type);
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
#ifndef MDEC_H
|
|
||||||
#define MDEC_H
|
|
||||||
|
|
||||||
#include "render.h"
|
|
||||||
#include <psxcd.h>
|
|
||||||
|
|
||||||
//#define BLOCK_SIZE 24
|
|
||||||
#define BLOCK_SIZE 16
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint16_t width, height;
|
|
||||||
uint32_t bs_data[0x2000]; // Bitstream data read from the disc
|
|
||||||
uint32_t mdec_data[0x8000]; // Decompressed data to be fed to the MDEC
|
|
||||||
} StreamBuffer;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
StreamBuffer frames[2];
|
|
||||||
uint32_t slices[2][BLOCK_SIZE * SCREEN_YRES / 2];
|
|
||||||
|
|
||||||
int frame_id, sector_count;
|
|
||||||
int dropped_frames;
|
|
||||||
RECT slice_pos;
|
|
||||||
int frame_width;
|
|
||||||
|
|
||||||
volatile int8_t sector_pending, frame_ready;
|
|
||||||
volatile int8_t cur_frame, cur_slice;
|
|
||||||
} StreamContext;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint16_t magic; // Always 0x0160
|
|
||||||
uint16_t type; // 0x8001 for MDEC
|
|
||||||
uint16_t sector_id; // Chunk number (0 = first chunk of this frame)
|
|
||||||
uint16_t sector_count; // Total number of chunks for this frame
|
|
||||||
uint32_t frame_id; // Frame number
|
|
||||||
uint32_t bs_length; // Total length of this frame in bytes
|
|
||||||
|
|
||||||
uint16_t width, height;
|
|
||||||
uint8_t bs_header[8];
|
|
||||||
uint32_t _reserved;
|
|
||||||
} STR_Header;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
FMV_SONICTEAM = 0,
|
|
||||||
FMV_PS30YRS = 1,
|
|
||||||
FMV_NUM_VIDEOS = FMV_PS30YRS + 1,
|
|
||||||
} FMVOption;
|
|
||||||
|
|
||||||
// Load all locations of FMVs on disc
|
|
||||||
void mdec_fmv_init();
|
|
||||||
|
|
||||||
// Use this function as main playback entrypoint.
|
|
||||||
// It will override the game loop and be stuck in a playback loop.
|
|
||||||
// Do not call the other functions directly unless you know what you're doing.
|
|
||||||
void mdec_play(FMVOption);
|
|
||||||
|
|
||||||
void mdec_start(volatile CdlLOC *loc);
|
|
||||||
void mdec_stop();
|
|
||||||
void mdec_loop();
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -7,7 +7,6 @@ typedef enum {
|
||||||
SCREEN_DISCLAIMER,
|
SCREEN_DISCLAIMER,
|
||||||
SCREEN_LEVELSELECT,
|
SCREEN_LEVELSELECT,
|
||||||
SCREEN_LEVEL,
|
SCREEN_LEVEL,
|
||||||
/* SCREEN_FMV, */
|
|
||||||
SCREEN_TITLE,
|
SCREEN_TITLE,
|
||||||
SCREEN_MODELTEST,
|
SCREEN_MODELTEST,
|
||||||
SCREEN_SLIDE,
|
SCREEN_SLIDE,
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
#ifndef SCREENS_FMV_H
|
|
||||||
#define SCREENS_FMV_H
|
|
||||||
|
|
||||||
#include "screen.h"
|
|
||||||
#include "mdec.h"
|
|
||||||
|
|
||||||
void screen_fmv_load();
|
|
||||||
void screen_fmv_unload(void *);
|
|
||||||
void screen_fmv_update(void *);
|
|
||||||
void screen_fmv_draw(void *);
|
|
||||||
|
|
||||||
void screen_fmv_set_next(ScreenIndex next);
|
|
||||||
void screen_fmv_enqueue(FMVOption);
|
|
||||||
|
|
||||||
#endif
|
|
9
iso.xml
9
iso.xml
|
@ -350,15 +350,6 @@
|
||||||
source="${PROJECT_SOURCE_DIR}/assets/bgm/EVENT001.XA" />
|
source="${PROJECT_SOURCE_DIR}/assets/bgm/EVENT001.XA" />
|
||||||
</dir>
|
</dir>
|
||||||
|
|
||||||
<!-- <dir name="FMV"> -->
|
|
||||||
<!-- <file name="SONICT.STR" -->
|
|
||||||
<!-- type="mixed" -->
|
|
||||||
<!-- source="${PROJECT_SOURCE_DIR}/assets/fmv/SONICT.STR" /> -->
|
|
||||||
<!-- <file name="PS30YRS.STR" -->
|
|
||||||
<!-- type="mixed" -->
|
|
||||||
<!-- source="${PROJECT_SOURCE_DIR}/assets/fmv/PS30YRS.STR" /> -->
|
|
||||||
<!-- </dir> -->
|
|
||||||
|
|
||||||
<dummy sectors="1024"/>
|
<dummy sectors="1024"/>
|
||||||
</directory_tree>
|
</directory_tree>
|
||||||
</track>
|
</track>
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
#include <psxetc.h>
|
#include <psxetc.h>
|
||||||
|
|
||||||
extern void _xa_cd_event_callback(CdlIntrResult, uint8_t *);
|
extern void _xa_cd_event_callback(CdlIntrResult, uint8_t *);
|
||||||
/* extern void _mdec_cd_event_callback(CdlIntrResult, uint8_t *); */
|
|
||||||
/* extern void _mdec_dma_callback(void); */
|
|
||||||
|
|
||||||
void
|
void
|
||||||
cd_set_callbacks(PlaybackType type)
|
cd_set_callbacks(PlaybackType type)
|
||||||
|
@ -15,10 +13,6 @@ cd_set_callbacks(PlaybackType type)
|
||||||
case PLAYBACK_XA:
|
case PLAYBACK_XA:
|
||||||
CdReadyCallback(_xa_cd_event_callback);
|
CdReadyCallback(_xa_cd_event_callback);
|
||||||
break;
|
break;
|
||||||
case PLAYBACK_STR:
|
|
||||||
/* DMACallback(1, &_mdec_dma_callback); */
|
|
||||||
/* CdReadyCallback(_mdec_cd_event_callback); */
|
|
||||||
break;
|
|
||||||
default: break; // ???????????
|
default: break; // ???????????
|
||||||
}
|
}
|
||||||
ExitCriticalSection();
|
ExitCriticalSection();
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include "memalloc.h"
|
#include "memalloc.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "basic_font.h"
|
#include "basic_font.h"
|
||||||
/* #include "mdec.h" */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Locations of common textures on frame buffer:
|
Locations of common textures on frame buffer:
|
||||||
|
@ -69,7 +68,6 @@ main(void)
|
||||||
// Initial loads from disc
|
// Initial loads from disc
|
||||||
render_loading_logo();
|
render_loading_logo();
|
||||||
sound_bgm_init();
|
sound_bgm_init();
|
||||||
/* mdec_fmv_init(); */
|
|
||||||
|
|
||||||
// Set first scene
|
// Set first scene
|
||||||
scene_change(SCREEN_DISCLAIMER);
|
scene_change(SCREEN_DISCLAIMER);
|
||||||
|
|
376
src/mdec.c.old
376
src/mdec.c.old
|
@ -1,376 +0,0 @@
|
||||||
#include "mdec.h"
|
|
||||||
#include <psxpress.h>
|
|
||||||
#include <psxapi.h>
|
|
||||||
#include <psxetc.h>
|
|
||||||
#include <psxcd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "input.h"
|
|
||||||
|
|
||||||
#include "cd_callback.h"
|
|
||||||
#include "memalloc.h"
|
|
||||||
#include "screen.h"
|
|
||||||
|
|
||||||
#define VRAM_X_COORD(x) ((x) * BLOCK_SIZE / 16)
|
|
||||||
|
|
||||||
#define MAX_FRAME_BUFFER_ATTEMPTS 10
|
|
||||||
|
|
||||||
// Since the stream context in this case can use approx. 320K of RAM,
|
|
||||||
// it seems like a better choice to let it live on the heap.
|
|
||||||
// No need to manage this using an arena allocator, though.
|
|
||||||
static volatile StreamContext *str_ctx = NULL;
|
|
||||||
|
|
||||||
// MDEC lookup table, generally lives on the scratchpad
|
|
||||||
// Using v2 because it looks much better after AVI conversion!
|
|
||||||
static volatile VLC_TableV2 *lookup_table;
|
|
||||||
|
|
||||||
// Temporary area for CD sectors read from CD.
|
|
||||||
// Accessed by DMA, therefore must not be on heap
|
|
||||||
static volatile STR_Header sector_header;
|
|
||||||
|
|
||||||
static int decode_errors;
|
|
||||||
static int frame_time;
|
|
||||||
static volatile int should_abort;
|
|
||||||
|
|
||||||
// Extern structure and functions related to render buffers
|
|
||||||
extern RenderContext ctx;
|
|
||||||
extern int debug_mode;
|
|
||||||
extern void render_mdec_prepare(void);
|
|
||||||
extern void render_mdec_dispose(void);
|
|
||||||
|
|
||||||
// Forward declarations
|
|
||||||
void _mdec_dma_callback(void);
|
|
||||||
void _mdec_cd_event_callback(CdlIntrResult, uint8_t *);
|
|
||||||
void _mdec_cd_sector_handler(void);
|
|
||||||
StreamBuffer *_mdec_get_next_frame(void);
|
|
||||||
int _mdec_should_abort();
|
|
||||||
void _mdec_swap_buffers();
|
|
||||||
|
|
||||||
// FMV table register
|
|
||||||
typedef struct {
|
|
||||||
const char *filename;
|
|
||||||
CdlLOC loc;
|
|
||||||
} FMVTableEntry;
|
|
||||||
|
|
||||||
static volatile FMVTableEntry fmv_table[] = {
|
|
||||||
{ "\\FMV\\SONICT.STR;1", (CdlLOC){ 0 } },
|
|
||||||
{ "\\FMV\\PS30YRS.STR;1", (CdlLOC){ 0 } },
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
mdec_fmv_init()
|
|
||||||
{
|
|
||||||
CdlFILE file;
|
|
||||||
for(int i = 0; i < FMV_NUM_VIDEOS; i++) {
|
|
||||||
// Find file on CD.
|
|
||||||
// We won't be using the util.h library since we need to find
|
|
||||||
// the actual file position to stream
|
|
||||||
if(!CdSearchFile((CdlFILE *)&file, fmv_table[i].filename)) {
|
|
||||||
printf("Could not find .STR file %s.\n", fmv_table[i].filename);
|
|
||||||
fmv_table[i].loc = (CdlLOC){ 0 };
|
|
||||||
}
|
|
||||||
fmv_table[i].loc = file.pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
mdec_play(FMVOption option)
|
|
||||||
{
|
|
||||||
mdec_start(&fmv_table[option].loc);
|
|
||||||
mdec_loop();
|
|
||||||
mdec_stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
mdec_start(volatile CdlLOC *loc)
|
|
||||||
{
|
|
||||||
printf("Preparing MDEC playback.\n");
|
|
||||||
should_abort = 0;
|
|
||||||
DecDCTReset(0);
|
|
||||||
|
|
||||||
render_mdec_prepare();
|
|
||||||
|
|
||||||
// Hook MDEC callbacks
|
|
||||||
cd_set_callbacks(PLAYBACK_STR);
|
|
||||||
|
|
||||||
// Copy MDEC lookup table to scratchpad
|
|
||||||
lookup_table = fastalloc_malloc(sizeof(VLC_TableV2));
|
|
||||||
DecDCTvlcCopyTableV2((VLC_TableV2 *)lookup_table);
|
|
||||||
|
|
||||||
// Setup stream context.
|
|
||||||
// Use current screen storage for that
|
|
||||||
if(!str_ctx) str_ctx = screen_alloc(sizeof(volatile StreamContext));
|
|
||||||
|
|
||||||
str_ctx->cur_frame = 0;
|
|
||||||
str_ctx->cur_slice = 0;
|
|
||||||
|
|
||||||
decode_errors = 0;
|
|
||||||
frame_time = 1;
|
|
||||||
|
|
||||||
// Prepare context
|
|
||||||
str_ctx->frame_id = -1;
|
|
||||||
str_ctx->dropped_frames = 0;
|
|
||||||
str_ctx->sector_pending = 0;
|
|
||||||
str_ctx->frame_ready = 0;
|
|
||||||
|
|
||||||
// Read at 2x speed to play any XA-ADPCM sectors that could be
|
|
||||||
// interleaved with the data
|
|
||||||
// Start reading in real-time mode (doesn't retry in case of errors).
|
|
||||||
uint8_t mode = CdlModeRT | CdlModeSpeed | CdlModeAP;
|
|
||||||
CdControl(CdlSetmode, (const uint8_t *)&mode, 0);
|
|
||||||
CdControlF(CdlReadS, (const void *)loc);
|
|
||||||
|
|
||||||
// Wait for first frame to be buffered.
|
|
||||||
_mdec_get_next_frame();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
mdec_stop()
|
|
||||||
{
|
|
||||||
// Force playback end on context
|
|
||||||
cd_detach_callbacks();
|
|
||||||
str_ctx->frame_ready = -1;
|
|
||||||
CdControl(CdlPause, 0, 0);
|
|
||||||
|
|
||||||
if(str_ctx) {
|
|
||||||
str_ctx = NULL;
|
|
||||||
}
|
|
||||||
fastalloc_free();
|
|
||||||
render_mdec_dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
mdec_loop()
|
|
||||||
{
|
|
||||||
int frame_start, decode_time, cpu_usage;
|
|
||||||
frame_start = decode_time = cpu_usage = 0;
|
|
||||||
|
|
||||||
printf("Starting MDEC playback.\n");
|
|
||||||
while(1) {
|
|
||||||
if(debug_mode) frame_start = TIMER_VALUE(1);
|
|
||||||
|
|
||||||
// Wait for a full frame read from disc
|
|
||||||
StreamBuffer *frame = _mdec_get_next_frame();
|
|
||||||
if(!frame) { // If no frame was fetched, assume that playback ended
|
|
||||||
should_abort = 1;
|
|
||||||
printf("MDEC playback ended\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_mdec_should_abort()) {
|
|
||||||
printf("MDEC playback aborted\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((pad_pressing(PAD_L1) && pad_pressed(PAD_R1)) ||
|
|
||||||
(pad_pressed(PAD_L1) && pad_pressing(PAD_R1))) {
|
|
||||||
debug_mode = (debug_mode + 1) % 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(debug_mode) decode_time = TIMER_VALUE(1);
|
|
||||||
|
|
||||||
// Decompress bitstream into a format expected by the MDEC
|
|
||||||
VLC_Context vlc_ctx;
|
|
||||||
if(DecDCTvlcStart(
|
|
||||||
&vlc_ctx,
|
|
||||||
frame->mdec_data,
|
|
||||||
sizeof(frame->mdec_data) / 4,
|
|
||||||
frame->bs_data))
|
|
||||||
{
|
|
||||||
decode_errors++;
|
|
||||||
printf("Error decoding FMV! (#%d)\n", decode_errors);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(debug_mode) {
|
|
||||||
decode_time = (TIMER_VALUE(1) - decode_time) & 0xffff;
|
|
||||||
cpu_usage = decode_time * 100 / frame_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for MDEC to finish decoding the previous frame
|
|
||||||
// and manually flip the framebuffers
|
|
||||||
VSync(0);
|
|
||||||
DecDCTinSync(0);
|
|
||||||
DecDCToutSync(0);
|
|
||||||
|
|
||||||
if(debug_mode) {
|
|
||||||
// Draw overlay
|
|
||||||
FntPrint(-1, "FRAME:%6d READ ERRORS: %6d\n",
|
|
||||||
str_ctx->frame_id, str_ctx->dropped_frames);
|
|
||||||
FntPrint(-1, "CPU: %6d%% DECODE ERRORS:%6d\n",
|
|
||||||
cpu_usage, decode_errors);
|
|
||||||
FntFlush(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manual buffer swap
|
|
||||||
_mdec_swap_buffers();
|
|
||||||
|
|
||||||
// Feed newly compressed frame to the MDEC.
|
|
||||||
// The MDEC will not actually start decoding it until an output
|
|
||||||
// buffer is also configured by calling DecDCTout().
|
|
||||||
//DecDCTin(frame->mdec_data, DECDCT_MODE_24BPP);
|
|
||||||
DecDCTin(frame->mdec_data, DECDCT_MODE_16BPP);
|
|
||||||
|
|
||||||
// Place frame at the center of the currently active framebuffer,
|
|
||||||
// then start decoding the first slice.
|
|
||||||
// Decoded slices will be uploaded to VRAM in the background
|
|
||||||
// by _mdec_dma_callback().
|
|
||||||
RECT *clip = render_get_buffer_clip();
|
|
||||||
int offsetx = (clip->w - frame->width) >> 1;
|
|
||||||
int offsety = (clip->h - frame->height) >> 1;
|
|
||||||
|
|
||||||
str_ctx->slice_pos.x = clip->x + VRAM_X_COORD(offsetx);
|
|
||||||
str_ctx->slice_pos.y = clip->y + offsety;
|
|
||||||
str_ctx->slice_pos.w = BLOCK_SIZE;
|
|
||||||
str_ctx->slice_pos.h = frame->height;
|
|
||||||
str_ctx->frame_width = VRAM_X_COORD(frame->width);
|
|
||||||
|
|
||||||
DecDCTout(
|
|
||||||
(uint32_t *)str_ctx->slices[str_ctx->cur_slice],
|
|
||||||
BLOCK_SIZE * str_ctx->slice_pos.h / 2);
|
|
||||||
|
|
||||||
if(debug_mode) frame_time = (TIMER_VALUE(1) - frame_start) & 0xffff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ========================================== */
|
|
||||||
|
|
||||||
// DMA callback for MDEC transfers.
|
|
||||||
// NOTE: This is leveraged from cd_callback.h
|
|
||||||
void
|
|
||||||
_mdec_dma_callback(void)
|
|
||||||
{
|
|
||||||
// Handle sectors that were not processed by _mdec_cd_sector_handler
|
|
||||||
// while a DMA transfer is in progress.
|
|
||||||
// As the MDEC has just finished decoding a slice, they can be
|
|
||||||
// safely handled now
|
|
||||||
if(str_ctx->sector_pending) {
|
|
||||||
_mdec_cd_sector_handler();
|
|
||||||
str_ctx->sector_pending = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload the decoded slice to VRAM and start decoding
|
|
||||||
// the next slice into another buffer, if any
|
|
||||||
LoadImage((const RECT *)&str_ctx->slice_pos, (const uint32_t *)str_ctx->slices[str_ctx->cur_slice]);
|
|
||||||
|
|
||||||
str_ctx->cur_slice ^= 0x1;
|
|
||||||
str_ctx->slice_pos.x += BLOCK_SIZE;
|
|
||||||
|
|
||||||
if(str_ctx->slice_pos.x < str_ctx->frame_width) {
|
|
||||||
DecDCTout(
|
|
||||||
(uint32_t *)str_ctx->slices[str_ctx->cur_slice],
|
|
||||||
BLOCK_SIZE * str_ctx->slice_pos.h / 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CD event callback for FMV reading.
|
|
||||||
// NOTE: This is leveraged from cd_callback.h
|
|
||||||
void
|
|
||||||
_mdec_cd_event_callback(CdlIntrResult event, uint8_t *)
|
|
||||||
{
|
|
||||||
// Ignore events that are not that of a sector being ready
|
|
||||||
if(event != CdlDataReady) return;
|
|
||||||
|
|
||||||
// Only handle sectors immediately if the MDEC is not decoding
|
|
||||||
// a frame, otherwise defer handling to _mdec_dma_callback.
|
|
||||||
// This is a workaround for a hardware conflict between the
|
|
||||||
// DMA channels used for the CD drive and MDEC output, which
|
|
||||||
// do not run simultaneously.
|
|
||||||
if(DecDCTinSync(1)) str_ctx->sector_pending = 1;
|
|
||||||
else _mdec_cd_sector_handler();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// CD sector handler for processing sectors that were read from the CD.
|
|
||||||
void
|
|
||||||
_mdec_cd_sector_handler(void)
|
|
||||||
{
|
|
||||||
volatile StreamBuffer *frame = &str_ctx->frames[str_ctx->cur_frame];
|
|
||||||
|
|
||||||
// Fetch .STR header of the sector that has been read and
|
|
||||||
// make sure it is valid. If not, assume the file has ended
|
|
||||||
// and set frame_ready as a signal for the playback loop to
|
|
||||||
// stop playback.
|
|
||||||
CdGetSector((void *)§or_header, sizeof(STR_Header) >> 2);
|
|
||||||
if(sector_header.magic != 0x0160) {
|
|
||||||
str_ctx->frame_ready = -1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore any non-MDEC sectors.
|
|
||||||
if(sector_header.type != 0x8001) return;
|
|
||||||
|
|
||||||
// If this sector is part of a new frame, validate the sectors
|
|
||||||
// that have been read so far and flip the bistream data buffers.
|
|
||||||
// If the frame number is lower than the current one, assume the
|
|
||||||
// drive has started another .STR file and stop playback.
|
|
||||||
if((int)sector_header.frame_id < str_ctx->frame_id) {
|
|
||||||
str_ctx->frame_ready = -1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((int)sector_header.frame_id > str_ctx->frame_id) {
|
|
||||||
// Do not set ready flag if a sector has been missed.
|
|
||||||
if(str_ctx->sector_count) str_ctx->dropped_frames++;
|
|
||||||
else str_ctx->frame_ready = 1;
|
|
||||||
|
|
||||||
str_ctx->frame_id = sector_header.frame_id;
|
|
||||||
str_ctx->sector_count = sector_header.sector_count;
|
|
||||||
str_ctx->cur_frame ^= 0x1;
|
|
||||||
|
|
||||||
frame = &str_ctx->frames[str_ctx->cur_frame];
|
|
||||||
|
|
||||||
// Initialize the next frame; round up dimensions
|
|
||||||
// to the nearest multiple of 16 since the MDEC
|
|
||||||
// operates on 16x16 blocks
|
|
||||||
frame->width = (sector_header.width + 15) & 0xfff0;
|
|
||||||
frame->height = (sector_header.height + 15) & 0xfff0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append payload contained in this sector to the current buffer
|
|
||||||
str_ctx->sector_count--;
|
|
||||||
CdGetSector(
|
|
||||||
(void *)&(frame->bs_data[2016 / 4 * sector_header.sector_id]),
|
|
||||||
2016 / 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamBuffer *
|
|
||||||
_mdec_get_next_frame(void)
|
|
||||||
{
|
|
||||||
while(!str_ctx->frame_ready) {
|
|
||||||
__asm__ volatile("");
|
|
||||||
if(_mdec_should_abort()) {
|
|
||||||
should_abort = 1;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(str_ctx->frame_ready < 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
str_ctx->frame_ready = 0;
|
|
||||||
return (StreamBuffer *)&str_ctx->frames[str_ctx->cur_frame ^ 0x1];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
_mdec_should_abort()
|
|
||||||
{
|
|
||||||
// Call this when the CPU isn't doing anything or
|
|
||||||
// should check for user input.
|
|
||||||
pad_update();
|
|
||||||
return pad_pressed(PAD_CROSS) || pad_pressed(PAD_START);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
_mdec_swap_buffers()
|
|
||||||
{
|
|
||||||
ctx.active_buffer ^= 1;
|
|
||||||
DrawSync(0);
|
|
||||||
PutDrawEnv(&ctx.buffers[ctx.active_buffer].draw_env);
|
|
||||||
PutDispEnv(&ctx.buffers[ctx.active_buffer ^ 1].disp_env);
|
|
||||||
SetDispMask(1);
|
|
||||||
}
|
|
42
src/render.c
42
src/render.c
|
@ -4,7 +4,6 @@
|
||||||
#include <inline_c.h>
|
#include <inline_c.h>
|
||||||
|
|
||||||
RenderContext ctx;
|
RenderContext ctx;
|
||||||
static int using_mdec = 0;
|
|
||||||
|
|
||||||
void
|
void
|
||||||
setup_context()
|
setup_context()
|
||||||
|
@ -22,7 +21,6 @@ setup_context()
|
||||||
SetDefDispEnv(&ctx.buffers[1].disp_env, 0, SCREEN_YRES, SCREEN_XRES, SCREEN_YRES);
|
SetDefDispEnv(&ctx.buffers[1].disp_env, 0, SCREEN_YRES, SCREEN_XRES, SCREEN_YRES);
|
||||||
|
|
||||||
// Set the default background color and enable auto-clearing.
|
// Set the default background color and enable auto-clearing.
|
||||||
using_mdec = 0;
|
|
||||||
set_clear_color(0, 0, 0);
|
set_clear_color(0, 0, 0);
|
||||||
ctx.buffers[0].draw_env.isbg = 1;
|
ctx.buffers[0].draw_env.isbg = 1;
|
||||||
ctx.buffers[1].draw_env.isbg = 1;
|
ctx.buffers[1].draw_env.isbg = 1;
|
||||||
|
@ -89,9 +87,7 @@ swap_buffers()
|
||||||
// prevent screen tearing.
|
// prevent screen tearing.
|
||||||
DrawSync(0);
|
DrawSync(0);
|
||||||
|
|
||||||
// Don't use vsync here on MDEC since MDEC playback handles it
|
VSync(0);
|
||||||
// accordingly
|
|
||||||
if(!using_mdec) VSync(0);
|
|
||||||
|
|
||||||
RenderBuffer *draw_buffer = &ctx.buffers[ctx.active_buffer];
|
RenderBuffer *draw_buffer = &ctx.buffers[ctx.active_buffer];
|
||||||
RenderBuffer *disp_buffer = &ctx.buffers[ctx.active_buffer ^ 1];
|
RenderBuffer *disp_buffer = &ctx.buffers[ctx.active_buffer ^ 1];
|
||||||
|
@ -99,17 +95,14 @@ swap_buffers()
|
||||||
// Display the framebuffer the GPU has just finished drawing and start
|
// Display the framebuffer the GPU has just finished drawing and start
|
||||||
// rendering the display list that was filled up in the main loop.
|
// rendering the display list that was filled up in the main loop.
|
||||||
PutDispEnv(&disp_buffer->disp_env);
|
PutDispEnv(&disp_buffer->disp_env);
|
||||||
if(!using_mdec)
|
DrawOTagEnv(&draw_buffer->ot[OT_LENGTH - 1], &draw_buffer->draw_env);
|
||||||
DrawOTagEnv(&draw_buffer->ot[OT_LENGTH - 1], &draw_buffer->draw_env);
|
|
||||||
|
|
||||||
// Switch over to the next buffer, clear it and reset the packet allocation
|
// Switch over to the next buffer, clear it and reset the packet allocation
|
||||||
// pointer.
|
// pointer.
|
||||||
ctx.active_buffer ^= 1;
|
ctx.active_buffer ^= 1;
|
||||||
ctx.next_packet = disp_buffer->buffer;
|
ctx.next_packet = disp_buffer->buffer;
|
||||||
|
|
||||||
// No need if not using MDEC. Save those CPU cycles. :)
|
ClearOTagR(disp_buffer->ot, OT_LENGTH);
|
||||||
if(!using_mdec)
|
|
||||||
ClearOTagR(disp_buffer->ot, OT_LENGTH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
|
@ -168,35 +161,6 @@ render_get_buffer_clip(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
render_mdec_prepare(void)
|
|
||||||
{
|
|
||||||
using_mdec = 1;
|
|
||||||
|
|
||||||
// Disable buffer clearing to prevent flickering
|
|
||||||
ctx.buffers[0].draw_env.isbg = 0;
|
|
||||||
ctx.buffers[1].draw_env.isbg = 0;
|
|
||||||
|
|
||||||
// Set color mode to 24bpp
|
|
||||||
//ctx.buffers[0].disp_env.isrgb24 = 1;
|
|
||||||
//ctx.buffers[1].disp_env.isrgb24 = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
render_mdec_dispose(void)
|
|
||||||
{
|
|
||||||
using_mdec = 0;
|
|
||||||
|
|
||||||
// Enable buffer clearing
|
|
||||||
ctx.buffers[0].draw_env.isbg = 1;
|
|
||||||
ctx.buffers[1].draw_env.isbg = 1;
|
|
||||||
|
|
||||||
// Set color mode to 16bpp
|
|
||||||
//ctx.buffers[0].disp_env.isrgb24 = 0;
|
|
||||||
//ctx.buffers[1].disp_env.isrgb24 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
draw_quad(int16_t vx, int16_t vy,
|
draw_quad(int16_t vx, int16_t vy,
|
||||||
int16_t w, int16_t h,
|
int16_t w, int16_t h,
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include "screens/disclaimer.h"
|
#include "screens/disclaimer.h"
|
||||||
#include "screens/levelselect.h"
|
#include "screens/levelselect.h"
|
||||||
#include "screens/level.h"
|
#include "screens/level.h"
|
||||||
/* #include "screens/fmv.h" */
|
|
||||||
#include "screens/title.h"
|
#include "screens/title.h"
|
||||||
#include "screens/modeltest.h"
|
#include "screens/modeltest.h"
|
||||||
#include "screens/slide.h"
|
#include "screens/slide.h"
|
||||||
|
@ -76,7 +75,6 @@ scene_load()
|
||||||
case SCREEN_DISCLAIMER: screen_disclaimer_load(); break;
|
case SCREEN_DISCLAIMER: screen_disclaimer_load(); break;
|
||||||
case SCREEN_LEVELSELECT: screen_levelselect_load(); break;
|
case SCREEN_LEVELSELECT: screen_levelselect_load(); break;
|
||||||
case SCREEN_LEVEL: screen_level_load(); break;
|
case SCREEN_LEVEL: screen_level_load(); break;
|
||||||
/* case SCREEN_FMV: screen_fmv_load(); break; */
|
|
||||||
case SCREEN_TITLE: screen_title_load(); break;
|
case SCREEN_TITLE: screen_title_load(); break;
|
||||||
case SCREEN_MODELTEST: screen_modeltest_load(); break;
|
case SCREEN_MODELTEST: screen_modeltest_load(); break;
|
||||||
case SCREEN_SLIDE: screen_slide_load(); break;
|
case SCREEN_SLIDE: screen_slide_load(); break;
|
||||||
|
@ -93,7 +91,6 @@ scene_unload()
|
||||||
case SCREEN_DISCLAIMER: screen_disclaimer_unload(scene_data); break;
|
case SCREEN_DISCLAIMER: screen_disclaimer_unload(scene_data); break;
|
||||||
case SCREEN_LEVELSELECT: screen_levelselect_unload(scene_data); break;
|
case SCREEN_LEVELSELECT: screen_levelselect_unload(scene_data); break;
|
||||||
case SCREEN_LEVEL: screen_level_unload(scene_data); break;
|
case SCREEN_LEVEL: screen_level_unload(scene_data); break;
|
||||||
/* case SCREEN_FMV: screen_fmv_unload(scene_data); break; */
|
|
||||||
case SCREEN_TITLE: screen_title_unload(scene_data); break;
|
case SCREEN_TITLE: screen_title_unload(scene_data); break;
|
||||||
case SCREEN_MODELTEST: screen_modeltest_unload(scene_data); break;
|
case SCREEN_MODELTEST: screen_modeltest_unload(scene_data); break;
|
||||||
case SCREEN_SLIDE: screen_slide_unload(scene_data); break;
|
case SCREEN_SLIDE: screen_slide_unload(scene_data); break;
|
||||||
|
@ -110,7 +107,6 @@ scene_update()
|
||||||
case SCREEN_DISCLAIMER: screen_disclaimer_update(scene_data); break;
|
case SCREEN_DISCLAIMER: screen_disclaimer_update(scene_data); break;
|
||||||
case SCREEN_LEVELSELECT: screen_levelselect_update(scene_data); break;
|
case SCREEN_LEVELSELECT: screen_levelselect_update(scene_data); break;
|
||||||
case SCREEN_LEVEL: screen_level_update(scene_data); break;
|
case SCREEN_LEVEL: screen_level_update(scene_data); break;
|
||||||
/* case SCREEN_FMV: screen_fmv_update(scene_data); break; */
|
|
||||||
case SCREEN_TITLE: screen_title_update(scene_data); break;
|
case SCREEN_TITLE: screen_title_update(scene_data); break;
|
||||||
case SCREEN_MODELTEST: screen_modeltest_update(scene_data); break;
|
case SCREEN_MODELTEST: screen_modeltest_update(scene_data); break;
|
||||||
case SCREEN_SLIDE: screen_slide_update(scene_data); break;
|
case SCREEN_SLIDE: screen_slide_update(scene_data); break;
|
||||||
|
@ -128,7 +124,6 @@ scene_draw()
|
||||||
case SCREEN_DISCLAIMER: screen_disclaimer_draw(scene_data); break;
|
case SCREEN_DISCLAIMER: screen_disclaimer_draw(scene_data); break;
|
||||||
case SCREEN_LEVELSELECT: screen_levelselect_draw(scene_data); break;
|
case SCREEN_LEVELSELECT: screen_levelselect_draw(scene_data); break;
|
||||||
case SCREEN_LEVEL: screen_level_draw(scene_data); break;
|
case SCREEN_LEVEL: screen_level_draw(scene_data); break;
|
||||||
/* case SCREEN_FMV: screen_fmv_draw(scene_data); break; */
|
|
||||||
case SCREEN_TITLE: screen_title_draw(scene_data); break;
|
case SCREEN_TITLE: screen_title_draw(scene_data); break;
|
||||||
case SCREEN_MODELTEST: screen_modeltest_draw(scene_data); break;
|
case SCREEN_MODELTEST: screen_modeltest_draw(scene_data); break;
|
||||||
case SCREEN_SLIDE: screen_slide_draw(scene_data); break;
|
case SCREEN_SLIDE: screen_slide_draw(scene_data); break;
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
|
|
||||||
//#include "screens/fmv.h"
|
|
||||||
#include "screens/slide.h"
|
#include "screens/slide.h"
|
||||||
#include "screens/level.h"
|
#include "screens/level.h"
|
||||||
|
|
||||||
|
@ -53,9 +52,6 @@ screen_disclaimer_update(void *d)
|
||||||
// Change to level select
|
// Change to level select
|
||||||
scene_change(SCREEN_LEVELSELECT);
|
scene_change(SCREEN_LEVELSELECT);
|
||||||
} else {
|
} else {
|
||||||
//screen_fmv_set_next(SCREEN_TITLE);
|
|
||||||
//screen_fmv_enqueue(FMV_PS30YRS);
|
|
||||||
//scene_change(SCREEN_FMV);
|
|
||||||
screen_slide_set_next(SLIDE_SEGALOGO);
|
screen_slide_set_next(SLIDE_SEGALOGO);
|
||||||
scene_change(SCREEN_SLIDE);
|
scene_change(SCREEN_SLIDE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
#include "screens/fmv.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <psxcd.h>
|
|
||||||
|
|
||||||
#include "screen.h"
|
|
||||||
#include "mdec.h"
|
|
||||||
|
|
||||||
#define FMV_QUEUE_MAX 3
|
|
||||||
|
|
||||||
// Default screen to level select.
|
|
||||||
// Can't wait for hackers using ACE to manipulate this someday :)
|
|
||||||
static ScreenIndex next_screen = SCREEN_LEVELSELECT;
|
|
||||||
|
|
||||||
static FMVOption fmv_queue[FMV_QUEUE_MAX];
|
|
||||||
static uint8_t fmv_count = 0;
|
|
||||||
|
|
||||||
void screen_fmv_load() {}
|
|
||||||
|
|
||||||
void
|
|
||||||
screen_fmv_unload(void *)
|
|
||||||
{
|
|
||||||
fmv_count = 0;
|
|
||||||
// Since screen allocations are used for MDEC playback,
|
|
||||||
// dispose of any used memory
|
|
||||||
screen_free();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
screen_fmv_update(void *)
|
|
||||||
{
|
|
||||||
for(uint8_t i = 0; i < fmv_count; i++) {
|
|
||||||
mdec_play(fmv_queue[i]);
|
|
||||||
}
|
|
||||||
scene_change(next_screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void screen_fmv_draw(void *) {}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
screen_fmv_set_next(ScreenIndex next)
|
|
||||||
{
|
|
||||||
next_screen = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
screen_fmv_enqueue(FMVOption option)
|
|
||||||
{
|
|
||||||
if(fmv_count < FMV_QUEUE_MAX) {
|
|
||||||
fmv_queue[fmv_count] = option;
|
|
||||||
fmv_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "screens/level.h"
|
#include "screens/level.h"
|
||||||
/* #include "screens/fmv.h" */
|
|
||||||
#include "screens/slide.h"
|
#include "screens/slide.h"
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
@ -16,7 +15,6 @@
|
||||||
#include "basic_font.h"
|
#include "basic_font.h"
|
||||||
|
|
||||||
#define CHOICE_SOUNDTEST 19
|
#define CHOICE_SOUNDTEST 19
|
||||||
/* #define CHOICE_MDEC 20 */
|
|
||||||
#define CHOICE_SLIDE 20
|
#define CHOICE_SLIDE 20
|
||||||
#define CHOICE_SPRITETEST 21
|
#define CHOICE_SPRITETEST 21
|
||||||
#define CHOICE_MODELTEST 22
|
#define CHOICE_MODELTEST 22
|
||||||
|
@ -40,7 +38,6 @@ typedef struct {
|
||||||
uint16_t bg_timer;
|
uint16_t bg_timer;
|
||||||
uint8_t music_selected;
|
uint8_t music_selected;
|
||||||
uint8_t soundtest_selection;
|
uint8_t soundtest_selection;
|
||||||
/* uint8_t mdectest_selection; */
|
|
||||||
uint8_t slidetest_selection;
|
uint8_t slidetest_selection;
|
||||||
} screen_levelselect_data;
|
} screen_levelselect_data;
|
||||||
|
|
||||||
|
@ -66,7 +63,6 @@ static const char *menutext[] = {
|
||||||
" 3",
|
" 3",
|
||||||
"\n",
|
"\n",
|
||||||
"SOUND TEST *??*",
|
"SOUND TEST *??*",
|
||||||
/* "MDEC TEST *??*", */
|
|
||||||
"SLIDE TEST *??*",
|
"SLIDE TEST *??*",
|
||||||
"\n",
|
"\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
@ -106,7 +102,6 @@ screen_levelselect_load()
|
||||||
|
|
||||||
data->music_selected = 0;
|
data->music_selected = 0;
|
||||||
data->soundtest_selection = 0x00;
|
data->soundtest_selection = 0x00;
|
||||||
/* data->mdectest_selection = 0x00; */
|
|
||||||
data->slidetest_selection = 0x00;
|
data->slidetest_selection = 0x00;
|
||||||
|
|
||||||
// Regardless of the level, reset score.
|
// Regardless of the level, reset score.
|
||||||
|
@ -174,16 +169,6 @@ screen_levelselect_update(void *d)
|
||||||
data->soundtest_selection = 0;
|
data->soundtest_selection = 0;
|
||||||
else data->soundtest_selection++;
|
else data->soundtest_selection++;
|
||||||
}
|
}
|
||||||
/* } else if(data->menu_choice == CHOICE_MDEC) { */
|
|
||||||
/* if(pad_pressed(PAD_LEFT)) { */
|
|
||||||
/* if(data->mdectest_selection == 0) */
|
|
||||||
/* data->mdectest_selection = FMV_NUM_VIDEOS; */
|
|
||||||
/* else data->mdectest_selection--; */
|
|
||||||
/* } else if(pad_pressed(PAD_RIGHT)) { */
|
|
||||||
/* if(data->mdectest_selection == FMV_NUM_VIDEOS) */
|
|
||||||
/* data->mdectest_selection = 0; */
|
|
||||||
/* else data->mdectest_selection++; */
|
|
||||||
/* } */
|
|
||||||
} else if(data->menu_choice == CHOICE_SLIDE) {
|
} else if(data->menu_choice == CHOICE_SLIDE) {
|
||||||
if(pad_pressed(PAD_LEFT)) {
|
if(pad_pressed(PAD_LEFT)) {
|
||||||
if(data->slidetest_selection == 0)
|
if(data->slidetest_selection == 0)
|
||||||
|
@ -203,7 +188,6 @@ screen_levelselect_update(void *d)
|
||||||
else data->menu_choice--;
|
else data->menu_choice--;
|
||||||
} else if(
|
} else if(
|
||||||
(data->menu_choice != CHOICE_SOUNDTEST)
|
(data->menu_choice != CHOICE_SOUNDTEST)
|
||||||
/* && (data->menu_choice != CHOICE_MDEC) */
|
|
||||||
&& (data->menu_choice != CHOICE_SLIDE)
|
&& (data->menu_choice != CHOICE_SLIDE)
|
||||||
&& (pad_pressed(PAD_LEFT) || pad_pressed(PAD_RIGHT))) {
|
&& (pad_pressed(PAD_LEFT) || pad_pressed(PAD_RIGHT))) {
|
||||||
if(data->menu_choice < MAX_COLUMN_CHOICES - 1) {
|
if(data->menu_choice < MAX_COLUMN_CHOICES - 1) {
|
||||||
|
@ -218,12 +202,6 @@ screen_levelselect_update(void *d)
|
||||||
if(pad_pressed(PAD_START) || pad_pressed(PAD_CROSS)) {
|
if(pad_pressed(PAD_START) || pad_pressed(PAD_CROSS)) {
|
||||||
if(data->menu_choice == CHOICE_TITLE) {
|
if(data->menu_choice == CHOICE_TITLE) {
|
||||||
scene_change(SCREEN_TITLE);
|
scene_change(SCREEN_TITLE);
|
||||||
/* } else if(data->menu_choice == CHOICE_MDEC) { */
|
|
||||||
/* if(data->mdectest_selection > 0) { */
|
|
||||||
/* screen_fmv_set_next(SCREEN_LEVELSELECT); */
|
|
||||||
/* screen_fmv_enqueue(data->mdectest_selection - 1); */
|
|
||||||
/* scene_change(SCREEN_FMV); */
|
|
||||||
/* } */
|
|
||||||
} else if(data->menu_choice == CHOICE_MODELTEST) {
|
} else if(data->menu_choice == CHOICE_MODELTEST) {
|
||||||
scene_change(SCREEN_MODELTEST);
|
scene_change(SCREEN_MODELTEST);
|
||||||
} else if(data->menu_choice == CHOICE_SLIDE) {
|
} else if(data->menu_choice == CHOICE_SLIDE) {
|
||||||
|
@ -313,11 +291,6 @@ screen_levelselect_draw(void *d)
|
||||||
snprintf(buffer, 80, "SOUND TEST *%02X*",
|
snprintf(buffer, 80, "SOUND TEST *%02X*",
|
||||||
data->soundtest_selection);
|
data->soundtest_selection);
|
||||||
font_draw_sm(buffer, vx, vy);
|
font_draw_sm(buffer, vx, vy);
|
||||||
/* } else if(cursel == CHOICE_MDEC) { */
|
|
||||||
/* char buffer[80]; */
|
|
||||||
/* snprintf(buffer, 80, "MDEC TEST *%02X*", */
|
|
||||||
/* data->mdectest_selection); */
|
|
||||||
/* font_draw_sm(buffer, vx, vy); */
|
|
||||||
} else if(cursel == CHOICE_SLIDE) {
|
} else if(cursel == CHOICE_SLIDE) {
|
||||||
char buffer[80];
|
char buffer[80];
|
||||||
snprintf(buffer, 80, "SLIDE TEST *%02X*",
|
snprintf(buffer, 80, "SLIDE TEST *%02X*",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue