/*
	SyncStarmap.
	By SL (shadowlord13@gmail.com)
	March-February 2006.
	Changed FRI 13th of April (Aiie! Friday the 13th!) to point to http://home.deds.nl/~megagun/starmap/ instead of the old ecwhost domain.
	
	This compiles to these programs:
	1) SyncStarmap - downloads and imports SM3 inboxes, writes SM3 outboxes. (No extra defines are defined)
	2) CreateInbox3 - Creates SM3 inboxes from SM3 outboxes. (CREATEINBOX3 is defined)
	
	Also, as of Feb 1, 2006, SyncStarmap now writes SM2 for NoctisMapper when it finishes running. The
		code which does that is named 'Styracosaurus'. (It's small because it uses the numerous functions already coded for SyncStarmap,
		as well as its data structures, which makes the code for Styracosaurus fairly small (about 70 lines) compared to
		Triceratops (about 373 lines of code). Of course, Triceratops was written long before SyncStarmap...
		
	probably never TODO: 3) Protoceratops - Converts a SM1 inbox (or outbox, since the file format is the same) to a SM3 outbox (which can then be imported and turned into a SM3 inbox by CreateInbox3). (PROTOCERATOPS would be defined)
	
*/

/*
Here's what SyncStarmap and CreateInbox3 do:	
	
	SyncStarmap:	
		Open syncStarmap.ini, read username, password, path to data files (defaults to data).
		Load starmap3.map into memory.
		Load starmap3.gd into memory.
		Load starmap3.usr into memory.
		Download http://home.deds.nl/~megagun/starmap/starmap-version.txt, read version # from it
		Check starmap3.usr for our user ID#. It might not be there.
		For each version newer than our current version # (if any):
			Download inbox from http://home.deds.nl/~megagun/starmap/
			Unzip inbox.7z to inbox.sm3, open inbox.sm3.
			Read version #, and number of map, guide, and user entries.
			Read user entries and merge them.
				If our username is in the entries to be merged, record its user ID#.
			Read map entries and merge them:
				For each entry:
					If the discoverer (in the inbox) is us (matches our previously recorded user ID#), unmark 'Sent but not received' if it is set.
					If the entry in the inbox is marked 'local only' carry that over to our copy.
					Don't overwrite an entry of our own if we have 'include in outbox' set.
					If the discoverer is NOT us, but we have the object in our map already, make a linked entry for the new one.
			Read guide entries and merge them:
			Update version number and save map, guide, and user files. Reload files if necessary.
		Create outbox.sm3 file:
			Write version, username, and password, and 0 for the map and guide num entries.
			Find entries in starmap3 which have 'include in outbox' or 'sent but not received' but NOT 'local only', to go in the outbox.
				Write each one out
				Locally, mark each one 'sent but not received', and unmark 'include in outbox'.
			Find entries in guide which have 'include in outbox' or 'sent but not received' but NOT 'local only', to go in the outbox.
				Write each one out
				Locally, mark each one 'sent but not received', and unmark 'include in outbox'.
			Set map and guide num entries in the file.
			Close file
			Zip outbox to outbox.7z. Delete outbox.sm3.
		Tell user to send outbox.7z to jorisvddonk@gmail.com
		
	CreateInbox3:
		Open createInbox.ini, read path to data files. (defaults to data\original)
		Load starmap3.map into memory.
		Load starmap3.gd into memory.
		Load starmap3.svr into memory.
		Increment internal version number.
		Create inbox-#.sm3 file and write out the new version number to it.
		
		For each outbox.### file, open it and:
			Read version, username, and password, and the number of map and guide entries.
			If it's a new username, at it to our list of new users and give it a number.
			Read map entries and merge them:
				For each entry:
					Look for the same object in our current starmap. If found, check it and all linked ones to see if the discoverer already has an entry for that object. If so, make their changes to it and mark it with 'include in inbox'.
					If the discoverer doesn't have an entry for this star yet:
						If there are no entries for it already:
							Make a new entry and mark it 'include in inbox'.
						If there are one or two entries for it already:
							Make a new entry, mark it 'include in inbox', and link the old entry to it (as an alternate name).
						If there are three entries for it:
							Ignore the new one.
			Read guide entries and merge them:
				Add it, or mark it deleted (and mark it 'include in inbox').
		Take 'include in inbox' users and write them to the inbox, then unmark 'include in inbox' on them.
		Take 'include in inbox' starmap entries and write them to the inbox, then unmark 'include in inbox' on them.
		Take 'include in inbox' guide entries and write them to the inbox, then unmark 'include in inbox' on them.
		Zip the inbox with 7zip.
*/

char usernameBuffer[21];
char passwordBuffer[21];
char folderPath[256];

#include <stdlib.h>
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <urlmon.h>
#include <sys/stat.h>
#include <stdarg.h>	//for DebugPrintf and RunCommand (SL)
#define WINDOWS
#include "defs.h"
#include "randalg.h"
#include "genalg.h"

void __cdecl DebugPrintf(const char * format, ...) {
	#ifdef SYNCSMDEBUG
		char stringBuffer[1024];
		va_list argptr;
		va_start(argptr, format);
		vprintf(format, argptr);
		va_end(argptr);
	#endif
}

#pragma pack(1)
struct Starmap3StarEntry;
struct Starmap3BodyEntry;

class Starmap3GenericEntry {
	public:
	Udword idx;
	Udword filePosition;
	Uchar flags;
	Udword discoverer;
	char name[20];
	union {
		Udword starIdx;
		Udword bodyIdx;
	};
	
	Starmap3StarEntry * star();
	Starmap3BodyEntry * body();
	Udword prevCount();
};

class Starmap3StarEntry {
	public:
	Udword idx;
	double id;
	Dword sx;
	Dword sy;
	Dword sz;
	Uword data;
	union {
		Udword nextAlternateIdx;
		Udword nextAlternateFP;
	};
	Udword prevAlternateIdx;
		
	Starmap3GenericEntry * nextAlternate();
	Starmap3GenericEntry * prevAlternate();
};

class Starmap3BodyEntry {
	public:
	Udword idx;
	Uword data;
	union {
		Udword nextAlternateIdx;
		Udword nextAlternateFP;
	};
	Udword prevAlternateIdx;
	
	union {
		Udword starIdx;
		Udword starFP;
	};
	
	Starmap3GenericEntry * star();
	Starmap3GenericEntry * nextAlternate();
	Starmap3GenericEntry * prevAlternate();
};

class Starmap3Pointer {
	public:
	Udword entryIdx;
	
	Starmap3GenericEntry *entry();
};

class GenericPointer {
	public:
	void *entry;
} sparePointer;

struct StarmapSearchEntry {
	Dword sx;
	Dword sy;
	Dword sz;
	Uchar inflatedBodyNumber;
	Bool deleted;
	
	char name[20];
};
#pragma pack()

#pragma pack(1)
struct Guide3Entry {
	Uchar flags;
	Dword subjectsx;
	Dword subjectsy;
	Dword subjectsz;
	Uword subject_pNum;
	Uword subject_mNum;
	Udword writer;	//the person who wrote the entry
	char entry[76];
};

struct User3Entry {
	Udword id;	//the person who wrote the entry
	char name[20];
	#ifdef CREATEINBOX3
	Uchar flags;
	char password[20];
	Udword entriesSubmitted;
	Udword invalidEntriesSubmitted;
	Udword badEntriesSubmitted;
	#endif
};

#pragma pack()

Starmap3GenericEntry *genericStarmapEntries = NULL;
Starmap3StarEntry *starmapStars = NULL;
Starmap3BodyEntry *starmapBodies = NULL;
Starmap3GenericEntry *search3Key = NULL;
Starmap3StarEntry *search3SKey = NULL;
Starmap3BodyEntry *search3BKey = NULL;
StarmapSearchEntry *searchKey = NULL;
Guide3Entry *guideEntries = NULL;
User3Entry *userEntries = NULL;
Starmap3GenericEntry *localOnlyStarmapEntries = NULL;

Udword searchFPKey = 0;

Starmap3StarEntry * Starmap3GenericEntry::star() {
	return &(starmapStars[starIdx]);
}
Starmap3BodyEntry * Starmap3GenericEntry::body() {
	return &(starmapBodies[bodyIdx]);
}
Starmap3GenericEntry * Starmap3StarEntry::nextAlternate() {
	if (nextAlternateIdx==0xffffffff) return NULL;
	return &(genericStarmapEntries[nextAlternateIdx]);
}
Starmap3GenericEntry * Starmap3BodyEntry::nextAlternate() {
	if (nextAlternateIdx==0xffffffff) return NULL;
	return &(genericStarmapEntries[nextAlternateIdx]);
}
Starmap3GenericEntry * Starmap3StarEntry::prevAlternate() {
	if (prevAlternateIdx==0xffffffff) return NULL;
	return &(genericStarmapEntries[prevAlternateIdx]);
}
Starmap3GenericEntry * Starmap3BodyEntry::prevAlternate() {
	if (prevAlternateIdx==0xffffffff) return NULL;
	return &(genericStarmapEntries[prevAlternateIdx]);
}
Starmap3GenericEntry * Starmap3BodyEntry::star() {
	return &(genericStarmapEntries[starIdx]);
}
Starmap3GenericEntry * Starmap3Pointer::entry() {
	return &(genericStarmapEntries[entryIdx]);	
}

Udword Starmap3GenericEntry::prevCount() {
	Udword count = 0;
	if (flags&0x20) {
		return 0;	//Prev/next has no meaning for deleted entries.
	}
	Starmap3GenericEntry *temp = this;
	if (flags&0x8) {
		while (temp->star()->prevAlternateIdx!=0xffffffff) {
			if (temp->star()->prevAlternate()==temp) {
				DebugPrintf("ERROR: Circular reference in star entry at %x [%.20s]!\n", temp->filePosition, temp->name);
				return count;
			}
			temp=temp->star()->prevAlternate();
			count++;
		}
	} else {
		while (temp->body()->prevAlternateIdx!=0xffffffff) {
			temp=temp->body()->prevAlternate();
			if (temp->body()->prevAlternate()==temp) {
				DebugPrintf("ERROR: Circular reference in body entry at %x [%.20s]!\n", temp->filePosition, temp->name);
				return count;
			}
			count++;
		}
	}
	return count;
}

long long numGSEntries=0;
long long maxGSEntries=0;
long long numSSEntries=0;
long long maxSSEntries=0;
long long numBSEntries=0;
long long maxBSEntries=0;
long long numGEntries=0;
long long maxGEntries=0;
long long numUEntries=0;
long long maxUEntries=0;
long long sortedNumGSEntries=0;
long long sortedNumGEntries=0;
long long sortedNumUEntries=0;

//localOnlyStarmapEntries
long long numLOSEntries=0;
long long maxLOSEntries=0;

Dword starmap3NextRecordPos;

Uchar StellarClass(Uword data) {
	return data&0xF;
}
Uchar NumPlanets(Uword data) {
	return (data>>4)&0x1F;
}
Uchar NumMoons(Uword data) {
	return (data>>9)&0x7F;
}
Uchar PlanetType(Uword data) {
	return data&0xF;
}
Uchar PlanetNumber(Uword data) {
	return (data>>4)&0x1F;
}
Uchar MoonNumber(Uword data) {
	return (data>>9)&0x1F;
}
Uchar CalcAndVerifyBodyNumber(Uchar pN, Uchar mN, Dword sx, Dword sy, Dword sz) {
	Uchar retval = 0xff;	//If we don't find it, it's invalid.
	PrepareFromSectorCoords((double)sx, (double)sy, (double)sz);
	extract_foundstar_infos();
	prepare_foundstar();
	DebugPrintf("foundstar_nop=%i foundstar_nob=%i pN=%i mN=%i\n", foundstar_nop, foundstar_nob, pN, mN);
	if (mN==0x1F) {
		if (pN>=0 && pN<foundstar_nop) {
			retval = pN;
		} // else we didn't find it, so it's invalid.
	} else {
		for (Uchar body = foundstar_nop; body<foundstar_nob; body++) {
			if (foundstar_p_owner[body]==pN && foundstar_p_moonid[body]==mN) {
				retval=body;
				break;
			}
		} // If we don't break, retval isn't set (we didn't find it, so it's invalid).
	}
	return retval;
}

Uchar BodyNumber(Starmap3BodyEntry *a) {
	Uchar retval = 0;
	Uchar pN = PlanetNumber(a->data);
	Uchar mN = MoonNumber(a->data);
	if (mN==0x1F) {
		retval = pN;
	} else {
		Starmap3StarEntry *star = a->star()->star();
		PrepareFromSectorCoords(star->sx, star->sy, star->sz);
		extract_foundstar_infos();
		prepare_foundstar();
		for (Uchar body = foundstar_nop; body<foundstar_nob; body++) {
			if (foundstar_p_owner[body]==pN && foundstar_p_moonid[body]==mN) {
				retval=body;
				break;
			}
		}
	}
	return retval;
}

void GetSortVars(Starmap3GenericEntry *a, double *id, int *pos) {
	if (a->flags&0x8) {
		*id = a->star()->id;
		*pos=0;
	} else {
		*id = a->body()->star()->star()->id;
		Uchar pN = PlanetNumber(a->body()->data);
		Uchar mN = MoonNumber(a->body()->data);
		if (mN!=0x1F) {
			*pos = 21+((1+pN)*20)+mN;
		} else {
			*pos = pN+1;
		}
	}
}

int GetInflatedBodyNumber(Uword data) {
	Uchar pN = PlanetNumber(data);
	Uchar mN = MoonNumber(data);
	if (mN!=0x1F) {
		return 21+((1+pN)*20)+mN;
	} else {
		return pN+1;
	}
}

static int __CLIB CompareSM3E(const void* aa, const void* ab) {
	Starmap3GenericEntry *a = ((Starmap3GenericEntry*)aa);
	Starmap3GenericEntry *b = ((Starmap3GenericEntry*)ab);
	double id1, id2;
	int pos1, pos2;
	GetSortVars(a, &id1, &pos1);
	GetSortVars(b, &id2, &pos2);
	Udword ap = a->prevCount();
	Udword bp = b->prevCount();
	
	int retval = 0;
	if ((a->flags&0x20) && (b->flags&0x20)==0) {
		retval=-1;
	} else if ((a->flags&0x20)==0 && (b->flags&0x20)) {
		retval=1;
	} else if (id1 < id2) {
		retval=-1;
	} else if (id1 > id2) {
		retval=1;
	} else if (pos1 < pos2) {
		retval=-1;
	} else if (pos1 > pos2) {
		retval=1;
	} else if (ap < bp) {
		retval=-1;
	} else if (ap > bp) {
		retval=1;
	}
	return retval;
}

static int __CLIB ComparePointerFP(const void *aa, const void *ab) {
	Starmap3Pointer *a = ((Starmap3Pointer*)aa);
	Starmap3Pointer *b = ((Starmap3Pointer*)ab);
	int retval=0;
	if (a->entry()->filePosition < b->entry()->filePosition) {
		retval=-1;
	} else if (a->entry()->filePosition > b->entry()->filePosition) {
		retval=1;
	}
	return retval;
}
static int __CLIB CompareFPPointer(const void *aa, const void *ab) {
	Udword* a = ((Udword*)aa);
	Starmap3Pointer *b = ((Starmap3Pointer*)ab);
	int retval=0;
	if (*a < b->entry()->filePosition) {
		retval=-1;
	} else if (*a > b->entry()->filePosition) {
		retval=1;
	}
	//DebugPrintf("retval is %i. a is %x, *a is %x, befp=%x.\n", retval, a, *a, b->entry()->filePosition);
	return retval;
}


static int __CLIB ComparePos(const void *aa, const void *ab) {
	Starmap3Pointer *a = ((Starmap3Pointer*)aa);
	Starmap3Pointer *b = ((Starmap3Pointer*)ab);
	//DebugPrintf("Comparing %08x and %08x.\n", (Udword)a, (Udword)b);
	int retval=0;
	Dword asx, asy, asz, bsx, bsy, bsz;
	Udword ap;
	Udword bp;
	if (a->entry()->flags&0x8) {
		//DebugPrintf"AP1a\n");
		asx = a->entry()->star()->sx;
		//DebugPrintf"AP1b\n");
		asy = a->entry()->star()->sy;
		//DebugPrintf"AP1c\n");
		asz = a->entry()->star()->sz;
		//DebugPrintf"AP1d\n");
		ap = a->entry()->prevCount();
		//DebugPrintf"AP1e\n");
	} else {
		//DebugPrintf"AP2a\n");
		asx = a->entry()->body()->star()->star()->sx;
		//DebugPrintf"AP2b\n");
		asy = a->entry()->body()->star()->star()->sy;
		//DebugPrintf"AP2c\n");
		asz = a->entry()->body()->star()->star()->sz;
		//DebugPrintf"AP2d\n");
		ap = a->entry()->prevCount();
		//DebugPrintf"AP2e\n");
	}
	if (b->entry()->flags&0x8) {
		//DebugPrintf"BP1a\n");
		bsx = b->entry()->star()->sx;
		//DebugPrintf"BP1b\n");
		bsy = b->entry()->star()->sy;
		//DebugPrintf"BP1c\n");
		bsz = b->entry()->star()->sz;
		//DebugPrintf"BP1d\n");
		bp = b->entry()->prevCount();
		//DebugPrintf"BP1e\n");
	} else {
		//DebugPrintf"BP2a\n");
		bsx = b->entry()->body()->star()->star()->sx;
		//DebugPrintf"BP2b\n");
		bsy = b->entry()->body()->star()->star()->sy;
		//DebugPrintf"BP2c\n");
		bsz = b->entry()->body()->star()->star()->sz;
		//DebugPrintf"BP2d\n");
		bp = b->entry()->prevCount();
		//DebugPrintf"BP2e\n");
	}
	//DebugPrintf"BP3a\n");
	if ((a->entry()->flags&0x20) && (b->entry()->flags&0x20)==0) {
		retval=-1;
	} else if ((a->entry()->flags&0x20)==0 && (b->entry()->flags&0x20)) {
		retval=1;
	} else if (asx < bsx) {
		retval=-1;
	} else if (asx > bsx) {
		retval=1;
	} else if (asy < bsy) {
		retval=-1;
	} else if (asy > bsy) {
		retval=1;
	} else if (asz < bsz) {
		retval=-1;
	} else if (asz > bsz) {
		retval=1;
	} else {
		double id;
		int a_inflatedBodyNumber;
		GetSortVars(a->entry(), &id, &a_inflatedBodyNumber);
		int b_inflatedBodyNumber;
		GetSortVars(b->entry(), &id, &b_inflatedBodyNumber);
		if (a_inflatedBodyNumber < b_inflatedBodyNumber) {
			retval=-1;
		} else if (a_inflatedBodyNumber > b_inflatedBodyNumber) {
			retval=1;
		} else if (ap < bp) {
			retval=-1;
		} else if (ap > bp) {
			retval=1;
		}
	}
	//DebugPrintf"BP3b\n");
	return retval;
}

static int __CLIB FindPosEntry(const void *aa, const void *ab) {
	StarmapSearchEntry *a = ((StarmapSearchEntry*)aa);
	Starmap3GenericEntry *b = ((Starmap3GenericEntry*)ab);
	int retval=0;
	Dword bsx, bsy, bsz;
	Udword ap = 0;
	Udword bp;
	if (b->flags&0x8) {
		bsx = b->star()->sx;
		bsy = b->star()->sy;
		bsz = b->star()->sz;
		bp = b->prevCount();
	} else {
		bsx = b->body()->star()->star()->sx;
		bsy = b->body()->star()->star()->sy;
		bsz = b->body()->star()->star()->sz;
		bp = b->prevCount();
	}
	if ((a->deleted)==True && (b->flags&0x20)==0) {
		retval=-1;
	} else if ((a->deleted)==False && (b->flags&0x20)) {
		retval=1;
	} else if (a->sx < bsx) {
		retval=-1;
	} else if (a->sx > bsx) {
		retval=1;
	} else if (a->sy < bsy) {
		retval=-1;
	} else if (a->sy > bsy) {
		retval=1;
	} else if (a->sz < bsz) {
		retval=-1;
	} else if (a->sz > bsz) {
		retval=1;
	} else {
		double id;
		int b_inflatedBodyNumber;
		GetSortVars(b, &id, &b_inflatedBodyNumber);
		if (a->inflatedBodyNumber < b_inflatedBodyNumber) {
			retval=-1;
		} else if (a->inflatedBodyNumber > b_inflatedBodyNumber) {
			retval=1;
		} else if (ap < bp) {
			retval=-1;
		} else if (ap > bp) {
			retval=1;
		}
	}
	return retval;
}

static int __CLIB FindPos(const void *aa, const void *ab) {
	StarmapSearchEntry *a = ((StarmapSearchEntry*)aa);
	Starmap3Pointer *b = ((Starmap3Pointer*)ab);
	return FindPosEntry(a, b->entry());
}

/*
void *	__CLIB bsearch(const void *,const void *,size_t,size_t,
	int (__CLIB *)(const void *,const void *));
*/

//Returned values:
//0: Not found
//1: Found by binary search, 'result' contains a pointer into the firstArray.
//-1: Found by second search, 'result' contains a pointer into the secondArray.
int __CLIB combinedSearch(const void * key, const void * firstArray, Dword numSortedEntries, Dword firstArrayElementSize,
	int cdecl firstCompareFunction(const void *,const void *), void * secondArray, Dword secondArrayStartPos, Dword secondArrayNumEntries,
	Dword secondArrayElementSize, int cdecl secondCompareFunction (const void *,const void *), void ** result)
{
	int resultVal=0;
	*result = bsearch(key, firstArray, numSortedEntries, firstArrayElementSize, firstCompareFunction);
	if (*result==NULL) {
		Uchar * elementPtr = (((Uchar*)secondArray)+secondArrayStartPos*secondArrayElementSize);
		for (int idx=secondArrayStartPos; idx<secondArrayNumEntries; idx++) {
			DebugPrintf("Checking idx=%i of %i to %i. elementPtr=%x\n", idx, secondArrayStartPos, secondArrayNumEntries, elementPtr);
			int val = secondCompareFunction(key, elementPtr);
			if (val==0) {
				*result=elementPtr;
				resultVal=-1;
				break;
			} else {
				elementPtr+=secondArrayElementSize;
			}
		}
	} else {
		resultVal=1;
	}
	return resultVal;
}
	
static int __CLIB CompareName(const void *aa, const void *ab) {
	Starmap3Pointer *a = ((Starmap3Pointer*)aa);
	Starmap3Pointer *b = ((Starmap3Pointer*)ab);
	return memcmp(a->entry()->name, b->entry()->name, 20);
}

static int __CLIB FindName(const void *aa, const void *ab) {
	StarmapSearchEntry *a = ((StarmapSearchEntry*)aa);
	Starmap3Pointer *b = ((Starmap3Pointer*)ab);
	return memcmp(a->name, b->entry()->name, 20);
}

int ReadLine(FILE *inFile, char *string, int max) {
	//Reads a line of text from a text file:
	int nextByte = fgetc(inFile);
	int pos = 0;
	while (nextByte=='\n' || nextByte=='\r') {
		nextByte = fgetc(inFile);
	}

	//Loop until we reach a new-line character:
	while (nextByte!='\n' && nextByte!='\r' && pos<max) {
		//Check for end of file:
		if (nextByte==EOF) {
			string[pos]='\0';
			return 0;
		}
		string[pos++]=(char)nextByte;
		nextByte = fgetc(inFile);
	}

	string[pos]='\0';
	return 1;
}

Starmap3GenericEntry *ExtractGenericEntry(int resVal, void *result) {
	int resultcount=0;
	Starmap3GenericEntry *entry = NULL;
	if (resVal==1) {
		if (result!=NULL) {
			Starmap3Pointer *resultSP = (Starmap3Pointer *)result;
			entry = resultSP->entry();
		}
	} else if (resVal==-1) {
		if (result!=NULL) {
			entry = (Starmap3GenericEntry*) result;
		}
	}
	return entry;
}					

Starmap3GenericEntry * NewGenericEntry(Udword curFPos, Uchar flags, Udword discoverer, char *name) {
	if (numGSEntries>=maxGSEntries) {
		Starmap3GenericEntry *newEntries = new Starmap3GenericEntry[maxGSEntries+1024];
		memcpy(newEntries, genericStarmapEntries, maxGSEntries*sizeof(Starmap3GenericEntry));
		delete [] genericStarmapEntries;
		genericStarmapEntries = newEntries;
		maxGSEntries+=1024;
	}
	int index = numGSEntries;
	numGSEntries++;
	Starmap3GenericEntry *entry = &(genericStarmapEntries[index]);
	entry->idx=index;
	entry->filePosition=curFPos;
	entry->flags=flags;
	entry->discoverer=discoverer;
	memcpy(entry->name, name, 20);
	return entry;
}

Starmap3GenericEntry * NewLocalOnlyEntry(Uchar flags, Udword discoverer, char *name) {
	if (numLOSEntries>=maxLOSEntries) {
		Starmap3GenericEntry *newEntries = new Starmap3GenericEntry[maxLOSEntries+1024];
		memcpy(newEntries, localOnlyStarmapEntries, maxLOSEntries*sizeof(Starmap3GenericEntry));
		delete [] localOnlyStarmapEntries;
		localOnlyStarmapEntries = newEntries;
		maxLOSEntries+=1024;
	}
	int index = numLOSEntries;
	numLOSEntries++;
	Starmap3GenericEntry *entry = &(localOnlyStarmapEntries[index]);
	entry->idx=index;
	entry->filePosition=0;
	entry->flags=flags;
	entry->discoverer=discoverer;
	memcpy(entry->name, name, 20);
	return entry;
}

Starmap3StarEntry * NewStarEntry(Dword sx, Dword sy, Dword sz, Uword data, Udword nextAlternateFP) {
	if (numSSEntries>=maxSSEntries) {
		Starmap3StarEntry *newEntries = new Starmap3StarEntry[maxSSEntries+1024];
		memcpy(newEntries, starmapStars, maxSSEntries*sizeof(Starmap3StarEntry));
		delete [] starmapStars;
		starmapStars = newEntries;
		maxSSEntries+=1024;
	}
	int index = numSSEntries;
	numSSEntries++;
	Starmap3StarEntry *entry = &(starmapStars[index]);
	entry->idx=index;
	entry->sx=sx;
	entry->sy=sy;
	entry->sz=sz;
	entry->data=data;
	entry->nextAlternateFP=nextAlternateFP;
	entry->prevAlternateIdx=0xffffffff;
	return entry;
}

Starmap3BodyEntry * NewBodyEntry(Udword starFP, Uword data, Udword nextAlternateFP) {
	if (numBSEntries>=maxBSEntries) {
		Starmap3BodyEntry *newEntries = new Starmap3BodyEntry[maxBSEntries+1024];
		memcpy(newEntries, starmapBodies, maxBSEntries*sizeof(Starmap3BodyEntry));
		Starmap3BodyEntry *ptr;
		/*for (int index=0; index<numGSEntries; index++) {
			if (!(genericStarmapEntries[index].flags&0x8)) {
				genericStarmapEntries[index].body -= (Uqword) starmapBodies;
				genericStarmapEntries[index].body += (Uqword) newEntries;
			}
		}*/
		delete [] starmapBodies;
		starmapBodies = newEntries;
		maxBSEntries+=1024;
	}
	int index = numBSEntries;
	numBSEntries++;
	Starmap3BodyEntry *entry = &(starmapBodies[index]);
	entry->idx=index;
	entry->starFP=starFP;
	entry->data=data;
	entry->nextAlternateFP=nextAlternateFP;
	entry->prevAlternateIdx=0xffffffff;
	return entry;
}

Guide3Entry * NewGuideEntry(Uchar flags, Dword subjectsx, Dword subjectsy, Dword subjectsz, Uword subject_pNum, Uword subject_mNum, Udword writer, char *text) {
	if (numGEntries>=maxGEntries) {
		Guide3Entry *newEntries = new Guide3Entry[maxGEntries+1024];
		memcpy(newEntries, guideEntries, maxGEntries*sizeof(Guide3Entry));
		delete [] guideEntries;
		guideEntries = newEntries;
		maxGEntries+=1024;
	}
	int index = numGEntries;
	numGEntries++;
	Guide3Entry *entry = &(guideEntries[index]);
	entry->flags=flags;
	entry->writer=writer;
	entry->subjectsx=subjectsx;
	entry->subjectsy=subjectsy;
	entry->subjectsz=subjectsz;
	entry->subject_pNum=subject_pNum;
	entry->subject_mNum=subject_mNum;	
	memcpy(entry->entry, text, 76);
	return entry;
}

#ifdef CREATEINBOX3		
int NewUserEntry(Udword id, char *name, Uchar flags, char *password, Udword entriesSubmitted, Udword invalidEntriesSubmitted, Udword badEntriesSubmitted) {
#else
int NewUserEntry(Udword id, char *name) {
#endif
	if (numUEntries>=maxUEntries) {
		User3Entry *newEntries = new User3Entry[maxUEntries+1024];
		memcpy(newEntries, userEntries, maxUEntries*sizeof(User3Entry));
		delete [] userEntries;
		userEntries = newEntries;
		maxUEntries+=1024;
	}
	int index = numUEntries;
	numUEntries++;
	User3Entry *entry = &(userEntries[index]);
	entry->id=id;
	memcpy(entry->name, name, 20);
	#ifdef CREATEINBOX3
	entry->flags=flags;
	memcpy(entry->password, password, 20);
	entry->entriesSubmitted=entriesSubmitted;
	entry->invalidEntriesSubmitted=invalidEntriesSubmitted;
	entry->badEntriesSubmitted=badEntriesSubmitted;
	#endif
	return index;
}

void FixFilePosRefs(int numGSEntries, Starmap3GenericEntry *genericStarmapEntries) {
	//Make an array of pointers to sort by FP, so we can quickly find entries by FP.
	
	Starmap3Pointer * pointers = new Starmap3Pointer[numGSEntries];
	for (int a=0; a<numGSEntries; a++) {
		pointers[a].entryIdx = a;
	}
	qsort(pointers, numGSEntries, sizeof(Starmap3Pointer),
		(int (__CLIB *)
		(const void *,const void *))ComparePointerFP);
	
	for (int g=0; g<numGSEntries; g++) {
		Starmap3GenericEntry *generic = &(genericStarmapEntries[g]);
		if (generic->flags&0x8) {	//star
			if (generic->star()->nextAlternateFP!=0) {
				searchFPKey = generic->star()->nextAlternateFP;
				Starmap3Pointer *result = (Starmap3Pointer*) bsearch(&searchFPKey, pointers, numGSEntries, sizeof(Starmap3Pointer),
						(int (__CLIB *)
						(const void *,const void *))CompareFPPointer);
				if (result==NULL) {
					generic->star()->nextAlternateIdx=0xffffffff;
					printf("Unexpectedly, there is no entry at FP %x! Star %.20s refers to this file position for its alternate FP.\n", searchFPKey, generic->name);
				} else if (result->entry()->flags&0x8) {
						generic->star()->nextAlternateIdx = result->entryIdx;
						generic->star()->nextAlternate()->star()->prevAlternateIdx = generic->idx;
				} else {
					generic->star()->nextAlternateIdx=0xffffffff;
					printf("Unexpectedly, the entry at FP %x is the wrong type! Star %.20s refers to this file position for its alternate FP, but the entry there is for a body instead (%0.20s).\n", searchFPKey, generic->name, result->entry()->name);
				}
			} else {
				generic->star()->nextAlternateIdx=0xffffffff;
			}
		} else {
			if (generic->body()->nextAlternateFP!=0) {
				searchFPKey = generic->body()->nextAlternateFP;
				//DebugPrintf("searchFPKey=%x and g->b->naFP=%x, g is %i.\n", searchFPKey, generic->body()->nextAlternateFP, g);
				Starmap3Pointer *result = (Starmap3Pointer*) bsearch(&searchFPKey, pointers, numGSEntries, sizeof(Starmap3Pointer),
						(int (__CLIB *)
						(const void *,const void *))CompareFPPointer);
				//DebugPrintf("result=%x.\n", result);
				if (result==NULL) {
					printf("Unexpectedly, there is no entry at FP %x! Body %.20s refers to this file position for its alternate FP.\n", searchFPKey, generic->name);
					generic->body()->nextAlternateIdx=0xffffffff;
				} else if (result->entry()->flags&0x8) {
					generic->body()->nextAlternateIdx=0xffffffff;
					printf("Unexpectedly, the entry at FP %x is the wrong type! Body %.20s refers to this file position for its alternate FP, but the entry there is for a star instead (%0.20s).\n", searchFPKey, generic->name, result->entry()->name);
				} else {
					generic->body()->nextAlternateIdx = result->entry()->idx;
					generic->body()->nextAlternate()->body()->prevAlternateIdx = generic->idx;
				}
			} else {
				generic->body()->nextAlternateIdx=0xffffffff;
			}
			if (generic->body()->starFP!=0) {
				searchFPKey = generic->body()->starFP;
				Starmap3Pointer *result = (Starmap3Pointer*) bsearch(&searchFPKey, pointers, numGSEntries, sizeof(Starmap3Pointer),
						(int (__CLIB *)
						(const void *,const void *))CompareFPPointer);
				if (result==NULL) {
					printf("Unexpectedly, there is no entry at FP %x! (Body %.20s refers to this file position for its star FP. This may cause a crash (or maybe the entry is deleted and it won't).\n", searchFPKey, generic->name);
					generic->body()->starIdx=0xffffffff;
				} else {
					generic->body()->starIdx = result->entry()->idx;
				}
			} else {
				printf("Error: StarFP for [%.20s] is zero! Sync-starmap will probably crash in a moment.\n", generic->name);
				generic->body()->starIdx=0xffffffff;
			}
		}
	}
}

Bool downloadComplete=False;
Bool downloadFailed=False;

class SyncSmDownloadCallback : public IBindStatusCallback {
	private:
	ULONG refCount;
	public:
	SyncSmDownloadCallback() {
		refCount=0;
	}
	LONG STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) {
		if (::IsEqualIID(riid, IID_IUnknown)) {
			*ppvObject=this;
		} else if (::IsEqualIID(riid, IID_IBindStatusCallback)) {
			*ppvObject=static_cast<IBindStatusCallback*> (this);
		} else {
			*ppvObject=NULL;
			return E_NOINTERFACE;
		}
		(* reinterpret_cast<LPUNKNOWN*> (ppvObject) )->AddRef();
		return S_OK;
	}
	
	ULONG STDMETHODCALLTYPE AddRef() {
		refCount++;
		return refCount;
	}
	ULONG STDMETHODCALLTYPE Release() {
		refCount--;
		return refCount;
	}

	HRESULT STDMETHODCALLTYPE OnStartBinding(DWORD dwReserved, IBinding __RPC_FAR *pib) {
		return E_NOTIMPL;
	}
	HRESULT STDMETHODCALLTYPE GetPriority(LONG __RPC_FAR *pnPriority) {
		return E_NOTIMPL;
	}
	HRESULT STDMETHODCALLTYPE OnLowResource(DWORD reserved) {
		return E_NOTIMPL;
	}
	//This is the one which we actually need
	HRESULT STDMETHODCALLTYPE OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText) {
		DebugPrintf("\tDownload Status: ");
		if (szStatusText!=NULL) {
			#ifdef SYNCSMDEBUG
			wprintf(szStatusText);
			#endif
		} else {
			DebugPrintf("(null)");
		}
		DebugPrintf(" and ulStatusCode=%x.\n", ulStatusCode);
		if (ulStatusCode==BINDSTATUS_USINGCACHEDCOPY || ulStatusCode==BINDSTATUS_ENDDOWNLOADDATA) {
			downloadComplete=True;
		}
		return S_OK;
	}
	HRESULT STDMETHODCALLTYPE OnStopBinding(HRESULT hresult, LPCWSTR szError) {
		return E_NOTIMPL;
	}
	
	HRESULT STDMETHODCALLTYPE GetBindInfo(DWORD __RPC_FAR *grfBINDF, BINDINFO __RPC_FAR *pbindinfo) {
		*grfBINDF=0;
		pbindinfo=NULL;	//This might not be correct.
		return S_OK;
	}
	HRESULT STDMETHODCALLTYPE OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC __RPC_FAR *pformatetc, STGMEDIUM __RPC_FAR *pstgmed) {
		return S_OK;
	}
	HRESULT STDMETHODCALLTYPE OnObjectAvailable(REFIID riid, IUnknown __RPC_FAR *punk) {
		return S_OK;
	}
	
	
} downloadCallback;

//SyncSmDownloadCallback downloadCallback();
//extern STDAPI URLDownloadToCacheFileA(LPUNKNOWN,LPCSTR,LPTSTR,DWORD,DWORD,LPBINDSTATUSCALLBACK);

FILE * starmap3File=NULL;
FILE * starmap2File=NULL;
FILE * guide3File=NULL;
FILE * user3File=NULL;
FILE * svr3File=NULL;
FILE * inboxFile=NULL;
FILE * outboxFile=NULL;

void DeletePlanet(Starmap3GenericEntry * entry) {
	//fix linked entries
	entry->flags=entry->flags|0x20;
	entry->body()->prevAlternate()->body()->nextAlternateIdx = entry->body()->nextAlternateIdx;
	entry->body()->nextAlternateIdx=0xffffffff;
	entry->body()->prevAlternateIdx=0xffffffff;
	entry->body()->starIdx=0xffffffff;
}

void DeleteStar(Starmap3GenericEntry * entry) {
	//fix linked entries
	entry->flags=entry->flags|0x20;
	entry->star()->prevAlternate()->star()->nextAlternateIdx = entry->star()->nextAlternateIdx;
	entry->star()->nextAlternateIdx=0xffffffff;
	entry->star()->prevAlternateIdx=0xffffffff;
	//delete planet entries in this system
	for (int a=0; a<numGSEntries; a++) {
		if ((entry->flags&0x8)==0) {
			if (entry->body()->starIdx==genericStarmapEntries[a].idx) {
				//delete it too
				DeletePlanet(&(genericStarmapEntries[a]));
			}
		}
	}
}

void SaveStarmap(Uqword starmap3Version) {
	char path[256];
	sprintf(path, "%s%s", folderPath, "starmap3.map");
	starmap3File = fopen(path, "wb");
	if (starmap3File==NULL) {
		printf("Unable to write %s.\n", path);
		return;
	}
	long curLen = 0;
	Uword fileFormatVersion = 2;
	fwrite(&fileFormatVersion, 2, 1, starmap3File);
	fwrite(&starmap3Version, 8, 1, starmap3File);
	Udword nextAlternateFP;
	Udword starFP;
	for (int idx=0; idx<numGSEntries; idx++) {
		Udword filePos = ftell(starmap3File);
		Starmap3GenericEntry *generic = &(genericStarmapEntries[idx]);
		if (filePos>0 && generic->filePosition>0) {
			if (filePos!=generic->filePosition) {
				printf("Error! Incorrect file position (%u is not %u, which was expected for starmap entry #%i!\n", filePos, generic->filePosition, idx);
			}
			fwrite(&(generic->flags), 1, 1, starmap3File);
			fwrite(&(generic->discoverer), 4, 1, starmap3File);
			fwrite((generic->name), 20, 1, starmap3File);
			if (generic->flags&0x8) {
				Starmap3StarEntry *star = generic->star();
				fwrite(&(star->sx), 4, 1, starmap3File);
				fwrite(&(star->sy), 4, 1, starmap3File);
				fwrite(&(star->sz), 4, 1, starmap3File);
				fwrite(&(star->data), 2, 1, starmap3File);
				nextAlternateFP = 0;
				Starmap3GenericEntry *next = star->nextAlternate();
				if (next!=NULL) {
					nextAlternateFP = next->filePosition;
				}
				fwrite(&nextAlternateFP, 4, 1, starmap3File);
			} else {
				Starmap3BodyEntry *body = generic->body();
				starFP = 0;
				Starmap3GenericEntry *star = body->star();
				if (star!=NULL) {
					starFP = star->filePosition;
				}
				fwrite(&starFP, 4, 1, starmap3File);
				fwrite(&(body->data), 2, 1, starmap3File);
				
				nextAlternateFP = 0;
				Starmap3GenericEntry *next = body->nextAlternate();
				if (next!=NULL) {
					nextAlternateFP = next->filePosition;
				}
				fwrite(&nextAlternateFP, 4, 1, starmap3File);
			}
		}
	}
	fclose(starmap3File);
}

void RunStyracosaurus() {
	printf("Also updated starmap2.bin for NoctisMapper.\n");
	char path[256];
	sprintf(path, "%s%s", folderPath, "starmap2.bin");
	starmap2File = fopen(path, "wb");
	if (starmap2File==NULL) {
		printf("Unable to write %s.\n", path);
		return;
	}
	for (int idx=0; idx<numGSEntries; idx++) {
		Udword filePos = ftell(starmap2File);
		Starmap3GenericEntry *generic = &(genericStarmapEntries[idx]);
		if (generic->filePosition>0) {
			//fwrite(&(generic->flags), 1, 1, starmap3File);
			//fwrite(&(generic->discoverer), 4, 1, starmap3File);
			//fwrite((generic->name), 20, 1, starmap3File);
			int valid=0;
			char objtype[4];
			objtype[0]=' ';
			if (generic->flags&0x8) {
				Starmap3StarEntry *star = generic->star();
				if (DoesStarExist(star->sx, star->sy, star->sz)) {	//this calls PrepareFromSectorCoords too.
					valid=1;
					Dword starX = (Dword) foundstar_x;
					Dword starY = (Dword) foundstar_y;
					Dword starZ = (Dword) foundstar_z;
					fwrite(&(starX), 4, 1, starmap2File);
					fwrite(&(starY), 4, 1, starmap2File);
					fwrite(&(starZ), 4, 1, starmap2File);
					int zero = 0;
					fwrite(&(zero), 4, 1, starmap2File);
					fwrite(&(zero), 4, 1, starmap2File);
					objtype[1]='S';
					int foundstar_class = StellarClass(star->data);
					//Since there isn't a class 20 (or higher), we can do this check to save the cost of doing % and /.
					if (foundstar_class<10) {
						objtype[2]='0';
						objtype[3]='0'+foundstar_class;
					} else {
						objtype[2]='1';
						objtype[3]='0'+foundstar_class-10;
					}
				}
			} else {
				Starmap3BodyEntry *body = generic->body();
				Starmap3StarEntry *star = body->star()->star();
				
				if (DoesStarExist(star->sx, star->sy, star->sz)) {	//this calls PrepareFromSectorCoords too.
					valid=1;
					Dword starX = (Dword) foundstar_x;
					Dword starY = (Dword) foundstar_y;
					Dword starZ = (Dword) foundstar_z;
					fwrite(&(starX), 4, 1, starmap2File);
					fwrite(&(starY), 4, 1, starmap2File);
					fwrite(&(starZ), 4, 1, starmap2File);
					int bodyNumber = BodyNumber(body);
					int iddiff = bodyNumber+1;
					fwrite(&(iddiff), 4, 1, starmap2File);
					int planetType = PlanetType(body->data);
					fwrite(&(planetType), 4, 1, starmap2File);
					objtype[1]='P';
					objtype[2]='0'+(bodyNumber/10);
					objtype[3]='0'+(bodyNumber%10);
				}
			}
			if (valid) {
				fwrite((generic->name), 20, 1, starmap2File);
				fwrite((objtype), 4, 1, starmap2File);
			}
		}
	}
	fclose(starmap2File);
	
}

void SaveGuide(Uqword guide3Version) {
	char path[256];
	sprintf(path, "%s%s", folderPath, "starmap3.gd");
	guide3File = fopen(path, "wb");
	if (guide3File==NULL) {
		printf("Unable to write %s.\n", path);
		return;
	}
	long curLen = 0;
	Uword fileFormatVersion = 2;
	fwrite(&fileFormatVersion, 2, 1, guide3File);
	fwrite(&guide3Version, 8, 1, guide3File);
	for (int idx=0; idx<numGEntries; idx++) {
		Guide3Entry *guide = &(guideEntries[idx]);
		Uchar flags = guide->flags;
		Bool reallyDelete = flags&0x20 && flags&0x80 && flags&0x40;
		if (!reallyDelete) {
			fwrite(&(guide->flags), 1, 1, guide3File);
			fwrite(&(guide->subjectsx), 4, 1, guide3File);
			fwrite(&(guide->subjectsy), 4, 1, guide3File);
			fwrite(&(guide->subjectsz), 4, 1, guide3File);
			fwrite(&(guide->subject_pNum), 2, 1, guide3File);
			fwrite(&(guide->subject_mNum), 2, 1, guide3File);
			fwrite(&(guide->writer), 4, 1, guide3File);
			fwrite((guide->entry), 76, 1, guide3File);
		}
	}
	fclose(guide3File);
}

void SaveUsers(Uqword user3Version) {
	char path[256];
	sprintf(path, "%s%s", folderPath, "starmap3.usr");
	user3File = fopen(path, "wb");
	if (user3File==NULL) {
		printf("Unable to write %s.\n", path);
		return;
	}
	#ifdef CREATEINBOX3
	sprintf(path, "%s%s", folderPath, "starmap3.svr");
	svr3File = fopen(path, "wb");
	if (svr3File==NULL) {
		printf("Unable to write %s.\n", path);
		return;
	}
	#endif
	long curLen = 0;
	Uword fileFormatVersion = 2;
	fwrite(&fileFormatVersion, 2, 1, user3File);
	fwrite(&user3Version, 8, 1, user3File);
	#ifdef CREATEINBOX3
	fwrite(&fileFormatVersion, 2, 1, svr3File);
	fwrite(&user3Version, 8, 1, svr3File);
	#endif
	
	for (int idx=0; idx<numUEntries; idx++) {
		User3Entry *user = &(userEntries[idx]);
		fwrite(&(user->id), 4, 1, user3File);
		fwrite((user->name), 20, 1, user3File);
		#ifdef CREATEINBOX3
		fwrite(&(user->id), 4, 1, svr3File);
		fwrite((user->name), 20, 1, svr3File);
		fwrite((user->password), 20, 1, svr3File);
		fwrite(&(user->entriesSubmitted), 4, 1, svr3File);
		fwrite(&(user->invalidEntriesSubmitted), 4, 1, svr3File);
		fwrite(&(user->badEntriesSubmitted), 4, 1, svr3File);
		#endif
	}
	fclose(user3File);
	#ifdef CREATEINBOX3
	fclose(svr3File);
	#endif
}

//Used to back up the starmap3 files:
const char *monthNames[] = {"None", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"};
	
void TranslateName(char *nameout) {
	SYSTEMTIME stTime;
	GetSystemTime(&stTime);
	sprintf(nameout, "from %i - Month %.02i (%s) - Day %.02i - %.02ih %.02im %.02is", stTime.wYear, stTime.wMonth, monthNames[stTime.wMonth], stTime.wDay, stTime.wHour, stTime.wMinute, stTime.wSecond);
}

int NameOrPasswordAreInvalid(int showError, char *username=NULL, char *password=NULL) {
	int invalid=0;
	if (username==NULL) username=usernameBuffer;
	if (password==NULL) password=passwordBuffer;
	if (strlen(username)<=1) {
		if (showError) printf("Username ('%.20s') is too short! It needs to be at least 2 characters long.\n", usernameBuffer);
		invalid=1;
	}
	int hasNormalChars=0;
	int hasSpaces=0;
	int hasOtherChars=0;
	int spacesAtEnd=0;
	int repeatedSpaces=0;
	for (int pos=0; pos<strlen(username); pos++) {
		if (username[pos]==' ') {
			hasSpaces++;
			if (spacesAtEnd>0) {
				repeatedSpaces=1;
			}
			spacesAtEnd=1;
			if (hasOtherChars==0 && hasNormalChars==0) {
				if (showError) printf("You can't have spaces at the beginning of your username.\n");
				invalid=1;
			}
		} else if ((username[pos]>='A' && username[pos]<='Z') || (username[pos]>='a' && username[pos]<='z') || (username[pos]>='0' && username[pos]<='9')) {
			hasNormalChars++;
			spacesAtEnd=0;
		} else {
			if (hasOtherChars==0) {
				if (showError) printf("Invalid characters in username: %c", username[pos]);
				invalid=1;
			} else {
				if (showError) printf(", %c", username[pos]);
			}
			hasOtherChars++;
			spacesAtEnd=0;
		}
	}
	if (hasOtherChars) {
		if (showError) printf("\n");
	}
	if (spacesAtEnd) {
		if (showError) printf("You can't have spaces at the end of your username.\n");
		invalid=1;
	}
	if (repeatedSpaces) {
		if (showError) printf("You can't have multiple spaces in a row in your username.\n");
		invalid=1;
	}
	if (hasNormalChars==0) {
		if (showError) printf("You have to have alphanumeric characters in your username (a-z, A-Z, 0-9)\n");
		invalid=1;
	}
	if (strlen(password)<=5) {
		if (showError) printf("Password ('%.20s') is too short! It needs to be at least 6 characters long.\n", password);
		invalid=1;
	}
	return invalid;
}

void main2() {
	#ifdef CREATEINBOX3
		FILE * ini = fopen("createInbox.ini", "rt");
		if (ini==NULL) {
			ini = fopen("createInbox.ini", "wt");
			//request username, etc
			strcpy(folderPath, "data\\original\\");
			fprintf(ini, "%s\n", folderPath);
			fclose(ini);
		} else {
			ReadLine(ini, folderPath, 256);
			fclose(ini);
		}
	#else
		FILE * ini = fopen("syncStarmap.ini", "rt");
		int repeatedLoop=0;
		while (NameOrPasswordAreInvalid(repeatedLoop)) {
			if (ini==NULL || repeatedLoop) {
				ini = fopen("syncStarmap.ini", "wt");
				//request username, etc
				if (repeatedLoop) {
					printf("The username you entered before ('%.20s'), or the password ('%.20s') was invalid.\n", usernameBuffer, passwordBuffer);
				}
				printf("Enter the username you want, or your existing username (max 20 chars, one word only, no spaces or tabs - USERNAMES ARE CASE SENSITIVE): ");
				scanf("%20s", usernameBuffer);
				printf("Enter the password you want, or your existing password (max 20 chars, no spaces or tabs): ");
				scanf("%20s", passwordBuffer);
				strcpy(folderPath, "data\\");
				fprintf(ini, "%s\n%s\n%s\n", usernameBuffer, passwordBuffer, folderPath);
				fclose(ini);
			} else {
				ReadLine(ini, usernameBuffer, 256);
				ReadLine(ini, passwordBuffer, 256);
				ReadLine(ini, folderPath, 256);
				fclose(ini);
			}
			repeatedLoop=1;
		}
	#endif
	//------------------------------
	//Load starmap3.map into memory.
	printf("Loading starmap3.map.\n");
	char path[256];
	char frompath[256];
	char topath[256];
	sprintf(path, "%s%s", folderPath, "starmap3.map");
	starmap3File = fopen(path, "rb");
	if (starmap3File==NULL) {
		printf("Missing %s.\n", path);
		return;
	}
	long starmap3Len = 0;
	fseek(starmap3File, 0, SEEK_END);
	starmap3Len = ftell(starmap3File);
	fseek(starmap3File, 0, SEEK_SET);
	long curLen = 0;
	Uword fileFormatVersion;
	Uqword starmap3Version;
	fread(&fileFormatVersion, 2, 1, starmap3File);
	fread(&starmap3Version, 8, 1, starmap3File);
	Udword curFPos;
	if (fileFormatVersion>2) {
		printf("starmap3.map is a newer format than we can understand. You need a new version of SyncStarmap.\n");
		return;
	} else {
		Uchar flags;
		Udword discoverer;
		char name[20];
		Dword sx, sy, sz;
		Uword data;
		Udword nextAlternateFP;
		Udword starFP;
		//int stillZero=1;
		while ((curFPos = ftell(starmap3File))<starmap3Len) {	//feof(starmap3File)) {
			/*
			feof doesn't seem to be working the way I expected. This debug message, in place of this if statement:
				DebugPrintf("starmap3Len=%i, curFPos=%i\n", starmap3Len, curFPos);
			prints this:
				[...]
				starmap3Len=878940, curFPos=878862
				starmap3Len=878940, curFPos=878905
				starmap3Len=878940, curFPos=878940
			*/
			fread(&flags, 1, 1, starmap3File);
			fread(&discoverer, 4, 1, starmap3File);
			fread(&name, 20, 1, starmap3File);
			
			Starmap3GenericEntry *generic = NewGenericEntry(curFPos, flags, discoverer, name);
			if (flags&0x8) {	//star
				fread(&sx, 4, 1, starmap3File);
				fread(&sy, 4, 1, starmap3File);
				fread(&sz, 4, 1, starmap3File);
				fread(&data, 2, 1, starmap3File);
				fread(&nextAlternateFP, 4, 1, starmap3File);
				Starmap3StarEntry *star = NewStarEntry(sx, sy, sz, data, nextAlternateFP);
				generic->starIdx = star->idx;
				
			} else {
				fread(&starFP, 4, 1, starmap3File);
				fread(&data, 2, 1, starmap3File);
				fread(&nextAlternateFP, 4, 1, starmap3File);
				Starmap3BodyEntry *body = NewBodyEntry(starFP, data, nextAlternateFP);
				generic->bodyIdx = body->idx;
				/*//This was for debugging and might be useful again in the future:
				if (curFPos>0xd6ac0) {	//the error was occuring after that point
					int data_pType = (data&0xF);
					int data_pNum = ((data>>4)&0x1F);
					int data_mNum = ((data>>9)&0x1F);
					DebugPrintf("Loaded body flags=%x discoverer=%i name=[%.20s], starFP=%x data=%x nextAlternateFP=%x. pType=%x pNum=%x mNum=%x\n", (int)flags, discoverer, name, starFP, (int)data, nextAlternateFP, data_pType, data_pNum, data_mNum);
								
				}*/
			}
		}
		starmap3NextRecordPos = ftell(starmap3File);
	}
	DebugPrintf("Fixing file pos refs.\n");
	FixFilePosRefs(numGSEntries, genericStarmapEntries);
	Starmap3Pointer *starmapSortedByPos = new Starmap3Pointer[numGSEntries];
	for (int a=0; a<numGSEntries; a++) {
		starmapSortedByPos[a].entryIdx = a;
	}
	
	DebugPrintf("Indexing starmap entries.\n");
	DebugPrintf("\tBy pos.\n");
	qsort(starmapSortedByPos, numGSEntries, sizeof(Starmap3Pointer),
		(int (__CLIB *)
		(const void *,const void *))ComparePos);
	Starmap3Pointer *starmapSortedByName = new Starmap3Pointer[numGSEntries];
	for (int a=0; a<numGSEntries; a++) {
		starmapSortedByName[a].entryIdx = a;
	}
	DebugPrintf("\tBy name.\n");
	qsort(starmapSortedByName, numGSEntries, sizeof(Starmap3Pointer),
		(int (__CLIB *)
		(const void *,const void *))CompareName);
	sortedNumGSEntries = numGSEntries;
	DebugPrintf("\tDone.\n");
	
	//-----------------------------
	//Load starmap3.gd into memory.
	printf("Loading starmap3.gd.\n");
	sprintf(path, "%s%s", folderPath, "starmap3.gd");
	guide3File = fopen(path, "r+b");
	if (guide3File==NULL) {
		printf("Missing %s.\n", path);
		return;
	}
	long guide3Len = 0;
	fseek(guide3File, 0, SEEK_END);
	guide3Len = ftell(guide3File);
	fseek(guide3File, 0, SEEK_SET);
	curLen = 0;
	Uqword guide3Version;
	fread(&fileFormatVersion, 2, 1, guide3File);
	fread(&guide3Version, 8, 1, guide3File);
	if (fileFormatVersion>2) {
		printf("starmap3.gd is a newer format than we can understand. You need a new version of SyncStarmap.\n");
		return;
	} else {
		Uchar flags;	//bit 7 is 'include in outbox', bit 6 is 'local only'. bit 5 is 'deleted'. The other bits aren't used at this time.
		Dword subjectsx;
		Dword subjectsy;
		Dword subjectsz;
		Uword subject_pNum;
		Uword subject_mNum;
		Udword writer;	//the person who wrote the entry
		char entry[76];
		while ((curFPos = ftell(guide3File))<guide3Len) {
			fread(&flags, 1, 1, guide3File);
			fread(&subjectsx, 4, 1, guide3File);
			fread(&subjectsy, 4, 1, guide3File);
			fread(&subjectsz, 4, 1, guide3File);
			fread(&subject_pNum, 2, 1, guide3File);
			fread(&subject_mNum, 2, 1, guide3File);
			fread(&writer, 4, 1, guide3File);
			fread(&entry, 76, 1, guide3File);
			Guide3Entry *gentry = NewGuideEntry(flags, subjectsx, subjectsy, subjectsz, subject_pNum, subject_mNum, writer, entry);
		}
	}
	//We don't actually sort guide entries. If we were going to, we might do this:
	/*GenericPointer guideSortedByPos* = new GenericPointer[numGEntries];
	for (int a=0; a<numGEntries; a++) {
		guideSortedByPos[a].entry = &(guideEntries[a]);
	}
	qsort(guideSortedByPos, numGEntries, sizeof(GenericPointer),
		(int (__CLIB *)
		(const void *,const void *))CompareGuidePos);*/
	sortedNumGEntries = numGEntries;
	
	//------------------------------
	//Load starmap3.usr into memory.
	printf("Loading starmap3.usr.\n");
	#ifdef CREATEINBOX3
	sprintf(path, "%s%s", folderPath, "starmap3.svr");
	#else
	sprintf(path, "%s%s", folderPath, "starmap3.usr");
	#endif
	user3File = fopen(path, "rb");
	if (user3File==NULL) {
		printf("Missing %s\n", path);
		return;
	}
	long user3Len = 0;
	fseek(user3File, 0, SEEK_END);
	user3Len = ftell(user3File);
	fseek(user3File, 0, SEEK_SET);
	curLen = 0;
	Uqword user3Version;
	
	Udword high_drifter_id=0;
	
	fread(&fileFormatVersion, 2, 1, user3File);
	fread(&user3Version, 8, 1, user3File);
	if (fileFormatVersion>2) {
		#ifdef CREATEINBOX3
		printf("starmap3.svr is a newer format than we can understand. You need a new version of CreateInbox3.\n");
		#else
		printf("starmap3.usr is a newer format than we can understand. You need a new version of SyncStarmap.\n");
		#endif
		return;
	} else {
		Udword drifter_id=0;	//the person who wrote the entry
		char drifter_name[20];
		#ifdef CREATEINBOX3
		char password[20];
		Udword entriesSubmitted;
		Udword invalidEntriesSubmitted;
		Udword badEntriesSubmitted;
		#endif
		while ((curFPos = ftell(user3File))<user3Len) {
			fread(&drifter_id, 4, 1, user3File);
			fread(drifter_name, 20, 1, user3File);
			#ifdef CREATEINBOX3
			fread(password, 20, 1, user3File);
			fread(&entriesSubmitted, 4, 1, user3File);
			fread(&invalidEntriesSubmitted, 4, 1, user3File);
			fread(&badEntriesSubmitted, 4, 1, user3File);
			NewUserEntry(drifter_id, drifter_name, 0, password, entriesSubmitted, invalidEntriesSubmitted, badEntriesSubmitted);
			//debugging
			DebugPrintf("Loaded user entry: %i id, name [%.20s].\n", drifter_id, drifter_name, password);
			#else
			NewUserEntry(drifter_id, drifter_name);
			#endif
			if (drifter_id>high_drifter_id) {
				high_drifter_id = drifter_id;
			}
			
		}
	}
	sortedNumUEntries = numUEntries;
	searchKey = new StarmapSearchEntry;
	
	fclose(starmap3File); starmap3File=NULL;
	fclose(user3File); user3File=NULL;
	fclose(guide3File); guide3File=NULL;
	
	printf("Backing up starmap3 files.\n");
	//Back up the starmap3.* files
	#ifdef CREATEINBOX3
	#define NUMPATHS 5
	#else
	#define NUMPATHS 3
	#endif
	char partOfName[256];
	char paths[NUMPATHS][256];
	TranslateName(partOfName);
	
	{	//we want it to discard the stat structure after this.
		struct stat statInfo;
		sprintf(paths[0], "%sbackups", folderPath);	//We're using paths[0] for temporary storage here
		if (stat(paths[0], &statInfo)!=0) {
			sprintf(paths[0], "mkdir %sbackups", folderPath);
			system(paths[0]);
		}
		sprintf(paths[0], "%sbackups\\%s", folderPath, partOfName);	//We're still using paths[0] for temporary storage
		if (stat(paths[0], &statInfo)!=0) {
			sprintf(paths[0], "mkdir \"%sbackups\\%s\"", folderPath, partOfName);
			system(paths[0]);
		}
		//We're done using paths[0] for temporary storage now.
	}
	
	sprintf(paths[0], "%s", "starmap3.map");
	sprintf(paths[1], "%s", "starmap3.gd");
	sprintf(paths[2], "%s", "starmap3.usr");
	#ifdef CREATEINBOX3
	sprintf(paths[3], "%s", "starmap3.svr");
	sprintf(paths[4], "%s", "starmap-version.txt");
	#endif
	for (int i=0; i<NUMPATHS; i++) {
		sprintf(frompath, "%s%s", folderPath, paths[i]);
		sprintf(topath, "%sbackups\\%s\\%s", folderPath, partOfName, paths[i]);
		
		if (CopyFile(frompath, topath, FALSE)) {
			printf("Made a backup from [%s] to [%s].\n", frompath, topath);
		} else {
			Dword err = GetLastError();
			char *errMsgPtr;
			char msg[512];
			sprintf(msg, "Failed to make a backup from [%s] to [%s]. Reason: ", frompath, topath);
			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, err, 0, msg+strlen(msg), 511-strlen(msg), NULL);
			MessageBox(NULL, msg, "Error", MB_OK|MB_ICONERROR);
		}
	}
	#ifdef CREATEINBOX3
		if (user3Version!=starmap3Version) {
			printf("Warning: starmap3.usr version (%llx) does not match starmap3.map version (%llx)! Changing user version to match starmap version.\n", user3Version, starmap3Version);
			user3Version=starmap3Version;
		}
		if (guide3Version!=starmap3Version) {
			printf("Warning: starmap3.gd version (%llx) does not match starmap3.map version (%llx)! Changing guide version to match starmap version.\n", guide3Version, starmap3Version);
			guide3Version=starmap3Version;
		}
		Qword outboxVersion;
		Dword numStarmapEntries=0;
		Dword numGuideEntries=0;
		Dword numUserEntries=0;
		char outboxFilename[20];
		char outboxUsername[20];
		char outboxPassword[20];
		Udword outboxNumMapEntries;
		Udword outboxNumGuideEntries;
		
		printf("Merging outboxes and preparing inbox %llx from them.\n", starmap3Version);
		//for (int mergeId=1; ; mergeId++) {
		for (int mergeId=0; ; mergeId++) {
			//sprintf(outboxFilename, "%soutbox.7z.%03i", folderPath, mergeId);
			if (mergeId==0) {
				sprintf(outboxFilename, "%soutbox.7z", folderPath, mergeId);
			} else {
				sprintf(outboxFilename, "%soutbox-%i.7z", folderPath, mergeId);
			}
			outboxFile = fopen(outboxFilename, "rb");
			if (outboxFile!=NULL) {
				fclose(outboxFile);
				printf("Un7zipping outbox file #%03i.\n", mergeId);
				sprintf(path, "syncSmFiles\\lzma d %s %soutbox.sm3", outboxFilename, folderPath);
				system(path);
			} else {
				if (mergeId==0) {
					printf("Outboxes should be named starting with 'outbox.7z', and then 'outbox-1.7z', 'outbox-2.7z', etc, and they should be in the [%s] folder.\n", folderPath);
				}
				printf("%i outboxes merged.\n", mergeId-1);
				break;
			}
			sprintf(outboxFilename, "%soutbox.sm3", folderPath);
			outboxFile = fopen(outboxFilename, "rb");
			if (outboxFile==NULL) {
				printf("Failed to un7zip outbox %i?\n", mergeId);
				break;
			} else {
				printf("Processing outbox %i.\n", mergeId);
				fread(&fileFormatVersion, 2, 1, outboxFile);
				if (fileFormatVersion>1) {
					printf("%s has a different file format version: %i\n", outboxFilename, fileFormatVersion);
				} else {
					fileFormatVersion=1;
					fread(&outboxVersion, 8, 1, outboxFile);
					fread(outboxUsername, 20, 1, outboxFile);
					fread(outboxPassword, 20, 1, outboxFile);
					printf("Checking to see if the username (%.20s) and password in outbox %i are valid.\n", outboxUsername, mergeId);
					if (NameOrPasswordAreInvalid(1, outboxUsername, outboxPassword)) {
						printf("Outbox %i is being skipped because of an invalid username or password!\n", mergeId);
						printf("Press any key to continue.\n");
						getch();
					} else {
						fread(&outboxNumMapEntries, 4, 1, outboxFile);
						fread(&outboxNumGuideEntries, 4, 1, outboxFile);
						Udword userIdx = 0xffffffff;
						Udword highUserIdx = 0;
						DebugPrintf("\tLooking for the username [%.20s] with password [%.20s].\n", outboxUsername, outboxPassword);
						//This needs to set drifter_id and high_drifter_id in addition to userIdx and highUserIdx.
						
						for (Udword idx=0; idx<numUEntries; idx++) {
							DebugPrintf("Comparing outbox username [%.20s] with user idx %i, [%.20s].\n", outboxUsername, idx, userEntries[idx].name);
							if (memcmp(outboxUsername, userEntries[idx].name, 20)==0) {
								userIdx=idx;
								printf("Found 'em: %i.\n", userIdx);
							} else {
								if (idx>highUserIdx) highUserIdx=idx;
								if (userEntries[idx].id>high_drifter_id) high_drifter_id=userEntries[idx].id;
							}
						}
						if (userIdx==0xffffffff) {
							printf("\tOutbox username not found. Making new account.\n", outboxUsername);
							//make a new user name, write to inbox.
							if (high_drifter_id==0xfffffffe) {
								printf("Can't make a new account - Too many drifters already exist.\n");
								return;
							} else {
								Udword drifter_id = high_drifter_id+1;	//the person who wrote the entry
								userIdx = NewUserEntry(drifter_id, outboxUsername, 0x80, outboxPassword, 0, 0, 0);
								printf("Created account: %i [%.20s]\n", drifter_id, outboxUsername);
							}
						} else {
							if (!memcmp(outboxPassword, userEntries[userIdx].password, 20)==0) {
								printf("In outbox '%s', invalid password for username '%.20s'.\n", outboxFilename, outboxUsername);
								continue;
							} else {
								printf("\tOutbox username found.\n", outboxUsername);
							}
						}
						//It's a valid username and password.
						
						Uchar flags;
						Udword discoverer = userEntries[userIdx].id;
						char name[20];
						Dword sx, sy, sz;
						Uword data;
						Udword nextAlternateFP;
						Udword starFP;
						Udword entryNum = 0;
						printf("The outbox says there are %i map entries and %i guide entries.\n", outboxNumMapEntries, outboxNumGuideEntries);
						while (entryNum < outboxNumMapEntries) {
							curFPos = ftell(outboxFile);
							DebugPrintf("\tcurFPos=%i Map entryNum=%i outboxNumMapEntries=%i\n", curFPos, entryNum, outboxNumMapEntries);
							fread(&flags, 1, 1, outboxFile);
							fread(&name, 20, 1, outboxFile);
							flags = flags&0x2b;
							flags|=0x80;
							fread(&sx, 4, 1, outboxFile);
							fread(&sy, 4, 1, outboxFile);
							fread(&sz, 4, 1, outboxFile);
							fread(&data, 2, 1, outboxFile);
							
							if (flags&0x8) {	//star
								//make sure it's legal
								if (DoesStarExist(sx, sy, sz)) {	//this calls PrepareFromSectorCoords too.
									//Calculate data.
									extract_foundstar_infos();
									prepare_foundstar();
									//stellar class, number of planets, number of moons
									data = (foundstar_class&0xF) | ((foundstar_nop&0x1F)<<4) | (((foundstar_nob-foundstar_nop)&0x7F)<<9);
									
									//TODO: Multi-Galaxy stuff.
									searchKey->sx=sx;
									searchKey->sy=sy;
									searchKey->sz=sz;
									searchKey->inflatedBodyNumber=0;
									searchKey->deleted=False;
									void *result = NULL;
									int resVal = combinedSearch(searchKey, starmapSortedByPos, sortedNumGSEntries, sizeof(Starmap3Pointer),
										(int (__CLIB *)
										(const void *,const void *))FindPos,
										genericStarmapEntries, sortedNumGSEntries, numGSEntries, sizeof(Starmap3GenericEntry), (int (__CLIB *)
										(const void *,const void *))FindPosEntry, &result);
									Starmap3GenericEntry * entry = ExtractGenericEntry(resVal, result);
									Starmap3GenericEntry * lastEntry=entry;
									Dword resultcount=0;
									while (entry!=NULL) {
										if (entry->discoverer==discoverer && memcmp(name, entry->name, 20)==0) {
											//It's them
											Uchar oldflags=entry->flags;
											if (oldflags|0x80==flags) {
												printf("Star entry '%.20s' by discoverer %i is already in the starmap, and is unchanged in this outbox, so we're ignoring it.\n", entry->name, discoverer);
											} else {
												printf("Updating star entry '%.20s' by discoverer %i.\n", name, discoverer);
												entry->flags=flags;
												printf("\tChanged flags from %x to %x.\n", (int)oldflags, (int)entry->flags);
											}
											if (flags&0x20) {
												DeleteStar(entry);
												printf("\tDeleted entry.\n");
											}
											resultcount=-1;
											lastEntry=entry;
											entry=NULL;
										} else {
											resultcount++;
											lastEntry = entry;
											if (entry->flags&0x8) {
												entry=entry->star()->nextAlternate();
											} else {
												entry=entry->body()->nextAlternate();
											}
										}
									}
									if (resultcount==0 && (!(flags&0x20))) {	//There are no entries for it already, and it isn't marked 'deleted'
										//Make a new entry and mark it 'include in inbox'.
										Udword filepos = starmap3NextRecordPos;
										starmap3NextRecordPos+=43;
										Starmap3GenericEntry *generic = NewGenericEntry(filepos, flags, discoverer, name);
										Starmap3StarEntry *star = NewStarEntry(sx, sy, sz, data, 0);
										generic->starIdx=star->idx;
										star->nextAlternateIdx = 0xffffffff;
										star->prevAlternateIdx = 0xffffffff;
										printf("New star labelled '%.20s' at sector %i,%i,%i with flags %x by discoverer %i.\n", name, sx, sy, sz, (int)flags, discoverer);
									} else if (resultcount==-1) {
										//It already exists, and we've updated it now.
									} else if (resultcount<3 && (!(flags&0x20))) {
										//Make a new entry, mark it 'include in inbox', and link the old entry to it (as an alternate name).
										Udword filepos = starmap3NextRecordPos;
										starmap3NextRecordPos+=43;
										Starmap3GenericEntry *generic = NewGenericEntry(filepos, flags, discoverer, name);
										Starmap3StarEntry *star = NewStarEntry(sx, sy, sz, data, 0);
										generic->starIdx=star->idx;
										lastEntry->star()->nextAlternateIdx = generic->idx;
										star->prevAlternateIdx = lastEntry->idx;
										star->nextAlternateIdx = 0xffffffff;
										printf("Existing star '%.20s' labelled with alternate name '%.20s' at sector %i,%i,%i with flags %x by discoverer %i.\n", lastEntry->name, name, sx, sy, sz, (int)flags, discoverer);
										
									} else {
										//Include the new one with 'local only' marked.
										Starmap3GenericEntry *generic = NewLocalOnlyEntry(flags|0x80|0x40, discoverer, name);
										Starmap3StarEntry *star = NewStarEntry(sx, sy, sz, data, 0);
										generic->starIdx=star->idx;
										star->nextAlternateIdx = 0xffffffff;
										star->prevAlternateIdx = 0xffffffff;
										printf("There are 3 entries already for this star, so we're including this new entry as a local only one, which will only be in the discoverer's starmap: Existing star '%.20s' would have been labelled with alternate name '%.20s' at sector %i,%i,%i with flags %x by discoverer %i.\n", lastEntry->name, name, sx, sy, sz, (int)flags, discoverer);
									}
								} else {
									printf("User '%s' sent invalid star entry '%.20s', sector coords %i,%i,%i\n", outboxUsername, name, sx, sy, sz);
									userEntries[userIdx].invalidEntriesSubmitted++;
								}
							} else {
								if (DoesStarExist(sx, sy, sz)) {	//this calls PrepareFromSectorCoords too.
									//Calculate & validate data.
									extract_foundstar_infos();
									prepare_foundstar();
									int data_pType = (data&0xF);
									int data_pNum = ((data>>4)&0x1F);
									int data_mNum = ((data>>9)&0x1F);
									int bNum = CalcAndVerifyBodyNumber(data_pNum, data_mNum, sx, sy, sz);
									if (bNum==0xff || foundstar_p_type[bNum]!=data_pType) {
										//invalid
										printf("User '%s' sent invalid body entry '%.20s', sector coords %i,%i,%i. Sent pType=%i pNum=%i mNum=%i calculated bNum=%i, for ", outboxUsername, name, sx, sy, sz, data_pType, data_pNum, data_mNum, bNum);
										if (data_mNum==0x1f) {
											printf("planet %i.", data_pNum);
										} else {
											printf("moon %i of planet %i.", data_mNum, data_pNum);
										}
										if (bNum==0xff) {
											if (data_mNum==0x1f) {
												printf(" There is no such planet.\n");
											} else {
												printf(" There is no such moon.\n");
											}
										} else {
											printf(" It is the wrong type (actual type %i).\n", data_pType);
										}
										userEntries[userIdx].invalidEntriesSubmitted++;
									} else {
										//valid!
										//search for its star first.
										searchKey->sx=sx;
										searchKey->sy=sy;
										searchKey->sz=sz;
										searchKey->inflatedBodyNumber=0;
										searchKey->deleted=False;
										void *result = NULL;
										int resVal = combinedSearch(searchKey, starmapSortedByPos, sortedNumGSEntries, sizeof(Starmap3Pointer),
											(int (__CLIB *)
											(const void *,const void *))FindPos,
											genericStarmapEntries, sortedNumGSEntries, numGSEntries, sizeof(Starmap3GenericEntry), (int (__CLIB *)
											(const void *,const void *))FindPosEntry, &result);
										Starmap3GenericEntry *starEntry = ExtractGenericEntry(resVal, result);
										if (starEntry==NULL) {
											printf("User '%s' sent invalid entry '%.20s', which is a body for a system that we don't know about (sector coods: %i, %i, %i.\n", outboxUsername, name, sx, sy, sz);
											userEntries[userIdx].invalidEntriesSubmitted++;
										} else {
											//We found the star.
											searchKey->inflatedBodyNumber=GetInflatedBodyNumber(data);
											searchKey->deleted=False;
											int resVal = combinedSearch(searchKey, starmapSortedByPos, sortedNumGSEntries, sizeof(Starmap3Pointer),
												(int (__CLIB *)
												(const void *,const void *))FindPos,
												genericStarmapEntries, sortedNumGSEntries, numGSEntries, sizeof(Starmap3GenericEntry), (int (__CLIB *)
												(const void *,const void *))FindPosEntry, &result);
											int resultcount=0;
											Starmap3GenericEntry *entry = ExtractGenericEntry(resVal, result);
											Starmap3GenericEntry *lastEntry = entry;
											while (entry!=NULL) {
												if (entry->discoverer==discoverer && memcmp(name, entry->name, 20)==0) {
													Uchar oldflags=entry->flags;
													if (oldflags|0x80==flags) {
														printf("Body entry '%.20s' by discoverer %i is already in the starmap, and is unchanged in this outbox, so we're ignoring it.\n", entry->name, discoverer);
													} else {
														printf("Updating body entry '%.20s' by discoverer %i.\n", name, discoverer);
														entry->flags=flags;
														printf("\tChanged flags from %x to %x.\n", (int)oldflags, (int)entry->flags);
													}
													if (flags&0x20) {
														printf("\tDeleted entry [%.20s] by discoverer %i.\n", entry->name, discoverer);
														DeletePlanet(entry);
														
													}
													resultcount=-1;
													lastEntry=entry;
													entry=NULL;
												} else {
													resultcount++;
													lastEntry = entry;
													if (entry->flags&0x8) {
														entry=entry->star()->nextAlternate();
													} else {
														entry=entry->body()->nextAlternate();
													}
												}
											}
											
											if (resultcount==0 && (!(flags&0x20))) {	//There are no entries for it already
												//Make a new entry and mark it 'include in inbox'.
												Udword filepos = starmap3NextRecordPos;
												starmap3NextRecordPos+=35;
												Starmap3GenericEntry *generic = NewGenericEntry(filepos, flags, discoverer, name);
												Starmap3BodyEntry *body = NewBodyEntry(0, data, 0);
												body->starIdx = starEntry->idx;
												generic->bodyIdx=body->idx;
												body->nextAlternateIdx = 0xffffffff;
												body->prevAlternateIdx = 0xffffffff;
												printf("New body labelled '%.20s' at sector %i,%i,%i with flags %x and data %x by discoverer %i.\n", name, sx, sy, sz, (int)flags, data, discoverer);
											} else if (resultcount==-1) {
												//It already exists, and we've updated it now.
											} else if (resultcount<3 && (!(flags&0x20))) {
												//Make a new entry, mark it 'include in inbox', and link the old entry to it (as an alternate name).
												Udword filepos = starmap3NextRecordPos;
												starmap3NextRecordPos+=35;
												Starmap3GenericEntry *generic = NewGenericEntry(filepos, flags, discoverer, name);
												Starmap3BodyEntry *body = NewBodyEntry(0, data, 0);
												body->starIdx = starEntry->idx;
												generic->bodyIdx=body->idx;
												lastEntry->body()->nextAlternateIdx = generic->idx;
												body->prevAlternateIdx = lastEntry->idx;
												body->nextAlternateIdx = 0xffffffff;
												printf("Existing body '%.20s' labelled with alternate name '%.20s' at sector %i,%i,%i with flags %x by discoverer %i.\n", lastEntry->name, name, sx, sy, sz, (int)flags, discoverer);
											} else {
												//Include the new one with 'local only' marked.
												Starmap3GenericEntry *generic = NewLocalOnlyEntry(flags|0x80|0x40, discoverer, name);
												Starmap3BodyEntry *body = NewBodyEntry(0, data, 0);
												body->starIdx = starEntry->idx;
												generic->bodyIdx=body->idx;
												body->nextAlternateIdx = 0xffffffff;
												body->prevAlternateIdx = 0xffffffff;
												printf("There are 3 entries already for this body, so we're including this new entry as a local only one, which will only be in the discoverer's starmap: Existing body '%.20s' would have been labelled with alternate name '%.20s' at sector %i,%i,%i with flags %x by discoverer %i.\n", lastEntry->name, name, sx, sy, sz, (int)flags, discoverer);
											}
																					
										}
									}
									
									
								} else {
									printf("User '%s' sent invalid entry '%.20s', sector coords %i,%i,%i\n", outboxUsername, name, sx, sy, sz);
									userEntries[userIdx].invalidEntriesSubmitted++;
								}
							}
							entryNum++;
						}
						char guideEntryText[77];
						entryNum=0;
						Uword spn, smn;
						while (entryNum < outboxNumGuideEntries) {
							DebugPrintf("\tcurFPos=%i Guide entryNum=%i outboxNumMapEntries=%i\n", curFPos, entryNum, outboxNumMapEntries);
							curFPos = ftell(outboxFile);
							fread(&flags, 1, 1, outboxFile);
							//bit 7 is 'include in outbox', bit 6 is 'local only'. bit 5 is 'deleted'. The other bits aren't used at this time.
							flags=(flags&0x20)|0x80;	//We want to keep bit 5 only.
							fread(&sx, 4, 1, outboxFile);
							fread(&sy, 4, 1, outboxFile);
							fread(&sz, 4, 1, outboxFile);
							fread(&spn, 2, 1, outboxFile);
							fread(&smn, 2, 1, outboxFile);
							fread(guideEntryText, 76, 1, outboxFile);
							guideEntryText[76]=0;
							int found=0;
							for (int a=0; a<numGEntries; a++) {
								if (guideEntries[a].subjectsx==sx && guideEntries[a].subjectsy==sy && guideEntries[a].subjectsz==sz && guideEntries[a].subject_pNum==spn && guideEntries[a].subject_mNum==smn && memcmp(guideEntryText, guideEntries[a].entry, 76)==0 && discoverer==guideEntries[a].writer) {
									guideEntries[a].flags=flags;
									found=1;
									break;
								}
							}
							if (found==0) {
								NewGuideEntry(flags, sx, sy, sz, spn, smn, discoverer, guideEntryText);
							}
							entryNum++;
						}
					}
				}
				fclose(outboxFile); outboxFile=NULL;
				
			}
		}
		printf("Preparing inbox.\n");
		starmap3Version++;
		user3Version++;
		guide3Version++;
		sprintf(path, "%sinbox-%llx.sm3", folderPath, starmap3Version);
		inboxFile = fopen(path, "wb");
		if (inboxFile==NULL) {
			printf("Unable to write %s.\n", path);
			return;
		} else {
			printf("Writing %s.\n", path);
		}
		fileFormatVersion=1;
		fwrite(&fileFormatVersion, 2, 1, inboxFile);
		fwrite(&starmap3Version, 8, 1, inboxFile);
		Dword numEntriesPos = ftell(inboxFile);
		Dword zero = 0;
		fwrite(&zero, 4, 1, inboxFile);
		fwrite(&zero, 4, 1, inboxFile);
		fwrite(&zero, 4, 1, inboxFile);
		printf("Writing new user entries.\n");
		for (int idx=0; idx<numUEntries; idx++) {
			if (userEntries[idx].flags&0x80) {	//include in inbox
				fwrite(&(userEntries[idx].id), 4, 1, inboxFile);
				fwrite(userEntries[idx].name, 20, 1, inboxFile);
				userEntries[idx].flags=userEntries[idx].flags&~0x80;
				numUserEntries++;
			}
		}
		printf("Writing new starmap entries.\n");
		for (int idx=0; idx<numGSEntries+numLOSEntries; idx++) {
			Starmap3GenericEntry *entry = NULL;
			if (idx<numGSEntries) {
				entry = &(genericStarmapEntries[idx]);
			} else {
				entry = &(localOnlyStarmapEntries[idx-numGSEntries]);
			}
			if (entry->flags&0x80) {	//include in inbox
				entry->flags=entry->flags&~0x80;
				fwrite(&(entry->flags), 1, 1, inboxFile);
				fwrite(entry->name, 20, 1, inboxFile);
				fwrite(&(entry->discoverer), 4, 1, inboxFile);
				if (entry->flags&0x8) {
					fwrite(&(entry->star()->sx), 4, 1, inboxFile);
					fwrite(&(entry->star()->sy), 4, 1, inboxFile);
					fwrite(&(entry->star()->sz), 4, 1, inboxFile);
					fwrite(&(entry->star()->data), 2, 1, inboxFile);
				} else {
					fwrite(&(entry->body()->star()->star()->sx), 4, 1, inboxFile);
					fwrite(&(entry->body()->star()->star()->sy), 4, 1, inboxFile);
					fwrite(&(entry->body()->star()->star()->sz), 4, 1, inboxFile);
					fwrite(&(entry->body()->data), 2, 1, inboxFile);
				}
				numStarmapEntries++;
			}
		}
		printf("Writing new guide entries.\n");
		for (int idx=0; idx<numGEntries; idx++) {
			if (guideEntries[idx].flags&0x80) {	//include in inbox
				guideEntries[idx].flags=guideEntries[idx].flags&~0x80;
				fwrite(&(guideEntries[idx].flags), 1, 1, inboxFile);
				fwrite(&(guideEntries[idx].subjectsx), 4, 1, inboxFile);
				fwrite(&(guideEntries[idx].subjectsy), 4, 1, inboxFile);
				fwrite(&(guideEntries[idx].subjectsz), 4, 1, inboxFile);
				fwrite(&(guideEntries[idx].subject_pNum), 2, 1, inboxFile);
				fwrite(&(guideEntries[idx].subject_mNum), 2, 1, inboxFile);
				fwrite(&(guideEntries[idx].writer), 4, 1, inboxFile);
				fwrite((guideEntries[idx].entry), 76, 1, inboxFile);
				
				numGuideEntries++;
			}
		}
		//Write # users, starmap, and guide entries in inbox.
		printf("New user entries (%i), new starmap entries (%i), new guide entries (%i).\n", numUserEntries, numStarmapEntries, numGuideEntries);
		fseek(inboxFile, numEntriesPos, SEEK_SET);
		fwrite(&numUserEntries, 4, 1, inboxFile);
		fwrite(&numStarmapEntries, 4, 1, inboxFile);
		fwrite(&numGuideEntries, 4, 1, inboxFile);
		fclose(inboxFile); inboxFile=NULL;
		
		if (numStarmapEntries>0 || numGuideEntries>0 || numUserEntries>0) {
			//Write starmap, guide, users, version file.
			printf("Saving starmap, guide, users, and version file.\n");
			SaveStarmap(starmap3Version);
			SaveGuide(guide3Version);
			SaveUsers(user3Version);
			
			sprintf(path, "%sstarmap-version.txt", folderPath);
			FILE* versionFile = fopen(path, "wt");
			if (versionFile==NULL) {
				printf("Unable to write %s.\n", path);
				return;
			} else {
				printf("Writing %s.\n", path);
				fprintf(versionFile, "%llx", starmap3Version);
				fclose(versionFile);
			}
			
			//7zip the inbox file
			printf("7zipping the inbox file.\n");
			sprintf(path, "syncSmFiles\\lzma e %sinbox-%llx.sm3 %sinbox-%llx.7z", folderPath, starmap3Version, folderPath, starmap3Version);
			system(path);
			
			printf("\nDone.\n");
		} else {
			sprintf(path, "%sinbox-%llx.sm3", folderPath, starmap3Version);
			remove(path);
			printf("Cancelled creating inbox, files not updated, because there's nothing new.");
		}
	#else
		printf("Attempting to download starmap-version.txt\n");
		downloadComplete=False;
		int hresult = URLDownloadToCacheFile(NULL, "http://home.deds.nl/~megagun/starmap/starmap-version.txt", path, sizeof(path), 0, &downloadCallback);

		if (hresult!=S_OK) {
			printf("Download failed!\n");
			//printf("hresult=%x. S_OK=%x E_FAIL=%x E_OUTOFMEMORY=%x\n", hresult, S_OK, E_FAIL, E_OUTOFMEMORY);
			return;
		} else{
			while (!downloadComplete) {
				//printf("Sleeping, download not complete.\n");
				Sleep(1);
			}
			printf("Download complete.\n");
		}
		Udword userIdx = 0xffffffff;
		for (Udword idx=0; idx<numUEntries; idx++) {
			if (memcmp(usernameBuffer, userEntries[idx].name, 20)==0) {
				userIdx=idx;
				break;
			}
		}
		
		FILE * versionFile = fopen(path, "rb");
		//The starmap-version file contains the version number of the latest starmap written in a string in hex (e.g. it's human-readable).
		Uqword latestVersionNumber = 0;
		while (!feof(versionFile)) {
			char c = fgetc(versionFile);
			if (c==-1) {
				break;
			} else {
				latestVersionNumber<<=4;	//Since the version string is hex, we would multiply *16 before adding the next digit. <<4 does the same thing as *16, but does it faster.
				if (c>='a' && c<='z') {
					latestVersionNumber+=(Udword)(c-'a')+10;
				} else if (c>='A' && c<='Z') {
					latestVersionNumber+=(Udword)(c-'A')+10;
				} else if (c>='0' && c<='9') {
					latestVersionNumber+=(Udword)(c-'0');
				} else {
					latestVersionNumber>>=4;	//undo the shift we did earlier
					if (c=='\n') {	//If it's an endline we don't print an error, we just stop processing the file.
						break;
					} else {
						printf("starmap-version.txt contains unexpected character '%c' (%i)! You should check to see if there's a new version of NICE available.\n", c, (int)c);
					}
					
				}
			}
		}
		fclose(versionFile); versionFile=NULL;
		
		//Check version numbers.
		Uqword lowestVersionNumber = starmap3Version;
		if (guide3Version<lowestVersionNumber) lowestVersionNumber=guide3Version;
		if (user3Version<lowestVersionNumber) lowestVersionNumber=user3Version;
		
		if (user3Version!=starmap3Version) {
			printf("Warning: starmap3.usr version (%llx) does not match starmap3.map version (%llx)!\n", user3Version, starmap3Version);
		}
		if (guide3Version!=starmap3Version) {
			printf("Warning: starmap3.gd version (%llx) does not match starmap3.map version (%llx)!\n", guide3Version, starmap3Version);
		}
		if (lowestVersionNumber==0xffffffffffffffff) {
			printf("AHHHH! ACK! OUT OF VERSION NUMBERS!\n");
			return;
		}
		if (lowestVersionNumber<starmap3Version) {
			Uqword lvn = (lowestVersionNumber+1);
			Uqword s3v = (starmap3Version+1);
			printf("We're going to start with inbox %llx instead of %llx, since some of our files are not up to date.\n", lvn, s3v);
		}
		printf("Latest starmap version: %llx. We have: %llx.\n", latestVersionNumber, lowestVersionNumber);
		//See if there are any new inboxes, by comparing the version from starmap-version to our version.
		
		char path2[256];
		while (lowestVersionNumber<latestVersionNumber) {
			Uqword tmpVer = lowestVersionNumber+1;
			//Download inbox from http://home.deds.nl/~megagun/starmap/
			printf("Attempting to download inbox %llx\n", tmpVer);
			downloadComplete=False;
			sprintf(path2, "http://home.deds.nl/~megagun/starmap/inbox-%llx.7z", tmpVer);
			int hresult = URLDownloadToCacheFile(NULL, path2, path, sizeof(path), 0, &downloadCallback);
			if (hresult!=S_OK) {
				printf("Download failed!\n");
				//printf("hresult=%x. S_OK=%x E_FAIL=%x E_OUTOFMEMORY=%x\n", hresult, S_OK, E_FAIL, E_OUTOFMEMORY);
				continue;
			} else{
				while (!downloadComplete) {
					//printf("Sleeping, download not complete.\n");
					Sleep(1);
				}
				printf("Download complete.\n");
			}
			
			//Unzip inbox.7z to inbox.sm3, open inbox.sm3.
			
			printf("Un-7zipping inbox %llx.\n", tmpVer);
			sprintf(path2, "syncSmFiles\\lzma d \"%s\" inbox-%llx.sm3", path, tmpVer);
			system(path2);
			printf("Done un-7zipping.\n");
			
			sprintf(path, "inbox-%llx.sm3", tmpVer);
			FILE * inboxFile = fopen(path, "rb");
			
			Uqword inboxVersion = 0;
			//Read version #, and number of map, guide, and user entries.
			//int testfilepos = ftell(inboxFile); printf("We are at position %i.\n", testfilepos);
			fread(&fileFormatVersion, 2, 1, inboxFile);
			if (fileFormatVersion>1) {
				printf("This inbox is a newer format than we can understand. You need a new version of SyncStarmap.\n");
				return;
			} else {
				//testfilepos = ftell(inboxFile); printf("We are now at position %i.\n", testfilepos);
				fread(&inboxVersion, 8, 1, inboxFile);
				//testfilepos = ftell(inboxFile); printf("And now at position %i. (Inbox version is %llx)\n", testfilepos, inboxVersion);
				Udword numUserEntries;
				Udword numMapEntries;
				Udword numGuideEntries;
				fread(&numUserEntries, 4, 1, inboxFile);
				fread(&numMapEntries, 4, 1, inboxFile);
				fread(&numGuideEntries, 4, 1, inboxFile);
				//testfilepos = ftell(inboxFile); printf("Finally, position %i.\n", testfilepos);
				//printf("numUserEntries(%i) numMapEntries(%i) numGuideEntries(%i)\n", numUserEntries, numMapEntries, numGuideEntries);
				//return;
				
				/*
				Read user entries and merge them.
				If our username is in the entries to be merged, record its user ID#.
				*/
				
				Udword drifter_id;
				char drifter_name[20];
				for (int newEntryNum=0; newEntryNum<numUserEntries; newEntryNum++) {
					//printf("newEntryNum(%i) numUserEntries(%i)\n", newEntryNum, numUserEntries);
					fread(&drifter_id, 4, 1, inboxFile);
					fread(drifter_name, 20, 1, inboxFile);
					int idx = NewUserEntry(drifter_id, drifter_name);
					if (memcmp(drifter_name, usernameBuffer, 20)==0) {
						userIdx=idx;
					}
				}
				printf("Done adding new user entries.\n");
	
				
				Uchar flags;
				char name[20];
				Udword discoverer;
				Dword sx, sy, sz;
				Uword data;
				
				//Read map entries and merge them
				for (int newEntryNum=0; newEntryNum<numMapEntries; newEntryNum++) {
					fread(&flags, 1, 1, inboxFile);
					fread(name, 20, 1, inboxFile);
					fread(&discoverer, 4, 1, inboxFile);
					fread(&sx, 4, 1, inboxFile);
					fread(&sy, 4, 1, inboxFile);
					fread(&sz, 4, 1, inboxFile);
					fread(&data, 2, 1, inboxFile);
					
					/*
					For each entry:
						If the discoverer (in the inbox) is us (matches our previously recorded user ID#), unmark 'Sent but not received' if it is set.
						If the entry in the inbox is marked 'local only' carry that over to our copy (IF it's our entry).
						Don't overwrite an entry of our own if we have 'include in outbox' set.
						If the discoverer is NOT us, but we have the object in our map already, make a linked entry for the new one.
						Local only entries which weren't made by us should be ignored.
					*/
					//-------------------------------------------------------------------
					if (!(flags&0x40) || discoverer==userEntries[userIdx].id) {
						if (flags&0x8) {	//star
							//make sure it's legal
							if (DoesStarExist(sx, sy, sz)) {	//this calls PrepareFromSectorCoords too.
								//Calculate data.
								extract_foundstar_infos();
								prepare_foundstar();
								//stellar class, number of planets, number of moons
								//data = (foundstar_class&0xF) | ((foundstar_nop&0x1F)<<4) | (((foundstar_nob-foundstar_nop)&0x7F)<<9);
								
								searchKey->sx=sx;
								searchKey->sy=sy;
								searchKey->sz=sz;
								searchKey->inflatedBodyNumber=0;
								searchKey->deleted=False;
								void *result = NULL;
								int resVal = combinedSearch(searchKey, starmapSortedByPos, sortedNumGSEntries, sizeof(Starmap3Pointer),
									(int (__CLIB *)
									(const void *,const void *))FindPos,
									genericStarmapEntries, sortedNumGSEntries, numGSEntries, sizeof(Starmap3GenericEntry), (int (__CLIB *)
									(const void *,const void *))FindPosEntry, &result);
								Starmap3GenericEntry * entry = ExtractGenericEntry(resVal, result);
								if (entry==NULL && flags&0x20) {
									searchKey->deleted=True;
									result = NULL;
									resVal = combinedSearch(searchKey, starmapSortedByPos, sortedNumGSEntries, sizeof(Starmap3Pointer),
										(int (__CLIB *)
										(const void *,const void *))FindPos,
										genericStarmapEntries, sortedNumGSEntries, numGSEntries, sizeof(Starmap3GenericEntry), (int (__CLIB *)
										(const void *,const void *))FindPosEntry, &result);
									entry = ExtractGenericEntry(resVal, result);
								}
								Starmap3GenericEntry * lastEntry=entry;
								Dword resultcount=0;
								while (entry!=NULL) {
									if (entry->discoverer==0xFFFFFFFE) {	//This means "Us!"
										entry->discoverer=userEntries[userIdx].id;
									}
									if (entry->discoverer==discoverer) {
										DebugPrintf("entry->discoverer now=%x.\n", entry->discoverer);
										if (discoverer==userEntries[userIdx].id) {	//It's us
											entry->flags=entry->flags&~0x04;	//unmark 'Sent but not received' if it is set.
											if (flags&0x40) { //local only
												entry->flags|=0x40;
												entry->flags=entry->flags&~0x80;	//Unmark 'include in outbox'
											}
										}
										if (discoverer==userEntries[userIdx].id || !(flags&0x40)) {
											if (entry->flags&0x80) {	//'include in outbox'
												//We've changed the entry, so we can't merge it.
												entry=NULL;
												resultcount=-1;
											} else {
												memcpy(entry->name, name, 20);
												if (flags&0x20 && !(entry->flags&0x20)) {
													DeleteStar(entry);
												}
												
												entry->flags=flags;
												lastEntry=entry;
												entry=NULL;
												resultcount=-1;
											}
										}
									}
									if (entry!=NULL) {
										resultcount++;
										lastEntry = entry;
										if (entry->flags&0x8) {
											entry=entry->star()->nextAlternate();
										} else {
											entry=entry->body()->nextAlternate();
										}
									}
								}
								if (resultcount==0) {	//There are no entries for it already
									//Make a new entry.
									Udword filepos = starmap3NextRecordPos;
									starmap3NextRecordPos+=43;
									Starmap3GenericEntry *generic = NewGenericEntry(filepos, flags, discoverer, name);
									Starmap3StarEntry *star = NewStarEntry(sx, sy, sz, data, 0);
									star->nextAlternateIdx = 0xffffffff;
									star->prevAlternateIdx = 0xffffffff;
									generic->starIdx=star->idx;
								} else if (resultcount==-1) {
									//It already exists, and we've updated it now, or it can't be merged.
								} else {
									//Make a new entry, mark it 'include in inbox', and link the old entry to it (as an alternate name).
									Udword filepos = starmap3NextRecordPos;
									starmap3NextRecordPos+=43;
									Starmap3GenericEntry *generic = NewGenericEntry(filepos, flags, discoverer, name);
									Starmap3StarEntry *star = NewStarEntry(sx, sy, sz, data, 0);
									generic->starIdx=star->idx;
									star->nextAlternateIdx = 0xffffffff;
									lastEntry->star()->nextAlternateIdx = generic->idx;
									star->prevAlternateIdx = lastEntry->idx;
								}
							} else {
								printf("Inbox contains invalid star entry '%.20s', sector coords %i,%i,%i\n", name, sx, sy, sz);
							}
						} else {
							if (DoesStarExist(sx, sy, sz)) {	//this calls PrepareFromSectorCoords too.
								//Calculate & validate data.
								extract_foundstar_infos();
								prepare_foundstar();
								int data_pType = (data&0xF);
								int data_pNum = ((data>>4)&0x1F);
								int data_mNum = ((data>>9)&0x1F);
								int bNum = CalcAndVerifyBodyNumber(data_pNum, data_mNum, sx, sy, sz);
								
								//search for its star first.
								searchKey->sx=sx;
								searchKey->sy=sy;
								searchKey->sz=sz;
								searchKey->inflatedBodyNumber=0;
								searchKey->deleted=False;
								void *result = NULL;
								int resVal = combinedSearch(searchKey, starmapSortedByPos, sortedNumGSEntries, sizeof(Starmap3Pointer),
									(int (__CLIB *)
									(const void *,const void *))FindPos,
									genericStarmapEntries, sortedNumGSEntries, numGSEntries, sizeof(Starmap3GenericEntry), (int (__CLIB *)
									(const void *,const void *))FindPosEntry, &result);
								Starmap3GenericEntry *starEntry = ExtractGenericEntry(resVal, result);
								if (starEntry==NULL && flags&0x20) {
									searchKey->deleted=True;
									result = NULL;
									resVal = combinedSearch(searchKey, starmapSortedByPos, sortedNumGSEntries, sizeof(Starmap3Pointer),
										(int (__CLIB *)
										(const void *,const void *))FindPos,
										genericStarmapEntries, sortedNumGSEntries, numGSEntries, sizeof(Starmap3GenericEntry), (int (__CLIB *)
										(const void *,const void *))FindPosEntry, &result);
									starEntry = ExtractGenericEntry(resVal, result);
								}
								if (starEntry==NULL) {
									printf("Inbox contains invalid entry '%.20s', which is a body for a system that we don't know about (sector coods: %i, %i, %i.\n", name, sx, sy, sz);
								} else {
									//We found the star.
									
									searchKey->inflatedBodyNumber=GetInflatedBodyNumber(data);
									searchKey->deleted=False;
									int resVal = combinedSearch(searchKey, starmapSortedByPos, sortedNumGSEntries, sizeof(Starmap3Pointer),
										(int (__CLIB *)
										(const void *,const void *))FindPos,
										genericStarmapEntries, sortedNumGSEntries, numGSEntries, sizeof(Starmap3GenericEntry), (int (__CLIB *)
										(const void *,const void *))FindPosEntry, &result);
									int resultcount=0;
									Starmap3GenericEntry *entry = ExtractGenericEntry(resVal, result);
									if (entry==NULL) DebugPrintf("Found no body entry.\n");
									if (entry==NULL && flags&0x20) {
										searchKey->deleted=True;
										result = NULL;
										resVal = combinedSearch(searchKey, starmapSortedByPos, sortedNumGSEntries, sizeof(Starmap3Pointer),
											(int (__CLIB *)
											(const void *,const void *))FindPos,
											genericStarmapEntries, sortedNumGSEntries, numGSEntries, sizeof(Starmap3GenericEntry), (int (__CLIB *)
											(const void *,const void *))FindPosEntry, &result);
										resultcount=0;
										entry = ExtractGenericEntry(resVal, result);
									}
									if (entry==NULL) DebugPrintf("Still no body entry.\n");
									Starmap3GenericEntry *lastEntry = entry;
									while (entry!=NULL) {
										DebugPrintf("entry->discoverer=%x.\n", entry->discoverer);
										if (entry->discoverer==0xFFFFFFFE) {	//This means "Us!"
											entry->discoverer=userEntries[userIdx].id;
										}
										DebugPrintf("entry->discoverer now=%x.\n", entry->discoverer);
										if (entry->discoverer==discoverer) {
											if (discoverer==userEntries[userIdx].id) {	//It's us
												entry->flags=entry->flags&~0x04;	//unmark 'Sent but not received' if it is set.
												if (flags&0x40) { //local only
													entry->flags|=0x40;
													entry->flags=entry->flags&~0x80;	//Unmark 'include in outbox'
												}
											}
											if (discoverer==userEntries[userIdx].id || !(flags&0x40)) {
												if (entry->flags&0x80) {	//'include in outbox'
													//We've changed the entry, so we can't merge it.
													resultcount=-1;
													lastEntry=entry;
													entry=NULL;
												} else {
													memcpy(entry->name, name, 20);
													if (flags&0x20 && !(entry->flags&0x20)) {
														DeletePlanet(entry);
													}
													entry->flags=flags;
													resultcount=-1;
													lastEntry=entry;
													entry=NULL;
												}
											}
										}
										if (entry!=NULL) {
											resultcount++;
											lastEntry = entry;
											if (entry->flags&0x8) {
												entry=entry->star()->nextAlternate();
											} else {
												entry=entry->body()->nextAlternate();
											}
										}
									}
									DebugPrintf("Resultcount is %i, and ", resultcount);
									if (entry==NULL) {
										DebugPrintf("entry is null, and ");
									} else {
										DebugPrintf("entry is [%.20s], and ", entry->name);
									}
									if (lastEntry==NULL) {
										DebugPrintf("lastEntry is null.\n");
									} else {
										DebugPrintf("lastEntry is [%.20s].\n", lastEntry->name);
									}
									if (resultcount==0) {	//There are no entries for it already
										//Make a new entry and mark it 'include in inbox'.
										Udword filepos = starmap3NextRecordPos;
										starmap3NextRecordPos+=35;
										Starmap3GenericEntry *generic = NewGenericEntry(filepos, flags, discoverer, name);
										Starmap3BodyEntry *body = NewBodyEntry(0, data, 0);
										body->starIdx = starEntry->idx;
										body->nextAlternateIdx = 0xffffffff;
										body->prevAlternateIdx = 0xffffffff;
										generic->bodyIdx=body->idx;
									} else if (resultcount==-1) {
										//It already exists, and we've updated it now, or we can't merge it.
									} else {
										//Make a new entry, mark it 'include in inbox', and link the old entry to it (as an alternate name).
										Udword filepos = starmap3NextRecordPos;
										starmap3NextRecordPos+=35;
										Starmap3GenericEntry *generic = NewGenericEntry(filepos, flags, discoverer, name);
										Starmap3BodyEntry *body = NewBodyEntry(0, data, 0);
										body->starIdx = starEntry->idx;
										generic->bodyIdx=body->idx;
										body->nextAlternateIdx = 0xffffffff;
										lastEntry->body()->nextAlternateIdx = generic->idx;
										body->prevAlternateIdx = lastEntry->idx;
									}
									
								}
							} else {
								printf("Inbox contains invalid entry '%.20s', sector coords %i,%i,%i\n", name, sx, sy, sz);
							}
						}
					} else {
						DebugPrintf("Ignoring local-only entry [%.20s] which belongs to another drifter (%i, we're %i)\n", name, discoverer, userEntries[userIdx].id);
					}
					//-------------------------------------------------------------------
				}
				//Read guide entries and merge them
				Dword subjectsx;
				Dword subjectsy;
				Dword subjectsz;
				Uword subject_pNum;
				Uword subject_mNum;
				Udword writer;	//the person who wrote the entry
				char guideEntryText[76];
				for (int newEntryNum=0; newEntryNum<numGuideEntries; newEntryNum++) {
					fread(&flags, 1, 1, inboxFile);
					fread(&subjectsx, 4, 1, inboxFile);
					fread(&subjectsy, 4, 1, inboxFile);
					fread(&subjectsz, 4, 1, inboxFile);
					fread(&subject_pNum, 2, 1, inboxFile);
					fread(&subject_mNum, 2, 1, inboxFile);
					fread(&writer, 4, 1, inboxFile);
					fread(guideEntryText, 76, 1, inboxFile);
					int found = 0;
					for (int a=0; a<numGEntries; a++) {
						//DebugPrintf("Compare sx=%i sy=%i sz=%i pn=%i mn=%i [%.76s]\n to new sx=%i sy=%i sz=%i pn=%i mn=%i [%.76s].\n", guideEntries[a].subjectsx, guideEntries[a].subjectsy, guideEntries[a].subjectsz, guideEntries[a].subject_pNum, guideEntries[a].subject_mNum, guideEntries[a].entry, subjectsx, subjectsy, subjectsz, subject_pNum, subject_mNum, guideEntryText);
						if (guideEntries[a].subjectsx==subjectsx && guideEntries[a].subjectsy==subjectsy && guideEntries[a].subjectsz==subjectsz && guideEntries[a].subject_pNum==subject_pNum && guideEntries[a].subject_mNum==subject_mNum && memcmp(guideEntryText, guideEntries[a].entry, 76)==0) {
							//DebugPrintf("Got existing guide entry.\n");
							guideEntries[a].flags=flags;
							guideEntries[a].writer=writer;
							found=1;
							break;
						}
					}
					if (found==0) {
						//DebugPrintf("Got new guide entry.\n");
						NewGuideEntry(flags, subjectsx, subjectsy, subjectsz, subject_pNum, subject_mNum, writer, guideEntryText);
					}
				}
			}
			
			fclose(inboxFile);
			
			//Remake the sorted pointer arrays.
			delete [] starmapSortedByPos;
			starmapSortedByPos = new Starmap3Pointer[numGSEntries];
			for (int a=0; a<numGSEntries; a++) {
				starmapSortedByPos[a].entryIdx = a;
			}
			qsort(starmapSortedByPos, numGSEntries, sizeof(Starmap3Pointer),
				(int (__CLIB *)
				(const void *,const void *))ComparePos);
			delete [] starmapSortedByName;
			starmapSortedByName = new Starmap3Pointer[numGSEntries];
			for (int a=0; a<numGSEntries; a++) {
				starmapSortedByName[a].entryIdx = a;
			}
			qsort(starmapSortedByName, numGSEntries, sizeof(Starmap3Pointer),
				(int (__CLIB *)
				(const void *,const void *))CompareName);
			sortedNumGSEntries = numGSEntries;
			
			//Save map, guide, and user files.
			SaveStarmap(inboxVersion);
			SaveGuide(inboxVersion);
			SaveUsers(inboxVersion);
			lowestVersionNumber=inboxVersion;
		}
		//Clean up never-sent deleted entries.
		int doOutbox=0;
		printf("Looking for deleted entries that we can clean up.\n");
		for (int idx=0; idx<numGSEntries; idx++) {
			Uchar flags = genericStarmapEntries[idx].flags;
			if ((flags&0x40) && (flags&0x80)) {	//Special condition which means this entry has never been sent in an outbox. We'll clear this now.
				//So: 0xC0=Never sent, 0xC8=Never sent star, 0xE8=Never sent deleted star.
				if (flags&0x20) {	//entry is deleted
					int isStar = genericStarmapEntries[idx].flags&0x8;
					//Cleanup on aisle idx!
					//Assumuptions which should be true:
					//	Given an entry 'B' which refers to an entry 'A':
					//		1. B can never be before A in the file.
					//		2. B can never be sent in an outbox before A is sent.
					//		3. If B is a planet orbiting A, B should be deleted if A is deleted.
					//		4. B cannot refer to A as an alternate entry because alternate entries refer from existing entries to a new one, so it would be A to B.
					printf("Cleaning up deleted entry %i (%0.20s).\n", idx, genericStarmapEntries[idx].name);
					Udword filePosition = genericStarmapEntries[idx].filePosition;
					Udword adjust = (isStar?43:35);
					for (int idx2=0; idx2<numGSEntries; idx2++) {
						if (idx2!=idx) {
							if (genericStarmapEntries[idx2].filePosition>filePosition) {
								genericStarmapEntries[idx2].filePosition-=adjust;
							}
						}
					}
					genericStarmapEntries[idx].filePosition=0;
				} else {
					DebugPrintf("Not cleaning up [%.20s] - it isn't deleted.\n", genericStarmapEntries[idx].name);
					flags=flags&~0x40;
					genericStarmapEntries[idx].flags=flags;
				}
			}
			if (!doOutbox) {
				if ((flags&0x80 || flags&0x4) && !(flags&0x40)) {
					doOutbox=1;
				}
			}
		}
		
		if (!doOutbox) {
			for (int idx=0; idx<numGEntries; idx++) {
				//Find entries in guide which have 'include in outbox' or 'sent but not received' but NOT 'local only', to go in the outbox.
				Uchar flags = guideEntries[idx].flags;
				if ((flags&0x40) && (flags&0x80)) {	//Special condition which means this entry has never been sent in an outbox. We'll clear this now.
					if (flags&0x20) {	//entry is deleted
						//don't clear it, we leave the flags so it doesn't get saved.
					} else {
						flags=flags&~0x40;
						guideEntries[idx].flags=flags;
					}
				}
				if ((flags&0x80 || flags&0x4) && !(flags&0x40)) {
					doOutbox=1;
					break;
				}
			}
		}
		
		if (!doOutbox) {
			printf("No entries need to be outboxed, so we're done.\n");
			MessageBox(NULL, "No entries need to be outboxed, so we're done.", "Sync-Starmap", MB_OK);
		} else {
			printf("Creating outbox.\n");
			//Create outbox.sm3 file
			sprintf(path, "outbox.sm3");
			outboxFile = fopen(path, "wb");
			if (outboxFile==NULL) {
				printf("Unable to write %s.\n", path);
				return;
			} else {
				printf("Writing %s.\n", path);
			}
			fileFormatVersion=1;
			//Write version, username, and password, and 0 for the map and guide num entries.
			fwrite(&fileFormatVersion, 2, 1, outboxFile);
			fwrite(&lowestVersionNumber, 8, 1, outboxFile);
			fwrite(usernameBuffer, 20, 1, outboxFile);
			fwrite(passwordBuffer, 20, 1, outboxFile);
			Dword numEntriesPos = ftell(outboxFile);
			Dword numStarmapEntries=0;
			Dword numGuideEntries=0;
			Dword zero = 0;
			fwrite(&zero, 4, 1, outboxFile);
			fwrite(&zero, 4, 1, outboxFile);
			
			printf("Outboxing starmap entries.\n");
			for (int idx=0; idx<numGSEntries; idx++) {
				//Find entries in starmap3 which have 'include in outbox' or 'sent but not received' but NOT 'local only', to go in the outbox.
				Uchar flags = genericStarmapEntries[idx].flags;
				if ((flags&0x80 || flags&0x4) && !(flags&0x40)) {
					DebugPrintf("Writing entry %0.20s with flags %x.\n", genericStarmapEntries[idx].name, (int)flags);
					//Write it out
					//Mark it 'sent but not received', and unmark 'include in outbox'.
					flags=(flags&~0x80)|0x4;
					genericStarmapEntries[idx].flags=flags;
					fwrite(&(genericStarmapEntries[idx].flags), 1, 1, outboxFile);
					fwrite(genericStarmapEntries[idx].name, 20, 1, outboxFile);
					if (genericStarmapEntries[idx].flags&0x8) {
						fwrite(&(genericStarmapEntries[idx].star()->sx), 4, 1, outboxFile);
						fwrite(&(genericStarmapEntries[idx].star()->sy), 4, 1, outboxFile);
						fwrite(&(genericStarmapEntries[idx].star()->sz), 4, 1, outboxFile);
						fwrite(&(genericStarmapEntries[idx].star()->data), 2, 1, outboxFile);
					} else {
						fwrite(&(genericStarmapEntries[idx].body()->star()->star()->sx), 4, 1, outboxFile);
						fwrite(&(genericStarmapEntries[idx].body()->star()->star()->sy), 4, 1, outboxFile);
						fwrite(&(genericStarmapEntries[idx].body()->star()->star()->sz), 4, 1, outboxFile);
						fwrite(&(genericStarmapEntries[idx].body()->data), 2, 1, outboxFile);
					}
					numStarmapEntries++;
					
				} else {
					if (flags&0x40) {
						DebugPrintf("Ignoring deleted entry %i, %0.20s with flags %x.\n", idx, genericStarmapEntries[idx].name, (int)flags);
					} else {
						//DebugPrintf("Ignoring entry %0.20s with flags %x.\n", genericStarmapEntries[idx].name, (int)flags);
					}
				}
			}
			printf("Outboxing guide entries.\n");
			for (int idx=0; idx<numGEntries; idx++) {
				//Find entries in guide which have 'include in outbox' or 'sent but not received' but NOT 'local only', to go in the outbox.
				Uchar flags = guideEntries[idx].flags;
				//Bool reallyDelete = flags&0x20 && flags&0x80 && flags&0x40;
				//if (!reallyDelete && (flags&0x80 || flags&0x4) && !(flags&0x40)) {
				if ((flags&0x80 || flags&0x4) && !(flags&0x40)) {
					flags=(flags&~0x80)|0x4;
					guideEntries[idx].flags=flags;
					fwrite(&(guideEntries[idx].flags), 1, 1, outboxFile);
					fwrite(&(guideEntries[idx].subjectsx), 4, 1, outboxFile);
					fwrite(&(guideEntries[idx].subjectsy), 4, 1, outboxFile);
					fwrite(&(guideEntries[idx].subjectsz), 4, 1, outboxFile);
					fwrite(&(guideEntries[idx].subject_pNum), 2, 1, outboxFile);
					fwrite(&(guideEntries[idx].subject_mNum), 2, 1, outboxFile);
					fwrite((guideEntries[idx].entry), 76, 1, outboxFile);
					
					numGuideEntries++;
				}
			}
			//Write # users, starmap, and guide entries in inbox.
			printf("Outboxed %i starmap entries and %i guide entries.\n", numStarmapEntries, numGuideEntries);
			fseek(outboxFile, numEntriesPos, SEEK_SET);
			fwrite(&numStarmapEntries, 4, 1, outboxFile);
			fwrite(&numGuideEntries, 4, 1, outboxFile);
			fclose(outboxFile); outboxFile=NULL;
			
			printf("Done writing outbox.\n");
			//Zip outbox to outbox.7z. Delete outbox.sm3.
			//Tell user to send outbox.7z to jorisvddonk@gmail.com
			
			printf("7zipping the outbox file.\n");
			sprintf(path, "syncSmFiles\\lzma e outbox.sm3 outbox.7z");
			system(path);
			
			printf("\nDone.\n");
			sprintf(path, "We've written an 'outbox.7z' file in the same folder as Sync-Starmap.exe. Do NOT send this outbox file through the 'tell us' form at AnywhereBB!!! Please email the outbox.7z file to jorisvddonk@gmail.com. Thanks.\n");
			printf("\n\n%s\n", path);
			MessageBox(NULL, path, "Sync-Starmap", MB_OK);
		}
		//Save files again (since we fiddled with flags)
		RunStyracosaurus();	//saves a copy of the starmap in the starmap2 format for NoctisMapper
		SaveStarmap(lowestVersionNumber);
		SaveGuide(lowestVersionNumber);
		SaveUsers(lowestVersionNumber);
		
	#endif
}

void main() {
	main2();
	if (starmap3File!=NULL) {
		fclose(starmap3File);
		starmap3File=NULL;
	}
	if (guide3File!=NULL) {
		fclose(guide3File);
		guide3File=NULL;
	}
	if (user3File!=NULL) {
		fclose(user3File);
		user3File=NULL;
	}
	if (outboxFile!=NULL) {
		fclose(outboxFile);
		outboxFile=NULL;
	}
	if (inboxFile!=NULL) {
		fclose(inboxFile);
		inboxFile=NULL;
	}
	printf("Done. Press any key to continue.");
	getch();
	
}