Esempio n. 1
0
/* TextEditor::replaceAll
 * Replaces all occurrences of [find] in the text with [replace].
 * Returns the number of occurrences replaced
 *******************************************************************/
int TextEditor::replaceAll(string find, string replace, int flags)
{
	// Check search string
	if (find.IsEmpty())
		return false;

	// Start at beginning
	SetSelection(0, 0);

	// Loop of death
	int replaced = 0;
	while (true)
	{
		SearchAnchor();
		int found = SearchNext(flags, find);
		if (found < 0)
			break;	// No matches, finished
		else
		{
			// Replace text & increment counter
			Replace(found, found + find.length(), replace);
			replaced++;

			// Continue from end of replaced text
			SetSelection(found + find.length(), found + find.length());
		}
	}

	// Return number of instances replaced
	return replaced;
}
Esempio n. 2
0
/* TextEditor::findNext
 * Finds the next occurrence of the [find] after the caret position,
 * selects it and scrolls to it if needed. Returns false if the
 * [find] was invalid or no match was found, true otherwise
 *******************************************************************/
bool TextEditor::findNext(string find)
{
	// Check search string
	if (find.IsEmpty())
		return false;

	// Setup target range
	SetTargetEnd(GetTextLength());
	SetTargetStart(GetSelectionEnd());

	// Search within current target range
	if (SearchInTarget(find) < 0)
	{
		// None found, search again from start
		SetTargetStart(0);
		SetTargetEnd(GetTextLength());
		if (SearchInTarget(find) < 0)
		{
			// No matches found in entire text
			return false;
		}
	}

	// Select matched text
	SetSelection(GetTargetStart(), GetTargetEnd());

	// Scroll to selection
	EnsureCaretVisible();

	return true;
}
Esempio n. 3
0
SToolBarButton::SToolBarButton(wxWindow* parent, string action, string icon)
	: wxControl(parent, -1, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE, wxDefaultValidator, "stbutton")
{
	// Init variables
	this->action = theApp->getAction(action);
	this->state = STATE_NORMAL;

	// Set size
	int size = 22;
	SetSizeHints(size, size, size, size);
	SetMinSize(wxSize(size, size));
	SetSize(size, size);

	// Load icon
	if (icon.IsEmpty())
		this->icon = getIcon(this->action->getIconName());
	else
		this->icon = getIcon(icon);

	// Set tooltip
	string tip = this->action->getText();
	tip.Replace("&", "");
	SetToolTip(tip);

	// Bind events
	Bind(wxEVT_PAINT, &SToolBarButton::onPaint, this);
	Bind(wxEVT_ENTER_WINDOW, &SToolBarButton::onMouseEvent, this);
	Bind(wxEVT_LEAVE_WINDOW, &SToolBarButton::onMouseEvent, this);
	Bind(wxEVT_LEFT_DOWN, &SToolBarButton::onMouseEvent, this);
	Bind(wxEVT_LEFT_UP, &SToolBarButton::onMouseEvent, this);
	Bind(wxEVT_KILL_FOCUS, &SToolBarButton::onFocus, this);
	Bind(wxEVT_LEFT_DCLICK, &SToolBarButton::onMouseEvent, this);
	Bind(wxEVT_ERASE_BACKGROUND, &SToolBarButton::onEraseBackground, this);
}
Esempio n. 4
0
/* TextEditor::replaceCurrent
 * Replaces the currently selected occurrence of [find] with
 * [replace], then selects and scrolls to the next occurrence of
 * [find] in the text. Returns false if [find] is invalid or the
 * current selection does not match it, true otherwise
 *******************************************************************/
bool TextEditor::replaceCurrent(string find, string replace)
{
	// Check search string
	if (find.IsEmpty())
		return false;

	// Check that we've done a find previously
	// (by searching for the find string within the current selection)
	if (GetSelectedText().Length() != find.Length())
		return false;
	SetTargetStart(GetSelectionStart());
	SetTargetEnd(GetSelectionEnd());
	if (SearchInTarget(find) < 0)
		return false;

	// Do the replace
	ReplaceTarget(replace);

	// Update selection
	SetSelection(GetTargetStart(), GetTargetEnd());

	// Do find next
	findNext(find);

	return true;
}
Esempio n. 5
0
static void PrepareOptFolder(string &strSrc, int IsLocalPath_FarPath)
{
	if (strSrc.IsEmpty())
	{
		strSrc = g_strFarPath;
		DeleteEndSlash(strSrc);
	}
	else
	{
		apiExpandEnvironmentStrings(strSrc, strSrc);
	}

	if (!StrCmp(strSrc,L"/"))
	{
		strSrc = g_strFarPath;

		if (IsLocalPath_FarPath)
		{
			strSrc.SetLength(2);
			strSrc += L"\\";
		}
	}
	else
	{
		CheckShortcutFolder(&strSrc,FALSE,TRUE);
	}

	//ConvertNameToFull(strSrc,strSrc);
}
Esempio n. 6
0
/* TextEditor::replaceAll
 * Replaces all occurrences of [find] in the text with [replace].
 * Returns the number of occurrences replaced
 *******************************************************************/
int TextEditor::replaceAll(string find, string replace)
{
	// Check search string
	if (find.IsEmpty())
		return false;

	// Init search target to entire text
	SetTargetStart(0);
	SetTargetEnd(GetTextLength());

	// Loop of death
	int replaced = 0;
	while (1)
	{
		if (SearchInTarget(find) < 0)
			break;	// No matches, finished
		else
		{
			// Replace text & increment counter
			ReplaceTarget(replace);
			replaced++;

			// Continue search from end of replaced text to end of text
			SetTargetStart(GetTargetEnd());
			SetTargetEnd(GetTextLength());
		}
	}

	// Return number of instances replaced
	return replaced;
}
Esempio n. 7
0
/* BrowserCanvas::filterItems
 * Filters the visible items by [filter], by name
 *******************************************************************/
void BrowserCanvas::filterItems(string filter)
{
	// Clear current filter list
	items_filter.clear();

	// If the filter is empty, just add all items to the filter
	if (filter.IsEmpty())
	{
		for (unsigned a = 0; a < items.size(); a++)
			items_filter.push_back(a);
	}
	else
	{
		// Setup filter string
		filter.MakeLower();
		filter += "*";

		// Go through items
		for (unsigned a = 0; a < items.size(); a++)
		{
			// Add to filter list if name matches
			if (items[a]->getName().Lower().Matches(filter))
				items_filter.push_back(a);
		}
	}

	// Update scrollbar and refresh
	updateScrollBar();
	Refresh();
}
Esempio n. 8
0
/* BrowserCanvas::filterItems
 * Filters the visible items by [filter], by name
 *******************************************************************/
void BrowserCanvas::filterItems(string filter)
{
	// Find the currently-viewed item before we change the item list
	int viewed_index = getViewedIndex();

	// Clear current filter list
	items_filter.clear();

	// If the filter is empty, just add all items to the filter
	if (filter.IsEmpty())
	{
		for (unsigned a = 0; a < items.size(); a++)
			items_filter.push_back(a);
	}
	else
	{
		// Setup filter string
		filter.MakeLower();
		filter += "*";

		// Go through items
		for (unsigned a = 0; a < items.size(); a++)
		{
			// Add to filter list if name matches
			if (items[a]->getName().Lower().Matches(filter))
				items_filter.push_back(a);
		}
	}

	// Update scrollbar and refresh
	updateLayout(viewed_index);
}
Esempio n. 9
0
/* Archive::save
 * This is the general, all-purpose 'save archive' function. Takes
 * into account whether the archive is contained within another,
 * is already on the disk, etc etc. Does a 'save as' if [filename]
 * is specified, unless the archive is contained within another.
 * Returns false if saving was unsuccessful, true otherwise
 *******************************************************************/
bool Archive::save(string filename)
{
	bool success = false;

	// Check if the archive is read-only
	if (read_only)
	{
		Global::error = "Archive is read-only";
		return false;
	}

	// If the archive has a parent ArchiveEntry, just write it to that
	if (parent)
	{
		success = write(parent->getMCData());
		parent->setState(1);
	}
	else
	{
		// Otherwise, file stuff
		if (!filename.IsEmpty())
		{
			// New filename is given (ie 'save as'), write to new file and change archive filename accordingly
			success = write(filename);
			if (success) this->filename = filename;

			// Update variables
			this->on_disk = true;
		}
		else if (!this->filename.IsEmpty())
		{
			// No filename is given, but the archive has a filename, so overwrite it (and make a backup)

			// Create backup
			if (wxFileName::FileExists(this->filename) && save_backup)
			{
				// Copy current file contents to new backup file
				string bakfile = this->filename + ".bak";
				wxLogMessage("Creating backup %s", bakfile);
				wxCopyFile(this->filename, bakfile, true);
			}

			// Write it to the file
			success = write(this->filename);

			// Update variables
			this->on_disk = true;
		}
	}

	// If saving was successful, update variables and announce save
	if (success)
	{
		setModified(false);
		announce("saved");
	}

	return success;
}
Esempio n. 10
0
/* Executables::nExternalExes
 * Returns the number of external executables for [category], or
 * all if [category] is not specified
 *******************************************************************/
int Executables::nExternalExes(string category)
{
	int num = 0;
	for (auto& exe : external_exes)
		if (category.IsEmpty() || exe.category == category)
			num++;

	return num;
}
Esempio n. 11
0
/* Executables::getExternalExe
 * Returns the external executable matching [name] and [category].
 * If [category] is empty, it is ignored
 *******************************************************************/
Executables::external_exe_t Executables::getExternalExe(string name, string category)
{
	for (auto& exe : external_exes)
		if (category.IsEmpty() || exe.category == category)
			if (exe.name == name)
				return exe;

	return external_exe_t();
}
Esempio n. 12
0
/* Executables::getExternalExe
 * Returns a list of all external executables matching [category].
 * If [category] is empty, it is ignored
 *******************************************************************/
vector<Executables::external_exe_t> Executables::getExternalExes(string category)
{
	vector<external_exe_t> ret;
	for (auto& exe : external_exes)
		if (category.IsEmpty() || exe.category == category)
			ret.push_back(exe);

	return ret;
}
Esempio n. 13
0
/* PaletteManager::addPalette
 * Adds the palette [pal] to the list of managed palettes, identified
 * by [name]. Returns false if the palette doesn't exist or the name
 * is invalid, true otherwise
 *******************************************************************/
bool PaletteManager::addPalette(Palette8bit* pal, string name)
{
	// Check palette and name were given
	if (!pal || name.IsEmpty())
		return false;

	// Add palette+name
	palettes.push_back(pal);
	pal_names.push_back(name);

	return true;
}
Esempio n. 14
0
void farPrepareFileName(string& strFileName)
{
	apiExpandEnvironmentStrings(strFileName, strFileName);

	farUnquote(strFileName);
	farTrim(strFileName);

	if ( strFileName.IsEmpty() )
		strFileName = _T(".");

	farGetFullPathName(strFileName, strFileName);
}
Esempio n. 15
0
/* SImage::open
 * Detects the format of [data] and, if it's a valid image format,
 * loads it into this image
 *******************************************************************/
bool SImage::open(MemChunk& data, int index, string type_hint)
{
	// Check with type hint format first
	if (!type_hint.IsEmpty())
	{
		SIFormat* format = SIFormat::getFormat(type_hint);
		if (format != SIFormat::unknownFormat() && format->isThisFormat(data))
			return format->loadImage(*this, data, index);
	}

	// No type hint given or didn't match, autodetect format with SIFormat system instead
	return SIFormat::determineFormat(data)->loadImage(*this, data, index);
}
Esempio n. 16
0
/* KeyBind::addBind
 * Adds a new keybind
 *******************************************************************/
bool KeyBind::addBind(string name, keypress_t key, string desc, string group, bool ignore_shift, int priority)
{
    // Find keybind
    KeyBind* bind = NULL;
    for (unsigned a = 0; a < keybinds.size(); a++)
    {
        if (keybinds[a].name == name)
        {
            bind = &keybinds[a];
            break;
        }
    }

    // Add keybind if it doesn't exist
    if (!bind)
    {
        keybinds.push_back(KeyBind(name));
        bind = &keybinds.back();
        bind->ignore_shift = ignore_shift;
    }

    // Set keybind description/group
    if (!desc.IsEmpty())
    {
        bind->description = desc;
        bind->group = group;
    }

    // Check if the key is already bound to it
    for (unsigned a = 0; a < bind->keys.size(); a++)
    {
        if (bind->keys[a].alt == key.alt &&
                bind->keys[a].ctrl == key.ctrl &&
                bind->keys[a].shift == key.shift &&
                bind->keys[a].key == key.key)
        {
            // It is, remove the bind
            bind->keys.erase(bind->keys.begin() + a);
            return false;
        }
    }

    // Set priority
    if (priority >= 0)
        bind->priority = priority;

    // Add the keybind
    bind->addKey(key.key, key.alt, key.ctrl, key.shift);

    return true;
}
Esempio n. 17
0
string& WINAPI RemoveTrailingSpaces(string &strStr)
{
	if (strStr.IsEmpty())
		return strStr;

	const wchar_t *Str = strStr;
	const wchar_t *ChPtr = Str + strStr.GetLength() - 1;

	for (; ChPtr >= Str && (IsSpace(*ChPtr) || IsEol(*ChPtr)); ChPtr--)
		;

	strStr.SetLength(ChPtr < Str ? 0 : ChPtr-Str+1);
	return strStr;
}
Esempio n. 18
0
//------------------------------------------------
// Функция проверяет заголовок окна и возвращает
// не нулевое значение если окно является окном
// ввода пароля улюча
// Результат:
//            1 - Окно ввода основного пароля
//            2 - Окно ввода второго пароля
//------------------------------------------------
int TPrivatBank::IsKeyPassWnd(const string& WndText)
{
	if (!WndText.IsEmpty())
    {
		// Проверяем основной пароль
		string Mask = GetStr(EStrPrivatBankKeyPassWndMask);
		if (WildCmp(WndText.t_str(), Mask.t_str()))
			return 1;

		// Проверяем второй пароль
		Mask = GetStr(EStrPrivatBankKeyPassWndMask2);
		if (WildCmp(WndText.t_str(), Mask.t_str()))
			return 2;
    }
	return 0;
}
Esempio n. 19
0
/* STreeNode::getChildren
 * Returns a list of all the node's children matching [name]. Also
 * handles paths as per getChild
 *******************************************************************/
vector<STreeNode*> STreeNode::getChildren(string name)
{
	// Init return vector
	vector<STreeNode*> ret;

	// Check name was given
	if (name.IsEmpty())
		return ret;

	// If name ends with /, remove it
	if (name.EndsWith("/"))
		name.RemoveLast(1);

	// Convert name to path for processing
	wxFileName fn(name);

	// If no directories were given
	if (fn.GetDirCount() == 0)
	{
		// Find child of this node
		for (unsigned a = 0; a < children.size(); a++)
		{
			if (S_CMPNOCASE(name, children[a]->getName()))
				ret.push_back(children[a]);
		}
	}
	else
	{
		// Directories were given, get the first directory
		string dir = fn.GetDirs()[0];

		// See if it is a child of this node
		STreeNode* child = getChild(dir);
		if (child)
		{
			// It is, remove the first directory and continue searching that child
			fn.RemoveDir(0);
			return child->getChildren(fn.GetFullPath(wxPATH_UNIX));
		}
	}

	return ret;
}
Esempio n. 20
0
/* NodesPrefsPanel::populateOptions
 * Populates the options CheckListBox with options for the currently
 * selected node builder
 *******************************************************************/
void NodesPrefsPanel::populateOptions(string options)
{
	// Get current builder
	NodeBuilders::builder_t& builder = NodeBuilders::getBuilder(choice_nodebuilder->GetSelection());

	// Set builder path
	text_path->SetValue(builder.path);

	// Clear current options
	clb_options->Clear();

	// Add builder options
	for (unsigned a = 0; a < builder.option_desc.size(); a++)
	{
		clb_options->Append(builder.option_desc[a]);
		if (!options.IsEmpty() && options.Contains(S_FMT(" %s ", builder.options[a])))
			clb_options->Check(a);
	}
}
Esempio n. 21
0
	SIDCacheItem(const wchar_t *Computer,PSID InitSID)
	{
		Sid=xf_malloc(GetLengthSid(InitSID));
		if(Sid)
		{
			if(CopySid(GetLengthSid(InitSID),Sid,InitSID))
			{
				DWORD AccountLength=0,DomainLength=0;
				SID_NAME_USE snu;
				LookupAccountSid(Computer,Sid,nullptr,&AccountLength,nullptr,&DomainLength,&snu);
				if (AccountLength && DomainLength)
				{
					string strAccountName,strDomainName;
					LPWSTR AccountName=strAccountName.GetBuffer(AccountLength);
					LPWSTR DomainName=strDomainName.GetBuffer(DomainLength);
					if (AccountName && DomainName)
					{
						if(LookupAccountSid(Computer,Sid,AccountName,&AccountLength,DomainName,&DomainLength,&snu))
						{
							strUserName=string(DomainName).Append(L"\\").Append(AccountName);
						}
					}
				}
				else
				{
					LPWSTR StrSid;
					if(ConvertSidToStringSid(Sid, &StrSid))
					{
						strUserName = StrSid;
						LocalFree(StrSid);
					}
				}
			}
		}

		if(strUserName.IsEmpty())
		{
			xf_free(Sid);
			Sid=nullptr;
		}
	}
Esempio n. 22
0
void SToolBar::deleteGroup(string name)
{
	// Do nothing if no name specified
	if (name.IsEmpty())
		return;

	// Find group to delete
	for (unsigned a = 0; a < groups.size(); a++)
	{
		if (S_CMPNOCASE(groups[a]->getName(), name))
		{
			// Delete group
			delete groups[a];
			groups.erase(groups.begin() + a);
			break;
		}
	}

	// Update layout
	updateLayout(true);
}
/* PreferencesDialog::openPreferences
 * Opens a preferences dialog on top of [parent], showing either the
 * last viewed page or [initial_page] if it is specified
 *******************************************************************/
void PreferencesDialog::openPreferences(wxWindow* parent, string initial_page)
{
	// Setup dialog
	PreferencesDialog dlg(parent);
	if (initial_page.IsEmpty())
		initial_page = last_page;
	dlg.showPage(initial_page);
	if (width > 0 && height > 0)
		dlg.SetSize(width, height);
	dlg.initPages();
	dlg.CenterOnParent();

	// Show dialog
	if (dlg.ShowModal() == wxID_OK)
		dlg.applyPreferences();
	theMainWindow->getArchiveManagerPanel()->refreshAllTabs();

	// Save state
	last_page = dlg.currentPage();
	width = dlg.GetSize().x;
	height = dlg.GetSize().y;
}
Esempio n. 24
0
BOOL DeleteEndSlash(string &strPath, bool AllEndSlash)
{
	BOOL Ret=FALSE;

	if (!strPath.IsEmpty())
	{
		size_t len=strPath.GetLength();
		wchar_t *lpwszPath = strPath.GetBuffer();

		while (len && IsSlash(lpwszPath[--len]))
		{
			Ret=TRUE;
			lpwszPath[len] = L'\0';

			if (!AllEndSlash)
				break;
		}

		strPath.ReleaseBuffer();
	}

	return Ret;
}
Esempio n. 25
0
bool ArchiveModuleManager::GetCommand(
		ArchiveFormat* pFormat,
		int nCommand,
		string& strCommand
		)
{
	if ( !pFormat )
		return false;

	if ( m_pConfig->GetCommand(pFormat, nCommand, strCommand) )
		return true;

	bool bEnabled;

	if ( pFormat->GetDefaultCommand(
			nCommand, 
			strCommand, 
			bEnabled
			) && bEnabled && !strCommand.IsEmpty() )
		return true;
	
	return false;
}
Esempio n. 26
0
/* MapTextureManager::getVerticalOffset
 * Detects offset hacks such as that used by the wall torch thing in
 * Heretic (type 50). If the Y offset is noticeably larger than the
 * sprite height, that means the thing is supposed to be rendered
 * above its real position.
 *******************************************************************/
int MapTextureManager::getVerticalOffset(string name)
{
	// Don't bother looking for nameless sprites
	if (name.IsEmpty())
		return 0;

	// Get sprite matching name
	ArchiveEntry* entry = theResourceManager->getPatchEntry(name, "sprites", archive);
	if (!entry) entry = theResourceManager->getPatchEntry(name, "", archive);
	if (entry)
	{
		SImage image;
		Misc::loadImageFromEntry(&image, entry);
		int h = image.getHeight();
		int o = image.offset().y;
		if (o > h)
			return o - h;
		else
			return 0;
	}

	return 0;
}
Esempio n. 27
0
	SLADECrashDialog(SLADEStackTrace& st) : wxDialog(wxTheApp->GetTopWindow(), -1, "SLADE3 Application Crash")
	{
		// Setup sizer
		wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
		SetSizer(sizer);

		// Add general crash method
		string message = "SLADE3 has crashed unexpectedly. To help fix the problem that caused this crash,\nplease copy+paste the information from the window below to a text file, and email\nit to <*****@*****.**> along with a description of what you were\ndoing at the time of the crash. Sorry for the inconvenience.";
		sizer->Add(new wxStaticText(this, -1, message), 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 4);

		// Setup stack trace string
		string trace = S_FMT("Version: %s\n", Global::version);
		if (current_action.IsEmpty())
			trace += "No current action\n";
		else
			trace += S_FMT("Current action: %s", current_action);
		trace += "\n";
		trace += st.getTraceString();

		// Add stack trace text area
		text_stack = new wxTextCtrl(this, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL);
		text_stack->SetValue(trace);
		text_stack->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
		sizer->Add(text_stack, 1, wxEXPAND|wxALL, 4);

		// Dump stack trace to a file (just in case)
		wxFile file(appPath("slade3_crash.log", DIR_USER), wxFile::write);
		file.Write(trace);
		file.Close();

		// Add standard 'OK' button
		sizer->Add(CreateStdDialogButtonSizer(wxOK), 0, wxEXPAND|wxALL, 4);

		// Setup layout
		Layout();
		SetInitialSize(wxSize(500, 500));
	}
Esempio n. 28
0
bool SearchTreeNode::s2i(const string& s,int& i)
{
    bool is_ok = true;
    i = 0;
    unsigned int u = 0;
    if(!s.IsEmpty())
    {
        if(s[0]==_T('-'))
        {
            if(!s2u(s.substr(1),u))
                is_ok = false;
            else
                i = 0 - u;
        }
        else
        {
            if(!s2u(s.substr(1),u))
                is_ok = false;
            else
                i = u;
        }
    }
    return is_ok;
}
Esempio n. 29
0
/* TextEditor::findPrev
 * Finds the previous occurrence of the [find] after the caret
 * position, selects it and scrolls to it if needed. Returns false
 * if the [find] was invalid or no match was found, true otherwise
 *******************************************************************/
bool TextEditor::findPrev(string find, int flags)
{
	// Check search string
	if (find.IsEmpty())
		return false;

	// Get current selection
	int sel_start = GetSelectionStart();
	int sel_end = GetSelectionEnd();

	// Search back from the start of the current selection
	SetSelection(sel_start, sel_start);
	SearchAnchor();
	int found = SearchPrev(flags, find);
	if (found < 0)
	{
		// Not found, loop back to end
		SetSelection(GetTextLength() - 1, GetTextLength() - 1);
		SearchAnchor();
		found = SearchPrev(flags, find);
		if (found < 0)
		{
			// No match found in entire text, reset selection
			SetSelection(sel_start, sel_end);
			return false;
		}
	}

	// Set caret to the end of the matching text
	// (it defaults to the start for some dumb reason)
	// and scroll to the selection
	SetSelection(found, found + find.length());
	EnsureCaretVisible();

	return true;
}
Esempio n. 30
0
/* RunDialog::getSelectedCommandLine
 * Returns a command line based on the currently selected run
 * configuration and resources
 *******************************************************************/
string RunDialog::getSelectedCommandLine(Archive* archive, string map_name, string map_file)
{
	Executables::game_exe_t* exe = Executables::getGameExe(choice_game_exes->GetSelection());
	if (exe)
	{
		// Get exe path
		const string exe_path = getExecutablePath(exe);

		if (exe_path.IsEmpty())
			return "";

		string path = S_FMT("\"%s\"", exe_path);

		unsigned cfg = choice_config->GetSelection();
		if (cfg < exe->configs.size())
		{
			path += " ";
			path += exe->configs[cfg].value;
		}

		// IWAD
		Archive* bra = theArchiveManager->baseResourceArchive();
		path.Replace("%i", S_FMT("\"%s\"", bra ? bra->getFilename() : ""));

		// Resources
		path.Replace("%r", getSelectedResourceList());

		// Archive (+ temp map if specified)
		if (map_file.IsEmpty() && archive)
			path.Replace("%a", S_FMT("\"%s\"", archive->getFilename()));
		else
		{
			if (archive)
				path.Replace("%a", S_FMT("\"%s\" \"%s\"", archive->getFilename(), map_file));
			else
				path.Replace("%a", S_FMT("\"%s\"", map_file));
		}

		// Running an archive yields no map name, so don't try to warp
		if (map_name.IsEmpty())
		{
			path.Replace("-warp ", wxEmptyString);
			path.Replace("+map ",  wxEmptyString);
			path.Replace("%mn",    wxEmptyString);
			path.Replace("%mw",    wxEmptyString);
		}
		// Map name
		else
		{
			path.Replace("%mn", map_name);

			// Map warp
			if (path.Contains("%mw"))
			{
				string mn = map_name.Lower();

				// MAPxx
				string mapnum;
				if (mn.StartsWith("map", &mapnum))
					path.Replace("%mw", mapnum);

				// ExMx
				else if (map_name.length() == 4 && mn[0] == 'e' && mn[2] == 'm')
					path.Replace("%mw", S_FMT("%c %c", mn[1], mn[3]));
			}
		}

		// Extra parameters
		if (!text_extra_params->GetValue().IsEmpty())
		{
			path += " ";
			path += text_extra_params->GetValue();
		}

		LOG_MESSAGE(2, "Run command: %s", path);
		return path;
	}

	return "";
}