diff --git a/src/common/FrameBufferGL.cxx b/src/common/FrameBufferGL.cxx index 1b57435f3..20800b426 100644 --- a/src/common/FrameBufferGL.cxx +++ b/src/common/FrameBufferGL.cxx @@ -1648,7 +1648,7 @@ GLuint FBSurfaceGL::genShader(ShaderType type) // These shader files are stored in 'BASEDIR/shaders/' char* buffer = NULL; const string& filename = - myFB.myOSystem->baseDir() + BSPF_PATH_SEPARATOR + "shaders" + + myFB.myOSystem->baseDir() + "shaders" + BSPF_PATH_SEPARATOR + fFile; ifstream in(filename.c_str()); if(in && in.is_open()) diff --git a/src/debugger/CartDebug.cxx b/src/debugger/CartDebug.cxx index 58f5ad166..6efb95407 100644 --- a/src/debugger/CartDebug.cxx +++ b/src/debugger/CartDebug.cxx @@ -26,9 +26,9 @@ #include "CartDebug.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -CartDebug::CartDebug(Debugger& dbg, Console& console, const RamAreaList& areas, - const Settings& settings) +CartDebug::CartDebug(Debugger& dbg, Console& console, const OSystem& osystem) : DebuggerSystem(dbg, console), + myOSystem(osystem), myRWPortAddress(0), myLabelLength(5) // longest pre-defined label { @@ -36,6 +36,7 @@ CartDebug::CartDebug(Debugger& dbg, Console& console, const RamAreaList& areas, addRamArea(0x80, 128, 0, 0); // Add extended RAM + const RamAreaList& areas = console.cartridge().ramAreas(); for(RamAreaList::const_iterator i = areas.begin(); i != areas.end(); ++i) addRamArea(i->start, i->size, i->roffset, i->woffset); @@ -65,7 +66,7 @@ CartDebug::CartDebug(Debugger& dbg, Console& console, const RamAreaList& areas, // Add settings for Distella DiStella::settings.gfx_format = - settings.getInt("gfxformat") == 16 ? kBASE_16 : kBASE_2; + myOSystem.settings().getInt("gfxformat") == 16 ? kBASE_16 : kBASE_2; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -346,7 +347,8 @@ string CartDebug::disassemble(uInt16 start, uInt16 lines) const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartDebug::addDirective(CartDebug::DisasmType type, uInt16 start, uInt16 end) +bool CartDebug::addDirective(CartDebug::DisasmType type, + uInt16 start, uInt16 end, int bank) { #define PRINT_TAG(d) \ cerr << (d.type == CartDebug::CODE ? "CODE" : \ @@ -360,10 +362,13 @@ bool CartDebug::addDirective(CartDebug::DisasmType type, uInt16 start, uInt16 en PRINT_TAG((*d)); \ cerr << endl; - BankInfo& info = (myDebugger.cpuDebug().pc() & 0x1000) ? - myBankInfo[getBank()] : myBankInfo[myBankInfo.size()-1]; + if(bank < 0) // Do we want the current bank or ZP RAM? + bank = (myDebugger.cpuDebug().pc() & 0x1000) ? getBank() : myBankInfo.size()-1; + bank = BSPF_min(bank, bankCount() - 1); + BankInfo& info = myBankInfo[bank]; DirectiveList& list = info.directiveList; + DirectiveTag tag; tag.type = type; tag.start = start; @@ -642,13 +647,118 @@ string CartDebug::loadSymbolFile(const string& f) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartDebug::loadConfigFile(const string& f) +string CartDebug::loadConfigFile(string file) { -return "NOT YET IMPLEMENTED"; + FilesystemNode node(file); + + if(file == "") // Use config file based on ROM name + { + file = myOSystem.romFile(); + string::size_type spos; + if((spos = file.find_last_of('.')) != string::npos ) + file.replace(spos, file.size(), ".cfg"); + else + file += ".cfg"; + FilesystemNode usercfg(file); + if(usercfg.exists()) + { + node = usercfg; + } + else // Use global config file based on properties cart name + { + const string& globalfile = myOSystem.cfgDir() + + myConsole.properties().get(Cartridge_Name) + ".cfg"; + FilesystemNode globalcfg(globalfile); + if(globalcfg.exists()) + node = globalcfg; + } + } + + if(node.exists() && !node.isDirectory()) + { + ifstream in(node.getPath().c_str()); + if(!in.is_open()) + return "Unable to read directives from " + node.getPath(); + + int currentbank = 0; + while(!in.eof()) + { + // Skip leading space + int c = in.peek(); + while(c == ' ' && c == '\t') + { + in.get(); + c = in.peek(); + } + + string line; + c = in.peek(); + if(c == '/') // Comment, swallow line and continue + { + getline(in, line); + continue; + } + else if(c == '[') + { + in.get(); + getline(in, line, ']'); + stringstream buf(line); + buf >> currentbank; + } + else // Should be commands from this point on + { + getline(in, line); + stringstream buf; + buf << line; + + string directive; + uInt16 start = 0, end = 0; + buf >> directive; + if(BSPF_startsWithIgnoreCase(directive, "ORG")) + { + buf >> hex >> start; +// TODO - figure out what to do with this + cerr << "ignoring directive: " + << directive << " " << HEX4 << start << endl; + } + else if(BSPF_startsWithIgnoreCase(directive, "BLOCK")) + { + buf >> hex >> start; + buf >> hex >> end; +// TODO - add directive for this + cerr << "ignoring directive: " << directive << " " + << HEX4 << start << " " << HEX4 << end << endl; + } + else if(BSPF_startsWithIgnoreCase(directive, "CODE")) + { + buf >> hex >> start; + buf >> hex >> end; + addDirective(CartDebug::CODE, start, end, currentbank); + } + else if(BSPF_startsWithIgnoreCase(directive, "DATA")) + { + buf >> hex >> start; + buf >> hex >> end; + addDirective(CartDebug::DATA, start, end, currentbank); + } + else if(BSPF_startsWithIgnoreCase(directive, "GFX")) + { + buf >> hex >> start; + buf >> hex >> end; + addDirective(CartDebug::GFX, start, end, currentbank); + } + } + } + in.close(); + + return "loaded " + node.getRelativePath() + " OK"; + } + else + return DebuggerParser::red("config file not found"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string CartDebug::saveConfigFile(const string& f) +string CartDebug::saveConfigFile(string file) { return "NOT YET IMPLEMENTED"; } @@ -676,7 +786,7 @@ string CartDebug::listConfig(int bank) << (i->type == CartDebug::CODE ? "CODE" : i->type == CartDebug::DATA ? "DATA" : "GFX") - << " " << hex << i->start << " " << hex << i->end << endl; + << " " << HEX4 << i->start << " " << HEX4 << i->end << endl; } getBankDirectives(buf, info); } diff --git a/src/debugger/CartDebug.hxx b/src/debugger/CartDebug.hxx index 774ccac1a..19275e9f7 100644 --- a/src/debugger/CartDebug.hxx +++ b/src/debugger/CartDebug.hxx @@ -74,8 +74,7 @@ class CartDebug : public DebuggerSystem } Disassembly; public: - CartDebug(Debugger& dbg, Console& console, const RamAreaList& areas, - const Settings& settings); + CartDebug(Debugger& dbg, Console& console, const OSystem& osystem); virtual ~CartDebug(); const DebuggerState& getState(); @@ -161,10 +160,12 @@ class CartDebug : public DebuggerSystem @param type Currently, CODE/DATA/GFX are supported @param start The start address (inclusive) to mark with the given type @param end The end address (inclusive) to mark with the given type + @param bank Bank to which these directive apply (0 indicated current bank) @return True if directive was added, else false if it was removed */ - bool addDirective(CartDebug::DisasmType type, uInt16 start, uInt16 end); + bool addDirective(CartDebug::DisasmType type, uInt16 start, uInt16 end, + int bank = -1); // The following are convenience methods that query the cartridge object // for the desired information. @@ -216,8 +217,8 @@ class CartDebug : public DebuggerSystem /** Load/save Distella config file (Distella directives) */ - string loadConfigFile(const string& file = ""); - string saveConfigFile(const string& file = ""); + string loadConfigFile(string file = ""); + string saveConfigFile(string file = ""); /** Show Distella directives (both set by the user and determined by Distella) @@ -274,6 +275,8 @@ class CartDebug : public DebuggerSystem int extractValue(const char* c) const; private: + const OSystem& myOSystem; + CartState myState; CartState myOldState; diff --git a/src/debugger/Debugger.cxx b/src/debugger/Debugger.cxx index 54db1e395..3b8d54b10 100644 --- a/src/debugger/Debugger.cxx +++ b/src/debugger/Debugger.cxx @@ -211,10 +211,9 @@ void Debugger::setConsole(Console* console) delete myCartDebug; // Register any RAM areas in the Cartridge // Zero-page RAM is automatically recognized by CartDebug - myCartDebug = new CartDebug(*this, *myConsole, myConsole->cartridge().ramAreas(), - myOSystem->settings()); -cerr << myOSystem->romFile() << endl; + myCartDebug = new CartDebug(*this, *myConsole, *myOSystem); myCartDebug->loadSymbolFile(myOSystem->romFile()); + myCartDebug->loadConfigFile(); delete myRiotDebug; myRiotDebug = new RiotDebug(*this, *myConsole); @@ -256,8 +255,7 @@ string Debugger::autoExec() ostringstream buf; // autoexec.stella is always run - FilesystemNode autoexec(myOSystem->baseDir() + BSPF_PATH_SEPARATOR + - "autoexec.stella"); + FilesystemNode autoexec(myOSystem->baseDir() + "autoexec.stella"); buf << "autoExec():" << endl << myParser->exec(autoexec) << endl; diff --git a/src/debugger/DebuggerParser.cxx b/src/debugger/DebuggerParser.cxx index 7308cb91d..1575452a7 100644 --- a/src/debugger/DebuggerParser.cxx +++ b/src/debugger/DebuggerParser.cxx @@ -1024,7 +1024,6 @@ void DebuggerParser::executeJump() ((address & 0xFFF) >= 0)) address--; -cerr << "address " << dec << address << " on line " << dec << line << endl; if(line >= 0 && address >= 0) { debugger->myRom->scrollTo(line); diff --git a/src/emucore/Console.cxx b/src/emucore/Console.cxx index 6399cc68d..5c1036e59 100644 --- a/src/emucore/Console.cxx +++ b/src/emucore/Console.cxx @@ -673,16 +673,14 @@ void Console::setControllers(const string& rommd5) } else if(right == "ATARIVOX") { - const string& eepromfile = myOSystem->eepromDir() + BSPF_PATH_SEPARATOR + - "atarivox_eeprom.dat"; + const string& eepromfile = myOSystem->eepromDir() + "atarivox_eeprom.dat"; myControllers[rightPort] = new AtariVox(Controller::Right, *myEvent, *mySystem, myOSystem->serialPort(), myOSystem->settings().getString("avoxport"), eepromfile); } else if(right == "SAVEKEY") { - const string& eepromfile = myOSystem->eepromDir() + BSPF_PATH_SEPARATOR + - "savekey_eeprom.dat"; + const string& eepromfile = myOSystem->eepromDir() + "savekey_eeprom.dat"; myControllers[rightPort] = new SaveKey(Controller::Right, *myEvent, *mySystem, eepromfile); } diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index 04313ac3b..e1191cd50 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -572,8 +572,8 @@ void EventHandler::poll(uInt64 time) case SDLK_s: // Ctrl-s saves properties to a file { - string filename = myOSystem->baseDir() + BSPF_PATH_SEPARATOR + - myOSystem->console().properties().get(Cartridge_Name) + ".pro"; + string filename = myOSystem->baseDir() + + myOSystem->console().properties().get(Cartridge_Name) + ".pro"; ofstream out(filename.c_str(), ios::out); if(out) { @@ -1992,13 +1992,9 @@ void EventHandler::takeSnapshot(uInt32 number) { // Figure out the correct snapshot name string filename; - string sspath = myOSystem->snapshotDir(); bool showmessage = number == 0; - - if(sspath.length() > 0) - if(sspath.substr(sspath.length()-1) != BSPF_PATH_SEPARATOR) - sspath += BSPF_PATH_SEPARATOR; - sspath += myOSystem->console().properties().get(Cartridge_Name); + string sspath = myOSystem->snapshotDir() + + myOSystem->console().properties().get(Cartridge_Name); // Check whether we want multiple snapshots created if(number > 0) diff --git a/src/emucore/FSNode.hxx b/src/emucore/FSNode.hxx index da9be650d..78ff51d4b 100644 --- a/src/emucore/FSNode.hxx +++ b/src/emucore/FSNode.hxx @@ -299,7 +299,7 @@ class AbstractFilesystemNode * * @note This method is very architecture dependent, please check the concrete implementation for more information. */ - virtual string getName() const = 0; + virtual string getName() const = 0; /** * Returns the 'path' of the current node, usable in fopen(). diff --git a/src/emucore/OSystem.cxx b/src/emucore/OSystem.cxx index 1f5730288..b171850fb 100644 --- a/src/emucore/OSystem.cxx +++ b/src/emucore/OSystem.cxx @@ -311,44 +311,28 @@ void OSystem::setConfigPaths() FilesystemNode node; string s; - s = mySettings->getString("statedir"); - if(s == "") s = myBaseDir + BSPF_PATH_SEPARATOR + "state"; - node = FilesystemNode(s); - myStateDir = node.getPath(); - mySettings->setString("statedir", node.getRelativePath()); - if(!node.isDirectory()) - AbstractFilesystemNode::makeDir(myStateDir); + validatePath("statedir", "state", myStateDir); - s = mySettings->getString("ssdir"); - if(s == "") s = myBaseDir + BSPF_PATH_SEPARATOR + "snapshots"; - node = FilesystemNode(s); - mySnapshotDir = node.getPath(); - mySettings->setString("ssdir", node.getRelativePath()); - if(!node.isDirectory()) - AbstractFilesystemNode::makeDir(mySnapshotDir); + validatePath("ssdir", "snapshots", mySnapshotDir); - s = mySettings->getString("eepromdir"); - if(s == "") s = myBaseDir; - node = FilesystemNode(s); - myEEPROMDir = node.getPath(); - mySettings->setString("eepromdir", node.getRelativePath()); - if(!node.isDirectory()) - AbstractFilesystemNode::makeDir(myEEPROMDir); + validatePath("eepromdir", "", myEEPROMDir); + + validatePath("cfgdir", "cfg", myCfgDir); s = mySettings->getString("cheatfile"); - if(s == "") s = myBaseDir + BSPF_PATH_SEPARATOR + "stella.cht"; + if(s == "") s = myBaseDir + "stella.cht"; node = FilesystemNode(s); myCheatFile = node.getPath(); mySettings->setString("cheatfile", node.getRelativePath()); s = mySettings->getString("palettefile"); - if(s == "") s = myBaseDir + BSPF_PATH_SEPARATOR + "stella.pal"; + if(s == "") s = myBaseDir + "stella.pal"; node = FilesystemNode(s); myPaletteFile = node.getPath(); mySettings->setString("palettefile", node.getRelativePath()); s = mySettings->getString("propsfile"); - if(s == "") s = myBaseDir + BSPF_PATH_SEPARATOR + "stella.pro"; + if(s == "") s = myBaseDir + "stella.pro"; node = FilesystemNode(s); myPropertiesFile = node.getPath(); mySettings->setString("propsfile", node.getRelativePath()); @@ -369,7 +353,10 @@ void OSystem::setBaseDir(const string& basedir) FilesystemNode node(basedir); myBaseDir = node.getPath(); if(!node.isDirectory()) + { AbstractFilesystemNode::makeDir(myBaseDir); + myBaseDir = FilesystemNode(node.getPath()).getPath(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -866,6 +853,22 @@ void OSystem::resetLoopTiming() myTimingInfo.totalFrames = 0; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void OSystem::validatePath(const string& setting, const string& partialpath, + string& fullpath) +{ + const string& s = mySettings->getString(setting) != "" ? + mySettings->getString(setting) : myBaseDir + partialpath; + FilesystemNode node = FilesystemNode(s); + if(!node.isDirectory()) + { + AbstractFilesystemNode::makeDir(s); + node = FilesystemNode(node.getPath()); + } + fullpath = node.getPath(); + mySettings->setString(setting, node.getRelativePath()); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void OSystem::setDefaultJoymap(Event::Type event, EventMode mode) { diff --git a/src/emucore/OSystem.hxx b/src/emucore/OSystem.hxx index 857b1de79..369d1d2f7 100644 --- a/src/emucore/OSystem.hxx +++ b/src/emucore/OSystem.hxx @@ -276,6 +276,11 @@ class OSystem */ const string& eepromDir() const { return myEEPROMDir; } + /** + Return the full/complete directory name for storing Distella cfg files. + */ + const string& cfgDir() const { return myCfgDir; } + /** This method should be called to get the full path of the cheat file. @@ -540,6 +545,7 @@ class OSystem string myStateDir; string mySnapshotDir; string myEEPROMDir; + string myCfgDir; string myCheatFile; string myConfigFile; @@ -641,6 +647,14 @@ class OSystem */ void resetLoopTiming(); + /** + Validate the directory name, and create it if necessary. + Also, update the settings with the new name. For now, validation + means that the path must always end with the appropriate separator. + */ + void validatePath(const string& setting, const string& partialpath, + string& fullpath); + // Copy constructor isn't supported by this class so make it private OSystem(const OSystem&); diff --git a/src/emucore/Settings.cxx b/src/emucore/Settings.cxx index 824168878..a490f828c 100644 --- a/src/emucore/Settings.cxx +++ b/src/emucore/Settings.cxx @@ -102,6 +102,7 @@ Settings::Settings(OSystem* osystem) setInternal("palettefile", ""); setInternal("propsfile", ""); setInternal("eepromdir", ""); + setInternal("cfgdir", ""); // ROM browser options setInternal("uselauncher", "true"); @@ -418,6 +419,7 @@ void Settings::usage() << " -palettefile Full pathname of user-defined palette file\n" << " -propsfile Full pathname of ROM properties file\n" << " -eepromdir Directory in which to save EEPROM files\n" + << " -cfgdir Directory in which to save Distella config files\n" << " -avoxport The name of the serial port where an AtariVox is connected\n" << " -maxres Used by developers to force the maximum size of the application window\n" << " -holdreset Start the emulator with the Game Reset switch held down\n" @@ -433,6 +435,7 @@ void Settings::usage() << endl << " -resolvedata \n" + << " -gfxformat <2|16> Set base to use for displaying GFX sections in the disassembler\n" << " -debuggerres The resolution to use in debugger mode\n" << " -break
Set a breakpoint at 'address'\n" << " -debug Start in debugger mode\n" diff --git a/src/emucore/StateManager.cxx b/src/emucore/StateManager.cxx index 0ad3fd7eb..565935575 100644 --- a/src/emucore/StateManager.cxx +++ b/src/emucore/StateManager.cxx @@ -61,7 +61,7 @@ bool StateManager::toggleRecordMode() { myActiveMode = kOffMode; - string moviefile = /*myOSystem->baseDir() + BSPF_PATH_SEPARATOR +*/ "test.inp"; + string moviefile = /*myOSystem->baseDir() +*/ "test.inp"; if(myMovieWriter.isOpen()) myMovieWriter.close(); if(!myMovieWriter.open(moviefile)) @@ -109,7 +109,7 @@ bool StateManager::toggleRewindMode() { myActiveMode = kOffMode; - string moviefile = /*myOSystem->baseDir() + BSPF_PATH_SEPARATOR +*/ "test.inp"; + string moviefile = /*myOSystem->baseDir() + */ "test.inp"; if(myMovieReader.isOpen()) myMovieReader.close(); if(!myMovieReader.open(moviefile)) @@ -178,7 +178,7 @@ void StateManager::loadState(int slot) if(slot < 0) slot = myCurrentSlot; ostringstream buf; - buf << myOSystem->stateDir() << BSPF_PATH_SEPARATOR + buf << myOSystem->stateDir() << myOSystem->console().properties().get(Cartridge_Name) << ".st" << slot; @@ -222,7 +222,7 @@ void StateManager::saveState(int slot) if(slot < 0) slot = myCurrentSlot; ostringstream buf; - buf << myOSystem->stateDir() << BSPF_PATH_SEPARATOR + buf << myOSystem->stateDir() << myOSystem->console().properties().get(Cartridge_Name) << ".st" << slot; diff --git a/src/gui/FileSnapDialog.cxx b/src/gui/FileSnapDialog.cxx index 6456214cd..da66078b5 100644 --- a/src/gui/FileSnapDialog.cxx +++ b/src/gui/FileSnapDialog.cxx @@ -222,9 +222,7 @@ void FileSnapDialog::saveConfig() void FileSnapDialog::setDefaults() { FilesystemNode node; - string basedir = instance().baseDir(); - if(basedir.compare(basedir.length()-1, 1, BSPF_PATH_SEPARATOR, 0, 1) != 0) - basedir.append(BSPF_PATH_SEPARATOR); + const string& basedir = instance().baseDir(); node = FilesystemNode("~"); myRomPath->setEditString(node.getRelativePath()); diff --git a/src/gui/RomAuditDialog.cxx b/src/gui/RomAuditDialog.cxx index 94f660e18..afe3d0158 100644 --- a/src/gui/RomAuditDialog.cxx +++ b/src/gui/RomAuditDialog.cxx @@ -147,11 +147,7 @@ void RomAuditDialog::auditRoms() // Only rename the file if we found a valid properties entry if(name != "" && name != files[idx].getDisplayName()) { - // Check for terminating separator - string newfile = auditPath; - if(newfile.find_last_of(BSPF_PATH_SEPARATOR) != newfile.length()-1) - newfile += BSPF_PATH_SEPARATOR; - newfile += name + "." + extension; + const string& newfile = node.getPath() + name + "." + extension; if(files[idx].getPath() != newfile) if(AbstractFilesystemNode::renameFile(files[idx].getPath(), newfile)) diff --git a/src/gui/RomInfoWidget.cxx b/src/gui/RomInfoWidget.cxx index 6df6e1637..ca5e627a6 100644 --- a/src/gui/RomInfoWidget.cxx +++ b/src/gui/RomInfoWidget.cxx @@ -109,8 +109,8 @@ void RomInfoWidget::parseProperties() myRomInfo.clear(); // Get a valid filename representing a snapshot file for this rom - const string& filename = instance().snapshotDir() + BSPF_PATH_SEPARATOR + - myProperties.get(Cartridge_Name) + ".png"; + const string& filename = instance().snapshotDir() + + myProperties.get(Cartridge_Name) + ".png"; // Read the PNG file try diff --git a/src/unix/FSNodePOSIX.cxx b/src/unix/FSNodePOSIX.cxx index 3f64597bc..49664975b 100644 --- a/src/unix/FSNodePOSIX.cxx +++ b/src/unix/FSNodePOSIX.cxx @@ -161,7 +161,13 @@ POSIXFilesystemNode::POSIXFilesystemNode(const string& p, bool verify) _displayName = lastPathComponent(_path); if (verify) + { setFlags(); + + // Add a trailing slash, if necessary + if (_isDirectory && _path.length() > 0 && _path[_path.length()-1] != '/') + _path += '/'; + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/win32/FSNodeWin32.cxx b/src/win32/FSNodeWin32.cxx index f5198ecdf..cd00a1cf6 100644 --- a/src/win32/FSNodeWin32.cxx +++ b/src/win32/FSNodeWin32.cxx @@ -278,7 +278,7 @@ WindowsFilesystemNode::WindowsFilesystemNode(const string& p) _isDirectory = ((fileAttribs & FILE_ATTRIBUTE_DIRECTORY) != 0); _isValid = true; - // Add a trailing slash, if necessary. + // Add a trailing backslash, if necessary if (_isDirectory && _path.length() > 0 && _path[_path.length()-1] != '\\') _path += '\\'; }