1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include "Bankswitch.hxx"
19#include "Console.hxx"
20#include "Cart.hxx"
21#include "MouseControl.hxx"
22#include "SaveKey.hxx"
23#include "Dialog.hxx"
24#include "EditTextWidget.hxx"
25#include "RadioButtonWidget.hxx"
26#include "Launcher.hxx"
27#include "OSystem.hxx"
28#include "CartDetector.hxx"
29#include "ControllerDetector.hxx"
30#include "PopUpWidget.hxx"
31#include "Props.hxx"
32#include "PropsSet.hxx"
33#include "TabWidget.hxx"
34#include "TIAConstants.hxx"
35#include "Widget.hxx"
36#include "Font.hxx"
37
38#include "FrameBuffer.hxx"
39#include "TIASurface.hxx"
40#include "TIA.hxx"
41#include "Switches.hxx"
42#include "AudioSettings.hxx"
43
44#include "GameInfoDialog.hxx"
45
46// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
47GameInfoDialog::GameInfoDialog(
48 OSystem& osystem, DialogContainer& parent, const GUI::Font& font,
49 GuiObject* boss, int max_w, int max_h)
50 : Dialog(osystem, parent, font, "Game properties"),
51 CommandSender(boss)
52{
53 const GUI::Font& ifont = instance().frameBuffer().infoFont();
54 const int lineHeight = font.getLineHeight(),
55 fontWidth = font.getMaxCharWidth(),
56 fontHeight = font.getFontHeight(),
57 buttonHeight = font.getLineHeight() + 4,
58 infoLineHeight = ifont.getLineHeight();
59 const int VBORDER = 8;
60 const int HBORDER = 10;
61 const int VGAP = 4;
62
63 int xpos, ypos, lwidth, fwidth, pwidth, tabID;
64 WidgetArray wid;
65 VariantList items, ports, ctrls;
66 StaticTextWidget* t;
67
68 // Set real dimensions
69 setSize(53 * fontWidth + 8,
70 8 * (lineHeight + VGAP) + 1 * (infoLineHeight + VGAP) + VBORDER * 2 + _th +
71 buttonHeight + fontHeight + ifont.getLineHeight() + 20,
72 max_w, max_h);
73
74 // The tab widget
75 myTab = new TabWidget(this, font, 2, 4 + _th, _w - 2 * 2,
76 _h - (_th + buttonHeight + 20));
77 addTabWidget(myTab);
78
79 //////////////////////////////////////////////////////////////////////////////
80 // 1) Emulation properties
81 tabID = myTab->addTab("Emulation");
82
83 ypos = VBORDER;
84
85 t = new StaticTextWidget(myTab, font, HBORDER, ypos + 1, "Type (*) ");
86 pwidth = font.getStringWidth("CM (SpectraVideo CompuMate)");
87 items.clear();
88 for(uInt32 i = 0; i < uInt32(Bankswitch::Type::NumSchemes); ++i)
89 VarList::push_back(items, Bankswitch::BSList[i].desc, Bankswitch::BSList[i].name);
90 myBSType = new PopUpWidget(myTab, font, t->getRight() + 8, ypos,
91 pwidth, lineHeight, items, "");
92 wid.push_back(myBSType);
93 ypos += lineHeight + VGAP;
94
95 myTypeDetected = new StaticTextWidget(myTab, ifont, t->getRight() + 8, ypos,
96 "CM (SpectraVideo CompuMate) detected");
97 ypos += ifont.getLineHeight() + VGAP;
98
99 // Start bank
100 myStartBankLabel = new StaticTextWidget(myTab, font, HBORDER, ypos + 1, "Start bank (*) ");
101 items.clear();
102 myStartBank = new PopUpWidget(myTab, font, myStartBankLabel->getRight(), ypos,
103 font.getStringWidth("AUTO"), lineHeight, items, "", 0, 0);
104 wid.push_back(myStartBank);
105 ypos += lineHeight + VGAP * 4;
106
107 pwidth = font.getStringWidth("Auto-detect");
108 t = new StaticTextWidget(myTab, font, HBORDER, ypos + 1, "TV format ");
109 items.clear();
110 VarList::push_back(items, "Auto-detect", "AUTO");
111 VarList::push_back(items, "NTSC", "NTSC");
112 VarList::push_back(items, "PAL", "PAL");
113 VarList::push_back(items, "SECAM", "SECAM");
114 VarList::push_back(items, "NTSC50", "NTSC50");
115 VarList::push_back(items, "PAL60", "PAL60");
116 VarList::push_back(items, "SECAM60", "SECAM60");
117 myFormat = new PopUpWidget(myTab, font, t->getRight(), ypos,
118 pwidth, lineHeight, items, "", 0, 0);
119 wid.push_back(myFormat);
120
121 myFormatDetected = new StaticTextWidget(myTab, ifont, myFormat->getRight() + 8, ypos + 4, "SECAM60 detected");
122
123 // Phosphor
124 ypos += lineHeight + VGAP * 2;
125 myPhosphor = new CheckboxWidget(myTab, font, HBORDER, ypos + 1, "Phosphor (enabled for all ROMs)", kPhosphorChanged);
126 wid.push_back(myPhosphor);
127
128 ypos += lineHeight + VGAP;
129 myPPBlend = new SliderWidget(myTab, font,
130 HBORDER + 20, ypos,
131 "Blend ", 0, kPPBlendChanged, 4 * fontWidth, "%");
132 myPPBlend->setMinValue(0); myPPBlend->setMaxValue(100);
133 myPPBlend->setTickmarkIntervals(2);
134 wid.push_back(myPPBlend);
135
136 ypos += lineHeight + VGAP * 4;
137 mySound = new CheckboxWidget(myTab, font, HBORDER, ypos + 1, "Stereo sound");
138 wid.push_back(mySound);
139
140 // Add message concerning usage
141 ypos = myTab->getHeight() - 5 - fontHeight - ifont.getFontHeight() - 10;
142 new StaticTextWidget(myTab, ifont, HBORDER, ypos,
143 "(*) Changes require a ROM reload");
144
145 // Add items for tab 0
146 addToFocusList(wid, myTab, tabID);
147
148 //////////////////////////////////////////////////////////////////////////////
149 // 2) Console properties
150 wid.clear();
151 tabID = myTab->addTab("Console");
152
153 xpos = HBORDER; ypos = VBORDER;
154 lwidth = font.getStringWidth(GUI::RIGHT_DIFFICULTY + " ");
155
156 new StaticTextWidget(myTab, font, xpos, ypos + 1, "TV type");
157 myTVTypeGroup = new RadioButtonGroup();
158 RadioButtonWidget* r = new RadioButtonWidget(myTab, font, xpos + lwidth, ypos + 1,
159 "Color", myTVTypeGroup);
160 wid.push_back(r);
161 ypos += lineHeight;
162 r = new RadioButtonWidget(myTab, font, xpos + lwidth, ypos + 1,
163 "B/W", myTVTypeGroup);
164 wid.push_back(r);
165 ypos += lineHeight + VGAP * 2;
166
167 new StaticTextWidget(myTab, font, xpos, ypos+1, GUI::LEFT_DIFFICULTY);
168 myLeftDiffGroup = new RadioButtonGroup();
169 r = new RadioButtonWidget(myTab, font, xpos + lwidth, ypos + 1,
170 "A (Expert)", myLeftDiffGroup);
171 wid.push_back(r);
172 ypos += lineHeight;
173 r = new RadioButtonWidget(myTab, font, xpos + lwidth, ypos + 1,
174 "B (Novice)", myLeftDiffGroup);
175 wid.push_back(r);
176 ypos += lineHeight + VGAP * 2;
177
178 new StaticTextWidget(myTab, font, xpos, ypos+1, GUI::RIGHT_DIFFICULTY);
179 myRightDiffGroup = new RadioButtonGroup();
180 r = new RadioButtonWidget(myTab, font, xpos + lwidth, ypos + 1,
181 "A (Expert)", myRightDiffGroup);
182 wid.push_back(r);
183 ypos += lineHeight;
184 r = new RadioButtonWidget(myTab, font, xpos + lwidth, ypos + 1,
185 "B (Novice)", myRightDiffGroup);
186 wid.push_back(r);
187
188 // Add items for tab 1
189 addToFocusList(wid, myTab, tabID);
190
191 //////////////////////////////////////////////////////////////////////////////
192 // 3) Controller properties
193 wid.clear();
194 tabID = myTab->addTab("Controllers");
195
196 ctrls.clear();
197 VarList::push_back(ctrls, "Auto-detect", "AUTO");
198 VarList::push_back(ctrls, "Joystick", "JOYSTICK");
199 VarList::push_back(ctrls, "Paddles", "PADDLES");
200 VarList::push_back(ctrls, "Paddles_IAxis", "PADDLES_IAXIS");
201 VarList::push_back(ctrls, "Paddles_IAxDr", "PADDLES_IAXDR");
202 VarList::push_back(ctrls, "BoosterGrip", "BOOSTERGRIP");
203 VarList::push_back(ctrls, "Driving", "DRIVING");
204 VarList::push_back(ctrls, "Keyboard", "KEYBOARD");
205 VarList::push_back(ctrls, "AmigaMouse", "AMIGAMOUSE");
206 VarList::push_back(ctrls, "AtariMouse", "ATARIMOUSE");
207 VarList::push_back(ctrls, "Trakball", "TRAKBALL");
208 VarList::push_back(ctrls, "AtariVox", "ATARIVOX");
209 VarList::push_back(ctrls, "SaveKey", "SAVEKEY");
210 VarList::push_back(ctrls, "Sega Genesis", "GENESIS");
211 VarList::push_back(ctrls, "KidVid", "KIDVID");
212 VarList::push_back(ctrls, "MindLink", "MINDLINK");
213
214 ypos = VBORDER;
215 pwidth = font.getStringWidth("Paddles_IAxis");
216 myLeftPortLabel = new StaticTextWidget(myTab, font, HBORDER, ypos+1, "Left port ");
217 myLeftPort = new PopUpWidget(myTab, font, myLeftPortLabel->getRight(),
218 myLeftPortLabel->getTop()-1,
219 pwidth, lineHeight, ctrls, "", 0, kLeftCChanged);
220 wid.push_back(myLeftPort);
221 ypos += lineHeight + VGAP;
222
223 myLeftPortDetected = new StaticTextWidget(myTab, ifont, myLeftPort->getLeft(), ypos,
224 "Sega Genesis detected");
225 ypos += ifont.getLineHeight() + VGAP;
226
227 myRightPortLabel = new StaticTextWidget(myTab, font, HBORDER, ypos+1, "Right port ");
228 myRightPort = new PopUpWidget(myTab, font, myRightPortLabel->getRight(),
229 myRightPortLabel->getTop()-1,
230 pwidth, lineHeight, ctrls, "", 0, kRightCChanged);
231 wid.push_back(myRightPort);
232 ypos += lineHeight + VGAP;
233 myRightPortDetected = new StaticTextWidget(myTab, ifont, myRightPort->getLeft(), ypos,
234 "Sega Genesis detected");
235 ypos += ifont.getLineHeight() + VGAP + 4;
236
237 mySwapPorts = new CheckboxWidget(myTab, font, myLeftPort->getRight() + fontWidth*4,
238 myLeftPort->getTop()+1, "Swap ports");
239 wid.push_back(mySwapPorts);
240 mySwapPaddles = new CheckboxWidget(myTab, font, myRightPort->getRight() + fontWidth*4,
241 myRightPort->getTop()+1, "Swap paddles");
242 wid.push_back(mySwapPaddles);
243
244 // EEPROM erase button for left/right controller
245 //ypos += lineHeight + VGAP + 4;
246 pwidth = myRightPort->getWidth(); //font.getStringWidth("Erase EEPROM ") + 23;
247 myEraseEEPROMLabel = new StaticTextWidget(myTab, font, HBORDER, ypos, "AtariVox/SaveKey ");
248 myEraseEEPROMButton = new ButtonWidget(myTab, font, myEraseEEPROMLabel->getRight(), ypos - 4,
249 pwidth, buttonHeight, "Erase EEPROM", kEEButtonPressed);
250 wid.push_back(myEraseEEPROMButton);
251 myEraseEEPROMInfo = new StaticTextWidget(myTab, ifont, myEraseEEPROMButton->getRight() + 4,
252 myEraseEEPROMLabel->getTop() + 3, "(for this game only)");
253
254 ypos += lineHeight + VGAP * 4;
255 myMouseControl = new CheckboxWidget(myTab, font, xpos, ypos + 1, "Specific mouse axes", kMCtrlChanged);
256 wid.push_back(myMouseControl);
257
258 // Mouse controller specific axis
259 pwidth = font.getStringWidth("MindLink 0");
260 items.clear();
261 VarList::push_back(items, "None", static_cast<uInt32>(MouseControl::Type::NoControl));
262 VarList::push_back(items, "Paddle 0", static_cast<uInt32>(MouseControl::Type::Paddle0));
263 VarList::push_back(items, "Paddle 1", static_cast<uInt32>(MouseControl::Type::Paddle1));
264 VarList::push_back(items, "Paddle 2", static_cast<uInt32>(MouseControl::Type::Paddle2));
265 VarList::push_back(items, "Paddle 3", static_cast<uInt32>(MouseControl::Type::Paddle3));
266 VarList::push_back(items, "Driving 0", static_cast<uInt32>(MouseControl::Type::Driving0));
267 VarList::push_back(items, "Driving 1", static_cast<uInt32>(MouseControl::Type::Driving1));
268 VarList::push_back(items, "MindLink 0", static_cast<uInt32>(MouseControl::Type::MindLink0));
269 VarList::push_back(items, "MindLink 1", static_cast<uInt32>(MouseControl::Type::MindLink1));
270
271 xpos += 20;
272 ypos += lineHeight + VGAP;
273 myMouseX = new PopUpWidget(myTab, font, xpos, ypos, pwidth, lineHeight, items,
274 "X-Axis is ");
275 wid.push_back(myMouseX);
276
277 ypos += lineHeight + VGAP;
278 myMouseY = new PopUpWidget(myTab, font, myMouseX->getLeft(), ypos, pwidth, lineHeight, items,
279 "Y-Axis is ");
280 wid.push_back(myMouseY);
281
282 xpos = HBORDER; ypos += lineHeight + VGAP;
283 myMouseRange = new SliderWidget(myTab, font, HBORDER, ypos,
284 "Mouse axes range ", 0, 0, fontWidth * 4, "%");
285 myMouseRange->setMinValue(1); myMouseRange->setMaxValue(100);
286 myMouseRange->setTickmarkIntervals(4);
287 wid.push_back(myMouseRange);
288
289 // Add items for tab 2
290 addToFocusList(wid, myTab, tabID);
291
292 //////////////////////////////////////////////////////////////////////////////
293 // 4) Cartridge properties
294 wid.clear();
295 tabID = myTab->addTab("Cartridge");
296
297 xpos = HBORDER; ypos = VBORDER;
298 lwidth = font.getStringWidth("Manufacturer ");
299 fwidth = _w - lwidth - HBORDER * 2 - 2;
300 new StaticTextWidget(myTab, font, xpos, ypos + 1, lwidth, fontHeight, "Name");
301 myName = new EditTextWidget(myTab, font, xpos + lwidth, ypos - 1,
302 fwidth, lineHeight, "");
303 wid.push_back(myName);
304
305 ypos += lineHeight + VGAP;
306 new StaticTextWidget(myTab, font, xpos, ypos + 1, lwidth, fontHeight, "MD5");
307 myMD5 = new EditTextWidget(myTab, font, xpos + lwidth, ypos - 1,
308 fwidth, lineHeight, "");
309 myMD5->setEditable(false);
310
311 ypos += lineHeight + VGAP;
312 new StaticTextWidget(myTab, font, xpos, ypos + 1, lwidth, fontHeight, "Manufacturer");
313 myManufacturer = new EditTextWidget(myTab, font, xpos + lwidth, ypos - 1,
314 fwidth, lineHeight, "");
315 wid.push_back(myManufacturer);
316
317 ypos += lineHeight + VGAP;
318 new StaticTextWidget(myTab, font, xpos, ypos + 1, lwidth, fontHeight,
319 "Model", TextAlign::Left);
320 myModelNo = new EditTextWidget(myTab, font, xpos + lwidth, ypos - 1,
321 fwidth, lineHeight, "");
322 wid.push_back(myModelNo);
323
324 ypos += lineHeight + VGAP;
325 new StaticTextWidget(myTab, font, xpos, ypos + 1, lwidth, fontHeight, "Rarity");
326 myRarity = new EditTextWidget(myTab, font, xpos + lwidth, ypos - 1,
327 fwidth, lineHeight, "");
328 wid.push_back(myRarity);
329
330 ypos += lineHeight + VGAP;
331 new StaticTextWidget(myTab, font, xpos, ypos + 1, lwidth, fontHeight, "Note");
332 myNote = new EditTextWidget(myTab, font, xpos + lwidth, ypos - 1,
333 fwidth, lineHeight, "");
334 wid.push_back(myNote);
335
336 // Add items for tab 3
337 addToFocusList(wid, myTab, tabID);
338
339 // Activate the first tab
340 myTab->setActiveTab(0);
341
342 // Add Defaults, OK and Cancel buttons
343 wid.clear();
344 addDefaultsOKCancelBGroup(wid, font);
345 addBGroupToFocusList(wid);
346}
347
348// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
349void GameInfoDialog::loadConfig()
350{
351 if(instance().hasConsole())
352 {
353 myGameProperties = instance().console().properties();
354 }
355 else
356 {
357 const string& md5 = instance().launcher().selectedRomMD5();
358 instance().propSet().getMD5(md5, myGameProperties);
359 }
360
361 loadEmulationProperties(myGameProperties);
362 loadConsoleProperties(myGameProperties);
363 loadControllerProperties(myGameProperties);
364 loadCartridgeProperties(myGameProperties);
365
366 myTab->loadConfig();
367}
368
369// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
370void GameInfoDialog::loadEmulationProperties(const Properties& props)
371{
372 string bsDetected = "";
373
374 myBSType->setSelected(props.get(PropType::Cart_Type), "AUTO");
375 if(myBSType->getSelectedTag().toString() == "AUTO")
376 {
377 if(instance().hasConsole())
378 {
379 string bs = instance().console().about().BankSwitch;
380 size_t pos = bs.find_first_of('*');
381 // remove '*':
382 if(pos != string::npos)
383 bs = bs.substr(0, pos) + bs.substr(pos + 1);
384 bsDetected = bs + "detected";
385 }
386 else
387 {
388 const FilesystemNode& node = FilesystemNode(instance().launcher().selectedRom());
389 ByteBuffer image;
390 string md5 = props.get(PropType::Cart_MD5);
391 uInt32 size = 0;
392
393 // try to load the image for auto detection
394 if(!instance().hasConsole() &&
395 node.exists() && !node.isDirectory() && (image = instance().openROM(node, md5, size)) != nullptr)
396 {
397 bsDetected = Bankswitch::typeToName(CartDetector::autodetectType(image, size)) + " detected";
398 }
399 }
400 }
401 myTypeDetected->setLabel(bsDetected);
402
403 // Start bank
404 VariantList items;
405
406 VarList::push_back(items, "Auto", "AUTO");
407 if(instance().hasConsole())
408 {
409 uInt16 numBanks = instance().console().cartridge().bankCount();
410
411 for(uInt16 i = 0; i < numBanks; ++i)
412 VarList::push_back(items, i, i);
413 myStartBank->setEnabled(true);
414 }
415 else
416 {
417 string startBank = props.get(PropType::Cart_StartBank);
418
419 VarList::push_back(items, startBank, startBank);
420 myStartBank->setEnabled(false);
421 }
422 myStartBank->addItems(items);
423 myStartBank->setSelected(props.get(PropType::Cart_StartBank), "AUTO");
424
425 myFormat->setSelected(props.get(PropType::Display_Format), "AUTO");
426 if(instance().hasConsole() && myFormat->getSelectedTag().toString() == "AUTO")
427 {
428 const string& format = instance().console().about().DisplayFormat;
429 string label = format.substr(0, format.length() - 1);
430 myFormatDetected->setLabel(label + " detected");
431 }
432 else
433 myFormatDetected->setLabel("");
434
435 // if phosphor is always enabled, disable game specific phosphor settings
436 bool alwaysPhosphor = instance().settings().getString("tv.phosphor") == "always";
437 bool usePhosphor = props.get(PropType::Display_Phosphor) == "YES";
438 myPhosphor->setState(usePhosphor);
439 myPhosphor->setEnabled(!alwaysPhosphor);
440 if (alwaysPhosphor)
441 myPhosphor->setLabel("Phosphor (enabled for all ROMs)");
442 else
443 myPhosphor->setLabel("Phosphor");
444 myPPBlend->setEnabled(!alwaysPhosphor && usePhosphor);
445
446 const string& blend = props.get(PropType::Display_PPBlend);
447 myPPBlend->setValue(atoi(blend.c_str()));
448
449 mySound->setState(props.get(PropType::Cart_Sound) == "STEREO");
450 // if stereo is always enabled, disable game specific stereo setting
451 mySound->setEnabled(!instance().audioSettings().stereo());
452}
453
454// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
455void GameInfoDialog::loadConsoleProperties(const Properties& props)
456{
457 myLeftDiffGroup->setSelected(props.get(PropType::Console_LeftDiff) == "A" ? 0 : 1);
458 myRightDiffGroup->setSelected(props.get(PropType::Console_RightDiff) == "A" ? 0 : 1);
459 myTVTypeGroup->setSelected(props.get(PropType::Console_TVType) == "BW" ? 1 : 0);
460}
461
462// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
463void GameInfoDialog::loadControllerProperties(const Properties& props)
464{
465 string controller = props.get(PropType::Controller_Left);
466 myLeftPort->setSelected(controller, "AUTO");
467 controller = props.get(PropType::Controller_Right);
468 myRightPort->setSelected(controller, "AUTO");
469
470 mySwapPorts->setState(props.get(PropType::Console_SwapPorts) == "YES");
471 mySwapPaddles->setState(props.get(PropType::Controller_SwapPaddles) == "YES");
472
473 // MouseAxis property (potentially contains 'range' information)
474 istringstream m_axis(props.get(PropType::Controller_MouseAxis));
475 string m_control, m_range;
476 m_axis >> m_control;
477 bool autoAxis = BSPF::equalsIgnoreCase(m_control, "AUTO");
478 myMouseControl->setState(!autoAxis);
479 if(autoAxis)
480 {
481 myMouseX->setSelectedIndex(0);
482 myMouseY->setSelectedIndex(0);
483 }
484 else
485 {
486 myMouseX->setSelected(m_control[0] - '0');
487 myMouseY->setSelected(m_control[1] - '0');
488 }
489 myMouseX->setEnabled(!autoAxis);
490 myMouseY->setEnabled(!autoAxis);
491 if(m_axis >> m_range)
492 {
493 myMouseRange->setValue(atoi(m_range.c_str()));
494 }
495 else
496 {
497 myMouseRange->setValue(100);
498 }
499
500 updateControllerStates();
501}
502
503// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
504void GameInfoDialog::loadCartridgeProperties(const Properties& props)
505{
506 myName->setText(props.get(PropType::Cart_Name));
507 myMD5->setText(props.get(PropType::Cart_MD5));
508 myManufacturer->setText(props.get(PropType::Cart_Manufacturer));
509 myModelNo->setText(props.get(PropType::Cart_ModelNo));
510 myRarity->setText(props.get(PropType::Cart_Rarity));
511 myNote->setText(props.get(PropType::Cart_Note));
512}
513
514// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
515void GameInfoDialog::saveConfig()
516{
517 // Emulation properties
518 myGameProperties.set(PropType::Cart_Type, myBSType->getSelectedTag().toString());
519 myGameProperties.set(PropType::Cart_StartBank, myStartBank->getSelectedTag().toString());
520 myGameProperties.set(PropType::Display_Format, myFormat->getSelectedTag().toString());
521 myGameProperties.set(PropType::Display_Phosphor, myPhosphor->getState() ? "YES" : "NO");
522 myGameProperties.set(PropType::Display_PPBlend, myPPBlend->getValueLabel() == "Off" ? "0" :
523 myPPBlend->getValueLabel());
524 myGameProperties.set(PropType::Cart_Sound, mySound->getState() ? "STEREO" : "MONO");
525
526 // Console properties
527 myGameProperties.set(PropType::Console_TVType, myTVTypeGroup->getSelected() ? "BW" : "COLOR");
528 myGameProperties.set(PropType::Console_LeftDiff, myLeftDiffGroup->getSelected() ? "B" : "A");
529 myGameProperties.set(PropType::Console_RightDiff, myRightDiffGroup->getSelected() ? "B" : "A");
530
531 // Controller properties
532 myGameProperties.set(PropType::Controller_Left, myLeftPort->getSelectedTag().toString());
533 myGameProperties.set(PropType::Controller_Right, myRightPort->getSelectedTag().toString());
534 myGameProperties.set(PropType::Console_SwapPorts, (mySwapPorts->isEnabled() && mySwapPorts->getState()) ? "YES" : "NO");
535 myGameProperties.set(PropType::Controller_SwapPaddles, (/*mySwapPaddles->isEnabled() &&*/ mySwapPaddles->getState()) ? "YES" : "NO");
536
537 // MouseAxis property (potentially contains 'range' information)
538 string mcontrol = "AUTO";
539 if(myMouseControl->getState())
540 mcontrol = myMouseX->getSelectedTag().toString() +
541 myMouseY->getSelectedTag().toString();
542 string range = myMouseRange->getValueLabel();
543 if(range != "100")
544 mcontrol += " " + range;
545 myGameProperties.set(PropType::Controller_MouseAxis, mcontrol);
546
547 // Cartridge properties
548 myGameProperties.set(PropType::Cart_Name, myName->getText());
549 myGameProperties.set(PropType::Cart_Manufacturer, myManufacturer->getText());
550 myGameProperties.set(PropType::Cart_ModelNo, myModelNo->getText());
551 myGameProperties.set(PropType::Cart_Rarity, myRarity->getText());
552 myGameProperties.set(PropType::Cart_Note, myNote->getText());
553
554 // Always insert; if the properties are already present, nothing will happen
555 instance().propSet().insert(myGameProperties);
556 instance().saveConfig();
557
558 // In any event, inform the Console
559 if(instance().hasConsole())
560 {
561 instance().console().setProperties(myGameProperties);
562
563 // update 'Emulation' tab settings immediately
564 instance().console().setFormat(myFormat->getSelected());
565 instance().frameBuffer().tiaSurface().enablePhosphor(myPhosphor->getState(), myPPBlend->getValue());
566 instance().console().initializeAudio();
567
568 // update 'Console' tab settings immediately
569 instance().console().switches().setTvColor(myTVTypeGroup->getSelected() == 0);
570 instance().console().switches().setLeftDifficultyA(myLeftDiffGroup->getSelected() == 0);
571 instance().console().switches().setRightDifficultyA(myRightDiffGroup->getSelected() == 0);
572
573 // update 'Controllers' tab settings immediately
574 instance().console().setControllers(myGameProperties.get(PropType::Cart_MD5));
575 }
576}
577
578// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
579void GameInfoDialog::setDefaults()
580{
581 // Load the default properties
582 Properties defaultProperties;
583 const string& md5 = myGameProperties.get(PropType::Cart_MD5);
584
585 instance().propSet().getMD5(md5, defaultProperties, true);
586
587 switch(myTab->getActiveTab())
588 {
589 case 0: // Emulation properties
590 loadEmulationProperties(defaultProperties);
591 break;
592
593 case 1: // Console properties
594 loadConsoleProperties(defaultProperties);
595 break;
596
597 case 2: // Controller properties
598 loadControllerProperties(defaultProperties);
599 break;
600
601 case 3: // Cartridge properties
602 loadCartridgeProperties(defaultProperties);
603 break;
604
605 default: // make the compiler happy
606 break;
607 }
608}
609
610// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
611void GameInfoDialog::updateControllerStates()
612{
613 bool swapPorts = mySwapPorts->getState();
614 bool autoDetect = false;
615 ByteBuffer image;
616 string md5 = myGameProperties.get(PropType::Cart_MD5);
617 uInt32 size = 0;
618
619 // try to load the image for auto detection
620 if(!instance().hasConsole())
621 {
622 const FilesystemNode& node = FilesystemNode(instance().launcher().selectedRom());
623
624 autoDetect = node.exists() && !node.isDirectory() && (image = instance().openROM(node, md5, size)) != nullptr;
625 }
626 string label = "";
627 Controller::Type type = Controller::getType(myLeftPort->getSelectedTag().toString());
628
629 if(type == Controller::Type::Unknown)
630 {
631 if(instance().hasConsole())
632 label = (!swapPorts ? instance().console().leftController().name()
633 : instance().console().rightController().name()) + " detected";
634 else if(autoDetect)
635 label = ControllerDetector::detectName(image.get(), size, type,
636 !swapPorts ? Controller::Jack::Left : Controller::Jack::Right,
637 instance().settings()) + " detected";
638 }
639 myLeftPortDetected->setLabel(label);
640
641 label = "";
642 type = Controller::getType(myRightPort->getSelectedTag().toString());
643
644 if(type == Controller::Type::Unknown)
645 {
646 if(instance().hasConsole())
647 label = (!swapPorts ? instance().console().rightController().name()
648 : instance().console().leftController().name()) + " detected";
649 else if(autoDetect)
650 label = ControllerDetector::detectName(image.get(), size, type,
651 !swapPorts ? Controller::Jack::Right : Controller::Jack::Left,
652 instance().settings()) + " detected";
653 }
654 myRightPortDetected->setLabel(label);
655
656 const string& contrLeft = myLeftPort->getSelectedTag().toString();
657 const string& contrRight = myRightPort->getSelectedTag().toString();
658 bool enableEEEraseButton = false;
659
660 // Compumate bankswitching scheme doesn't allow to select controllers
661 bool enableSelectControl = myBSType->getSelectedTag() != "CM";
662 // Enable Swap Paddles checkbox only for paddle games
663 bool enableSwapPaddles = BSPF::startsWithIgnoreCase(contrLeft, "PADDLES") ||
664 BSPF::startsWithIgnoreCase(contrRight, "PADDLES") ||
665 BSPF::startsWithIgnoreCase(myLeftPortDetected->getLabel(), "Paddles") ||
666 BSPF::startsWithIgnoreCase(myRightPortDetected->getLabel(), "Paddles");
667
668 if(instance().hasConsole())
669 {
670 const Controller& lport = instance().console().leftController();
671 const Controller& rport = instance().console().rightController();
672
673 // we only enable the button if we have a valid previous and new controller.
674 bool enableBtnForLeft =
675 (contrLeft == "AUTO" || contrLeft == "SAVEKEY" || contrLeft == "ATARIVOX") &&
676 (lport.type() == Controller::Type::SaveKey || lport.type() == Controller::Type::AtariVox);
677 bool enableBtnForRight =
678 (contrRight == "AUTO" || contrRight == "SAVEKEY" || contrRight == "ATARIVOX") &&
679 (rport.type() == Controller::Type::SaveKey || rport.type() == Controller::Type::AtariVox);
680 enableEEEraseButton = enableBtnForLeft || enableBtnForRight;
681 }
682
683 myLeftPortLabel->setEnabled(enableSelectControl);
684 myRightPortLabel->setEnabled(enableSelectControl);
685 myLeftPort->setEnabled(enableSelectControl);
686 myRightPort->setEnabled(enableSelectControl);
687
688 mySwapPorts->setEnabled(enableSelectControl);
689 mySwapPaddles->setEnabled(enableSwapPaddles);
690
691 myEraseEEPROMLabel->setEnabled(enableEEEraseButton);
692 myEraseEEPROMButton->setEnabled(enableEEEraseButton);
693 myEraseEEPROMInfo->setEnabled(enableEEEraseButton);
694}
695
696// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
697void GameInfoDialog::eraseEEPROM()
698{
699 Controller& lport = instance().console().leftController();
700 Controller& rport = instance().console().rightController();
701
702 if(lport.type() == Controller::Type::SaveKey || lport.type() == Controller::Type::AtariVox)
703 {
704 SaveKey& skey = static_cast<SaveKey&>(lport);
705 skey.eraseCurrent();
706 }
707
708 if(rport.type() == Controller::Type::SaveKey || rport.type() == Controller::Type::AtariVox)
709 {
710 SaveKey& skey = static_cast<SaveKey&>(rport);
711 skey.eraseCurrent();
712 }
713}
714
715// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
716void GameInfoDialog::handleCommand(CommandSender* sender, int cmd,
717 int data, int id)
718{
719 switch (cmd)
720 {
721 case GuiObject::kOKCmd:
722 saveConfig();
723 close();
724 break;
725
726 case GuiObject::kDefaultsCmd:
727 setDefaults();
728 break;
729
730 case TabWidget::kTabChangedCmd:
731 if(data == 2) // 'Controllers' tab selected
732 updateControllerStates();
733
734 // The underlying dialog still needs access to this command
735 Dialog::handleCommand(sender, cmd, data, 0);
736 break;
737
738 case kLeftCChanged:
739 case kRightCChanged:
740 updateControllerStates();
741 break;
742
743 case kEEButtonPressed:
744 eraseEEPROM();
745 break;
746
747 case kPhosphorChanged:
748 {
749 bool status = myPhosphor->getState();
750 myPPBlend->setEnabled(status);
751 break;
752 }
753
754 case kPPBlendChanged:
755 if(myPPBlend->getValue() == 0)
756 {
757 myPPBlend->setValueLabel("Off");
758 myPPBlend->setValueUnit("");
759 }
760 else
761 myPPBlend->setValueUnit("%");
762 break;
763
764 case kMCtrlChanged:
765 {
766 bool state = myMouseControl->getState();
767 myMouseX->setEnabled(state);
768 myMouseY->setEnabled(state);
769 break;
770 }
771
772 default:
773 Dialog::handleCommand(sender, cmd, data, 0);
774 break;
775 }
776}
777