mirror of https://github.com/stella-emu/stella.git
553 lines
16 KiB
C++
553 lines
16 KiB
C++
//============================================================================
|
|
//
|
|
// SSSS tt lll lll
|
|
// SS SS tt ll ll
|
|
// SS tttttt eeee ll ll aaaa
|
|
// SSSS tt ee ee ll ll aa
|
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
|
// SS SS tt ee ll ll aa aa
|
|
// SSSS ttt eeeee llll llll aaaaa
|
|
//
|
|
// Copyright (c) 1995-2023 by Bradford W. Mott, Stephen Anthony
|
|
// and the Stella Team
|
|
//
|
|
// See the file "License.txt" for information on usage and redistribution of
|
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
//============================================================================
|
|
|
|
#include "Console.hxx"
|
|
#include "Event.hxx"
|
|
#include "FSNode.hxx"
|
|
#include "OSystem.hxx"
|
|
#include "Sound.hxx"
|
|
#include "Switches.hxx"
|
|
|
|
#include "KidVid.hxx"
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
KidVid::KidVid(Jack jack, const Event& event, const OSystem& osystem,
|
|
const System& system, string_view romMd5,
|
|
const onMessageCallbackForced& callback)
|
|
: Controller(jack, event, system, Controller::Type::KidVid),
|
|
myEnabled{myJack == Jack::Right},
|
|
myOSystem{osystem},
|
|
myCallback{callback}
|
|
{
|
|
// Right now, there are only two games that use the KidVid
|
|
if(romMd5 == "ee6665683ebdb539e89ba620981cb0f6")
|
|
myGame = Game::BBears; // Berenstain Bears
|
|
else if(romMd5 == "a204cd4fb1944c86e800120706512a64")
|
|
myGame = Game::Smurfs; // Smurfs Save the Day
|
|
else
|
|
myEnabled = false;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void KidVid::write(DigitalPin pin, bool value)
|
|
{
|
|
// Change the pin state based on value
|
|
switch(pin)
|
|
{
|
|
// Pin 1: Signal tape running or stopped
|
|
case DigitalPin::One:
|
|
setPin(DigitalPin::One, value);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void KidVid::update()
|
|
{
|
|
if(!myEnabled)
|
|
return;
|
|
|
|
if(myContinueSong)
|
|
{
|
|
// Continue playing song after state load
|
|
const uInt8 temp = ourSongPositions[mySongPointer - 1] & 0x7f;
|
|
const uInt32 songLength = ourSongStart[temp + 1] - ourSongStart[temp] - (262 * ClickFrames);
|
|
|
|
// Play the remaining WAV file
|
|
const string& fileName = myOSystem.baseDir().getPath() +
|
|
((temp < 10) ? "KVSHARED.WAV": getFileName());
|
|
myOSystem.sound().playWav(fileName, ourSongStart[temp] + (songLength - mySongLength), mySongLength);
|
|
|
|
myContinueSong = false;
|
|
}
|
|
|
|
if(myGame == Game::Smurfs && myEvent.get(Event::ConsoleReset)) // Reset does not work with BBears!
|
|
{
|
|
myTape = 0; // rewind Kid Vid tape
|
|
myFilesFound = mySongPlaying = false;
|
|
myOSystem.sound().stopWav();
|
|
}
|
|
else if(myEvent.get(Event::RightKeyboard6) || myEvent.get(Event::ConsoleSelect) ||
|
|
(myOSystem.hasConsole() && !myOSystem.console().switches().tvColor()))
|
|
{
|
|
// Some first songs trigger a sequence of timed actions, they cannot be skipped
|
|
if(mySongPointer &&
|
|
ourSongPositions[mySongPointer - 1] != 0 && // First song of all BBears games
|
|
ourSongPositions[mySongPointer - 1] != 11) // First song of Harmony Smurf
|
|
myOSystem.sound().stopWav();
|
|
}
|
|
if(!myTape)
|
|
{
|
|
if(myEvent.get(Event::RightKeyboard1))
|
|
myTape = 2;
|
|
else if(myEvent.get(Event::RightKeyboard2))
|
|
myTape = 3;
|
|
else if(myEvent.get(Event::RightKeyboard3))
|
|
myTape = myGame == Game::BBears ? 4 : 1; // Berenstain Bears or Smurfs Save The Day?
|
|
// If no Keyboard controller is available, use SELECT and the same
|
|
// switches settings as for playing Smurfs without a Kid Vid.
|
|
else if(myEvent.get(Event::ConsoleSelect) && myOSystem.hasConsole())
|
|
{
|
|
// Harmony (2) : A B
|
|
// Handy (3) : B A
|
|
// Greedy (1/4): B B
|
|
myTape = 1
|
|
+ (myOSystem.console().switches().leftDifficultyA() ? 1 : 0)
|
|
+ (myOSystem.console().switches().rightDifficultyA() ? 2 : 0);
|
|
if(myTape == 4) // ignore A A
|
|
myTape = 0;
|
|
else if(myTape == 1 && myGame == Game::BBears)
|
|
myTape = 4;
|
|
}
|
|
if(myTape)
|
|
{
|
|
static constexpr uInt32 gameNumber[4] = { 3, 1, 2, 3 };
|
|
static constexpr string_view gameName[6] = {
|
|
"Harmony Smurf", "Handy Smurf", "Greedy Smurf",
|
|
"Big Number Hunt", "Great Letter Roundup", "Spooky Spelling Bee"
|
|
};
|
|
|
|
myIdx = myGame == Game::BBears ? NumBlockBits : 0; // KVData48/KVData44
|
|
myBlockIdx = NumBlockBits;
|
|
myBlock = 0;
|
|
openSampleFiles();
|
|
|
|
ostringstream msg;
|
|
msg << "Game #" << gameNumber[myTape - 1] << " - \""
|
|
<< gameName[gameNumber[myTape - 1] + (myGame == Game::Smurfs ? -1 : 2)] << "\"";
|
|
myCallback(msg.str(), true);
|
|
}
|
|
}
|
|
|
|
// Is the tape running?
|
|
if(myTape && getPin(DigitalPin::One) && !myTapeBusy)
|
|
{
|
|
setPin(DigitalPin::Four, (ourData[myIdx >> 3] << (myIdx & 0x07)) & 0x80);
|
|
|
|
#ifdef DEBUG_BUILD
|
|
cerr << (DigitalPin::Four, (ourData[myIdx >> 3] << (myIdx & 0x07)) & 0x80 ? "X" : ".");
|
|
#endif
|
|
|
|
// increase to next bit
|
|
++myIdx;
|
|
--myBlockIdx;
|
|
|
|
// increase to next block (byte)
|
|
if(!myBlockIdx)
|
|
{
|
|
if(!myBlock)
|
|
myIdx = ((myTape * 6) + 12 - NumBlocks) * 8; // KVData00 - ourData = 12 (2 * 6)
|
|
else
|
|
{
|
|
const uInt32 lastBlock = myGame == Game::Smurfs
|
|
? ourBlocks[myTape - 1]
|
|
: ourBlocks[myTape + 2 - 1];
|
|
if(myBlock >= lastBlock)
|
|
myIdx = 42 * 8; // KVData80 - ourData = 42 (7 * 6)
|
|
else
|
|
{
|
|
myIdx = 36 * 8; // KVPause - ourData = 36 (6 * 6)
|
|
setNextSong();
|
|
}
|
|
}
|
|
++myBlock;
|
|
myBlockIdx = NumBlockBits;
|
|
}
|
|
}
|
|
|
|
if(myFilesFound)
|
|
{
|
|
if(mySongPlaying)
|
|
{
|
|
mySongLength = myOSystem.sound().wavSize();
|
|
myTapeBusy = (mySongLength > 262 * TapeFrames) || !myBeep;
|
|
// Check for end of played sample
|
|
if(mySongLength == 0)
|
|
{
|
|
mySongPlaying = false;
|
|
myTapeBusy = !myBeep;
|
|
if(!myBeep)
|
|
setNextSong();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(mySongLength)
|
|
{
|
|
--mySongLength;
|
|
myTapeBusy = (mySongLength > TapeFrames);
|
|
}
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool KidVid::save(Serializer& out) const
|
|
{
|
|
// Save WAV player state
|
|
out.putInt(myTape);
|
|
out.putBool(myFilesFound);
|
|
out.putBool(myTapeBusy);
|
|
out.putBool(myBeep);
|
|
out.putBool(mySongPlaying);
|
|
out.putInt(mySongPointer);
|
|
out.putInt(mySongLength);
|
|
// Save tape input simulation state
|
|
out.putInt(myIdx);
|
|
out.putInt(myBlockIdx);
|
|
out.putInt(myBlock);
|
|
|
|
return Controller::save(out);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool KidVid::load(Serializer& in)
|
|
{
|
|
// Load WAV player state
|
|
myTape = in.getInt();
|
|
myFilesFound = in.getBool();
|
|
myTapeBusy = in.getBool();
|
|
myBeep = in.getBool();
|
|
mySongPlaying = in.getBool();
|
|
mySongPointer = in.getInt();
|
|
mySongLength = in.getInt();
|
|
// Load tape input simulation state
|
|
myIdx = in.getInt();
|
|
myBlockIdx = in.getInt();
|
|
myBlock = in.getInt();
|
|
|
|
myContinueSong = myFilesFound && mySongPlaying;
|
|
|
|
return Controller::load(in);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
const char* KidVid::getFileName() const
|
|
{
|
|
static constexpr const char* const fileNames[6] = {
|
|
"KVS3.WAV", "KVS1.WAV", "KVS2.WAV",
|
|
"KVB3.WAV", "KVB1.WAV", "KVB2.WAV"
|
|
};
|
|
|
|
int i = myGame == Game::Smurfs ? myTape - 1 : myTape + 2;
|
|
if(myTape == 4) i = 3;
|
|
|
|
return fileNames[i];
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void KidVid::openSampleFiles()
|
|
{
|
|
#ifdef SOUND_SUPPORT
|
|
static constexpr uInt32 firstSongPointer[6] = {
|
|
44 + 38,
|
|
0,
|
|
44,
|
|
44 + 38 + 42 + 62 + 80,
|
|
44 + 38 + 42,
|
|
44 + 38 + 42 + 62
|
|
};
|
|
|
|
if(!myFilesFound)
|
|
{
|
|
int i = myGame == Game::Smurfs ? myTape - 1 : myTape + 2;
|
|
if(myTape == 4) i = 3;
|
|
|
|
myFilesFound = FSNode(myOSystem.baseDir().getPath() + getFileName()).exists() &&
|
|
FSNode(myOSystem.baseDir().getPath() + "KVSHARED.WAV").exists();
|
|
|
|
#ifdef DEBUG_BUILD
|
|
if(myFilesFound)
|
|
cerr << endl
|
|
<< "found file: " << getFileName() << endl
|
|
<< "found file: " << "KVSHARED.WAV" << endl;
|
|
#endif
|
|
|
|
mySongLength = 0;
|
|
mySongPointer = firstSongPointer[i];
|
|
}
|
|
myTapeBusy = false;
|
|
#endif
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void KidVid::setNextSong()
|
|
{
|
|
if(myFilesFound)
|
|
{
|
|
myBeep = (ourSongPositions[mySongPointer] & 0x80) == 0;
|
|
|
|
const uInt8 temp = ourSongPositions[mySongPointer] & 0x7f;
|
|
mySongLength = ourSongStart[temp + 1] - ourSongStart[temp] - (262 * ClickFrames);
|
|
|
|
// Play the WAV file
|
|
const string& fileName = (temp < 10) ? "KVSHARED.WAV" : getFileName();
|
|
myOSystem.sound().playWav(myOSystem.baseDir().getPath() + fileName,
|
|
ourSongStart[temp], mySongLength);
|
|
ostringstream msg;
|
|
msg << "Read song #" << mySongPointer << " (" << fileName << ")";
|
|
myCallback(msg.str(), false);
|
|
|
|
#ifdef DEBUG_BUILD
|
|
cerr << fileName << ": " << (ourSongPositions[mySongPointer] & 0x7f) << endl;
|
|
#endif
|
|
|
|
mySongPlaying = myTapeBusy = true;
|
|
++mySongPointer;
|
|
}
|
|
else
|
|
{
|
|
myBeep = true;
|
|
myTapeBusy = true;
|
|
mySongLength = TapeFrames + 32; /* delay needed for Harmony without tape */
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
const std::array<uInt8, KidVid::NumBlocks> KidVid::ourBlocks = {
|
|
2+40, 2+21, 2+35, /* Smurfs tapes 3, 1, 2 */
|
|
42+60, 42+78, 42+60 /* BBears tapes 1, 2, 3 (40 extra blocks for intro) */
|
|
};
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
const std::array<uInt8, KidVid::NumBlockBits> KidVid::ourData = {
|
|
/* KVData44, Smurfs */
|
|
0x7b, // 0111 1011b ; (1)0
|
|
0x1e, // 0001 1110b ; 1
|
|
0xc6, // 1100 0110b ; 00
|
|
0x31, // 0011 0001b ; 01
|
|
0xec, // 1110 1100b ; 0
|
|
0x60, // 0110 0000b ; 0+
|
|
|
|
/* KVData48, BBears */
|
|
0x7b, // 0111 1011b ; (1)0
|
|
0x1e, // 0001 1110b ; 1
|
|
0xc6, // 1100 0110b ; 00
|
|
0x3d, // 0011 1101b ; 10
|
|
0x8c, // 1000 1100b ; 0
|
|
0x60, // 0110 0000b ; 0+
|
|
|
|
/* KVData00 */
|
|
0xf6, // 1111 0110b
|
|
0x31, // 0011 0001b
|
|
0x8c, // 1000 1100b
|
|
0x63, // 0110 0011b
|
|
0x18, // 0001 1000b
|
|
0xc0, // 1100 0000b
|
|
|
|
/* KVData01 */
|
|
0xf6, // 1111 0110b
|
|
0x31, // 0011 0001b
|
|
0x8c, // 1000 1100b
|
|
0x63, // 0110 0011b
|
|
0x18, // 0001 1000b
|
|
0xf0, // 1111 0000b
|
|
|
|
/* KVData02 */
|
|
0xf6, // 1111 0110b
|
|
0x31, // 0011 0001b
|
|
0x8c, // 1000 1100b
|
|
0x63, // 0110 0011b
|
|
0x1e, // 0001 1110b
|
|
0xc0, // 1100 0000b
|
|
|
|
/* KVData03 */
|
|
0xf6, // 1111 0110b
|
|
0x31, // 0011 0001b
|
|
0x8c, // 1000 1100b
|
|
0x63, // 0110 0011b
|
|
0x1e, // 0001 1110b
|
|
0xf0, // 1111 0000b
|
|
|
|
/* KVPause */
|
|
0x3f, // 0011 1111b
|
|
0xf0, // 1111 0000b
|
|
0x00, // 0000 0000b
|
|
0x00, // 0000 0000b
|
|
0x00, // 0000 0000b
|
|
0x00, // 0000 0000b
|
|
|
|
/* KVData80 */
|
|
0xf7, // 1111 0111b ; marks end of tape (green/yellow screen)
|
|
0xb1, // 1011 0001b
|
|
0x8c, // 1000 1100b
|
|
0x63, // 0110 0011b
|
|
0x18, // 0001 1000b
|
|
0xc0 // 1100 0000b
|
|
};
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
const std::array<uInt8, KidVid::SongPosSize> KidVid::ourSongPositions = {
|
|
/* kvs1 44 */
|
|
11, 12+0x80, 13+0x80, 14, 15+0x80, 16, 8+0x80, 17, 18+0x80, 19, 20+0x80,
|
|
21, 8+0x80, 22, 15+0x80, 23, 18+0x80, 14, 20+0x80, 16, 18+0x80,
|
|
17, 15+0x80, 19, 8+0x80, 21, 20+0x80, 22, 18+0x80, 23, 15+0x80,
|
|
14, 20+0x80, 16, 8+0x80, 22, 15+0x80, 23, 18+0x80, 14, 20+0x80,
|
|
16, 8+0x80, 9,
|
|
|
|
/* kvs2 38 */
|
|
25+0x80, 26, 27, 28, 8, 29, 30, 26, 27, 28, 8, 29, 30, 26, 27, 28, 8, 29,
|
|
30, 26, 27, 28, 8, 29, 30, 26, 27, 28, 8, 29, 30, 26, 27, 28, 8, 29,
|
|
30+0x80, 9,
|
|
|
|
/* kvs3 42 */
|
|
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 34, 42, 36, 43, 40, 39, 38, 37,
|
|
34, 43, 36, 39, 40, 37, 38, 43, 34, 37, 36, 43, 40, 39, 38, 37, 34, 43,
|
|
36, 39, 40, 37, 38+0x80, 9,
|
|
|
|
/* kvb1 62 */
|
|
0, 1, 45, 2, 3, 46, 4, 5, 47, 6, 7, 48, 4, 3, 49, 2, 1, 50, 6, 7, 51,
|
|
4, 5, 52, 6, 1, 53, 2, 7, 54, 6, 5, 45, 2, 1, 46, 4, 3, 47, 2, 5, 48,
|
|
4, 7, 49, 6, 1, 50, 2, 5, 51, 6, 3, 52, 4, 7, 53, 2, 1, 54, 6+0x80, 9,
|
|
|
|
/* kvb2 80 */
|
|
0, 1, 56, 4, 3, 57, 2, 5, 58, 6, 7, 59, 2, 3, 60, 4, 1, 61, 6, 7, 62,
|
|
2, 5, 63, 6, 1, 64, 4, 7, 65, 6, 5, 66, 4, 1, 67, 2, 3, 68, 6, 5, 69,
|
|
2, 7, 70, 4, 1, 71, 2, 5, 72, 4, 3, 73, 6, 7, 74, 2, 1, 75, 6, 3, 76,
|
|
4, 5, 77, 6, 7, 78, 2, 3, 79, 4, 1, 80, 2, 7, 81, 4+0x80, 9,
|
|
|
|
/* kvb3 62 */
|
|
0, 1, 83, 2, 3, 84, 4, 5, 85, 6, 7, 86, 4, 3, 87, 2, 1, 88, 6, 7, 89,
|
|
2, 5, 90, 6, 1, 91, 4, 7, 92, 6, 5, 93, 4, 1, 94, 2, 3, 95, 6, 5, 96,
|
|
2, 7, 97, 4, 1, 98, 6, 5, 99, 4, 3, 100, 2, 7, 101, 4, 1, 102, 2+0x80, 9
|
|
};
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
const std::array<uInt32, KidVid::SongStartSize> KidVid::ourSongStart = {
|
|
/* kvshared */
|
|
44, /* Welcome + intro Berenstain Bears */
|
|
980829, /* boulders in the road */
|
|
1178398, /* standing ovations */
|
|
1430063, /* brother bear */
|
|
1691136, /* good work */
|
|
1841665, /* crossing a bridge */
|
|
2100386, /* not bad (applause) */
|
|
2283843, /* ourgame */
|
|
2629588, /* start the parade */
|
|
2824805, /* rewind */
|
|
3059116,
|
|
|
|
/* kvs1 */
|
|
44, /* Harmony intro 1 */
|
|
164685, /* falling notes (intro 2) */
|
|
395182, /* instructions */
|
|
750335, /* high notes are high */
|
|
962016, /* my hat's off to you */
|
|
1204273, /* 1 2 3 do re mi */
|
|
1538258, /* Harmony */
|
|
1801683, /* congratulations (all of the Smurfs voted) */
|
|
2086276, /* line or space */
|
|
2399093, /* hooray */
|
|
2589606, /* hear yeeh */
|
|
2801287, /* over the river */
|
|
3111752, /* musical deduction */
|
|
3436329,
|
|
|
|
/* kvs2 */
|
|
44, /* Handy intro + instructions */
|
|
778557, /* place in shape */
|
|
1100782, /* sailor mate + whistle */
|
|
// 1281887,
|
|
1293648, /* attention */
|
|
1493569, /* colours */
|
|
1801682, /* congratulations (Handy and friends voted) */
|
|
2086275,
|
|
|
|
/* kvs3 */
|
|
44, /* Greedy and Clumsy intro + instructions */
|
|
686829, /* red */
|
|
893806, /* don't count your chicken */
|
|
1143119, /* yellow */
|
|
1385376, /* thank you */
|
|
1578241, /* mixin' and matchin' */
|
|
1942802, /* fun / colour shake */
|
|
2168595, /* colours can be usefull */
|
|
2493172, /* hip hip horay */
|
|
2662517, /* green */
|
|
3022374, /* purple */
|
|
3229351, /* white */
|
|
3720920,
|
|
|
|
/* kvb1 */
|
|
44, /* 3 */ // can be one too late!
|
|
592749, /* 5 */
|
|
936142, /* 2 */
|
|
1465343, /* 4 */
|
|
1787568, /* 1 */
|
|
2145073, /* 7 */
|
|
2568434, /* 9 */
|
|
2822451, /* 8 */
|
|
3045892, /* 6 */
|
|
3709157, /* 0 */
|
|
4219542,
|
|
|
|
/* kvb2 */
|
|
44, /* A */
|
|
303453, /* B */
|
|
703294, /* C */
|
|
1150175, /* D */
|
|
1514736, /* E */
|
|
2208577, /* F */
|
|
2511986, /* G */
|
|
2864787, /* H */
|
|
3306964, /* I */
|
|
3864389, /* J */
|
|
4148982, /* K */
|
|
4499431, /* L */
|
|
4824008, /* M */
|
|
5162697, /* N */
|
|
5581354, /* O */
|
|
5844779, /* P */
|
|
6162300, /* Q */
|
|
6590365, /* R */
|
|
6839678, /* S */
|
|
7225407, /* T */
|
|
7552336, /* U */
|
|
7867505, /* V */
|
|
8316738, /* W */
|
|
8608387, /* X */
|
|
8940020, /* Y */
|
|
9274005, /* Z */
|
|
9593878,
|
|
|
|
/* kvb3 */
|
|
44, /* cat */
|
|
341085, /* one */
|
|
653902, /* red */
|
|
1018463, /* two */
|
|
1265424, /* dog */
|
|
1669969, /* six */
|
|
1919282, /* hat */
|
|
2227395, /* ten */
|
|
2535508, /* mom */
|
|
3057653, /* dad */
|
|
3375174, /* ball */
|
|
3704455, /* fish */
|
|
4092536, /* nine */
|
|
4487673, /* bear */
|
|
5026282, /* four */
|
|
5416715, /* bird */
|
|
5670732, /* tree */
|
|
6225805, /* rock */
|
|
6736190, /* book */
|
|
7110159, /* road */
|
|
7676992
|
|
};
|