diff options
Diffstat (limited to 'src/emu/machine/ldcore.c')
-rw-r--r-- | src/emu/machine/ldcore.c | 1576 |
1 files changed, 1576 insertions, 0 deletions
diff --git a/src/emu/machine/ldcore.c b/src/emu/machine/ldcore.c new file mode 100644 index 00000000000..05b6f512858 --- /dev/null +++ b/src/emu/machine/ldcore.c @@ -0,0 +1,1576 @@ +/************************************************************************* + + ldcore.c + + Private core laserdisc player implementation. + + Copyright Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +*************************************************************************/ + +#include "driver.h" +#include "ldcore.h" +#include "avcomp.h" +#include "streams.h" +#include "vbiparse.h" +#include "config.h" +#include "sound/custom.h" + + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define LOG_POSITION(x) /*printf x*/ + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* we simulate extra lead-in and lead-out tracks */ +#define VIRTUAL_LEAD_IN_TRACKS 200 +#define VIRTUAL_LEAD_OUT_TRACKS 200 + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* core-specific data */ +struct _ldcore_data +{ + /* general config */ + laserdisc_config config; /* copy of the inline config */ + ldplayer_interface intf; /* interface to the player */ + + /* disc parameters */ + chd_file * disc; /* handle to the disc itself */ + av_codec_decompress_config avconfig; /* decompression configuration */ + UINT8 readpending; /* true if a read is pending */ + UINT32 maxtrack; /* maximum track number */ + + /* core states */ + UINT8 audiosquelch; /* audio squelch state: bit 0 = audio 1, bit 1 = audio 2 */ + UINT8 videosquelch; /* video squelch state: bit 0 = on/off */ + UINT8 fieldnum; /* field number (0 or 1) */ + INT32 curtrack; /* current track */ + + /* video data */ + bitmap_t * videoframe[3]; /* currently cached frames */ + bitmap_t * videovisframe[3]; /* wrapper around videoframe with only visible lines */ + UINT8 videofields[3]; /* number of fields in each frame */ + UINT32 videoframenum[3]; /* frame number contained in each frame */ + UINT8 videoindex; /* index of the current video buffer */ + bitmap_t videotarget; /* fake target bitmap for decompression */ + bitmap_t * emptyframe; /* blank frame */ + + /* audio data */ + INT16 * audiobuffer[2]; /* buffer for audio samples */ + UINT32 audiobufsize; /* size of buffer */ + UINT32 audiobufin; /* input index */ + UINT32 audiobufout; /* output index */ + UINT32 audiocursamples; /* current samples this track */ + UINT32 audiomaxsamples; /* maximum samples per track */ + int audiocustom; /* custom sound index */ + int samplerate; /* playback samplerate */ + + /* metadata */ + vbi_metadata metadata[2]; /* metadata parsed from the stream, for each field */ + int last_frame; /* last seen frame number */ + int last_chapter; /* last seen chapter number */ + + /* I/O data */ + UINT8 datain; /* current input data value */ + UINT8 linein[LASERDISC_INPUT_LINES]; /* current input line state */ + UINT8 dataout; /* current output data value */ + UINT8 lineout[LASERDISC_OUTPUT_LINES]; /* current output line state */ + + /* video updating */ + UINT8 videoenable; /* is video enabled? */ + render_texture * videotex; /* texture for the video */ + UINT8 overenable; /* is the overlay enabled? */ + bitmap_t * overbitmap[2]; /* overlay bitmaps */ + int overindex; /* index of the overlay bitmap */ + render_texture * overtex; /* texture for the overlay */ +}; + + +/* sound callback info */ +typedef struct _sound_token sound_token; +struct _sound_token +{ + sound_stream * stream; + laserdisc_state * ld; +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* generic helper functions */ +static void read_track_data(laserdisc_state *ld); +static void process_track_data(const device_config *device); +static void fake_metadata(UINT32 track, UINT8 which, vbi_metadata *metadata); +//static void render_display(UINT16 *videodata, UINT32 rowpixels, UINT32 width, int frame); +static void *custom_start(int clock, const custom_sound_interface *config); +static void custom_stream_callback(void *param, stream_sample_t **inputs, stream_sample_t **outputs, int samples); +static void configuration_load(running_machine *machine, int config_type, xml_data_node *parentnode); +static void configuration_save(running_machine *machine, int config_type, xml_data_node *parentnode); + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static const ldplayer_interface *player_interfaces[] = +{ +// &pr7820_interface, + &pr8210_interface, + &simutrek_interface, + &ldv1000_interface, +// &ldp1450_interface, +// &vp932_interface +}; + +const custom_sound_interface laserdisc_custom_interface = +{ + custom_start +}; + +static const UINT8 numberfont[10][8] = +{ + { + 0x30, // ..xx.... + 0x48, // .x..x... + 0x84, // x....x.. + 0x84, // x....x.. + 0x84, // x....x.. + 0x48, // .x..x... + 0x30 // ..xx.... + }, + { + 0x10, // ...x.... + 0x30, // ..xx.... + 0x50, // .x.x.... + 0x10, // ...x.... + 0x10, // ...x.... + 0x10, // ...x.... + 0x7c // .xxxxx.. + }, + { + 0x78, // .xxxx... + 0x84, // x....x.. + 0x04, // .....x.. + 0x38, // ..xxx... + 0x40, // .x...... + 0x80, // x....... + 0xfc // xxxxxx.. + }, + { + 0x78, // .xxxx... + 0x84, // x....x.. + 0x04, // .....x.. + 0x38, // ..xxx... + 0x04, // .....x.. + 0x84, // x....x.. + 0x78 // .xxxx... + }, + { + 0x08, // ....x... + 0x18, // ...xx... + 0x28, // ..x.x... + 0x48, // .x..x... + 0xfc, // xxxxxx.. + 0x08, // ....x... + 0x08 // ....x... + }, + { + 0xfc, // xxxxxx.. + 0x80, // x....... + 0x80, // x....... + 0xf8, // xxxxx... + 0x04, // .....x.. + 0x84, // x....x.. + 0x78 // .xxxx... + }, + { + 0x38, // ..xxx... + 0x40, // .x...... + 0x80, // x....... + 0xf8, // xxxxx... + 0x84, // x....x.. + 0x84, // x....x.. + 0x78 // .xxxx... + }, + { + 0xfc, // xxxxxx.. + 0x04, // .....x.. + 0x08, // ....x... + 0x10, // ...x.... + 0x20, // ..x..... + 0x20, // ..x..... + 0x20 // ..x..... + }, + { + 0x78, // .xxxx... + 0x84, // x....x.. + 0x84, // x....x.. + 0x78, // .xxxx... + 0x84, // x....x.. + 0x84, // x....x.. + 0x78 // .xxxx... + }, + { + 0x78, // .xxxx.. + 0x84, // x....x. + 0x84, // x....x. + 0x7c, // .xxxxx. + 0x04, // .....x. + 0x08, // ....x.. + 0x70, // .xxx... + } +}; + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + get_safe_token - makes sure that the passed + in device is, in fact, a laserdisc device +-------------------------------------------------*/ + +INLINE laserdisc_state *get_safe_token(const device_config *device) +{ + assert(device != NULL); + assert(device->token != NULL); + assert(device->type == LASERDISC); + + return (laserdisc_state *)device->token; +} + + +/*------------------------------------------------- + audio_channel_active - return TRUE if the + given audio channel should be output +-------------------------------------------------*/ + +INLINE int audio_channel_active(ldcore_data *ldcore, int channel) +{ + return (~ldcore->audiosquelch >> channel) & 1; +} + + +/*------------------------------------------------- + video_active - return TRUE if the video should + be output +-------------------------------------------------*/ + +INLINE int video_active(ldcore_data *ldcore) +{ + return !ldcore->videosquelch; +} + + +/*------------------------------------------------- + add_to_current_track - add a value to the + current track, stopping if we hit the min or + max +-------------------------------------------------*/ + +INLINE int add_to_current_track(ldcore_data *ldcore, INT32 delta) +{ + ldcore->curtrack += delta; + if (ldcore->curtrack < 1) + { + ldcore->curtrack = 1; + return TRUE; + } + else if (ldcore->curtrack >= ldcore->maxtrack - 1) + { + ldcore->curtrack = ldcore->maxtrack - 1; + return TRUE; + } + return FALSE; +} + + +/*------------------------------------------------- + fillbitmap_yuy16 - fill a YUY16 bitmap with a + given color pattern +-------------------------------------------------*/ + +INLINE void fillbitmap_yuy16(bitmap_t *bitmap, UINT8 yval, UINT8 cr, UINT8 cb) +{ + UINT16 color0 = (yval << 8) | cb; + UINT16 color1 = (yval << 8) | cr; + int x, y; + + /* write 32 bits of color (2 pixels at a time) */ + for (y = 0; y < bitmap->height; y++) + { + UINT16 *dest = (UINT16 *)bitmap->base + y * bitmap->rowpixels; + for (x = 0; x < bitmap->width / 2; x++) + { + *dest++ = color0; + *dest++ = color1; + } + } +} + + + +/*************************************************************************** + GENERIC IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + laserdisc_vsync - call this once per field + on the VSYNC signal +-------------------------------------------------*/ + +void laserdisc_vsync(const device_config *device) +{ + laserdisc_state *ld = get_safe_token(device); + ldcore_data *ldcore = ld->core; + + /* wait for previous read and decode to finish */ + process_track_data(device); + + /* update the state */ + if (ldcore->intf.update != NULL) + { + INT32 advanceby = (*ldcore->intf.update)(ld, &ldcore->metadata[ldcore->fieldnum], ldcore->fieldnum, timer_get_time()); + add_to_current_track(ldcore, advanceby); + } + + /* flush any audio before we read more */ + if (ldcore->audiocustom != -1) + { + sound_token *token = custom_get_token(ldcore->audiocustom); + stream_update(token->stream); + } + + /* start reading the track data for the next round */ + ldcore->fieldnum ^= 1; + read_track_data(ld); +} + + +/*------------------------------------------------- + laserdisc_data_w - write data to the given + laserdisc player +-------------------------------------------------*/ + +void laserdisc_data_w(const device_config *device, UINT8 data) +{ + laserdisc_state *ld = get_safe_token(device); + ldcore_data *ldcore = ld->core; + UINT8 prev = ldcore->datain; + ldcore->datain = data; + + /* call through to the player-specific write handler */ + if (ldcore->intf.writedata != NULL) + (*ldcore->intf.writedata)(ld, prev, data); +} + + +/*------------------------------------------------- + laserdisc_line_w - control an input line +-------------------------------------------------*/ + +void laserdisc_line_w(const device_config *device, UINT8 line, UINT8 newstate) +{ + laserdisc_state *ld = get_safe_token(device); + ldcore_data *ldcore = ld->core; + + assert(line < LASERDISC_INPUT_LINES); + assert(newstate == ASSERT_LINE || newstate == CLEAR_LINE || newstate == PULSE_LINE); + + /* assert */ + if (newstate == ASSERT_LINE || newstate == PULSE_LINE) + { + if (ldcore->linein[line] != ASSERT_LINE) + { + /* call through to the player-specific line handler */ + if (ldcore->intf.writeline[line] != NULL) + (*ldcore->intf.writeline[line])(ld, CLEAR_LINE, ASSERT_LINE); + } + ldcore->linein[line] = ASSERT_LINE; + } + + /* deassert */ + if (newstate == CLEAR_LINE || newstate == PULSE_LINE) + { + if (ldcore->linein[line] != CLEAR_LINE) + { + /* call through to the player-specific line handler */ + if (ldcore->intf.writeline[line] != NULL) + (*ldcore->intf.writeline[line])(ld, ASSERT_LINE, CLEAR_LINE); + } + ldcore->linein[line] = CLEAR_LINE; + } +} + + +/*------------------------------------------------- + laserdisc_data_r - return the current + data byte +-------------------------------------------------*/ + +UINT8 laserdisc_data_r(const device_config *device) +{ + laserdisc_state *ld = get_safe_token(device); + ldcore_data *ldcore = ld->core; + UINT8 result = ldcore->dataout; + + /* call through to the player-specific data handler */ + if (ldcore->intf.readdata != NULL) + result = (*ldcore->intf.readdata)(ld); + + return result; +} + + +/*------------------------------------------------- + laserdisc_line_r - return the current state + of an output line +-------------------------------------------------*/ + +UINT8 laserdisc_line_r(const device_config *device, UINT8 line) +{ + laserdisc_state *ld = get_safe_token(device); + ldcore_data *ldcore = ld->core; + UINT8 result; + + assert(line < LASERDISC_OUTPUT_LINES); + result = ldcore->lineout[line]; + + /* call through to the player-specific data handler */ + if (ldcore->intf.readline[line] != NULL) + result = (*ldcore->intf.readline[line])(ld); + + return result; +} + + +/*------------------------------------------------- + laserdisc_get_video - return the current + video frame +-------------------------------------------------*/ + +UINT32 laserdisc_get_video(const device_config *device, bitmap_t **bitmap) +{ + laserdisc_state *ld = get_safe_token(device); + ldcore_data *ldcore = ld->core; + int frameindex; + + /* determine the most recent live set of frames */ + frameindex = ldcore->videoindex; + if (ldcore->videofields[frameindex] < 2) + frameindex = (frameindex + ARRAY_LENGTH(ldcore->videofields) - 1) % ARRAY_LENGTH(ldcore->videofields); + + /* if no video present, return the empty frame */ + if (!video_active(ldcore) || ldcore->videofields[frameindex] < 2) + { + *bitmap = ldcore->emptyframe; + return 0; + } + else + { + *bitmap = ldcore->videovisframe[frameindex]; + return ldcore->videoframenum[frameindex]; + } +} + + +/*------------------------------------------------- + laserdisc_get_field_code - return raw field + information read from the disc +-------------------------------------------------*/ + +UINT32 laserdisc_get_field_code(const device_config *device, UINT8 code) +{ + laserdisc_state *ld = get_safe_token(device); + ldcore_data *ldcore = ld->core; + int field = ldcore->fieldnum ^ 1; + + /* if no video present, return */ + if (!video_active(ldcore)) + return 0; + + switch (code) + { + case LASERDISC_CODE_WHITE_FLAG: + return ldcore->metadata[field].white; + + case LASERDISC_CODE_LINE16: + return ldcore->metadata[field].line16; + + case LASERDISC_CODE_LINE17: + return ldcore->metadata[field].line17; + + case LASERDISC_CODE_LINE18: + return ldcore->metadata[field].line18; + } + + return 0; +} + + + +/*************************************************************************** + PLAYER-TO-CORE INTERFACES +***************************************************************************/ + +/*------------------------------------------------- + ldcore_get_safe_token - return a token with + type checking from a device +-------------------------------------------------*/ + +laserdisc_state *ldcore_get_safe_token(const device_config *device) +{ + return get_safe_token(device); +} + + +/*------------------------------------------------- + ldcore_set_audio_squelch - set the left/right + audio squelch states +-------------------------------------------------*/ + +void ldcore_set_audio_squelch(laserdisc_state *ld, UINT8 squelchleft, UINT8 squelchright) +{ + ld->core->audiosquelch = (squelchleft ? 1 : 0) | (squelchright ? 2 : 0); +} + + +/*------------------------------------------------- + ldcore_set_video_squelch - set the video + squelch state +-------------------------------------------------*/ + +void ldcore_set_video_squelch(laserdisc_state *ld, UINT8 squelch) +{ + ld->core->videosquelch = squelch; +} + + + +/*************************************************************************** + GENERIC HELPER FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + ldcore_generic_update - generically update in + a way that works for most situations +-------------------------------------------------*/ + +INT32 ldcore_generic_update(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime, ldplayer_state *newstate) +{ + INT32 advanceby = 0; + int frame; + + /* start by assuming the state doesn't change */ + *newstate = ld->state; + + /* handle things based on the state */ + switch (ld->state.state) + { + case LDSTATE_EJECTING: + /* when time expires, switch to the ejected state */ + if (attotime_compare(curtime, ld->state.endtime) >= 0) + newstate->state = LDSTATE_EJECTED; + break; + + case LDSTATE_EJECTED: + /* do nothing */ + break; + + case LDSTATE_PARKED: + /* do nothing */ + break; + + case LDSTATE_LOADING: + /* when time expires, switch to the spinup state */ + if (attotime_compare(curtime, ld->state.endtime) >= 0) + newstate->state = LDSTATE_SPINUP; + advanceby = -GENERIC_SEARCH_SPEED; + break; + + case LDSTATE_SPINUP: + /* when time expires, switch to the playing state */ + if (attotime_compare(curtime, ld->state.endtime) >= 0) + newstate->state = LDSTATE_PLAYING; + advanceby = -GENERIC_SEARCH_SPEED; + break; + + case LDSTATE_PAUSING: + /* if he hit the start of a frame, switch to paused state */ + if (is_start_of_frame(vbi)) + { + newstate->state = LDSTATE_PAUSED; + newstate->param = fieldnum; + } + + /* else advance until we hit it */ + else if (fieldnum == 1) + advanceby = 1; + break; + + case LDSTATE_PAUSED: + /* if we paused on field 1, we must flip back and forth */ + if (ld->state.param == 1) + advanceby = (fieldnum == 1) ? 1 : -1; + break; + + case LDSTATE_PLAYING: + /* if we hit the target frame, switch to the paused state */ + if (ld->state.param > 0 && is_start_of_frame(vbi) && frame_from_metadata(vbi) == ld->state.param) + { + newstate->state = LDSTATE_PAUSED; + newstate->param = fieldnum; + } + + /* otherwise after the second field of each frame */ + else if (fieldnum == 1) + advanceby = 1; + break; + + case LDSTATE_PLAYING_SLOW_REVERSE: + /* after the second field of each frame, see if we need to advance */ + if (fieldnum == 1 && ++ld->state.substate > ld->state.param) + { + advanceby = -1; + ld->state.substate = 0; + } + break; + + case LDSTATE_PLAYING_SLOW_FORWARD: + /* after the second field of each frame, see if we need to advance */ + if (fieldnum == 1 && ++ld->state.substate > ld->state.param) + { + advanceby = 1; + ld->state.substate = 0; + } + break; + + case LDSTATE_PLAYING_FAST_REVERSE: + /* advance after the second field of each frame */ + if (fieldnum == 1) + advanceby = -ld->state.param; + break; + + case LDSTATE_PLAYING_FAST_FORWARD: + /* advance after the second field of each frame */ + if (fieldnum == 1) + advanceby = ld->state.param; + break; + + case LDSTATE_SCANNING: + /* advance after the second field of each frame */ + if (fieldnum == 1) + advanceby = ld->state.param >> 8; + + /* after we run out of vsyncs, revert to the saved state */ + if (++ld->state.substate >= (ld->state.param & 0xff)) + *newstate = ld->savestate; + break; + + case LDSTATE_STEPPING_REVERSE: + /* wait for the first field of the frame and then leap backwards */ + if (is_start_of_frame(vbi)) + { + advanceby = (fieldnum == 1) ? -1 : -2; + newstate->state = LDSTATE_PAUSING; + } + break; + + case LDSTATE_STEPPING_FORWARD: + /* wait for the first field of the frame and then switch to pausing state */ + if (is_start_of_frame(vbi)) + newstate->state = LDSTATE_PAUSING; + break; + + case LDSTATE_SEEKING: + /* if we're in the final state, look for a matching frame and pause there */ + frame = frame_from_metadata(vbi); + if (ld->state.substate == 1 && is_start_of_frame(vbi) && frame == ld->state.param) + { + newstate->state = LDSTATE_PAUSED; + newstate->param = fieldnum; + } + + /* otherwise, if we got frame data from the VBI, update our seeking logic */ + else if (ld->state.substate == 0 && frame != FRAME_NOT_PRESENT) + { + INT32 delta = (ld->state.param - 2) - frame; + + /* if we're within a couple of frames, just play until we hit it */ + if (delta >= 0 && delta <= 2) + ld->state.substate++; + + /* otherwise, compute the delta assuming 1:1 track to frame; this will correct eventually */ + else + { + if (delta < 0) + delta--; + advanceby = delta; + advanceby = MIN(advanceby, GENERIC_SEARCH_SPEED); + advanceby = MAX(advanceby, -GENERIC_SEARCH_SPEED); + } + } + + /* otherwise, keep advancing until we know what's up */ + else + { + if (fieldnum == 1) + advanceby = 1; + } + break; + } + + return advanceby; +} + + +/*------------------------------------------------- + read_track_data - read and process data for + a particular video track +-------------------------------------------------*/ + +static void read_track_data(laserdisc_state *ld) +{ + ldcore_data *ldcore = ld->core; + UINT32 tracknum = ldcore->curtrack; + UINT32 fieldnum = ldcore->fieldnum; + chd_error err; + + /* if the previous field had the white flag, force the new field to pair with it */ + if (ldcore->metadata[fieldnum ^ 1].white) + ldcore->videofields[ldcore->videoindex] = 1; + + /* if we already have both fields on the current videoindex, advance */ + if (ldcore->videofields[ldcore->videoindex] >= 2) + { + ldcore->videoindex = (ldcore->videoindex + 1) % ARRAY_LENGTH(ldcore->videofields); + ldcore->videofields[ldcore->videoindex] = 0; + } + + /* set the video target information */ + ldcore->videotarget = *ldcore->videoframe[ldcore->videoindex]; + ldcore->videotarget.base = BITMAP_ADDR16(&ldcore->videotarget, fieldnum, 0); + ldcore->videotarget.height /= 2; + ldcore->videotarget.rowpixels *= 2; + ldcore->avconfig.video = &ldcore->videotarget; + + /* set the audio target information */ + if (ldcore->audiobufin + ldcore->audiomaxsamples <= ldcore->audiobufsize) + { + /* if we can fit without wrapping, just read the data directly */ + ldcore->avconfig.audio[0] = &ldcore->audiobuffer[0][ldcore->audiobufin]; + ldcore->avconfig.audio[1] = &ldcore->audiobuffer[1][ldcore->audiobufin]; + } + else + { + /* otherwise, read to the beginning of the buffer */ + ldcore->avconfig.audio[0] = &ldcore->audiobuffer[0][0]; + ldcore->avconfig.audio[1] = &ldcore->audiobuffer[1][0]; + } + + /* override if we're not decoding */ + ldcore->avconfig.maxsamples = ldcore->audiomaxsamples; + ldcore->avconfig.actsamples = &ldcore->audiocursamples; + ldcore->audiocursamples = 0; + if (!audio_channel_active(ldcore, 0)) + ldcore->avconfig.audio[0] = NULL; + if (!audio_channel_active(ldcore, 1)) + ldcore->avconfig.audio[1] = NULL; + + /* configure the codec and then read */ + if (ldcore->disc != NULL) + { + err = chd_codec_config(ldcore->disc, AV_CODEC_DECOMPRESS_CONFIG, &ldcore->avconfig); + if (err == CHDERR_NONE) + { + INT32 maxtrack = chd_get_header(ldcore->disc)->totalhunks / 2; + INT32 chdtrack = tracknum - 1 - VIRTUAL_LEAD_IN_TRACKS; + + /* clamp the track */ + chdtrack = MAX(chdtrack, 0); + chdtrack = MIN(chdtrack, maxtrack - 1); + err = chd_read_async(ldcore->disc, chdtrack * 2 + fieldnum, NULL); + ldcore->readpending = TRUE; + } + } +} + + +/*------------------------------------------------- + process_track_data - process data from a + track after it has been read +-------------------------------------------------*/ + +static void process_track_data(const device_config *device) +{ + laserdisc_state *ld = get_safe_token(device); + ldcore_data *ldcore = ld->core; + UINT32 tracknum = ldcore->curtrack; + UINT32 fieldnum = ldcore->fieldnum; + int frame, chapter; + chd_error chderr; + + /* wait for the async operation to complete */ + if (ldcore->disc != NULL && ldcore->readpending) + { + /* complete the async operation */ + chderr = chd_async_complete(ldcore->disc); + if (chderr != CHDERR_NONE && chderr != CHDERR_NO_ASYNC_OPERATION) + ldcore->avconfig.video = NULL; + } + ldcore->readpending = FALSE; + + /* parse the metadata */ + if (ldcore->disc != NULL && ldcore->avconfig.video != NULL) + vbi_parse_all((const UINT16 *)ldcore->avconfig.video->base, ldcore->avconfig.video->rowpixels, ldcore->avconfig.video->width, 8, &ldcore->metadata[fieldnum]); + else + fake_metadata(tracknum, fieldnum, &ldcore->metadata[fieldnum]); +// printf("Track %5d: Metadata = %d %08X %08X %08X %08X\n", tracknum, ldcore->metadata[fieldnum].white, ldcore->metadata[fieldnum].line16, ldcore->metadata[fieldnum].line17, ldcore->metadata[fieldnum].line18, ldcore->metadata[fieldnum].line1718); + + /* update the last seen frame and chapter */ + frame = frame_from_metadata(&ldcore->metadata[fieldnum]); + if (frame >= 1 && frame < 99999) + ldcore->last_frame = frame; + chapter = chapter_from_metadata(&ldcore->metadata[fieldnum]); + if (chapter != -1) + ldcore->last_chapter = chapter; + + /* render the display if present */ +// if (ldcore->display && ldcore->avconfig.video != NULL) +// render_display((UINT16 *)ldcore->avconfig.video->base, ldcore->avconfig.video->rowpixels, ldcore->avconfig.video->width, ldcore->last_frame); + + /* update video field */ + if (ldcore->avconfig.video != NULL) + { + ldcore->videofields[ldcore->videoindex]++; + ldcore->videoframenum[ldcore->videoindex] = ldcore->last_frame; + } + + /* pass the audio to the callback */ + if (ldcore->config.audio != NULL) + (*ldcore->config.audio)(device, ldcore->samplerate, ldcore->audiocursamples, ldcore->avconfig.audio[0], ldcore->avconfig.audio[1]); + + /* shift audio data if we read it into the beginning of the buffer */ + if (ldcore->audiocursamples != 0 && ldcore->audiobufin != 0) + { + int chnum; + + /* iterate over channels */ + for (chnum = 0; chnum < 2; chnum++) + if (ldcore->avconfig.audio[chnum] == &ldcore->audiobuffer[chnum][0]) + { + int samplesleft; + + /* move data to the end */ + samplesleft = ldcore->audiobufsize - ldcore->audiobufin; + samplesleft = MIN(samplesleft, ldcore->audiocursamples); + memmove(&ldcore->audiobuffer[chnum][ldcore->audiobufin], &ldcore->audiobuffer[chnum][0], samplesleft * 2); + + /* shift data at the beginning */ + if (samplesleft < ldcore->audiocursamples) + memmove(&ldcore->audiobuffer[chnum][0], &ldcore->audiobuffer[chnum][samplesleft], (ldcore->audiocursamples - samplesleft) * 2); + } + } + + /* update the input buffer pointer */ + ldcore->audiobufin = (ldcore->audiobufin + ldcore->audiocursamples) % ldcore->audiobufsize; +} + + +/*------------------------------------------------- + fake_metadata - fake metadata when there's + no disc present +-------------------------------------------------*/ + +static void fake_metadata(UINT32 track, UINT8 which, vbi_metadata *metadata) +{ + if (which == 0) + { + metadata->white = 1; + metadata->line16 = 0; + metadata->line17 = metadata->line18 = 0xf80000 | + (((track / 10000) % 10) << 16) | + (((track / 1000) % 10) << 12) | + (((track / 100) % 10) << 8) | + (((track / 10) % 10) << 4) | + (((track / 1) % 10) << 0); + } + else + memset(metadata, 0, sizeof(*metadata)); +} + + +/*------------------------------------------------- + render_display - draw the frame display +-------------------------------------------------*/ + +#ifdef UNUSED_FUNCTION +static void render_display(UINT16 *videodata, UINT32 rowpixels, UINT32 width, int frame) +{ + const int xscale = 4, yscale = 2; + char buffer[10]; + int x = width / 10; + int y = 50; + int ch; + int delta; + + /* do nothing if no data */ + if (videodata == NULL) + return; + + /* convert to a character string and render */ + sprintf(buffer, "%5d", frame); + + /* iterate over 5 positions: (-1,-1), (-1,1), (1,-1), (1,1) and (0,0) */ + /* render all in black except the last one */ + for (delta = 0; delta < 5; delta++) + { + int color = (delta < 4) ? 0x0080 : 0xff80; + int dx = (delta < 4) ? ((delta & 1) ? -1 : 1) : 0; + int dy = (delta < 4) ? ((delta & 2) ? -1 : 1) : 0; + + /* iterate over 5 characters */ + for (ch = 0; ch < 5; ch++) + if (buffer[ch] >= '0' && buffer[ch] <= '9') + { + const UINT8 *fontdata = &numberfont[buffer[ch] - '0'][0]; + int cy, cx; + + /* iterate over the rows of the character */ + for (cy = 0; cy < 8; cy++) + { + UINT8 bits = *fontdata++; + + /* and over the columns */ + for (cx = 0; cx < 8; cx++) + if (bits & (0x80 >> cx)) + { + int ix, iy; + + /* fill in an xscale x yscale pixel */ + for (iy = 0; iy < yscale; iy++) + for (ix = 0; ix < xscale; ix++) + videodata[(y + cy * yscale + iy + dy) * rowpixels + (x + (ch * 9 + cx) * xscale + ix + dx)] = color; + } + } + } + } +} +#endif + + +/*------------------------------------------------- + custom_start - custom audio start + for laserdiscs +-------------------------------------------------*/ + +static void *custom_start(int clock, const custom_sound_interface *config) +{ + sound_token *token = auto_malloc(sizeof(*token)); + token->stream = stream_create(0, 2, 48000, token, custom_stream_callback); + token->ld = NULL; + return token; +} + + +/*------------------------------------------------- + custom_stream_callback - audio streamer + for laserdiscs +-------------------------------------------------*/ + +static void custom_stream_callback(void *param, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + sound_token *token = param; + laserdisc_state *ld = token->ld; + ldcore_data *ldcore = ld->core; + stream_sample_t *dst0 = outputs[0]; + stream_sample_t *dst1 = outputs[1]; + int samples_avail = 0; + + /* see if we have enough samples to fill the buffer; if not, drop out */ + if (ld != NULL) + { + samples_avail = ldcore->audiobufin - ldcore->audiobufout; + if (samples_avail < 0) + samples_avail += ldcore->audiobufsize; + } + + /* if no attached ld, just clear the buffers */ + if (samples_avail < samples) + { + memset(dst0, 0, samples * sizeof(dst0[0])); + memset(dst1, 0, samples * sizeof(dst1[0])); + } + + /* otherwise, stream from our buffer */ + else + { + INT16 *buffer0 = ldcore->audiobuffer[0]; + INT16 *buffer1 = ldcore->audiobuffer[1]; + int sampout = ldcore->audiobufout; + + /* copy samples, clearing behind us as we go */ + while (sampout != ldcore->audiobufin && samples-- > 0) + { + *dst0++ = buffer0[sampout]; + *dst1++ = buffer1[sampout]; + buffer0[sampout] = 0; + buffer1[sampout] = 0; + sampout++; + if (sampout >= ldcore->audiobufsize) + sampout = 0; + } + ldcore->audiobufout = sampout; + + /* clear out the rest of the buffer */ + if (samples > 0) + { + int sampout = (ldcore->audiobufout == 0) ? ldcore->audiobufsize - 1 : ldcore->audiobufout - 1; + stream_sample_t fill0 = buffer0[sampout]; + stream_sample_t fill1 = buffer1[sampout]; + + while (samples-- > 0) + { + *dst0++ = fill0; + *dst1++ = fill1; + } + } + } +} + + + +/*************************************************************************** + CONFIG SETTINGS ACCESS +***************************************************************************/ + +/*------------------------------------------------- + configuration_load - read and apply data from + the configuration file +-------------------------------------------------*/ + +static void configuration_load(running_machine *machine, int config_type, xml_data_node *parentnode) +{ + xml_data_node *overnode; + xml_data_node *ldnode; + + /* we only care about game files */ + if (config_type != CONFIG_TYPE_GAME) + return; + + /* might not have any data */ + if (parentnode == NULL) + return; + + /* iterate over overlay nodes */ + for (ldnode = xml_get_sibling(parentnode->child, "device"); ldnode != NULL; ldnode = xml_get_sibling(ldnode->next, "device")) + { + const char *devtag = xml_get_attribute_string(ldnode, "tag", ""); + const device_config *device = device_list_find_by_tag(machine->config->devicelist, LASERDISC, devtag); + if (device != NULL) + { + laserdisc_state *ld = get_safe_token(device); + ldcore_data *ldcore = ld->core; + + /* handle the overlay node */ + overnode = xml_get_sibling(ldnode->child, "overlay"); + if (overnode != NULL) + { + /* fetch positioning controls */ + ldcore->config.overposx = xml_get_attribute_float(overnode, "hoffset", ldcore->config.overposx); + ldcore->config.overscalex = xml_get_attribute_float(overnode, "hstretch", ldcore->config.overscalex); + ldcore->config.overposy = xml_get_attribute_float(overnode, "voffset", ldcore->config.overposy); + ldcore->config.overscaley = xml_get_attribute_float(overnode, "vstretch", ldcore->config.overscaley); + } + } + } +} + + +/*------------------------------------------------- + configuration_save - save data to the + configuration file +-------------------------------------------------*/ + +static void configuration_save(running_machine *machine, int config_type, xml_data_node *parentnode) +{ + const device_config *device; + + /* we only care about game files */ + if (config_type != CONFIG_TYPE_GAME) + return; + + /* iterate over disc devices */ + for (device = device_list_first(machine->config->devicelist, LASERDISC); device != NULL; device = device_list_next(device, LASERDISC)) + { + laserdisc_config *origconfig = device->inline_config; + laserdisc_state *ld = get_safe_token(device); + ldcore_data *ldcore = ld->core; + xml_data_node *overnode; + xml_data_node *ldnode; + + /* create a node */ + ldnode = xml_add_child(parentnode, "device", NULL); + if (ldnode != NULL) + { + int changed = FALSE; + + /* output the basics */ + xml_set_attribute(ldnode, "tag", device->tag); + + /* add an overlay node */ + overnode = xml_add_child(ldnode, "overlay", NULL); + if (overnode != NULL) + { + /* output the positioning controls */ + if (ldcore->config.overposx != origconfig->overposx) + { + xml_set_attribute_float(overnode, "hoffset", ldcore->config.overposx); + changed = TRUE; + } + + if (ldcore->config.overscalex != origconfig->overscalex) + { + xml_set_attribute_float(overnode, "hstretch", ldcore->config.overscalex); + changed = TRUE; + } + + if (ldcore->config.overposy != origconfig->overposy) + { + xml_set_attribute_float(overnode, "voffset", ldcore->config.overposy); + changed = TRUE; + } + + if (ldcore->config.overscaley != origconfig->overscaley) + { + xml_set_attribute_float(overnode, "vstretch", ldcore->config.overscaley); + changed = TRUE; + } + } + + /* if nothing changed, kill the node */ + if (!changed) + xml_delete_node(ldnode); + } + } +} + + + +/*************************************************************************** + VIDEO INTERFACE +***************************************************************************/ + +/*------------------------------------------------- + laserdisc_video_enable - enable/disable the + video +-------------------------------------------------*/ + +void laserdisc_video_enable(const device_config *device, int enable) +{ + laserdisc_state *ld = get_safe_token(device); + ld->core->videoenable = enable; +} + + +/*------------------------------------------------- + laserdisc_video_enable - enable/disable the + video +-------------------------------------------------*/ + +void laserdisc_overlay_enable(const device_config *device, int enable) +{ + laserdisc_state *ld = get_safe_token(device); + ld->core->overenable = enable; +} + + +/*------------------------------------------------- + video update callback +-------------------------------------------------*/ + +VIDEO_UPDATE( laserdisc ) +{ + const device_config *laserdisc = device_list_first(screen->machine->config->devicelist, LASERDISC); + if (laserdisc != NULL) + { + laserdisc_state *ld = laserdisc->token; + ldcore_data *ldcore = ld->core; + bitmap_t *overbitmap = ldcore->overbitmap[ldcore->overindex]; + bitmap_t *vidbitmap = NULL; + + /* handle the overlay if present */ + if (overbitmap != NULL && ldcore->config.overupdate != NULL) + { + rectangle clip = *cliprect; + + /* scale the cliprect to the overlay size and then call the update callback */ + clip.min_x = 0; + clip.max_x = ldcore->config.overwidth - 1; + clip.min_y = cliprect->min_y * overbitmap->height / bitmap->height; + clip.max_y = (cliprect->max_y + 1) * overbitmap->height / bitmap->height - 1; + (*ldcore->config.overupdate)(screen, overbitmap, &clip); + } + + /* if this is the last update, do the rendering */ + if (cliprect->max_y == video_screen_get_visible_area(screen)->max_y) + { + float x0, y0, x1, y1; + + /* update the texture with the overlay contents */ + if (overbitmap != NULL) + { + if (overbitmap->format == BITMAP_FORMAT_INDEXED16) + render_texture_set_bitmap(ldcore->overtex, overbitmap, &ldcore->config.overclip, 0, TEXFORMAT_PALETTEA16); + else if (overbitmap->format == BITMAP_FORMAT_RGB32) + render_texture_set_bitmap(ldcore->overtex, overbitmap, &ldcore->config.overclip, 0, TEXFORMAT_ARGB32); + } + + /* get the laserdisc video */ + laserdisc_get_video(laserdisc, &vidbitmap); + if (vidbitmap != NULL) + render_texture_set_bitmap(ldcore->videotex, vidbitmap, NULL, 0, TEXFORMAT_YUY16); + + /* reset the screen contents */ + render_container_empty(render_container_get_screen(screen)); + + /* add the video texture */ + if (ldcore->videoenable) + render_screen_add_quad(screen, 0.0f, 0.0f, 1.0f, 1.0f, MAKE_ARGB(0xff,0xff,0xff,0xff), ldcore->videotex, PRIMFLAG_BLENDMODE(BLENDMODE_NONE) | PRIMFLAG_SCREENTEX(1)); + + /* add the overlay */ + if (ldcore->overenable && overbitmap != NULL) + { + x0 = 0.5f - 0.5f * ldcore->config.overscalex + ldcore->config.overposx; + y0 = 0.5f - 0.5f * ldcore->config.overscaley + ldcore->config.overposy; + x1 = x0 + ldcore->config.overscalex; + y1 = y0 + ldcore->config.overscaley; + render_screen_add_quad(screen, x0, y0, x1, y1, MAKE_ARGB(0xff,0xff,0xff,0xff), ldcore->overtex, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_SCREENTEX(1)); + } + + /* swap to the next bitmap */ + ldcore->overindex = (ldcore->overindex + 1) % ARRAY_LENGTH(ldcore->overbitmap); + } + } + + return 0; +} + + + +/*************************************************************************** + CONFIGURATION +***************************************************************************/ + +/*------------------------------------------------- + laserdisc_get_config - return a copy of the + current live configuration settings +-------------------------------------------------*/ + +void laserdisc_get_config(const device_config *device, laserdisc_config *config) +{ + laserdisc_state *ld = get_safe_token(device); + *config = ld->core->config; +} + + +/*------------------------------------------------- + laserdisc_get_config - change the current live + configuration settings +-------------------------------------------------*/ + +void laserdisc_set_config(const device_config *device, const laserdisc_config *config) +{ + laserdisc_state *ld = get_safe_token(device); + ld->core->config = *config; +} + + + +/*************************************************************************** + DEVICE INTERFACE +***************************************************************************/ + +/*------------------------------------------------- + device start callback +-------------------------------------------------*/ + +static DEVICE_START( laserdisc ) +{ + int fps = 30, fpsfrac = 0, width = 720, height = 240, interlaced = 1, channels = 2, rate = 44100; + const laserdisc_config *config = device->inline_config; + laserdisc_state *ld = get_safe_token(device); + UINT32 fps_times_1million; + ldcore_data *ldcore; + char metadata[256]; + int sndnum, index; + int statesize; + chd_error err; + + /* save a copy of the device pointer */ + ld->device = device; + + /* allocate memory for the core state */ + ld->core = auto_malloc(sizeof(*ld->core)); + memset(ld->core, 0, sizeof(*ld->core)); + ldcore = ld->core; + + /* determine the maximum player-specific state size and allocate it */ + statesize = 0; + for (index = 0; index < ARRAY_LENGTH(player_interfaces); index++) + statesize = MAX(statesize, player_interfaces[index]->statesize); + ld->player = auto_malloc(statesize); + memset(ld->player, 0, statesize); + + /* copy config data to the live state */ + ldcore->config = *config; + if (ldcore->config.overclip.max_x == ldcore->config.overclip.min_x || ldcore->config.overclip.max_y == ldcore->config.overclip.min_y) + { + ldcore->config.overclip.min_x = ldcore->config.overclip.min_y = 0; + ldcore->config.overclip.max_x = ldcore->config.overwidth - 1; + ldcore->config.overclip.max_y = ldcore->config.overheight - 1; + } + if (ldcore->config.overscalex == 0) + ldcore->config.overscalex = 1.0f; + if (ldcore->config.overscaley == 0) + ldcore->config.overscaley = 1.0f; + + /* find the disc */ + ldcore->disc = get_disk_handle(device->tag); + ldcore->audiocustom = 0; + for (sndnum = 0; sndnum < MAX_SOUND; sndnum++) + { + if (device->machine->config->sound[sndnum].tag != NULL && strcmp(device->machine->config->sound[sndnum].tag, device->tag) == 0) + break; + if (device->machine->config->sound[sndnum].type == SOUND_CUSTOM) + ldcore->audiocustom++; + } + if (sndnum == MAX_SOUND) + ldcore->audiocustom = -1; + + /* get the disc metadata and extract the ld */ + if (ldcore->disc != NULL) + { + /* require the A/V codec */ + if (chd_get_header(ldcore->disc)->compression != CHDCOMPRESSION_AV) + fatalerror("Laserdisc video must be compressed with the A/V codec!"); + + /* read and extract the metadata */ + err = chd_get_metadata(ldcore->disc, AV_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL); + if (err != CHDERR_NONE) + fatalerror("Non-A/V CHD file specified"); + if (sscanf(metadata, AV_METADATA_FORMAT, &fps, &fpsfrac, &width, &height, &interlaced, &channels, &rate) != 7) + fatalerror("Invalid metadata in CHD file"); + + /* require interlaced video */ + if (!interlaced) + fatalerror("Laserdisc video must be interlaced!"); + + /* determine the maximum track and allocate a frame buffer */ + ldcore->maxtrack = chd_get_header(ldcore->disc)->totalhunks / 2; + } + else + ldcore->maxtrack = 54000; + ldcore->maxtrack += VIRTUAL_LEAD_IN_TRACKS + VIRTUAL_LEAD_OUT_TRACKS; + + /* allocate video frames */ + for (index = 0; index < ARRAY_LENGTH(ldcore->videofields); index++) + { + /* first allocate a YUY16 bitmap at 2x the height */ + ldcore->videoframe[index] = auto_bitmap_alloc(width, height * 2, BITMAP_FORMAT_YUY16); + fillbitmap_yuy16(ldcore->videoframe[index], 40, 109, 240); + + /* make a copy of the bitmap that clips out the VBI and horizontal blanking areas */ + ldcore->videovisframe[index] = auto_malloc(sizeof(*ldcore->videovisframe[index])); + *ldcore->videovisframe[index] = *ldcore->videoframe[index]; + ldcore->videovisframe[index]->base = BITMAP_ADDR16(ldcore->videovisframe[index], 44, ldcore->videoframe[index]->width * 8 / 720); + ldcore->videovisframe[index]->height -= 44; + ldcore->videovisframe[index]->width -= 2 * ldcore->videoframe[index]->width * 8 / 720; + } + + /* allocate an empty frame of the same size */ + ldcore->emptyframe = auto_bitmap_alloc(width, height * 2, BITMAP_FORMAT_YUY16); + fillbitmap_yuy16(ldcore->emptyframe, 0, 128, 128); + + /* allocate audio buffers */ + fps_times_1million = fps * 1000000 + fpsfrac; + ldcore->audiomaxsamples = ((UINT64)rate * 1000000 + fps_times_1million - 1) / fps_times_1million; + ldcore->audiobufsize = ldcore->audiomaxsamples * 4; + ldcore->audiobuffer[0] = auto_malloc(ldcore->audiobufsize * sizeof(ldcore->audiobuffer[0][0])); + ldcore->audiobuffer[1] = auto_malloc(ldcore->audiobufsize * sizeof(ldcore->audiobuffer[1][0])); + ldcore->samplerate = rate; + + /* allocate texture for rendering */ + ldcore->videoenable = TRUE; + ldcore->videotex = render_texture_alloc(NULL, NULL); + if (ldcore->videotex == NULL) + fatalerror("Out of memory allocating video texture"); + + /* allocate overlay */ + if (ldcore->config.overwidth > 0 && ldcore->config.overheight > 0 && ldcore->config.overupdate != NULL) + { + ldcore->overenable = TRUE; + ldcore->overbitmap[0] = auto_bitmap_alloc(ldcore->config.overwidth, ldcore->config.overheight, ldcore->config.overformat); + ldcore->overbitmap[1] = auto_bitmap_alloc(ldcore->config.overwidth, ldcore->config.overheight, ldcore->config.overformat); + ldcore->overtex = render_texture_alloc(NULL, NULL); + if (ldcore->overtex == NULL) + fatalerror("Out of memory allocating overlay texture"); + } + + /* register callbacks */ + config_register(device->machine, "laserdisc", configuration_load, configuration_save); +} + + +/*------------------------------------------------- + device exit callback +-------------------------------------------------*/ + +static DEVICE_STOP( laserdisc ) +{ + laserdisc_state *ld = get_safe_token(device); + ldcore_data *ldcore = ld->core; + + /* make sure all async operations have completed */ + if (ldcore->disc != NULL) + chd_async_complete(ldcore->disc); + + /* free any textures */ + if (ldcore->videotex != NULL) + render_texture_free(ldcore->videotex); + if (ldcore->overtex != NULL) + render_texture_free(ldcore->overtex); +} + + +/*------------------------------------------------- + device reset callback +-------------------------------------------------*/ + +static DEVICE_RESET( laserdisc ) +{ + laserdisc_state *ld = get_safe_token(device); + ldcore_data *ldcore = ld->core; + int pltype, line; + + /* find our interface */ + for (pltype = 0; pltype < ARRAY_LENGTH(player_interfaces); pltype++) + if (player_interfaces[pltype]->type == ldcore->config.type) + break; + if (pltype == ARRAY_LENGTH(player_interfaces)) + fatalerror("No interface found for laserdisc player type %d\n", ldcore->config.type); + ldcore->intf = *player_interfaces[pltype]; + + /* attempt to wire up the audio */ + if (ldcore->audiocustom != -1) + { + sound_token *token = custom_get_token(ldcore->audiocustom); + token->ld = ld; + stream_set_sample_rate(token->stream, ldcore->samplerate); + } + + /* set up the general ld */ + ldcore->videosquelch = 1; + ldcore->audiosquelch = 3; + + /* default to track 1 */ + ldcore->curtrack = 1; + ldcore->last_frame = 0; + ldcore->last_chapter = 0; + + /* reset the I/O lines */ + for (line = 0; line < LASERDISC_INPUT_LINES; line++) + ldcore->linein[line] = CLEAR_LINE; + for (line = 0; line < LASERDISC_OUTPUT_LINES; line++) + ldcore->lineout[line] = CLEAR_LINE; + + /* call the initialization */ + if (ldcore->intf.init != NULL) + (*ldcore->intf.init)(ld); +} + + +/*------------------------------------------------- + device set info callback +-------------------------------------------------*/ + +static DEVICE_SET_INFO( laserdisc ) +{ + laserdisc_state *ld = get_safe_token(device); + switch (state) + { + /* --- the following bits of info are set as 64-bit signed integers --- */ + case LDINFO_INT_TYPE: + if (ld != NULL && ld->core != NULL && ld->core->config.type != info->i) + { + ld->core->config.type = info->i; + device_reset(device); + } + break; + } +} + + +/*------------------------------------------------- + device get info callback +-------------------------------------------------*/ + +DEVICE_GET_INFO( laserdisc ) +{ + const laserdisc_config *config = NULL; + int pltype; + + /* if we have a device, figure out where our config lives */ + if (device != NULL) + { + laserdisc_state *ld = device->token; + config = (ld == NULL || ld->core == NULL) ? device->inline_config : &ld->core->config; + } + + switch (state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(laserdisc_state); break; + case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(laserdisc_config); break; + case DEVINFO_INT_CLASS: info->i = DEVICE_CLASS_VIDEO; break; + case LDINFO_INT_TYPE: info->i = config->type; break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case DEVINFO_FCT_SET_INFO: info->set_info = DEVICE_SET_INFO_NAME(laserdisc); break; + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(laserdisc); break; + case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME(laserdisc); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(laserdisc); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: + info->s = "Unknown Laserdisc Player"; + for (pltype = 0; pltype < ARRAY_LENGTH(player_interfaces); pltype++) + if (player_interfaces[pltype]->type == config->type) + info->s = player_interfaces[pltype]->name; + break; + case DEVINFO_STR_FAMILY: info->s = "Laserdisc Player"; break; + case DEVINFO_STR_VERSION: info->s = "1.0"; break; + case DEVINFO_STR_SOURCE_FILE: info->s = __FILE__; break; + case DEVINFO_STR_CREDITS: info->s = "Copyright Nicola Salmoria and the MAME Team"; break; + } +} |