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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
33 | RomInfoWidget::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
48 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
58 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
69 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
81 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
169 | void 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 | |