496 lines
11 KiB
C
496 lines
11 KiB
C
|
/*
|
||
|
** Command & Conquer Renegade(tm)
|
||
|
** Copyright 2025 Electronic Arts Inc.
|
||
|
**
|
||
|
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
/****************************************************************************\
|
||
|
* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
|
||
|
******************************************************************************
|
||
|
Project Name:
|
||
|
File Name : linkedlist.h
|
||
|
Author : Neal Kettler
|
||
|
Start Date : June 19, 1997
|
||
|
Last Update : June 19, 1997
|
||
|
|
||
|
Linked list template. This is a fairly standard doubly linked list that
|
||
|
allows insertion and removal at any point in the list. A current pointer
|
||
|
is used to quickly access items when they are examined sequentially.
|
||
|
Copies of the data are stored instead of a pointer to the original.
|
||
|
|
||
|
If you want to store pointers then the template should be of a pointer type.
|
||
|
\****************************************************************************/
|
||
|
|
||
|
#ifndef LINKEDLIST_HEADER
|
||
|
#define LINKEDLIST_HEADER
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
#include "wstypes.h"
|
||
|
|
||
|
template <class T>
|
||
|
class LNode
|
||
|
{
|
||
|
public:
|
||
|
T Node;
|
||
|
LNode<T> *Next;
|
||
|
LNode<T> *Prev;
|
||
|
};
|
||
|
|
||
|
template <class T>
|
||
|
class LinkedList
|
||
|
{
|
||
|
public:
|
||
|
LinkedList();
|
||
|
LinkedList(LinkedList<T> &other);
|
||
|
~LinkedList();
|
||
|
|
||
|
// Remove all entries from the lsit
|
||
|
void clear(void);
|
||
|
|
||
|
// Add a node after the zero based 'pos'
|
||
|
bit8 add(IN T &node,sint32 pos, OUT T **newnodeptr=NULL);
|
||
|
bit8 addTail(IN T &node, OUT T **newnodeptr=NULL);
|
||
|
bit8 addHead(IN T &node, OUT T **newnodeptr=NULL);
|
||
|
|
||
|
// Remove a node
|
||
|
bit8 remove(OUT T &node,sint32 pos);
|
||
|
bit8 remove(sint32 pos);
|
||
|
bit8 removeHead(OUT T &node);
|
||
|
bit8 removeTail(OUT T &node);
|
||
|
|
||
|
|
||
|
// Get a node without removing from the list
|
||
|
bit8 get(OUT T &node,sint32 pos);
|
||
|
bit8 getHead(OUT T &node);
|
||
|
bit8 getTail(OUT T &node);
|
||
|
|
||
|
// Get a pointer to the internally managed data (careful!)
|
||
|
bit8 getPointer(OUT T **node, sint32 pos);
|
||
|
|
||
|
// Get the number of entries in the list
|
||
|
sint32 length(void);
|
||
|
|
||
|
// Print information on the list
|
||
|
void print(IN FILE *out);
|
||
|
|
||
|
// assignment operator
|
||
|
LinkedList<T> &operator=(LinkedList<T> &other);
|
||
|
|
||
|
private:
|
||
|
sint32 Entries; // Number of entries
|
||
|
LNode<T> *Head; // Head of the list
|
||
|
LNode<T> *Tail; // Tail of the list
|
||
|
|
||
|
LNode<T> *Current; // Current pointer & index for speed only
|
||
|
sint32 CurIndex;
|
||
|
};
|
||
|
|
||
|
|
||
|
//Create the empty list
|
||
|
template <class T>
|
||
|
LinkedList<T>::LinkedList()
|
||
|
{
|
||
|
Entries=0;
|
||
|
Head=Tail=Current=NULL;
|
||
|
CurIndex=-1; // Not valid when 0 entries
|
||
|
}
|
||
|
|
||
|
// copy constructor
|
||
|
template <class T>
|
||
|
LinkedList<T>::LinkedList(LinkedList<T> &other)
|
||
|
{
|
||
|
Entries=0;
|
||
|
Head=Tail=Current=NULL;
|
||
|
CurIndex=-1; // Not valid when 0 entries
|
||
|
(*this)=other;
|
||
|
}
|
||
|
|
||
|
//Free all the memory...
|
||
|
template <class T>
|
||
|
LinkedList<T>::~LinkedList()
|
||
|
{
|
||
|
clear(); // Remove the entries
|
||
|
}
|
||
|
|
||
|
// assignment operator
|
||
|
template <class T>
|
||
|
LinkedList<T> &LinkedList<T>::operator=(LinkedList<T> &other)
|
||
|
{
|
||
|
T node;
|
||
|
clear();
|
||
|
for (int i=0; i<other.length(); i++)
|
||
|
{
|
||
|
other.get(node,i);
|
||
|
addTail(node);
|
||
|
}
|
||
|
return(*this);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Remove all the entries and free the memory
|
||
|
template <class T>
|
||
|
void LinkedList<T>::clear()
|
||
|
{
|
||
|
LNode<T> *temp,*del;
|
||
|
|
||
|
temp=Head;
|
||
|
while (temp) {
|
||
|
del=temp;
|
||
|
temp=temp->Next;
|
||
|
delete(del);
|
||
|
}
|
||
|
Entries=0;
|
||
|
CurIndex=-1;
|
||
|
Head=Tail=Current=NULL;
|
||
|
}
|
||
|
|
||
|
// When adding into a position, the new node goes at the zero based slot
|
||
|
// specified by pos. All other nodes get moved one slot down.
|
||
|
template <class T>
|
||
|
bit8 LinkedList<T>::add(IN T &node,sint32 pos, OUT T **newnodeptr)
|
||
|
{
|
||
|
LNode<T> *temp;
|
||
|
LNode<T> *item;
|
||
|
|
||
|
if (pos<0)
|
||
|
pos=0;
|
||
|
if (pos>Entries)
|
||
|
pos=Entries;
|
||
|
|
||
|
item=(LNode<T> *)new LNode<T>;
|
||
|
assert(item!=NULL);
|
||
|
item->Node=node; // copy the passed in object
|
||
|
item->Prev=NULL;
|
||
|
item->Next=NULL;
|
||
|
|
||
|
if (newnodeptr)
|
||
|
*newnodeptr=&(item->Node);
|
||
|
|
||
|
if ((pos==0)||(pos==Entries)) { // Both cases can be true for a new list!
|
||
|
if (pos==0) {
|
||
|
item->Next=Head;
|
||
|
if (Head)
|
||
|
Head->Prev=item;
|
||
|
Head=item;
|
||
|
}
|
||
|
if (pos==Entries) {
|
||
|
item->Prev=Tail;
|
||
|
if (Tail)
|
||
|
Tail->Next=item;
|
||
|
Tail=item;
|
||
|
}
|
||
|
Entries++;
|
||
|
Current=item;
|
||
|
CurIndex=pos;
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
// If control is here, we know the new node is not an endpoint
|
||
|
|
||
|
// Check for possible speedup, so we don't have to scan the list
|
||
|
if (pos==CurIndex) {
|
||
|
item->Next=Current;
|
||
|
item->Prev=Current->Prev;
|
||
|
Current->Prev=item;
|
||
|
item->Prev->Next=item;
|
||
|
Current=item;
|
||
|
Entries++;
|
||
|
return(TRUE);
|
||
|
}
|
||
|
// Check the other possible speedup (adding after CurIndex)
|
||
|
if (pos==CurIndex+1) {
|
||
|
item->Next=Current->Next;
|
||
|
item->Prev=Current;
|
||
|
Current->Next=item;
|
||
|
item->Next->Prev=item;
|
||
|
Current=item;
|
||
|
CurIndex++;
|
||
|
Entries++;
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
// If control reaches here we have to scan the whole thing
|
||
|
temp=Head->Next; // Can start at node '1' because head was special cased
|
||
|
for (int i=1; i<pos; i++) {
|
||
|
temp=temp->Next;
|
||
|
assert(temp!=NULL);
|
||
|
}
|
||
|
item->Next=temp;
|
||
|
item->Prev=temp->Prev;
|
||
|
temp->Prev=item;
|
||
|
item->Prev->Next=item;
|
||
|
Current=item;
|
||
|
CurIndex=pos;
|
||
|
Entries++;
|
||
|
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Add to the first node, all others get shifted down one slot
|
||
|
template <class T>
|
||
|
bit8 LinkedList<T>::addHead(IN T &node, OUT T **newnodeptr)
|
||
|
{
|
||
|
return(add(node,0,newnodeptr));
|
||
|
}
|
||
|
|
||
|
|
||
|
// Append to the end of the list
|
||
|
template <class T>
|
||
|
bit8 LinkedList<T>::addTail(IN T &node, OUT T **newnodeptr)
|
||
|
{
|
||
|
return(add(node,length(),newnodeptr));
|
||
|
}
|
||
|
|
||
|
|
||
|
// Remove at the zero based index specified by 'pos'. When removing from
|
||
|
// a slot, all others get shifted up by one.
|
||
|
template <class T>
|
||
|
bit8 LinkedList<T>::remove(OUT T &node, sint32 pos)
|
||
|
{
|
||
|
////////LNode<T> *temp;
|
||
|
LNode<T> *item;
|
||
|
|
||
|
if (Entries==0)
|
||
|
return(FALSE);
|
||
|
|
||
|
if (pos<0)
|
||
|
pos=0;
|
||
|
if (pos>=Entries)
|
||
|
pos=Entries-1;
|
||
|
|
||
|
if ((pos==0)||(pos==Entries-1)) { // Both can be true for a 1 item list
|
||
|
if (pos==0) {
|
||
|
item=Head;
|
||
|
if (item->Next)
|
||
|
item->Next->Prev=NULL;
|
||
|
Head=item->Next;
|
||
|
node=item->Node;
|
||
|
Current=Head;
|
||
|
CurIndex=0;
|
||
|
}
|
||
|
if (pos==Entries-1) {
|
||
|
item=Tail;
|
||
|
if (item->Prev)
|
||
|
item->Prev->Next=NULL;
|
||
|
Tail=item->Prev;
|
||
|
node=item->Node;
|
||
|
Current=Tail;
|
||
|
CurIndex=Entries-2;
|
||
|
}
|
||
|
delete(item);
|
||
|
Entries--;
|
||
|
|
||
|
if (Entries==0) { // Super paranoia check
|
||
|
assert(Current==NULL);
|
||
|
assert(CurIndex==-1);
|
||
|
assert(Head==NULL);
|
||
|
assert(Tail==NULL);
|
||
|
}
|
||
|
return(TRUE);
|
||
|
}
|
||
|
// If control is here, we know the target node is not an endpoint
|
||
|
|
||
|
// Check for possible speedup, so we don't have to scan the list
|
||
|
if (pos==CurIndex) {
|
||
|
item=Current;
|
||
|
item->Prev->Next=item->Next;
|
||
|
item->Next->Prev=item->Prev;
|
||
|
Current=item->Next;
|
||
|
// CurIndex stays the same
|
||
|
node=item->Node;
|
||
|
delete(item);
|
||
|
Entries--;
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
// Check the other possible speedup (removing after CurIndex)
|
||
|
if (pos==CurIndex+1) {
|
||
|
item=Current->Next;
|
||
|
item->Prev->Next=item->Next;
|
||
|
item->Next->Prev=item->Prev;
|
||
|
Current=item->Next;
|
||
|
CurIndex++;
|
||
|
node=item->Node;
|
||
|
delete(item);
|
||
|
Entries--;
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
|
||
|
// If control reaches here we have to scan the whole thing
|
||
|
item=Head->Next; // Can start at node '1' because head was special cased
|
||
|
for (int i=1; i<pos; i++) {
|
||
|
item=item->Next;
|
||
|
assert(item!=NULL);
|
||
|
}
|
||
|
|
||
|
item->Prev->Next=item->Next;
|
||
|
item->Next->Prev=item->Prev;
|
||
|
Current=item->Next;
|
||
|
CurIndex=pos;
|
||
|
node=item->Node;
|
||
|
delete(item);
|
||
|
Entries--;
|
||
|
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// Remove at the zero based index specified by 'pos'. When removing from
|
||
|
// a slot, all others get shifted up by one.
|
||
|
template <class T>
|
||
|
bit8 LinkedList<T>::remove(sint32 pos)
|
||
|
{
|
||
|
T temp_node;
|
||
|
return(remove(temp_node,pos));
|
||
|
}
|
||
|
|
||
|
|
||
|
// Remove the first node of the list
|
||
|
template <class T>
|
||
|
bit8 LinkedList<T>::removeHead(OUT T &node)
|
||
|
{
|
||
|
return(remove(node,0));
|
||
|
}
|
||
|
|
||
|
|
||
|
// Remove the last node of the list
|
||
|
template <class T>
|
||
|
bit8 LinkedList<T>::removeTail(OUT T &node)
|
||
|
{
|
||
|
return(remove(node,Entries-1));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
template <class T>
|
||
|
bit8 LinkedList<T>::get(OUT T &node, sint32 pos)
|
||
|
{
|
||
|
T *objptr;
|
||
|
bool retval=getPointer(&objptr,pos);
|
||
|
if (retval && objptr)
|
||
|
node=*objptr;
|
||
|
|
||
|
return(retval);
|
||
|
}
|
||
|
|
||
|
|
||
|
template <class T>
|
||
|
bit8 LinkedList<T>::getPointer(OUT T **node,sint32 pos)
|
||
|
{
|
||
|
if ((node==0)||(Entries==0))
|
||
|
return(FALSE);
|
||
|
|
||
|
LNode<T> *item;
|
||
|
|
||
|
if (pos<0)
|
||
|
{
|
||
|
//pos=0;
|
||
|
return(FALSE);
|
||
|
}
|
||
|
if (pos>=Entries)
|
||
|
{
|
||
|
//pos=Entries-1;
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
if (pos==0) {
|
||
|
*node=&(Head->Node);
|
||
|
return(TRUE);
|
||
|
} else if (pos==Entries-1) {
|
||
|
*node=&(Tail->Node);
|
||
|
return(TRUE);
|
||
|
}
|
||
|
// If control reaches here, we know target is not an endpoint
|
||
|
|
||
|
// Check for possible speedup, so we don't have to scan the list
|
||
|
if (pos==CurIndex) {
|
||
|
*node=&(Current->Node);
|
||
|
return(TRUE);
|
||
|
} else if (pos==CurIndex+1) {
|
||
|
*node=&(Current->Next->Node);
|
||
|
CurIndex++;
|
||
|
Current=Current->Next;
|
||
|
return(TRUE);
|
||
|
} else if (pos==CurIndex-1) {
|
||
|
*node=&(Current->Prev->Node);
|
||
|
CurIndex--;
|
||
|
Current=Current->Prev;
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
// If control reaches here we have to scan the whole thing
|
||
|
item=Head->Next; // Can start at node '1' because head was special cased
|
||
|
for (int i=1; i<pos; i++) {
|
||
|
item=item->Next;
|
||
|
assert(item!=NULL);
|
||
|
}
|
||
|
*node=&(item->Node);
|
||
|
CurIndex=pos;
|
||
|
Current=item;
|
||
|
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Remove the first node of the list
|
||
|
template <class T>
|
||
|
bit8 LinkedList<T>::getHead(OUT T &node)
|
||
|
{
|
||
|
return(get(node,0));
|
||
|
}
|
||
|
|
||
|
|
||
|
// Remove the last node of the list
|
||
|
template <class T>
|
||
|
bit8 LinkedList<T>::getTail(OUT T &node)
|
||
|
{
|
||
|
return(get(node,Entries-1));
|
||
|
}
|
||
|
|
||
|
|
||
|
template <class T>
|
||
|
void LinkedList<T>::print(IN FILE *out)
|
||
|
{
|
||
|
LNode<T> *temp;
|
||
|
|
||
|
fprintf(out,"--------------------\n");
|
||
|
fprintf(out,"Entries = %d\n",length());
|
||
|
fprintf(out,"H = %8p C = %8p (%d) T = %8p\n",Head,Current,CurIndex,Tail);
|
||
|
|
||
|
temp=Head;
|
||
|
while (temp) {
|
||
|
fprintf(out," %8p<-((%8p))->%8p \n",temp->Prev,temp,temp->Next);
|
||
|
temp=temp->Next;
|
||
|
}
|
||
|
|
||
|
fprintf(out,"--------------------\n");
|
||
|
}
|
||
|
|
||
|
// Return the current length of the list
|
||
|
template <class T>
|
||
|
sint32 LinkedList<T>::length(void) {
|
||
|
return(Entries);
|
||
|
}
|
||
|
|
||
|
#endif
|