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 "EventHandler.hxx"
19#include "FrameBuffer.hxx"
20#include "Dialog.hxx"
21#include "FBSurface.hxx"
22#include "Font.hxx"
23#include "OSystem.hxx"
24#include "ControllerDetector.hxx"
25#include "Props.hxx"
26#include "PNGLibrary.hxx"
27#include "Rect.hxx"
28#include "Widget.hxx"
29#include "TIAConstants.hxx"
30#include "RomInfoWidget.hxx"
31
32// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
33RomInfoWidget::RomInfoWidget(GuiObject* boss, const GUI::Font& font,
34 int x, int y, int w, int h)
35 : Widget(boss, font, x, y, w, h),
36 mySurfaceIsValid(false),
37 myHaveProperties(false),
38 myAvail(w > 400 ?
39 Common::Size(TIAConstants::viewableWidth*2, TIAConstants::viewableHeight*2) :
40 Common::Size(TIAConstants::viewableWidth, TIAConstants::viewableHeight))
41{
42 _flags = Widget::FLAG_ENABLED;
43 _bgcolor = kDlgColor;
44 _bgcolorlo = kBGColorLo;
45}
46
47// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
48void RomInfoWidget::reloadProperties(const FilesystemNode& node)
49{
50 // The ROM may have changed since we were last in the browser, either
51 // by saving a different image or through a change in video renderer,
52 // so we reload the properties
53 if(myHaveProperties)
54 parseProperties(node);
55}
56
57// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
58void RomInfoWidget::setProperties(const Properties& props, const FilesystemNode& node)
59{
60 myHaveProperties = true;
61 myProperties = props;
62
63 // Decide whether the information should be shown immediately
64 if(instance().eventHandler().state() == EventHandlerState::LAUNCHER)
65 parseProperties(node);
66}
67
68// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
69void RomInfoWidget::clearProperties()
70{
71 myHaveProperties = mySurfaceIsValid = false;
72 if(mySurface)
73 mySurface->setVisible(mySurfaceIsValid);
74
75 // Decide whether the information should be shown immediately
76 if(instance().eventHandler().state() == EventHandlerState::LAUNCHER)
77 setDirty();
78}
79
80// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
81void RomInfoWidget::parseProperties(const FilesystemNode& node)
82{
83 // Check if a surface has ever been created; if so, we use it
84 // The surface will always be the maximum size, but sometimes we'll
85 // only draw certain parts of it
86 if(mySurface == nullptr)
87 {
88 mySurface = instance().frameBuffer().allocateSurface(
89 TIAConstants::viewableWidth*2, TIAConstants::viewableHeight*2);
90 mySurface->attributes().smoothing = true;
91 mySurface->applyAttributes();
92
93 dialog().addSurface(mySurface);
94 }
95
96 // Initialize to empty properties entry
97 mySurfaceErrorMsg = "";
98 mySurfaceIsValid = false;
99 myRomInfo.clear();
100
101#ifdef PNG_SUPPORT
102 // Get a valid filename representing a snapshot file for this rom
103 const string& filename = instance().snapshotLoadDir() +
104 myProperties.get(PropType::Cart_Name) + ".png";
105
106 // Read the PNG file
107 try
108 {
109 instance().png().loadImage(filename, *mySurface);
110
111 // Scale surface to available image area
112 const Common::Rect& src = mySurface->srcRect();
113 float scale = std::min(float(myAvail.w) / src.w(), float(myAvail.h) / src.h()) *
114 instance().frameBuffer().hidpiScaleFactor();
115 mySurface->setDstSize(uInt32(src.w() * scale), uInt32(src.h() * scale));
116 mySurfaceIsValid = true;
117 }
118 catch(const runtime_error& e)
119 {
120 mySurfaceErrorMsg = e.what();
121 }
122#else
123 mySurfaceErrorMsg = "PNG image loading not supported";
124#endif
125 if(mySurface)
126 mySurface->setVisible(mySurfaceIsValid);
127
128 // Now add some info for the message box below the image
129 myRomInfo.push_back("Name: " + myProperties.get(PropType::Cart_Name));
130 myRomInfo.push_back("Manufacturer: " + myProperties.get(PropType::Cart_Manufacturer));
131 myRomInfo.push_back("Model: " + myProperties.get(PropType::Cart_ModelNo));
132 myRomInfo.push_back("Rarity: " + myProperties.get(PropType::Cart_Rarity));
133 myRomInfo.push_back("Note: " + myProperties.get(PropType::Cart_Note));
134 bool swappedPorts = myProperties.get(PropType::Console_SwapPorts) == "YES";
135
136 // Load the image for controller auto detection
137 string left = myProperties.get(PropType::Controller_Left);
138 string right = myProperties.get(PropType::Controller_Right);
139 Controller::Type leftType = Controller::getType(left);
140 Controller::Type rightType = Controller::getType(right);
141 try
142 {
143 ByteBuffer image;
144 string md5 = myProperties.get(PropType::Cart_MD5);
145 uInt32 size = 0;
146
147 if(node.exists() && !node.isDirectory() &&
148 (image = instance().openROM(node, md5, size)) != nullptr)
149 {
150 left = ControllerDetector::detectName(image.get(), size, leftType,
151 !swappedPorts ? Controller::Jack::Left : Controller::Jack::Right,
152 instance().settings());
153 right = ControllerDetector::detectName(image.get(), size, rightType,
154 !swappedPorts ? Controller::Jack::Right : Controller::Jack::Left,
155 instance().settings());
156 }
157 }
158 catch(const runtime_error&)
159 {
160 // Do nothing; we simply don't update the controllers if openROM
161 // failed for any reason
162 left = right = "";
163 }
164 if(left != "" && right != "")
165 myRomInfo.push_back("Controllers: " + (left + " (left), " + right + " (right)"));
166}
167
168// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
169void RomInfoWidget::drawWidget(bool hilite)
170{
171 FBSurface& s = dialog().surface();
172 bool onTop = _boss->dialog().isOnTop();
173
174 const int yoff = myAvail.h + 10;
175
176 s.fillRect(_x+2, _y+2, _w-4, _h-4, onTop ? _bgcolor : _bgcolorlo);
177 s.frameRect(_x, _y, _w, _h, kColor);
178 s.frameRect(_x, _y+yoff, _w, _h-yoff, kColor);
179
180 if(!myHaveProperties) return;
181
182 if(mySurfaceIsValid)
183 {
184 const Common::Rect& dst = mySurface->dstRect();
185 const uInt32 scale = instance().frameBuffer().hidpiScaleFactor();
186 uInt32 x = _x*scale + ((_w*scale - dst.w()) >> 1);
187 uInt32 y = _y*scale + ((yoff*scale - dst.h()) >> 1);
188
189 // Make sure when positioning the snapshot surface that we take
190 // the dialog surface position into account
191 const Common::Rect& s_dst = s.dstRect();
192 mySurface->setDstPos(x + s_dst.x(), y + s_dst.y());
193 }
194 else if(mySurfaceErrorMsg != "")
195 {
196 const GUI::Font& font = instance().frameBuffer().font();
197 uInt32 x = _x + ((_w - font.getStringWidth(mySurfaceErrorMsg)) >> 1);
198 uInt32 y = _y + ((yoff - font.getLineHeight()) >> 1);
199 s.drawString(font, mySurfaceErrorMsg, x, y, _w - 10, onTop ? _textcolor : _shadowcolor);
200 }
201
202 int xpos = _x + 8, ypos = _y + yoff + 10;
203 for(const auto& info: myRomInfo)
204 {
205 int lines = s.drawString(_font, info, xpos, ypos, _w - 16, _font.getFontHeight() * 3,
206 onTop ? _textcolor : _shadowcolor);
207 ypos += _font.getLineHeight() + (lines - 1) * _font.getFontHeight();
208 }
209}
210