unit FileInNOut;

interface
	uses
		Palettes, Globals, GlobalUtils;

	function ErrorCheckFile: Boolean;
	function OpenHouse: Boolean;
	function DoOpen (whichType: Integer): Boolean;
	procedure DoClose (volNum: Integer);
	function GetHouse: Boolean;
	function ReadGame: Boolean;
	function SaveGameAs: Boolean;
	function SaveGame: Boolean;
	function WriteHouse: Boolean;

implementation

	const
		dlgTop = 50;
		dlgLeft = 85;
	var
		typeIs: Integer;

{=================================}

	function ErrorCheckFile;
		var
			nRooms, index: Integer;

	begin
		ErrorCheckFile := FALSE;

		with thisHouse do
		begin

			nRooms := numberORooms;
			if ((nRooms < 1) or (nRooms > 40)) then	{check for valid # of rooms}
			begin
				Exit(ErrorCheckFile);
			end;

			for index := 1 to nRooms do				{check PICT ID numbers}
			begin
				if (theRooms[index].backPictID < 200) or (theRooms[index].backPictID > 215) then
				begin
					Exit(ErrorCheckFile);
				end;
			end;

		end;

		ErrorCheckFile := TRUE;
	end;

{=================================}

	function IOCheck (theErr: OSErr): OSErr;
		var
			dummyInt: Integer;
			line1, line2: Str255;
			alertHandle: AlertTHndl;
			alertRect: Rect;

	begin
		InitCursor;
		UseResFile(gliderResNum);
		if (theErr <> NoErr) then
		begin
			case theErr of
				DskFulErr: 
					GetIndString(line1, rFileStrIDs, 1);
				FNFErr: 
				begin
					fileWasLost := TRUE;
					GetIndString(line1, rFileStrIDs, 2);
				end;
				WPrErr: 
					GetIndString(line1, rFileStrIDs, 3);
				FLckdErr: 
					GetIndString(line1, rFileStrIDs, 4);
				VLckdErr: 
					GetIndString(line1, rFileStrIDs, 5);
				FBsyErr, OpWrErr: 
					GetIndString(line1, rFileStrIDs, 6);
				EOFErr: 
					GetIndString(line1, rFileStrIDs, 7);
				otherwise
					GetIndString(line1, rFileStrIDs, 10);
			end;

			NumToString(theErr, line2);
			line2 := CONCAT('Error code = ', line2);
			ParamText(line1, line2, '', '');
			alertHandle := AlertTHndl(Get1Resource('ALRT', rFileAlertID));
			if (alertHandle <> nil) then
			begin
				HNoPurge(Handle(alertHandle));
				alertRect := alertHandle^^.boundsRect;
				OffsetRect(alertRect, -alertRect.left, -alertRect.top);
				dummyInt := (screenBits.bounds.right - alertRect.right) div 2;
				OffsetRect(alertRect, dummyInt, 0);
				dummyInt := (screenBits.bounds.bottom - alertRect.bottom) div 3;
				OffsetRect(alertRect, 0, dummyInt);
				alertHandle^^.boundsRect := alertRect;
				HPurge(Handle(alertHandle));
			end;
			dummyInt := Alert(rFileAlertID, nil);
		end;

		IOCheck := theErr;
	end;

{=================================}

	function OpenHouse;
		var
			index: Integer;
			textLength: LongInt;
			ignored: Boolean;

	begin
		OpenHouse := FALSE;

		theErr := SetVol(nil, houseVolNum);
		if (IOCheck(theErr) <> 0) then
			Exit(OpenHouse);

		theErr := FSOpen(housesName, houseVolNum, houseNumber);
		if (IOCheck(theErr) <> 0) then
		begin
			if (not DoOpen(kHouseType)) then
			begin
				playing := FALSE;
				theErr := FSClose(houseNumber);
				Exit(OpenHouse);
			end;
			theErr := FSOpen(housesName, houseVolNum, houseNumber);
			if (IOCheck(theErr) <> 0) then
			begin
				playing := FALSE;
				theErr := FSClose(houseNumber);
				Exit(OpenHouse);
			end;
		end;

		theErr := SetFPos(houseNumber, FSFromStart, 0);
		if (IOCheck(theErr) <> 0) then
		begin
			playing := FALSE;
			theErr := FSClose(houseNumber);
			Exit(OpenHouse);
		end;

		textLength := SIZEOF(thisHouse);

		theErr := FSRead(houseNumber, textLength, @thisHouse);
		if (IOCheck(theErr) <> 0) then
		begin
			playing := FALSE;
			theErr := FSClose(houseNumber);
			Exit(OpenHouse);
		end;

		OpenHouse := TRUE;
	end;

{=================================}

	function OpenSFGetHook (theSFitem: integer; theDialog: DialogPtr): integer;
		const
			statText = 11;  {DITL item number of textAppButton}
			firstTime = -1;
		var
			itemToChange: Handle;	{needed for GetDItem and SetCtlValue}
			itemBox: Rect;		{needed for GetDItem}
			itemType: integer;		{needed for GetDItem}
			statTitle: Str255;		{needed for GetIndString}

	begin
		OpenSFGetHook := theSFitem;
		case theSFitem of
			firstTime: 
			begin
				GetIndString(statTitle, rMiscStrID, typeIs);
				if (statTitle <> '') then
				begin 				{ if we really got the resource}
					GetDItem(theDialog, statText, itemType, itemToChange, itemBox);
					SetIText(itemToChange, statTitle);
				end
				else
					GenericAlert(kErrLoadingRes);
			end; {firstTime}
			otherwise
		end;
	end;

{=================================}

	function SimpleFileFilter (p: ParmBlkPtr): BOOLEAN;
		const
			houseType = 1;
			artType = 2;
			gameType = 3;
	begin
		SimpleFileFilter := TRUE;	   {Don't show it -- default}
		with p^.ioFlFndrInfo do
			case typeIs of
				houseType: 
					if (fdType = 'GLhs') then
						SimpleFileFilter := FALSE; {Show it}
				artType: 
					if (fdType = 'GLbk') then
						SimpleFileFilter := FALSE; {Show it}
				gameType: 
					if (fdType = 'GLgm') then
						SimpleFileFilter := FALSE; {Show it}
				otherwise
					;
			end;
	end;

{=================================}

	function DoOpen;
		var
			dlgOrigin: Point;
			theTypeList: SFTypeList;
			theReply: SFReply;

	begin
		UseResFile(gliderResNum);
		SetPt(dlgOrigin, dlgLeft + rightOffset, dlgTop + downOffset);
		typeIs := whichType + 1;

		SFPGetFile(dlgOrigin, '', @SimpleFileFilter, -1, theTypeList, @OpenSFGetHook, theReply, rCustGetID, nil);
		with theReply do
		begin
			if (good) then
			begin
				case whichType of
					kHouseType: 
					begin
						housesName := fName;
						houseVolNum := vRefNum;
					end;
					kArtType: 
					begin
						resourceName := fName;
						resVolNum := vRefNum;
					end;
					kGameType: 
					begin
						gameName := fName;
						gameVolNum := vRefNum;
					end;
					otherwise
						Exit(DoOpen);
				end;
			end;
			DoOpen := good;
		end;
	end;

{=================================}

	procedure DoClose;
	begin
		theErr := FSClose(houseNumber);
		if (theErr <> NoErr) then
		begin
			GenericAlert(theErr);
			Exit(DoClose);
		end;

		theErr := FlushVol(nil, volNum);
		if (theErr <> NoErr) then
		begin
			GenericAlert(theErr);
			Exit(DoClose);
		end;
	end;

{=================================}

	function GetHouse;
		var
			i: Integer;

	begin
		GetHouse := FALSE;
		if (not OpenHouse) then
		begin
			GenericAlert(kErrExitSansHouse);
			Exit(GetHouse);
		end;

		DoClose(houseVolNum);

{$IFC DemoVersion}
		if (thisHouse.timeStamp <> 16) then
		begin
			GenericAlert(kErrNotDemoHouse);
			Exit(GetHouse);
		end;
{$ENDC}

		if (not ErrorCheckFile) then
		begin
			GenericAlert(kErrWrongHouseVers);
			Exit(GetHouse);
		end;

		GetHouse := TRUE;
	end;

{=================================}

	function ReadGame;
		type
			miniObject = record
					theAmount: Integer;
					theExtra: Integer;
					theIsOn: Boolean;
				end;

			roomState = record
					stateCode: Integer;
					objectStates: array[1..16] of miniObject;
				end;

			gameRec = record
					version: Integer;
					houseStamp: LongInt;
					roomIs, roomsHas: Integer;
					nMortals, nBands, nEnergy: Integer;
					isRightFace, hasEnteredLeft: Boolean;
					theRoomScore, theSuppScore: LongInt;
					workingGameNumberIs: LongInt;
					whichHouse: string[32];
					firstHouse: string[32];
					prefs: Integer;	{temporary}
					roomWasFlags: array[1..40] of Boolean;
					stateOfRooms: array[1..40] of roomState;
				end;

		var
			theGame: gameRec;
			i, i2: Integer;
			bytesIn: LongInt;

	begin
		ReadGame := FALSE;

		bytesIn := SIZEOF(gameRec);

		theErr := SetVol(nil, gameVolNum);
		if (IOCheck(theErr) <> 0) then
			Exit(ReadGame);

		theErr := FSOpen(gameName, gameVolNum, gameNumber);	{open the game}
		if (IOCheck(theErr) <> 0) then
		begin
			if ((theErr = FNFErr) and (DoOpen(kGameType))) then
			begin
				theErr := FSOpen(gameName, gameVolNum, gameNumber);
				if (IOCheck(theErr) <> 0) then
					Exit(ReadGame)
			end
			else
				Exit(ReadGame);
		end;

		theErr := SetFPos(gameNumber, FSFromStart, 0);
		if (IOCheck(theErr) <> 0) then
		begin
			playing := FALSE;
			theErr := FSClose(gameNumber);
			Exit(ReadGame);
		end;

		theErr := FSRead(gameNumber, bytesIn, @theGame);		{read in the game}
		if (IOCheck(theErr) <> 0) then
		begin
			playing := FALSE;
			theErr := FSClose(gameNumber);
			Exit(ReadGame);
		end;

		theErr := FSClose(gameNumber);	{close game file}
		if (IOCheck(theErr) <> 0) then
		begin
			playing := FALSE;
			theErr := FSClose(gameNumber);
			Exit(ReadGame);
		end;

		with theGame do				{extract beginning game info}
		begin
			if (version >= $0200) then
			begin
				GenericAlert(kErrGameOldVers);
				Exit(ReadGame);
			end;

			roomAt := roomIs;
			roomsPassed := roomsHas;
			mortals := nMortals;
			theGlider.bands := nBands;
			theGlider.energy := nEnergy;
			theGlider.forVel := 4;
			theGlider.isRight := isRightFace;
			enteredLeft := hasEnteredLeft;
			roomScore := theRoomScore;
			suppScore := theSuppScore;
			workingGameNumber := workingGameNumberIs;
			housesName := whichHouse;
			firstFileName := firstHouse;
		end;

		CalcRoomScore;
		rollScore := roomScore + suppScore;

		if (not GetHouse) then			{open said house}
		begin
			GenericAlert(kErrExitSansHouse);
			Exit(ReadGame);
		end;

		if (theGame.houseStamp <> thisHouse.timeStamp) then
			GenericAlert(kErrHouseModified);

		with theGame do				{set remaining game info}
		begin
			for i := 1 to 40 do
			begin
				roomVisits[i] := roomWasFlags[i];
				with stateOfRooms[i], thisHouse.theRooms[i] do
				begin
					conditionCode := stateCode;
					for i2 := 1 to 16 do
					begin
						theObjects[i2].amount := objectStates[i2].theAmount;
						theObjects[i2].extra := objectStates[i2].theExtra;
						theObjects[i2].isOn := objectStates[i2].theIsOn;
					end;		{for i2}
				end;		{with thisHouse}
			end;		{for i}
		end;			{with theGame}

		ReadGame := TRUE;

	end;

{=================================}

	function WriteGame (fileNum, volNum: Integer): Boolean;
		type
			miniObject = record
					theAmount: Integer;
					theExtra: Integer;
					theIsOn: Boolean;
				end;

			roomState = record
					stateCode: Integer;
					objectStates: array[1..16] of miniObject;
				end;

			gameRec = record
					version: Integer;
					houseStamp: LongInt;
					roomIs, roomsHas: Integer;
					nMortals, nBands, nEnergy: Integer;
					isRightFace, hasEnteredLeft: Boolean;
					theRoomScore, theSuppScore: LongInt;
					workingGameNumberIs: LongInt;
					whichHouse: string[32];
					firstHouse: string[32];
					prefs: Integer;	{temporary}
					roomWasFlags: array[1..40] of Boolean;
					stateOfRooms: array[1..40] of roomState;
				end;

		var
			theGame: gameRec;
			i, i2: Integer;
			bytesOut: LongInt;

	begin
		WriteGame := FALSE;

		for i := 1 to nObjects do			{set current conditions of present room}
			case (eventKind[i, 0]) of
				awardIt, extraIt, energizeIt, bandIt: 
				begin
					thisHouse.theRooms[roomAt].theObjects[i].amount := eventKind[i, 1];
				end;
				lightIt, airOnIt: 
				begin
					if ((not lightsOut) and (not airOut)) then
						thisHouse.theRooms[roomAt].conditionCode := 0;
				end;
				otherwise
			end;

		CalcRoomScore;

		with theGame do
		begin
			version := kGlideVersion;
			houseStamp := thisHouse.timeStamp;
			roomIs := roomAt;
			roomsHas := roomsPassed;
			nMortals := mortals;
			nBands := theGlider.bands;
			nEnergy := theGlider.energy;
			isRightFace := theGlider.isRight;
			hasEnteredLeft := enteredLeft;
			theRoomScore := roomScore;
			theSuppScore := suppScore;
			workingGameNumberIs := workingGameNumber;
			whichHouse := housesName;
			firstHouse := firstFileName;
			prefs := 0;
			for i := 1 to 40 do
			begin
				roomWasFlags[i] := roomVisits[i];
				with stateOfRooms[i], thisHouse.theRooms[i] do
				begin
					stateCode := conditionCode;
					for i2 := 1 to 16 do
					begin
						objectStates[i2].theAmount := theObjects[i2].amount;
						objectStates[i2].theExtra := theObjects[i2].extra;
						objectStates[i2].theIsOn := theObjects[i2].isOn;
					end;		{for i2}
				end;		{with thisHouse}
			end;		{for i}
		end;			{with theGame}
		bytesOut := SIZEOF(gameRec);

		theErr := SetFPos(fileNum, FSFromStart, 0);
		if (IOCheck(theErr) <> 0) then
		begin
			theErr := FSClose(gameNumber);	{close game file}
			Exit(WriteGame);
		end;

		theErr := FSWrite(fileNum, bytesOut, @theGame);
		if (IOCheck(theErr) <> 0) then
		begin
			theErr := FSClose(gameNumber);	{close game file}
			Exit(WriteGame);
		end;

		theErr := SetEOF(fileNum, bytesOut);
		if (IOCheck(theErr) <> 0) then
		begin
			theErr := FSClose(gameNumber);	{close game file}
			Exit(WriteGame);
		end;
		theErr := FSClose(fileNum);						{close game file}
		if (IOCheck(theErr) <> 0) then
		begin
			theErr := FSClose(gameNumber);	{close game file}
			Exit(WriteGame);
		end;
		theErr := FlushVol(nil, volNum);
		if (IOCheck(theErr) <> 0) then
			Exit(WriteGame);

		WriteGame := TRUE;
	end;

{=================================}

	function SaveGameAs;
		var
			dlgOrigin: Point;
			theReply: SFReply;
			theInfo: FInfo;

	begin
		SaveGameAs := FALSE;
		SetPt(dlgOrigin, dlgLeft + rightOffset + 25, dlgTop + downOffset);
		SFPutFile(dlgOrigin, 'Name for game:', '', nil, theReply);
		with theReply do
		begin
			if (not good) then
				Exit(SaveGameAs);
			gameVolNum := vRefNum;
			theErr := GetFInfo(fName, gameVolNum, theInfo);
			case theErr of
				NoErr: 
				begin
					if (theInfo.fdType <> 'GLgm') then
					begin
						GenericAlert(kErrFileExists);
						Exit(SaveGameAs);
					end;
				end;
				FNFErr: 
				begin
					theErr := Create(fname, gameVolNum, 'GLID', 'GLgm');
					if (IOCheck(theErr) <> 0) then
					begin
						Exit(SaveGameAs);
					end;
				end;
				otherwise
				begin
					GenericAlert(theErr);
					Exit(SaveGameAs);
				end;
			end;	{end - case}

{SetCursor- watch}
			gameName := fName;

			theErr := FSOpen(fName, gameVolNum, gameNumber);
			if (IOCheck(theErr) <> 0) then
				Exit(SaveGameAs);
			if (not WriteGame(gameNumber, gameVolNum)) then
			begin
				theErr := FSClose(gameNumber);	{close game file}
				Exit(SaveGameAs);
			end;
		end;		{end - with}

		refuseHigh := TRUE;
		SaveGameAs := TRUE;
	end;		{end - function}

{=================================}

	function SaveGame;
		var
			gameVolName: Str255;

	begin
		SaveGame := FALSE;

		if (gameName = '') then
		begin
			if (not SaveGameAs) then
				Exit(SaveGame)
		end
		else
		begin
			theErr := FSOpen(gameName, gameVolNum, gameNumber);
			if (IOCheck(theErr) <> 0) then
			begin
				Exit(SaveGame);
			end;

			if (not WriteGame(gameNumber, gameVolNum)) then
			begin
				Exit(SaveGame);
			end;
		end;
		refuseHigh := TRUE;

		SaveGame := TRUE;
	end;

{=================================}

	function WriteHouse;
		var
			fileLength: LongInt;

	begin
		SpinBall;
		WriteHouse := FALSE;
		fileLength := SIZEOF(houseRec);

		SpinBall;
		theErr := SetVol(nil, houseVolNum);
		if (IOCheck(theErr) <> 0) then
			Exit(WriteHouse);

		theErr := FSOpen(housesName, houseVolNum, houseNumber);
		if (IOCheck(theErr) <> 0) then
		begin
			if (not DoOpen(kHouseType)) then
			begin
				playing := FALSE;
				theErr := FSClose(houseNumber);
				Exit(WriteHouse);
			end;
			theErr := FSOpen(housesName, houseVolNum, houseNumber);
			if (IOCheck(theErr) <> 0) then
			begin
				playing := FALSE;
				theErr := FSClose(houseNumber);
				Exit(WriteHouse);
			end;
		end;

		SpinBall;
		theErr := SetFPos(houseNumber, FSFromStart, 0);
		if (IOCheck(theErr) <> 0) then
			Exit(WriteHouse);

		SpinBall;
		theErr := FSWrite(houseNumber, fileLength, @thisHouse);
		if (IOCheck(theErr) <> 0) then
			Exit(WriteHouse);

		SpinBall;
		theErr := SetEOF(houseNumber, fileLength);
		if (IOCheck(theErr) <> 0) then
			Exit(WriteHouse);

		SpinBall;
		theErr := FSClose(houseNumber);
		if (IOCheck(theErr) <> 0) then
		begin
			Exit(WriteHouse);
		end;

		SpinBall;
		theErr := FlushVol(nil, houseVolNum);
		if (IOCheck(theErr) <> 0) then
			Exit(WriteHouse);

		SpinBall;
		WriteHouse := TRUE;
	end;

{=================================}

end.