From 0dbd505b9ba675002b3b0bb370349160a3aff8ff Mon Sep 17 00:00:00 2001 From: thrust26 Date: Wed, 16 Oct 2019 22:05:33 +0200 Subject: [PATCH] updated WD bankswitching (incl. detection) --- Changes.txt | 6 ++++ src/debugger/gui/CartWDWidget.cxx | 39 ++++++++++----------- src/emucore/Bankswitch.cxx | 5 ++- src/emucore/Bankswitch.hxx | 2 +- src/emucore/CartDetector.cxx | 16 ++++++++- src/emucore/CartDetector.hxx | 6 ++++ src/emucore/CartWD.cxx | 58 +++++++++++++++---------------- src/emucore/CartWD.hxx | 42 +++++++++------------- src/emucore/DefProps.hxx | 5 +-- src/emucore/stella.pro | 8 +++++ 10 files changed, 105 insertions(+), 82 deletions(-) diff --git a/Changes.txt b/Changes.txt index e174fdefd..db3411f57 100644 --- a/Changes.txt +++ b/Changes.txt @@ -104,6 +104,10 @@ * Disabled some developer options for 'Player settings'. + * Writes to RAM read ports are ignored now. + + * Added Developer setting, which breaks on writes to read ports. + * Improved breakpoints to now consider the banks. * Improved debugger's TIA display and zoom windows. @@ -131,6 +135,8 @@ * Enhanced UA bankswitching to support certain Brazilian carts. + * Fixed WD bankswitching + * Auto-detection of bankswitch scheme by file extension now includes more human-readable formats (not restricted to DOS 3-char length). See the documentation for the new names. diff --git a/src/debugger/gui/CartWDWidget.cxx b/src/debugger/gui/CartWDWidget.cxx index 8a401fe42..265bb27ae 100644 --- a/src/debugger/gui/CartWDWidget.cxx +++ b/src/debugger/gui/CartWDWidget.cxx @@ -32,8 +32,7 @@ CartridgeWDWidget::CartridgeWDWidget( "This scheme has eight 1K slices, which can be mapped into four 1K " "segments in various combinations. Each 'bank' selects a predefined " "segment arrangement (indicated in square brackets)\n" - "The last four banks swap in an extra 3 bytes from above the 8K " - "cart boundary into the third (uppermost) segment at $3FC - $3FE\n\n" + "In the third (uppermost) segment the byte at $3FC is overwritten with 0.\n\n" "64 bytes RAM @ $F000 - $F080\n" " $F000 - $F03F (R), $F040 - $F07F (W)\n"; @@ -41,26 +40,26 @@ CartridgeWDWidget::CartridgeWDWidget( ypos = addBaseInformation(myCart.mySize, "Wickstead Design", info, 12) + myLineHeight; VariantList items; - VarList::push_back(items, "0 ($30) [0,0,1,2]", 0); - VarList::push_back(items, "1 ($31) [0,1,3,2]", 1); + VarList::push_back(items, "0 ($30) [0,0,1,3]", 0); + VarList::push_back(items, "1 ($31) [0,1,2,3]", 1); VarList::push_back(items, "2 ($32) [4,5,6,7]", 2); - VarList::push_back(items, "3 ($33) [7,4,3,2]", 3); + VarList::push_back(items, "3 ($33) [7,4,2,3]", 3); VarList::push_back(items, "4 ($34) [0,0,6,7]", 4); VarList::push_back(items, "5 ($35) [0,1,7,6]", 5); - VarList::push_back(items, "6 ($36) [3,2,4,5]", 6); + VarList::push_back(items, "6 ($36) [2,3,4,5]", 6); VarList::push_back(items, "7 ($37) [6,0,5,1]", 7); - VarList::push_back(items, "8 ($38) [0,0,1,2]", 8); - VarList::push_back(items, "9 ($39) [0,1,3,2]", 9); + VarList::push_back(items, "8 ($38) [0,0,1,3]", 8); + VarList::push_back(items, "9 ($39) [0,1,2,3]", 9); VarList::push_back(items, "10 ($3A) [4,5,6,7]", 10); - VarList::push_back(items, "11 ($3B) [7,4,3,2]", 11); - VarList::push_back(items, "12 ($3C) [0,0,6,7*]", 12); - VarList::push_back(items, "13 ($3D) [0,1,7,6*]", 13); - VarList::push_back(items, "14 ($3E) [3,2,4,5*]", 14); - VarList::push_back(items, "15 ($3F) [6,0,5,1*]", 15); + VarList::push_back(items, "11 ($3B) [7,4,2,3]", 11); + VarList::push_back(items, "12 ($3C) [0,0,6,7]", 12); + VarList::push_back(items, "13 ($3D) [0,1,7,6]", 13); + VarList::push_back(items, "14 ($3E) [2,3,4,5]", 14); + VarList::push_back(items, "15 ($3F) [6,0,5,1]", 15); myBank = new PopUpWidget(boss, _font, xpos, ypos-2, - _font.getStringWidth("15 ($3F) [6,0,5,1*]"), - myLineHeight, items, "Set bank ", - _font.getStringWidth("Set bank "), kBankChanged); + _font.getStringWidth("15 ($3F) [6,0,5,1]"), + myLineHeight, items, "Set bank ", + _font.getStringWidth("Set bank "), kBankChanged); myBank->setTarget(this); addFocusWidget(myBank); } @@ -103,13 +102,11 @@ string CartridgeWDWidget::bankState() ostringstream& buf = buffer(); static const char* const segments[] = { - "[0,0,1,2]", "[0,1,3,2]", "[4,5,6,7]", "[7,4,3,2]", - "[0,0,6,7]", "[0,1,7,6]", "[3,2,4,5]", "[6,0,5,1]", - "[0,0,1,2]", "[0,1,3,2]", "[4,5,6,7]", "[7,4,3,2]", - "[0,0,6,7*]", "[0,1,7,6*]", "[3,2,4,5*]", "[6,0,5,1*]" + "[0,0,1,3]", "[0,1,2,3]", "[4,5,6,7]", "[7,4,2,3]", + "[0,0,6,7]", "[0,1,7,6]", "[2,3,4,5]", "[6,0,5,1]" }; uInt16 bank = myCart.getBank(); - buf << "Bank = " << std::dec << bank << ", segments = " << segments[bank]; + buf << "Bank = " << std::dec << bank << ", segments = " << segments[bank & 0x7]; return buf.str(); } diff --git a/src/emucore/Bankswitch.cxx b/src/emucore/Bankswitch.cxx index f919fc7b3..340633aa8 100644 --- a/src/emucore/Bankswitch.cxx +++ b/src/emucore/Bankswitch.cxx @@ -136,7 +136,8 @@ Bankswitch::Description Bankswitch::BSList[int(Bankswitch::Type::NumSchemes)] = { "SB" , "SB (128-256K SUPERbank)" }, { "UA" , "UA (8K UA Ltd.)" }, { "UASW" , "UASW (8K UA swapped banks)" }, - { "WD" , "WD (Experimental)" }, + { "WD" , "WD (Pink Panther)" }, + { "WDSW" , "WDSW (Pink Panther, bad)" }, { "X07" , "X07 (64K AtariAge)" }, #if defined(CUSTOM_ARM) { "CUSTOM" , "CUSTOM (ARM)" } @@ -222,6 +223,7 @@ Bankswitch::ExtensionMap Bankswitch::ourExtensions = { { "UA" , Bankswitch::Type::_UA }, { "UASW" , Bankswitch::Type::_UASW }, { "WD" , Bankswitch::Type::_WD }, + { "WDSW" , Bankswitch::Type::_WDSW }, { "X07" , Bankswitch::Type::_X07 } }; @@ -277,5 +279,6 @@ Bankswitch::NameToTypeMap Bankswitch::ourNameToTypes = { { "UA" , Bankswitch::Type::_UA }, { "UASW" , Bankswitch::Type::_UASW }, { "WD" , Bankswitch::Type::_WD }, + { "WDSW" , Bankswitch::Type::_WDSW }, { "X07" , Bankswitch::Type::_X07 } }; diff --git a/src/emucore/Bankswitch.hxx b/src/emucore/Bankswitch.hxx index e9f77a118..a55d5da77 100644 --- a/src/emucore/Bankswitch.hxx +++ b/src/emucore/Bankswitch.hxx @@ -45,7 +45,7 @@ class Bankswitch _DPC, _DPCP, _E0, _E7, _E78K, _EF, _EFSC, _F0, _F4, _F4SC, _F6, _F6SC, _F8, _F8SC, _FA, _FA2, _FE, _MDM, _SB, _UA, _UASW, - _WD, _X07, + _WD, _WDSW, _X07, #ifdef CUSTOM_ARM _CUSTOM, #endif diff --git a/src/emucore/CartDetector.cxx b/src/emucore/CartDetector.cxx index 9d37b07d1..0b9fedffb 100644 --- a/src/emucore/CartDetector.cxx +++ b/src/emucore/CartDetector.cxx @@ -327,6 +327,7 @@ CartDetector::createFromImage(const ByteBuffer& image, size_t size, Bankswitch:: case Bankswitch::Type::_SB: return make_unique(image, size, md5, settings); case Bankswitch::Type::_WD: + case Bankswitch::Type::_WDSW: return make_unique(image, size, md5, settings); case Bankswitch::Type::_X07: return make_unique(image, size, md5, settings); @@ -395,12 +396,14 @@ Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t si type = Bankswitch::Type::_0840; else if(isProbablyE78K(image, size)) type = Bankswitch::Type::_E78K; + else if (isProbablyWD(image,size)) + type = Bankswitch::Type::_WD; else type = Bankswitch::Type::_F8; } else if(size == 8_KB + 3) // 8195 bytes (Experimental) { - type = Bankswitch::Type::_WD; + type = Bankswitch::Type::_WDSW; } else if(size >= 10_KB && size <= 10_KB + 256) // ~10K - Pitfall2 { @@ -985,6 +988,17 @@ bool CartDetector::isProbablyUA(const ByteBuffer& image, size_t size) return false; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartDetector::isProbablyWD(const ByteBuffer& image, size_t size) +{ + // WD cart bankswitching switches banks by accessing address 0x30..0x3f + uInt8 signature[1][3] = { + { 0xA5, 0x39, 0x4C } // LDA $39, JMP + }; + return searchForBytes(image.get(), size, signature[0], 3, 1); +} + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartDetector::isProbablyX07(const ByteBuffer& image, size_t size) { diff --git a/src/emucore/CartDetector.hxx b/src/emucore/CartDetector.hxx index 727f5ba77..8f0c580bc 100644 --- a/src/emucore/CartDetector.hxx +++ b/src/emucore/CartDetector.hxx @@ -245,11 +245,17 @@ class CartDetector */ static bool isProbablyUA(const ByteBuffer& image, size_t size); + /** + Returns true if the image is probably a Wickstead Design bankswitching cartridge + */ + static bool isProbablyWD(const ByteBuffer& image, size_t size); + /** Returns true if the image is probably an X07 bankswitching cartridge */ static bool isProbablyX07(const ByteBuffer& image, size_t size); + private: // Following constructors and assignment operators not supported CartDetector() = delete; diff --git a/src/emucore/CartWD.cxx b/src/emucore/CartWD.cxx index 45e550155..164a24489 100644 --- a/src/emucore/CartWD.cxx +++ b/src/emucore/CartWD.cxx @@ -30,7 +30,16 @@ CartridgeWD::CartridgeWD(const ByteBuffer& image, size_t size, myCurrentBank(0) { // Copy the ROM image into my buffer - std::copy_n(image.get(), mySize, myImage.begin()); + if (mySize == 8_KB + 3) + { + // swap slices 2 & 3 + std::copy_n(image.get(), 1_KB * 2, myImage.begin()); + std::copy_n(image.get() + 1_KB * 3, 1_KB * 1, myImage.begin() + 1_KB * 2); + std::copy_n(image.get() + 1_KB * 2, 1_KB * 1, myImage.begin() + 1_KB * 3); + std::copy_n(image.get() + 1_KB * 4, 1_KB * 4, myImage.begin() + 1_KB * 4); + } + else + std::copy_n(image.get(), mySize, myImage.begin()); createCodeAccessBase(8_KB); } @@ -154,10 +163,10 @@ bool CartridgeWD::bank(uInt16 bank) myCurrentBank = bank; - segmentZero(ourBankOrg[bank].zero); - segmentOne(ourBankOrg[bank].one); - segmentTwo(ourBankOrg[bank].two); - segmentThree(ourBankOrg[bank].three, ourBankOrg[bank].map3bytes); + segmentZero(ourBankOrg[bank & 0x7].zero); + segmentOne(ourBankOrg[bank & 0x7].one); + segmentTwo(ourBankOrg[bank & 0x7].two); + segmentThree(ourBankOrg[bank & 0x7].three); return myBankChanged = true; } @@ -206,19 +215,14 @@ void CartridgeWD::segmentTwo(uInt8 slice) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeWD::segmentThree(uInt8 slice, bool map3bytes) +void CartridgeWD::segmentThree(uInt8 slice) { uInt16 offset = slice << 10; // Make a copy of the address space pointed to by the slice - // Then map in the extra 3 bytes, if required + // Then overwrite one byte with 0 std::copy_n(myImage.begin()+offset, mySegment3.size(), mySegment3.begin()); - if(mySize == 8_KB + 3 && map3bytes) - { - mySegment3[0x3FC] = myImage[0x2000+0]; - mySegment3[0x3FD] = myImage[0x2000+1]; - mySegment3[0x3FE] = myImage[0x2000+2]; - } + mySegment3[0x3FC] = 0; System::PageAccess access(this, System::PageAccessType::READ); @@ -305,21 +309,15 @@ bool CartridgeWD::load(Serializer& in) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -CartridgeWD::BankOrg CartridgeWD::ourBankOrg[16] = { - { 0, 0, 1, 2, false }, // Bank 0 - { 0, 1, 3, 2, false }, // Bank 1 - { 4, 5, 6, 7, false }, // Bank 2 - { 7, 4, 3, 2, false }, // Bank 3 - { 0, 0, 6, 7, false }, // Bank 4 - { 0, 1, 7, 6, false }, // Bank 5 - { 3, 2, 4, 5, false }, // Bank 6 - { 6, 0, 5, 1, false }, // Bank 7 - { 0, 0, 1, 2, false }, // Bank 8 - { 0, 1, 3, 2, false }, // Bank 9 - { 4, 5, 6, 7, false }, // Bank 10 - { 7, 4, 3, 2, false }, // Bank 11 - { 0, 0, 6, 7, true }, // Bank 12 - { 0, 1, 7, 6, true }, // Bank 13 - { 3, 2, 4, 5, true }, // Bank 14 - { 6, 0, 5, 1, true } // Bank 15 +CartridgeWD::BankOrg CartridgeWD::ourBankOrg[8] = { + // 0 1 2 3 4 5 6 7 + { 0, 0, 1, 3 }, // Bank 0, 8 2 1 - 1 - - - - + { 0, 1, 2, 3 }, // Bank 1, 9 1 1 1 1 - - - - + { 4, 5, 6, 7 }, // Bank 2, 10 - - - - 1 1 1 1 + { 7, 4, 2, 3 }, // Bank 3, 11 - - 1 1 1 - - 1 + { 0, 0, 6, 7 }, // Bank 4, 12 2 - - - - - 1 1 + { 0, 1, 7, 6 }, // Bank 5, 13 1 1 - - - - 1 1 + { 2, 3, 4, 5 }, // Bank 6, 14 - - 1 1 1 1 - - + { 6, 0, 5, 1 } // Bank 7, 15 1 1 - - - 1 1 - + // count 7 4 3 4 3 3 4 4 }; diff --git a/src/emucore/CartWD.hxx b/src/emucore/CartWD.hxx index 1afb7c856..e092f3fcd 100644 --- a/src/emucore/CartWD.hxx +++ b/src/emucore/CartWD.hxx @@ -28,32 +28,24 @@ class System; /** This is the cartridge class for a "Wickstead Design" prototype cart. - The ROM is normally 8K, but sometimes has an extra 3 bytes appended, - to be mapped as described below. There is also 64 bytes of RAM. + The ROM has 64 bytes of RAM. In this bankswitching scheme the 2600's 4K cartridge address space is broken into four 1K segments. The desired arrangement of 1K slices is selected by accessing $30 - $3F of TIA address space. The slices are mapped into all 4 segments at once as follows: - $0030: 0,0,1,2 - $0031: 0,1,3,2 - $0032: 4,5,6,7 - $0033: 7,4,3,2 - $0034: 0,0,6,7 - $0035: 0,1,7,6 - $0036: 3,2,4,5 - $0037: 6,0,5,1 - $0038: 0,0,1,2 - $0039: 0,1,3,2 - $003A: 4,5,6,7 - $003B: 7,4,3,2 - $003C: 0,0,6,7* - $003D: 0,1,7,6* - $003E: 3,2,4,5* - $003F: 6,0,5,1* + $0030, $0038: 0,0,1,3 + $0031, $0039: 0,1,2,3 + $0032, $003A: 4,5,6,7 + $0033, $003B: 7,4,2,3 - In the last 4 cases, the extra 3 bytes of ROM past the 8K boundary are - mapped into $3FC - $3FE of the uppermost (third) segment. + $0034, $003C: 0,0,6,7 + $0035, $003D: 0,1,7,6 + $0036, $003E: 2,3,4,5 + $0037, $003F: 6,0,5,1 + + + In the uppermost (third) segment, the byte at $3FC is overwritten by 0. The 64 bytes of RAM are accessible at $1000 - $103F (read port) and $1040 - $107F (write port). Because the RAM takes 128 bytes of address @@ -204,16 +196,15 @@ class CartridgeWD : public Cartridge /** Install the specified slice for segment three. - Note that this method also takes care of mapping extra 3 bytes. + Note that this method also takes care of setting one byte to 0. @param slice The slice to map into the segment - @param map3bytes Whether to map in an extra 3 bytes */ - void segmentThree(uInt8 slice, bool map3bytes); + void segmentThree(uInt8 slice); private: // The 8K ROM image of the cartridge - std::array myImage; + std::array myImage; // Indicates the actual size of the ROM image (either 8K or 8K + 3) size_t mySize; @@ -239,9 +230,8 @@ class CartridgeWD : public Cartridge // The arrangement of banks to use on each hotspot read struct BankOrg { uInt8 zero, one, two, three; - bool map3bytes; }; - static BankOrg ourBankOrg[16]; + static BankOrg ourBankOrg[8]; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/DefProps.hxx b/src/emucore/DefProps.hxx index 6db376b75..6d784356d 100644 --- a/src/emucore/DefProps.hxx +++ b/src/emucore/DefProps.hxx @@ -25,7 +25,7 @@ regenerated and the application recompiled. */ -#define DEF_PROPS_SIZE 3406 +#define DEF_PROPS_SIZE 3407 static const char* const DefProps[DEF_PROPS_SIZE][21] = { { "000509d1ed2b8d30a9d94be1b3b5febb", "Greg Zumwalt", "", "Jungle Jane (2003) (Greg Zumwalt) (Hack)", "Hack of Pitfall!", "Hack", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, @@ -1179,7 +1179,7 @@ static const char* const DefProps[DEF_PROPS_SIZE][21] = { { "55ace3c775f42eb46f08bb1dca9114e7", "", "", "Shadow Keep (04-03-2003) (Andrew Towers)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "55ef6ab2321ca0c3d369e63d59c059c8", "", "", "Pitfall! (Unknown) (PAL)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "55ef7b65066428367844342ed59f956c", "Atari - Roklan, Joe Gaucher, Alex Leavens", "CX2683", "Crazy Climber (1983) (Atari)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, - { "56210a3b9ea6d5dd8f417a357ed8ca92", "Probe 2000, Roger Booth, Todd Marshall, Robbin Daniels, Jim Wickstead", "3152VC", "Pursuit of the Pink Panther (Probe) (Prototype)", "AKA Adventures of the Pink Panther", "Prototype", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, + { "56210a3b9ea6d5dd8f417a357ed8ca92", "Probe 2000, Roger Booth, Todd Marshall, Robbin Daniels, Jim Wickstead", "3152VC", "Pursuit of the Pink Panther (Probe) (Prototype, bad dump)", "AKA Adventures of the Pink Panther", "Prototype", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "56300ed31fef018bd96768ccc982f7b4", "HES - Activision", "559", "Rad Action Pak - Kung-Fu Master, Freeway, Frostbite (1990) (HES) (PAL)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "5641c0ff707630d2dd829b26a9f2e98f", "Joystik", "", "Motocross (Joystik)", "AKA Motocross Racer", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "5643ee916f7dc760148fca4db3aa7d10", "", "", "Moon Patrol (Genesis)", "Genesis controller (C is jump)", "Hack of Moon Patrol", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, @@ -1343,6 +1343,7 @@ static const char* const DefProps[DEF_PROPS_SIZE][21] = { { "62921652f6634eb1a0940ed5489c7e18", "", "", "SCSIcide (V1.09) (2001) (Joe Grand)", "", "", "", "", "", "", "", "", "", "PADDLES_IAXDR", "", "", "AUTO 65", "", "", "", "" }, { "62992392ea651a16aa724a92e4596ed6", "Eric Mooney", "", "Invaders by Erik Mooney (Beta) (PD)", "", "New Release", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "62d1f50219edf9a429a9f004c19f31b3", "JWDA, Todd Marshall", "", "Euro Gen (02-01-83) (JWDA) (PAL)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, + { "62ee2b8f59e9cd6285bbdb674a952e8b", "Probe 2000, Roger Booth, Todd Marshall, Robbin Daniels, Jim Wickstead", "3152VC", "Pursuit of the Pink Panther (Probe) (Prototype)", "AKA Adventures of the Pink Panther", "Prototype", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "62f74a2736841191135514422b20382d", "", "", "Pharaoh's Curse (Unknown)", "", "", "", "", "", "", "", "", "", "", "", "", "", "PAL60", "20", "YES", "" }, { "62ffd175cac3f781ef6e4870136a2520", "", "", "2600 Digital Clock (V x.xx) (PD)", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, { "63166867f75869a3592b7a94ea62d147", "", "", "Indy 500 (Hack) [a1]", "Hack of Indy 500", "Hack", "", "", "", "", "", "", "", "DRIVING", "DRIVING", "", "", "", "", "", "" }, diff --git a/src/emucore/stella.pro b/src/emucore/stella.pro index 66d1a4bab..2f150812c 100644 --- a/src/emucore/stella.pro +++ b/src/emucore/stella.pro @@ -20368,6 +20368,14 @@ "Cart.MD5" "56210a3b9ea6d5dd8f417a357ed8ca92" "Cart.Manufacturer" "Probe 2000, Roger Booth, Todd Marshall, Robbin Daniels, Jim Wickstead" "Cart.ModelNo" "3152VC" +"Cart.Name" "Pursuit of the Pink Panther (Probe) (Prototype, bad dump)" +"Cart.Note" "AKA Adventures of the Pink Panther" +"Cart.Rarity" "Prototype" +"" + +"Cart.MD5" "62ee2b8f59e9cd6285bbdb674a952e8b" +"Cart.Manufacturer" "Probe 2000, Roger Booth, Todd Marshall, Robbin Daniels, Jim Wickstead" +"Cart.ModelNo" "3152VC" "Cart.Name" "Pursuit of the Pink Panther (Probe) (Prototype)" "Cart.Note" "AKA Adventures of the Pink Panther" "Cart.Rarity" "Prototype"