/*****************************************************************************/
/* */
/* Moras Ausrüstungsplaner für Dark Age of Camelot */
/* Copyright (C) 2003 - 2004 Mora */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* */
/*****************************************************************************/
//---------------------------------------------------------------------------
#include <assert.h>
#include <StrUtils.hpp>
#pragma hdrstop
#include "ChatLog.h"
#include "Utils.h"
#include "MainFrame.h" // Brauch die xml_config Struktur
// Mit diesem String beginnt der Info-Bereich
const char strInfoStart[] = "<Info-Beginn:";
// Mit diesem String endet der Info-Bereich
const char strInfoStop[] = "<Info-Ende>";
// Mit diesem String beginnt der Info-Bereich
const char strMagBoniStart[] = "Magieboni:";
// Ein paar Strings zur identifikation bestimmter Werte
const char strAF[] = "Ausgangswert";
const char strAbsorb[] = "Absorption"; // Daraus läßt sich Rüstungsklasse gewinnen
const char strQuality[] = "Qualität";
const char strSpeed[] = "Waffengeschwindigkeit";
const char strUnique[] = "Einzigartiger Gegenstand";
const char strCrafted[] = "Hergestellt von:";
const char strBonus[] = "Bonus auf";
const char strBonus2[] = "Bonus zum";
const char strRestriction[] = "Geeignet für:";
const char strArtifact[] = "Artefakt:";
const char strActivLevel[] = " (Benötigte Objektstufe:";
//---------------------------------------------------------------------------
CChatLog::CChatLog()
{
fChatLog = NULL;
arItemNames = new TStringList;
};
CChatLog::~CChatLog()
{
CloseChatLog();
delete arItemNames;
};
// Öffnet die ChatLog-Datei für Lesezugriff
int CChatLog::OpenChatLog(const char* Filename)
{
if (fChatLog != NULL) delete fChatLog;
// Seek funktioniert nur richtig im binary-mode. Nur ist jetzt immer /r hinter einem String
fChatLog = new fstream(Filename, ios_base::in | ios_base::binary);
iLineNo = 0;
if (fChatLog->is_open()) return true;
else return false;
};
void CChatLog::CloseChatLog()
{
if (fChatLog != NULL)
{
fChatLog->close();
delete fChatLog;
}
fChatLog = NULL;
};
// enumwerte des Parsers in Init
enum ParserStateInit
{
STATE_START, // Scanne nach Start einer Info-Section
STATE_ITEM, // Suche danach in der Sektion, das es ein Gegenstand ist
};
// Diese Funktion scannt erstmal das gesamte Log nach Gegenständen und merkt
// sich deren Position. In einem zweiten durchlauf wird versucht, die Herkunft
// der Gegenstände herauszubekommen. Doppelte Items werden herausgefiltert
void CChatLog::Init(void)
{
int posStart; // Startposition des aktuellen Info-Bereichs
AnsiString strName; // Name des aktuellen Info-Bereichs
char Line[1024]; // Hier eine maximale Länge eines Textes heruasfinden
bool bIsItem;
nItems = 0;
iLineNo = 0;
arItemNames->Clear();
arOffsets.Length = 0;
ParserStateInit State = STATE_START;
fChatLog->seekg(0, ios::beg); // Lesezeiger auf Dateianfang
fChatLog->getline(Line, 1024);
while(!fChatLog->eof())
{
assert(StrLen(Line) < 1024);
iLineNo++;
switch (State)
{
case STATE_START:
{ // Suche nach dem Start eines Info-Bereichs
char *pStart = AnsiStrPos(Line, (char*)strInfoStart);
if (pStart != NULL)
{
posStart = fChatLog->tellg() - fChatLog->gcount();
AnsiString sTemp = pStart;
strName = sTemp.SubString(sizeof(strInfoStart) + 1, StrLen(pStart) - sizeof(strInfoStart)- 2);
// Jetzt mal schauen, ob es einen Doppelpunkt gibt.
int p = strName.AnsiPos(':');
if (p > 0)
{ // Wenn ja, dann den String bis dorthin abschneiden
strName = strName.SubString(p + 2, strName.Length() - p - 1);
}
bIsItem = false;
State = STATE_ITEM;
}
}
break;
case STATE_ITEM:
{ // Suche nach Magieboni oder Info-Sektion Ende
char *pStart = AnsiStrPos(Line, (char*)strMagBoniStart);
if (pStart != NULL) bIsItem = true;
// Ist Info-Sektion zu Ende?
pStart = AnsiStrPos(Line, (char*)strInfoStop);
if (pStart != NULL)
{
State = STATE_START; // Nach der nächsten Info-Sektion suchen
if (bIsItem)
{ // Ende Info-Sektion und es ist ein Gegenstand
// Also in die Liste der zu untersuchenden Gegenstände aufnehmen
int index;
if ((index = arItemNames->IndexOf(strName)) == -1)
{ // Item mit diesem Namen gibts noch nicht (im Logfile)
arItemNames->Add(strName);
arOffsets.Length = nItems + 1;
arOffsets[nItems] = posStart;
nItems++;
}
else
{ // Name gibts schon. Offset aber auf diesen setzen,
// da immer das letzte das aktuellste sein sollte
arOffsets[index] = posStart;
}
}
}
}
break;
}
fChatLog->getline(Line, 1024);
}
}
// Diese Funktion macht die eigentliche Lesearbeit
// Input ist dabei der Index des zu beschaffenden Items und einen Zeiger
// auf eine CItem-Klasse, welche ausgefüllt werden soll
// Rückgabe ist true für keinen Fehler
int CChatLog::GetItem(int index, CItem* Item)
{
int i;
char Line[1024]; // Hier eine maximale Länge eines Textes heruasfinden
char sArg[64]; // Längere Bonusbezeichnungen gibts glaub nicht
int NextBonus = 0; // Welche Position soll der nächste Bonus eingetragen werden
int NextRestriction = 0; // Nächster Index einer Klassenbeschränkung
bool bRestriction = false;
if ((index < 0) && (index >= nItems)) return false; // Itemindex nicht im gültigen Bereich
Item->Init();
Item->Name = arItemNames->Strings[index];
Item->Realm = player->Realm; // Immer Realm des Spielers annehmen
Item->Quality = 0;
// Versuche Aufgrund des Namen auf den Itemslot zu schliessen
AnsiString strTemp = Item->Name.LowerCase();
for (i = 0; i < 18; i++)
{
for (int j = 0; j < xml_config.arItemSlots[i].arIds.Length; j++)
{
if (strTemp.AnsiPos(xml_config.arItemSlots[i].arIds[j]) > 0)
Item->Position = i;
}
}
// Lesezeiger auf Beginn der entsprechenden Info-Sektion stellen
fChatLog->clear(); // Ist nötig, da wir eventuell noch im eof-state sind
fChatLog->seekg(arOffsets[index], ios::beg);
fChatLog->getline(Line, 1024); // Erste Zeile Lesen (kann ignoriert werden)
fChatLog->getline(Line, 1024); // Erste Zeile mit Infos
while(!fChatLog->eof())
{
assert(StrLen(Line) < 1024);
int Value = 0;
// Ist Info-Sektion zu Ende?
char *pStart = AnsiStrPos(Line, (char*)strInfoStop);
if (pStart!= NULL) break;
if (Line[11] == '-')
{ // Alles danach ist ein Wert, der mich interessiert
// Suche erstmal nach einem Zahlenwert. Sollte immer nur einer sein
// Bonus-Argument ist immer der String vor einem Doppelpunkt,
// oder der String hinter einer Zahl
bool bValue = false; // Gibts eine Zahl?
bool bDoppel = false; // gibt es einen Doppelpunkt im String
bool bPercent = false; // Es ist ein Prozent-Wert
int pArg = 0; // Aktuelle Position im Argument-String
for (i = 13; i < StrLen(Line); i++)
{
if ((Line[i] >= '0') && (Line[i] <= '9'))
{
bValue = true;
Value = Value * 10 + Line[i] - '0';
}
else if (Line[i] == ':') bDoppel = true;
else if (Line[i] == ' ')
{
if (bValue && (bDoppel || (pArg > 0))) break;
pArg = 0;
}
else if (Line[i] == '%')
bPercent = true;
else if ((Line[i] == '\r') || (Line[i] == '.')) {} // Diese Zeichen ignorieren
else
{
sArg[pArg++] = Line[i];
sArg[pArg] = 0;
}
}
if (bValue)
{ // In xml_config nach einem entsprechendem Boni suchen
int bid = xml_config.GetBonusId(sArg, bPercent);
if (bid < 0)
{ // testen, ob der gesamte Text nicht vielleicht doch ein Bonus ist
bid = xml_config.GetBonusId(MidStr(Line, 14, i - 16), bPercent);
}
if (bid >= 0)
{ // Es ist ein regulärer Bonus
Item->Effect[NextBonus] = bid;
Item->EffectValue[NextBonus] = Value;
NextBonus++;
}
else
{ // Hier ein paar zusätzliche Werte nehmen, die keine Boni sind
if (strcmp(sArg, strAF) == 0) Item->AF = Value;
if (strcmp(sArg, strQuality) == 0) Item->Quality = Value;
if (strcmp(sArg, strSpeed) == 0) Item->Speed = Value;
if (strcmp(sArg, strAbsorb) == 0)
{ // Value in Rüstungsklasse umwandeln
switch (Value)
{
case 0: Item->Class = 0; Item->Level = Item->AF;break;
case 10: Item->Class = 1; Item->Level = Item->AF / 2; break;
case 19: Item->Class = 2; Item->Level = Item->AF / 2; break;
case 27: Item->Class = 3; Item->Level = Item->AF / 2; break;
case 34: Item->Class = 4; Item->Level = Item->AF / 2; break;
}
//if (Item->Position < 0)
// Item->Position = 0; // Alle Rüstungen als Handschuhe annehmen
// Es gibt leider keine Info darüber im Log
// Wenn wir die Position auf -1 lassen, dann kommt automatisch eine Auswahlbox
}
if (strcmp(sArg, "DPS") == 0)
{ // Nur ersten DPS-Wert speichern
if (Item->DPS == 0)
{
Item->DPS = Value;
// Berechne den Itemlevel
int level = (Value - 11) / 3;
if (level > 51) level = 51;
Item->Level = level;
}
}
}
}
}
// else
{ // noch ein paar besondere Strings auswerten
if (strncmp(&Line[11], strUnique, sizeof(strUnique) - 1) == 0)
{ // Unique Gegenstände nicht importieren
return false;
}
if (strncmp(&Line[11], strCrafted, sizeof(strCrafted) - 1) == 0)
{ // Craftet Gegenstände nicht importieren
return false;
}
if (strncmp(&Line[11], strArtifact, sizeof(strArtifact) - 1) == 0)
{
Item->MaxLevel = 10;
Item->Realm = 7;
}
if ((strncmp(&Line[11], strBonus, sizeof(strBonus) - 1) == 0) \
|| (strncmp(&Line[11], strBonus2, sizeof(strBonus2) - 1) == 0))
{ // Spezielle Werte (meist Bonuserhöhungen)
// Bonus auf BONUS: VALUE
unsigned int i;
for (i = 21; i < StrLen(Line); i++)
{ // Alles bis zum Doppelpunkt in sArg kopieren
if (Line[i] == ':') break;
sArg[i - 21] = Line[i];
}
sArg[i - 21] = 0;
for (; i < StrLen(Line); i++)
{
if ((Line[i] >= '0') && (Line[i] <= '9'))
{
Value = Value * 10 + Line[i] - '0';
}
}
// In xml_config nach einem entsprechendem Boni suchen
int bid = xml_config.GetBonusId(sArg);
if (bid >= 0)
{ // Es ist ein regulärer Bonus
Item->Effect[NextBonus] = bid;
Item->EffectValue[NextBonus] = Value;
NextBonus++;
}
else
{ // Fehlermeldung
Application->MessageBoxA(AnsiString("Ein Bonus '" + AnsiString(sArg) + "' ist unbekannt!\nDie Datei 'config.xml' bzw. die entsprechende Sprachdatei anpassen!").c_str(), "Fehler");
}
}
if (strncmp(&Line[11], strActivLevel, sizeof(strActivLevel) - 1) == 0)
{ // Danach kommt ne Zahl, welche den Level angibt
int level = Str2Int(&Line[11 + sizeof(strActivLevel)]);
Item->EffectLevel[NextBonus - 1] = level;
}
unsigned int cp = 12;
if ((strncmp(&Line[11], strRestriction, sizeof(strRestriction) - 1) == 0) \
|| (strncmp(&Line[12], strRestriction, sizeof(strRestriction) - 1) == 0))
{ // Solange nachfolgende Strings eine Klasse darstellen, solange
// diese bei den Beschränkungen eintragen
bRestriction = true;
cp = 12 + sizeof(strRestriction);
}
while (bRestriction && (cp < StrLen(Line)))
{
int pArg = 0;
for (; cp < StrLen(Line); cp++)
{
if ((Line[cp] == ' ') || (Line[cp] == ',') || (Line[cp] == '-') || (Line[cp] == '\r'))
{
sArg[pArg] = 0;
if (pArg > 0)
{ // Es gibt einen String. Schauen obs ne Klasse ist
int cid = xml_config.GetClassId(sArg);
if (cid >= 0)
{
Item->ClassRestriction[NextRestriction++] = cid;
}
else
{ // Keine Klasse mehr, Modus aufheben
bRestriction = false;
}
}
pArg = 0;
}
else
{
sArg[pArg++] = Line[cp];
}
}
}
}
fChatLog->getline(Line, 1024);
}
return true;
}
#pragma
package(smart_init)