diff options
author | 2008-09-04 08:48:57 +0000 | |
---|---|---|
committer | 2008-09-04 08:48:57 +0000 | |
commit | c1f0164f5d02bf60dce10b58503bd2fea2cec951 (patch) | |
tree | 0eaf4f47edbf064bd53df0359274e409a09d9b45 /src/emu/machine/ldcore.c | |
parent | b660cc0f7024b0ed063fd4b6d5e928e12ec08a81 (diff) |
Split core laserdisc functionality into separate file ldcore.c.
Each player now gets its own source file, along with new hooks
which enable more precise control over the behavior. Updated the
PR-8210 and LD-V1000 implementations to the new spec. Other
players will come online shortly.
Changed scan behavior so that it requires a constant stream of
signals to continue scanning. Updated ldplayer accordingly.
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; + } +} |