qt-agistudio/src/words.cpp

528 lines
15 KiB
C++

/*
* QT AGI Studio :: Copyright (C) 2000 Helen Zommer
*
* Almost all of this code was adapted from the Windows AGI Studio
* developed by Peter Kelly.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include "game.h"
#include "words.h"
#include "menu.h"
#include "wordsedit.h"
#include <stdio.h>
#include <sys/types.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <stdlib.h>
#include <ctype.h>
static TWordGroup NewWordGroup[MaxWordGroups];
static int ResPos;
static bool EndOfFileReached;
//***************************************************
WordList::WordList()
{
}
//***************************************************
static byte ReadByte(void)
{
byte ret;
if(ResPos < ResourceData.Size){
ret = ResourceData.Data[ResPos];
ResPos++;
}
else{
ret = 0;
EndOfFileReached = true;
}
return ret;
}
//***************************************************
static int ReadMSLSWord(void)
{
byte MSbyte,LSbyte;
MSbyte = ReadByte();
LSbyte = ReadByte();
return (MSbyte*256 + LSbyte);
}
//***************************************************
int WordList::GetNew_GroupIndex(int GroupNum)
{
int CurIndex;
if (NumGroups > 0){
for(CurIndex = 0; CurIndex < NumGroups; CurIndex++){
if (NewWordGroup[CurIndex].GroupNum == GroupNum){
return CurIndex;
}
}
}
return -1;
}
//************************************************
int WordList::GetWordGroupIndex(int GroupNum)
{
int CurIndex;
if (NumGroups > 0){
for (CurIndex = 0;CurIndex<NumGroups;CurIndex++){
if (WordGroup[CurIndex].GroupNum == GroupNum)
return CurIndex;
}
}
return -1;
}
//************************************************
static string GetWord(string s)
//returns the part of a string before the last ' '
{
int pos = s.find_last_of(' ');
return s.substr(0,pos);
}
static int GetGroupNum(string s)
//returns the number after the last ' ' in a string
{
int pos = s.find_last_of(' ');
return atoi(s.substr(pos+1).c_str());
}
//************************************************
int WordList::read(char *filename)
{
string CurWord,PrevWord;
byte CurByte,CharsFromPrevWord;
int CurIndex,CurAddGroup,GroupIndex,GroupNum;
int LowestRemainingGroup,SecondLowestRemainingGroup,SecondLowestRemainingGroupIndex=0;
int GroupAddOrder[MaxWordGroups];
FILE *fptr=fopen(filename,"rb");
if(fptr==NULL){
menu->errmes("Error opening file %s",filename);
return 1;
}
struct stat buf;
fstat(fileno(fptr),&buf);
int size=buf.st_size;
if(size > MaxResourceSize){
menu->errmes("Error: File %s is too big (>%d bytes)",filename,MaxResourceSize);
return 1;
}
EndOfFileReached = false;
ResourceData.Size=size;
fread(ResourceData.Data,size,1,fptr);
fclose(fptr);
NumGroups = 0;
ResPos = 0;
ResPos = ReadMSLSWord(); //start of words section
PrevWord = "";
do{
CurWord = "";
CharsFromPrevWord = ReadByte();
if (CharsFromPrevWord > PrevWord.length()){
CharsFromPrevWord = PrevWord.length();
}
if (CharsFromPrevWord > 0){
CurWord = PrevWord.substr(0,CharsFromPrevWord);
}
do{
CurByte = ReadByte();
if (CurByte < 0x80)CurWord += (CurByte ^ 0x7f);
}while(CurByte < 0x80 && !EndOfFileReached);
//we must check for end of file, otherwise if the file is invalid, the
//program may read indefinitely.
if(EndOfFileReached){
menu->errmes("Error! Invalid WORDS.TOK file.");
return 1;
}
CurWord += (0x7F ^ (CurByte-0x80));
GroupNum = ReadMSLSWord();
if (CurWord != PrevWord){ //this word different to previous, so add it
//in this way, no duplicates are added
GroupIndex = GetNew_GroupIndex(GroupNum);
if (GroupIndex >= 0){ //group exists
NewWordGroup[GroupIndex].Words.add(CurWord);
}
else{
if (NumGroups >= MaxWordGroups){
menu->errmes("Error ! Too many groups !");
return 1;
}
NumGroups++;
NewWordGroup[NumGroups-1].GroupNum = GroupNum;
NewWordGroup[NumGroups-1].Words = TStringList();
NewWordGroup[NumGroups-1].Words.add(CurWord);
}//group doesn't exist - create new one
PrevWord = CurWord;
}
CurByte = ReadByte();
if (CurByte == 0 && ResPos >= ResourceData.Size-1) EndOfFileReached = true;
else ResPos--;
}while(!EndOfFileReached);
LowestRemainingGroup = -1;
for (CurAddGroup = 0; CurAddGroup < NumGroups; CurAddGroup++){
SecondLowestRemainingGroup = 65536;
for (CurIndex = 0; CurIndex < NumGroups; CurIndex++){
if (NewWordGroup[CurIndex].GroupNum < SecondLowestRemainingGroup &&
NewWordGroup[CurIndex].GroupNum > LowestRemainingGroup){
SecondLowestRemainingGroup = NewWordGroup[CurIndex].GroupNum;
SecondLowestRemainingGroupIndex = CurIndex;
}
}
GroupAddOrder[CurAddGroup] = SecondLowestRemainingGroupIndex;
LowestRemainingGroup = SecondLowestRemainingGroup;
}
for (CurIndex = 0; CurIndex < NumGroups; CurIndex++){
WordGroup[CurIndex].GroupNum = NewWordGroup[GroupAddOrder[CurIndex]].GroupNum;
WordGroup[CurIndex].Words.lfree();
WordGroup[CurIndex].Words.copy(NewWordGroup[GroupAddOrder[CurIndex]].Words);
NewWordGroup[GroupAddOrder[CurIndex]].Words.lfree();
}
return 0;
}
//**************************************************
void WordList::clear()
{
for (int i = 0;i< NumGroups;i++){
WordGroup[i].Words.lfree();
}
NumGroups = 3;
WordGroup[0].GroupNum = 0;
WordGroup[0].Words = TStringList();
WordGroup[0].Words.add("a");
WordGroup[1].GroupNum = 1;
WordGroup[1].Words = TStringList();
WordGroup[1].Words.add("anyword");
WordGroup[2].GroupNum = 9999;
WordGroup[2].Words = TStringList();
WordGroup[2].Words.add("rol");
}
//**************************************************
int WordList::save(char *filename)
{
FILE *fptr;
int CurGroupIndex,NumEmptyWordGroups;
int CurWord,CurChar;
byte CurByte;
int LetterLoc[28];
string ThisWord,PrevWord;
int CurFirstLetter;
char FirstLetter;
int ThisGroupNum;
byte CharsFromPrevWord;
string s;
for(CurGroupIndex=0,NumEmptyWordGroups=0;CurGroupIndex<NumGroups;CurGroupIndex++){
if (WordGroup[CurGroupIndex].Words.num==0){
NumEmptyWordGroups++;
}
}
if (NumEmptyWordGroups > 0){
sprintf(tmp,"Warning: There are %d empty word groups.\nThese will not be saved.",NumEmptyWordGroups);
menu->warnmes(tmp);
}
if((fptr=fopen(filename,"wb"))==NULL){
menu->errmes("Error opening file %s",filename);
return 1;
}
TStringList AllWords = TStringList();
for(CurGroupIndex = 0; CurGroupIndex<NumGroups;CurGroupIndex++){
if (WordGroup[CurGroupIndex].Words.num > 0){
for(CurWord =0;CurWord<WordGroup[CurGroupIndex].Words.num;CurWord++){
s = WordGroup[CurGroupIndex].Words.at(CurWord) + " " + IntToStr(WordGroup[CurGroupIndex].GroupNum);
AllWords.addsorted((char *)s.c_str());
}
}
}
CurByte=0;
fwrite(&CurByte,1,51,fptr);
for (CurFirstLetter = 1;CurFirstLetter <= 26;CurFirstLetter++){
LetterLoc[CurFirstLetter] = 0;
}
FirstLetter = 'a';
LetterLoc[1] = ftell(fptr);
for (CurWord = 0; CurWord < AllWords.num; CurWord++){
ThisWord = GetWord(AllWords.at(CurWord));
if (ThisWord[0] != FirstLetter && ThisWord[0]>=97 && ThisWord[0]<=122){
FirstLetter = ThisWord[0];
LetterLoc[FirstLetter-96] = ftell(fptr);
}
ThisGroupNum = GetGroupNum(AllWords.at(CurWord));
//work out # chars from prev word
CharsFromPrevWord = 0;
CurChar = 0;
bool FinishedComparison = false;
do{
if(CurChar < (int)ThisWord.length() && (int)PrevWord.length() > CurChar && PrevWord[CurChar] == ThisWord[CurChar])
CharsFromPrevWord++;
else FinishedComparison=true;
CurChar++;
}while(! FinishedComparison);
if (CharsFromPrevWord >= ThisWord.length()){
CharsFromPrevWord = ThisWord.length()-1;
}
//write # chars from prev word
fwrite(&CharsFromPrevWord,1,1,fptr);
PrevWord = ThisWord;
ThisWord = ThisWord.substr( CharsFromPrevWord,ThisWord.length()-CharsFromPrevWord);
if (ThisWord.length() > 1){
for(CurChar =0;CurChar <(int)ThisWord.length()-1;CurChar++){
CurByte = 0x7f ^ ThisWord[CurChar];
fwrite(&CurByte,1,1,fptr);
}
}
CurByte = 0x80 + (0x7f ^ ThisWord[ThisWord.length()-1]);
fwrite(&CurByte,1,1,fptr);
//write group number
CurByte = ThisGroupNum / 256;
fwrite(&CurByte,1,1,fptr);
CurByte = ThisGroupNum % 256;
fwrite(&CurByte,1,1,fptr);
}
CurByte = 0;
fwrite(&CurByte,1,1,fptr);
fseek(fptr,0,SEEK_SET);
for(CurFirstLetter = 1;CurFirstLetter <=26;CurFirstLetter++){
CurByte = LetterLoc[CurFirstLetter] / 256;
fwrite(&CurByte,1,1,fptr);
CurByte = LetterLoc[CurFirstLetter] % 256;
fwrite(&CurByte,1,1,fptr);
}
AllWords.lfree();
fclose(fptr);
return 0;
}
//**************************************************
int WordList::add_group(int num)
{
int i;
for (i = 0;i< NumGroups;i++){
if(num == WordGroup[i].GroupNum){
menu->errmes("Group %d already exists.",num);
return -1;
}
if(WordGroup[i].GroupNum > num){
break;
}
}
NumGroups++;
for(int k=NumGroups;k>i;k--){
WordGroup[k]=WordGroup[k-1];
}
WordGroup[i].Words = TStringList();
WordGroup[i].GroupNum = num;
return i;
}
//**************************************************
int WordList::delete_group(int num)
{
for(int i=num;i<NumGroups-1;i++){
WordGroup[i]=WordGroup[i+1];
}
NumGroups--;
return 0;
}
//**************************************************
int WordList::delete_word(char *word,int SelectedGroup)
{
if(SelectedGroup<0)return -1;
for(int k=0;k<WordGroup[SelectedGroup].Words.num;k++){
if(!strcmp(word,WordGroup[SelectedGroup].Words.at(k).c_str())){
WordGroup[SelectedGroup].Words.del(k);
return k;
}
}
return -1;
}
//**************************************************
int WordList::change_number(int oldnum, int newnum)
{
int i;
for (i = 0;i< NumGroups;i++){
if(newnum == WordGroup[i].GroupNum){
menu->errmes("Group %d already exists.",newnum);
return -1;
}
if(WordGroup[i].GroupNum > newnum){
break;
}
}
TStringList cur_g = TStringList();
cur_g.copy(WordGroup[oldnum].Words);
if(oldnum < i){
i--;
for(int k=oldnum;k<i;k++){
WordGroup[k]=WordGroup[k+1];
}
}
else{
for(int k=oldnum;k>i;k--){
WordGroup[k]=WordGroup[k-1];
}
}
WordGroup[i].Words = cur_g;
WordGroup[i].GroupNum = newnum;
return i;
}
//************************************************************
bool WordList::GroupExists(int GroupNum)
{
int CurGroupIndex;
if (NumGroups > 0){
for (CurGroupIndex=0;CurGroupIndex<NumGroups;CurGroupIndex++){
if(WordGroup[CurGroupIndex].GroupNum == GroupNum)return true;
}
}
return false;
}
//************************************************************
bool WordList::InsertWordGroup(int GroupNum)
{
int CurGroupIndex,InsertPosition=0;
bool InsertPositionFound;
if (NumGroups >= MaxWordGroups){
menu->errmes("Error! Too many groups (max %d).",MaxWordGroups);
return false;
}
CurGroupIndex = 0;
InsertPositionFound = false;
do{
if (WordGroup[CurGroupIndex].GroupNum > GroupNum){
InsertPosition = CurGroupIndex;
InsertPositionFound = true;
}
else CurGroupIndex++;
}while(!InsertPositionFound && (CurGroupIndex < NumGroups));
if (CurGroupIndex > NumGroups - 1)InsertPosition = NumGroups;
WordGroup[NumGroups].Words = TStringList();
if (NumGroups > 0){
for (CurGroupIndex = NumGroups - 1;CurGroupIndex>=InsertPosition;CurGroupIndex--){
WordGroup[CurGroupIndex+1].GroupNum = WordGroup[CurGroupIndex].GroupNum;
WordGroup[CurGroupIndex+1].Words.lfree();
WordGroup[CurGroupIndex+1].Words.copy(WordGroup[CurGroupIndex].Words);
}
}
NumGroups++;
WordGroup[InsertPosition].GroupNum = GroupNum;
WordGroup[InsertPosition].Words.lfree();
return true;
}
//************************************************************
int WordList::GroupIndexOfWord(string word)
//returns the group index of the group containing the specified word}
{
for (int i = 0;i< NumGroups;i++){
for(int k = 0;k<WordGroup[i].Words.num;k++){
if(WordGroup[i].Words.at(k) == word){
return i;
}
}
}
return -1;
}
//************************************************************
void WordList::merge(const WordList& NewWordList)
{
int CurIndex,CurWord,GroupIndex,NumWordsAdded,GroupIndexOfExistingWord;
string ThisWord;
WhatToDoWithExistingWords = AskUser;
if(NewWordList.NumGroups==0)return;
NumWordsAdded = 0;
for (CurIndex = 0;CurIndex<NewWordList.NumGroups;CurIndex++){
if (!GroupExists(NewWordList.WordGroup[CurIndex].GroupNum))
InsertWordGroup(NewWordList.WordGroup[CurIndex].GroupNum);
GroupIndex = GetWordGroupIndex(NewWordList.WordGroup[CurIndex].GroupNum);
if (GroupIndex >= 0 && NewWordList.WordGroup[CurIndex].Words.num > 0){
for (CurWord= 0;CurWord<NewWordList.WordGroup[CurIndex].Words.num;CurWord++){
GroupIndexOfExistingWord = GroupIndexOfWord(NewWordList.WordGroup[CurIndex].Words.at(CurWord));
if ((GroupIndexOfExistingWord < 0) || ((GroupIndexOfExistingWord >= 0) && (OKToReplaceWord(NewWordList.WordGroup[CurIndex].Words.at(CurWord),WordGroup[GroupIndexOfExistingWord].GroupNum,WordGroup[GroupIndex].GroupNum)))){
delete_word((char *)NewWordList.WordGroup[CurIndex].Words.at(CurWord).c_str(),GroupIndexOfExistingWord);
WordGroup[GroupIndex].Words.addsorted(NewWordList.WordGroup[CurIndex].Words.at(CurWord));
NumWordsAdded++;
}
}
}//{if (GroupIndex >= 0) and (NewWordList.WordGroup[CurIndex].Words.Count > 0)}
}
}
//************************************************************
bool WordList::OKToReplaceWord(string TheWord,int OldGroupNum, int NewGroupNum)
{
if (WhatToDoWithExistingWords == AlwaysReplace)return true;
if (WhatToDoWithExistingWords == NeverReplace)return false;
if (OldGroupNum == NewGroupNum)return true;
ReplaceWord *ask = new ReplaceWord(TheWord,OldGroupNum,NewGroupNum);
int AskResult = ask->exec();
if (AskResult == mrYesToAll)WhatToDoWithExistingWords = AlwaysReplace;
if (AskResult == mrNoToAll)WhatToDoWithExistingWords = NeverReplace;
if (AskResult == mrYes || AskResult == mrYesToAll)return true;
else return false;
}
//************************************************************