Merge remote-tracking branch 'remotes/origin/feature/dbg-save-as-dialog'
|
@ -32,6 +32,8 @@
|
|||
|
||||
* Added sound to Time Machine playback.
|
||||
|
||||
* Added browser dialogs for user saved files.
|
||||
|
||||
* Extended global hotkeys for input devices & ports settings.
|
||||
|
||||
* Increased sample size for CDFJ+.
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
<li><a href="#CartridgeRAMInformation">Detailed Cartridge Extended RAM Information</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#DistellaConfiguration">Distella Configuration Files</a></li>
|
||||
<li><a href="#DistellaConfiguration">DiStella Configuration Files</a></li>
|
||||
<li><a href="#Howtohack">Tutorial: How to hack a ROM</a></li>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -119,14 +119,14 @@ feature that no other 2600 debugger has; it's <b>completely</b> cross-platform.<
|
|||
|
||||
<li>Rewind previous advance operations and undo rewinds.</li>
|
||||
|
||||
<li>Supports Distella 'configuration directives', which may be used to
|
||||
<li>Supports DiStella 'configuration directives', which may be used to
|
||||
override automatic code/data determination in the disassembly. For now,
|
||||
the following directives are supported: CODE, GFX, PGFX, COL, PCOL, BCOL, AUD, DATA, ROW.
|
||||
These directives can be entered at the debugger prompt, or be (automatically)
|
||||
loaded and saved in configuration files.</li>
|
||||
|
||||
<li>Extensive disassembly support, both from the emulation core and with help
|
||||
from Distella. Where possible, the disassembly differentiates between code,
|
||||
from DiStella. Where possible, the disassembly differentiates between code,
|
||||
player and playfield graphics, colors and audio (ie, addresses stored in
|
||||
GRPx, PFx, COLUxx, AUDxx)
|
||||
and data (addresses used as an operand of a command). Code sections are also
|
||||
|
@ -302,7 +302,7 @@ more convenient.
|
|||
<li>
|
||||
"<rom_filename>.cfg"</br>
|
||||
This file is described in <a href="#DistellaConfiguration">
|
||||
<b>Distella Configuration Files</b></a>.
|
||||
<b>DiStella Configuration Files</b></a>.
|
||||
</br></br>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -929,7 +929,7 @@ Type "help 'cmd'" to see extended information about the given command.</p>
|
|||
c - Carry Flag: set (0 or 1), or toggle (no arg)
|
||||
cheat - Use a cheat code (see manual for cheat types)
|
||||
clearbreaks - Clear all breakpoints
|
||||
clearconfig - Clear Distella config directives [bank xx]
|
||||
clearconfig - Clear DiStella config directives [bank xx]
|
||||
clearsavestateifs - Clear all savestate points
|
||||
cleartraps - Clear all traps
|
||||
clearwatches - Clear all watches
|
||||
|
@ -966,11 +966,11 @@ clearsavestateifs - Clear all savestate points
|
|||
joy1fire - Set joystick 1 fire button to value <x> (0 or 1), or toggle (no arg)
|
||||
jump - Scroll disassembly to address xx
|
||||
listbreaks - List breakpoints
|
||||
listconfig - List Distella config directives [bank xx]
|
||||
listconfig - List DiStella config directives [bank xx]
|
||||
listfunctions - List user-defined functions
|
||||
listsavestateifs - List savestate points
|
||||
listtraps - List traps
|
||||
loadconfig - Load Distella config file
|
||||
loadconfig - Load DiStella config file
|
||||
loadallstates - Load all emulator states
|
||||
loadstate - Load emulator state xx (0-9)
|
||||
n - Negative Flag: set (0 or 1), or toggle (no arg)
|
||||
|
@ -989,14 +989,14 @@ clearsavestateifs - Clear all savestate points
|
|||
runto - Run until string xx in disassembly
|
||||
runtopc - Run until PC is set to value xx
|
||||
s - Set Stack Pointer to value xx
|
||||
save - Save breaks, watches, traps and functions to file xx
|
||||
saveaccess - Save access counters to CSV file
|
||||
saveconfig - Save Distella config file (with default name)
|
||||
savedis - Save Distella disassembly (with default name)
|
||||
saverom - Save (possibly patched) ROM (with default name)
|
||||
saveses - Save console session (with default name)
|
||||
save - Save breaks, watches, traps and functions to file xx (use ? for file dialog)
|
||||
saveaccess - Save access counters to CSV file (use ? for file dialog)
|
||||
saveconfig - Save DiStella config file (with default name)
|
||||
savedis - Save DiStella disassembly (use ? for file dialog)
|
||||
saverom - Save (possibly patched) ROM (use ? for file dialog)
|
||||
saveses - Save console session (use ? for file dialog)
|
||||
savesnap - Save current TIA image to PNG file
|
||||
saveallstatea - Save all emulator states
|
||||
saveallstates - Save all emulator states
|
||||
savestate - Save emulator state xx (valid args 0-9)
|
||||
savestateif - Create savestate on <condition>
|
||||
scanline - Advance emulation by <xx> scanlines (default=1)
|
||||
|
@ -1349,11 +1349,11 @@ file is loaded, the disassembly will have labels. Even without a symbol file, th
|
|||
differentiate between code, graphics, data and unused bytes. There are actually two
|
||||
levels of disassembly in Stella. First, the emulation core tracks accesses as a game
|
||||
is running, making for very accurate results. This is known as a <b>dynamic</b> analysis.
|
||||
Second, the built-in Distella code does a <b>static</b> analysis, which tentatively fills
|
||||
Second, the built-in DiStella code does a <b>static</b> analysis, which tentatively fills
|
||||
in sections that the dynamic disassembler missed (usually because the addresses haven't
|
||||
been accessed at runtime yet).</p>
|
||||
<p>As such, code can be marked in two ways (absolute, when done by the emulation core),
|
||||
and tentative (when done by Distella, and the emulation core hasn't accessed it yet).
|
||||
and tentative (when done by DiStella, and the emulation core hasn't accessed it yet).
|
||||
Such 'tentative' code is marked with the '*' symbol, indicating that it has the potential
|
||||
to be accessed as code sometime during the program run. This gives very useful information,
|
||||
since it can indicate areas toggled by an option in the game (ie, when a player dies,
|
||||
|
@ -1379,7 +1379,7 @@ by the <a href="#Breakpoints">break</a> command, <b>not</b> the conditional "bre
|
|||
(which makes sense: conditional breaks can break on any condition, the Program
|
||||
Counter isn't necessarily involved).</li>
|
||||
<li><b>Labels</b>: Any labels assigned to the given address, either generated
|
||||
automatically by Distella, read from a DASM symbol file or custom
|
||||
automatically by DiStella, read from a DASM symbol file or custom
|
||||
labels created by the user. If 'Show PC addresses'
|
||||
(see <a href="#DisassemblySettings"><b>ROM Disassembly Settings</b></a>) is enabled,
|
||||
the address will be shown in grey.</li>
|
||||
|
@ -1393,7 +1393,7 @@ Note that only code, graphics or data will show bytes and can be edited.</li>
|
|||
|
||||
<p>At this point, we should explain the various 'types' that the disassembler
|
||||
can use. These are known as 'directives', and partly correspond to configuration
|
||||
options from the standalone Distella program. They are listed in order of
|
||||
options from the standalone DiStella program. They are listed in order of
|
||||
decreasing hierarchy:</p>
|
||||
<table border="1" cellpadding=4>
|
||||
<tr>
|
||||
|
@ -1520,7 +1520,7 @@ matches the address of the disassembly line where the mouse was clicked.</li>
|
|||
<li><b>Disassemble @ current line</b>: Disassemble from the disassembly line where the mouse was clicked.
|
||||
This allows to fill gaps in the disassembly and is most useful for bankswitched ROMs.</li>
|
||||
|
||||
<li><b>Show tentative code</b>: Allow Distella to do a static analysis/disassembly.</li>
|
||||
<li><b>Show tentative code</b>: Allow DiStella to do a static analysis/disassembly.</li>
|
||||
|
||||
<li><b>Show PC addresses</b>: Show Program Counter addresses as labels (where there
|
||||
isn't already a defined label).</li>
|
||||
|
@ -1528,7 +1528,7 @@ isn't already a defined label).</li>
|
|||
<li><b>Show GFX as binary</b>: Switch between displaying/editing GFX and PGFX sections
|
||||
in either binary or hexidecimal.</li>
|
||||
|
||||
<li><b>Use address relocation</b>: Corresponds to the Distella '-r' option
|
||||
<li><b>Use address relocation</b>: Corresponds to the DiStella '-r' option
|
||||
(Relocate calls out of address range).</br>
|
||||
Note: The code will continue to run fine, but the ROM image will be altered.</li>
|
||||
|
||||
|
@ -1603,7 +1603,7 @@ the RAM in the DPC scheme is not viewable by the 6507, so its addresses start fr
|
|||
|
||||
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
||||
<br>
|
||||
<h1><a name="DistellaConfiguration">Distella Configuration Files</a></h1>
|
||||
<h1><a name="DistellaConfiguration">DiStella Configuration Files</a></h1>
|
||||
<p>As mentioned in <a href="#Disassembly"><b>ROM Disassembly</b></a>, Stella supports the following directives:
|
||||
CODE, GFX, PGFX, COL, PCOL, BCOL, AUD, DATA, ROW. While the debugger will try to automatically mark address
|
||||
space with the appropriate directive, there are times when it will fail. There are
|
||||
|
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
@ -3189,6 +3189,16 @@
|
|||
<td>Make the start path follow ROM launcher navigation.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><pre>-userdir <dir></pre></td>
|
||||
<td>Set the path to save user files (property exports, debugger saves) to.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><pre>-saveuserdir <0|1></pre></td>
|
||||
<td>Update the user path when navigating in browser.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><pre>-maxres <WxH></pre></td>
|
||||
<td>Useful for developers, this sets the maximum size of window that
|
||||
|
@ -3930,7 +3940,7 @@
|
|||
<tr><td>Allow all 4 directions ...</td><td>Allow all 4 joystick directions to be pressed simultaneously</td><td>-joyallow4</td></tr>
|
||||
<tr><td>Use modifier key combos</td><td>Enable using modifier keys in keyboard actions</td><td>-modcombo</td></tr>
|
||||
<tr><td>Swap Stelladaptor ports</td><td>Swap the order of the detected Stelladaptors/2600-daptors (see <b>Advanced Configuration - <a href="#Adaptor">Stelladaptor/2600-daptor Support</a></b>)</td><td>-saport</td></tr>
|
||||
<tr><td>Joystick database</td><td>Show all joysticks that Stella knows about, with the option to remove them</td><td> </td></tr>
|
||||
<tr><td>Joystick Database</td><td>Show all joysticks that Stella knows about, with the option to remove them</td><td> </td></tr>
|
||||
<tr><td>Erase EEPROM</td><td>Erase the whole AtariVox/SaveKey flash memory</td><td> </td></tr>
|
||||
<tr><td>AtariVox serial port</td><td>Described in further detail in <b>Advanced Configuration - <a href="#AtariVox">AtariVox/SaveKey Support</a></b> </td><td>-avoxport</td></tr>
|
||||
</table>
|
||||
|
|
|
@ -336,6 +336,17 @@ namespace BSPF
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool isWhiteSpace(const char s)
|
||||
{
|
||||
const string WHITESPACES = " ,.;:+-*&/\\'";
|
||||
|
||||
for(size_t i = 0; i < WHITESPACES.length(); ++i)
|
||||
if(s == WHITESPACES[i])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace BSPF
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1041,7 +1041,7 @@ string CartDebug::saveConfigFile()
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string CartDebug::saveDisassembly()
|
||||
string CartDebug::saveDisassembly(string path)
|
||||
{
|
||||
string NTSC_COLOR[16] = {
|
||||
"BLACK", "YELLOW", "BROWN", "ORANGE",
|
||||
|
@ -1350,9 +1350,16 @@ string CartDebug::saveDisassembly()
|
|||
// And finally, output the disassembly
|
||||
out << buf.str();
|
||||
|
||||
const string& propsname =
|
||||
myConsole.properties().get(PropType::Cart_Name) + ".asm";
|
||||
FilesystemNode node(myOSystem.defaultSaveDir().getPath() + propsname);
|
||||
|
||||
if(path.empty())
|
||||
path = myOSystem.userDir().getPath()
|
||||
+ myConsole.properties().get(PropType::Cart_Name) + ".asm";
|
||||
else
|
||||
// Append default extension when missing
|
||||
if(path.find_last_of('.') == string::npos)
|
||||
path += ".asm";
|
||||
|
||||
FilesystemNode node(path);
|
||||
stringstream retVal;
|
||||
try
|
||||
{
|
||||
|
@ -1370,11 +1377,18 @@ string CartDebug::saveDisassembly()
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string CartDebug::saveRom()
|
||||
string CartDebug::saveRom(string path)
|
||||
{
|
||||
const string& rom = myConsole.properties().get(PropType::Cart_Name) + ".a26";
|
||||
if(path.empty())
|
||||
path = myOSystem.userDir().getPath()
|
||||
+ myConsole.properties().get(PropType::Cart_Name) + ".a26";
|
||||
else
|
||||
// Append default extension when missing
|
||||
if(path.find_last_of('.') == string::npos)
|
||||
path += ".a26";
|
||||
|
||||
FilesystemNode node(path);
|
||||
|
||||
FilesystemNode node(myOSystem.defaultSaveDir().getPath() + rom);
|
||||
if(myConsole.cartridge().saveROM(node))
|
||||
return "saved ROM as " + node.getShortPath();
|
||||
else
|
||||
|
@ -1382,7 +1396,7 @@ string CartDebug::saveRom()
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string CartDebug::saveAccessFile()
|
||||
string CartDebug::saveAccessFile(string path)
|
||||
{
|
||||
stringstream out;
|
||||
out << myConsole.tia().getAccessCounters();
|
||||
|
@ -1391,8 +1405,15 @@ string CartDebug::saveAccessFile()
|
|||
|
||||
try
|
||||
{
|
||||
const string& rom = myConsole.properties().get(PropType::Cart_Name) + ".csv";
|
||||
FilesystemNode node(myOSystem.defaultSaveDir().getPath() + rom);
|
||||
if(path.empty())
|
||||
path = myOSystem.userDir().getPath()
|
||||
+ myConsole.properties().get(PropType::Cart_Name) + ".csv";
|
||||
else
|
||||
// Append default extension when missing
|
||||
if(path.find_last_of('.') == string::npos)
|
||||
path += ".csv";
|
||||
|
||||
FilesystemNode node(path);
|
||||
|
||||
node.write(out);
|
||||
return "saved access counters as " + node.getShortPath();
|
||||
|
|
|
@ -236,13 +236,13 @@ class CartDebug : public DebuggerSystem
|
|||
/**
|
||||
Save disassembly and ROM file
|
||||
*/
|
||||
string saveDisassembly();
|
||||
string saveRom();
|
||||
string saveDisassembly(string path);
|
||||
string saveRom(string path);
|
||||
|
||||
/**
|
||||
Save access counters file
|
||||
*/
|
||||
string saveAccessFile();
|
||||
string saveAccessFile(string path);
|
||||
|
||||
/**
|
||||
Show Distella directives (both set by the user and determined by Distella)
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "PromptWidget.hxx"
|
||||
#include "RomWidget.hxx"
|
||||
#include "ProgressDialog.hxx"
|
||||
#include "BrowserDialog.hxx"
|
||||
#include "TimerManager.hxx"
|
||||
#include "Vec.hxx"
|
||||
|
||||
|
@ -564,6 +565,12 @@ string DebuggerParser::eval()
|
|||
return buf.str();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const string& DebuggerParser::cartName() const
|
||||
{
|
||||
return debugger.myOSystem.console().properties().get(PropType::Cart_Name);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void DebuggerParser::listTraps(bool listCond)
|
||||
{
|
||||
|
@ -629,10 +636,6 @@ string DebuggerParser::trapStatus(const Trap& trap)
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string DebuggerParser::saveScriptFile(string file)
|
||||
{
|
||||
// Append 'script' extension when necessary
|
||||
if(file.find_last_of('.') == string::npos)
|
||||
file += ".script";
|
||||
|
||||
stringstream out;
|
||||
Debugger::FunctionDefMap funcs = debugger.getFunctionDefMap();
|
||||
for(const auto& [name, cmd]: funcs)
|
||||
|
@ -674,7 +677,16 @@ string DebuggerParser::saveScriptFile(string file)
|
|||
out << endl;
|
||||
}
|
||||
|
||||
FilesystemNode node(debugger.myOSystem.defaultSaveDir().getPath() + file);
|
||||
// Append 'script' extension when necessary
|
||||
if(file.find_last_of('.') == string::npos)
|
||||
file += ".script";
|
||||
|
||||
// Use user dir if no path is provided
|
||||
if(file.find_first_of(FilesystemNode::PATH_SEPARATOR) == string::npos)
|
||||
file = debugger.myOSystem.userDir().getPath() + file;
|
||||
|
||||
FilesystemNode node(file);
|
||||
|
||||
try
|
||||
{
|
||||
node.write(out);
|
||||
|
@ -1132,8 +1144,7 @@ void DebuggerParser::executeDump()
|
|||
else
|
||||
{
|
||||
ostringstream file;
|
||||
file << debugger.myOSystem.defaultSaveDir()
|
||||
<< debugger.myOSystem.console().properties().get(PropType::Cart_Name) << "_dbg_";
|
||||
file << debugger.myOSystem.userDir() << cartName() << "_dbg_";
|
||||
if(execDepth > 0)
|
||||
{
|
||||
file << execPrefix;
|
||||
|
@ -1228,7 +1239,7 @@ void DebuggerParser::executeExec()
|
|||
file += ".script";
|
||||
FilesystemNode node(file);
|
||||
if (!node.exists())
|
||||
node = FilesystemNode(debugger.myOSystem.defaultSaveDir().getPath() + file);
|
||||
node = FilesystemNode(debugger.myOSystem.userDir().getPath() + file);
|
||||
|
||||
if (argCount == 2) {
|
||||
execPrefix = argStrings[1];
|
||||
|
@ -1833,14 +1844,28 @@ void DebuggerParser::executeS()
|
|||
// "save"
|
||||
void DebuggerParser::executeSave()
|
||||
{
|
||||
commandResult << saveScriptFile(argStrings[0]);
|
||||
if(argCount && argStrings[0] == "?")
|
||||
{
|
||||
debugger.myDialog->showBrowser(DebuggerDialog::svScript, cartName() + ".script");
|
||||
// avoid printing a new prompt
|
||||
commandResult << "_EXIT_DEBUGGER";
|
||||
}
|
||||
else
|
||||
commandResult << saveScriptFile(argStrings[0]);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "saveaccess"
|
||||
void DebuggerParser::executeSaveAccess()
|
||||
{
|
||||
commandResult << debugger.cartDebug().saveAccessFile();
|
||||
if(argCount && argStrings[0] == "?")
|
||||
{
|
||||
debugger.myDialog->showBrowser(DebuggerDialog::svAccess, cartName() + ".csv");
|
||||
// avoid printing a new prompt
|
||||
commandResult << "_EXIT_DEBUGGER";
|
||||
}
|
||||
else
|
||||
commandResult << debugger.cartDebug().saveAccessFile(argCount ? argStrings[0] : EmptyString);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -1854,14 +1879,28 @@ void DebuggerParser::executeSaveconfig()
|
|||
// "savedis"
|
||||
void DebuggerParser::executeSavedisassembly()
|
||||
{
|
||||
commandResult << debugger.cartDebug().saveDisassembly();
|
||||
if(argCount && argStrings[0] == "?")
|
||||
{
|
||||
debugger.myDialog->showBrowser(DebuggerDialog::svDis, cartName() + ".asm");
|
||||
// avoid printing a new prompt
|
||||
commandResult << "_EXIT_DEBUGGER";
|
||||
}
|
||||
else
|
||||
commandResult << debugger.cartDebug().saveDisassembly(argCount ? argStrings[0] : EmptyString);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// "saverom"
|
||||
void DebuggerParser::executeSaverom()
|
||||
{
|
||||
commandResult << debugger.cartDebug().saveRom();
|
||||
if(argCount && argStrings[0] == "?")
|
||||
{
|
||||
debugger.myDialog->showBrowser(DebuggerDialog::svRom, cartName() + ".a26");
|
||||
// avoid printing a new prompt
|
||||
commandResult << "_EXIT_DEBUGGER";
|
||||
}
|
||||
else
|
||||
commandResult << debugger.cartDebug().saveRom(argCount ? argStrings[0] : EmptyString);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -1870,13 +1909,27 @@ void DebuggerParser::executeSaveses()
|
|||
{
|
||||
ostringstream filename;
|
||||
auto timeinfo = BSPF::localTime();
|
||||
filename << debugger.myOSystem.defaultSaveDir()
|
||||
<< std::put_time(&timeinfo, "session_%F_%H-%M-%S.txt");
|
||||
FilesystemNode file(filename.str());
|
||||
if(debugger.prompt().saveBuffer(file))
|
||||
commandResult << "saved " + file.getShortPath() + " OK";
|
||||
filename << std::put_time(&timeinfo, "session_%F_%H-%M-%S.txt");
|
||||
|
||||
if(argCount && argStrings[0] == "?")
|
||||
{
|
||||
debugger.myDialog->showBrowser(DebuggerDialog::svSession, filename.str());
|
||||
commandResult << "_EXIT_DEBUGGER";
|
||||
}
|
||||
else
|
||||
commandResult << "unable to save session";
|
||||
{
|
||||
ostringstream path;
|
||||
if(argCount)
|
||||
path << argStrings[0];
|
||||
else
|
||||
path << debugger.myOSystem.userDir() << filename.str();
|
||||
FilesystemNode file(path.str());
|
||||
|
||||
if(debugger.prompt().saveBuffer(file))
|
||||
commandResult << "saved " + file.getShortPath() + " OK";
|
||||
else
|
||||
commandResult << "unable to save session";
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -2616,9 +2669,10 @@ std::array<DebuggerParser::Command, 100> DebuggerParser::commands = { {
|
|||
"dump",
|
||||
"Dump data at address <xx> [to yy] [1: memory; 2: CPU state; 4: input regs]",
|
||||
"Example:\n"
|
||||
" dump f000 - dumps 128 bytes @ f000\n"
|
||||
" dump f000 - dumps 128 bytes from f000\n"
|
||||
" dump f000 f0ff - dumps all bytes from f000 to f0ff\n"
|
||||
" dump f000 f0ff 7 - dumps all bytes from f000 to f0ff, CPU state and input registers into a file",
|
||||
" dump f000 f0ff 7 - dumps all bytes from f000 to f0ff,\n"
|
||||
" CPU state and input registers into a file in user dir",
|
||||
true,
|
||||
false,
|
||||
{ Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
|
||||
|
@ -3042,7 +3096,8 @@ std::array<DebuggerParser::Command, 100> DebuggerParser::commands = { {
|
|||
{
|
||||
"save",
|
||||
"Save breaks, watches, traps and functions to file xx",
|
||||
"Example: save commands.script",
|
||||
"Example: save commands.script, save ?\n"
|
||||
"NOTE: saves to user dir by default",
|
||||
true,
|
||||
false,
|
||||
{ Parameters::ARG_FILE, Parameters::ARG_END_ARGS },
|
||||
|
@ -3052,10 +3107,11 @@ std::array<DebuggerParser::Command, 100> DebuggerParser::commands = { {
|
|||
{
|
||||
"saveaccess",
|
||||
"Save the access counters to CSV file",
|
||||
"Example: saveaccess (no parameters)",
|
||||
"Example: saveaccess, saveaccess ?\n"
|
||||
"NOTE: saves to user dir by default",
|
||||
false,
|
||||
false,
|
||||
{ Parameters::ARG_END_ARGS },
|
||||
{ Parameters::ARG_FILE, Parameters::ARG_END_ARGS },
|
||||
std::mem_fn(&DebuggerParser::executeSaveAccess)
|
||||
},
|
||||
|
||||
|
@ -3071,34 +3127,34 @@ std::array<DebuggerParser::Command, 100> DebuggerParser::commands = { {
|
|||
|
||||
{
|
||||
"savedis",
|
||||
"Save Distella disassembly (with default name)",
|
||||
"Example: savedis\n"
|
||||
"NOTE: saves to default save location",
|
||||
"Save Distella disassembly",
|
||||
"Example: savedis, savedis ?\n"
|
||||
"NOTE: saves to user dir by default",
|
||||
false,
|
||||
false,
|
||||
{ Parameters::ARG_END_ARGS },
|
||||
{ Parameters::ARG_FILE, Parameters::ARG_END_ARGS },
|
||||
std::mem_fn(&DebuggerParser::executeSavedisassembly)
|
||||
},
|
||||
|
||||
{
|
||||
"saverom",
|
||||
"Save (possibly patched) ROM (with default name)",
|
||||
"Example: saverom\n"
|
||||
"NOTE: saves to default save location",
|
||||
"Save (possibly patched) ROM",
|
||||
"Example: saverom, saverom ?\n"
|
||||
"NOTE: saves to user dir by default",
|
||||
false,
|
||||
false,
|
||||
{ Parameters::ARG_END_ARGS },
|
||||
{ Parameters::ARG_FILE, Parameters::ARG_END_ARGS },
|
||||
std::mem_fn(&DebuggerParser::executeSaverom)
|
||||
},
|
||||
|
||||
{
|
||||
"saveses",
|
||||
"Save console session (with default name)",
|
||||
"Example: saveses\n"
|
||||
"NOTE: saves to default save location",
|
||||
"Save console session",
|
||||
"Example: saveses, saveses ?\n"
|
||||
"NOTE: saves to user dir by default",
|
||||
false,
|
||||
false,
|
||||
{ Parameters::ARG_END_ARGS },
|
||||
{ Parameters::ARG_FILE, Parameters::ARG_END_ARGS },
|
||||
std::mem_fn(&DebuggerParser::executeSaveses)
|
||||
},
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@ class DebuggerParser
|
|||
bool validateArgs(int cmd);
|
||||
string eval();
|
||||
string saveScriptFile(string file);
|
||||
const string& cartName() const;
|
||||
|
||||
private:
|
||||
// Constants for argument processing
|
||||
|
|
|
@ -34,7 +34,7 @@ DataGridOpsWidget::DataGridOpsWidget(GuiObject* boss, const GUI::Font& font,
|
|||
_zeroButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
|
||||
"0", kDGZeroCmd);
|
||||
_zeroButton->setToolTip("Zero currently selected value");
|
||||
|
||||
|
||||
ypos += bheight + space;
|
||||
_invButton = new ButtonWidget(boss, font, xpos, ypos, bwidth, bheight,
|
||||
"Inv", kDGInvertCmd);
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "ConsoleMediumBFont.hxx"
|
||||
#include "StellaMediumFont.hxx"
|
||||
#include "OptionsDialog.hxx"
|
||||
#include "BrowserDialog.hxx"
|
||||
#include "StateManager.hxx"
|
||||
#include "FrameManager.hxx"
|
||||
#include "OSystem.hxx"
|
||||
|
@ -258,6 +259,30 @@ void DebuggerDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
loadConfig();
|
||||
break;
|
||||
|
||||
case kSvAccessCmd:
|
||||
runCommand("saveaccess");
|
||||
break;
|
||||
|
||||
case kSvDisCmd:
|
||||
runCommand("savedis");
|
||||
break;
|
||||
|
||||
case kSvRomCmd:
|
||||
runCommand("saverom");
|
||||
break;
|
||||
|
||||
case kSvScriptCmd:
|
||||
runCommand("save");
|
||||
break;
|
||||
|
||||
case kSvSessionCmd:
|
||||
runCommand("saveses");
|
||||
break;
|
||||
|
||||
case kBdCancelCmd:
|
||||
runCommand();
|
||||
break;
|
||||
|
||||
case RomWidget::kInvalidateListing:
|
||||
// Only do a full redraw if the disassembly tab is actually showing
|
||||
myRom->invalidate(myRomTab->getActiveTab() == 0);
|
||||
|
@ -399,6 +424,82 @@ void DebuggerDialog::createFont()
|
|||
tooltip().setFont(*myNFont);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void DebuggerDialog::showBrowser(BrowserType type, const string& defaultName)
|
||||
{
|
||||
int cmd;
|
||||
string title;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case BrowserType::svAccess:
|
||||
cmd = kSvAccessCmd;
|
||||
title = "Access Counters";
|
||||
break;
|
||||
|
||||
case BrowserType::svDis:
|
||||
cmd = kSvDisCmd;
|
||||
title = "Disassembly";
|
||||
break;
|
||||
|
||||
case BrowserType::svRom:
|
||||
cmd = kSvRomCmd;
|
||||
title = "ROM";
|
||||
break;
|
||||
|
||||
case BrowserType::svScript:
|
||||
cmd = kSvScriptCmd;
|
||||
title = "Workbench";
|
||||
break;
|
||||
|
||||
case BrowserType::svSession:
|
||||
cmd = kSvSessionCmd;
|
||||
title = "Session";
|
||||
break;
|
||||
|
||||
default:
|
||||
cmd = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if(cmd)
|
||||
{
|
||||
createBrowser("Save " + title + " as");
|
||||
|
||||
const string path = instance().userDir().getPath() + defaultName;
|
||||
myBrowser->show(path, BrowserDialog::FileSave, cmd, kBdCancelCmd);
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void DebuggerDialog::runCommand(const string& command)
|
||||
{
|
||||
if(command != EmptyString)
|
||||
{
|
||||
FilesystemNode dir(myBrowser->getResult());
|
||||
|
||||
string result = instance().debugger().parser().run(command + " {" + dir.getShortPath() + "}");
|
||||
prompt().print(result + '\n');
|
||||
}
|
||||
prompt().printPrompt();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void DebuggerDialog::createBrowser(const string& title)
|
||||
{
|
||||
uInt32 w = 0, h = 0;
|
||||
getDynamicBounds(w, h);
|
||||
if(w > uInt32(_font.getMaxCharWidth() * 80))
|
||||
w = _font.getMaxCharWidth() * 80;
|
||||
|
||||
// Create file browser dialog
|
||||
if(!myBrowser || uInt32(myBrowser->getWidth()) != w ||
|
||||
uInt32(myBrowser->getHeight()) != h)
|
||||
myBrowser = make_unique<BrowserDialog>(this, instance().frameBuffer().font(), w, h, title);
|
||||
else
|
||||
myBrowser->setTitle(title);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void DebuggerDialog::showFatalMessage(const string& msg)
|
||||
{
|
||||
|
|
|
@ -34,6 +34,7 @@ class TiaZoomWidget;
|
|||
class CartDebugWidget;
|
||||
class CartRamWidget;
|
||||
class OptionsDialog;
|
||||
class BrowserDialog;
|
||||
|
||||
namespace GUI {
|
||||
class MessageBox;
|
||||
|
@ -54,6 +55,13 @@ class DebuggerDialog : public Dialog
|
|||
kMediumFontMinW = 1160, kMediumFontMinH = 770,
|
||||
kLargeFontMinW = 1160, kLargeFontMinH = 870
|
||||
};
|
||||
enum BrowserType {
|
||||
svAccess, // saveaccess
|
||||
svDis, // savedis
|
||||
svRom, // saverom
|
||||
svScript, // save
|
||||
svSession // saveses
|
||||
};
|
||||
|
||||
DebuggerDialog(OSystem& osystem, DialogContainer& parent,
|
||||
int x, int y, int w, int h);
|
||||
|
@ -74,6 +82,7 @@ class DebuggerDialog : public Dialog
|
|||
|
||||
void showFatalMessage(const string& msg);
|
||||
void saveConfig() override;
|
||||
void showBrowser(BrowserType type, const string& defaultName);
|
||||
|
||||
private:
|
||||
void setPosition() override { positionAt(0); }
|
||||
|
@ -113,11 +122,20 @@ class DebuggerDialog : public Dialog
|
|||
kDDSAdvCmd = 'DDsv',
|
||||
kDDRewindCmd = 'DDrw',
|
||||
kDDUnwindCmd = 'DDuw',
|
||||
kDDRunCmd = 'DDex',
|
||||
kDDRunCmd = 'DDex',
|
||||
kDDExitFatalCmd = 'DDer',
|
||||
kDDOptionsCmd = 'DDop'
|
||||
kDDOptionsCmd = 'DDop',
|
||||
kSvAccessCmd = 'SvAc',
|
||||
kSvDisCmd = 'SvDs',
|
||||
kSvRomCmd = 'SvRm',
|
||||
kSvScriptCmd = 'SvSc',
|
||||
kSvSessionCmd = 'SvSs',
|
||||
kBdCancelCmd = 'SvCn'
|
||||
};
|
||||
|
||||
void runCommand(const string& command = EmptyString);
|
||||
void createBrowser(const string& title);
|
||||
|
||||
TabWidget *myTab{nullptr}, *myRomTab{nullptr};
|
||||
|
||||
PromptWidget* myPrompt{nullptr};
|
||||
|
@ -135,7 +153,8 @@ class DebuggerDialog : public Dialog
|
|||
ButtonWidget* myUnwindButton{nullptr};
|
||||
|
||||
unique_ptr<GUI::MessageBox> myFatalError;
|
||||
unique_ptr<OptionsDialog> myOptions;
|
||||
unique_ptr<OptionsDialog> myOptions;
|
||||
unique_ptr<BrowserDialog> myBrowser;
|
||||
|
||||
unique_ptr<GUI::Font> myLFont; // used for labels
|
||||
unique_ptr<GUI::Font> myNFont; // used for normal text
|
||||
|
|
|
@ -49,14 +49,6 @@ void FilesystemNode::setPath(const string& path)
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
FilesystemNode& FilesystemNode::operator/=(const string& path)
|
||||
{
|
||||
// This part could probably be put in a virtual function, but it seems like
|
||||
// a waste since almost every system uses the same separator, except Windows
|
||||
#ifdef BSPF_WINDOWS
|
||||
#define PATH_SEPARATOR '\\'
|
||||
#else
|
||||
#define PATH_SEPARATOR '/'
|
||||
#endif
|
||||
|
||||
if (path != EmptyString)
|
||||
{
|
||||
string newPath = getPath();
|
||||
|
@ -67,7 +59,6 @@ FilesystemNode& FilesystemNode::operator/=(const string& path)
|
|||
}
|
||||
|
||||
return *this;
|
||||
#undef PATH_SEPARATOR
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -48,6 +48,13 @@ class FSList : public vector<FilesystemNode> { };
|
|||
*/
|
||||
class FilesystemNode
|
||||
{
|
||||
public:
|
||||
#ifdef BSPF_WINDOWS
|
||||
static constexpr char PATH_SEPARATOR = '\\';
|
||||
#else
|
||||
static constexpr char PATH_SEPARATOR = '/';
|
||||
#endif
|
||||
|
||||
public:
|
||||
/**
|
||||
* Flag to tell listDir() which kind of files to list.
|
||||
|
@ -486,7 +493,7 @@ class AbstractFSNode
|
|||
const char* start = str.c_str();
|
||||
const char* cur = start + str.size() - 2;
|
||||
|
||||
while (cur >= start && !(*cur == '/' || *cur == '\\'))
|
||||
while (cur >= start && *cur != FilesystemNode::PATH_SEPARATOR)
|
||||
--cur;
|
||||
|
||||
return cur + 1;
|
||||
|
|
|
@ -217,8 +217,8 @@ void OSystem::loadConfig(const Settings::Options& options)
|
|||
{
|
||||
// Get base directory and config file from derived class
|
||||
// It will decide whether it can override its default location
|
||||
string baseDir, cfgFile, defSaveDir, defLoadDir;
|
||||
getBaseDirAndConfig(baseDir, cfgFile, defSaveDir, defLoadDir,
|
||||
string baseDir, cfgFile, homeDir;
|
||||
getBaseDirAndConfig(baseDir, cfgFile, homeDir,
|
||||
ourOverrideBaseDirWithApp, ourOverrideBaseDir);
|
||||
|
||||
// Get fully-qualified pathnames, and make directories when needed
|
||||
|
@ -228,13 +228,9 @@ void OSystem::loadConfig(const Settings::Options& options)
|
|||
if(!cfgFile.empty())
|
||||
myConfigFile = FilesystemNode(cfgFile);
|
||||
|
||||
myDefaultSaveDir = FilesystemNode(defSaveDir);
|
||||
if(!myDefaultSaveDir.isDirectory())
|
||||
myDefaultSaveDir.makeDir();
|
||||
|
||||
myDefaultLoadDir = FilesystemNode(defLoadDir);
|
||||
if(!myDefaultLoadDir.isDirectory())
|
||||
myDefaultLoadDir.makeDir();
|
||||
myHomeDir = FilesystemNode(homeDir);
|
||||
if(!myHomeDir.isDirectory())
|
||||
myHomeDir.makeDir();
|
||||
|
||||
#ifdef SQLITE_SUPPORT
|
||||
mySettingsDb = make_shared<SettingsDb>(myBaseDir.getPath(), "settings");
|
||||
|
@ -243,9 +239,16 @@ void OSystem::loadConfig(const Settings::Options& options)
|
|||
#endif
|
||||
|
||||
mySettings->setRepository(createSettingsRepository());
|
||||
|
||||
mySettings->load(options);
|
||||
|
||||
// userDir is NOT affected by '-baseDir'and '-basedirinapp' params
|
||||
string userDir = mySettings->getString("userdir");
|
||||
if(userDir.empty())
|
||||
userDir = homeDir;
|
||||
myUserDir = FilesystemNode(userDir);
|
||||
if(!myUserDir.isDirectory())
|
||||
myUserDir.makeDir();
|
||||
|
||||
Logger::instance().setLogParameters(mySettings->getInt("loglevel"),
|
||||
mySettings->getBool("logtoconsole"));
|
||||
Logger::debug("Loading config options ...");
|
||||
|
@ -295,7 +298,7 @@ void OSystem::setConfigPaths()
|
|||
#ifdef PNG_SUPPORT
|
||||
const string& ssSaveDir = mySettings->getString("snapsavedir");
|
||||
if(ssSaveDir == EmptyString)
|
||||
mySnapshotSaveDir = defaultSaveDir();
|
||||
mySnapshotSaveDir = userDir();
|
||||
else
|
||||
mySnapshotSaveDir = FilesystemNode(ssSaveDir);
|
||||
if(!mySnapshotSaveDir.isDirectory())
|
||||
|
@ -303,7 +306,7 @@ void OSystem::setConfigPaths()
|
|||
|
||||
const string& ssLoadDir = mySettings->getString("snaploaddir");
|
||||
if(ssLoadDir == EmptyString)
|
||||
mySnapshotLoadDir = defaultLoadDir();
|
||||
mySnapshotLoadDir = userDir();
|
||||
else
|
||||
mySnapshotLoadDir = FilesystemNode(ssLoadDir);
|
||||
if(!mySnapshotLoadDir.isDirectory())
|
||||
|
@ -333,6 +336,14 @@ void OSystem::setConfigPaths()
|
|||
#endif
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void OSystem::setUserDir(const string& path)
|
||||
{
|
||||
mySettings->setValue("userdir", path);
|
||||
|
||||
myUserDir = FilesystemNode(path);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool OSystem::checkUserPalette(bool outputError) const
|
||||
{
|
||||
|
|
|
@ -318,11 +318,11 @@ class OSystem
|
|||
const FilesystemNode& romFile() const { return myRomFile; }
|
||||
|
||||
/**
|
||||
The default locations for saving and loading various files that
|
||||
don't already have a specific location.
|
||||
The default and user defined locations for saving and loading various
|
||||
files that don't already have a specific location.
|
||||
*/
|
||||
const FilesystemNode& defaultSaveDir() const { return myDefaultSaveDir; }
|
||||
const FilesystemNode& defaultLoadDir() const { return myDefaultLoadDir; }
|
||||
const FilesystemNode& homeDir() const { return myHomeDir; }
|
||||
const FilesystemNode& userDir() const { return myUserDir; }
|
||||
|
||||
/**
|
||||
Open the given ROM and return an array containing its contents.
|
||||
|
@ -427,6 +427,9 @@ class OSystem
|
|||
static void overrideBaseDir(const string& path) { ourOverrideBaseDir = path; }
|
||||
static void overrideBaseDirWithApp() { ourOverrideBaseDirWithApp = true; }
|
||||
|
||||
// Update the path of the user directory
|
||||
void setUserDir(const string& path);
|
||||
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// The following methods are system-specific and can be overrided in
|
||||
|
@ -460,8 +463,7 @@ class OSystem
|
|||
@param basedir The base directory for all configuration files
|
||||
@param cfgfile The fully qualified pathname of the config file
|
||||
(including the base directory)
|
||||
@param savedir The default directory to save various other files
|
||||
@param loaddir The default directory to load various other files
|
||||
@param homedir The default directory to store various other files
|
||||
@param useappdir A hint that the base dir should be set to the
|
||||
app directory; not all ports can do this, so
|
||||
they are free to ignore it
|
||||
|
@ -470,8 +472,8 @@ class OSystem
|
|||
they are free to ignore it
|
||||
*/
|
||||
virtual void getBaseDirAndConfig(string& basedir, string& cfgfile,
|
||||
string& savedir, string& loaddir,
|
||||
bool useappdir, const string& usedir) = 0;
|
||||
string& homedir,
|
||||
bool useappdir, const string& usedir) = 0;
|
||||
|
||||
protected:
|
||||
// Pointer to the EventHandler object
|
||||
|
@ -552,7 +554,8 @@ class OSystem
|
|||
|
||||
private:
|
||||
FilesystemNode myBaseDir, myStateDir, mySnapshotSaveDir, mySnapshotLoadDir,
|
||||
myNVRamDir, myCfgDir, myDefaultSaveDir, myDefaultLoadDir;
|
||||
myNVRamDir, myCfgDir, myHomeDir,
|
||||
myUserDir;
|
||||
FilesystemNode myCheatFile, myConfigFile, myPaletteFile, myPropertiesFile;
|
||||
FilesystemNode myRomFile; string myRomMD5;
|
||||
|
||||
|
|
|
@ -135,6 +135,8 @@ Settings::Settings()
|
|||
|
||||
// Config files and paths
|
||||
setPermanent("romdir", "");
|
||||
setPermanent("userdir", "");
|
||||
setPermanent("saveuserdir", "false");
|
||||
|
||||
// ROM browser options
|
||||
setPermanent("exitlauncher", "false");
|
||||
|
@ -526,14 +528,14 @@ void Settings::usage() const
|
|||
<< endl
|
||||
<< " -saveonexit <none|current| Automatically save state(s) when exiting\n"
|
||||
<< " all> emulation\n"
|
||||
<< " -autoslot <1|0> Automatically change to next save slot when\n"
|
||||
<< " -autoslot <0|1> Automatically change to next save slot when\n"
|
||||
<< " state saving\n"
|
||||
<< endl
|
||||
<< " -rominfo <rom> Display detailed information for the given ROM\n"
|
||||
<< " -listrominfo Display contents of stella.pro, one line per ROM\n"
|
||||
<< " entry\n"
|
||||
<< endl
|
||||
<< " -exitlauncher <1|0> On exiting a ROM, go back to the ROM launcher\n"
|
||||
<< " -exitlauncher <0|1> On exiting a ROM, go back to the ROM launcher\n"
|
||||
<< " -launcherpos <XxY> Sets the window position in windowed EOM launcher mode\n"
|
||||
<< " -launcherdisplay <number> Sets the display for the ROM launcher\n"
|
||||
<< " -launcherres <WxH> The resolution to use in ROM launcher mode\n"
|
||||
|
@ -542,11 +544,14 @@ void Settings::usage() const
|
|||
<< " medium|large|\n"
|
||||
<< " large12|large14|\n"
|
||||
<< " large16>\n"
|
||||
<< " -launcherroms <1|0> Show only ROMs in the launcher (vs. all files)\n"
|
||||
<< " -launchersubdirs <1|0> Show files from subdirectories too\n"
|
||||
<< " -romviewer <float> Show ROM info viewer at given zoom level in ROM\n"
|
||||
<< " launcher (use 0 for off)\n"
|
||||
<< " -followlauncher <0|1> Default ROM path follows launcher navigation\n"
|
||||
<< " -launcherroms <1|0> Show only ROMs in the launcher (vs. all files)\n"
|
||||
<< " -launchersubdirs <0|1> Show files from subdirectories too\n"
|
||||
<< " -romdir <dir> Set the path where the ROM launcher will start\n"
|
||||
<< " -followlauncher <0|1> Default ROM path follows launcher navigation\n"
|
||||
<< " -userdir <dir> Set the path to save user files to\n"
|
||||
<< " -saveuserdir <0|1> Update user path when navigating in browser\n"
|
||||
<< " -lastrom <name> Last played ROM, automatically selected in\n"
|
||||
<< " launcher\n"
|
||||
<< " -romloadcount <number> Number of ROM to load next from multicard\n"
|
||||
|
@ -570,8 +575,6 @@ void Settings::usage() const
|
|||
<< " -ctrlrate <rate> Rate per second of repeated controller input in\n"
|
||||
<< " UI\n"
|
||||
<< " -basic_settings <0|1> Display only a basic settings dialog\n"
|
||||
<< " -romdir <dir> Set the directory where the ROM launcher will\n"
|
||||
<< " start\n"
|
||||
<< " -avoxport <name> The name of the serial port where an AtariVox is\n"
|
||||
<< " connected\n"
|
||||
<< " -holdreset Start the emulator with the Game Reset switch\n"
|
||||
|
|
|
@ -61,8 +61,13 @@ BrowserDialog::BrowserDialog(GuiObject* boss, const GUI::Font& font,
|
|||
_currentPath = new EditTextWidget(this, font, xpos + t->getWidth(), ypos,
|
||||
_w - t->getWidth() - 2 * xpos, lineHeight);
|
||||
_currentPath->setEditable(false);
|
||||
|
||||
xpos = _w - (HBORDER + _font.getStringWidth("Save") + CheckboxWidget::prefixSize(_font));
|
||||
_savePathBox = new CheckboxWidget(this, font, xpos, ypos + 2, "Save");
|
||||
_savePathBox->setToolTip("Check to save current path as default.");
|
||||
|
||||
// Add file list
|
||||
ypos += lineHeight + VGAP * 2;
|
||||
xpos = HBORDER; ypos += lineHeight + VGAP * 2;
|
||||
_fileList = new FileListWidget(this, font, xpos, ypos, _w - 2 * xpos,
|
||||
_h - selectHeight - buttonHeight - ypos - VBORDER * 2);
|
||||
_fileList->setEditable(false);
|
||||
|
@ -71,24 +76,29 @@ BrowserDialog::BrowserDialog(GuiObject* boss, const GUI::Font& font,
|
|||
// Add currently selected item
|
||||
ypos += _fileList->getHeight() + VGAP * 2;
|
||||
|
||||
_type = new StaticTextWidget(this, font, xpos, ypos + 2, "Name ");
|
||||
_selected = new EditTextWidget(this, font, xpos + _type->getWidth(), ypos,
|
||||
_w - _type->getWidth() - 2 * xpos, lineHeight, "");
|
||||
_selected->setEditable(false);
|
||||
_name = new StaticTextWidget(this, font, xpos, ypos + 2, "Name ");
|
||||
_selected = new EditTextWidget(this, font, xpos + _name->getWidth(), ypos,
|
||||
_w - _name->getWidth() - 2 * xpos, lineHeight, "");
|
||||
addFocusWidget(_selected);
|
||||
|
||||
// Buttons
|
||||
_goUpButton = new ButtonWidget(this, font, xpos, _h - buttonHeight - VBORDER,
|
||||
buttonWidth, buttonHeight, "Go up", kGoUpCmd);
|
||||
addFocusWidget(_goUpButton);
|
||||
|
||||
_basedirButton =
|
||||
new ButtonWidget(this, font, _goUpButton->getRight() + BUTTON_GAP, _h - buttonHeight - VBORDER,
|
||||
buttonWidth, buttonHeight, "Base Dir", kBaseDirCmd);
|
||||
addFocusWidget(_basedirButton);
|
||||
b = new ButtonWidget(this, font, _goUpButton->getRight() + BUTTON_GAP, _h - buttonHeight - VBORDER,
|
||||
buttonWidth, buttonHeight, "Base Dir", kBaseDirCmd);
|
||||
b->setToolTip("Go to Stella's base directory.");
|
||||
addFocusWidget(b);
|
||||
|
||||
b = new ButtonWidget(this, font, b->getRight() + BUTTON_GAP, _h - buttonHeight - VBORDER,
|
||||
buttonWidth, buttonHeight, "Home Dir", kHomeDirCmd);
|
||||
b->setToolTip("Go to user's home directory.");
|
||||
addFocusWidget(b);
|
||||
|
||||
#ifndef BSPF_MACOS
|
||||
b = new ButtonWidget(this, font, _w - (2 * buttonWidth + BUTTON_GAP + HBORDER), _h - buttonHeight - VBORDER,
|
||||
buttonWidth, buttonHeight, "Choose", kChooseCmd);
|
||||
buttonWidth, buttonHeight, "OK", kChooseCmd);
|
||||
addFocusWidget(b);
|
||||
addOKWidget(b);
|
||||
b = new ButtonWidget(this, font, _w - (buttonWidth + HBORDER), _h - buttonHeight - VBORDER,
|
||||
|
@ -101,55 +111,108 @@ BrowserDialog::BrowserDialog(GuiObject* boss, const GUI::Font& font,
|
|||
addFocusWidget(b);
|
||||
addCancelWidget(b);
|
||||
b = new ButtonWidget(this, font, _w - (buttonWidth + HBORDER), _h - buttonHeight - VBORDER,
|
||||
buttonWidth, buttonHeight, "Choose", kChooseCmd);
|
||||
buttonWidth, buttonHeight, "OK", kChooseCmd);
|
||||
addFocusWidget(b);
|
||||
addOKWidget(b);
|
||||
#endif
|
||||
|
||||
// add last to avoid focus problems
|
||||
addFocusWidget(_savePathBox);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void BrowserDialog::show(const string& startpath,
|
||||
BrowserDialog::ListMode mode, int cmd,
|
||||
BrowserDialog::ListMode mode, int cmd, int cancelCmd,
|
||||
const string& ext)
|
||||
{
|
||||
_cmd = cmd;
|
||||
const int fontWidth = _font.getMaxCharWidth(),
|
||||
fontHeight = _font.getFontHeight(),
|
||||
HBORDER = fontWidth * 1.25,
|
||||
VGAP = fontHeight / 4;
|
||||
|
||||
_mode = mode;
|
||||
_cmd = cmd;
|
||||
_cancelCmd = cancelCmd;
|
||||
string directory;// = EmptyString;
|
||||
string fileName;// = EmptyString;
|
||||
bool fileSelected = true;
|
||||
|
||||
// Set start path
|
||||
if(_mode != Directories)
|
||||
{
|
||||
// split startpath into path and filename
|
||||
FilesystemNode fs = FilesystemNode(startpath);
|
||||
fileName = fs.getName();
|
||||
directory = fs.isDirectory() ? "" : fs.getParent().getPath();
|
||||
}
|
||||
|
||||
switch(_mode)
|
||||
{
|
||||
case FileLoad:
|
||||
_fileList->setListMode(FilesystemNode::ListMode::All);
|
||||
_fileList->setNameFilter([&ext](const FilesystemNode& node) {
|
||||
_fileList->setNameFilter([ext](const FilesystemNode& node) {
|
||||
return BSPF::endsWithIgnoreCase(node.getName(), ext);
|
||||
});
|
||||
_selected->setEditable(false);
|
||||
_fileList->setHeight(_selected->getTop() - VGAP * 2 - _fileList->getTop());
|
||||
|
||||
_currentPath->setWidth(_savePathBox->getLeft() - _currentPath->getLeft() - fontWidth);
|
||||
_savePathBox->setEnabled(true);
|
||||
_savePathBox->clearFlags(Widget::FLAG_INVISIBLE);
|
||||
_savePathBox->setState(instance().settings().getBool("saveuserdir"));
|
||||
|
||||
_name->clearFlags(Widget::FLAG_INVISIBLE);
|
||||
_selected->clearFlags(Widget::FLAG_INVISIBLE);
|
||||
_type->clearFlags(Widget::FLAG_INVISIBLE);
|
||||
_selected->setEditable(false);
|
||||
_selected->setEnabled(false);
|
||||
_okWidget->setLabel("Load");
|
||||
break;
|
||||
|
||||
case FileSave:
|
||||
_fileList->setListMode(FilesystemNode::ListMode::All);
|
||||
_fileList->setNameFilter([&ext](const FilesystemNode& node) {
|
||||
_fileList->setNameFilter([ext](const FilesystemNode& node) {
|
||||
return BSPF::endsWithIgnoreCase(node.getName(), ext);
|
||||
});
|
||||
_selected->setEditable(false); // FIXME - disable user input for now
|
||||
_fileList->setHeight(_selected->getTop() - VGAP * 2 - _fileList->getTop());
|
||||
|
||||
_currentPath->setWidth(_savePathBox->getLeft() - _currentPath->getLeft() - fontWidth);
|
||||
_savePathBox->setEnabled(true);
|
||||
_savePathBox->clearFlags(Widget::FLAG_INVISIBLE);
|
||||
_savePathBox->setState(instance().settings().getBool("saveuserdir"));
|
||||
|
||||
_name->clearFlags(Widget::FLAG_INVISIBLE);
|
||||
_selected->clearFlags(Widget::FLAG_INVISIBLE);
|
||||
_type->clearFlags(Widget::FLAG_INVISIBLE);
|
||||
_selected->setEditable(true);
|
||||
_selected->setEnabled(true);
|
||||
_selected->setText(fileName);
|
||||
_okWidget->setLabel("Save");
|
||||
fileSelected = false;
|
||||
break;
|
||||
|
||||
case Directories:
|
||||
_fileList->setListMode(FilesystemNode::ListMode::DirectoriesOnly);
|
||||
_fileList->setNameFilter([](const FilesystemNode&) { return true; });
|
||||
_selected->setEditable(false);
|
||||
// TODO: scrollbar affected too!
|
||||
_fileList->setHeight(_selected->getBottom() - _fileList->getTop());
|
||||
|
||||
_currentPath->setWidth(_savePathBox->getRight() - _currentPath->getLeft());
|
||||
_savePathBox->setEnabled(false);
|
||||
_savePathBox->setFlags(Widget::FLAG_INVISIBLE);
|
||||
|
||||
_name->setFlags(Widget::FLAG_INVISIBLE);
|
||||
_selected->setFlags(Widget::FLAG_INVISIBLE);
|
||||
_type->setFlags(Widget::FLAG_INVISIBLE);
|
||||
_selected->setEditable(false);
|
||||
_selected->setEnabled(false);
|
||||
_okWidget->setLabel("OK");
|
||||
break;
|
||||
}
|
||||
|
||||
// Set start path
|
||||
_fileList->setDirectory(FilesystemNode(startpath));
|
||||
if(_mode != Directories)
|
||||
_fileList->setDirectory(FilesystemNode(directory), fileName);
|
||||
else
|
||||
_fileList->setDirectory(FilesystemNode(startpath));
|
||||
|
||||
updateUI();
|
||||
updateUI(fileSelected);
|
||||
|
||||
// Finally, open the dialog after it has been fully updated
|
||||
open();
|
||||
|
@ -159,30 +222,16 @@ void BrowserDialog::show(const string& startpath,
|
|||
const FilesystemNode& BrowserDialog::getResult() const
|
||||
{
|
||||
if(_mode == FileLoad || _mode == FileSave)
|
||||
return _fileList->selected();
|
||||
{
|
||||
static FilesystemNode node;
|
||||
|
||||
return node
|
||||
= FilesystemNode(_fileList->currentDir().getShortPath() + _selected->getText());
|
||||
}
|
||||
else
|
||||
return _fileList->currentDir();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void BrowserDialog::updateUI()
|
||||
{
|
||||
// Only hilite the 'up' button if there's a parent directory
|
||||
_goUpButton->setEnabled(_fileList->currentDir().hasParent());
|
||||
|
||||
// Update the path display
|
||||
_currentPath->setText(_fileList->currentDir().getShortPath());
|
||||
|
||||
// Enable/disable OK button based on current mode
|
||||
bool enable = _mode == Directories || !_fileList->selected().isDirectory();
|
||||
_okWidget->setEnabled(enable);
|
||||
|
||||
if(!_fileList->selected().isDirectory())
|
||||
_selected->setText(_fileList->getSelectedString());
|
||||
else
|
||||
_selected->setText("");
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void BrowserDialog::handleCommand(CommandSender* sender, int cmd,
|
||||
int data, int id)
|
||||
|
@ -193,10 +242,26 @@ void BrowserDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
case FileListWidget::ItemActivated:
|
||||
// Send a signal to the calling class that a selection has been made
|
||||
// Since we aren't derived from a widget, we don't have a 'data' or 'id'
|
||||
if(_mode != Directories)
|
||||
{
|
||||
// TODO: check if affected by '-baseDir'and 'basedirinapp' params
|
||||
bool savePath = _savePathBox->getState();
|
||||
|
||||
instance().settings().setValue("saveuserdir", savePath);
|
||||
if(savePath)
|
||||
instance().setUserDir(_fileList->currentDir().getShortPath());
|
||||
}
|
||||
if(_cmd) sendCommand(_cmd, -1, -1);
|
||||
close();
|
||||
break;
|
||||
|
||||
case kCloseCmd:
|
||||
// Send a signal to the calling class that the dialog was closed without selection
|
||||
// Since we aren't derived from a widget, we don't have a 'data' or 'id'
|
||||
if(_cancelCmd) sendCommand(_cancelCmd, -1, -1);
|
||||
close();
|
||||
break;
|
||||
|
||||
case kGoUpCmd:
|
||||
_fileList->selectParent();
|
||||
break;
|
||||
|
@ -205,8 +270,17 @@ void BrowserDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
_fileList->setDirectory(FilesystemNode(instance().baseDir()));
|
||||
break;
|
||||
|
||||
case kHomeDirCmd:
|
||||
_fileList->setDirectory(FilesystemNode(instance().homeDir()));
|
||||
break;
|
||||
|
||||
case EditableWidget::kChangedCmd:
|
||||
Dialog::handleCommand(sender, cmd, data, 0);
|
||||
updateUI(false);
|
||||
break;
|
||||
|
||||
case FileListWidget::ItemChanged:
|
||||
updateUI();
|
||||
updateUI(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -214,3 +288,23 @@ void BrowserDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void BrowserDialog::updateUI(bool fileSelected)
|
||||
{
|
||||
// Only hilite the 'up' button if there's a parent directory
|
||||
_goUpButton->setEnabled(_fileList->currentDir().hasParent());
|
||||
|
||||
// Update the path display
|
||||
_currentPath->setText(_fileList->currentDir().getShortPath());
|
||||
|
||||
// Enable/disable OK button based on current mode and status
|
||||
bool enable = true;
|
||||
|
||||
if(_mode != Directories)
|
||||
enable = !_selected->getText().empty();
|
||||
_okWidget->setEnabled(enable);
|
||||
|
||||
if(fileSelected && !_fileList->selected().isDirectory())
|
||||
_selected->setText(_fileList->getSelectedString());
|
||||
}
|
||||
|
|
|
@ -45,30 +45,32 @@ class BrowserDialog : public Dialog, public CommandSender
|
|||
|
||||
/** Place the browser window onscreen, using the given attributes */
|
||||
void show(const string& startpath,
|
||||
BrowserDialog::ListMode mode, int cmd, const string& ext = "");
|
||||
BrowserDialog::ListMode mode, int cmd, int cancelCmd = 0, const string& ext = "");
|
||||
|
||||
/** Get resulting file node (called after receiving kChooseCmd) */
|
||||
const FilesystemNode& getResult() const;
|
||||
|
||||
private:
|
||||
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
|
||||
void updateUI();
|
||||
void updateUI(bool fileSelected);
|
||||
|
||||
private:
|
||||
enum {
|
||||
kChooseCmd = 'CHOS',
|
||||
kGoUpCmd = 'GOUP',
|
||||
kBaseDirCmd = 'BADR'
|
||||
kBaseDirCmd = 'BADR',
|
||||
kHomeDirCmd = 'HODR'
|
||||
};
|
||||
|
||||
int _cmd{0};
|
||||
int _cancelCmd{0};
|
||||
|
||||
FileListWidget* _fileList{nullptr};
|
||||
EditTextWidget* _currentPath{nullptr};
|
||||
StaticTextWidget* _type{nullptr};
|
||||
StaticTextWidget* _name{nullptr};
|
||||
EditTextWidget* _selected{nullptr};
|
||||
ButtonWidget* _goUpButton{nullptr};
|
||||
ButtonWidget* _basedirButton{nullptr};
|
||||
CheckboxWidget* _savePathBox{nullptr};
|
||||
|
||||
BrowserDialog::ListMode _mode{Directories};
|
||||
|
||||
|
|
|
@ -75,10 +75,10 @@ class Dialog : public GuiObject
|
|||
void addToFocusList(const WidgetArray& list, TabWidget* w, int tabId);
|
||||
void addBGroupToFocusList(const WidgetArray& list) { _buttonGroup = list; }
|
||||
void addTabWidget(TabWidget* w);
|
||||
void addDefaultWidget(Widget* w) { _defaultWidget = w; }
|
||||
void addExtraWidget(Widget* w) { _extraWidget = w; }
|
||||
void addOKWidget(Widget* w) { _okWidget = w; }
|
||||
void addCancelWidget(Widget* w) { _cancelWidget = w; }
|
||||
void addDefaultWidget(ButtonWidget* w) { _defaultWidget = w; }
|
||||
void addExtraWidget(ButtonWidget* w) { _extraWidget = w; }
|
||||
void addOKWidget(ButtonWidget* w) { _okWidget = w; }
|
||||
void addCancelWidget(ButtonWidget* w) { _cancelWidget = w; }
|
||||
void setFocus(Widget* w);
|
||||
|
||||
/** Returns the base surface associated with this dialog. */
|
||||
|
@ -196,10 +196,10 @@ class Dialog : public GuiObject
|
|||
Widget* _mouseWidget{nullptr};
|
||||
Widget* _focusedWidget{nullptr};
|
||||
Widget* _dragWidget{nullptr};
|
||||
Widget* _defaultWidget{nullptr};
|
||||
Widget* _extraWidget{nullptr};
|
||||
Widget* _okWidget{nullptr};
|
||||
Widget* _cancelWidget{nullptr};
|
||||
ButtonWidget* _defaultWidget{nullptr};
|
||||
ButtonWidget* _extraWidget{nullptr};
|
||||
ButtonWidget* _okWidget{nullptr};
|
||||
ButtonWidget* _cancelWidget{nullptr};
|
||||
|
||||
bool _visible{false};
|
||||
bool _processCancel{false};
|
||||
|
|
|
@ -657,7 +657,7 @@ bool EditableWidget::killWord(int direction)
|
|||
{
|
||||
while(currentPos > 0)
|
||||
{
|
||||
if(_editString[currentPos - 1] == ' ')
|
||||
if(BSPF::isWhiteSpace(_editString[currentPos - 1]))
|
||||
{
|
||||
if(!space)
|
||||
break;
|
||||
|
@ -673,7 +673,7 @@ bool EditableWidget::killWord(int direction)
|
|||
{
|
||||
while(currentPos < int(_editString.size()))
|
||||
{
|
||||
if(currentPos && _editString[currentPos - 1] == ' ')
|
||||
if(currentPos && BSPF::isWhiteSpace(_editString[currentPos - 1]))
|
||||
{
|
||||
if(!space)
|
||||
break;
|
||||
|
@ -709,7 +709,7 @@ bool EditableWidget::moveWord(int direction, bool select)
|
|||
{
|
||||
while (currentPos > 0)
|
||||
{
|
||||
if (_editString[currentPos - 1] == ' ')
|
||||
if (BSPF::isWhiteSpace(_editString[currentPos - 1]))
|
||||
{
|
||||
if (!space)
|
||||
break;
|
||||
|
@ -728,7 +728,7 @@ bool EditableWidget::moveWord(int direction, bool select)
|
|||
{
|
||||
while (currentPos < int(_editString.size()))
|
||||
{
|
||||
if (currentPos && _editString[currentPos - 1] == ' ')
|
||||
if (currentPos && BSPF::isWhiteSpace(_editString[currentPos - 1]))
|
||||
{
|
||||
if (!space)
|
||||
break;
|
||||
|
@ -754,14 +754,14 @@ bool EditableWidget::markWord()
|
|||
|
||||
while(_caretPos + _selectSize < int(_editString.size()))
|
||||
{
|
||||
if(_editString[_caretPos + _selectSize] == ' ')
|
||||
if(BSPF::isWhiteSpace(_editString[_caretPos + _selectSize]))
|
||||
break;
|
||||
_selectSize++;
|
||||
}
|
||||
|
||||
while(_caretPos > 0)
|
||||
{
|
||||
if(_editString[_caretPos - 1] == ' ')
|
||||
if(BSPF::isWhiteSpace(_editString[_caretPos - 1]))
|
||||
break;
|
||||
_caretPos--;
|
||||
_selectSize++;
|
||||
|
|
|
@ -54,7 +54,7 @@ void FileListWidget::setDirectory(const FilesystemNode& node,
|
|||
while(tmp.hasParent() && !_history.full())
|
||||
{
|
||||
string name = tmp.getName();
|
||||
if(name.back() == '/' || name.back() == '\\')
|
||||
if(name.back() == FilesystemNode::PATH_SEPARATOR)
|
||||
name.pop_back();
|
||||
if(!BSPF::startsWithIgnoreCase(name, " ["))
|
||||
name = " [" + name.append("]");
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "Paddles.hxx"
|
||||
#include "PopUpWidget.hxx"
|
||||
#include "PropsSet.hxx"
|
||||
#include "BrowserDialog.hxx"
|
||||
#include "QuadTariDialog.hxx"
|
||||
#include "TabWidget.hxx"
|
||||
#include "TIAConstants.hxx"
|
||||
|
@ -92,7 +93,7 @@ GameInfoDialog::GameInfoDialog(
|
|||
myTab->setActiveTab(0);
|
||||
|
||||
// Add Defaults, OK and Cancel buttons
|
||||
addDefaultsExtraOKCancelBGroup(wid, font, "Export", kExportPressed);
|
||||
addDefaultsExtraOKCancelBGroup(wid, font, "Export" + ELLIPSIS, kExportPressed);
|
||||
_extraWidget->setToolTip("Export the current ROM's properties\n"
|
||||
"into the default directory.");
|
||||
addBGroupToFocusList(wid);
|
||||
|
@ -660,12 +661,27 @@ void GameInfoDialog::addHighScoresTab()
|
|||
addToFocusList(wid, myTab, tabID);
|
||||
}
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
GameInfoDialog::~GameInfoDialog()
|
||||
{
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void GameInfoDialog::createBrowser(const string& title)
|
||||
{
|
||||
uInt32 w = 0, h = 0;
|
||||
getDynamicBounds(w, h);
|
||||
if(w > uInt32(_font.getMaxCharWidth() * 80))
|
||||
w = _font.getMaxCharWidth() * 80;
|
||||
|
||||
// Create file browser dialog
|
||||
if(!myBrowser || uInt32(myBrowser->getWidth()) != w ||
|
||||
uInt32(myBrowser->getHeight()) != h)
|
||||
myBrowser = make_unique<BrowserDialog>(this, _font, w, h, title);
|
||||
else
|
||||
myBrowser->setTitle(title);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void GameInfoDialog::loadConfig()
|
||||
{
|
||||
|
@ -1350,16 +1366,14 @@ void GameInfoDialog::exportCurrentPropertiesToDisk()
|
|||
|
||||
try
|
||||
{
|
||||
FilesystemNode propfile = instance().defaultSaveDir();
|
||||
propfile /= myGameFile.getNameWithExt(".pro");
|
||||
FilesystemNode propfile(myBrowser->getResult().getShortPath());
|
||||
|
||||
propfile.write(out);
|
||||
instance().frameBuffer().showTextMessage("Properties exported to " +
|
||||
propfile.getShortPath());
|
||||
instance().frameBuffer().showTextMessage("ROM properties exported");
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
instance().frameBuffer().showTextMessage("Error exporting properties");
|
||||
instance().frameBuffer().showTextMessage("Error exporting ROM properties");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1379,6 +1393,15 @@ void GameInfoDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
break;
|
||||
|
||||
case kExportPressed:
|
||||
// This dialog is resizable under certain conditions, so we need
|
||||
// to re-create it as necessary
|
||||
createBrowser("Export Properties as");
|
||||
|
||||
myBrowser->show(instance().userDir().getPath() + myGameFile.getNameWithExt(".pro"),
|
||||
BrowserDialog::FileSave, kExportChosen);
|
||||
break;
|
||||
|
||||
case kExportChosen:
|
||||
exportCurrentPropertiesToDisk();
|
||||
break;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ class RadioButtonGroup;
|
|||
class TabWidget;
|
||||
class SliderWidget;
|
||||
class QuadTariDialog;
|
||||
class BrowserDialog;
|
||||
|
||||
#include "Dialog.hxx"
|
||||
#include "Command.hxx"
|
||||
|
@ -78,6 +79,7 @@ class GameInfoDialog : public Dialog, public CommandSender
|
|||
void setAddressVal(EditTextWidget* address, EditTextWidget* val,
|
||||
bool isBCD = true, bool zeroBased = false, uInt8 maxVal = 255);
|
||||
void exportCurrentPropertiesToDisk();
|
||||
void createBrowser(const string& title);
|
||||
|
||||
private:
|
||||
TabWidget* myTab{nullptr};
|
||||
|
@ -168,6 +170,8 @@ class GameInfoDialog : public Dialog, public CommandSender
|
|||
StaticTextWidget* myHighScoreNotesLabel{nullptr};
|
||||
EditTextWidget* myHighScoreNotes{nullptr};
|
||||
|
||||
unique_ptr<BrowserDialog> myBrowser;
|
||||
|
||||
enum {
|
||||
kVCenterChanged = 'Vcch',
|
||||
kPhosphorChanged = 'PPch',
|
||||
|
@ -180,7 +184,8 @@ class GameInfoDialog : public Dialog, public CommandSender
|
|||
kHiScoresChanged = 'HSch',
|
||||
kPXCenterChanged = 'Pxch',
|
||||
kPYCenterChanged = 'Pych',
|
||||
kExportPressed = 'GIsp'
|
||||
kExportPressed = 'Expr',
|
||||
kExportChosen = 'Exch'
|
||||
};
|
||||
|
||||
// Game properties for currently loaded ROM
|
||||
|
|
|
@ -56,6 +56,17 @@ ListWidget::ListWidget(GuiObject* boss, const GUI::Font& font,
|
|||
_w = w - 1;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void ListWidget::setHeight(int h)
|
||||
{
|
||||
Widget::setHeight(h);
|
||||
if(_useScrollbar)
|
||||
_scrollBar->setHeight(h);
|
||||
|
||||
_rows = (h - 2) / _lineHeight;
|
||||
recalc();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void ListWidget::setSelected(int item)
|
||||
{
|
||||
|
|
|
@ -47,6 +47,7 @@ class ListWidget : public EditableWidget
|
|||
|
||||
int rows() const { return _rows; }
|
||||
int currentPos() const { return _currentPos; }
|
||||
void setHeight(int h) override;
|
||||
|
||||
int getSelected() const { return _selectedItem; }
|
||||
void setSelected(int item);
|
||||
|
|
|
@ -116,7 +116,7 @@ void LoggerDialog::saveConfig()
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void LoggerDialog::saveLogFile()
|
||||
{
|
||||
FilesystemNode node = instance().defaultSaveDir();
|
||||
FilesystemNode node = instance().userDir();
|
||||
node /= "stella.log";
|
||||
|
||||
try
|
||||
|
|
|
@ -198,7 +198,7 @@ void RomAuditDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
break;
|
||||
|
||||
case kChooseAuditDirCmd:
|
||||
createBrowser("Select ROM directory to audit");
|
||||
createBrowser("Select ROM Directory to Audit");
|
||||
myBrowser->show(myRomPath->getText(),
|
||||
BrowserDialog::Directories, kAuditDirChosenCmd);
|
||||
break;
|
||||
|
@ -223,6 +223,8 @@ void RomAuditDialog::createBrowser(const string& title)
|
|||
{
|
||||
uInt32 w = 0, h = 0;
|
||||
getDynamicBounds(w, h);
|
||||
if(w > uInt32(_font.getMaxCharWidth() * 80))
|
||||
w = _font.getMaxCharWidth() * 80;
|
||||
|
||||
// Create file browser dialog
|
||||
if(!myBrowser || uInt32(myBrowser->getWidth()) != w ||
|
||||
|
|
|
@ -131,7 +131,7 @@ void SnapshotDialog::saveConfig()
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void SnapshotDialog::setDefaults()
|
||||
{
|
||||
mySnapSavePath->setText(instance().defaultSaveDir().getShortPath());
|
||||
mySnapSavePath->setText(instance().userDir().getShortPath());
|
||||
mySnapInterval->setValue(2);
|
||||
mySnapName->setState(false);
|
||||
mySnapSingle->setState(false);
|
||||
|
@ -156,7 +156,7 @@ void SnapshotDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
case kChooseSnapSaveDirCmd:
|
||||
// This dialog is resizable under certain conditions, so we need
|
||||
// to re-create it as necessary
|
||||
createBrowser("Select snapshot save directory");
|
||||
createBrowser("Select Snapshot Save Directory");
|
||||
myBrowser->show(mySnapSavePath->getText(),
|
||||
BrowserDialog::Directories, kSnapSaveDirChosenCmd);
|
||||
break;
|
||||
|
@ -183,6 +183,8 @@ void SnapshotDialog::createBrowser(const string& title)
|
|||
{
|
||||
uInt32 w = 0, h = 0;
|
||||
getDynamicBounds(w, h);
|
||||
if(w > uInt32(_font.getMaxCharWidth() * 80))
|
||||
w = _font.getMaxCharWidth() * 80;
|
||||
|
||||
// Create file browser dialog
|
||||
if(!myBrowser || uInt32(myBrowser->getWidth()) != w ||
|
||||
|
|
|
@ -515,7 +515,7 @@ void UIDialog::setDefaults()
|
|||
myLauncherHeightSlider->setValue(h);
|
||||
myLauncherFontPopup->setSelected("medium", "");
|
||||
myRomViewerSize->setValue(35);
|
||||
mySnapLoadPath->setText(instance().defaultLoadDir().getShortPath());
|
||||
mySnapLoadPath->setText(instance().userDir().getShortPath());
|
||||
myLauncherExitWidget->setState(false);
|
||||
break;
|
||||
}
|
||||
|
@ -586,7 +586,7 @@ void UIDialog::handleCommand(CommandSender* sender, int cmd, int data, int id)
|
|||
case kChooseRomDirCmd:
|
||||
// This dialog is resizable under certain conditions, so we need
|
||||
// to re-create it as necessary
|
||||
createBrowser("Select ROM directory");
|
||||
createBrowser("Select ROM Directory");
|
||||
myBrowser->show(myRomPath->getText(),
|
||||
BrowserDialog::Directories, LauncherDialog::kRomDirChosenCmd);
|
||||
break;
|
||||
|
@ -602,7 +602,7 @@ void UIDialog::handleCommand(CommandSender* sender, int cmd, int data, int id)
|
|||
case kChooseSnapLoadDirCmd:
|
||||
// This dialog is resizable under certain conditions, so we need
|
||||
// to re-create it as necessary
|
||||
createBrowser("Select snapshot load directory");
|
||||
createBrowser("Select ROM Info Viewer Image Directory");
|
||||
myBrowser->show(mySnapLoadPath->getText(),
|
||||
BrowserDialog::Directories, kSnapLoadDirChosenCmd);
|
||||
break;
|
||||
|
@ -706,6 +706,8 @@ void UIDialog::createBrowser(const string& title)
|
|||
{
|
||||
uInt32 w = 0, h = 0;
|
||||
getDynamicBounds(w, h);
|
||||
if(w > uInt32(_font.getMaxCharWidth() * 80))
|
||||
w = _font.getMaxCharWidth() * 80;
|
||||
|
||||
// Create file browser dialog
|
||||
if(!myBrowser || uInt32(myBrowser->getWidth()) != w ||
|
||||
|
|
|
@ -203,6 +203,36 @@ void Widget::setPos(const Common::Point& pos)
|
|||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Widget::setWidth(int w)
|
||||
{
|
||||
setSize(w, _h);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Widget::setHeight(int h)
|
||||
{
|
||||
setSize(_w, h);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Widget::setSize(int w, int h)
|
||||
{
|
||||
setSize(Common::Point(w, h));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Widget::setSize(const Common::Point& pos)
|
||||
{
|
||||
if(pos != Common::Point(_w, _h))
|
||||
{
|
||||
_w = pos.x;
|
||||
_h = pos.y;
|
||||
// we have to redraw the whole dialog!
|
||||
dialog().setDirty();
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Widget::handleMouseEntered()
|
||||
{
|
||||
|
|
|
@ -57,6 +57,10 @@ class Widget : public GuiObject
|
|||
virtual void setPosY(int y);
|
||||
virtual void setPos(int x, int y);
|
||||
virtual void setPos(const Common::Point& pos);
|
||||
void setWidth(int w) override;
|
||||
void setHeight(int h) override;
|
||||
virtual void setSize(int w, int h);
|
||||
virtual void setSize(const Common::Point& pos);
|
||||
|
||||
virtual bool handleText(char text) { return false; }
|
||||
virtual bool handleKeyDown(StellaKey key, StellaMod mod) { return false; }
|
||||
|
@ -236,6 +240,8 @@ class ButtonWidget : public StaticTextWidget, public CommandSender
|
|||
int cmd = 0, bool repeat = false);
|
||||
~ButtonWidget() override = default;
|
||||
|
||||
bool handleEvent(Event::Type event) override;
|
||||
|
||||
void setCmd(int cmd) { _cmd = cmd; }
|
||||
int getCmd() const { return _cmd; }
|
||||
/* Sets/changes the button's bitmap **/
|
||||
|
@ -247,7 +253,6 @@ class ButtonWidget : public StaticTextWidget, public CommandSender
|
|||
void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
|
||||
void handleMouseEntered() override;
|
||||
void handleMouseLeft() override;
|
||||
bool handleEvent(Event::Type event) override;
|
||||
|
||||
void drawWidget(bool hilite) override;
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void OSystemLIBRETRO::getBaseDirAndConfig(string& basedir, string& cfgfile,
|
||||
string& savedir, string& loaddir,
|
||||
bool useappdir, const string& usedir)
|
||||
string& homedir,
|
||||
bool useappdir, const string& usedir)
|
||||
{
|
||||
loaddir = savedir = cfgfile = basedir = "." + slash;
|
||||
basedir = cfgfile = homedir = "." + slash;
|
||||
}
|
||||
|
|
|
@ -41,8 +41,7 @@ class OSystemLIBRETRO : public OSystem
|
|||
@param basedir The base directory for all configuration files
|
||||
@param cfgfile The fully qualified pathname of the config file
|
||||
(including the base directory)
|
||||
@param savedir The default directory to save various other files
|
||||
@param loaddir The default directory to load various other files
|
||||
@param homedir The default directory to store various other files
|
||||
@param useappdir A hint that the base dir should be set to the
|
||||
app directory; not all ports can do this, so
|
||||
they are free to ignore it
|
||||
|
@ -51,8 +50,8 @@ class OSystemLIBRETRO : public OSystem
|
|||
they are free to ignore it
|
||||
*/
|
||||
void getBaseDirAndConfig(string& basedir, string& cfgfile,
|
||||
string& savedir, string& loaddir,
|
||||
bool useappdir, const string& usedir) override;
|
||||
string& homedir,
|
||||
bool useappdir, const string& usedir) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void OSystemMACOS::getBaseDirAndConfig(string& basedir, string& cfgfile,
|
||||
string& savedir, string& loaddir,
|
||||
bool useappdir, const string& usedir)
|
||||
string& homedir,
|
||||
bool useappdir, const string& usedir)
|
||||
{
|
||||
basedir = "~/Library/Application Support/Stella/";
|
||||
|
||||
|
@ -31,14 +31,11 @@ void OSystemMACOS::getBaseDirAndConfig(string& basedir, string& cfgfile,
|
|||
if(useappdir)
|
||||
cout << "ERROR: base dir in app folder not supported" << endl;
|
||||
else if(usedir != "")
|
||||
{
|
||||
basedir = FilesystemNode(usedir).getPath();
|
||||
savedir = loaddir = basedir;
|
||||
}
|
||||
#endif
|
||||
|
||||
FilesystemNode desktop("~/Desktop/");
|
||||
savedir = loaddir = desktop.isDirectory() ? desktop.getShortPath() : "~/";
|
||||
homedir = desktop.isDirectory() ? desktop.getShortPath() : "~/";
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -41,8 +41,7 @@ class OSystemMACOS : public OSystem
|
|||
@param basedir The base directory for all configuration files
|
||||
@param cfgfile The fully qualified pathname of the config file
|
||||
(including the base directory)
|
||||
@param savedir The default directory to save various other files
|
||||
@param loaddir The default directory to load various other files
|
||||
@param homedir The default directory to store various other files
|
||||
@param useappdir A hint that the base dir should be set to the
|
||||
app directory; not all ports can do this, so
|
||||
they are free to ignore it
|
||||
|
@ -51,8 +50,8 @@ class OSystemMACOS : public OSystem
|
|||
they are free to ignore it
|
||||
*/
|
||||
void getBaseDirAndConfig(string& basedir, string& cfgfile,
|
||||
string& savedir, string& loaddir,
|
||||
bool useappdir, const string& usedir) override;
|
||||
string& homedir,
|
||||
bool useappdir, const string& usedir) override;
|
||||
|
||||
protected:
|
||||
virtual shared_ptr<KeyValueRepository> createSettingsRepository() override;
|
||||
|
|
|
@ -23,23 +23,21 @@
|
|||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void OSystemUNIX::getBaseDirAndConfig(string& basedir, string& cfgfile,
|
||||
string& savedir, string& loaddir,
|
||||
bool useappdir, const string& usedir)
|
||||
string& homedir,
|
||||
bool useappdir, const string& usedir)
|
||||
{
|
||||
// Use XDG_CONFIG_HOME if defined, otherwise use the default
|
||||
string configDir = BSPF::getenv("XDG_CONFIG_HOME");
|
||||
|
||||
if(configDir == EmptyString) configDir = "~/.config";
|
||||
basedir = configDir + "/stella";
|
||||
savedir = loaddir = "~/";
|
||||
homedir = "~/";
|
||||
|
||||
// Check to see if basedir overrides are active
|
||||
if(useappdir)
|
||||
cout << "ERROR: base dir in app folder not supported" << endl;
|
||||
else if(usedir != "")
|
||||
{
|
||||
basedir = FilesystemNode(usedir).getPath();
|
||||
savedir = loaddir = basedir;
|
||||
}
|
||||
|
||||
// (Currently) non-documented alternative for using version-specific
|
||||
// config file
|
||||
|
|
|
@ -41,8 +41,7 @@ class OSystemUNIX : public OSystem
|
|||
@param basedir The base directory for all configuration files
|
||||
@param cfgfile The fully qualified pathname of the config file
|
||||
(including the base directory)
|
||||
@param savedir The default directory to save various other files
|
||||
@param loaddir The default directory to load various other files
|
||||
@param homedir The default directory to store various other files
|
||||
@param useappdir A hint that the base dir should be set to the
|
||||
app directory; not all ports can do this, so
|
||||
they are free to ignore it
|
||||
|
@ -51,8 +50,8 @@ class OSystemUNIX : public OSystem
|
|||
they are free to ignore it
|
||||
*/
|
||||
void getBaseDirAndConfig(string& basedir, string& cfgfile,
|
||||
string& savedir, string& loaddir,
|
||||
bool useappdir, const string& usedir) override;
|
||||
string& homedir,
|
||||
bool useappdir, const string& usedir) override;
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void OSystemR77::getBaseDirAndConfig(string& basedir, string& cfgfile,
|
||||
string& savedir, string& loaddir, bool, const string&)
|
||||
string& homeDir, bool, const string&)
|
||||
{
|
||||
basedir = savedir = loaddir = "/mnt/stella";
|
||||
basedir = homeDir = "/mnt/stella";
|
||||
cfgfile = "/mnt/stella/stellarc";
|
||||
}
|
||||
|
|
|
@ -43,8 +43,7 @@ class OSystemR77 : public OSystem
|
|||
@param basedir The base directory for all configuration files
|
||||
@param cfgfile The fully qualified pathname of the config file
|
||||
(including the base directory)
|
||||
@param savedir The default directory to save various other files
|
||||
@param loaddir The default directory to load various other files
|
||||
@param homedir The default directory to store various other files
|
||||
@param useappdir A hint that the base dir should be set to the
|
||||
app directory; not all ports can do this, so
|
||||
they are free to ignore it
|
||||
|
@ -53,8 +52,8 @@ class OSystemR77 : public OSystem
|
|||
they are free to ignore it
|
||||
*/
|
||||
void getBaseDirAndConfig(string& basedir, string& cfgfile,
|
||||
string& savedir, string& loaddir,
|
||||
bool useappdir, const string& usedir) override;
|
||||
string& homedir,
|
||||
bool useappdir, const string& usedir) override;
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
|
|
|
@ -23,11 +23,12 @@
|
|||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void OSystemWINDOWS::getBaseDirAndConfig(string& basedir, string& cfgfile,
|
||||
string& savedir, string& loaddir,
|
||||
bool useappdir, const string& usedir)
|
||||
string& homedir,
|
||||
bool useappdir, const string& usedir)
|
||||
{
|
||||
HomeFinder homefinder;
|
||||
FilesystemNode appdata(homefinder.getAppDataPath());
|
||||
|
||||
if(appdata.isDirectory())
|
||||
{
|
||||
basedir = appdata.getShortPath();
|
||||
|
@ -35,8 +36,9 @@ void OSystemWINDOWS::getBaseDirAndConfig(string& basedir, string& cfgfile,
|
|||
basedir += '\\';
|
||||
basedir += "Stella\\";
|
||||
}
|
||||
FilesystemNode defaultLoadSaveDir(homefinder.getDesktopPath());
|
||||
savedir = loaddir = defaultLoadSaveDir.getShortPath();
|
||||
|
||||
FilesystemNode defaultHomeDir(homefinder.getDesktopPath());
|
||||
homedir = defaultHomeDir.getShortPath();
|
||||
|
||||
// Check to see if basedir overrides are active
|
||||
if(useappdir)
|
||||
|
@ -44,12 +46,13 @@ void OSystemWINDOWS::getBaseDirAndConfig(string& basedir, string& cfgfile,
|
|||
char filename[MAX_PATH];
|
||||
GetModuleFileNameA(NULL, filename, sizeof(filename));
|
||||
FilesystemNode appdir(filename);
|
||||
|
||||
appdir = appdir.getParent();
|
||||
if(appdir.isDirectory())
|
||||
savedir = loaddir = basedir = appdir.getPath();
|
||||
basedir = appdir.getPath();
|
||||
}
|
||||
else if(usedir != "")
|
||||
savedir = loaddir = basedir = FilesystemNode(usedir).getPath();
|
||||
basedir = FilesystemNode(usedir).getPath();
|
||||
|
||||
cfgfile = basedir + "stella.ini";
|
||||
}
|
||||
|
|
|
@ -41,8 +41,7 @@ class OSystemWINDOWS : public OSystem
|
|||
@param basedir The base directory for all configuration files
|
||||
@param cfgfile The fully qualified pathname of the config file
|
||||
(including the base directory)
|
||||
@param savedir The default directory to save various other files
|
||||
@param loaddir The default directory to load various other files
|
||||
@param homedir The default directory to store various other files
|
||||
@param useappdir A hint that the base dir should be set to the
|
||||
app directory; not all ports can do this, so
|
||||
they are free to ignore it
|
||||
|
@ -51,8 +50,8 @@ class OSystemWINDOWS : public OSystem
|
|||
they are free to ignore it
|
||||
*/
|
||||
void getBaseDirAndConfig(string& basedir, string& cfgfile,
|
||||
string& savedir, string& loaddir,
|
||||
bool useappdir, const string& usedir) override;
|
||||
string& homedir,
|
||||
bool useappdir, const string& usedir) override;
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
|
|