mw4print: MySQL export, configurable banner text
MySQL database export (dbexport.h / dbexport.cpp): - New files dbexport.h / dbexport.cpp implement late-bound MySQL export. libmysql.dll is loaded at runtime via LoadLibrary/GetProcAddress so no MySQL SDK is required at compile time; the app runs normally if the DLL is absent. - After each print job, match data is exported to a MySQL server before PrintDlg() is called: one row in 'match', one row per player in 'player_result', one row per attacker/victim pair in 'pvp'. An optional 'event' table records every individual SRecScore entry (off by default). Tables are created automatically (CREATE TABLE IF NOT EXISTS) on first connect. - Config stored in mw4print.ini (app directory), section [MySQLExport]: Enabled, Host, Port, Database, Username, Password, ExportEvents. - Config loaded at startup (OnCreate); DB_LoadConfig() / DB_SaveConfig(). - Connection timeout set to 5 seconds so the app does not hang if the server is unreachable. - libmysql.dll (MySQL Connector/C 32-bit) added to Gameleap/mw4/ so the deploy script copies it to MW4/ alongside mw4print.exe. Database Settings dialog (File > Database Settings... / Ctrl+D): - MFC dialog: enable checkbox, Host/Port/Database/Username/Password fields, Export Events checkbox, Test Connection button with live status label, OK/Cancel. OK persists settings to mw4print.ini immediately. Configurable banner text (File > Banner Setting...): - The 'WWW.MECHJOCK.COM' URL string printed at the bottom of every score sheet is now configurable. Stored as BannerText= in options.ini under [battle tech print] (same section/file as the other print layout params). File > Banner Setting... opens a dialog to edit it; OK saves to options.ini and takes effect on the next print job with no restart needed. Default value is the original MECHJOCK string if the key is absent.
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "mw4dummy.h"
|
#include "mw4dummy.h"
|
||||||
#include "recscore.h"
|
#include "recscore.h"
|
||||||
|
#include "dbexport.h"
|
||||||
typedef unsigned long UINT4;
|
typedef unsigned long UINT4;
|
||||||
typedef unsigned char* POINTER;
|
typedef unsigned char* POINTER;
|
||||||
#include "logreport.h"
|
#include "logreport.h"
|
||||||
@@ -23,6 +24,7 @@ static char THIS_FILE[] = __FILE__;
|
|||||||
|
|
||||||
#include "mw4dummy.cpp"
|
#include "mw4dummy.cpp"
|
||||||
#include "recscore.cpp"
|
#include "recscore.cpp"
|
||||||
|
#include "dbexport.cpp"
|
||||||
#include "logreport.cpp"
|
#include "logreport.cpp"
|
||||||
|
|
||||||
#define IDT_TIMER 1000
|
#define IDT_TIMER 1000
|
||||||
@@ -37,6 +39,12 @@ BEGIN_MESSAGE_MAP(CChildView,CWnd )
|
|||||||
ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
|
ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
|
||||||
ON_COMMAND(ID_FILE_LOGREPORT, OnFileLogReport)
|
ON_COMMAND(ID_FILE_LOGREPORT, OnFileLogReport)
|
||||||
ON_COMMAND(ID_FILE_LOG2DATES, OnFileLog2Dates)
|
ON_COMMAND(ID_FILE_LOG2DATES, OnFileLog2Dates)
|
||||||
|
ON_COMMAND(ID_FILE_DBSETTINGS, OnFileDbSettings)
|
||||||
|
ON_COMMAND(ID_FILE_BANNERSETTING, OnFileBannerSetting)
|
||||||
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
|
BEGIN_MESSAGE_MAP(CDlgDBSettings, CDialog)
|
||||||
|
ON_BN_CLICKED(IDC_DB_TEST, OnTestConnection)
|
||||||
END_MESSAGE_MAP()
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
BEGIN_MESSAGE_MAP(CDlgGet2Dates, CDialog)
|
BEGIN_MESSAGE_MAP(CDlgGet2Dates, CDialog)
|
||||||
@@ -299,7 +307,9 @@ int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
|
|||||||
{
|
{
|
||||||
if (CWnd ::OnCreate(lpCreateStruct) == -1)
|
if (CWnd ::OnCreate(lpCreateStruct) == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
DB_LoadConfig();
|
||||||
|
|
||||||
m_uTimer = SetTimer(IDT_TIMER, 1 * 1000, NULL);
|
m_uTimer = SetTimer(IDT_TIMER, 1 * 1000, NULL);
|
||||||
if (!m_uTimer)
|
if (!m_uTimer)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -343,6 +353,121 @@ void CChildView::OnTimer(UINT nIDEvent)
|
|||||||
CWnd ::OnTimer(nIDEvent);
|
CWnd ::OnTimer(nIDEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CChildView::OnFileBannerSetting()
|
||||||
|
{
|
||||||
|
CDlgBannerSetting dlg(this);
|
||||||
|
dlg.DoModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CDlgBannerSetting
|
||||||
|
|
||||||
|
CDlgBannerSetting::CDlgBannerSetting(CWnd* pParent)
|
||||||
|
: CDialog(CDlgBannerSetting::IDD, pParent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN_MESSAGE_MAP(CDlgBannerSetting, CDialog)
|
||||||
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
|
void CDlgBannerSetting::DoDataExchange(CDataExchange* pDX)
|
||||||
|
{
|
||||||
|
CDialog::DoDataExchange(pDX);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CDlgBannerSetting::OnInitDialog()
|
||||||
|
{
|
||||||
|
CDialog::OnInitDialog();
|
||||||
|
SetDlgItemText(IDC_BANNER_TEXT, g_szPrintBanner);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDlgBannerSetting::OnOK()
|
||||||
|
{
|
||||||
|
GetDlgItemText(IDC_BANNER_TEXT, g_szPrintBanner, sizeof(g_szPrintBanner));
|
||||||
|
|
||||||
|
// Persist to options.ini so it survives a restart.
|
||||||
|
char szINI[MAX_PATH];
|
||||||
|
sprintf(szINI, "%s\\options.ini", AssetsDirectory1);
|
||||||
|
WritePrivateProfileString("battle tech print", "BannerText", g_szPrintBanner, szINI);
|
||||||
|
|
||||||
|
CDialog::OnOK();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CChildView::OnFileDbSettings()
|
||||||
|
{
|
||||||
|
CDlgDBSettings dlg(this);
|
||||||
|
dlg.DoModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CDlgDBSettings
|
||||||
|
|
||||||
|
CDlgDBSettings::CDlgDBSettings(CWnd* pParent)
|
||||||
|
: CDialog(CDlgDBSettings::IDD, pParent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDlgDBSettings::DoDataExchange(CDataExchange* pDX)
|
||||||
|
{
|
||||||
|
CDialog::DoDataExchange(pDX);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CDlgDBSettings::OnInitDialog()
|
||||||
|
{
|
||||||
|
CDialog::OnInitDialog();
|
||||||
|
|
||||||
|
CheckDlgButton(IDC_DB_ENABLED, g_dbConfig.bEnabled ? BST_CHECKED : BST_UNCHECKED);
|
||||||
|
CheckDlgButton(IDC_DB_EXPORTEVENTS, g_dbConfig.bExportEvents ? BST_CHECKED : BST_UNCHECKED);
|
||||||
|
|
||||||
|
SetDlgItemText(IDC_DB_HOST, g_dbConfig.szHost);
|
||||||
|
SetDlgItemInt (IDC_DB_PORT, g_dbConfig.nPort, FALSE);
|
||||||
|
SetDlgItemText(IDC_DB_DATABASE, g_dbConfig.szDatabase);
|
||||||
|
SetDlgItemText(IDC_DB_USERNAME, g_dbConfig.szUsername);
|
||||||
|
SetDlgItemText(IDC_DB_PASSWORD, g_dbConfig.szPassword);
|
||||||
|
|
||||||
|
SetDlgItemText(IDC_DB_STATUS, "");
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDlgDBSettings::OnOK()
|
||||||
|
{
|
||||||
|
g_dbConfig.bEnabled = IsDlgButtonChecked(IDC_DB_ENABLED) == BST_CHECKED;
|
||||||
|
g_dbConfig.bExportEvents = IsDlgButtonChecked(IDC_DB_EXPORTEVENTS) == BST_CHECKED;
|
||||||
|
|
||||||
|
GetDlgItemText(IDC_DB_HOST, g_dbConfig.szHost, sizeof(g_dbConfig.szHost));
|
||||||
|
g_dbConfig.nPort = (int)GetDlgItemInt(IDC_DB_PORT, NULL, FALSE);
|
||||||
|
GetDlgItemText(IDC_DB_DATABASE, g_dbConfig.szDatabase, sizeof(g_dbConfig.szDatabase));
|
||||||
|
GetDlgItemText(IDC_DB_USERNAME, g_dbConfig.szUsername, sizeof(g_dbConfig.szUsername));
|
||||||
|
GetDlgItemText(IDC_DB_PASSWORD, g_dbConfig.szPassword, sizeof(g_dbConfig.szPassword));
|
||||||
|
|
||||||
|
DB_SaveConfig();
|
||||||
|
CDialog::OnOK();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDlgDBSettings::OnTestConnection()
|
||||||
|
{
|
||||||
|
// Read fields into a temporary config so we test what's in the UI,
|
||||||
|
// not necessarily what was last saved.
|
||||||
|
SDBConfig saved = g_dbConfig;
|
||||||
|
|
||||||
|
GetDlgItemText(IDC_DB_HOST, g_dbConfig.szHost, sizeof(g_dbConfig.szHost));
|
||||||
|
g_dbConfig.nPort = (int)GetDlgItemInt(IDC_DB_PORT, NULL, FALSE);
|
||||||
|
GetDlgItemText(IDC_DB_DATABASE, g_dbConfig.szDatabase, sizeof(g_dbConfig.szDatabase));
|
||||||
|
GetDlgItemText(IDC_DB_USERNAME, g_dbConfig.szUsername, sizeof(g_dbConfig.szUsername));
|
||||||
|
GetDlgItemText(IDC_DB_PASSWORD, g_dbConfig.szPassword, sizeof(g_dbConfig.szPassword));
|
||||||
|
|
||||||
|
SetDlgItemText(IDC_DB_STATUS, "Testing...");
|
||||||
|
UpdateWindow();
|
||||||
|
|
||||||
|
char szError[512] = "";
|
||||||
|
bool bOK = DB_TestConnection(szError, sizeof(szError));
|
||||||
|
SetDlgItemText(IDC_DB_STATUS, bOK ? "OK - Connected successfully." : szError);
|
||||||
|
|
||||||
|
g_dbConfig = saved; // restore; OnOK() will save the final values
|
||||||
|
}
|
||||||
|
|
||||||
void CChildView::OnFileOpen()
|
void CChildView::OnFileOpen()
|
||||||
{
|
{
|
||||||
// TODO: Add your command handler code here
|
// TODO: Add your command handler code here
|
||||||
|
|||||||
@@ -9,11 +9,41 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#endif // _MSC_VER > 1000
|
#endif // _MSC_VER > 1000
|
||||||
|
|
||||||
|
class CDlgBannerSetting : public CDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CDlgBannerSetting(CWnd* pParent = NULL);
|
||||||
|
enum { IDD = IDD_BANNER_SETTING };
|
||||||
|
protected:
|
||||||
|
virtual void DoDataExchange(CDataExchange* pDX);
|
||||||
|
virtual BOOL OnInitDialog();
|
||||||
|
virtual void OnOK();
|
||||||
|
DECLARE_MESSAGE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
// CChildView window
|
// CChildView window
|
||||||
|
|
||||||
class MW4PRINT_COPYDATASTRUCT;
|
class MW4PRINT_COPYDATASTRUCT;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CDlgDBSettings dialog
|
||||||
|
|
||||||
|
class CDlgDBSettings : public CDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CDlgDBSettings(CWnd* pParent = NULL);
|
||||||
|
|
||||||
|
enum { IDD = IDD_DB_SETTINGS };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void DoDataExchange(CDataExchange* pDX);
|
||||||
|
virtual BOOL OnInitDialog();
|
||||||
|
virtual void OnOK();
|
||||||
|
afx_msg void OnTestConnection();
|
||||||
|
DECLARE_MESSAGE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
class CMyPrintInfo : public TDBLNK(CMyPrintInfo*)
|
class CMyPrintInfo : public TDBLNK(CMyPrintInfo*)
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -131,6 +161,8 @@ protected:
|
|||||||
afx_msg void OnFileOpen();
|
afx_msg void OnFileOpen();
|
||||||
afx_msg void OnFileLogReport();
|
afx_msg void OnFileLogReport();
|
||||||
afx_msg void OnFileLog2Dates();
|
afx_msg void OnFileLog2Dates();
|
||||||
|
afx_msg void OnFileDbSettings();
|
||||||
|
afx_msg void OnFileBannerSetting();
|
||||||
DECLARE_MESSAGE_MAP()
|
DECLARE_MESSAGE_MAP()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,486 @@
|
|||||||
|
// dbexport.cpp - MySQL export implementation for mw4print
|
||||||
|
//
|
||||||
|
// Included directly from ChildView.cpp (same pattern as recscore.cpp).
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "dbexport.h"
|
||||||
|
|
||||||
|
// ---- Config default values -----------------------------------------------
|
||||||
|
|
||||||
|
SDBConfig g_dbConfig = {
|
||||||
|
false, // bEnabled
|
||||||
|
"localhost", // szHost
|
||||||
|
3306, // nPort
|
||||||
|
"firestorm_stats", // szDatabase
|
||||||
|
"mw4", // szUsername
|
||||||
|
"", // szPassword
|
||||||
|
false // bExportEvents
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- MySQL late binding --------------------------------------------------
|
||||||
|
//
|
||||||
|
// We load libmysql.dll at runtime so the exe does not hard-link against it.
|
||||||
|
// All function pointers are NULL until DB_LoadMySQL() succeeds.
|
||||||
|
|
||||||
|
static HMODULE g_hMySQL = NULL;
|
||||||
|
|
||||||
|
typedef void* MYSQLH; // opaque MYSQL* handle
|
||||||
|
typedef unsigned long MYULONG;
|
||||||
|
typedef unsigned __int64 MYULLONG;
|
||||||
|
|
||||||
|
// MYSQL_OPT_CONNECT_TIMEOUT = 0
|
||||||
|
#define MY_OPT_CONNECT_TIMEOUT 0
|
||||||
|
|
||||||
|
typedef MYSQLH (__cdecl *PFN_mysql_init) (MYSQLH);
|
||||||
|
typedef MYSQLH (__cdecl *PFN_mysql_real_connect) (MYSQLH, const char*, const char*, const char*, const char*, unsigned int, const char*, unsigned long);
|
||||||
|
typedef void (__cdecl *PFN_mysql_options) (MYSQLH, int, const char*);
|
||||||
|
typedef int (__cdecl *PFN_mysql_real_query) (MYSQLH, const char*, unsigned long);
|
||||||
|
typedef MYULLONG (__cdecl *PFN_mysql_insert_id) (MYSQLH);
|
||||||
|
typedef void (__cdecl *PFN_mysql_close) (MYSQLH);
|
||||||
|
typedef const char* (__cdecl *PFN_mysql_error) (MYSQLH);
|
||||||
|
typedef MYULONG (__cdecl *PFN_mysql_escape_string) (char*, const char*, MYULONG);
|
||||||
|
|
||||||
|
static PFN_mysql_init pfn_init = NULL;
|
||||||
|
static PFN_mysql_real_connect pfn_real_connect = NULL;
|
||||||
|
static PFN_mysql_options pfn_options = NULL;
|
||||||
|
static PFN_mysql_real_query pfn_real_query = NULL;
|
||||||
|
static PFN_mysql_insert_id pfn_insert_id = NULL;
|
||||||
|
static PFN_mysql_close pfn_close = NULL;
|
||||||
|
static PFN_mysql_error pfn_error = NULL;
|
||||||
|
static PFN_mysql_escape_string pfn_escape_string = NULL;
|
||||||
|
|
||||||
|
static bool DB_LoadMySQL()
|
||||||
|
{
|
||||||
|
if (g_hMySQL)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Try app directory first, then system path.
|
||||||
|
char szPath[MAX_PATH];
|
||||||
|
sprintf(szPath, "%s\\libmysql.dll", AssetsDirectory1);
|
||||||
|
g_hMySQL = LoadLibrary(szPath);
|
||||||
|
if (!g_hMySQL)
|
||||||
|
g_hMySQL = LoadLibrary("libmysql.dll");
|
||||||
|
if (!g_hMySQL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#define GETPROC(name, pfn) pfn = (PFN_##name)GetProcAddress(g_hMySQL, #name); if (!pfn) { FreeLibrary(g_hMySQL); g_hMySQL=NULL; return false; }
|
||||||
|
GETPROC(mysql_init, pfn_init)
|
||||||
|
GETPROC(mysql_real_connect, pfn_real_connect)
|
||||||
|
GETPROC(mysql_options, pfn_options)
|
||||||
|
GETPROC(mysql_real_query, pfn_real_query)
|
||||||
|
GETPROC(mysql_insert_id, pfn_insert_id)
|
||||||
|
GETPROC(mysql_close, pfn_close)
|
||||||
|
GETPROC(mysql_error, pfn_error)
|
||||||
|
GETPROC(mysql_escape_string, pfn_escape_string)
|
||||||
|
#undef GETPROC
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Helpers -------------------------------------------------------------
|
||||||
|
|
||||||
|
// Open a connection using current config. Returns handle on success, NULL
|
||||||
|
// on failure. Caller must call pfn_close on the returned handle.
|
||||||
|
static MYSQLH DB_Connect(char* pszError, int nErrBufSize)
|
||||||
|
{
|
||||||
|
if (!DB_LoadMySQL()) {
|
||||||
|
if (pszError)
|
||||||
|
strncpy(pszError, "libmysql.dll not found. Install MySQL Connector/C and place libmysql.dll next to mw4print.exe.", nErrBufSize - 1);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MYSQLH h = pfn_init(NULL);
|
||||||
|
if (!h) {
|
||||||
|
if (pszError)
|
||||||
|
strncpy(pszError, "mysql_init failed (out of memory?)", nErrBufSize - 1);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Short connect timeout so the app doesn't hang if server is unreachable.
|
||||||
|
int nTimeout = 5;
|
||||||
|
pfn_options(h, MY_OPT_CONNECT_TIMEOUT, (const char*)&nTimeout);
|
||||||
|
|
||||||
|
MYSQLH conn = pfn_real_connect(h,
|
||||||
|
g_dbConfig.szHost,
|
||||||
|
g_dbConfig.szUsername,
|
||||||
|
g_dbConfig.szPassword,
|
||||||
|
g_dbConfig.szDatabase,
|
||||||
|
(unsigned int)g_dbConfig.nPort,
|
||||||
|
NULL, 0);
|
||||||
|
|
||||||
|
if (!conn) {
|
||||||
|
if (pszError) {
|
||||||
|
strncpy(pszError, pfn_error(h), nErrBufSize - 1);
|
||||||
|
pszError[nErrBufSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
pfn_close(h);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute a query string; return true on success.
|
||||||
|
static bool DB_Exec(MYSQLH h, const char* pszSQL)
|
||||||
|
{
|
||||||
|
return pfn_real_query(h, pszSQL, (unsigned long)strlen(pszSQL)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape a string for use inside SQL single-quoted literals.
|
||||||
|
// Writes into pszOut (must be at least 2*len+1 bytes).
|
||||||
|
static void DB_Escape(char* pszOut, const char* pszIn)
|
||||||
|
{
|
||||||
|
if (pfn_escape_string)
|
||||||
|
pfn_escape_string(pszOut, pszIn, (MYULONG)strlen(pszIn));
|
||||||
|
else {
|
||||||
|
// Fallback: manual escape of ' and backslash
|
||||||
|
char* dst = pszOut;
|
||||||
|
for (const char* src = pszIn; *src; ++src) {
|
||||||
|
if (*src == '\'' || *src == '\\')
|
||||||
|
*dst++ = '\\';
|
||||||
|
*dst++ = *src;
|
||||||
|
}
|
||||||
|
*dst = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- CREATE TABLE statements (run on first connect) ----------------------
|
||||||
|
|
||||||
|
static const char* s_szCreateMatch =
|
||||||
|
"CREATE TABLE IF NOT EXISTS `match` ("
|
||||||
|
" `match_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,"
|
||||||
|
" `match_time` DATETIME NOT NULL,"
|
||||||
|
" `server_name` VARCHAR(256) NOT NULL DEFAULT '',"
|
||||||
|
" `server_ip` VARCHAR(64) NOT NULL DEFAULT '',"
|
||||||
|
" `map_name` VARCHAR(128) NOT NULL DEFAULT '',"
|
||||||
|
" `scenario_name` VARCHAR(128) NOT NULL DEFAULT '',"
|
||||||
|
" `game_type` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `game_type_name` VARCHAR(64) NOT NULL DEFAULT '',"
|
||||||
|
" `is_team_game` TINYINT(1) NOT NULL DEFAULT 0,"
|
||||||
|
" `player_count` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `team_count` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `team_scores` VARCHAR(64) NOT NULL DEFAULT '',"
|
||||||
|
" `game_length` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `kill_limit` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `respawn_limit` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `friendly_fire` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `heat_on` TINYINT(1) NOT NULL DEFAULT 0,"
|
||||||
|
" `armor_mode` TINYINT(1) NOT NULL DEFAULT 0,"
|
||||||
|
" `unlimited_ammo` TINYINT(1) NOT NULL DEFAULT 0,"
|
||||||
|
" `is_night` TINYINT(1) NOT NULL DEFAULT 0,"
|
||||||
|
" `min_tonnage` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `max_tonnage` INT NOT NULL DEFAULT 0,"
|
||||||
|
" PRIMARY KEY (`match_id`)"
|
||||||
|
") ENGINE=InnoDB DEFAULT CHARSET=utf8";
|
||||||
|
|
||||||
|
static const char* s_szCreatePlayerResult =
|
||||||
|
"CREATE TABLE IF NOT EXISTS `player_result` ("
|
||||||
|
" `result_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,"
|
||||||
|
" `match_id` INT UNSIGNED NOT NULL,"
|
||||||
|
" `player_name` VARCHAR(64) NOT NULL DEFAULT '',"
|
||||||
|
" `mech_name` VARCHAR(64) NOT NULL DEFAULT '',"
|
||||||
|
" `team` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `score` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `kills` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `deaths` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `is_bot` TINYINT(1) NOT NULL DEFAULT 0,"
|
||||||
|
" `pilot_decal` INT NOT NULL DEFAULT 0,"
|
||||||
|
" PRIMARY KEY (`result_id`),"
|
||||||
|
" KEY `match_id` (`match_id`)"
|
||||||
|
") ENGINE=InnoDB DEFAULT CHARSET=utf8";
|
||||||
|
|
||||||
|
static const char* s_szCreatePVP =
|
||||||
|
"CREATE TABLE IF NOT EXISTS `pvp` ("
|
||||||
|
" `pvp_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,"
|
||||||
|
" `match_id` INT UNSIGNED NOT NULL,"
|
||||||
|
" `attacker` VARCHAR(64) NOT NULL DEFAULT '',"
|
||||||
|
" `victim` VARCHAR(64) NOT NULL DEFAULT '',"
|
||||||
|
" `kills` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `score` INT NOT NULL DEFAULT 0,"
|
||||||
|
" PRIMARY KEY (`pvp_id`),"
|
||||||
|
" KEY `match_id` (`match_id`)"
|
||||||
|
") ENGINE=InnoDB DEFAULT CHARSET=utf8";
|
||||||
|
|
||||||
|
static const char* s_szCreateEvent =
|
||||||
|
"CREATE TABLE IF NOT EXISTS `event` ("
|
||||||
|
" `event_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,"
|
||||||
|
" `match_id` INT UNSIGNED NOT NULL,"
|
||||||
|
" `event_type` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `actor` VARCHAR(64) NOT NULL DEFAULT '',"
|
||||||
|
" `target` VARCHAR(64) NOT NULL DEFAULT '',"
|
||||||
|
" `weapon` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `body_zone` INT NOT NULL DEFAULT 0,"
|
||||||
|
" `param` INT NOT NULL DEFAULT 0,"
|
||||||
|
" PRIMARY KEY (`event_id`),"
|
||||||
|
" KEY `match_id` (`match_id`)"
|
||||||
|
") ENGINE=InnoDB DEFAULT CHARSET=utf8";
|
||||||
|
|
||||||
|
// Map CBucketManager::Bucket_Type int to a game_type name string.
|
||||||
|
static const char* GameTypeName(int n)
|
||||||
|
{
|
||||||
|
switch (n) {
|
||||||
|
case 0: return "StockDestruction";
|
||||||
|
case 1: return "StockTeamDestruction";
|
||||||
|
case 2: return "StockAttrition";
|
||||||
|
case 3: return "StockTeamAttrition";
|
||||||
|
case 4: return "StockCaptureTheFlag";
|
||||||
|
case 5: return "StockKingOfTheHill";
|
||||||
|
case 6: return "StockTeamKingOfTheHill";
|
||||||
|
case 7: return "StockTerritories";
|
||||||
|
case 8: return "StockStealTheBacon";
|
||||||
|
case 9: return "StockCaptureBase";
|
||||||
|
case 10: return "StockDestroyObjective";
|
||||||
|
case 11: return "StockEscort";
|
||||||
|
case 12: return "StockMasterTrial";
|
||||||
|
case 13: return "StockSiegeAssault";
|
||||||
|
case 14: return "StockCampaign";
|
||||||
|
case 15: return "CustomDestruction";
|
||||||
|
case 16: return "CustomTeamDestruction";
|
||||||
|
case 17: return "CustomAttrition";
|
||||||
|
case 18: return "CustomTeamAttrition";
|
||||||
|
case 19: return "CustomCaptureTheFlag";
|
||||||
|
case 20: return "CustomKingOfTheHill";
|
||||||
|
case 21: return "CustomTeamKingOfTheHill";
|
||||||
|
case 22: return "CustomTerritories";
|
||||||
|
case 23: return "CustomStealTheBacon";
|
||||||
|
case 24: return "CustomCaptureBase";
|
||||||
|
case 25: return "CustomDestroyObjective";
|
||||||
|
case 26: return "CustomEscort";
|
||||||
|
case 27: return "CustomMasterTrial";
|
||||||
|
case 28: return "CustomSiegeAssault";
|
||||||
|
case 29: return "CustomCampaign";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve a ReplicatorID to a player name by scanning aRSOs[].
|
||||||
|
static const char* ResolvePlayerName(
|
||||||
|
const ReplicatorID& id,
|
||||||
|
PCRecScoreObject aRSOs[],
|
||||||
|
int nPlayers)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < nPlayers; i++) {
|
||||||
|
if (aRSOs[i]->m_who == id)
|
||||||
|
return aRSOs[i]->GetName();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Config I/O ----------------------------------------------------------
|
||||||
|
|
||||||
|
static void DB_GetINIPath(char* pszOut)
|
||||||
|
{
|
||||||
|
sprintf(pszOut, "%s\\mw4print.ini", AssetsDirectory1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DB_LoadConfig()
|
||||||
|
{
|
||||||
|
char szINI[MAX_PATH];
|
||||||
|
DB_GetINIPath(szINI);
|
||||||
|
|
||||||
|
const char* sec = "MySQLExport";
|
||||||
|
g_dbConfig.bEnabled = !!GetPrivateProfileInt(sec, "Enabled", 0, szINI);
|
||||||
|
GetPrivateProfileString(sec, "Host", "localhost", g_dbConfig.szHost, sizeof(g_dbConfig.szHost), szINI);
|
||||||
|
g_dbConfig.nPort = GetPrivateProfileInt(sec, "Port", 3306, szINI);
|
||||||
|
GetPrivateProfileString(sec, "Database", "firestorm_stats", g_dbConfig.szDatabase, sizeof(g_dbConfig.szDatabase), szINI);
|
||||||
|
GetPrivateProfileString(sec, "Username", "mw4", g_dbConfig.szUsername, sizeof(g_dbConfig.szUsername), szINI);
|
||||||
|
GetPrivateProfileString(sec, "Password", "", g_dbConfig.szPassword, sizeof(g_dbConfig.szPassword), szINI);
|
||||||
|
g_dbConfig.bExportEvents = !!GetPrivateProfileInt(sec, "ExportEvents", 0, szINI);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DB_SaveConfig()
|
||||||
|
{
|
||||||
|
char szINI[MAX_PATH];
|
||||||
|
DB_GetINIPath(szINI);
|
||||||
|
|
||||||
|
const char* sec = "MySQLExport";
|
||||||
|
char szBuf[32];
|
||||||
|
|
||||||
|
WritePrivateProfileString(sec, "Enabled", g_dbConfig.bEnabled ? "1" : "0", szINI);
|
||||||
|
WritePrivateProfileString(sec, "Host", g_dbConfig.szHost, szINI);
|
||||||
|
_itoa(g_dbConfig.nPort, szBuf, 10);
|
||||||
|
WritePrivateProfileString(sec, "Port", szBuf, szINI);
|
||||||
|
WritePrivateProfileString(sec, "Database", g_dbConfig.szDatabase, szINI);
|
||||||
|
WritePrivateProfileString(sec, "Username", g_dbConfig.szUsername, szINI);
|
||||||
|
WritePrivateProfileString(sec, "Password", g_dbConfig.szPassword, szINI);
|
||||||
|
WritePrivateProfileString(sec, "ExportEvents", g_dbConfig.bExportEvents ? "1" : "0", szINI);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Test connection -----------------------------------------------------
|
||||||
|
|
||||||
|
bool DB_TestConnection(char* pszError, int nErrBufSize)
|
||||||
|
{
|
||||||
|
if (pszError && nErrBufSize > 0)
|
||||||
|
pszError[0] = '\0';
|
||||||
|
|
||||||
|
MYSQLH h = DB_Connect(pszError, nErrBufSize);
|
||||||
|
if (!h)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool bOK = DB_Exec(h, "SELECT 1");
|
||||||
|
if (!bOK && pszError) {
|
||||||
|
strncpy(pszError, pfn_error(h), nErrBufSize - 1);
|
||||||
|
pszError[nErrBufSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
pfn_close(h);
|
||||||
|
return bOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Main export ---------------------------------------------------------
|
||||||
|
|
||||||
|
bool DB_ExportMatch(
|
||||||
|
const CRecScoreFull& rsf,
|
||||||
|
PCRecScoreObject aRSOs[],
|
||||||
|
int nPlayers,
|
||||||
|
const int naTeams[8],
|
||||||
|
int nTeamCount,
|
||||||
|
const MWNetMissionParameters& params)
|
||||||
|
{
|
||||||
|
char szError[512];
|
||||||
|
MYSQLH h = DB_Connect(szError, sizeof(szError));
|
||||||
|
if (!h)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
char szSQL[4096];
|
||||||
|
char szEsc[512];
|
||||||
|
bool bOK = true;
|
||||||
|
|
||||||
|
// Ensure tables exist (silent if already present).
|
||||||
|
DB_Exec(h, s_szCreateMatch);
|
||||||
|
DB_Exec(h, s_szCreatePlayerResult);
|
||||||
|
DB_Exec(h, s_szCreatePVP);
|
||||||
|
if (g_dbConfig.bExportEvents)
|
||||||
|
DB_Exec(h, s_szCreateEvent);
|
||||||
|
|
||||||
|
// ---- Insert match row ------------------------------------------------
|
||||||
|
|
||||||
|
// Build team_scores as comma-separated string e.g. "100,80,0,0,0,0,0,0"
|
||||||
|
char szTeamScores[64];
|
||||||
|
sprintf(szTeamScores, "%d,%d,%d,%d,%d,%d,%d,%d",
|
||||||
|
rsf.m_naTeamScores[0], rsf.m_naTeamScores[1],
|
||||||
|
rsf.m_naTeamScores[2], rsf.m_naTeamScores[3],
|
||||||
|
rsf.m_naTeamScores[4], rsf.m_naTeamScores[5],
|
||||||
|
rsf.m_naTeamScores[6], rsf.m_naTeamScores[7]);
|
||||||
|
|
||||||
|
char szServerNameE[512], szServerIPE[512], szMapE[256], szScenarioE[256], szGameTypeNameE[128];
|
||||||
|
DB_Escape(szServerNameE, params.m_serverName);
|
||||||
|
DB_Escape(szServerIPE, params.m_serverIP);
|
||||||
|
DB_Escape(szMapE, params.m_mapName);
|
||||||
|
DB_Escape(szScenarioE, params.m_scenarioName);
|
||||||
|
DB_Escape(szGameTypeNameE, GameTypeName(params.m_ruleType));
|
||||||
|
|
||||||
|
sprintf(szSQL,
|
||||||
|
"INSERT INTO `match` "
|
||||||
|
"(match_time, server_name, server_ip, map_name, scenario_name, "
|
||||||
|
" game_type, game_type_name, is_team_game, player_count, team_count, "
|
||||||
|
" team_scores, game_length, kill_limit, respawn_limit, friendly_fire, "
|
||||||
|
" heat_on, armor_mode, unlimited_ammo, is_night, min_tonnage, max_tonnage) "
|
||||||
|
"VALUES ("
|
||||||
|
"'%04d-%02d-%02d %02d:%02d:%02d'," // match_time
|
||||||
|
"'%s','%s','%s','%s'," // server_name, server_ip, map_name, scenario_name
|
||||||
|
"%d,'%s',%d,%d,%d," // game_type, game_type_name, is_team_game, player_count, team_count
|
||||||
|
"'%s'," // team_scores
|
||||||
|
"%d,%d,%d,%d," // game_length, kill_limit, respawn_limit, friendly_fire
|
||||||
|
"%d,%d,%d,%d,%d,%d" // heat_on, armor_mode, unlimited_ammo, is_night, min_tonnage, max_tonnage
|
||||||
|
")",
|
||||||
|
rsf.m_SysTime.wYear, rsf.m_SysTime.wMonth, rsf.m_SysTime.wDay,
|
||||||
|
rsf.m_SysTime.wHour, rsf.m_SysTime.wMinute, rsf.m_SysTime.wSecond,
|
||||||
|
szServerNameE, szServerIPE, szMapE, szScenarioE,
|
||||||
|
params.m_ruleType, szGameTypeNameE,
|
||||||
|
rsf.m_bTeamGame ? 1 : 0, nPlayers, nTeamCount,
|
||||||
|
szTeamScores,
|
||||||
|
params.m_gameLength,
|
||||||
|
params.m_killLimit ? params.m_killLimitNumber : 0,
|
||||||
|
params.m_respawnLimit ? params.m_respawnLimitNumber : 0,
|
||||||
|
params.m_friendlyFirePercentage,
|
||||||
|
params.m_heatOn ? 1 : 0,
|
||||||
|
params.m_armormodeOn ? 1 : 0,
|
||||||
|
params.m_unlimitedAmmo ? 1 : 0,
|
||||||
|
params.m_isNight ? 1 : 0,
|
||||||
|
params.m_minimumTonnage,
|
||||||
|
params.m_maximumTonnage);
|
||||||
|
|
||||||
|
bOK = DB_Exec(h, szSQL);
|
||||||
|
if (!bOK) {
|
||||||
|
pfn_close(h);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MYULLONG nMatchID = pfn_insert_id(h);
|
||||||
|
|
||||||
|
// ---- Insert player_result rows ---------------------------------------
|
||||||
|
|
||||||
|
for (int i = 0; i < nPlayers && bOK; i++) {
|
||||||
|
const CRecScoreObject& rso = *aRSOs[i];
|
||||||
|
char szNameE[128], szMechE[128];
|
||||||
|
DB_Escape(szNameE, rso.GetName());
|
||||||
|
DB_Escape(szMechE, rso.GetMech());
|
||||||
|
|
||||||
|
sprintf(szSQL,
|
||||||
|
"INSERT INTO `player_result` "
|
||||||
|
"(match_id, player_name, mech_name, team, score, kills, deaths, is_bot, pilot_decal) "
|
||||||
|
"VALUES (%I64u,'%s','%s',%d,%d,%d,%d,%d,%d)",
|
||||||
|
nMatchID,
|
||||||
|
szNameE, szMechE,
|
||||||
|
rso.m_nPilotTeam,
|
||||||
|
rso.m_nScore,
|
||||||
|
rso.m_nKills,
|
||||||
|
rso.m_nDeaths,
|
||||||
|
rso.m_bBOT ? 1 : 0,
|
||||||
|
rso.m_nPilotDecal);
|
||||||
|
|
||||||
|
bOK = DB_Exec(h, szSQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Insert pvp rows -------------------------------------------------
|
||||||
|
|
||||||
|
for (int p = 0; p < rsf.m_nPVPs && bOK; p++) {
|
||||||
|
const CRecScoreFull::SPVP_Rec& pvp = rsf.m_pPVPs[p];
|
||||||
|
const char* pszAttacker = ResolvePlayerName(pvp.m_Inf, aRSOs, nPlayers);
|
||||||
|
const char* pszVictim = ResolvePlayerName(pvp.m_Rcv, aRSOs, nPlayers);
|
||||||
|
|
||||||
|
char szAttE[128], szVicE[128];
|
||||||
|
DB_Escape(szAttE, pszAttacker);
|
||||||
|
DB_Escape(szVicE, pszVictim);
|
||||||
|
|
||||||
|
sprintf(szSQL,
|
||||||
|
"INSERT INTO `pvp` (match_id, attacker, victim, kills, score) "
|
||||||
|
"VALUES (%I64u,'%s','%s',%d,%d)",
|
||||||
|
nMatchID, szAttE, szVicE, pvp.m_nKills, pvp.m_nScore);
|
||||||
|
|
||||||
|
bOK = DB_Exec(h, szSQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Insert event rows (optional) ------------------------------------
|
||||||
|
|
||||||
|
if (bOK && g_dbConfig.bExportEvents) {
|
||||||
|
// Walk the CRecScorePack linked list.
|
||||||
|
const CRecScorePack* pPack = rsf.m_pScorePackStart;
|
||||||
|
while (pPack && bOK) {
|
||||||
|
for (int e = 0; e < pPack->m_nCount && bOK; e++) {
|
||||||
|
const SRecScore& rs = pPack->m_a[e];
|
||||||
|
const char* pszActor = ResolvePlayerName(rs.m_who, aRSOs, nPlayers);
|
||||||
|
const char* pszTarget = ResolvePlayerName(rs.m_whom, aRSOs, nPlayers);
|
||||||
|
char szActE[128], szTgtE[128];
|
||||||
|
DB_Escape(szActE, pszActor);
|
||||||
|
DB_Escape(szTgtE, pszTarget);
|
||||||
|
|
||||||
|
sprintf(szSQL,
|
||||||
|
"INSERT INTO `event` (match_id, event_type, actor, target, weapon, body_zone, param) "
|
||||||
|
"VALUES (%I64u,%d,'%s','%s',%d,%d,%d)",
|
||||||
|
nMatchID,
|
||||||
|
rs.m_nType,
|
||||||
|
szActE, szTgtE,
|
||||||
|
rs.m_nBy,
|
||||||
|
rs.m_Where.m_xWhere,
|
||||||
|
rs.m_nParam);
|
||||||
|
|
||||||
|
bOK = DB_Exec(h, szSQL);
|
||||||
|
}
|
||||||
|
pPack = pPack->m_pNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pfn_close(h);
|
||||||
|
return bOK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
// dbexport.h - MySQL export interface for mw4print
|
||||||
|
//
|
||||||
|
// Connects to an external MySQL server and writes match results after each
|
||||||
|
// print job. MySQL is loaded at runtime via late binding (libmysql.dll) so
|
||||||
|
// no MySQL SDK is required at compile time.
|
||||||
|
//
|
||||||
|
// Config is stored in mw4print.ini next to the exe:
|
||||||
|
// [MySQLExport]
|
||||||
|
// Enabled=0
|
||||||
|
// Host=192.168.1.100
|
||||||
|
// Port=3306
|
||||||
|
// Database=firestorm_stats
|
||||||
|
// Username=mw4
|
||||||
|
// Password=
|
||||||
|
// ExportEvents=0 (1 = also export per-event SRecScore rows)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __DBEXPORT_H__
|
||||||
|
#define __DBEXPORT_H__
|
||||||
|
|
||||||
|
// ---- Config struct -------------------------------------------------------
|
||||||
|
|
||||||
|
struct SDBConfig
|
||||||
|
{
|
||||||
|
bool bEnabled;
|
||||||
|
char szHost[256];
|
||||||
|
int nPort;
|
||||||
|
char szDatabase[64];
|
||||||
|
char szUsername[64];
|
||||||
|
char szPassword[64];
|
||||||
|
bool bExportEvents; // export individual SRecScore events (can be many rows)
|
||||||
|
};
|
||||||
|
|
||||||
|
extern SDBConfig g_dbConfig;
|
||||||
|
|
||||||
|
// ---- Public API ----------------------------------------------------------
|
||||||
|
|
||||||
|
// Load / save config from mw4print.ini in the app directory.
|
||||||
|
void DB_LoadConfig();
|
||||||
|
void DB_SaveConfig();
|
||||||
|
|
||||||
|
// Try to open a connection using current g_dbConfig, run a trivial query,
|
||||||
|
// then disconnect. Returns true on success; on failure fills pszError
|
||||||
|
// (up to nErrBufSize bytes) with the MySQL error string.
|
||||||
|
bool DB_TestConnection(char* pszError, int nErrBufSize);
|
||||||
|
|
||||||
|
// Export one completed match to the database. Called from
|
||||||
|
// CRecScoreFull::DoPrint() just before PrintDlg().
|
||||||
|
// Returns true on success.
|
||||||
|
bool DB_ExportMatch(
|
||||||
|
const CRecScoreFull& rsf,
|
||||||
|
PCRecScoreObject aRSOs[],
|
||||||
|
int nPlayers,
|
||||||
|
const int naTeams[8],
|
||||||
|
int nTeamCount,
|
||||||
|
const MWNetMissionParameters& params
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif // __DBEXPORT_H__
|
||||||
@@ -93,6 +93,9 @@ BEGIN
|
|||||||
MENUITEM "&Log report...\t^L", ID_FILE_LOGREPORT
|
MENUITEM "&Log report...\t^L", ID_FILE_LOGREPORT
|
||||||
MENUITEM "Log report(&Block)...\t^B", ID_FILE_LOG2DATES
|
MENUITEM "Log report(&Block)...\t^B", ID_FILE_LOG2DATES
|
||||||
MENUITEM SEPARATOR
|
MENUITEM SEPARATOR
|
||||||
|
MENUITEM "&Database Settings...\t^D", ID_FILE_DBSETTINGS
|
||||||
|
MENUITEM "Ban&ner Setting...", ID_FILE_BANNERSETTING
|
||||||
|
MENUITEM SEPARATOR
|
||||||
MENUITEM "E&xit", ID_APP_EXIT
|
MENUITEM "E&xit", ID_APP_EXIT
|
||||||
END
|
END
|
||||||
POPUP "&Help"
|
POPUP "&Help"
|
||||||
@@ -110,6 +113,7 @@ END
|
|||||||
IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE
|
IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE
|
||||||
BEGIN
|
BEGIN
|
||||||
"B", ID_FILE_LOG2DATES, VIRTKEY, CONTROL, NOINVERT
|
"B", ID_FILE_LOG2DATES, VIRTKEY, CONTROL, NOINVERT
|
||||||
|
"D", ID_FILE_DBSETTINGS, VIRTKEY, CONTROL, NOINVERT
|
||||||
"L", ID_FILE_LOGREPORT, VIRTKEY, CONTROL, NOINVERT
|
"L", ID_FILE_LOGREPORT, VIRTKEY, CONTROL, NOINVERT
|
||||||
"O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
|
"O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
|
||||||
END
|
END
|
||||||
@@ -132,6 +136,44 @@ BEGIN
|
|||||||
DEFPUSHBUTTON "OK",IDOK,178,7,50,14,WS_GROUP
|
DEFPUSHBUTTON "OK",IDOK,178,7,50,14,WS_GROUP
|
||||||
END
|
END
|
||||||
|
|
||||||
|
IDD_BANNER_SETTING DIALOG DISCARDABLE 0, 0, 300, 65
|
||||||
|
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||||
|
CAPTION "Banner Setting"
|
||||||
|
FONT 8, "MS Sans Serif"
|
||||||
|
BEGIN
|
||||||
|
LTEXT "Banner text:",IDC_STATIC,7,9,50,8
|
||||||
|
EDITTEXT IDC_BANNER_TEXT,60,7,233,14,ES_AUTOHSCROLL
|
||||||
|
DEFPUSHBUTTON "OK",IDOK,189,43,50,14
|
||||||
|
PUSHBUTTON "Cancel",IDCANCEL,244,43,50,14
|
||||||
|
LTEXT "(Printed at the bottom of every score sheet. Leave blank to hide.)",
|
||||||
|
IDC_STATIC,7,28,286,16
|
||||||
|
END
|
||||||
|
|
||||||
|
IDD_DB_SETTINGS DIALOG DISCARDABLE 0, 0, 300, 175
|
||||||
|
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||||
|
CAPTION "Database Export Settings"
|
||||||
|
FONT 8, "MS Sans Serif"
|
||||||
|
BEGIN
|
||||||
|
CONTROL "Enable MySQL export",IDC_DB_ENABLED,"Button",
|
||||||
|
BS_AUTOCHECKBOX | WS_TABSTOP,7,7,200,12
|
||||||
|
LTEXT "Host:",IDC_STATIC,7,26,30,8
|
||||||
|
EDITTEXT IDC_DB_HOST,55,24,170,14,ES_AUTOHSCROLL
|
||||||
|
LTEXT "Port:",IDC_STATIC,7,46,30,8
|
||||||
|
EDITTEXT IDC_DB_PORT,55,44,50,14,ES_AUTOHSCROLL | ES_NUMBER
|
||||||
|
LTEXT "Database:",IDC_STATIC,7,66,40,8
|
||||||
|
EDITTEXT IDC_DB_DATABASE,55,64,170,14,ES_AUTOHSCROLL
|
||||||
|
LTEXT "Username:",IDC_STATIC,7,86,40,8
|
||||||
|
EDITTEXT IDC_DB_USERNAME,55,84,170,14,ES_AUTOHSCROLL
|
||||||
|
LTEXT "Password:",IDC_STATIC,7,106,40,8
|
||||||
|
EDITTEXT IDC_DB_PASSWORD,55,104,170,14,ES_AUTOHSCROLL | ES_PASSWORD
|
||||||
|
CONTROL "Export individual events (many rows)",IDC_DB_EXPORTEVENTS,
|
||||||
|
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,123,200,12
|
||||||
|
PUSHBUTTON "Test Connection",IDC_DB_TEST,226,104,68,14
|
||||||
|
LTEXT "",IDC_DB_STATUS,7,141,286,18,SS_NOPREFIX
|
||||||
|
DEFPUSHBUTTON "OK",IDOK,189,155,50,14
|
||||||
|
PUSHBUTTON "Cancel",IDCANCEL,244,155,50,14
|
||||||
|
END
|
||||||
|
|
||||||
IDD_GET_2DATES DIALOG DISCARDABLE 0, 0, 187, 75
|
IDD_GET_2DATES DIALOG DISCARDABLE 0, 0, 187, 75
|
||||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||||
CAPTION "Date selection"
|
CAPTION "Date selection"
|
||||||
|
|||||||
@@ -268,6 +268,10 @@ static RECT s_rcTextMargin; // check!!! - margin 2 panes on bottom relative to t
|
|||||||
// so g_aFonts[FNTI_INFO_TEXT/FNTI_INFO_TEXT_BOLD].m_nTextHeight should less then s_nTextHeight
|
// so g_aFonts[FNTI_INFO_TEXT/FNTI_INFO_TEXT_BOLD].m_nTextHeight should less then s_nTextHeight
|
||||||
static int s_nTextHeight; // check!!! - text line height
|
static int s_nTextHeight; // check!!! - text line height
|
||||||
|
|
||||||
|
// Configurable banner text printed at the bottom of each sheet.
|
||||||
|
// Set via [battle tech print] BannerText= in options.ini next to the exe.
|
||||||
|
char g_szPrintBanner[256] = "Join the worlds greatest Battletech community at WWW.MECHJOCK.COM today!";
|
||||||
|
|
||||||
void _stdcall GetFileName4GUID(char szBuf[256], const char* pcszDrv, const char* pcszPath, const char* pcszExt, const GUID& guid = *(const GUID*)NULL);
|
void _stdcall GetFileName4GUID(char szBuf[256], const char* pcszDrv, const char* pcszPath, const char* pcszExt, const GUID& guid = *(const GUID*)NULL);
|
||||||
|
|
||||||
void WriteMString(MemoryStream& mf, const MString& ms)
|
void WriteMString(MemoryStream& mf, const MString& ms)
|
||||||
@@ -2602,6 +2606,15 @@ int CRecScoreFull::DoPrint() const
|
|||||||
MemoryStream stream(m_pMissionParam, m_nMissionParamSize, 0);
|
MemoryStream stream(m_pMissionParam, m_nMissionParamSize, 0);
|
||||||
params.LoadParameters(&stream);
|
params.LoadParameters(&stream);
|
||||||
|
|
||||||
|
#ifdef MW4PRINT
|
||||||
|
// ---- Database export (before printing) ----
|
||||||
|
// Export match data to the external MySQL database if configured.
|
||||||
|
if (g_dbConfig.bEnabled) {
|
||||||
|
DB_ExportMatch(*this, aRSOs, nPlayers, naTeams, nTeamCount, params);
|
||||||
|
}
|
||||||
|
// -------------------------------------------
|
||||||
|
#endif // MW4PRINT
|
||||||
|
|
||||||
static int s_nIndex = -1; // for job name - not important!!
|
static int s_nIndex = -1; // for job name - not important!!
|
||||||
if (++s_nIndex >= 10)
|
if (++s_nIndex >= 10)
|
||||||
s_nIndex = 0;
|
s_nIndex = 0;
|
||||||
@@ -2925,8 +2938,7 @@ void CRecScoreFull::DoPrint(HDC hDC, const RECT& rcDraw, const POINT& DPI, int n
|
|||||||
|
|
||||||
Rectangle(hDC, rcMH.left, rcURL.top, rcMH.right, rcURL.bottom);
|
Rectangle(hDC, rcMH.left, rcURL.top, rcMH.right, rcURL.bottom);
|
||||||
|
|
||||||
static const char s_szURL[] = "Join the worlds greatest Battletech community at WWW.MECHJOCK.COM today!";
|
DrawText(hDC, g_szPrintBanner, strlen(g_szPrintBanner), &rcURL, DT_CENTER | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
|
||||||
DrawText(hDC, s_szURL, strlen(s_szURL), &rcURL, DT_CENTER | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
|
|
||||||
|
|
||||||
SelectObject(hDC, hFontOld);
|
SelectObject(hDC, hFontOld);
|
||||||
// end URL Text
|
// end URL Text
|
||||||
@@ -3498,6 +3510,11 @@ void CRecScoreFull::LoadPrintParams() const
|
|||||||
LoadPrintParam(strOI, "TextMarginR", s_rcTextMargin.right, U_point(300), 0, U_cm(100));
|
LoadPrintParam(strOI, "TextMarginR", s_rcTextMargin.right, U_point(300), 0, U_cm(100));
|
||||||
LoadPrintParam(strOI, "TextMarginB", s_rcTextMargin.bottom, U_point(300), 0, U_cm(100));
|
LoadPrintParam(strOI, "TextMarginB", s_rcTextMargin.bottom, U_point(300), 0, U_cm(100));
|
||||||
LoadPrintParam(strOI, "TextHeight", s_nTextHeight, U_point(1100), U_point(600), U_point(1500));
|
LoadPrintParam(strOI, "TextHeight", s_nTextHeight, U_point(1100), U_point(600), U_point(1500));
|
||||||
|
|
||||||
|
// Configurable banner/URL text printed at the bottom of each sheet.
|
||||||
|
GetPrivateProfileString("battle tech print", "BannerText",
|
||||||
|
"Join the worlds greatest Battletech community at WWW.MECHJOCK.COM today!",
|
||||||
|
g_szPrintBanner, sizeof(g_szPrintBanner), strOI);
|
||||||
LoadPrintParam(strOI, "ImageTitleHeight", g_aFonts[FNTI_IMAGE_TITLE].m_nTextHeight, U_point(1500), U_point(900), U_point(2000));
|
LoadPrintParam(strOI, "ImageTitleHeight", g_aFonts[FNTI_IMAGE_TITLE].m_nTextHeight, U_point(1500), U_point(900), U_point(2000));
|
||||||
LoadPrintParam(strOI, "TableTextHeight", g_aFonts[FNTI_TABLE_TEXT].m_nTextHeight, U_point(900), U_point(600), U_point(1200));
|
LoadPrintParam(strOI, "TableTextHeight", g_aFonts[FNTI_TABLE_TEXT].m_nTextHeight, U_point(900), U_point(600), U_point(1200));
|
||||||
LoadPrintParam(strOI, "MHTextHeight", g_aFonts[FNTI_MISSION_HIGHLIGHTS].m_nTextHeight, U_point(1200), U_point(800), U_point(1600));
|
LoadPrintParam(strOI, "MHTextHeight", g_aFonts[FNTI_MISSION_HIGHLIGHTS].m_nTextHeight, U_point(1200), U_point(800), U_point(1600));
|
||||||
|
|||||||
@@ -10,15 +10,31 @@
|
|||||||
#define IDC_DNT_END 1001
|
#define IDC_DNT_END 1001
|
||||||
#define ID_FILE_LOGREPORT 32771
|
#define ID_FILE_LOGREPORT 32771
|
||||||
#define ID_FILE_LOG2DATES 32772
|
#define ID_FILE_LOG2DATES 32772
|
||||||
|
#define ID_FILE_DBSETTINGS 32773
|
||||||
|
#define ID_FILE_BANNERSETTING 32774
|
||||||
|
|
||||||
|
#define IDD_DB_SETTINGS 131
|
||||||
|
#define IDD_BANNER_SETTING 132
|
||||||
|
|
||||||
|
#define IDC_DB_ENABLED 1002
|
||||||
|
#define IDC_DB_HOST 1003
|
||||||
|
#define IDC_DB_PORT 1004
|
||||||
|
#define IDC_DB_DATABASE 1005
|
||||||
|
#define IDC_DB_USERNAME 1006
|
||||||
|
#define IDC_DB_PASSWORD 1007
|
||||||
|
#define IDC_DB_TEST 1008
|
||||||
|
#define IDC_DB_STATUS 1009
|
||||||
|
#define IDC_DB_EXPORTEVENTS 1010
|
||||||
|
#define IDC_BANNER_TEXT 1011
|
||||||
|
|
||||||
// Next default values for new objects
|
// Next default values for new objects
|
||||||
//
|
//
|
||||||
#ifdef APSTUDIO_INVOKED
|
#ifdef APSTUDIO_INVOKED
|
||||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
#define _APS_3D_CONTROLS 1
|
#define _APS_3D_CONTROLS 1
|
||||||
#define _APS_NEXT_RESOURCE_VALUE 131
|
#define _APS_NEXT_RESOURCE_VALUE 133
|
||||||
#define _APS_NEXT_COMMAND_VALUE 32772
|
#define _APS_NEXT_COMMAND_VALUE 32775
|
||||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
#define _APS_NEXT_CONTROL_VALUE 1012
|
||||||
#define _APS_NEXT_SYMED_VALUE 101
|
#define _APS_NEXT_SYMED_VALUE 101
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
BIN
Binary file not shown.
Reference in New Issue
Block a user