DBG+BRIDGE+GUI: plugin hotkeys
|
@ -1441,6 +1441,11 @@ BRIDGE_IMPEXP void GuiMenuSetEntryName(int hEntry, const char* name)
|
|||
_gui_sendmessage(GUI_MENU_SET_VISIBLE, (void*)hEntry, (void*)name);
|
||||
}
|
||||
|
||||
BRIDGE_IMPEXP void GuiMenuSetEntryHotkey(int hEntry, const char* hack)
|
||||
{
|
||||
_gui_sendmessage(GUI_MENU_SET_ENTRY_HOTKEY, (void*)hEntry, (void*)hack);
|
||||
}
|
||||
|
||||
BRIDGE_IMPEXP void GuiShowCpu()
|
||||
{
|
||||
_gui_sendmessage(GUI_SHOW_CPU, 0, 0);
|
||||
|
|
|
@ -999,6 +999,7 @@ typedef enum
|
|||
GUI_MENU_SET_NAME, // param1=int hMenu, param2=const char* name
|
||||
GUI_MENU_SET_ENTRY_NAME, // param1=int hEntry, param2=const char* name
|
||||
GUI_FLUSH_LOG, // param1=unused, param2=unused
|
||||
GUI_MENU_SET_ENTRY_HOTKEY, // param1=int hEntry, param2=const char* hack
|
||||
} GUIMSG;
|
||||
|
||||
//GUI Typedefs
|
||||
|
@ -1130,6 +1131,7 @@ BRIDGE_IMPEXP void GuiMenuSetVisible(int hMenu, bool visible);
|
|||
BRIDGE_IMPEXP void GuiMenuSetEntryVisible(int hEntry, bool visible);
|
||||
BRIDGE_IMPEXP void GuiMenuSetName(int hMenu, const char* name);
|
||||
BRIDGE_IMPEXP void GuiMenuSetEntryName(int hEntry, const char* name);
|
||||
BRIDGE_IMPEXP void GuiMenuSetEntryHotkey(int hEntry, const char* hack);
|
||||
BRIDGE_IMPEXP void GuiShowCpu();
|
||||
BRIDGE_IMPEXP void GuiAddQWidgetTab(void* qWidget);
|
||||
BRIDGE_IMPEXP void GuiShowQWidgetTab(void* qWidget);
|
||||
|
|
|
@ -123,6 +123,11 @@ PLUG_IMPEXP void _plugin_menuentrysetname(int pluginHandle, int hEntry, const ch
|
|||
pluginmenuentrysetname(pluginHandle, hEntry, name);
|
||||
}
|
||||
|
||||
PLUG_IMPEXP void _plugin_menuentrysethotkey(int pluginHandle, int hEntry, const char* hotkey)
|
||||
{
|
||||
pluginmenuentrysethotkey(pluginHandle, hEntry, hotkey);
|
||||
}
|
||||
|
||||
PLUG_IMPEXP void _plugin_startscript(CBPLUGINSCRIPT cbScript)
|
||||
{
|
||||
dbgstartscriptthread(cbScript);
|
||||
|
|
|
@ -273,6 +273,7 @@ typedef bool (*CBPLUGINCOMMAND)(int argc, char** argv);
|
|||
typedef void (*CBPLUGINSCRIPT)();
|
||||
typedef duint(*CBPLUGINEXPRFUNCTION)(int argc, duint* argv, void* userdata);
|
||||
typedef FORMATRESULT(*CBPLUGINFORMATFUNCTION)(char* dest, size_t destCount, int argc, char* argv[], duint value, void* userdata);
|
||||
typedef bool (*CBPLUGINPREDICATE)(void* userdata);
|
||||
|
||||
//exports
|
||||
#ifdef __cplusplus
|
||||
|
@ -300,6 +301,7 @@ PLUG_IMPEXP void _plugin_menusetvisible(int pluginHandle, int hMenu, bool visibl
|
|||
PLUG_IMPEXP void _plugin_menuentrysetvisible(int pluginHandle, int hEntry, bool visible);
|
||||
PLUG_IMPEXP void _plugin_menusetname(int pluginHandle, int hMenu, const char* name);
|
||||
PLUG_IMPEXP void _plugin_menuentrysetname(int pluginHandle, int hEntry, const char* name);
|
||||
PLUG_IMPEXP void _plugin_menuentrysethotkey(int pluginHandle, int hEntry, const char* hotkey);
|
||||
PLUG_IMPEXP void _plugin_startscript(CBPLUGINSCRIPT cbScript);
|
||||
PLUG_IMPEXP bool _plugin_waituntilpaused();
|
||||
PLUG_IMPEXP bool _plugin_registerexprfunction(int pluginHandle, const char* name, int argc, CBPLUGINEXPRFUNCTION cbFunction, void* userdata);
|
||||
|
|
|
@ -867,6 +867,32 @@ void pluginmenuentrysetname(int pluginHandle, int hEntry, const char* name)
|
|||
}
|
||||
}
|
||||
|
||||
void pluginmenuentrysethotkey(int pluginHandle, int hEntry, const char* hotkey)
|
||||
{
|
||||
if(hEntry == -1 || !hotkey)
|
||||
return;
|
||||
SHARED_ACQUIRE(LockPluginMenuList);
|
||||
for(const auto & currentMenu : pluginMenuList)
|
||||
{
|
||||
if(currentMenu.pluginHandle == pluginHandle && currentMenu.hEntryPlugin == hEntry)
|
||||
{
|
||||
for(const auto & plugin : pluginList)
|
||||
{
|
||||
if(plugin.initStruct.pluginHandle == pluginHandle)
|
||||
{
|
||||
char name[MAX_PATH] = "";
|
||||
strcpy_s(name, plugin.plugname);
|
||||
*strrchr(name, '.') = '\0';
|
||||
auto hack = StringUtils::sprintf("%s\1%s_%d", hotkey, name, hEntry);
|
||||
GuiMenuSetEntryHotkey(currentMenu.hEntryMenu, hack.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool pluginexprfuncregister(int pluginHandle, const char* name, int argc, CBPLUGINEXPRFUNCTION cbFunction, void* userdata)
|
||||
{
|
||||
PLUG_EXPRFUNCTION plugExprfunction;
|
||||
|
|
|
@ -85,6 +85,7 @@ void pluginmenusetvisible(int pluginHandle, int hMenu, bool visible);
|
|||
void pluginmenuentrysetvisible(int pluginHandle, int hEntry, bool visible);
|
||||
void pluginmenusetname(int pluginHandle, int hMenu, const char* name);
|
||||
void pluginmenuentrysetname(int pluginHandle, int hEntry, const char* name);
|
||||
void pluginmenuentrysethotkey(int pluginHandle, int hEntry, const char* hotkey);
|
||||
bool pluginexprfuncregister(int pluginHandle, const char* name, int argc, CBPLUGINEXPRFUNCTION cbFunction, void* userdata);
|
||||
bool pluginexprfuncunregister(int pluginHandle, const char* name);
|
||||
bool pluginformatfuncregister(int pluginHandle, const char* type, CBPLUGINFORMATFUNCTION cbFunction, void* userdata);
|
||||
|
|
|
@ -782,6 +782,18 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
|
|||
case GUI_FLUSH_LOG:
|
||||
emit flushLog();
|
||||
break;
|
||||
|
||||
case GUI_MENU_SET_ENTRY_HOTKEY:
|
||||
{
|
||||
BridgeResult result;
|
||||
auto params = QString((const char*)param2).split('\1');
|
||||
if(params.length() == 2)
|
||||
{
|
||||
emit setHotkeyMenuEntry(int(param1), params[0], params[1]);
|
||||
result.Wait();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
|
|
@ -117,6 +117,7 @@ signals:
|
|||
void setVisibleMenu(int hMenu, bool visible);
|
||||
void setNameMenuEntry(int hEntry, QString name);
|
||||
void setNameMenu(int hMenu, QString name);
|
||||
void setHotkeyMenuEntry(int hEntry, QString hotkey, QString id);
|
||||
void showCpu();
|
||||
void addQWidgetTab(QWidget* qWidget);
|
||||
void showQWidgetTab(QWidget* qWidget);
|
||||
|
|
|
@ -917,9 +917,21 @@ void CPUDisassembly::setCommentSlot()
|
|||
mLineEdit.setWindowTitle(tr("Add comment at ") + addr_text);
|
||||
if(mLineEdit.exec() != QDialog::Accepted)
|
||||
return;
|
||||
if(!DbgSetCommentAt(wVA, mLineEdit.editText.replace('\r', "").replace('\n', "").toUtf8().constData()))
|
||||
QString comment = mLineEdit.editText.replace('\r', "").replace('\n', "");
|
||||
if(!DbgSetCommentAt(wVA, comment.toUtf8().constData()))
|
||||
SimpleErrorBox(this, tr("Error!"), tr("DbgSetCommentAt failed!"));
|
||||
|
||||
static bool easter = isEaster();
|
||||
if(easter && comment.toLower() == "oep")
|
||||
{
|
||||
QFile file(":/icons/images/egg.wav");
|
||||
if(file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QByteArray egg = file.readAll();
|
||||
PlaySoundA(egg.data(), 0, SND_MEMORY | SND_ASYNC | SND_NODEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
GuiUpdateAllViews();
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,7 @@ MainWindow::MainWindow(QWidget* parent)
|
|||
connect(Bridge::getBridge(), SIGNAL(setIconMenu(int, QIcon)), this, SLOT(setIconMenu(int, QIcon)));
|
||||
connect(Bridge::getBridge(), SIGNAL(setIconMenuEntry(int, QIcon)), this, SLOT(setIconMenuEntry(int, QIcon)));
|
||||
connect(Bridge::getBridge(), SIGNAL(setCheckedMenuEntry(int, bool)), this, SLOT(setCheckedMenuEntry(int, bool)));
|
||||
connect(Bridge::getBridge(), SIGNAL(setHotkeyMenuEntry(int, QString, QString)), this, SLOT(setHotkeyMenuEntry(int, QString, QString)));
|
||||
connect(Bridge::getBridge(), SIGNAL(showCpu()), this, SLOT(displayCpuWidget()));
|
||||
connect(Bridge::getBridge(), SIGNAL(addQWidgetTab(QWidget*)), this, SLOT(addQWidgetTab(QWidget*)));
|
||||
connect(Bridge::getBridge(), SIGNAL(showQWidgetTab(QWidget*)), this, SLOT(showQWidgetTab(QWidget*)));
|
||||
|
@ -708,6 +709,10 @@ void MainWindow::refreshShortcuts()
|
|||
|
||||
setGlobalShortcut(ui->actionStrings, ConfigShortcut("ActionFindStrings"));
|
||||
setGlobalShortcut(ui->actionCalls, ConfigShortcut("ActionFindIntermodularCalls"));
|
||||
|
||||
for(const MenuEntryInfo & entry : mEntryList)
|
||||
if(!entry.hotkeyId.isEmpty())
|
||||
entry.mAction->setShortcut(ConfigShortcut(entry.hotkeyId));
|
||||
}
|
||||
|
||||
void MainWindow::updateMRUMenu()
|
||||
|
@ -1014,7 +1019,7 @@ const MainWindow::MenuInfo* MainWindow::findMenu(int hMenu)
|
|||
void MainWindow::addMenuToList(QWidget* parent, QMenu* menu, int hMenu, int hParentMenu)
|
||||
{
|
||||
if(!findMenu(hMenu))
|
||||
mMenuList.push_back(MenuInfo(parent, menu, hMenu, hParentMenu));
|
||||
mMenuList.push_back(MenuInfo(parent, menu, hMenu, hParentMenu, hMenu == GUI_PLUGIN_MENU));
|
||||
Bridge::getBridge()->setResult();
|
||||
}
|
||||
|
||||
|
@ -1030,7 +1035,7 @@ void MainWindow::addMenu(int hMenu, QString title)
|
|||
QWidget* parent = hMenu == -1 ? this : menu->parent;
|
||||
QMenu* wMenu = new QMenu(title, parent);
|
||||
wMenu->menuAction()->setVisible(false);
|
||||
mMenuList.push_back(MenuInfo(parent, wMenu, hMenuNew, hMenu));
|
||||
mMenuList.push_back(MenuInfo(parent, wMenu, hMenuNew, hMenu, menu->globalMenu));
|
||||
if(hMenu == -1) //top-level
|
||||
ui->menuBar->addMenu(wMenu);
|
||||
else //deeper level
|
||||
|
@ -1056,6 +1061,7 @@ void MainWindow::addMenuEntry(int hMenu, QString title)
|
|||
QWidget* parent = hMenu == -1 ? this : menu->parent;
|
||||
QAction* wAction = new QAction(title, parent);
|
||||
wAction->setObjectName(QString().sprintf("ENTRY|%d", hEntryNew));
|
||||
wAction->setShortcutContext(menu->globalMenu ? Qt::ApplicationShortcut : Qt::WidgetShortcut);
|
||||
parent->addAction(wAction);
|
||||
connect(wAction, SIGNAL(triggered()), this, SLOT(menuEntrySlot()));
|
||||
newInfo.mAction = wAction;
|
||||
|
@ -1199,6 +1205,55 @@ void MainWindow::setCheckedMenuEntry(int hEntry, bool checked)
|
|||
Bridge::getBridge()->setResult();
|
||||
}
|
||||
|
||||
QString MainWindow::nestedMenuDescription(const MenuInfo* menu)
|
||||
{
|
||||
auto found = findMenu(menu->hParentMenu);
|
||||
if(!found)
|
||||
return menu->mMenu->title();
|
||||
auto nest = nestedMenuDescription(found);
|
||||
if(nest.isEmpty())
|
||||
{
|
||||
switch(menu->hParentMenu)
|
||||
{
|
||||
case GUI_DISASM_MENU:
|
||||
nest = tr("&Plugins") + " -> " + tr("Disassembly");
|
||||
break;
|
||||
case GUI_DUMP_MENU:
|
||||
nest = tr("&Plugins") + " -> " + tr("Dump");
|
||||
break;
|
||||
case GUI_STACK_MENU:
|
||||
nest = tr("&Plugins") + " -> " + tr("Stack");
|
||||
break;
|
||||
}
|
||||
}
|
||||
nest += " -> ";
|
||||
return nest + menu->mMenu->title();
|
||||
}
|
||||
|
||||
QString MainWindow::nestedMenuEntryDescription(const MenuEntryInfo & entry)
|
||||
{
|
||||
return QString(nestedMenuDescription(findMenu(entry.hParentMenu)) + " -> " + entry.mAction->text()).replace("&", "");
|
||||
}
|
||||
|
||||
void MainWindow::setHotkeyMenuEntry(int hEntry, QString hotkey, QString id)
|
||||
{
|
||||
for(int i = 0; i < mEntryList.size(); i++)
|
||||
{
|
||||
if(mEntryList.at(i).hEntry == hEntry)
|
||||
{
|
||||
MenuEntryInfo & entry = mEntryList[i];
|
||||
entry.hotkeyId = QString("Plugin_") + id;
|
||||
id.truncate(id.lastIndexOf('_'));
|
||||
entry.hotkey = hotkey;
|
||||
entry.hotkeyGlobal = entry.mAction->shortcutContext() == Qt::ApplicationShortcut;
|
||||
Config()->setPluginShortcut(entry.hotkeyId, nestedMenuEntryDescription(entry), hotkey, entry.hotkeyGlobal);
|
||||
refreshShortcuts();
|
||||
break;
|
||||
}
|
||||
}
|
||||
Bridge::getBridge()->setResult();
|
||||
}
|
||||
|
||||
void MainWindow::setVisibleMenuEntry(int hEntry, bool visible)
|
||||
{
|
||||
for(int i = 0; i < mEntryList.size(); i++)
|
||||
|
@ -1234,6 +1289,7 @@ void MainWindow::setNameMenuEntry(int hEntry, QString name)
|
|||
{
|
||||
const MenuEntryInfo & entry = mEntryList.at(i);
|
||||
entry.mAction->setText(name);
|
||||
Config()->setPluginShortcut(entry.hotkeyId, nestedMenuEntryDescription(entry), entry.hotkey, entry.hotkeyGlobal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,6 +108,7 @@ public slots:
|
|||
void setIconMenuEntry(int hEntry, QIcon icon);
|
||||
void setIconMenu(int hMenu, QIcon icon);
|
||||
void setCheckedMenuEntry(int hEntry, bool checked);
|
||||
void setHotkeyMenuEntry(int hEntry, QString hotkey, QString id);
|
||||
void setVisibleMenuEntry(int hEntry, bool visible);
|
||||
void setVisibleMenu(int hMenu, bool visible);
|
||||
void setNameMenuEntry(int hEntry, QString name);
|
||||
|
@ -212,23 +213,24 @@ private:
|
|||
QAction* mAction;
|
||||
int hEntry;
|
||||
int hParentMenu;
|
||||
QString hotkey;
|
||||
QString hotkeyId;
|
||||
bool hotkeyGlobal;
|
||||
};
|
||||
|
||||
struct MenuInfo
|
||||
{
|
||||
public:
|
||||
MenuInfo(QWidget* parent, QMenu* mMenu, int hMenu, int hParentMenu)
|
||||
MenuInfo(QWidget* parent, QMenu* mMenu, int hMenu, int hParentMenu, bool globalMenu)
|
||||
: parent(parent), mMenu(mMenu), hMenu(hMenu), hParentMenu(hParentMenu), globalMenu(globalMenu)
|
||||
{
|
||||
this->parent = parent;
|
||||
this->mMenu = mMenu;
|
||||
this->hMenu = hMenu;
|
||||
this->hParentMenu = hParentMenu;
|
||||
}
|
||||
|
||||
QWidget* parent;
|
||||
QMenu* mMenu;
|
||||
int hMenu;
|
||||
int hParentMenu;
|
||||
bool globalMenu;
|
||||
};
|
||||
|
||||
QList<MenuEntryInfo> mEntryList;
|
||||
|
@ -238,6 +240,8 @@ private:
|
|||
|
||||
void initMenuApi();
|
||||
const MenuInfo* findMenu(int hMenu);
|
||||
QString nestedMenuDescription(const MenuInfo* menu);
|
||||
QString nestedMenuEntryDescription(const MenuEntryInfo & entry);
|
||||
|
||||
bool bCanClose;
|
||||
MainWindowCloseThread* mCloseThread;
|
||||
|
|
|
@ -921,6 +921,12 @@ void Configuration::setShortcut(const QString key_id, const QKeySequence key_seq
|
|||
noMoreMsgbox = true;
|
||||
}
|
||||
|
||||
void Configuration::setPluginShortcut(const QString key_id, QString description, QString defaultShortcut, bool global)
|
||||
{
|
||||
defaultShortcuts[key_id] = Shortcut(description, defaultShortcut, global);
|
||||
readShortcuts();
|
||||
}
|
||||
|
||||
QColor Configuration::colorFromConfig(const QString id)
|
||||
{
|
||||
char setting[MAX_SETTING_SIZE] = "";
|
||||
|
|
|
@ -66,6 +66,7 @@ public:
|
|||
const QFont getFont(const QString id) const;
|
||||
const Shortcut getShortcut(const QString key_id) const;
|
||||
void setShortcut(const QString key_id, const QKeySequence key_sequence);
|
||||
void setPluginShortcut(const QString key_id, QString description, QString defaultShortcut, bool global);
|
||||
|
||||
//default setting maps
|
||||
QMap<QString, QColor> defaultColors;
|
||||
|
|
|
@ -76,18 +76,45 @@ QString getSymbolicName(duint addr)
|
|||
return addrText;
|
||||
}
|
||||
|
||||
static bool isChristmas()
|
||||
static bool allowSeasons()
|
||||
{
|
||||
srand(GetTickCount());
|
||||
duint setting = 0;
|
||||
if(BridgeSettingGetUint("Misc", "NoChristmas", &setting) && setting)
|
||||
return false;
|
||||
return !BridgeSettingGetUint("Misc", "NoSeasons", &setting) || !setting;
|
||||
}
|
||||
|
||||
static bool isChristmas()
|
||||
{
|
||||
auto date = QDateTime::currentDateTime().date();
|
||||
return date.month() == 12 && date.day() >= 23 && date.day() <= 26;
|
||||
}
|
||||
|
||||
QString couldItBeChristmas(QString icon)
|
||||
//https://www.daniweb.com/programming/software-development/threads/463261/c-easter-day-calculation
|
||||
bool isEaster()
|
||||
{
|
||||
auto date = QDateTime::currentDateTime().date();
|
||||
int K, M, S, A, D, R, OG, SZ, OE, X = date.year();
|
||||
K = X / 100; // Secular number
|
||||
M = 15 + (3 * K + 3) / 4 - (8 * K + 13) / 25; // Secular Moon shift
|
||||
S = 2 - (3 * K + 3) / 4; // Secular sun shift
|
||||
A = X % 19; // Moon parameter
|
||||
D = (19 * A + M) % 30; // Seed for 1st full Moon in spring
|
||||
R = D / 29 + (D / 28 - D / 29) * (A / 11); // Calendarian correction quantity
|
||||
OG = 21 + D - R; // Easter limit
|
||||
SZ = 7 - (X + X / 4 + S) % 7; // 1st sunday in March
|
||||
OE = 7 - (OG - SZ) % 7; // Distance Easter sunday from Easter limit in days
|
||||
int MM = ((OG + OE) > 31) ? 4 : 3;
|
||||
int DD = (((OG + OE) % 31) == 0) ? 31 : ((OG + OE) % 31);
|
||||
return date.month() == MM && date.day() >= DD - 2 && date.day() <= DD + 1;
|
||||
}
|
||||
|
||||
QString couldItBeSeasonal(QString icon)
|
||||
{
|
||||
static bool christmas = isChristmas();
|
||||
return christmas ? QString("christmas%0.png").arg(rand() % 8 + 1) : icon;
|
||||
static bool easter = isEaster();
|
||||
if(christmas)
|
||||
return QString("christmas%1.png").arg(rand() % 8 + 1);
|
||||
else if(easter)
|
||||
return QString("easter%1.png").arg(rand() % 8 + 1);
|
||||
return icon;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@ bool SimpleInputBox(QWidget* parent, const QString & title, QString defaultValue
|
|||
void SimpleErrorBox(QWidget* parent, const QString & title, const QString & text);
|
||||
void SimpleWarningBox(QWidget* parent, const QString & title, const QString & text);
|
||||
QString getSymbolicName(duint addr);
|
||||
QString couldItBeChristmas(QString icon);
|
||||
bool isEaster();
|
||||
QString couldItBeSeasonal(QString icon);
|
||||
|
||||
#define DIcon(file) QIcon(QString(":/icons/images/").append(couldItBeChristmas(file)))
|
||||
#define DIcon(file) QIcon(QString(":/icons/images/").append(couldItBeSeasonal(file)))
|
||||
#endif // MISCUTIL_H
|
||||
|
|
After Width: | Height: | Size: 566 B |
After Width: | Height: | Size: 751 B |
After Width: | Height: | Size: 744 B |
After Width: | Height: | Size: 777 B |
After Width: | Height: | Size: 935 B |
After Width: | Height: | Size: 574 B |
After Width: | Height: | Size: 824 B |
After Width: | Height: | Size: 756 B |
|
@ -297,5 +297,14 @@
|
|||
<file>images/edit-script.png</file>
|
||||
<file>images/load-script.png</file>
|
||||
<file>images/import.png</file>
|
||||
<file>images/easter1.png</file>
|
||||
<file>images/easter2.png</file>
|
||||
<file>images/easter3.png</file>
|
||||
<file>images/easter4.png</file>
|
||||
<file>images/easter5.png</file>
|
||||
<file>images/easter6.png</file>
|
||||
<file>images/easter7.png</file>
|
||||
<file>images/easter8.png</file>
|
||||
<file>images/egg.wav</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -333,7 +333,7 @@ FORMS += \
|
|||
##
|
||||
## Libraries
|
||||
##
|
||||
LIBS += -luser32 -ladvapi32
|
||||
LIBS += -luser32 -ladvapi32 -lwinmm
|
||||
|
||||
!contains(QMAKE_HOST.arch, x86_64) {
|
||||
# Windows x86 (32bit) specific build
|
||||
|
|