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 "bspf.hxx"
19#include "Launcher.hxx"
20#include "Bankswitch.hxx"
21#include "BrowserDialog.hxx"
22#include "DialogContainer.hxx"
23#include "EditTextWidget.hxx"
24#include "ProgressDialog.hxx"
25#include "FSNode.hxx"
26#include "Font.hxx"
27#include "MessageBox.hxx"
28#include "FrameBuffer.hxx"
29#include "MD5.hxx"
30#include "Props.hxx"
31#include "PropsSet.hxx"
32#include "Settings.hxx"
33#include "RomAuditDialog.hxx"
34
35// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
36RomAuditDialog::RomAuditDialog(OSystem& osystem, DialogContainer& parent,
37 const GUI::Font& font, int max_w, int max_h)
38 : Dialog(osystem, parent, font, "Audit ROMs"),
39 myFont(font),
40 myMaxWidth(max_w),
41 myMaxHeight(max_h)
42{
43 const int VBORDER = 10 + _th;
44 const int HBORDER = 10;
45
46 const int lineHeight = font.getLineHeight(),
47 fontWidth = font.getMaxCharWidth(),
48 fontHeight = font.getFontHeight(),
49 buttonWidth = font.getStringWidth("Audit path" + ELLIPSIS) + 20,
50 buttonHeight = font.getLineHeight() + 4,
51 lwidth = font.getStringWidth("ROMs without properties (skipped) ");
52 int xpos, ypos = VBORDER;
53 WidgetArray wid;
54
55 // Set real dimensions
56 _w = 64 * fontWidth + HBORDER * 2;
57 _h = 7 * (lineHeight + 4) + VBORDER;
58
59 // Audit path
60 ButtonWidget* romButton =
61 new ButtonWidget(this, font, HBORDER, ypos, buttonWidth, buttonHeight,
62 "Audit path" + ELLIPSIS, kChooseAuditDirCmd);
63 wid.push_back(romButton);
64 xpos = HBORDER + buttonWidth + 8;
65 myRomPath = new EditTextWidget(this, font, xpos, ypos + 1,
66 _w - xpos - HBORDER, lineHeight, "");
67 wid.push_back(myRomPath);
68
69 // Show results of ROM audit
70 ypos += buttonHeight + 16;
71 new StaticTextWidget(this, font, HBORDER, ypos, lwidth, fontHeight,
72 "ROMs with properties (renamed) ", TextAlign::Left);
73 myResults1 = new EditTextWidget(this, font, HBORDER + lwidth, ypos - 2,
74 fontWidth * 6, lineHeight, "");
75 myResults1->setEditable(false, true);
76 ypos += buttonHeight;
77 new StaticTextWidget(this, font, HBORDER, ypos, lwidth, fontHeight,
78 "ROMs without properties (skipped) ", TextAlign::Left);
79 myResults2 = new EditTextWidget(this, font, HBORDER + lwidth, ypos - 2,
80 fontWidth * 6, lineHeight, "");
81 myResults2->setEditable(false, true);
82
83 ypos += buttonHeight + 8;
84 new StaticTextWidget(this, font, HBORDER, ypos, _w - 20, fontHeight,
85 "(*) WARNING: Operation cannot be undone!",
86 TextAlign::Left);
87
88 // Add OK and Cancel buttons
89 addOKCancelBGroup(wid, font, "Audit", "Close");
90 addBGroupToFocusList(wid);
91}
92
93// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
94RomAuditDialog::~RomAuditDialog()
95{
96}
97
98// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
99void RomAuditDialog::loadConfig()
100{
101 const string& currentdir =
102 instance().launcher().currentNode().getShortPath();
103 const string& path = currentdir == "" ?
104 instance().settings().getString("romdir") : currentdir;
105
106 myRomPath->setText(path);
107 myResults1->setText("");
108 myResults2->setText("");
109}
110
111// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
112void RomAuditDialog::auditRoms()
113{
114 const string& auditPath = myRomPath->getText();
115 myResults1->setText("");
116 myResults2->setText("");
117
118 FilesystemNode node(auditPath);
119 FSList files;
120 files.reserve(2048);
121 node.getChildren(files, FilesystemNode::ListMode::FilesOnly);
122
123 // Create a progress dialog box to show the progress of processing
124 // the ROMs, since this is usually a time-consuming operation
125 ProgressDialog progress(this, instance().frameBuffer().font(),
126 "Auditing ROM files ...");
127 progress.setRange(0, int(files.size()) - 1, 5);
128
129 Properties props;
130 int renamed = 0, notfound = 0;
131 for(uInt32 idx = 0; idx < files.size(); ++idx)
132 {
133 string extension;
134 if(files[idx].isFile() &&
135 Bankswitch::isValidRomName(files[idx], extension))
136 {
137 bool renameSucceeded = false;
138
139 // Calculate the MD5 so we can get the rest of the info
140 // from the PropertiesSet (stella.pro)
141 const string& md5 = MD5::hash(files[idx]);
142 if(instance().propSet().getMD5(md5, props))
143 {
144 const string& name = props.get(PropType::Cart_Name);
145
146 // Only rename the file if we found a valid properties entry
147 if(name != "" && name != files[idx].getName())
148 {
149 const string& newfile = node.getPath() + name + "." + extension;
150 if(files[idx].getPath() != newfile && files[idx].rename(newfile))
151 renameSucceeded = true;
152 }
153 }
154 if(renameSucceeded)
155 ++renamed;
156 else
157 ++notfound;
158 }
159
160 // Update the progress bar, indicating one more ROM has been processed
161 progress.setProgress(idx);
162 }
163 progress.close();
164
165 myResults1->setText(Variant(renamed).toString());
166 myResults2->setText(Variant(notfound).toString());
167}
168
169// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
170void RomAuditDialog::handleCommand(CommandSender* sender, int cmd,
171 int data, int id)
172{
173 switch (cmd)
174 {
175 case GuiObject::kOKCmd:
176 if(!myConfirmMsg)
177 {
178 StringList msg;
179 msg.push_back("This operation cannot be undone. Your ROMs");
180 msg.push_back("will be modified, and as such there is a chance");
181 msg.push_back("that files may be lost. You are recommended");
182 msg.push_back("to back up your files before proceeding.");
183 msg.push_back("");
184 msg.push_back("If you're sure you want to proceed with the");
185 msg.push_back("audit, click 'OK', otherwise click 'Cancel'.");
186 myConfirmMsg = make_unique<GUI::MessageBox>
187 (this, myFont, msg, myMaxWidth, myMaxHeight, kConfirmAuditCmd,
188 "OK", "Cancel", "ROM Audit", false);
189 }
190 myConfirmMsg->show();
191 break;
192
193 case kConfirmAuditCmd:
194 auditRoms();
195 instance().launcher().reload();
196 break;
197
198 case kChooseAuditDirCmd:
199 createBrowser("Select ROM directory to audit");
200 myBrowser->show(myRomPath->getText(),
201 BrowserDialog::Directories, kAuditDirChosenCmd);
202 break;
203
204 case kAuditDirChosenCmd:
205 {
206 FilesystemNode dir(myBrowser->getResult());
207 myRomPath->setText(dir.getShortPath());
208 myResults1->setText("");
209 myResults2->setText("");
210 break;
211 }
212
213 default:
214 Dialog::handleCommand(sender, cmd, data, 0);
215 break;
216 }
217}
218
219// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
220void RomAuditDialog::createBrowser(const string& title)
221{
222 uInt32 w = 0, h = 0;
223 getDynamicBounds(w, h);
224
225 // Create file browser dialog
226 if(!myBrowser || uInt32(myBrowser->getWidth()) != w ||
227 uInt32(myBrowser->getHeight()) != h)
228 myBrowser = make_unique<BrowserDialog>(this, myFont, w, h, title);
229 else
230 myBrowser->setTitle(title);
231}
232