//--------------------------------------------------------------------------- #include #pragma hdrstop #include "Names.h" //--------------------------------------------------------------------------- #pragma package(smart_init) /* Moduł zarządzający plikami oraz wyszukiwaniem obiektów wg nazw. 1. Ma przydzielony z góry (EU07.INI) obszar pamięci (rzędu 16MB). 2. W przypadku przepełnienia dostępnej pamięci wystąpi błąd wczytywania. 3. Obszar ten będzie zużywany na rekordy obiektów oraz ciągi tekstowe z nazwami. 4. Rekordy będą sortowane w ramach typu (tekstury, dźwięki, modele, node, eventy). 5. Pierwszy etap wyszukiwania to 5 bitów z pierwszego bajtu i 3 z drugiego (256). 6. Dla plików istnieje możliwość wczytania ich w innym terminie. 7. Możliwość wczytania plików w oddzielnym watku (np. tekstur). Obsługiwane pliki: 1. Tekstury, można wczytywać później, rekord przechowuje numer podany przez kartę graficzną. 2. Dźwięki, można wczytać później. 3. Modele, można wczytać później o ile nie mają animacji eventami i nie dotyczą pojazdów. Obiekty sortowane wg nazw, można dodawać i usuwać komórki scenerii: 4. Tory, drogi, rzeki - wyszukiwanie w celu sprawdzenia zajetości. 5. Eventy - wyszukiwane przy zewnętrznym wywołaniu oraz podczas wczytywania. 6. Pojazdy - wyszukiwane w celu wysyłania komend. 7. Egzemplarze modeli animowanych - wyszukiwanie w celu połączenia z animacjami. */ void __fastcall ItemRecord::TreeAdd(ItemRecord *r,int c) {//dodanie rekordu do drzewa - ustalenie w której gałęzi //zapisać w (iFlags) ile znaków jest zgodnych z nadrzędnym, żeby nie sprawdzać wszystkich od zera if ((cName[c]&&r->cName[c])?cName[c]==r->cName[c]:false) TreeAdd(r,c+1); //ustawić wg kolejnego znaku, chyba że zero else if ((unsigned char)(cName[c])<(unsigned char)(r->cName[c])) {//zero jest najmniejsze - doczepiamy jako (rNext) if (!rNext) rNext=r; else rNext->TreeAdd(r,0); //doczepić do tej gałęzi } else { if (!rPrev) rPrev=r; else rPrev->TreeAdd(r,0); //doczepić do tej gałęzi } }; void __fastcall ItemRecord::ListGet(ItemRecord *r,int*&n) {//rekurencyjne wypełnianie posortowanej listy na podstawie drzewa if (rPrev) rPrev->ListGet(r,n); //dodanie wszystkich wcześniejszych *n++=this-r; //dodanie swojego indeksu do tabeli if (rNext) rNext->ListGet(r,n); //dodanie wszystkich późniejszych }; void* __fastcall ItemRecord::TreeFind(const char *n) {//wyszukanie ciągu (n) ItemRecord *r=TreeFindRecord(n); return r?r->pData:NULL; }; ItemRecord* __fastcall ItemRecord::TreeFindRecord(const char *n) {//wyszukanie ciągu (n) ItemRecord *r=this; //żeby nie robić rekurencji int i=0; do { if (!n[i]) if (!r->cName[i]) return r; //znaleziony if (n[i]==r->cName[i]) ++i; //porównać kolejny znak else if ((unsigned char)(n[i])<(unsigned char)(r->cName[i])) { i=0; //porównywać od nowa r=r->rPrev; //wcześniejsza gałąź drzewa } else { i=0; //porównywać od nowa r=r->rNext; //późniejsza gałąź drzewa } } while (r); return NULL; }; __fastcall TNames::TNames() {//tworzenie bufora iSize=16*1024*1024; //rozmiar bufora w bajtach cBuffer=new char[iSize]; ZeroMemory(cBuffer,iSize); //nie trzymać jakiś starych śmieci rRecords=(ItemRecord*)cBuffer; cLast=cBuffer+iSize; //bajt za buforem iLast=-1; ZeroMemory(rTypes,20*sizeof(ItemRecord*)); }; int __fastcall TNames::Add(int t,const char *n) {//dodanie obiektu typu (t) o nazwie (n) int len=strlen(n)+1; //ze znacznikiem końca cLast-=len; //rezerwacja miejsca memcpy(cLast,n,len); //przekopiowanie tekstu do bufora //cLast[len-1]='\0'; rRecords[++iLast].cName=cLast; //połączenie nazwy z rekordem rRecords[iLast].iFlags=t; if (!rTypes[t]) rTypes[t]=rRecords+iLast; //korzeń drzewa, bo nie było wcześniej else rTypes[t]->TreeAdd(rRecords+iLast,0); //doczepienie jako gałąź //rTypes[t]=Sort(t); //sortowanie uruchamiać ręcznie if ((iLast&0x3F)==0) //nie za często, bo sortowania zajmą więcej czasu niż wyszukiwania Sort(t); //optymalizacja drzewa co jakiś czas return iLast; } int __fastcall TNames::Add(int t,const char *n,void *d) { int i=Add(t,n); rRecords[iLast].pData=d; return i; }; bool __fastcall TNames::Update(int t,const char *n,void *d) {//dodanie jeśli nie ma, wymiana (d), gdy jest ItemRecord *r=FindRecord(t,n); //najpierw sprawdzić, czy już jest if (r) {//przy zdublowaniu nazwy podmieniać w drzewku na późniejszy r->pData=d; return true; //duplikat } //Add(t,n,d); //nazwa unikalna return false; //został dodany nowy }; ItemRecord* __fastcall TNames::TreeSet(int *n,int d,int u) {//rekurencyjne wypełnianie drzewa pozycjami od (d) do (u) if (d==u) { rRecords[n[d]].rPrev=rRecords[n[d]].rNext=NULL; return rRecords+n[d]; //tej gałęzi nie ma } else if (d>u) return NULL; int p=(u+d)>>1; //połowa rRecords[n[p]].rPrev=TreeSet(n,d,p-1); //zapisanie wcześniejszych gałęzi rRecords[n[p]].rNext=TreeSet(n,p+1,u); //zapisanie późniejszych gałęzi return rRecords+n[p]; }; void __fastcall TNames::Sort(int t) {//przebudowa drzewa typu (t), zwraca wierzchołek drzewa if (iLast<3) return; //jak jest mało, to nie ma sensu sortować if (rTypes[t]) //jeśli jest jakiś rekord danego typu {int *r=new int[iLast+1]; //robocza tablica indeksów - numery posortowanych rekordów int *q=r; //wskaźnik roboczy, przekazywany przez referencję rTypes[t]->ListGet(rRecords,q); //drzewo jest już posortowane - zamienić je na listę rTypes[t]=TreeSet(r,0,(q-r)-1); delete[] r; } return; }; ItemRecord* __fastcall TNames::FindRecord(const int t,const char *n) {//poszukiwanie rekordu w celu np. zmiany wskaźnika return rTypes[t]?rTypes[t]->TreeFindRecord(n):NULL; };