From 87b0fe3f2a431183b704bdabb623b43c21131e97 Mon Sep 17 00:00:00 2001 From: Adrian Siekierka Date: Sun, 16 Feb 2025 16:00:41 +0100 Subject: [PATCH] libpsxav: fix EOF flag being misapplied in xa/xacd output (#7) * libpsxav: fix EOF flag being misapplied in xa/xacd output * psxavenc: refactor str2/str2cd output to match xa/xacd output cleanup * psxavenc: fix EDC not being calculated in 2336-byte sector mode --- libpsxav/adpcm.c | 44 +++++++++++++++++++++--------------- libpsxav/libpsxav.h | 54 ++++++++++++++++++++++++++++++++++++++++++++- psxavenc/cdrom.c | 25 ++++++++++----------- psxavenc/common.h | 2 +- psxavenc/filefmt.c | 15 +++++-------- psxavenc/mdec.c | 9 ++------ 6 files changed, 100 insertions(+), 49 deletions(-) diff --git a/libpsxav/adpcm.c b/libpsxav/adpcm.c index 631f17f..96c0ad0 100644 --- a/libpsxav/adpcm.c +++ b/libpsxav/adpcm.c @@ -232,23 +232,30 @@ uint32_t psx_audio_xa_get_sector_interleave(psx_audio_xa_settings_t settings) { return interleave; } -static void psx_audio_xa_encode_init_sector(uint8_t *buffer, psx_audio_xa_settings_t settings) { +static inline void psx_audio_xa_sync_subheader_copy(psx_cdrom_sector_mode2_t *buffer) { + memcpy(buffer->subheader + 1, buffer->subheader, sizeof(psx_cdrom_sector_xa_subheader_t)); +} + +static void psx_audio_xa_encode_init_sector(psx_cdrom_sector_mode2_t *buffer, psx_audio_xa_settings_t settings) { if (settings.format == PSX_AUDIO_XA_FORMAT_XACD) { - memset(buffer, 0, 2352); - memset(buffer+0x001, 0xFF, 10); - buffer[0x00F] = 0x02; + memset(buffer, 0, PSX_CDROM_SECTOR_SIZE); + memset(buffer->sync + 1, 0xFF, 10); + buffer->header.mode = 0x02; } else { - memset(buffer + 0x10, 0, 2336); + memset(buffer->subheader, 0, PSX_CDROM_SECTOR_SIZE - 16); } - buffer[0x010] = settings.file_number; - buffer[0x011] = settings.channel_number & 0x1F; - buffer[0x012] = 0x24 | 0x40; - buffer[0x013] = - (settings.stereo ? 1 : 0) - | (settings.frequency >= PSX_AUDIO_XA_FREQ_DOUBLE ? 0 : 4) - | (settings.bits_per_sample >= 8 ? 16 : 0); - memcpy(buffer + 0x014, buffer + 0x010, 4); + buffer->subheader[0].file = settings.file_number; + buffer->subheader[0].channel = settings.channel_number & PSX_CDROM_SECTOR_XA_CHANNEL_MASK; + buffer->subheader[0].submode = + PSX_CDROM_SECTOR_XA_SUBMODE_AUDIO + | PSX_CDROM_SECTOR_XA_SUBMODE_FORM2 + | PSX_CDROM_SECTOR_XA_SUBMODE_RT; + buffer->subheader[0].coding = + (settings.stereo ? PSX_CDROM_SECTOR_XA_CODING_STEREO : PSX_CDROM_SECTOR_XA_CODING_MONO) + | (settings.frequency >= PSX_AUDIO_XA_FREQ_DOUBLE ? PSX_CDROM_SECTOR_XA_CODING_FREQ_DOUBLE : PSX_CDROM_SECTOR_XA_CODING_FREQ_SINGLE) + | (settings.bits_per_sample >= 8 ? PSX_CDROM_SECTOR_XA_CODING_BITS_8 : PSX_CDROM_SECTOR_XA_CODING_BITS_4); + psx_audio_xa_sync_subheader_copy(buffer); } int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output) { @@ -261,8 +268,8 @@ int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_stat if (settings.stereo) { sample_count <<= 1; } for (i = 0, j = 0; i < sample_count || ((j % 18) != 0); i += sample_jump, j++) { - uint8_t *sector_data = output + ((j/18) * xa_sector_size) - xa_offset; - uint8_t *block_data = sector_data + 0x18 + ((j%18) * 0x80); + psx_cdrom_sector_mode2_t *sector_data = (psx_cdrom_sector_mode2_t*) (output + ((j/18) * xa_sector_size) - xa_offset); + uint8_t *block_data = sector_data->data + ((j%18) * 0x80); if (init_sector) { psx_audio_xa_encode_init_sector(sector_data, settings); @@ -275,7 +282,7 @@ int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_stat memcpy(block_data + 12, block_data + 8, 4); if ((j+1)%18 == 0) { - psx_cdrom_calculate_checksums(sector_data, PSX_CDROM_SECTOR_TYPE_MODE2_FORM2); + psx_cdrom_calculate_checksums((uint8_t*) sector_data, PSX_CDROM_SECTOR_TYPE_MODE2_FORM2); init_sector = 1; } } @@ -285,8 +292,9 @@ int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_stat void psx_audio_xa_encode_finalize(psx_audio_xa_settings_t settings, uint8_t *output, int output_length) { if (output_length >= 2336) { - output[output_length - 2352 + 0x12] |= 0x80; - output[output_length - 2352 + 0x18] |= 0x80; + psx_cdrom_sector_mode2_t *sector = (psx_cdrom_sector_mode2_t*) &output[output_length - 2352]; + sector->subheader[0].submode |= PSX_CDROM_SECTOR_XA_SUBMODE_EOF; + psx_audio_xa_sync_subheader_copy(sector); } } diff --git a/libpsxav/libpsxav.h b/libpsxav/libpsxav.h index 5558200..e20138e 100644 --- a/libpsxav/libpsxav.h +++ b/libpsxav/libpsxav.h @@ -79,6 +79,58 @@ void psx_audio_spu_set_flag_at_sample(uint8_t* spu_data, int sample_pos, int fla #define PSX_CDROM_SECTOR_SIZE 2352 +typedef struct { + uint8_t minute; + uint8_t second; + uint8_t sector; + uint8_t mode; +} psx_cdrom_sector_header_t; + +typedef struct { + uint8_t file; + uint8_t channel; + uint8_t submode; + uint8_t coding; +} psx_cdrom_sector_xa_subheader_t; + +typedef struct { + uint8_t sync[12]; + psx_cdrom_sector_header_t header; + uint8_t data[0x920]; +} psx_cdrom_sector_mode1_t; + +typedef struct { + uint8_t sync[12]; + psx_cdrom_sector_header_t header; + psx_cdrom_sector_xa_subheader_t subheader[2]; + uint8_t data[0x918]; +} psx_cdrom_sector_mode2_t; + +_Static_assert(sizeof(psx_cdrom_sector_mode1_t) == PSX_CDROM_SECTOR_SIZE, "Invalid Mode1 sector size"); +_Static_assert(sizeof(psx_cdrom_sector_mode2_t) == PSX_CDROM_SECTOR_SIZE, "Invalid Mode2 sector size"); + +#define PSX_CDROM_SECTOR_XA_CHANNEL_MASK 0x1F + +#define PSX_CDROM_SECTOR_XA_SUBMODE_EOR 0x01 +#define PSX_CDROM_SECTOR_XA_SUBMODE_VIDEO 0x02 +#define PSX_CDROM_SECTOR_XA_SUBMODE_AUDIO 0x04 +#define PSX_CDROM_SECTOR_XA_SUBMODE_DATA 0x08 +#define PSX_CDROM_SECTOR_XA_SUBMODE_TRIGGER 0x10 +#define PSX_CDROM_SECTOR_XA_SUBMODE_FORM2 0x20 +#define PSX_CDROM_SECTOR_XA_SUBMODE_RT 0x40 +#define PSX_CDROM_SECTOR_XA_SUBMODE_EOF 0x80 + +#define PSX_CDROM_SECTOR_XA_CODING_MONO 0x00 +#define PSX_CDROM_SECTOR_XA_CODING_STEREO 0x01 +#define PSX_CDROM_SECTOR_XA_CODING_CHANNEL_MASK 0x03 +#define PSX_CDROM_SECTOR_XA_CODING_FREQ_DOUBLE 0x00 +#define PSX_CDROM_SECTOR_XA_CODING_FREQ_SINGLE 0x04 +#define PSX_CDROM_SECTOR_XA_CODING_FREQ_MASK 0x0C +#define PSX_CDROM_SECTOR_XA_CODING_BITS_4 0x00 +#define PSX_CDROM_SECTOR_XA_CODING_BITS_8 0x10 +#define PSX_CDROM_SECTOR_XA_CODING_BITS_MASK 0x30 +#define PSX_CDROM_SECTOR_XA_CODING_EMPHASIS 0x40 + typedef enum { PSX_CDROM_SECTOR_TYPE_MODE1, PSX_CDROM_SECTOR_TYPE_MODE2_FORM1, @@ -87,4 +139,4 @@ typedef enum { void psx_cdrom_calculate_checksums(uint8_t *sector, psx_cdrom_sector_type_t type); -#endif /* __LIBPSXAV_H__ */ \ No newline at end of file +#endif /* __LIBPSXAV_H__ */ diff --git a/psxavenc/cdrom.c b/psxavenc/cdrom.c index c73aac4..17cfec4 100644 --- a/psxavenc/cdrom.c +++ b/psxavenc/cdrom.c @@ -23,23 +23,22 @@ freely, subject to the following restrictions: #include "common.h" -void init_sector_buffer_video(uint8_t *buffer, settings_t *settings) { - int offset; +void init_sector_buffer_video(psx_cdrom_sector_mode2_t *buffer, settings_t *settings) { if (settings->format == FORMAT_STR2CD) { - memset(buffer, 0, 2352); - memset(buffer+0x001, 0xFF, 10); - buffer[0x00F] = 0x02; - offset = 0x10; + memset(buffer, 0, PSX_CDROM_SECTOR_SIZE); + memset(buffer->sync + 1, 0xFF, 10); + buffer->header.mode = 0x02; } else { - memset(buffer, 0, 2336); - offset = 0; + memset(buffer->subheader, 0, PSX_CDROM_SECTOR_SIZE - 16); } - buffer[offset+0] = settings->file_number; - buffer[offset+1] = settings->channel_number & 0x1F; - buffer[offset+2] = 0x08 | 0x40; - buffer[offset+3] = 0x00; - memcpy(buffer + offset + 4, buffer + offset, 4); + buffer->subheader[0].file = settings->file_number; + buffer->subheader[0].channel = settings->channel_number & PSX_CDROM_SECTOR_XA_CHANNEL_MASK; + buffer->subheader[0].submode = + PSX_CDROM_SECTOR_XA_SUBMODE_DATA + | PSX_CDROM_SECTOR_XA_SUBMODE_RT; + buffer->subheader[0].coding = 0; + memcpy(buffer->subheader + 1, buffer->subheader, sizeof(psx_cdrom_sector_xa_subheader_t)); } void calculate_edc_data(uint8_t *buffer) diff --git a/psxavenc/common.h b/psxavenc/common.h index 3408555..bc75f6f 100644 --- a/psxavenc/common.h +++ b/psxavenc/common.h @@ -124,7 +124,7 @@ typedef struct { } settings_t; // cdrom.c -void init_sector_buffer_video(uint8_t *buffer, settings_t *settings); +void init_sector_buffer_video(psx_cdrom_sector_mode2_t *buffer, settings_t *settings); void calculate_edc_data(uint8_t *buffer); // decoding.c diff --git a/psxavenc/filefmt.c b/psxavenc/filefmt.c index 35a2bf4..0b8ef35 100644 --- a/psxavenc/filefmt.c +++ b/psxavenc/filefmt.c @@ -315,7 +315,7 @@ void encode_file_str(settings_t *settings, FILE *output) { if ((j%interleave) < video_sectors_per_block) { // Video sector - init_sector_buffer_video(buffer, settings); + init_sector_buffer_video((psx_cdrom_sector_mode2_t*) buffer, settings); encode_sector_str(settings->video_frames, buffer, settings); } else { // Audio sector @@ -342,16 +342,13 @@ void encode_file_str(settings_t *settings, FILE *output) { buffer[0x00C] = ((t/75/60)%10)|(((t/75/60)/10)<<4); buffer[0x00D] = (((t/75)%60)%10)|((((t/75)%60)/10)<<4); buffer[0x00E] = ((t%75)%10)|(((t%75)/10)<<4); - - // FIXME: EDC is not calculated in 2336-byte sector mode (shouldn't - // matter anyway, any CD image builder will have to recalculate it - // due to the sector's MSF changing) - if((j%interleave) < video_sectors_per_block) { - calculate_edc_data(buffer); - } } - fwrite(buffer, sector_size, 1, output); + if((j%interleave) < video_sectors_per_block) { + calculate_edc_data(buffer); + } + + fwrite(buffer + 2352 - sector_size, sector_size, 1, output); time_t t = get_elapsed_time(settings); if (t) { diff --git a/psxavenc/mdec.c b/psxavenc/mdec.c index b7f7157..c88964e 100644 --- a/psxavenc/mdec.c +++ b/psxavenc/mdec.c @@ -614,13 +614,8 @@ void encode_sector_str(uint8_t *video_frames, uint8_t *output, settings_t *setti header[0x00E] = (uint8_t)(settings->state_vid.bytes_used>>16); header[0x00F] = (uint8_t)(settings->state_vid.bytes_used>>24); - if (settings->format == FORMAT_STR2CD) { - memcpy(output + 0x018, header, sizeof(header)); - memcpy(output + 0x018 + 0x020, settings->state_vid.frame_output + settings->state_vid.frame_data_offset, 2016); - } else { - memcpy(output + 0x008, header, sizeof(header)); - memcpy(output + 0x008 + 0x020, settings->state_vid.frame_output + settings->state_vid.frame_data_offset, 2016); - } + memcpy(output + 0x018, header, sizeof(header)); + memcpy(output + 0x018 + 0x020, settings->state_vid.frame_output + settings->state_vid.frame_data_offset, 2016); settings->state_vid.frame_data_offset += 2016; }