#if !defined(__RECSCORE_CPP__)
#define __RECSCORE_CPP__

// for printing...
#define UNIT                        1440
#define UNIT00                      (UNIT * 100)

#define LP2PP(ScrDPI, iLogPos)      (((ScrDPI) * (iLogPos) + (UNIT / 2)) / UNIT)

// check!!! - below macros are in 1/100 precesion...
// this macros used to handle variable printer resolutions
// when printing most parameters supplied in logical unit(0.3 cm, 15 point, 0.5 inch...) not in pixel coord...
#define U_mm(mm00)                  ((mm00)*UNIT/2540)                      // 0.## mm
#define U_inch(i00)                 ((i00)*UNIT/100)                        // 0.## "
#define U_point(pt00)               ((pt00)*UNIT/7200)                      // 0.## point
#define U_cm(cm00)                  ((cm00)*UNIT/254)                       // 0.## cm
//

#define MAX_SCORE_PACK 100

#define FIXED_LEFT_COLUMNS          5 // TitleRow - Pilot/Mech/Score/Kill/Deaths
#define MISSION_SUMMARY_LINES       4 // Mission & Date/Drop Time & Time/Seperater/Conditions

#define Sub_Private_Armor   1000
#define Sub_Private_Engine  1001
#define Sub_Private_Torso   1002

DWORD g_dwPR_Meta = 0x3957a768;
DWORD g_dwPR_MetaOldC = 0x3957a767;
DWORD g_dwPR_MetaOldB = 0x3957a766;
DWORD g_dwPR_MetaOldA = 0x3957a765;
extern HWND hWindow;

void __OutStringDebug(const char* pcszFormat, ...)
{
    va_list ap;

    va_start(ap, pcszFormat);
    char sz[256];

    vsprintf(sz, pcszFormat, ap);

    va_end(ap);

    ::OutputDebugString(sz);
}
//#define _DEBUG_TRACE __OutStringDebug
#define _DEBUG_TRACE 1 ? 0: __OutStringDebug

CRecScoreFull g_RSF;
extern char AssetsDirectory1[MAX_PATH];

typedef const char* PCSZ;

int __cdecl compare_RSO(const void* elem1, const void* elem2);
int __cdecl compare_PCRecInfo(const void* elem1, const void* elem2);
int __cdecl compare_PCRecInfo2(const void* elem1, const void* elem2);
void GetDividedRect(const RECT& rcFull, RECT& rcNew, int n, int nMax, bool bHorz);
int GetTextWidth(HDC hDC, const char* pcszText, int nLen = -1);
const char* GetTextWordStart(const char* pcsz);
const char* GetTextWord(const char* pcsz, const char*& pcszWordStart);
const char* GetNextLinePos(HDC hDC, const char*& pcsz, int nAlignWidth, int& nControlCode);
int CountLines(HDC hDC, const char* pcsz, int nAlignWidth, int nMaxTextLines);
bool SetupFonts(const POINT& DPI);
void CleanFonts();
const char* RuleStr(int nRuleType);
const char* FogStr(int nWeather);
void __cdecl OutString(MString& str, const char* pcszFormat, ...);
char* __cdecl DoTextFormat(char* pBuf, const char* pcszFormat, ...);
int DoTextWidths(HDC hDC, PCSZ* ppBuf, int nBuf, int* pWidths = (int*)NULL);
void AddComponentMasks(int nWhere, int& n1, int& n2, int& n3);
void AddComponentStrings(MString& str, int nBase, DWORD dwMask, int& nComps);
void AddPartStrings(MString& str, DWORD dwMask);
const char* WeaponIDStr(int nWeaponID);
const char* ArmorZoneStr(int nArmorZone);
const char* DamageTypeStr(int nDamageType);
const char* SubsystemToStr(int nSubSystem);

SRecScore s_aRSs[MAX_SCORE_PACK_PER_FRAME];
class CRecInfo : public MString
{
public:
    const SRecScore* m_pRS;
    int m_nRank;
    int m_nTime;
};
typedef CRecInfo* PRecInfo;
typedef const CRecInfo* PCRecInfo;

int g_naComps[] = {
	Sub_IFF_Jammer,
	Sub_AdvancedGyro,
    Sub_JumpJet,
    Sub_AMS,
    Sub_LAMS,
    Sub_ECM,
    Sub_HeatSink,
    Sub_Beagle,
    Sub_LightAmp,
    Sub_Private_Armor,
    Sub_Private_Engine,
    Sub_Private_Torso,
    W_SmallLaser,
    W_MediumLaser,
    W_LargeLaser,
    W_SmallPulse,
    W_MediumPulse,
    W_LargePulse,
    W_ERSmallLaser,
    W_ERMediumLaser,
    W_ERLargeLaser,
	W_SmallXPulse,
	W_MedXPulse,
	W_LargeXPulse,
    W_ClanSmallPulse,
    W_ClanMediumPulse,
    W_ClanLargePulse,
    W_ClanERSmallLaser,
    W_ClanERMediumLaser,
    W_ClanERLargeLaser,
    W_PPC,
	W_ERPPC,
    W_ClanERPPC,
    W_Flamer,
    W_ERFlamer,
    W_Bombast,
    W_SRM2,
    W_SRM4,
    W_SRM6,
    W_ClanSRM2,
    W_ClanSRM4,
    W_ClanSRM6,
    W_SSRM2,
    W_SSRM4,
    W_SSRM6,
    W_ClanSSRM2,
    W_ClanSSRM4,
    W_ClanSSRM6,
    W_MRM10,
    W_MRM20,
    W_MRM30,
    W_MRM40,
    W_SMRM10,
    W_SMRM20,
    W_SMRM30,
    W_SMRM40,
    W_ClanSMRM10,
    W_ClanSMRM20,
    W_ClanSMRM30,
    W_ClanSMRM40,
    W_LRM5,
    W_LRM10,
    W_LRM15,
    W_LRM20,
    W_ClanLRM5,
    W_ClanLRM10,
    W_ClanLRM15,
    W_ClanLRM20,
    W_Thunderbolt,
    W_NarcBeacon,
    W_ClanNarcBeacon,
    W_Flare,
    W_HighExplosive,
    W_ArtilleryStrike,
	W_AC2,
    W_AC5,
    W_AC10,
	W_AC20,
    W_LBXAC10,
    W_LBXAC20,
    W_ClanLBXAC10,
    W_ClanLBXAC20,
    W_UltraAC2,
    W_UltraAC5,
    W_UltraAC10,
    W_UltraAC20,
    W_ClanUltraAC2,
    W_ClanUltraAC5,
    W_ClanUltraAC10,
	W_ClanUltraAC20,
    W_LightGauss,
    W_Gauss,
	W_HeavyGauss,
    W_ClanGauss,
    W_MachineGun,
    W_ClanMachineGun,
    W_LongTom,
};

enum { // font index
    FNTI_IMAGE_TITLE = 0,
    FNTI_TABLE_TEXT,
    FNTI_TABLE_TEXT_BOLD,
    FNTI_MISSION_HIGHLIGHTS,
    FNTI_INFO_TEXT,
    FNTI_INFO_TEXT_BOLD,
};

struct SFontInfo
{
    BYTE m_bCharSet;
    BYTE m_bPitchAndFamily;
    const char* m_pcszFaceName;
    int m_nTextHeight;
    bool m_bBold;
    HFONT m_hFont;
public:
    HFONT CreateFont(const POINT& DPI, int nTextHeight) const;
    void DeleteFont();
} g_aFonts[] = {
    // these are font informations to be used when score-sheet printed

    // FNTI_IMAGE_TITLE - Image Title
    { ANSI_CHARSET, VARIABLE_PITCH | FF_ROMAN, "Times New Roman", 0, true }, // check!!!
    // FNTI_TABLE_TEXT - for Table text
    { ANSI_CHARSET, VARIABLE_PITCH | FF_SWISS, "Arial", 0, false }, // check!!!
    // FNTI_TABLE_TEXT_BOLD - for Table text Bold
    { ANSI_CHARSET, VARIABLE_PITCH | FF_SWISS, "Arial", 0, true }, // check!!!
    // FNTI_MISSION_HIGHLIGHTS - Mission Highlights
    { ANSI_CHARSET, VARIABLE_PITCH | FF_ROMAN, "Times New Roman", 0, true }, // check!!!
    // FNTI_INFO_TEXT - Info text - normal
    { ANSI_CHARSET, VARIABLE_PITCH | FF_SWISS, "Arial", 0, false }, // check!!!
    // FNTI_INFO_TEXT_BOLD - Info text - bold emphasis
    { ANSI_CHARSET, VARIABLE_PITCH | FF_SWISS, "Arial", 0, true }, // check!!!
    //{ ANSI_CHARSET, VARIABLE_PITCH | FF_ROMAN, "Times New Roman", ... },
    //{ ANSI_CHARSET, VARIABLE_PITCH | FF_SWISS, "Arial", ... },
    //{ ANSI_CHARSET, FIXED_PITCH | FF_MODERN, "Courier New", ... },
};

#define CHAR_BOLD               '\x01'

#define CTRL_BOLD               "\tBold"
#define CTRL_EOL                "\tEOL"
//#define CTRL_LF                 "\tLF"

const char g_szBold[] = CTRL_BOLD; // no spaces allowed in control block
const char g_szEOL[] = CTRL_EOL; // no spaces allowed in control block
//const char g_szLF[] = CTRL_LF; // no spaces allowed in control block

const char* g_paControls[] = {
    &g_szBold[0],
    &g_szEOL[0],
    //&g_szLF[0],
};

const char* g_pcszGameDataSavePath = "\\mw4files";

bool g_bPRTest = false;
const char* g_pcszGameDataExtPR = ".pr";

int CRecScoreFull::s_nLang = -1;

static int s_nCellMarginLR; // check!!! - margin per each table-cell(left & right margin)...
static int s_nCellMarginTV; // check!!! - margin per each table-cell(top & bottom margin)...
static RECT s_rcMargin4NPA; // check!!! - paper margin(left/top/right/bottom)
static int s_nCheckTableDec = U_point(50);
static int s_nGapColumn; // check!!! - gap between 2 panes on top-image or bottom-info-text
static int s_nTopImageHeight; // check!!! - top image height
static int s_nGapTopImageTable; // check!!! - gap between top-image & table
static int s_nGapFFA; // check!!! - gap between real-players & bot-players for FFA
static int s_nGapTableMH; // check!!! - gap between table & mission highlights
static int s_nMHHeight; // check!!! - mission highlights height
static int s_nGapMHText; // check!!! - gap between mission highlights & 2 panes on bottom
static RECT s_rcTextMargin; // check!!! - margin 2 panes on bottom relative to text-border
// check!!! - line height in 2 panes on bottom
// s_nTextHeight is not a real-text-height but line-height
// g_aFonts[FNTI_INFO_TEXT/FNTI_INFO_TEXT_BOLD].m_nTextHeight is used for real-text-height...
// 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

// 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 WriteMString(MemoryStream& mf, const MString& ms)
{
    const char* pcsz = ms;
    size_t t;
    if (pcsz && pcsz[0]) {
        t = strlen(pcsz);
        mf.WriteBytes(&t, sizeof(t));
        mf.WriteBytes(pcsz, t);
    } else {
        t = 0;
        mf.WriteBytes(&t, sizeof(t));
    }
}

void ReadMString(MemoryStream& mf, MString& ms)
{
    size_t t;
    mf.ReadBytes(&t, sizeof(t));
    if (t) {
        char* psz = new char [t + 1];
        mf.ReadBytes(psz, t);
        psz[t] = '\0';
        ms = psz;
        delete [] psz;
    } else {
        ms = "";
    }
}

void _stdcall GetFileName4GUID(char szBuf[256], const char* pcszDrv, const char* pcszPath, const char* pcszExt, const GUID& guid/* = *(const GUID*)NULL*/)
{
/*
// {C85AFDE6-7731-4baa-AD32-51053D9CD614}
static const GUID <<name>> = 
{ 0xc85afde6, 0x7731, 0x4baa, { 0xad, 0x32, 0x51, 0x5, 0x3d, 0x9c, 0xd6, 0x14 } };
*/
    gosASSERT(pcszPath);
    gosASSERT(pcszExt);
    const GUID* p;
    GUID guid_null = GUID_NULL;
    if (&guid)
        p = &guid;
    else
        p = &guid_null;
    sprintf(szBuf, "%s%s\\%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x%s",
        pcszDrv, pcszPath,
        p->Data1, p->Data2, p->Data3,
        p->Data4[0], p->Data4[1],
        p->Data4[2], p->Data4[3], p->Data4[4], p->Data4[5], p->Data4[6], p->Data4[7],
        pcszExt);
}

int __cdecl compare_RSO(const void* elem1, const void* elem2)
{
    PCRecScoreObject p1 = *(PCRecScoreObject*)elem1;
    PCRecScoreObject p2 = *(PCRecScoreObject*)elem2;

    if (p1 == p2) {
        return 0;
    }
    int nDif;

    const CRecScoreFull& RSF = *p1->m_pRSF;
    gosASSERT(&RSF && (&RSF == p2->m_pRSF));
    if (RSF.m_bTeamGame && (p1->m_nPilotTeam != p2->m_nPilotTeam)) {
        nDif = -(RSF.GetTeamScore(p1->m_nPilotTeam) - RSF.GetTeamScore(p2->m_nPilotTeam));
        if (nDif == 0)
            nDif = p1->m_nPilotTeam - p2->m_nPilotTeam;
    } else {
        if (!p1->m_bBOT == !p2->m_bBOT) {
            nDif = -(p1->m_nScore - p2->m_nScore);
            if (nDif == 0) {
                nDif = -(p1->m_nKills - p2->m_nKills);
                if (nDif == 0) {
                    nDif = -(p1->m_nDeaths - p2->m_nDeaths);
                    if (nDif == 0) {
                        nDif = strcmp(p1->GetName(), p2->GetName());
                    }
                }
            }
        } else {
            if (p1->m_bBOT) {
                nDif = +1;
            } else {
                nDif = -1;
            }
        }
    }

    return nDif;
}

int __cdecl compare_PCRecInfo(const void* elem1, const void* elem2)
{
    PCRecInfo p1 = *(PCRecInfo*)elem1;
    PCRecInfo p2 = *(PCRecInfo*)elem2;

    int nDif = -(p1->m_nRank - p2->m_nRank);
    if (nDif == 0) {
        nDif = p1->m_nTime - p2->m_nTime;
    }

    return nDif;
}

int __cdecl compare_PCRecInfo2(const void* elem1, const void* elem2)
{
    PCRecInfo p1 = *(PCRecInfo*)elem1;
    PCRecInfo p2 = *(PCRecInfo*)elem2;

    return p1->m_nTime - p2->m_nTime;
}

void GetDividedRect(const RECT& rcFull, RECT& rcNew, int n, int nMax, bool bHorz)
{
    gosASSERT(0 < nMax);
    gosASSERT((0 <= n) && (n < nMax));

    if (bHorz) {
        int nWidth = rcFull.right - rcFull.left;
        rcNew.left = rcFull.left + nWidth * n / nMax;
        rcNew.right = rcFull.left + nWidth * (n + 1) / nMax;
        rcNew.top = rcFull.top;
        rcNew.bottom = rcFull.bottom;
    } else {
        int nHeight = rcFull.bottom - rcFull.top;
        rcNew.left = rcFull.left;
        rcNew.right = rcFull.right;
        rcNew.top = rcFull.top + (nHeight * n / nMax);
        rcNew.bottom = rcFull.top + nHeight * (n + 1) / nMax;
    }
}

int GetTextWidth(HDC hDC, const char* pcszText, int nLen/* = -1*/)
{
    if (pcszText) {
        if (nLen < 0) {
            nLen = strlen(pcszText);
        }
        RECT rc;
        rc.left = rc.top = 0;
        rc.right = 0;
        rc.bottom = 1000;

        DrawText(hDC, pcszText, nLen, &rc, DT_CALCRECT | DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_TOP);
        //GetTextExtentPoint

        return rc.right - rc.left;
    }

    return 0;
}

const char* GetTextWordStart(const char* pcsz)
{
    char c;
    while((c = *pcsz) == ' ') {
        pcsz++;
    }
    if (*pcsz == '\0') {
        return NULL;
    }
    return pcsz;
}

const char* GetTextWord(const char* pcsz, const char*& pcszWordStart)
{
    if ((pcsz = GetTextWordStart(pcsz)) != NULL) {
        pcszWordStart = pcsz;
        char c;
        while((c = *pcsz) != ' ') {
            if (c == '\0')
                break;
            pcsz++;
        }
    }

    return pcsz;
}

const char* GetNextLinePos(HDC hDC, const char*& pcsz, int nAlignWidth, int& nControlCode)
{
    nControlCode = -1;

    const char* pcszStart = NULL;
    const char* pcszEnd = NULL;

    const char* p2 = pcsz;

    const char* pcszWordStart;
    while((p2 = GetTextWord(p2, pcszWordStart)) != NULL) {
        if (pcszWordStart[0] == '\t') {
            if (!pcszStart) {
                // 1st control code
                int nControl;
                for(nControl = 0; nControl < sizeof(g_paControls) / sizeof(g_paControls[0]); nControl++) {
                    if (memcmp(pcszWordStart, g_paControls[nControl], strlen(g_paControls[nControl])) == 0) {
                        nControlCode = nControl;
                        pcsz = p2;
                        gosASSERT(pcszWordStart);
                        return pcszWordStart;
                    }
                }
                // unknown control code
                continue;
            }
            break;
        }
        if (!pcszStart) {
            pcszStart = pcszWordStart;
        }
        RECT rc;
        rc.left = rc.top = 0;
        rc.right = 0;
        rc.bottom = 1000;

        int nTextLen = p2 - pcszStart;
        DrawText(hDC, pcszStart, nTextLen, &rc, DT_CALCRECT | DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_TOP);
        if (nAlignWidth < (rc.right - rc.left)) {
            if (!pcszEnd) {
                while(nTextLen > 1) {
                    nTextLen--;

                    rc.left = rc.top = 0;
                    rc.right = 0;
                    rc.bottom = 1000;
                    DrawText(hDC, pcszStart, nTextLen, &rc, DT_CALCRECT | DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_TOP);
                    if ((rc.right - rc.left) <= nAlignWidth) {
                        break;
                    }
                }
                pcszEnd = &pcszStart[nTextLen];
            }
            break;
        } else {
            pcszEnd = p2;
        }
    }

    if (pcszStart) {
        gosASSERT(pcszEnd);
        pcsz = pcszEnd;
        return pcszStart;
    }

    return NULL;
}

int CountLines(HDC hDC, const char* pcsz, int nAlignWidth, int nMaxTextLines)
{
    bool bBOLD = false;
    HFONT hFontOld = (HFONT)SelectObject(hDC, g_aFonts[FNTI_INFO_TEXT].m_hFont);

    const char* pStart;
    int nControlCode;

    int nLines = 0;
    while((pStart = GetNextLinePos(hDC, pcsz, nAlignWidth, nControlCode)) != NULL) {
        switch(nControlCode) {
        case 0: // bold
            if (!bBOLD) {
                SelectObject(hDC, g_aFonts[FNTI_INFO_TEXT_BOLD].m_hFont);
                bBOLD = true;
            }
            break;
        case 1: // EOL
            if (bBOLD) {
                SelectObject(hDC, g_aFonts[FNTI_INFO_TEXT].m_hFont);
                bBOLD = false;
            }
            break;
        case 2: // LF
        default: // line of text...
            if (++nLines > nMaxTextLines)
                goto out_break;
        }
    }
out_break:
    SelectObject(hDC, hFontOld);
    return nLines;
}

HFONT SFontInfo::CreateFont(const POINT& DPI, int nTextHeight) const
{
    gosASSERT(this);

    LOGFONT lf;
    memset(&lf, 0, sizeof(lf));

    lf.lfCharSet = m_bCharSet;
    lf.lfPitchAndFamily = m_bPitchAndFamily;
    strcpy(lf.lfFaceName, m_pcszFaceName);

    lf.lfHeight = -LP2PP(DPI.y, nTextHeight);
    lf.lfWeight = m_bBold ? FW_BOLD: FW_NORMAL;

    lf.lfItalic = 0;
    lf.lfUnderline = 0;
    lf.lfStrikeOut = 0;
    lf.lfOutPrecision = OUT_TT_PRECIS;
    lf.lfClipPrecision = CLIP_STROKE_PRECIS;
    lf.lfQuality = PROOF_QUALITY;

    return CreateFontIndirect(&lf);
}

void SFontInfo::DeleteFont()
{
    gosASSERT(this);

    if (m_hFont) {
        DeleteObject(m_hFont);
        m_hFont = NULL;
    }
}

bool SetupFonts(const POINT& DPI)
{
    SFontInfo* pStart = &g_aFonts[0];
    SFontInfo* pEnd = &g_aFonts[sizeof(g_aFonts) / sizeof(g_aFonts[0])];

    while(pStart < pEnd) {
        if (!pStart->m_hFont) {
            pStart->m_hFont = pStart->CreateFont(DPI, pStart->m_nTextHeight);
            if (!pStart->m_hFont) {
                CleanFonts();
                return false;
            }
        }
        pStart++;
    }

    return true;
}

void CleanFonts()
{
    SFontInfo* pStart = &g_aFonts[0];
    SFontInfo* pEnd = &g_aFonts[sizeof(g_aFonts) / sizeof(g_aFonts[0])];

    while(pStart < pEnd) {
        pStart->DeleteFont();
        pStart++;
    }
}

const char* RuleStr(int nRuleType)
{
    if (CustomDestruction <= nRuleType)
        nRuleType -= CustomDestruction;
    switch(nRuleType)
    {
    default:
    case StockDestruction:
        return "Destruction";
    case StockTeamDestruction:
        return "Team Destruction";
    case StockAttrition:
        return "Attrition";
    case StockTeamAttrition:
        return "Team Attrition";
    case StockCaptureTheFlag:
        return "Capture The Flag";
    case StockKingOfTheHill:
        return "King Of The Hill";
    case StockTeamKingOfTheHill:
        return "Team King Of The Hill";
    case StockTerritories:
        return "Territories";
    case StockStealTheBacon:
        return "Steal The Beacon";
    case StockCaptureBase:
        return "Capture Base";
    case StockDestroyObjective:
        return "Destroy Objective";
    case StockEscort:
        return "Escort";
    case StockMasterTrial:
        return "Master Trial";
    case StockSiegeAssault:
        return "Siege Assault";
    case StockCampaign:
        return "Campaign";
    }
}

const char* FogStr(int nWeather)
{
    switch(nWeather) {
    default:
        return"Good";
    case 1:
        return "Clear";
    case 2:
        return "Light Fog";
    case 3:
        return "Heavy Fog";
    case 4:
        return "Peasoup Fog";
    }
}

void __cdecl OutString(MString& str, const char* pcszFormat, ...)
{
    va_list ap;

    va_start(ap, pcszFormat);
    char sz[256];

    vsprintf(sz, pcszFormat, ap);

    va_end(ap);

    if (&str) {
        if ((const char*)str)
            str += sz;
        else
            str = sz;
    } else {
        ::OutputDebugString(sz);
    }
}

char* __cdecl DoTextFormat(char* pBuf, const char* pcszFormat, ...)
{
    va_list ap;

    va_start(ap, pcszFormat);

    vsprintf(pBuf, pcszFormat, ap);

    va_end(ap);

    return &pBuf[strlen(pBuf) + 1];
}

int DoTextWidths(HDC hDC, PCSZ* ppBuf, int nBuf, int* pWidths/* = (int*)NULL*/)
{
    int nMaxWidth = 0;

    for(int i = 0; i < nBuf; i++) {
        int nWidth = GetTextWidth(hDC, ppBuf[i]);
        if (pWidths) {
            pWidths[i] = nWidth;
        }
        if (nMaxWidth < nWidth)
            nMaxWidth = nWidth;
    }

    return nMaxWidth;
}

void AddComponentMasks(int nWhere, int& n1, int& n2, int& n3)
{
    gosASSERT(sizeof(g_naComps) / sizeof(g_naComps[0]) <= (32 * 3));
    int* p2 = &g_naComps[sizeof(g_naComps) / sizeof(g_naComps[0])];
    while(&g_naComps[0] < p2) {
        if (*--p2 == nWhere) {
            int nIndex = p2 - &g_naComps[0];
            if (nIndex < 32) {
                n1 |= (1 << nIndex);
            } else if (nIndex < 64) {
                nIndex -= 32;
                n2 |= (1 << nIndex);
            } else {
                nIndex -= 64;
                n3 |= (1 << nIndex);
            }
        }
    }
}

void AddComponentStrings(MString& str, int nBase, DWORD dwMask, int& nComps)
{
    while(dwMask) {
        if (dwMask & 1) {
            const char* pcsz = SubsystemToStr(g_naComps[nBase]);
            gosASSERT(*pcsz);
            if (nComps != 0) {
                OutString(str, ", ");
            }
            OutString(str, "%s", pcsz);
            nComps++;
        }
        dwMask >>= 1;
        nBase++;
    }
}

void AddPartStrings(MString& str, DWORD dwMask)
{
    int n = 0, nParts = 0;

    while(dwMask) {
        if (dwMask & 1) {
            const char* pcsz = ArmorZoneStr(n);
            gosASSERT(*pcsz);
            if (nParts != 0) {
                OutString(str, ", ");
            } else {
                OutString(str, " [");
            }
            OutString(str, "%s", pcsz);
            nParts++;
        }
        dwMask >>= 1;
        n++;
    }
    if (nParts) {
        OutString(str, "]");
    }
}

const char* WeaponIDStr(int nWeaponID)
{
	CRecScoreFull::CheckLanguage();
    switch(nWeaponID)
    {
//Beams
    case W_Flamer:
        return "Flamer";
    case W_Bombast:
        return "Bombast";
    case W_ClanERLargeLaser:
        return "ClanERLargeLaser";
    case W_ClanERMediumLaser:
        return "ClanERMediumLaser";
    case W_ClanERSmallLaser:
        return "ClanERSmallLaser";
    case W_ERLargeLaser:
        return "ERLargeLaser";
    case W_ERMediumLaser:
        return "ERMediumLaser";
    case W_ERSmallLaser:
        return "ERSmallLaser";
    case W_LargeLaser:
        return "LargeLaser";
    case W_MediumLaser:
        return "MediumLaser";
    case W_SmallLaser:
        return "SmallLaser";
    case W_ERPPC:
        return "ERPPC";
    case W_ClanERPPC:
        return "ClanERPPC";
    case W_PPC:
        return "PPC";
    case W_ClanLargePulse:
        return "ClanLargePulse";
    case W_ClanMediumPulse:
        return "ClanMediumPulse";
    case W_ClanSmallPulse:
        return "ClanSmallPulse";
    case W_LargePulse:
        return "LargePulse";
    case W_MediumPulse:
        return "MediumPulse";
    case W_SmallPulse:
        return "SmallPulse";
    case W_ERFlamer:
        return "ERFlamer";
    case W_LargeXPulse:
        return "LargeXPulse";
    case W_MedXPulse:
        return "MediumXPulse";
    case W_SmallXPulse:
        return "SmallXPulse";
//Missiles
    case W_LRM5:
        return "LRM5";
    case W_LRM10:
        return "LRM10";
    case W_LRM15:
        return "LRM15";
    case W_LRM20:
        return "LRM20";
    case W_ClanLRM5:
        return "ClanLRM5";
    case W_ClanLRM10:
        return "ClanLRM10";
    case W_ClanLRM15:
        return "ClanLRM15";
    case W_ClanLRM20:
        return "ClanLRM20";
    case W_MRM10:
        return "MRM10";
    case W_MRM20:
        return "MRM20";
    case W_MRM30:
        return "MRM30";
    case W_MRM40:
        return "MRM40";
	case W_SMRM10:
        return "SMRM10";
    case W_SMRM20:
        return "SMRM20";
    case W_SMRM30:
        return "SMRM30";
    case W_SMRM40:
        return "SMRM40";
    case W_ClanSMRM10:
        return "ClanSMRM10";
    case W_ClanSMRM20:
        return "ClanSMRM20";
    case W_ClanSMRM30:
        return "ClanSMRM30";
    case W_ClanSMRM40:
        return "ClanSMRM40";
	case W_SRM2:
        return "SRM2";
    case W_SRM4:
        return "SRM4";
    case W_SRM6:
        return "SRM6";
	case W_ClanSRM2:
        return "ClanSRM2";
    case W_ClanSRM4:
        return "ClanSRM4";
    case W_ClanSRM6:
        return "ClanSRM6";
    case W_ClanSSRM2:
        return "ClanSSRM2";
    case W_ClanSSRM4:
        return "ClanSSRM4";
    case W_ClanSSRM6:
        return "ClanSSRM6";
    case W_Thunderbolt:
        return "Thunderbolt";
    case W_NarcBeacon:
        return "NarcBeacon";
    case W_ClanNarcBeacon:
        return "ClanNarcBeacon";
    case W_Flare:
        return "Flare";
    case W_HighExplosive:
        return "HighExplosive";
    case W_ArtilleryStrike:
        return "ArtilleryStrike";
    case W_SSRM2:
        return "SSRM2";
    case W_SSRM4:
        return "SSRM4";
    case W_SSRM6:
        return "SSRM6";
//Projectiles
    case W_AC2:
        return "AC2";
    case W_AC5:
        return "AC5";
    case W_AC10:
        return "AC10";
    case W_AC20:
        return "AC20";
    case W_LBXAC10:
        return "LBXAC10";
    case W_LBXAC20:
        return "LBXAC20";
    case W_ClanLBXAC10:
        return "ClanLBXAC10";
    case W_ClanLBXAC20:
        return "ClanLBXAC20";
    case W_UltraAC2:
        return "UltraAC2";
    case W_UltraAC5:
        return "UltraAC5";
    case W_UltraAC10:
        return "UltraAC10";
    case W_UltraAC20:
        return "UltraAC20";
    case W_ClanUltraAC2:
        return "ClanUltraAC2";
    case W_ClanUltraAC5:
        return "ClanUltraAC5";
    case W_ClanUltraAC10:
        return "ClanUltraAC10";
    case W_ClanGauss:
        return "ClanGauss";
    case W_Gauss:
        return "Gauss";
    case W_LightGauss:
        return "LightGauss";
    case W_LongTom:
        return "LongTom";
    case W_MachineGun:
        return "MachineGun";
    case W_ClanMachineGun:
        return "ClanMachineGun";
    case W_ClanUltraAC20:
        return "ClanUltraAC20";
    case W_HeavyGauss:
        return "HeavyGauss";
    }

    return (const char*)"";
}

const char* ArmorZoneStr(int nArmorZone)
{
    //return Adept::DamageObject::ArmorZoneAsciiToText(nArmorZone);
	CRecScoreFull::CheckLanguage();
	if (CRecScoreFull::s_nLang == 0) {
		// english
		switch(nArmorZone)
		{
		case Adept::DamageObject::NullArmorZone:
			return "Null";
		case Adept::DamageObject::LeftLegArmorZone:
			return "LeftLeg";
		case Adept::DamageObject::RightLegArmorZone:
			return "RightLeg";
		case Adept::DamageObject::LeftArmArmorZone:
			return "LeftArm";
		case Adept::DamageObject::RightArmArmorZone:
			return "RightArm";
		case Adept::DamageObject::RightTorsoArmorZone:
			return "RightTorso";
		case Adept::DamageObject::LeftTorsoArmorZone:
			return "LeftTorso";
		case Adept::DamageObject::CenterTorsoArmorZone:
			return "CenterTorso";
		case Adept::DamageObject::CenterRearTorsoArmorZone:
			return "CenterRearTorso";
		case Adept::DamageObject::HeadArmorZone:
			return "Head";
		case Adept::DamageObject::SpecialArmorZone1:
			return "Special-1";
		case Adept::DamageObject::SpecialArmorZone2:
			return "Special-2";
		case Adept::DamageObject::DefaultArmorZone:
			return "Default";
		}
	} else {
		// korea
		switch(nArmorZone)
		{
		case Adept::DamageObject::NullArmorZone:
			return "()";
		case Adept::DamageObject::LeftLegArmorZone:
			return "Left Leg";
		case Adept::DamageObject::RightLegArmorZone:
			return "Right Leg";
		case Adept::DamageObject::LeftArmArmorZone:
			return "Left Arm";
		case Adept::DamageObject::RightArmArmorZone:
			return "Right Arm";
		case Adept::DamageObject::RightTorsoArmorZone:
			return "Right Torso";
		case Adept::DamageObject::LeftTorsoArmorZone:
			return "Left Torso";
		case Adept::DamageObject::CenterTorsoArmorZone:
			return "Front";
		case Adept::DamageObject::CenterRearTorsoArmorZone:
			return "Rear";
		case Adept::DamageObject::HeadArmorZone:
			return "Head";
		case Adept::DamageObject::SpecialArmorZone1:
			return "Aux Device 1";
		case Adept::DamageObject::SpecialArmorZone2:
			return "Aux Device 2";
		case Adept::DamageObject::DefaultArmorZone:
			return "Default";
		}
	}

    return (const char*)"";
}

const char* DamageTypeStr(int nDamageType)
{
    switch(nDamageType)
    {
    case BeamDamageType:
        return "Beam";
	case MissileDamageType:
        return "Missile";
	case ProjectileDamageType:
        return "Projectile";
	case SplashDamageType:
        return "Splash";
	case HeatDamageType:
        return "Heat";
	case RammingDamageFromAboveType:
        return "RDAbove";
	case RammingDamageFromBelowType:
        return "RDBelow";
	case RammingDamageFromLeftType:
        return "RDLeft";
	case RammingDamageFromRightType:
        return "RDRight";
	case RammingDamageFromAheadType:
        return "RDFront";
	case RammingDamageFromBehindType:
        return "RDBack";
	case HighExplosiveDamageType:
        return "HighExplosive";
	case LavaDamageType:
        return "Lava";
	case AmmoFireDamageType:
        return "Ammo Bay Fire";
    }

    return (const char*)"";
}

const char* SubsystemToStr(int nSubSystem)
{
    switch(nSubSystem) {
	case Sub_IFF_Jammer:
		return "IFF Jammer";
	case Sub_AdvancedGyro:
		return "Gyro";
    case Sub_JumpJet:
        return "JumpJet";
    case Sub_AMS:
        return "AMS";
    case Sub_LAMS:
        return "LAMS";
    case Sub_ECM:
        return "ECM";
    case Sub_HeatSink:
        return "HeatSink";
    case Sub_Beagle:
        return "Beagle";
    case Sub_LightAmp:
        return "LightAmp";
    case Sub_Private_Armor:
        return "Armor";
    case Sub_Private_Engine:
        return "Engine";
    case Sub_Private_Torso:
        return "Torso";
    default:
        return WeaponIDStr(nSubSystem);
    }
}

void PART_WHERE::SetWhere(int nWhere)
{
	if ((Adept::DamageObject::LeftLegArmorZone <= nWhere) && (nWhere <= Adept::DamageObject::DefaultArmorZone))
        m_nWhere = nWhere;
    else
        m_nWhere = Adept::DamageObject::DefaultArmorZone;
    m_xMasks = 0;
}

void PART_WHERE::SetMask(int nWhere)
{
	if ((Adept::DamageObject::LeftLegArmorZone <= nWhere) && (nWhere <= Adept::DamageObject::DefaultArmorZone))
    {
        m_xMasks |= (1 << nWhere);
    }
}

CRecScorePack::CRecScorePack()
{
    m_pPrev = NULL;
    m_pNext = NULL;
    m_nCount = 0;
}

CRecScorePack::~CRecScorePack()
{
}

int CRecScorePack::GetMemSize() const
{
    int nSize = sizeof(CRecScorePack);

    nSize += m_nCount * sizeof(SRecScore);

    return nSize;
}

void CRecScorePack::Save(DynamicMemoryStream& mf)
{
    mf.WriteBytes(&m_nCount, sizeof(m_nCount));
    if (m_nCount > 0)
        mf.WriteBytes(&m_a[0], m_nCount * sizeof(SRecScore));
}

void CRecScorePack::Load(MemoryStream& mf)
{
    mf.ReadBytes(&m_nCount, sizeof(m_nCount));
    if (m_nCount > 0)
        mf.ReadBytes(&m_a[0], m_nCount * sizeof(SRecScore));
}

CRecScoreObject::CRecScoreObject(CRecScoreFull* pRSF)
    : m_pRSF(pRSF), m_pPrev(NULL), m_pNext(NULL), m_bRemoved(false), m_bBOT(false), m_who(ReplicatorID::Null), m_nPilotTeam(0), m_nTeamDecal(-1), m_nPilotDecal(-1)
    //, m_strName, m_strMech
{
    m_nScore = 0;
    m_nKills = 0;
    m_nDeaths = 0;
}
#if !defined(MW4PRINT)
CRecScoreObject::CRecScoreObject(CRecScoreFull* pRSF, const Mech* pMech, const char* pcszName, const char* pcszMech, int nPilotTeam, int nTeamDecal, int nPilotDecal)
    : m_pRSF(pRSF), m_pPrev(NULL), m_pNext(NULL), m_bRemoved(false), m_bBOT(!((Mech*)pMech)->IsPlayerVehicle()), m_who(((Mech*)pMech)->GetReplicatorID()), m_strName(pcszName), m_strMech(pcszMech), m_nPilotTeam(nPilotTeam), m_nTeamDecal(nTeamDecal), m_nPilotDecal(nPilotDecal)
{
    m_nScore = 0;
    m_nKills = 0;
    m_nDeaths = 0;
}
#endif // !defined(MW4PRINT)
CRecScoreObject::~CRecScoreObject()
{
}

const char* CRecScoreObject::GetName() const
{
    if ((const char*)m_strName > 0)
        return m_strName;
    return "";
}

const char* CRecScoreObject::GetMech() const
{
    if ((const char*)m_strMech > 0)
        return m_strMech;
    return "";
}
#if !defined(MW4PRINT)
bool CRecScoreObject::CheckRemove()
{
    if (!m_bRemoved) {
        // 1st remove: player leave, player disconnected, mission termination
        m_bRemoved = true;

        if (MWMission::GetInstance()) {
            MWMission* mission = Cast_Object(MWMission*, MWMission::GetInstance());
            if (mission && mission->m_BucketManager) {
                m_nScore = mission->m_BucketManager->FindBucketValue(CBucketManager::CUSTOM, m_who);
                m_nKills = mission->m_BucketManager->FindBucketValue(CBucketManager::KILLS, m_who);
                m_nDeaths = mission->m_BucketManager->FindBucketValue(CBucketManager::DEATHS, m_who);
            }
        }
        return true;
    }
    return false;
}
#endif // !defined(MW4PRINT)
int CRecScoreObject::GetMemSize() const
{
    int nSize = sizeof(CRecScoreObject);

    nSize += m_strName.GetLength() + 1;
    nSize += m_strMech.GetLength() + 1;

    return nSize;
}

void CRecScoreObject::Save(DynamicMemoryStream& mf, DWORD dwMeta)
{
    mf.WriteBit(m_bRemoved);
    mf.WriteBit(m_bBOT);
    mf.WriteByteAlign();
    mf.WriteBytes(&m_who.connectionID, sizeof(m_who.connectionID));
    mf.WriteBytes(&m_who.localID, sizeof(m_who.localID));
    WriteMString(mf, m_strName);
    WriteMString(mf, m_strMech);
    mf.WriteBytes(&m_nPilotTeam, sizeof(m_nPilotTeam));
    if ((dwMeta != g_dwPR_MetaOldB) && (dwMeta != g_dwPR_MetaOldA)) {
        mf.WriteBytes(&m_nTeamDecal, sizeof(m_nTeamDecal));
        mf.WriteBytes(&m_nPilotDecal, sizeof(m_nPilotDecal));
    }
    mf.WriteBytes(&m_nScore, sizeof(m_nScore));
    mf.WriteBytes(&m_nKills, sizeof(m_nKills));
    mf.WriteBytes(&m_nDeaths, sizeof(m_nDeaths));
}

void CRecScoreObject::Load(MemoryStream& mf, DWORD dwMeta)
{
    mf.ReadBit(m_bRemoved);
    mf.ReadBit(m_bBOT);
    mf.ReadByteAlign();
    mf.ReadBytes(&m_who.connectionID, sizeof(m_who.connectionID));
    mf.ReadBytes(&m_who.localID, sizeof(m_who.localID));
    ReadMString(mf, m_strName);
    ReadMString(mf, m_strMech);
    mf.ReadBytes(&m_nPilotTeam, sizeof(m_nPilotTeam));
    if ((dwMeta != g_dwPR_MetaOldB) && (dwMeta != g_dwPR_MetaOldA)) {
        mf.ReadBytes(&m_nTeamDecal, sizeof(m_nTeamDecal));
        mf.ReadBytes(&m_nPilotDecal, sizeof(m_nPilotDecal));
    } else {
        m_nTeamDecal = -1;
        m_nPilotDecal = -1;
    }
    mf.ReadBytes(&m_nScore, sizeof(m_nScore));
    mf.ReadBytes(&m_nKills, sizeof(m_nKills));
    mf.ReadBytes(&m_nDeaths, sizeof(m_nDeaths));
}

CRecScoreFull::CRecScoreFull()
    : m_nState(0), m_nMissionParamSize(0), m_pMissionParam(NULL), m_bTeamGame(false), m_pScoreObjects(NULL), m_nCount(-1), m_fFrameTime(0.0f), m_pScorePackStart(NULL), m_nPVPs(0), m_pPVPs(NULL)

{
    memset(m_naTeamScores, 0, sizeof(m_naTeamScores));
	CheckLanguage();
}

CRecScoreFull::~CRecScoreFull()
{
    Reset();
}

void CRecScoreFull::CheckLanguage()
{
	if (s_nLang == -1) {
		if (GetACP() == 949)
			s_nLang = 1; // korean
		else
			s_nLang = 0; // english
	}
}

#if !defined(MW4PRINT)
MWApplication* CRecScoreFull::Check()
{
    MWApplication* app = MWApplication::GetInstance();
    if (app) {
        if (app->networkingFlag) {
            if (app->serverFlag)
                return app;
        }
    }

    return NULL;
}
#endif // !defined(MW4PRINT)
bool CRecScoreFull::IsValidData() const
{
    if (m_pMissionParam) {
        if (m_pScoreObjects) {
            if (m_pScorePackStart) {
                return true;
            }
        }
    }

    return false;
}

void CRecScoreFull::Reset()
{
    m_nState = 0;

    m_nPVPs = 0;
    if (m_pPVPs) {
        delete [] m_pPVPs;
        m_pPVPs = NULL;
    }
    while(m_pScorePackStart) {
        CRecScorePack* pNext = m_pScorePackStart->m_pNext;

        delete m_pScorePackStart;
        m_pScorePackStart = pNext;
    }
    m_nCount = -1;
    m_fFrameTime = 0.0;
    memset(m_naTeamScores, 0, sizeof(m_naTeamScores));

    while(m_pScoreObjects) {
        CRecScoreObject* pNext = m_pScoreObjects->m_pNext;

        delete m_pScoreObjects;
        m_pScoreObjects = pNext;
    }

    if (m_pMissionParam) {
        delete [] m_pMissionParam;
        m_pMissionParam = NULL;
    }
    m_nMissionParamSize = 0;
    m_bTeamGame = false;
}
#if !defined(MW4PRINT)
void CRecScoreFull::Loading()
{
    Reset();
}

void CRecScoreFull::Loaded()
{
}

void CRecScoreFull::Starting()
{
    gosASSERT(m_nState == 0);
    m_nState = 0;
    extern int g_nMR;
    extern SYSTEMTIME g_SysTime;
    if (Check() && (g_nMR != 2)) {
        m_SysTime = g_SysTime;

        if (!CTCL_IsConsole()) {
            if (CTCL_IsConsoleX()) {
                gosASSERT(g_bIsServer);
                if (g_nPrintOut)
                    m_nState = +1;
            } else {
                if (g_bPRTest) {
                    m_nState = +1;
                }
            }
        }
    }
}

void CRecScoreFull::Started()
{
    if (m_nState != +1) {
        return;
    }
    MWApplication* app = Check();
    if (app) {
        gosASSERT(m_nState == +1);

        NetMissionParameters::MWNetMissionParameters* params = app->GetLocalNetParams();

        DynamicMemoryStream stream;
        params->SaveParameters(&stream);
	    stream.WriteByteAlign();
	    stream.Rewind();

        m_pMissionParam = new BYTE[stream.GetBufferBytesUsed()];
        memcpy(m_pMissionParam, stream.GetPointer(), stream.GetBufferBytesUsed());
        m_nMissionParamSize = stream.GetBufferBytesUsed();
        m_bTeamGame = !!params->m_teamAllowed;

        m_nState = +2;
    }
}

void CRecScoreFull::StartFrame()
{
    if (m_nState == +2) {
        MWMission* mission = Cast_Object(MWMission*, MWMission::GetInstance());
        if (mission && (m_nCount == -1)) {
            m_nCount++;
            m_fFrameTime = mission->GetMissionTime();
        }
    }
}

void CRecScoreFull::StopFrame()
{
    if (m_nState == +2) {
        if (m_nCount > 0) {
            ReduceFrame();

            SRecScore* pRS;

            bool bAdded = false;
            for(int i = 0; i < m_nCount; i++) {
                const SRecScore& RS = s_aRSs[i];
                if (RS.m_nType != -1) {
                    // !deleted...
                    pRS = AddRecScore();
                    *pRS = RS;
                    bAdded = true;
                }
            }
            if (bAdded) {
                pRS = AddRecScore();
                pRS->m_nType = -1;
                pRS->m_fTime = m_fFrameTime;
            }
            Snap();
        }
        m_nCount = -1;
    }
}

void CRecScoreFull::ReduceFrame()
{
    for(int i = 0; i < m_nCount; i++) {
        SRecScore& RS = s_aRSs[i];
        if (RS.m_nType == CBucketManager::COMPONENT_DEATHS) {
            // after kill or suisides...
            RS.m_nType = - 1;
        }
    }
}

void CRecScoreFull::Stopping()
{
    if (m_nState != +2) {
        return;
    }
    CRecScoreObject* p = m_pScoreObjects;
    while(p) {
        p->CheckRemove();
        p = p->m_pNext;
    }
    StopFrame();
    if (MWMission::GetInstance()) {
        MWMission* mission = Cast_Object(MWMission*, MWMission::GetInstance());
        if (mission && mission->m_BucketManager) {
            for(int nTeam = 0; nTeam < 8; nTeam++) {
                m_naTeamScores[nTeam] = mission->m_BucketManager->FindBucketValue(CBucketManager::CUSTOM, -(nTeam + 1));;
            }
        }
        if (m_pPVPs) {
            delete [] m_pPVPs;
            m_pPVPs = NULL;
        }
        stlport::vector <CBucketManager::PVP_Rec>& aPVP_Recs = mission->m_BucketManager->m_aPVP_Recs;
        m_nPVPs = aPVP_Recs.size();
        if (0 < m_nPVPs) {
            m_pPVPs = new SPVP_Rec[m_nPVPs];
            int nCount = 0;
	        {for (stlport::vector<CBucketManager::PVP_Rec>::iterator i = aPVP_Recs.begin(); i != aPVP_Recs.end(); ++i)
	        {
                CBucketManager::PVP_Rec& pvp = (*i);
                SPVP_Rec& pvp2 = m_pPVPs[nCount];
                pvp2.m_Inf = pvp.m_Inf;
                pvp2.m_Rcv = pvp.m_Rcv;
                pvp2.m_nKills = pvp.m_nKills;
                pvp2.m_nScore = pvp.m_nScore;
                nCount++;
            }}
        }
    }

    m_nState = -1;
}

void CRecScoreFull::Stopped()
{
    if (m_nState != -1) {
        return;
    }
    m_nState = 0;
/*#if 0 // def _DEBUG
    _DEBUG_TRACE("========\n");
    CRecScoreObject* pO = m_pScoreObjects;
    while(pO) {
        _DEBUG_TRACE("name: \"%s%s\" mech: %s, team: %d, score: %d, kill: %d(%d), death: %d(%d), suicides: %d\n",
            pO->GetName(), pO->GetMech(), pO->m_bBOT ? "(BOT)": "",
            pO->m_nPilotTeam, pO->m_nScore,
            pO->m_nKills, CalcKillsAndDeaths(pO->m_who, ReplicatorID::Null),
            pO->m_nDeaths, CalcKillsAndDeaths(ReplicatorID::Null, pO->m_who),
            CalcKillsAndDeaths(pO->m_who, pO->m_who));
        CRecScoreObject* pO2 = m_pScoreObjects;
        while(pO2) {
            if (pO2 != pO) {
                _DEBUG_TRACE("  vs \"%s\" ==> Kill : %d, Death: %d\n",
                    pO2->GetName(), CalcKillsAndDeaths(pO->m_who, pO2->m_who), CalcKillsAndDeaths(pO2->m_who, pO->m_who));
            }
            pO2 = pO2->m_pNext;
        }
        pO = pO->m_pNext;
        Sleep(10);
    }
    _DEBUG_TRACE("========\n\n");

    CRecScorePack* p = m_pScorePackStart;
    if (p) {
        while(p->m_pNext) {
            p = p->m_pNext;
        }
        while(p) {
            int i, nCount = p->m_nCount;
            for(i = 0; i < nCount; i++) {
                const SRecScore& RS = p->m_a[i];
                if (RS.m_nType == -1)
                    _DEBUG_TRACE("<<EndFrame %f>>\n", RS.m_fTime);
                else
                    AddString(RS);
            }
            p = p->m_pPrev;
        }
    }
    _DEBUG_TRACE("========\n");
    int nTotals = GetTotalRECs();
    _DEBUG_TRACE("Totals: %d => %d bytes\n", nTotals, sizeof(SRecScore) * nTotals);
    _DEBUG_TRACE("========\n");
#endif // _DEBUG
*/
    if (!CTCL_IsConsole()) {
        if (CTCL_IsConsoleX()) {
            gosASSERT(g_bIsServer);
            if (g_nPrintOut)
                m_nState = +1;
        } else {
            m_nState = +1;
        }
    }
    if (IsValidData()) {
        char szFile[256];

        if (CTCL_IsConsoleX()) {
            GetFileName4GUID(szFile, "c:", g_pcszGameDataSavePath, g_pcszGameDataExtPR, g_guidGameDatas);
            if (SavePR(szFile)) {
                CTCL_SendFile(g_SysTime, g_guidGameDatas, 0);
            }
        } else {
            if (g_bPRTest) {
                GetFileName4GUID(szFile, "c:", g_pcszGameDataSavePath, g_pcszGameDataExtPR);
                SavePR(szFile);
                HWND hWndPrint = FindWindow(NULL, "mw4print");
                if (hWndPrint) {
                    MW4PRINT_COPYDATASTRUCT cd;

                    cd.dwData = MW4PRINT_COPYDATA_META;
                    cd.cbData = sizeof(cd) - sizeof(COPYDATASTRUCT);
                    cd.lpData = &cd.m_GUID;
                    cd.m_GUID = GUID_NULL;
                    strcpy(cd.m_szFile, szFile);
                    ::SendMessage(hWndPrint, WM_COPYDATA, (WPARAM)hWindow, (LPARAM)&cd);
                }
            }
        }
    }
}
#endif // !defined(MW4PRINT)

int CRecScoreFull::GetTotalRECs() const
{
    CRecScorePack* p = m_pScorePackStart;
    int nTotals = 0;
    while(p) {
        nTotals += p->m_nCount;
        p = p->m_pNext;
    }

    return nTotals;
}

int CRecScoreFull::CalcKillsAndDeaths(ReplicatorID who, ReplicatorID whom) const
{
    int nRet = 0;

    CRecScorePack* p = m_pScorePackStart;
    while(p) {
        int i, nCount = p->m_nCount;
        for(i = 0; i < nCount; i++) {
            const SRecScore& RS = p->m_a[i];
            if (RS.m_nType == CBucketManager::KILLS) {
                if (((who == ReplicatorID::Null) || (RS.m_who == who)) && ((whom == ReplicatorID::Null) || (RS.m_whom == whom))) {
                    nRet++;
                }
            } else if (RS.m_nType == CBucketManager::SUICIDES) {
                if (who == whom) {
                    if ((who == ReplicatorID::Null) || (RS.m_who == who)) {
                        nRet++;
                    }
                }
            }
        }
        p = p->m_pNext;
    }

    return nRet;
}

int CRecScoreFull::CalcKillsAndDeathsForTeam(int nTeam, ReplicatorID id, bool bKill) const
{
    // bKill ? return nTeam kill id: return id kill nTeam;
    int nRet = 0;

    CRecScoreObject* pO = m_pScoreObjects;
    while(pO) {
        if (pO->m_nPilotTeam == nTeam) {
            if (bKill) {
                nRet += CalcKillsAndDeaths(pO->m_who, id);
            } else {
                nRet += CalcKillsAndDeaths(id, pO->m_who);
            }
        }
        pO = pO->m_pNext;
    }

    return nRet;
}

int CRecScoreFull::GetTeamScore(int nTeam) const
{
    int nScore = 0;

    if (m_bTeamGame) {
#if 1
        if ((0 <= nTeam) && (nTeam < 8)) {
            nScore = m_naTeamScores[nTeam];
        }
#else
        const CRecScoreObject* pO = m_pScoreObjects;
        while(pO) {
            if (pO->m_nPilotTeam == nTeam)
                nScore += pO->m_nScore;
            pO = pO->m_pNext;
        }
#endif
    }

    return nScore;
}

int CRecScoreFull::RelativeGetScore4Player(const ReplicatorID& me, const ReplicatorID& whom, int& nKills, int& nDeaths) const
{
    nKills = 0;
    nDeaths = 0;
    int nScore = 0;

    if ((0 < m_nPVPs) && m_pPVPs) {
	    for(int i = 0; i < m_nPVPs; ++i)
	    {
            SPVP_Rec& pvp = m_pPVPs[i];
            if (pvp.m_Inf == whom) {
                if (pvp.m_Rcv == me) {
                    nKills = pvp.m_nKills;
                    nScore = pvp.m_nScore;
                }
            } else if (pvp.m_Inf == me) {
                if (pvp.m_Rcv == whom) {
                    nDeaths = pvp.m_nKills;
                }
            }
	    }
    }

	return nScore;
}

void CRecScoreFull::Snap()
{
}

CRecScoreObject& CRecScoreFull::FindReplicatorID(ReplicatorID who) const
{
    CRecScoreObject* p = m_pScoreObjects;
    while(p) {
        if (p->m_who == who)
            return *p;
        p = p->m_pNext;
    }

    return *(CRecScoreObject*)NULL;
}

#if !defined(MW4PRINT)
void CRecScoreFull::AddScoreObject(Mech* pMech, const char* pcszName, int nPilotTeam, int nTeamDecal, int nPilotDecal)
{
    gosASSERT(pMech);
    gosASSERT(pMech->GetClassData() == Mech::DefaultData);

    MWApplication* app = MWApplication::GetInstance();
    gosASSERT(app);

    const Mech::GameModel* model = pMech->GetGameModel();
	Check_Object(model);

    const char* pcszMech = app->GetLocString(model->m_nameIndex);

    CRecScoreObject* p = new CRecScoreObject(this, pMech, pcszName, pcszMech, nPilotTeam, nTeamDecal, nPilotDecal);
    p->m_pNext = m_pScoreObjects;
    if (m_pScoreObjects) {
        m_pScoreObjects->m_pPrev = p;
    }
    m_pScoreObjects = p;
}

void CRecScoreFull::RemoveScoreObject(Mech* pMech)
{
    gosASSERT(pMech);
    gosASSERT(pMech->GetClassData() == Mech::DefaultData);
    CRecScoreObject* p = m_pScoreObjects;
    while(p) {
        if (p->m_who == pMech->GetReplicatorID()) {
            if (p->CheckRemove()) {
                return;
            }
        }
        p = p->m_pNext;
    }
}
#endif // !defined(MW4PRINT)

SRecScore* CRecScoreFull::AddRecScore()
{
    if (!m_pScorePackStart || (m_pScorePackStart->m_nCount + 1 >= MAX_SCORE_PACK)) {
        CRecScorePack* p = new CRecScorePack;
        p->m_pPrev = NULL;
        p->m_pNext = m_pScorePackStart;
        if (m_pScorePackStart) {
            m_pScorePackStart->m_pPrev = p;
        }
        m_pScorePackStart = p;
    }

    return &m_pScorePackStart->m_a[m_pScorePackStart->m_nCount++];
}

#if !defined(MW4PRINT)
void CRecScoreFull::RecScore(int nType, ReplicatorID who, ReplicatorID whom/* = ReplicatorID::Null*/, int nBy/* = 0*/, int nWhere/* = 0*/, DWORD dwParam/* = 0*/)
{
    if (m_nState != +2) {
        return;
    }
    MWApplication *app = MWApplication::GetInstance();
    gosASSERT(app);
    MWMission* mission = Cast_Object(MWMission*, MWMission::GetInstance());
    gosASSERT(mission);

    CRecScoreObject* pRSO;

    if (nType != CBucketManager::COMPONENT_DEATHS)
        pRSO = &FindReplicatorID(who);
    else
        pRSO = NULL;
    CRecScoreObject& RSO1 = *pRSO;

    Subsystem* pS;
    Entity* pParent = NULL;

    if (nType == CBucketManager::COMPONENT_DEATHS) {
        pS = (Subsystem*)dwParam;
        gosASSERT(pS);
        MWObject* pPMO = pS->GetParentVehicle();
        gosASSERT(pPMO);
        pParent = pPMO;
        whom = pParent->GetReplicatorID();
        pRSO = &FindReplicatorID(whom);
    } else {
        if ((nType != CBucketManager::SUICIDES) &&
			(nType != CBucketManager::FLAGS_CAPTURED) &&
			(nType != CBucketManager::FRIENDLY_BUILDING_KILLS) &&
			(nType != CBucketManager::ENEMY_BUILDING_KILLS))
            pRSO = &FindReplicatorID(whom);
        else
            pRSO = NULL;
        pS = NULL;
    }
    CRecScoreObject& RSO2 = *pRSO;

    gosASSERT((0 <= m_nCount) && (m_nCount < (MAX_SCORE_PACK_PER_FRAME - 1)));
    bool bADD = false;
    switch(nType) {
    case CBucketManager::COMPONENT_KILLS: // attacker, me, 0, 0, num_subsystems_destroyed
        if (&RSO1 && &RSO2) {
            gosASSERT(dwParam != 0);
            int k = m_nCount;
            while(k-- > 0) {
                SRecScore& RS = s_aRSs[k];
                if (RS.m_nType == CBucketManager::COMPONENT_DEATHS) {
                    if (RS.m_whom == whom) {
                        RS.m_nType = CBucketManager::COMPONENT_KILLS;
                        RS.m_who = who;
                        break;
                    }
                }
            }
        } else {
            return;
        }
        break;
    case CBucketManager::COMPONENT_DEATHS: // ReplicatorID::Null, ReplicatorID::Null, 0, 0, (DWORD)subsystem);
      {
        const Subsystem::GameModel* pModel = pS->GetGameModel();
        nWhere = pModel->itemID;
        const char* pcszSubSystem = SubsystemToStr(pModel->itemID);
        if (*pcszSubSystem == '\0') {
            gosASSERT(pModel->itemID == -1);
            pcszSubSystem = pS->GetClassString();
            if (strcmpi(pcszSubSystem, "MechWarrior4::Armor") == 0) {
                pcszSubSystem = "Armor";
                nWhere = Sub_Private_Armor;
            } else if (strcmpi(pcszSubSystem, "Mechwarrior4::Engine") == 0) {
                pcszSubSystem = "Engine";
                nWhere = Sub_Private_Engine;
            } else if (strcmpi(pcszSubSystem, "Mechwarrior4::Torso") == 0) {
                pcszSubSystem = "Torso";
                nWhere = Sub_Private_Torso;
            }
        }
        if (&RSO2) {
            if (nWhere != -1) {
                bool bAdded = false;
                int k = m_nCount;
                while(!bAdded && (k-- > 0)) {
                    SRecScore& RS = s_aRSs[k];
                    switch(RS.m_nType) {
                    case CBucketManager::COMPONENT_DEATHS:
                    //case CBucketManager::KILLS:
                    //case CBucketManager::SUICIDES:
                        if (RS.m_whom == whom) {
                            AddComponentMasks(nWhere, RS.m_nBy, RS.m_Where.m_nWhere, RS.m_nParam);
                            bAdded = true;
                        }
                        break;
                    }
                }
                if (!bAdded) {
                    // 1st component death...
                    int nWhere2 = nWhere;
                    nBy = nWhere = 0; dwParam = 0; // for components mask
                    AddComponentMasks(nWhere2, nBy, nWhere, *(int*)&dwParam);
                    bADD = true;
                }
            }
        } else {
            return;
        }
        break;
      }
    case CBucketManager::KILLS: // inflicting_id2, victim_id
        if (&RSO1 && &RSO2) {
            gosASSERT(&RSO1 != &RSO2);
            nBy = nWhere = 0; dwParam = 0; // for components mask
            bADD = true;
        } else {
            return;
        }
        break;
    case CBucketManager::SUICIDES: // victim_id
        if (&RSO1) {
            whom = who; // for components mask
            nBy = nWhere = 0; dwParam = 0; // for components mask
            bADD = true;
        } else {
            return;
        }
        break;
    case CBucketManager::SHOTS_HIT: // message->inflictingEntityID, GetReplicatorID(), message->weaponID, part_hit->armorZone, message->typeOfDamage
        if (&RSO1 && &RSO2) {
            //gosASSERT(&RSO1 != &RSO2);
            if (dwParam == SplashDamageType) {
                int k = m_nCount;
                while(k-- > 0) {
                    SRecScore& RS = s_aRSs[k];
                    if ((RS.m_nType == CBucketManager::SHOTS_HIT) && (RS.m_who == who) && (RS.m_whom == whom) && (RS.m_nBy == nBy)) {
                        RS.m_Where.SetMask(nWhere);
                        break;
                    }
                }
                if (k < 0) { // no hitted & splash only...

                    PART_WHERE pw;

                    pw.SetWhere(Adept::DamageObject::DefaultArmorZone);
                    pw.SetMask(nWhere);
                    nWhere = pw.m_nWhere;

                    bADD = true;
                } else {
                }
            } else {
                PART_WHERE pw;

                pw.SetWhere(nWhere);
                nWhere = pw.m_nWhere;

                bADD = true;
            }
        } else {
            return;
        }
        break;
    case CBucketManager::FLAGS_CAPTURED: // Attached()->GetReplicatorID()
        if (&RSO1) {
            whom = who;
            bADD = true;
        } else {
            return;
        }
        break;
// MSL 5.05 Building Scoring
    case CBucketManager::FRIENDLY_BUILDING_KILLS: // inflicting_id2
        if (&RSO1) {
            whom = who; // for components mask
            bADD = true;
        } else {
            return;
        }
        break;
// MSL 5.05 Building Scoring
    case CBucketManager::ENEMY_BUILDING_KILLS: // inflicting_id2
        if (&RSO1) {
            whom = who; // for components mask
            bADD = true;
        } else {
            return;
        }
        break;
    default:
        gosASSERT(false);
        break;
    }
    if (bADD) {
        SRecScore& RS = s_aRSs[m_nCount];
        RS.m_nType = nType;
        RS.m_who = who;
        RS.m_whom = whom;
        RS.m_Where.m_nWhere = nWhere;
        RS.m_nBy = nBy;
        RS.m_dwParam = dwParam;

        m_nCount++;
    }
}
#endif // !defined(MW4PRINT)

int CRecScoreFull::AddString(MString& str, const SRecScore& RS, const CRecScoreObject& For) const
{
    gosASSERT(&str && &For);
struct SMSGFMT
{
public:
	const char* m_pcszMsg;
	int m_nMsgType;
};

// 0 - RSO1.GetName(),RSO1.GetMech(),RSO2.GetName(),RSO2.GetMech()
// 1 - RSO2.GetName(),RSO2.GetMech(),RSO1.GetName()
// 2 - RSO1.GetName(),RSO2.GetName(),RSO2.GetMech()
// 3 - RSO2.GetName(),RSO2.GetMech(),RSO1.GetName(),RSO1.GetMech()
// 4 - RSO1.GetName(),RSO2.GetName()
// 5 - RSO2.GetName(),RSO1.GetName(),RSO2.GetMech()
// 6 - RSO1.GetName(),RSO1.GetMech(),RSO2.GetName()
// 7 - RSO2.GetName(),RSO1.GetName()
// 8 - RSO2.GetName()
// 9 - RSO2.GetName(),RSO1.GetName(),RSO2.GetName()
//10 - RSO2.GetName(),RSO1.GetName(),RSO1.GetName()
static SMSGFMT s_aKill[20][2] = {
	{
		{ "%s's %s destroys %s's %s!", 0 }, // ok!
		{ "%s's %s destroyed %s's %s!", 0 }, // ok!
	},
	{
		{ "%s's %s explodes in a fire-ball, thanks to %s.", 1 }, // ok!
		{ "%s's %s explodes in flames! %s lands a perfect hit!", 1 }, // ok!
	},
	{
		{ "%s delivers the last blow as %s's %s explodes in flames!", 2 }, // ok!
		{ "%s took a hit to %s's %s!", 2 - 100}, // ok!
	},
	{
		{ "Smoking wreckage is all that's left of %s's %s after %s delivers the fatal blow!", 1 },
		{ "%s's merciless assault leaves only burning wreckage of %s's %s.", 2 }, // ok!
	},
	{
		{ "Glorious victory goes to %s as the wreckage from %s's %s lies burning on the battlefield!", 2 },
		{ "The battle escalates rapidly - %s's survival probability is 0%%!", 8 }, // ok!
	},
	{
		{ "%s's %s is put out of it's misery by a devastating shot from %s.", 1 },
		{ "Under %s's overwhelming firepower, %s crumbles miserably.", 4 }, // ok!
	},
	{
		{ "%s racks up another kill marker from reducing %s's %s to rubble!", 2 },
		{ "%s knocked out %s's %s and earned a new kill mark.", 2 }, // ok!
	},
	{
		{ "Fresh paint is applied to %s's %s to mark the destruction of %s's %s!", 0 }, // ok!
		{ "%s added a kill mark on %s for shooting down %s.", 0 - 100 },
	},
	{
		{ "The burning wreckage of %s's %s is a reminder to all of the prowess of %s's battle skills!", 1 },
		{ "The wreckage of %s will be %s's trophy of victory.", 7 }, // ok!
	},
	{
		{ "The thunderous explosion of %s's %s is music to the ears of %s!", 1 },
		{ "A death prelude echoes in %s's ears. The conductor: %s.", 7 }, // ok!
	},
	{
		{ "%s's %s is destroyed by %s's %s.", 3 },
		{ "%s's %s was destroyed by %s's %s.", 3 }, // ok!
	},
	{
		{ "%s brings the four horsemen of the apocalypse down upon %s.", 4 },
		{ "The pendulum of judgment swung by %s strikes %s.", 4 }, // ok!
	},
	{
		{ "%s's life flashes as %s reduces the %s to a burning wreck.", 5 },
		{ "On %s's last lifeline, %s!", 7 - 100 }, // ok!
	},
	{
		{ "%s is deafened by the resounding roar of the exploding %s, thanks to %s.", 1 },
		{ "%s's mech explodes skyward. Glory to %s!", 7 }, // ok!
	},
	{
		{ "A kill marker is placed on %s's %s to mark the victory over %s.", 6 },
		{ "A new kill mark should be added to %s's %s, victim: %s", 6 }, // ok!
	},
	{
		{ "%s is delivered unto the inferno with regards from %s.", 7 },
		{ "%s headed to hell under %s's escort. Rest in peace, %s!", 9 }, // ok!
	},
	{
		{ "Another kill marker is given up by %s, as %s celebrates victory.", 7 },
		{ "%s became %s's new kill mark. Rest in peace...", 7 - 100 },
	},
	{
		{ "Best wishes in the afterlife are presented to %s from %s.", 7 },
		{ "All that remains for %s is revenge against %s!", 7 }, // ok!
	},
	{
		{ "Tragedy strikes %s as %s guns the %s down.", 5 },
		{ "%s brought all firepower to bear and destroyed %s's %s.", 2 }, // ok!
	},
	{
		{ "%s earns the revenge of %s after the tragic destruction of the %s.", 2 },
		{ "A tragic end for %s! %s can only watch %s in despair.", 9 }, // ok!
	},
};

// 0 - RSO1.GetName(),RSO1.GetMech()
// 1 - RSO1.GetMech(),RSO1.GetName()
// 2 - RSO1.GetName(),RSO1.GetName()
// 3 - RSO1.GetName()
static SMSGFMT s_aSuicide[2][2] = {
	{
        { "%s leaves the battlefield in disgrace after causing the destruction of the %s.", 0 },
        { "%s self-destructed! %s left the battlefield in disgrace!", 2 },
	},
	{
        { "%s will not grace the halls of the honored mechwarriors after self-destructing the %s.", 0 },
        { "%s self-destructed! Coward, do you truly desire victory?", 3 },
	},
};

//pcszArmorZone = ArmorZoneStr(RS.m_Where.m_xWhere)
//pcszWeapon = WeaponIDStr(RS.m_nBy), should not be NULL
// 0 - RSO1.GetName(),RSO1.GetMech(),pcszWeapon,pcszArmorZone,RSO2.GetName(),RSO2.GetMech()
// 1 - RSO1.GetName(),RSO2.GetName(),RSO2.GetMech(),pcszWeapon,RSO2.GetName(),RSO2.GetMech()
// 2 - RSO1.GetName(),RSO1.GetMech(),pcszWeapon,RSO2.GetName(),RSO2.GetMech(),pcszArmorZone
// 3 - RSO2.GetName(),RSO2.GetMech(),pcszArmorZone,RSO1.GetName()
// 4 - RSO2.GetName(),RSO2.GetMech(),RSO1.GetName(),pcszArmorZone
// 5 - RSO2.GetName(),RSO2.GetMech(),pcszArmorZone,RSO1.GetName(),pcszWeapon
// 6 - RSO2.GetName(),RSO2.GetMech(),RSO1.GetName(),pcszWeapon,pcszArmorZone
// 7 - RSO1.GetName(),RSO2.GetName(),pcszArmorZone
// 8 - RSO2.GetName(),pcszArmorZone,RSO1.GetName()
// 9 - pcszArmorZone,RSO2.GetName(),RSO2.GetMech(),RSO1.GetName(),pcszWeapon
//10 - RSO1.GetName(),pcszWeapon,RSO2.GetName(),RSO2.GetMech(),pcszArmorZone
//11 - RSO2.GetName(),RSO2.GetMech(),pcszWeapon,RSO1.GetName()
//12 - RSO1.GetName(),pcszWeapon,RSO2.GetName(),RSO2.GetMech()
//13 - RSO2.GetName(),pcszArmorZone,RSO1.GetName(),RSO2.GetMech()
//14 - RSO1.GetName(),RSO2.GetMech(),pcszArmorZone,RSO2.GetName(),pcszArmorZone
//15 - RSO1.GetName(),pcszWeapon,RSO2.GetName()
static SMSGFMT s_aShot[10][2] = {
	{
        { "%s's %s fires the %s and damages the %s of %s's %s.", 0 },
		{ "%s attacked %s's %s with %s", 1 }, // ok!
	},
	{
        { "%s fires the %s's %s and decimates the %s of %s's %s.", 2 },
        { "%s piloting %s damaged %s's %s on %s's %s.", 2 },
	},
	{
        { "%s's %s takes a devastating hit in the %s from %s.", 3 },
        { "%s's %s took damage to the %s under %s's overwhelming attack.", 4 }, // ok!
	},
	{
        { "%s's %s suffers a direct hit to it's %s from %s's %s.", 5 },
        { "%s's %s took direct damage from %s's %s. Damage: %s!", 6 -100},
	},
	{
        { "%s damages %s's %s.", 7 },
        { "%s dealt damage to %s's %s..", 7 }, // ok!
	},
	{
        { "Dense black smoke pours from %s's %s as a result of %s's targeted hit.", 8 },
        { "Black smoke billows from %s's %s from %s's precise attack.", 7 }, // ok!
	},
	{
        { "Fire boils from the damaged %s of %s's %s after %s hits it with a %s.", 9 },
        { "%s fired %s, dealing damage to the %s of %s's %s.", 10 }, // ok!
	},
	{
        { "Smoke and fire are trailing from %s's %s, all results of the %s blast inflicted by %s.", 11 },
        { "Fire and smoke engulf %s's mech from %s's %s attack.", 15 }, // ok!
	},
	{
        { "%s targets and fires the %s; %s cries in dismay as the %s's %s is damaged.", 10 },
        { "%s aimed %s and pulled the trigger, hitting the %s of %s's %s.", 10 }, // ok!
	},
	{
        { "%s takes a severe wound to the %s as %s aims for the %s again.", 13 },
        { "%s aimed again at %s's %s and fired. %s seems to have taken serious damage to %s.", 14 -100},
	},
};

// 0 - RSO1.GetName()
static SMSGFMT s_aCTF[1][2] = {
	{
        { "%s captured flag.", 0 },
        { "%s captured the flag!", 0 },
	},
};
static SMSGFMT s_aFBK[1][2] = {
	{
        { "%s destroyed friendly building.", 0 },
        { "%s captured the flag!", 0 },
	},
};
static SMSGFMT s_aEBK[1][2] = {
	{
        { "%s destroyed enemy building.", 0 },
        { "%s captured the flag!", 0 },
	},
};
	const SMSGFMT* pcFMT;

    // check!!! big-number represent important-information, 0 r minus value means "Do not print this event!!!"
    // see below "nRank = ..."
    int nRank = -1;

    CRecScoreObject& RSO1 = FindReplicatorID(RS.m_who);
    CRecScoreObject& RSO2 = FindReplicatorID(RS.m_whom);
    gosASSERT(&RSO1 && &RSO2);

    switch(RS.m_nType) {
    case CBucketManager::COMPONENT_KILLS: // attacker, me, 3 MASKS
        if (&RSO1 == &RSO2) {
            if (&RSO1 == &For) {
                return -1;
                //OutString(str, "Your component(s) destroyed."); // check!!!
                //nRank = 10000;
            } else {
                return -1;
                //OutString(str, "%s's component(s) destroyed.", RSO1.GetName());
            }
        } else {
            if (&RSO1 == &For) {
                return -1;
                //OutString(str, "You destroyed %s's component(s).", RSO2.GetName()); // check!!!
                //nRank = 15000;
            } else if (&RSO2 == &For) {
                return -1;
                //OutString(str, "%s destroyed your component(s).", RSO1.GetName()); // check!!!
                //nRank = 14000;
            } else {
                return -1;
                //OutString(str, "%s destroyed %s's component(s).", RSO1.GetName(), RSO2.GetName());
            }
        }
        /*
        // check!!! - below lines will add destroyed components(weapons/engine/torso...)
        {
        OutString(str, " [");
        int nComps = 0;
        AddComponentStrings(str, 0, *(DWORD*)&RS.m_nBy, nComps);
        AddComponentStrings(str, 32, *(DWORD*)&RS.m_Where.m_nWhere, nComps);
        AddComponentStrings(str, 64, *(DWORD*)&RS.m_nParam, nComps);
        OutString(str, "]");
        }
        */
        break;
    case CBucketManager::KILLS: // inflicting_id2, victim_id
        //OutString(str, "%s destroyed %s's %s.", RSO1.GetName(), RSO2.GetName(), RSO2.GetMech()); // check!!!
        //{case} third-person(==RSO1) kills third-person(==RSO2)
		do {
			pcFMT = &s_aKill[rand() % 20][s_nLang];
		} while(pcFMT->m_nMsgType < 0);
        switch(pcFMT->m_nMsgType) {
        case 0:
            OutString(str, pcFMT->m_pcszMsg, RSO1.GetName(),RSO1.GetMech(),RSO2.GetName(),RSO2.GetMech());
            break;
        case 1:
            OutString(str, pcFMT->m_pcszMsg, RSO2.GetName(),RSO2.GetMech(),RSO1.GetName());
            break;
        case 2:
            OutString(str, pcFMT->m_pcszMsg, RSO1.GetName(),RSO2.GetName(),RSO2.GetMech());
            break;
        case 3:
            OutString(str, pcFMT->m_pcszMsg, RSO2.GetName(),RSO2.GetMech(),RSO1.GetName(),RSO1.GetMech());
            break;
        case 4:
            OutString(str, pcFMT->m_pcszMsg, RSO1.GetName(),RSO2.GetName());
            break;
        case 5:
            OutString(str, pcFMT->m_pcszMsg, RSO2.GetName(),RSO1.GetName(),RSO2.GetMech());
            break;
        case 6:
            OutString(str, pcFMT->m_pcszMsg, RSO1.GetName(),RSO1.GetMech(),RSO2.GetName());
            break;
        case 7:
            OutString(str, pcFMT->m_pcszMsg, RSO2.GetName(),RSO1.GetName());
            break;
		case 8:
            OutString(str, pcFMT->m_pcszMsg, RSO2.GetName());
            break;
		default: // case 9:
			OutString(str, pcFMT->m_pcszMsg, RSO2.GetName(),RSO1.GetName(),RSO2.GetName());
            break;
        }
        if (&RSO1 == &For) {
            nRank = 25000;
        } else if (&RSO2 == &For) {
            nRank = 24000;
        } else {
            nRank = 6000;
        }
        break;
    case CBucketManager::SUICIDES: // victim_id
        //OutString(str, "%s ejected due to Pilot Error.", RSO1.GetName()); // check!!!
        //{case}Third person(==RSO1) suisides himself
		do {
			pcFMT = &s_aSuicide[rand() % 2][s_nLang];
		} while(pcFMT->m_nMsgType < 0);
        switch(pcFMT->m_nMsgType) {
        case 0:
            OutString(str, pcFMT->m_pcszMsg, RSO1.GetName(),RSO1.GetMech());
            break;
        case 1:
            OutString(str, pcFMT->m_pcszMsg, RSO1.GetMech(),RSO1.GetName());
            break;
		case 2:
            OutString(str, pcFMT->m_pcszMsg, RSO1.GetName(),RSO1.GetName());
            break;
		default: // case 3:
            OutString(str, pcFMT->m_pcszMsg, RSO1.GetName());
            break;
        }
        if (&RSO1 == &For) {
            nRank = 23000;
        } else {
            nRank = 5000; // 1000;
        }
        break;
    case CBucketManager::SHOTS_HIT: // message->inflictingEntityID, GetReplicatorID(), message->weaponID, part_hit->armorZone, message->typeOfDamage
      {
        const char* pcszWeapon = WeaponIDStr(RS.m_nBy);
        const char* pcszDamage = DamageTypeStr(RS.m_nParam);
			if (&RSO1 == &RSO2)
			{
            // shot oneself - heat/splash...
				if (&RSO1 == &For)
				{
					if (*pcszWeapon)
					{
                    return -1;
                    //OutString(str, "You splashed your %s with a %s.", ArmorZoneStr(RS.m_Where.m_xWhere), pcszWeapon);
					}
					else
					{
                    return -1;
                    //OutString(str, "You damaged your %s via %s.", ArmorZoneStr(RS.m_Where.m_xWhere), pcszDamage);
                }
				}
				else
				{
					if (*pcszWeapon)
					{
                    return -1;
                    //OutString(str, "%s splashed %s's %s with a %s.", RSO1.GetName(), RSO1.GetName(), ArmorZoneStr(RS.m_Where.m_xWhere), pcszWeapon);
					}
					else
					{
                    return -1;
                    //OutString(str, "%s damaged %s's %s via %s.", RSO1.GetName(), RSO1.GetName(), ArmorZoneStr(RS.m_Where.m_xWhere), pcszDamage);
                }
            }
			}
			else
			{
				if (*pcszWeapon)
				{
                //OutString(str, "%s shot %s's %s with a %s.", RSO1.GetName(), RSO2.GetName(), ArmorZoneStr(RS.m_Where.m_xWhere), pcszWeapon);
				const char* pcszArmorZone = ArmorZoneStr(RS.m_Where.m_xWhere);
					do
					{
					pcFMT = &s_aShot[rand() % 10][s_nLang];
					}
					while(pcFMT->m_nMsgType < 0);
					switch(pcFMT->m_nMsgType)
					{
				case 0:
					OutString(str, pcFMT->m_pcszMsg, RSO1.GetName(),RSO1.GetMech(),pcszWeapon,pcszArmorZone,RSO2.GetName(),RSO2.GetMech());
					break;
				case 1:
					OutString(str, pcFMT->m_pcszMsg, RSO1.GetName(),RSO2.GetName(),RSO2.GetMech(),pcszWeapon,RSO2.GetName(),RSO2.GetMech());
					break;
				case 2:
					OutString(str, pcFMT->m_pcszMsg, RSO1.GetName(),RSO1.GetMech(),pcszWeapon,pcszArmorZone,RSO2.GetName(),RSO2.GetMech());
					break;
				case 3:
					OutString(str, pcFMT->m_pcszMsg, RSO2.GetName(),RSO2.GetMech(),pcszArmorZone,RSO1.GetName());
					break;
				case 4:
					OutString(str, pcFMT->m_pcszMsg, RSO2.GetName(),RSO2.GetMech(),RSO1.GetName(),pcszArmorZone);
					break;
				case 5:
					OutString(str, pcFMT->m_pcszMsg, RSO2.GetName(),RSO2.GetMech(),pcszArmorZone,RSO1.GetName(),pcszWeapon);
					break;
				case 6:
					OutString(str, pcFMT->m_pcszMsg, RSO2.GetName(),RSO2.GetMech(),RSO1.GetName(),pcszWeapon,pcszArmorZone);
					break;
				case 7:
					OutString(str, pcFMT->m_pcszMsg, RSO1.GetName(),RSO2.GetName(),pcszArmorZone);
					break;
				case 8:
					OutString(str, pcFMT->m_pcszMsg, RSO2.GetName(),pcszArmorZone,RSO1.GetName());
					break;
				case 9:
					OutString(str, pcFMT->m_pcszMsg, pcszArmorZone,RSO2.GetName(),RSO2.GetMech(),RSO1.GetName(),pcszWeapon);
					break;
				case 10:
					OutString(str, pcFMT->m_pcszMsg, RSO1.GetName(),pcszWeapon,RSO2.GetName(),RSO2.GetMech(),pcszArmorZone);
					break;
				case 11:
					OutString(str, pcFMT->m_pcszMsg, RSO2.GetName(),RSO2.GetMech(),pcszWeapon,RSO1.GetName());
					break;
				case 12:
					OutString(str, pcFMT->m_pcszMsg, RSO1.GetName(),pcszWeapon,RSO2.GetName(),RSO2.GetMech());
					break;
				case 13:
					OutString(str, pcFMT->m_pcszMsg, RSO2.GetName(),pcszArmorZone,RSO1.GetName(),RSO2.GetMech());
					break;
				case 14:
					OutString(str, pcFMT->m_pcszMsg, RSO1.GetName(),RSO2.GetMech(),pcszArmorZone,RSO2.GetName(),pcszArmorZone);
					break;
				default: // case 15:
					OutString(str, pcFMT->m_pcszMsg, RSO1.GetName(),pcszWeapon,RSO2.GetName());
					break;
				}
					if (&RSO1 == &For)
					{
                    nRank = 8000;
					}
					else if (&RSO2 == &For)
					{
                    nRank = 7000;
					}
					else
					{
                    nRank = 4000;
                }
				}
				else
				{
                return -1;
                //OutString(str, "%s damaged %s's %s via %s.", RSO1.GetName(), RSO2.GetName(), ArmorZoneStr(RS.m_Where.m_xWhere), pcszDamage);
            }
        }
        }
        break;
    case CBucketManager::FLAGS_CAPTURED: // Attached()->GetReplicatorID()
		do
		{
			pcFMT = &s_aCTF[0][s_nLang];
		}
		while(pcFMT->m_nMsgType < 0);
        switch(pcFMT->m_nMsgType)
		{
        default: // case 0:
            OutString(str, pcFMT->m_pcszMsg, RSO1.GetName());
            break;
        }
        if (&RSO1 == &For)
		{
            nRank = 25000;
        }
		else
		{
            nRank = 24000;
        }
        break;
    case CBucketManager::FRIENDLY_BUILDING_KILLS: // Attached()->GetReplicatorID()
		do
		{
			pcFMT = &s_aFBK[0][s_nLang];
		}
		while(pcFMT->m_nMsgType < 0);
        switch(pcFMT->m_nMsgType)
		{
			default: // case 0:
				OutString(str, pcFMT->m_pcszMsg, RSO1.GetName());
				break;
        }
        if (&RSO1 == &For)
		{
            nRank = 25000;
        }
		else
		{
            nRank = 24000;
        }
        break;
    case CBucketManager::ENEMY_BUILDING_KILLS: // Attached()->GetReplicatorID()
		do
		{
			pcFMT = &s_aEBK[0][s_nLang];
		}
		while(pcFMT->m_nMsgType < 0);
        switch(pcFMT->m_nMsgType)
		{
			default: // case 0:
				OutString(str, pcFMT->m_pcszMsg, RSO1.GetName());
				break;
        }
        if (&RSO1 == &For)
		{
            nRank = 25000;
        }
		else
		{
            nRank = 24000;
        }
        break;
    }

    return nRank;
}

MString* CRecScoreFull::MakeInfoTable(PCRecScoreObject aRSOs[], int nPlayers, const int naTeams[8], int nTeamCount) const
{
    int nTableCols = FIXED_LEFT_COLUMNS + nPlayers;
    int nTableRows = 1/*Title*/ + nPlayers + nTeamCount;

    MString* pInfoTable = new MString[nTableCols * nTableRows];

    pInfoTable[0] = "Pilot:";
    pInfoTable[1] = "'Mech:";
    pInfoTable[2] = "Score:";
    pInfoTable[3] = "Kills:";
    pInfoTable[4] = "Deaths:";

    char szBuf[64];
    int nLastTeam = -1;

    int x, y;
    MString* pRows = &pInfoTable[nTableCols];
    for(y = 0; y < nPlayers; y++) {
        const CRecScoreObject& RSORow = *aRSOs[y];
        if ((nTeamCount > 0) && (RSORow.m_nPilotTeam != nLastTeam)) {
            sprintf(szBuf, "%cTeam %d", CHAR_BOLD, RSORow.m_nPilotTeam + 1);
            pRows[0] = szBuf;
            sprintf(szBuf, "%d", GetTeamScore(RSORow.m_nPilotTeam));
            pRows[2] = szBuf; // "####";
            sprintf(szBuf, "%d", CalcKillsAndDeathsForTeam(RSORow.m_nPilotTeam, ReplicatorID::Null, true));
            pRows[3] = szBuf;
            sprintf(szBuf, "%d", CalcKillsAndDeathsForTeam(RSORow.m_nPilotTeam, ReplicatorID::Null, false));
            pRows[4] = szBuf;

            pRows = &pRows[FIXED_LEFT_COLUMNS];

            for(x = 0; x < nPlayers; x++) {
                const CRecScoreObject& RSOCol = *aRSOs[x];
                sprintf(szBuf, "%d", CalcKillsAndDeathsForTeam(RSORow.m_nPilotTeam, RSOCol.m_who, true));
                pRows[x] = szBuf;
            }

            pRows = &pRows[nPlayers];
            nLastTeam = RSORow.m_nPilotTeam;
        }

        const char* pcszName = RSORow.GetName();

        pInfoTable[FIXED_LEFT_COLUMNS + y] = pcszName;

        pRows[0] = pcszName;
        pRows[1] = RSORow.GetMech();
        sprintf(szBuf, "%d", RSORow.m_nScore);
        pRows[2] = szBuf;
        sprintf(szBuf, "%d", RSORow.m_nKills);
        pRows[3] = szBuf;
        sprintf(szBuf, "%d", RSORow.m_nDeaths);
        pRows[4] = szBuf;

        pRows = &pRows[FIXED_LEFT_COLUMNS];

        for(x = 0; x < nPlayers; x++) {
            const CRecScoreObject& RSOCol = *aRSOs[x];
            int nKills = CalcKillsAndDeaths(RSORow.m_who, RSOCol.m_who);
            int nKillsR, nDeathsR;
            int nScoreR = RelativeGetScore4Player(RSOCol.m_who, RSORow.m_who, nKillsR, nDeathsR);
            sprintf(szBuf, "%d / %d", nKills, nScoreR);
            pRows[x] = szBuf;
        }

        pRows = &pRows[nPlayers];
    }

    return pInfoTable;
}

int CRecScoreFull::DoPrint() const
{
    int nErr = 0;
    int i, nPlayers = 0;

    LoadPrintParams();

    PCRecScoreObject aRSOs[64];

    PCRecScoreObject pO = m_pScoreObjects;
    while(pO) {
        aRSOs[nPlayers] = pO;
        nPlayers++;
        pO = pO->m_pNext;
    }
    if (nPlayers > 0) {
        qsort(&aRSOs[0], nPlayers, sizeof(PCRecScoreObject), compare_RSO);
        int nTotals = GetTotalRECs();

        PRINTDLG pd;

        memset(&pd, 0, sizeof(pd));
        pd.lStructSize = sizeof(pd);
        pd.hwndOwner = hWindow;
        pd.Flags = PD_RETURNDEFAULT | PD_RETURNDC;
        pd.nCopies = 1;

        if (PrintDlg(&pd)) {
            gosASSERT(pd.hDC != NULL);

            int naTeams[8];
            int nTeamCount = 0;

            for(i = 0; i < 8; i++) {
                naTeams[i] = 0;
            }
            if (m_bTeamGame) {
                for(i = 0; i < nPlayers; i++) {
                    int nTeam = aRSOs[i]->m_nPilotTeam;
                    gosASSERT((0 <= nTeam) && (nTeam < 8));
                    if (naTeams[nTeam]++ == 0) {
                        nTeamCount++;
                    }
                }
            } else {
                nTeamCount = 0;
            }

            MString* pInfoTable = MakeInfoTable(aRSOs, nPlayers, naTeams, nTeamCount);

            NetMissionParameters::MWNetMissionParameters params;

            MemoryStream stream(m_pMissionParam, m_nMissionParamSize, 0);
            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!!
            if (++s_nIndex >= 10)
                s_nIndex = 0;

            char sz[64/*32bytes used*/];
            sprintf(sz, "%02d/%02d/%d-%02d:%02d:%02d-%d",
                m_SysTime.wMonth, m_SysTime.wDay, m_SysTime.wYear,
                m_SysTime.wHour, m_SysTime.wMinute, m_SysTime.wSecond, s_nIndex);

		    DOCINFO docInfo;
		    memset(&docInfo, 0, sizeof(DOCINFO));
		    docInfo.cbSize = sizeof(DOCINFO);
            docInfo.lpszDocName = &sz[0];
	        //docInfo.lpszOutput = NULL; // "default port";
            docInfo.fwType = DI_ROPS_READ_DESTINATION;

            POINT DPI;
            DPI.x = GetDeviceCaps(pd.hDC, LOGPIXELSX);
            DPI.y = GetDeviceCaps(pd.hDC, LOGPIXELSY);
            POINT PrintOffset;
		    Escape(pd.hDC, GETPRINTINGOFFSET, 0, NULL, (LPVOID)&PrintOffset);

            RECT rcDraw;

            // compute non-printable area
            rcDraw.left = -PrintOffset.x;
            rcDraw.top = -PrintOffset.y;
            rcDraw.right = GetDeviceCaps(pd.hDC, HORZRES) + PrintOffset.x;
            rcDraw.bottom  = GetDeviceCaps(pd.hDC, VERTRES) + PrintOffset.y;

            rcDraw.left += LP2PP(DPI.x, s_rcMargin4NPA.left);
            rcDraw.top += LP2PP(DPI.y, s_rcMargin4NPA.top);
            rcDraw.right -= LP2PP(DPI.x, s_rcMargin4NPA.right);
            rcDraw.bottom -= LP2PP(DPI.y, s_rcMargin4NPA.bottom);
			InflateRect(&rcDraw, -3, -3);

            if (SetupFonts(DPI)) {
                if (StartDoc(pd.hDC, &docInfo) > 0) {
                    int nLastTeam = -1;
                    int nRow = 1;
                    bool b1stPage = true;
                    int naColWidths[64];
                    int nCellTextHeight = 0;

                    for(i = 0; (nErr == 0) && (i < nPlayers); i++) {
                        const CRecScoreObject& RSORow = *aRSOs[i];
                        if ((nTeamCount > 0) && (RSORow.m_nPilotTeam != nLastTeam)) {
                            nRow++;
                            nLastTeam = RSORow.m_nPilotTeam;
                        }
                        if (g_bPRTest || !RSORow.m_bBOT) {
                            if (StartPage(pd.hDC) > 0) {

                                HBRUSH hBrushOld = (HBRUSH)SelectObject(pd.hDC, GetStockObject(NULL_BRUSH));
                                int nBkModeOld = SetBkMode(pd.hDC, TRANSPARENT);

                                if (b1stPage) {
                                    SFontInfo& FI = g_aFonts[FNTI_TABLE_TEXT];
                                    SFontInfo& FI_Bold = g_aFonts[FNTI_TABLE_TEXT_BOLD];
                                    int nTextHeight = FI.m_nTextHeight;

                                    while(!CheckTableFont(pd.hDC, rcDraw, DPI, pInfoTable, nPlayers, nTeamCount, naColWidths, nCellTextHeight)) {
                                        nTextHeight -= s_nCheckTableDec; // check!!! - decriment 1.0 point...
                                        if (nTextHeight < U_point(500))
                                            break;
                                        FI.DeleteFont();
                                        FI.m_hFont = FI.CreateFont(DPI, nTextHeight);
                                        FI_Bold.DeleteFont();
                                        FI_Bold.m_hFont = FI_Bold.CreateFont(DPI, nTextHeight);
                                    }
                                    b1stPage = false;
                                }

                                DoPrint(pd.hDC, rcDraw, DPI, i, pInfoTable, naColWidths, nCellTextHeight, nRow, aRSOs, nPlayers, naTeams, nTeamCount, nTotals, &params);

                                SetBkMode(pd.hDC, nBkModeOld);
                                SelectObject(pd.hDC, hBrushOld);

                                if (EndPage(pd.hDC) <= 0) {
                                    nErr = -4;
                                }
                            }
                        }
                        nRow++;
                    }
                    if (nErr != 0) {
                        AbortDoc(pd.hDC);
                    } else {
                        EndDoc(pd.hDC);
                    }
                } else {
                    nErr = -3;
                }
                CleanFonts();
            } else {
                nErr = -2;
            }
            delete [] pInfoTable;

            DeleteDC(pd.hDC);
            pd.hDC = NULL;
        } else {
            nErr = -1;
        }
    }

    return nErr;
}

bool CRecScoreFull::CheckTableFont(HDC hDC, const RECT& rcDraw, const POINT& DPI, const MString* pInfoTable, int nPlayers, int nTeamCount, int naColWidths[], int& nCellTextHeight) const
{
    int nTableCols = FIXED_LEFT_COLUMNS + nPlayers;
    int nTableRows = 1/*Title*/ + nPlayers + nTeamCount;

    int nTotalWidth = 0;
    HFONT hFontOld = (HFONT)SelectObject(hDC, g_aFonts[FNTI_TABLE_TEXT].m_hFont);
    for(int x = 0; x < nTableCols; x++) {
        int nMaxWidth = 0;
        for(int y = 0; y < nTableRows; y++) {
            const char* pcsz = pInfoTable[y * nTableCols + x];
            if (pcsz) {
                int nWidth;
                if (pcsz[0] == CHAR_BOLD) {
                    pcsz++;
                    SelectObject(hDC, g_aFonts[FNTI_TABLE_TEXT_BOLD].m_hFont);
                    nWidth = GetTextWidth(hDC, pcsz);
                    SelectObject(hDC, g_aFonts[FNTI_TABLE_TEXT].m_hFont);
                } else {
                    nWidth = GetTextWidth(hDC, pcsz);
                }
                if (nMaxWidth < nWidth)
                    nMaxWidth = nWidth;
            }
        }
        naColWidths[x] = nMaxWidth;

        nTotalWidth += nMaxWidth;
    }
    static const char s_sz[] = "~!@#$%^&*()_+|-=\\[];',./{}:\"<>?01234567890LMOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz";
    SIZE sz;
    GetTextExtentPoint(hDC, s_sz, strlen(s_sz), &sz);
    SelectObject(hDC, hFontOld);

    int nCellMargin = LP2PP(DPI.x, s_nCellMarginLR);
    nTotalWidth += nCellMargin * 2 * nTableCols;

    nCellTextHeight = (int)sz.cy;
    if (nTotalWidth <= rcDraw.right - rcDraw.left) {
        return true;
    }

    return false;
}

void CRecScoreFull::DrawTable(HDC hDC, const MString* pInfoTable, const int naX[], const int naY[], const int nTableCols, const int nTableRows, const int nInvCol, const int nInvRow) const
{
    HFONT hFontOld = (HFONT)SelectObject(hDC, g_aFonts[FNTI_TABLE_TEXT].m_hFont);

    int x, y;

    const MString* pInfoText = &pInfoTable[0];
    for(y = 0; y < nTableRows; y++) {
        for(x = 0; x < nTableCols; x++) {
            RECT rc;

            // vertical
            rc.left = naX[x] + 1;
            rc.top = naY[y] + 1;
            rc.right = naX[x + 1];
            rc.bottom = naY[y + 1];

            // Cell Backgound - CROSS
            if ((y == nInvRow) || (x == nInvCol)) {
                FillRect(hDC, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
            }
            const char* pcszText = *pInfoText;
            if (pcszText && *pcszText) {
                if (*pcszText == CHAR_BOLD) {
                    pcszText++;
                    SelectObject(hDC, g_aFonts[FNTI_TABLE_TEXT_BOLD].m_hFont);
                    DrawText(hDC, pcszText, strlen(pcszText), &rc, DT_CENTER | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
                    SelectObject(hDC, g_aFonts[FNTI_TABLE_TEXT].m_hFont);
                } else {
                    DrawText(hDC, pcszText, strlen(pcszText), &rc, DT_CENTER | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
                }
            }

            pInfoText++;
        }
    }

    for(y = 0; y <= nTableRows; y++) {
        int nY = naY[y];
        MoveToEx(hDC, naX[0], nY, NULL);
        LineTo(hDC, naX[nTableCols] + 1, nY);
    }
    for(x = 0; x <= nTableCols; x++) {
        int nX = naX[x];
        MoveToEx(hDC, nX, naY[0], NULL);
        LineTo(hDC, nX, naY[nTableRows] + 1);
    }

    SelectObject(hDC, hFontOld);
}

void CRecScoreFull::DoPrint(HDC hDC, const RECT& rcDraw, const POINT& DPI, int nCurPlayer, const MString* pInfoTable, const int naColWidths[], int nCellTextHeight, int nRow, PCRecScoreObject aRSOs[], int nPlayers, const int naTeams[8], int nTeamCount, int nTotals, const void* pParams) const
{
    gosASSERT((0 <= nCurPlayer) && (nCurPlayer < nPlayers));

    int nTableCols = FIXED_LEFT_COLUMNS + nPlayers;
    int nTableRows = 1/*Title*/ + nPlayers + nTeamCount;

    const CRecScoreObject& RSO = *aRSOs[nCurPlayer];
    const NetMissionParameters::MWNetMissionParameters& params = *(const NetMissionParameters::MWNetMissionParameters*)pParams;

    int i, x, y;

    static char szBuf[1024];
    static char* paBuf[(16 + 5) * 16];

    int nMaxWidth;

    // calculate section rects...
    int nDrawWitdth = rcDraw.right - rcDraw.left;
    int nGapColumn = LP2PP(DPI.x, s_nGapColumn);

    RECT rcImage(rcDraw);
    rcImage.bottom = rcImage.top + LP2PP(DPI.y, s_nTopImageHeight);

    RECT rcImgLeft(rcImage), rcImgRight(rcImage);

    rcImgLeft.right = ((rcImage.left + rcImage.right) - nGapColumn) / 2;
    rcImgRight.left = rcImgLeft.right + nGapColumn;

    RECT rcTable(rcDraw);
    rcTable.top = rcImage.bottom + LP2PP(DPI.y, s_nGapTopImageTable);

    int nCellHeight = LP2PP(DPI.y, s_nCellMarginTV) * 2;
    nCellHeight += nCellTextHeight;

    int nTableHeight = nTableRows * nCellHeight;

    int nRealPlayers = 0;
    for(i = 0; i < nPlayers; i++) {
        if (!aRSOs[i]->m_bBOT)
            nRealPlayers++;
    }

    int nGapFFA = 0;
    if (nTeamCount == 0) {
        // is free for all game?
        if (nRealPlayers != nPlayers) {
            nGapFFA = LP2PP(DPI.y, s_nGapFFA);
        }
    }

    rcTable.bottom = rcTable.top + nTableHeight + nGapFFA;

    RECT rcURL(rcDraw);
    rcURL.top = rcTable.bottom + LP2PP(DPI.y, s_nGapTableMH);

    rcURL.bottom = rcURL.top + LP2PP(DPI.y, s_nMHHeight);

    RECT rcMH(rcDraw);
    rcMH.top = rcURL.bottom + LP2PP(DPI.y, s_nGapTableMH);

    rcMH.bottom = rcMH.top + LP2PP(DPI.y, s_nMHHeight);

    RECT rcText(rcDraw);
    rcText.top = rcMH.bottom + LP2PP(DPI.y, s_nGapMHText);

    RECT rcTextMargin;
    rcTextMargin.left = LP2PP(DPI.x, s_rcTextMargin.left);
    rcTextMargin.right = LP2PP(DPI.x, s_rcTextMargin.right);
    rcTextMargin.top = LP2PP(DPI.x, s_rcTextMargin.top);
    rcTextMargin.bottom = LP2PP(DPI.x, s_rcTextMargin.bottom);

    rcText.top += rcTextMargin.top;
    rcText.bottom -= rcTextMargin.bottom;

    int nTextHeight = LP2PP(DPI.y, s_nTextHeight);

    int nTextLines = (rcText.bottom - rcText.top) / nTextHeight;
    rcText.bottom = rcText.top + nTextLines * nTextHeight;

    RECT rcLeft(rcText), rcRight(rcText);

    rcLeft.right = ((rcText.left + rcText.right) - nGapColumn) / 2;
    rcRight.left = rcLeft.right + nGapColumn;

    rcLeft.left += rcTextMargin.left;
    rcLeft.right -= rcTextMargin.right;

    rcRight.left += rcTextMargin.left;
    rcRight.right -= rcTextMargin.right;

    // start of drawing...
    HFONT hFontOld;

    HANDLE hBitmap;
    // start top-left image...
    sprintf(szBuf, "%s\\hsh\\mechs\\%s.bmp", AssetsDirectory1, RSO.GetMech());
    hBitmap = LoadImage(NULL, szBuf, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
    if (hBitmap) {
        BITMAP bm;
        if (GetObject(hBitmap, sizeof(bm), &bm)) {
            HDC hDC2 = CreateCompatibleDC(hDC);
            if (hDC2) {
                HANDLE hBitmapOld = SelectObject(hDC2, hBitmap);
                StretchBlt(hDC, rcImgLeft.left, rcImgLeft.top, rcImgLeft.right - rcImgLeft.left, rcImgLeft.bottom - rcImgLeft.top,
                    hDC2, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
                SelectObject(hDC2, hBitmapOld);
                DeleteDC(hDC2);
            }
        }
        DeleteObject(hBitmap);
    }

    // start URL Text
    hFontOld = (HFONT)SelectObject(hDC, g_aFonts[FNTI_MISSION_HIGHLIGHTS].m_hFont);

    Rectangle(hDC, rcMH.left, rcURL.top, rcMH.right, rcURL.bottom);

    DrawText(hDC, g_szPrintBanner, strlen(g_szPrintBanner), &rcURL, DT_CENTER | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);

    SelectObject(hDC, hFontOld);
	// end URL Text

    Rectangle(hDC, rcImgLeft.left, rcImgLeft.top, rcImgLeft.right, rcImgLeft.bottom);

    // start top-right game info
    hFontOld = (HFONT)SelectObject(hDC, g_aFonts[FNTI_IMAGE_TITLE].m_hFont);

	int nImgRight = rcImgRight.right;
    int nDecal = m_bTeamGame ? RSO.m_nTeamDecal: RSO.m_nPilotDecal;
    if ((0 <= nDecal) && (nDecal < 64)) {
        sprintf(szBuf, "%s\\hsh\\decals\\decal_%02d.bmp", AssetsDirectory1, nDecal);
        hBitmap = LoadImage(NULL, szBuf, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
        if (hBitmap) {
            BITMAP bm;
            if (GetObject(hBitmap, sizeof(bm), &bm)) {
                HDC hDC2 = CreateCompatibleDC(hDC);
                if (hDC2) {
                    RECT rcTemp(rcImgRight);

                    rcTemp.left = rcTemp.right - (rcImgRight.bottom - rcImgRight.top);

                    HANDLE hBitmapOld = SelectObject(hDC2, hBitmap);
                    StretchBlt(hDC, rcTemp.left, rcTemp.top, rcTemp.right - rcTemp.left, rcTemp.bottom - rcTemp.top,
                        hDC2, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
                    SelectObject(hDC2, hBitmapOld);
                    DeleteDC(hDC2);

                    nImgRight = rcTemp.left;
                }
            }
            DeleteObject(hBitmap);
        }
    }

    Rectangle(hDC, rcImgRight.left, rcImgRight.top, rcImgRight.right, rcImgRight.bottom);
	rcImgRight.right = nImgRight;

    paBuf[0] = &szBuf[0];
    paBuf[1] = DoTextFormat(paBuf[0], "MechWarrior: %s", RSO.GetName());
    paBuf[2] = DoTextFormat(paBuf[1], "Score: %d", RSO.m_nScore);
    paBuf[3] = DoTextFormat(paBuf[2], "Kills: %d", RSO.m_nKills);
    //paBuf[4] = DoTextFormat(paBuf[3], "Deaths: %d", RSO.m_nDeaths);

    nMaxWidth = DoTextWidths(hDC, (PCSZ*)&paBuf[0], 3);
    x = ((rcImgRight.right - rcImgRight.left) - nMaxWidth) / 2;
    for(i = 0; i < 3; i++) {
        RECT rc;
        GetDividedRect(rcImgRight, rc, i, 3, false);
        rc.left += x;
        DrawText(hDC, paBuf[i], strlen(paBuf[i]), &rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
    }
    SelectObject(hDC, hFontOld);
    // end top-right

    // table grid...
    int nTotalWidth = 0;
    int naX[64];
    for(x = 0; x < nTableCols; x++) {
        naX[x] = naColWidths[x];
        nTotalWidth += naColWidths[x];
    }
    if (nTotalWidth != nDrawWitdth) {
        int nGap1 = nDrawWitdth - nTotalWidth;
        int nIncDec;
        if (0 < nGap1) {
            nIncDec = +1;
        } else {
            nGap1 = -nGap1;
            nIncDec = -1;
        }
        int nGap2 = nGap1 % nTableCols;

        nGap1 -=  nGap2;
        nGap1 /= nTableCols;
        nGap1 *= nIncDec;
        for(x = 0; x < nTableCols; x++) {
            naX[x] += nGap1;
            if (nGap2 > 0) {
                naX[x] += nIncDec;
                nGap2--;
            }
        }
        int nPosX = rcTable.left;
        for(x = 0; x < nTableCols; x++) {
            int nW = naX[x];
            naX[x] = nPosX;

            nPosX += nW;
        }
        naX[x] = nPosX;
    }

    int naY[64];

    if (nGapFFA) {
        for(y = 0; y <= (nTableRows + 1); y++) {
            if (1 + nRealPlayers < y) {
                naY[y] = rcTable.top + (y - 1) * nCellHeight;
                naY[y] += nGapFFA;
            } else {
                naY[y] = rcTable.top + y * nCellHeight;
            }
        }
    } else {
        for(y = 0; y <= nTableRows; y++) {
            naY[y] = rcTable.top + y * nCellHeight;
        }
    }
    if (nGapFFA) {
        int nAbove = 1 + nRealPlayers;
        int nBelow = nTableRows - nAbove;
        DrawTable(hDC, pInfoTable, naX, naY, nTableCols, nAbove, FIXED_LEFT_COLUMNS + nCurPlayer, nRow);
        DrawTable(hDC, &pInfoTable[nAbove * nTableCols], naX, &naY[nAbove + 1], nTableCols, nBelow, FIXED_LEFT_COLUMNS + nCurPlayer, nRow - nAbove);
    } else {
        DrawTable(hDC, pInfoTable, naX, naY, nTableCols, nTableRows, FIXED_LEFT_COLUMNS + nCurPlayer, nRow);
    }

    // start Mission highlight
    hFontOld = (HFONT)SelectObject(hDC, g_aFonts[FNTI_MISSION_HIGHLIGHTS].m_hFont);

    Rectangle(hDC, rcMH.left, rcMH.top, rcMH.right, rcMH.bottom);

    static const char s_szMH[] = "Mission Highlights";
    DrawText(hDC, s_szMH, strlen(s_szMH), &rcMH, DT_CENTER | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);

    SelectObject(hDC, hFontOld);
    // end Mission highlight

    // before Draw Info Text
    CRecInfo* pRIs = new CRecInfo[nTotals + 1];
    CRecScorePack* p = m_pScorePackStart;
    int nNewTotals = 0;
    if (p) {
        while(p->m_pNext) {
            p = p->m_pNext;
        }
        int nOldFrame = 0;
        while(p) {
            int i, nCount = p->m_nCount;
            for(i = 0; i < nCount; i++) {
                const SRecScore& RS = p->m_a[i];
                if (RS.m_nType == -1) {
                    int nTime = (int)(RS.m_fTime * 1000.0);
                    while(nOldFrame < nNewTotals) {
                        pRIs[nOldFrame].m_nTime = nTime;
                        nOldFrame++;
                    }
                } else {
                    CRecInfo& RI = pRIs[nNewTotals];
                    RI.m_pRS = &RS;
                    int nRank = AddString(RI, RS, RSO);
                    if (nRank > 0) {
                        if ((const char*)RI) {
                            RI.m_nRank = nRank + rand() % 300;
                            nNewTotals++;
                        }
                    }
                }
            }
            p = p->m_pPrev;
        }
    }

    int nTextLinesAll2 = nTextLines * 2;
    int nTextLines1 = nTextLines - MISSION_SUMMARY_LINES;
    int nTextLines2 = nTextLines;
    int nTextLinesAll = nTextLines1 + nTextLines2;

    PRecInfo* ppRIs = new PRecInfo[nNewTotals + 1];
    for(i = 0; i < nNewTotals; i++) {
        ppRIs[i] = &pRIs[i];
    }
    MString strInfo;
    if (nNewTotals > 0) {
        bool bDone = false;
        bool bOver = false;
        int nUnder = 0, nOver = nNewTotals;
        int nTest = nUnder + (nOver - nUnder + 1) / 2;
        while(true) {
            qsort((void*)ppRIs, nNewTotals, sizeof(PRecInfo), compare_PCRecInfo);
            qsort((void*)ppRIs, nTest, sizeof(PRecInfo), compare_PCRecInfo2);

            int nTimeLast = -1;
            strInfo = "";
            for(i = 0; i < nTest; i++) {
                PCRecInfo pRI = ppRIs[i];
                int nType = pRI->m_pRS->m_nType;
                bool bKillAndDeath;
                if ((nType == CBucketManager::KILLS)
					|| (nType == CBucketManager::SUICIDES)
					|| (nType == CBucketManager::ENEMY_BUILDING_KILLS)
					|| (nType == CBucketManager::FRIENDLY_BUILDING_KILLS)) {
                    bKillAndDeath = true;
                    strInfo += " " CTRL_BOLD " ";
                } else {
                    bKillAndDeath = false;
                }
                if (bKillAndDeath || (nTimeLast < 0) || ((nTimeLast + 3000) <= pRI->m_nTime)) {
                    nTimeLast = pRI->m_nTime;
                    int nSec = (nTimeLast + 500) / 1000;
                    char sz[20];
                    sprintf(sz, "   (%02d:%02d) ", nSec / 60, nSec % 60); // check!!! - adding events to be printed & Time-Marker too...
                    strInfo += sz;
                } else {
                    strInfo += " ";
                }
                strInfo += *pRI;
                if (bKillAndDeath) {
                    strInfo += " " CTRL_EOL " ";
                }
            }
            if (bDone)
                break;
            int nLines = CountLines(hDC, strInfo, rcLeft.right - rcLeft.left, nTextLinesAll);
            if (nTextLinesAll < nLines) {
                // overflow
                nOver = nTest;
                bOver = true;
            } else if (nLines < nTextLinesAll) {
                // underflow
                nUnder = nTest;
            } else {
                // fitted...
                break;
            }
            int nTestNew = nUnder + (nOver - nUnder + 1) / 2;
            if (nTestNew == nTest) {
                if (!bOver) {
                    nTest = nOver;
                    bOver = true;
                } else {
                    if (nTextLinesAll < nLines) {
                        if (nUnder == nTest)
                            break;
                        nTest = nUnder;
                        bDone = true;
                    } else {
                        break;
                    }
                }
            } else {
                nTest = nTestNew;
            }
        }
    } else {
        strInfo = "";
    }

    const char* pcsz = strInfo;
    const char* pStart;

    hFontOld = (HFONT)SelectObject(hDC, g_aFonts[FNTI_INFO_TEXT].m_hFont);
    int nLines = -1;
    while(++nLines < MISSION_SUMMARY_LINES) {
        RECT rc;

        rc.left = rcLeft.left;
        rc.right = rcLeft.right;
        rc.top = rcLeft.top + nLines * nTextHeight;
        rc.bottom = rc.top + nTextHeight;

        RECT rcL(rc), rcR(rc);
        rcL.right = (rc.left + rc.right) / 2;
        rcR.left = rcL.right;

        //sprintf(szBuf, "Rule: %s", RuleStr(params.m_ruleType));
        switch(nLines)
        {
        case 0:
            sprintf(szBuf, "Mission: %s", params.m_mapName);
            DrawText(hDC, szBuf, strlen(szBuf), &rcL, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
            sprintf(szBuf, "Date: %02d/%02d/%d", m_SysTime.wMonth, m_SysTime.wDay, m_SysTime.wYear);
            DrawText(hDC, szBuf, strlen(szBuf), &rcR, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
            break;
        case 1:
            sprintf(szBuf, "Drop Time: %s", params.m_isNight ? "Night": "Day");
            DrawText(hDC, szBuf, strlen(szBuf), &rcL, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
            sprintf(szBuf, "Time: %02d:%02d", m_SysTime.wHour, m_SysTime.wMinute);
            DrawText(hDC, szBuf, strlen(szBuf), &rcR, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
            break;
        case 2:
            sprintf(szBuf, "Visibility: %s%s", FogStr(params.m_visibility), params.m_weather ? "/Weather.": ".");
            DrawText(hDC, szBuf, strlen(szBuf), &rcL, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
			sprintf(szBuf, "Game: %s", RuleStr(params.m_ruleType));
            DrawText(hDC, szBuf, strlen(szBuf), &rcR, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
        			break;
        case 3:
            MoveToEx(hDC, rc.left, (rc.top + rc.bottom) / 2 - 2, NULL);
            LineTo(hDC, rc.right, (rc.top + rc.bottom) / 2 - 2);
            MoveToEx(hDC, rc.left, (rc.top + rc.bottom) / 2 + 2, NULL);
            LineTo(hDC, rc.right, (rc.top + rc.bottom) / 2 + 2);
            break;
        }
    }
    SelectObject(hDC, hFontOld);

    hFontOld = (HFONT)SelectObject(hDC, g_aFonts[FNTI_INFO_TEXT].m_hFont);
    bool bBOLD = false;
    int nControlCode;

    while((pStart = GetNextLinePos(hDC, pcsz, rcLeft.right - rcLeft.left, nControlCode)) != NULL) {
        switch(nControlCode) {
        case 0: // bold
            if (!bBOLD) {
                SelectObject(hDC, g_aFonts[FNTI_INFO_TEXT_BOLD].m_hFont);
                bBOLD = true;
            }
            break;
        case 1: // EOL
            if (bBOLD) {
                SelectObject(hDC, g_aFonts[FNTI_INFO_TEXT].m_hFont);
                bBOLD = false;
            }
            break;
        case 2: // LF
            if (++nLines >= nTextLinesAll2)
                goto out_break;
            break;
        default: // line of text...
          {
            RECT rc;
            if (nLines < nTextLines) {
                rc.left = rcLeft.left;
                rc.right = rcLeft.right;
                rc.top = rcLeft.top + nLines * nTextHeight;
                rc.bottom = rc.top + nTextHeight;
            } else {
                rc.left = rcRight.left;
                rc.right = rcRight.right;
                rc.top = rcRight.top + (nLines - nTextLines) * nTextHeight;
                rc.bottom = rc.top + nTextHeight;
            }
            DrawText(hDC, pStart, pcsz - pStart, &rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
#if 0 // def _DEBUG
            memcpy(szBuf, pStart, pcsz - pStart);
            szBuf[pcsz - pStart] = '\0';
            ::OutputDebugString(szBuf);
            ::OutputDebugString("\n");
            Sleep(5);
#endif // _DEBUG
            if (++nLines >= nTextLinesAll2)
                goto out_break;
            break;
          }
        }
    }
out_break:
    SelectObject(hDC, hFontOld);

    rcLeft.left -= rcTextMargin.left;
    rcLeft.right += rcTextMargin.right;
    rcLeft.top -= rcTextMargin.top;
    rcLeft.bottom += rcTextMargin.bottom;

    rcRight.left -= rcTextMargin.left;
    rcRight.right += rcTextMargin.right;
    rcRight.top -= rcTextMargin.top;
    rcRight.bottom += rcTextMargin.bottom;

    Rectangle(hDC, rcLeft.left, rcLeft.top, rcLeft.right, rcLeft.bottom);
    Rectangle(hDC, rcRight.left, rcRight.top, rcRight.right, rcRight.bottom);

    delete [] ppRIs;

    delete [] pRIs;
}

int CRecScoreFull::GetMemSize() const
{
    int nSize = sizeof(CRecScoreFull);

    nSize += m_nMissionParamSize;

    CRecScoreObject* p1 = m_pScoreObjects;
    while(p1) {
        nSize += p1->GetMemSize();
        nSize += sizeof(int);
        p1 = p1->m_pNext;
    }
    CRecScorePack* p2 = m_pScorePackStart;
    while(p2) {
        nSize += p2->GetMemSize();
        nSize += sizeof(int);
        p2 = p2->m_pNext;
    }

    return nSize;
}
#if !defined(MW4PRINT)
bool CRecScoreFull::SavePR(const char* pcszFile)
{
    gosASSERT(pcszFile && pcszFile[0]);
    FILE* f = fopen(pcszFile, "wb");
    if (f) {
        DynamicMemoryStream mf(GetMemSize());

        mf.WriteBytes(&g_dwPR_Meta, sizeof(g_dwPR_Meta));
        mf.WriteBytes(&m_nState, sizeof(m_nState));
        mf.WriteBytes(&m_SysTime, sizeof(m_SysTime));
        mf.WriteBytes(&m_nMissionParamSize, sizeof(m_nMissionParamSize));
        mf.WriteBytes(m_pMissionParam, m_nMissionParamSize);
        mf.WriteBit(m_bTeamGame);
        mf.WriteByteAlign();
        mf.WriteBytes(&m_nCount, sizeof(m_nCount));
        mf.WriteBytes(&m_fFrameTime, sizeof(m_fFrameTime));
        if (g_dwPR_Meta != g_dwPR_MetaOldA) {
            mf.WriteBytes(&m_naTeamScores[0], sizeof(m_naTeamScores));
        }

        int n;
        CRecScoreObject* p1 = m_pScoreObjects;
        n = 0;
        while(p1) {
            n++;
            p1 = p1->m_pNext;
        }
        mf.WriteBytes(&n, sizeof(n));

        p1 = m_pScoreObjects;
        while(n-- > 0) {
            p1->Save(mf, g_dwPR_Meta);
            p1 = p1->m_pNext;
        }

        CRecScorePack* p2 = m_pScorePackStart;
        n = 0;
        while(p2) {
            n++;
            p2 = p2->m_pNext;
        }
        mf.WriteBytes(&n, sizeof(n));

        p2 = m_pScorePackStart;
        while(p2) {
            p2->Save(mf);
            p2 = p2->m_pNext;
        }
        mf.WriteByteAlign();

        if ((g_dwPR_Meta != g_dwPR_MetaOldA) && (g_dwPR_Meta != g_dwPR_MetaOldB) && (g_dwPR_Meta != g_dwPR_MetaOldC)) {
            mf.WriteBytes(&m_nPVPs, sizeof(m_nPVPs));
            if (0 < m_nPVPs) {
                mf.WriteBytes(m_pPVPs, m_nPVPs * sizeof(m_pPVPs[0]));
            }
        }
        mf.WriteByteAlign();
        
        fwrite(mf.GetStreamStart(), mf.GetBytesUsed(), 1, f);
        fclose(f);

        return true;
    }

    return false;
}
#endif // !defined(MW4PRINT)
bool CRecScoreFull::LoadPR(const char* pcszFile)
{
    gosASSERT(pcszFile && pcszFile[0]);
    bool bRet = false;

    FILE* f = fopen(pcszFile, "rb");
    if (f) {
        Reset();

        long lSize = _filelength(fileno(f));
        BYTE* pBuf = (BYTE*)malloc(lSize);
        fread(pBuf, lSize, 1, f);
        MemoryStream mf(pBuf, lSize);

        DWORD dwMeta;
        mf.ReadBytes(&dwMeta, sizeof(dwMeta));
        if ((dwMeta == g_dwPR_Meta) || (dwMeta == g_dwPR_MetaOldC) || (dwMeta == g_dwPR_MetaOldB) || (dwMeta == g_dwPR_MetaOldA)) {
            mf.ReadBytes(&m_nState, sizeof(m_nState));
            mf.ReadBytes(&m_SysTime, sizeof(m_SysTime));
            mf.ReadBytes(&m_nMissionParamSize, sizeof(m_nMissionParamSize));
            m_pMissionParam = new BYTE[m_nMissionParamSize];
            mf.ReadBytes(m_pMissionParam, m_nMissionParamSize);
            mf.ReadBit(m_bTeamGame);
            mf.ReadByteAlign();
            mf.ReadBytes(&m_nCount, sizeof(m_nCount));
            mf.ReadBytes(&m_fFrameTime, sizeof(m_fFrameTime));
            if (dwMeta != g_dwPR_MetaOldA) {
                mf.ReadBytes(&m_naTeamScores[0], sizeof(m_naTeamScores));
            }

            int n;

            mf.ReadBytes(&n, sizeof(n));
            CRecScoreObject* p1Last = NULL;
            while(n-- > 0) {
                CRecScoreObject* p1 = new CRecScoreObject(this);
                if (p1Last) {
                    p1Last->m_pNext = p1;
                    p1->m_pPrev = p1Last;
                } else {
                    m_pScoreObjects = p1;
                }
                p1Last = p1;
                p1->Load(mf, dwMeta);
            }

            mf.ReadBytes(&n, sizeof(n));
            CRecScorePack* p2Last = NULL;
            while(n-- > 0) {
                CRecScorePack* p2 = new CRecScorePack;
                if (p2Last) {
                    p2Last->m_pNext = p2;
                    p2->m_pPrev = p2Last;
                } else {
                    m_pScorePackStart = p2;
                }
                p2Last = p2;
                p2->Load(mf);
            }

            if ((dwMeta != g_dwPR_MetaOldA) && (dwMeta != g_dwPR_MetaOldB) && (dwMeta != g_dwPR_MetaOldC)) {
                mf.ReadBytes(&m_nPVPs, sizeof(m_nPVPs));
                if (m_pPVPs) {
                    delete [] m_pPVPs;
                    m_pPVPs = NULL;
                }
                if (0 < m_nPVPs) {
                    m_pPVPs = new SPVP_Rec[m_nPVPs];
                    mf.ReadBytes(m_pPVPs, m_nPVPs * sizeof(m_pPVPs[0]));
                }
                mf.ReadByteAlign();
            }

            mf.Rewind();

            bRet = true;
        }

        free(pBuf);
        fclose(f);
        //if (!bRet) {
        //  remove(pcszFile);
        //}

        return true;
    }

    return bRet;
}

void CRecScoreFull::LoadPrintParams() const
{
    MString strOI(&AssetsDirectory1[0]);
    strOI += "options.ini";

    LoadPrintParam(strOI, "CellMarginLR", s_nCellMarginLR, U_point(200), U_point(10), U_point(500));
    LoadPrintParam(strOI, "CellMarginTV", s_nCellMarginTV, U_point(200), U_point(10), U_point(500));
    LoadPrintParam(strOI, "PaperMarginL", s_rcMargin4NPA.left, U_cm(100), U_cm(20), U_cm(300));
    LoadPrintParam(strOI, "PaperMarginT", s_rcMargin4NPA.top, U_cm(100), U_cm(20), U_cm(300));
    LoadPrintParam(strOI, "PaperMarginR", s_rcMargin4NPA.right, U_cm(100), U_cm(20), U_cm(300));
    LoadPrintParam(strOI, "PaperMarginB", s_rcMargin4NPA.bottom, U_cm(100), U_cm(20), U_cm(300));
    LoadPrintParam(strOI, "GapColumn", s_nGapColumn, U_cm(40), U_cm(10), U_cm(300));
    LoadPrintParam(strOI, "TopImageHeight", s_nTopImageHeight, U_cm(300), U_cm(100), U_cm(1000));
    LoadPrintParam(strOI, "GapTopImageTable", s_nGapTopImageTable, U_cm(30), U_cm(10), U_cm(100));
    LoadPrintParam(strOI, "GapFFA", s_nGapFFA, U_point(300), U_point(10), U_point(500));
    LoadPrintParam(strOI, "GapTableMH", s_nGapTableMH, U_cm(30), U_cm(10), U_cm(100));
    LoadPrintParam(strOI, "MHHeight", s_nMHHeight, U_point(2000), U_point(100), U_point(500));
    LoadPrintParam(strOI, "GapMHText", s_nGapMHText, U_cm(30), U_cm(10), U_cm(100));
    LoadPrintParam(strOI, "TextMarginL", s_rcTextMargin.left, U_point(300), 0, U_cm(100));
    LoadPrintParam(strOI, "TextMarginT", s_rcTextMargin.top, 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, "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, "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, "InfoTextHeight", g_aFonts[FNTI_INFO_TEXT].m_nTextHeight, U_point(900), U_point(600), U_point(1200));
    g_aFonts[FNTI_TABLE_TEXT_BOLD].m_nTextHeight = g_aFonts[FNTI_TABLE_TEXT].m_nTextHeight;
    g_aFonts[FNTI_INFO_TEXT_BOLD].m_nTextHeight = g_aFonts[FNTI_INFO_TEXT].m_nTextHeight;
}

void CRecScoreFull::LoadPrintParam(const char* pcszINI, const char* pcszKey, long& lValue, int nDef, int nMin, int nMax) const
{
    int nValue = lValue;
    LoadPrintParam(pcszINI, pcszKey, nValue, nDef, nMin, nMax);
    lValue = nValue;
}

void CRecScoreFull::LoadPrintParam(const char* pcszINI, const char* pcszKey, int& nValue, int nDef, int nMin, int nMax) const
{
    UINT u = GetPrivateProfileInt("battle tech print", pcszKey, nDef, pcszINI);

    int n = *(int*)&u;
    if ((nMin <= n) && (n <= nMax)) {
        nValue = n;
    } else {
        nValue = nDef;
    }
}
// for printing...
#undef UNIT
#undef UNIT00

#undef LP2PP
#undef U_mm
#undef U_inch
#undef U_point
#undef U_cm
//

#endif // !defined(__RECSCORE_CPP__)
