From 4e456cab15f188c15054214b6a064d8a99a9b8ce Mon Sep 17 00:00:00 2001 From: Jeremy Rand Date: Fri, 4 Jun 2021 00:41:11 -0400 Subject: [PATCH] Major rework because I am out of code space in the main segment. Move some stuff around in the C code to get them into other segments. Change the network code so that most of its globals are actually allocated dynamically when network functionality is detected. Change the network state machine so the connection is closed and re-opened on each transaction rather than trying to hold the connection open. It was failing during the game because the network is not being polled. Provide some visual feedback if there is a network problem in the game itself. Re-opening the connection isn't working for some reason but this is getting pretty close to working. --- BuGS.xcodeproj/project.pbxproj | 12 + BuGS/game.h | 3 + BuGS/globalScores.c | 474 ++++++++++++++++++++------------- BuGS/loadSounds.c | 92 +++++++ BuGS/loadSounds.h | 29 ++ BuGS/main.c | 254 ------------------ BuGS/settings.c | 201 ++++++++++++++ BuGS/settings.h | 20 ++ 8 files changed, 643 insertions(+), 442 deletions(-) create mode 100644 BuGS/loadSounds.c create mode 100644 BuGS/loadSounds.h create mode 100644 BuGS/settings.c create mode 100644 BuGS/settings.h diff --git a/BuGS.xcodeproj/project.pbxproj b/BuGS.xcodeproj/project.pbxproj index 750eb69..c2a7292 100644 --- a/BuGS.xcodeproj/project.pbxproj +++ b/BuGS.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 9D0DFAFA2669D85F00BDBD04 /* settings.c in Sources */ = {isa = PBXBuildFile; fileRef = 9D0DFAF92669D85F00BDBD04 /* settings.c */; }; + 9D0DFB002669DA5700BDBD04 /* loadSounds.c in Sources */ = {isa = PBXBuildFile; fileRef = 9D0DFAFF2669DA5700BDBD04 /* loadSounds.c */; }; 9D17168F2491C49300C83148 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 9D17168E2491C49300C83148 /* main.c */; }; 9D1716922491C49300C83148 /* main.rez in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9D1716912491C49300C83148 /* main.rez */; }; 9D1716942491C49300C83148 /* Makefile in Sources */ = {isa = PBXBuildFile; fileRef = 9D1716932491C49300C83148 /* Makefile */; }; @@ -58,6 +60,10 @@ 9D0CF94525B554BC0035D329 /* flea_loop_256b.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = flea_loop_256b.wav; sourceTree = ""; }; 9D0CF94625B554CE0035D329 /* scorpion.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = scorpion.wav; sourceTree = ""; }; 9D0DC8EF258C715E00DE9E87 /* extralife.raw */ = {isa = PBXFileReference; lastKnownFileType = text; path = extralife.raw; sourceTree = ""; }; + 9D0DFAF82669D85F00BDBD04 /* settings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = settings.h; sourceTree = ""; }; + 9D0DFAF92669D85F00BDBD04 /* settings.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = settings.c; sourceTree = ""; }; + 9D0DFAFE2669DA5700BDBD04 /* loadSounds.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = loadSounds.h; sourceTree = ""; }; + 9D0DFAFF2669DA5700BDBD04 /* loadSounds.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = loadSounds.c; sourceTree = ""; }; 9D1553DE257ACA1800657188 /* ACKNOWLEDGEMENTS.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = ACKNOWLEDGEMENTS.md; sourceTree = ""; }; 9D1553E9257F3E5200657188 /* fire.raw */ = {isa = PBXFileReference; lastKnownFileType = file; path = fire.raw; sourceTree = ""; }; 9D1553EA257F3E5200657188 /* bonus.raw */ = {isa = PBXFileReference; lastKnownFileType = file; path = bonus.raw; sourceTree = ""; }; @@ -203,6 +209,10 @@ children = ( 9D17168E2491C49300C83148 /* main.c */, 9D1716902491C49300C83148 /* main.h */, + 9D0DFAF82669D85F00BDBD04 /* settings.h */, + 9D0DFAF92669D85F00BDBD04 /* settings.c */, + 9D0DFAFE2669DA5700BDBD04 /* loadSounds.h */, + 9D0DFAFF2669DA5700BDBD04 /* loadSounds.c */, 9DB90E7E265AD04A003461C1 /* globalScores.h */, 9DB90E7F265AD04A003461C1 /* globalScores.c */, 9D8FFC602491CA28005C9327 /* game.s */, @@ -395,7 +405,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9D0DFAFA2669D85F00BDBD04 /* settings.c in Sources */, 9DB90E80265AD04A003461C1 /* globalScores.c in Sources */, + 9D0DFB002669DA5700BDBD04 /* loadSounds.c in Sources */, 9D1716942491C49300C83148 /* Makefile in Sources */, 9D17168F2491C49300C83148 /* main.c in Sources */, ); diff --git a/BuGS/game.h b/BuGS/game.h index 49f8c17..50606f6 100644 --- a/BuGS/game.h +++ b/BuGS/game.h @@ -9,9 +9,12 @@ #ifndef _GUARD_PROJECTBuGS_FILEgame_ #define _GUARD_PROJECTBuGS_FILEgame_ +// These are assembly functions called from C. extern void game(void); extern void randInit(void); +extern void waitForVbl(void); +extern void swapStereoChannels(void); #endif /* define _GUARD_PROJECTBuGS_FILEgame_ */ diff --git a/BuGS/globalScores.c b/BuGS/globalScores.c index e6df33c..a39bdc9 100644 --- a/BuGS/globalScores.c +++ b/BuGS/globalScores.c @@ -13,10 +13,12 @@ #include #include +#include + +#include "game.h" #include "globalScores.h" #include "tileData.h" -segment "highscores"; // Defines @@ -28,6 +30,8 @@ segment "highscores"; #define RESPONSE_TYPE_STATUS 2 #define GLOBAL_SCORE_REFRESH_TIME (15 * 60 * 60) +#define SET_SCORE_TIMEOUT (20 * 60) +#define SHUTDOWN_NETWORK_TIMEOUT (60 / 2) // Types @@ -94,6 +98,11 @@ typedef enum tGameNetworkState { GAME_NETWORK_LOOKUP_FAILED, GAME_NETWORK_SOCKET_ERROR, GAME_NETWORK_PROTOCOL_FAILED, + GAME_NETWORK_FAILURE, + + /* This is the state we are in during a game on a machine with a working network + (other possible states are fail states). */ + GAME_NETWORK_TCP_UNCONNECTED, /* All of these states below here have an ipid open and the TCP connection is open and in some state. */ @@ -104,63 +113,52 @@ typedef enum tGameNetworkState { GAME_NETWORK_WAITING_FOR_HELLO, GAME_NETWORK_REQUEST_SCORES, GAME_NETWORK_WAITING_FOR_SCORES, + GAME_NETWORK_SET_HIGH_SCORE, GAME_NETWORK_WAITING_FOR_SCORE_ACK, - /* This is the "quiescent" state. The hello has been retrieved and scores - have been downloaded at least once. This is the only state where a - new high score can be uploaded safely. */ - GAME_NETWORK_SCORES_RETRIEVED, + GAME_NETWORK_CLOSING_TCP, } tGameNetworkState; +typedef struct tGameNetworkGlobals { + Boolean networkStartedConnected; + tGameNetworkState gameNetworkState; + dnrBuffer domainNameResolution; + srBuff tcpStatus; + Word ipid; + rrBuff readResponseBuf; + tHelloResponse helloResponse; + uint32_t bytesRead; + md5WorkBlock hashWorkBlock; + uint32_t secrets[3]; + tHighScoreRequestWithHash highScoreRequest; + Boolean hasHighScoreToSend; + tStatusResponse setHighScoreResponse; +} tGameNetworkGlobals; + // Globals -Boolean networkToolsStarted = FALSE; -Boolean networkStartedConnected = FALSE; +// I am running out of space in the main segment so I am moving these globals into +// a dynamically allocated struct. It is only allocated if network capabilities are +// detected. +static tGameNetworkGlobals * networkGlobals = NULL; + +// The following globals are accessed by name from assembly so are not in the +// tGameNetworkGlobals structure. Boolean hasGlobalHighScores = FALSE; -tGameNetworkState gameNetworkState = GAME_NETWORK_UNCONNECTED; - -dnrBuffer domainNameResolution; -srBuff tcpStatus; - -Word ipid; -rrBuff readResponseBuf; -tHelloResponse helloResponse; -uint32_t bytesRead = 0; - -md5WorkBlock hashWorkBlock; -uint32_t secrets[3] = { NETWORK_SERVERSECRET1, NETWORK_SERVERSECRET2, 0 }; -tHighScoreRequestWithHash highScoreRequest; -tScoresResponse highScoreResponse = { - RESPONSE_TYPE_SCORES, - { - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0} - } -}; +tScoresResponse highScoreResponse; Word globalScoreAge = 0; - tSetHighScoreRequestWithHash setHighScoreRequest; -tStatusResponse setHighScoreResponse; - -// Extern from assembly code - -extern void waitForVbl(void); // Implementation + +segment "highscores"; + void initNetwork(void) { - networkToolsStarted = FALSE; + networkGlobals = NULL; if ((NETWORK_SERVER == NULL) || (NETWORK_SERVERPORT == 0)) @@ -191,54 +189,69 @@ void initNetwork(void) return; } - networkToolsStarted = TRUE; - - networkStartedConnected = TCPIPGetConnectStatus(); - if (networkStartedConnected) { - gameNetworkState = GAME_NETWORK_CONNECTED; - } else { - gameNetworkState = GAME_NETWORK_UNCONNECTED; + networkGlobals = malloc(sizeof(tGameNetworkGlobals)); + if (networkGlobals == NULL) { + hashShutDown(); + TCPIPShutDown(); + UnloadOneTool(128); + UnloadOneTool(54); + return; + } + networkGlobals->networkStartedConnected = TCPIPGetConnectStatus(); + if (networkGlobals->networkStartedConnected) { + networkGlobals->gameNetworkState = GAME_NETWORK_CONNECTED; + } else { + networkGlobals->gameNetworkState = GAME_NETWORK_UNCONNECTED; + } + + networkGlobals->secrets[0] = NETWORK_SERVERSECRET1; + networkGlobals->secrets[1] = NETWORK_SERVERSECRET2; + + networkGlobals->hasHighScoreToSend = FALSE; + setHighScoreRequest.setHighScoreRequest.is60Hz = !ReadBParam(hrtz50or60); } void shutdownNetwork(void) { - if (!networkToolsStarted) + if (networkGlobals == NULL) return; - if (gameNetworkState > GAME_NETWORK_WAITING_FOR_TCP) { - int i = 30; - TCPIPCloseTCP(ipid); - while (i > 0) { + if (networkGlobals->gameNetworkState > GAME_NETWORK_TCP_UNCONNECTED) { + int timeout = SHUTDOWN_NETWORK_TIMEOUT; + TCPIPCloseTCP(networkGlobals->ipid); + while (timeout > 0) { waitForVbl(); TCPIPPoll(); - if (TCPIPStatusTCP(ipid, &tcpStatus) != tcperrOK) { - i = 0; + if (TCPIPStatusTCP(networkGlobals->ipid, &(networkGlobals->tcpStatus)) != tcperrOK) { + timeout = 0; break; } - if (tcpStatus.srState == TCPSCLOSED) + if (networkGlobals->tcpStatus.srState == TCPSCLOSED) break; - i--; + timeout--; } - if (i == 0) { - TCPIPAbortTCP(ipid); + if (timeout == 0) { + TCPIPAbortTCP(networkGlobals->ipid); } - TCPIPLogout(ipid); + TCPIPLogout(networkGlobals->ipid); } - if ((!networkStartedConnected) && - (gameNetworkState > GAME_NETWORK_UNCONNECTED)) { + if ((!networkGlobals->networkStartedConnected) && + (networkGlobals->gameNetworkState > GAME_NETWORK_UNCONNECTED)) { TCPIPDisconnect(TRUE, NULL); } + free(networkGlobals); + hashShutDown(); TCPIPShutDown(); UnloadOneTool(128); // Unload the Hash toolset @@ -246,191 +259,301 @@ void shutdownNetwork(void) } +static void displayString(Word row, char * string) +{ + strcpy(&(highScoreResponse.highScores[row].scoreText[2]), string); +} + +static void displayNetworkError(char * line1, char * line2) +{ + Word row; + Word column; + + networkGlobals->gameNetworkState = GAME_NETWORK_FAILURE; + + for (row = 0; row < sizeof(highScoreResponse.highScores) / sizeof(highScoreResponse.highScores[0]); row++) { + for (column = 0; + column < sizeof(highScoreResponse.highScores[0].scoreText) / sizeof(highScoreResponse.highScores[0].scoreText[0]); + column++) { + highScoreResponse.highScores[row].scoreText[column] = ' '; + } + for (column = 0; + column < sizeof(highScoreResponse.highScores[0].who) / sizeof(highScoreResponse.highScores[0].who[0]); + column++) { + highScoreResponse.highScores[row].who[column] = ' '; + } + } + + displayString(1, "NETWORK"); + displayString(2, "PROBLEM"); + + displayString(4, line1); + displayString(5, line2); + + globalScoreAge = 0; + hasGlobalHighScores = TRUE; +} + + void pollNetwork(void) { - if (!networkToolsStarted) + if (networkGlobals == NULL) return; TCPIPPoll(); - switch (gameNetworkState) { + switch (networkGlobals->gameNetworkState) { case GAME_NETWORK_SOCKET_ERROR: + displayNetworkError("SOCKET", "ERROR"); + break; + case GAME_NETWORK_LOOKUP_FAILED: + displayNetworkError("LOOKUP", "FAILED"); + break; + case GAME_NETWORK_CONNECT_FAILED: + displayNetworkError("CONNECT", "FAILED"); + break; + case GAME_NETWORK_PROTOCOL_FAILED: - // If we end up in these error states, there is nothing more to do... + TCPIPCloseTCP(networkGlobals->ipid); + displayNetworkError("PROTOCOL", "FAILED"); + globalScoreAge = GLOBAL_SCORE_REFRESH_TIME; + networkGlobals->gameNetworkState = GAME_NETWORK_CLOSING_TCP; + break; + + case GAME_NETWORK_FAILURE: + // All of the different failure modes except protocol failure above end up here ultimately. And the state + // machine stays here once it arrives here. break; case GAME_NETWORK_UNCONNECTED: TCPIPConnect(NULL); // TODO - Perhaps some feedback here would be a better user experience so maybe I should provide some kind of display function. if ((!toolerror()) && (TCPIPGetConnectStatus())) { - gameNetworkState = GAME_NETWORK_CONNECTED; + networkGlobals->gameNetworkState = GAME_NETWORK_CONNECTED; } else { - gameNetworkState = GAME_NETWORK_CONNECT_FAILED; + networkGlobals->gameNetworkState = GAME_NETWORK_CONNECT_FAILED; } break; case GAME_NETWORK_CONNECTED: - TCPIPDNRNameToIP("\p" NETWORK_SERVER, &domainNameResolution); + TCPIPDNRNameToIP("\p" NETWORK_SERVER, &(networkGlobals->domainNameResolution)); if (toolerror()) - gameNetworkState = GAME_NETWORK_LOOKUP_FAILED; + networkGlobals->gameNetworkState = GAME_NETWORK_LOOKUP_FAILED; else - gameNetworkState = GAME_NETWORK_RESOLVING_NAME; + networkGlobals->gameNetworkState = GAME_NETWORK_RESOLVING_NAME; break; case GAME_NETWORK_RESOLVING_NAME: - if (domainNameResolution.DNRstatus == DNR_Pending) + if (networkGlobals->domainNameResolution.DNRstatus == DNR_Pending) break; - if (domainNameResolution.DNRstatus != DNR_OK) { - gameNetworkState = GAME_NETWORK_LOOKUP_FAILED; + if (networkGlobals->domainNameResolution.DNRstatus != DNR_OK) { + networkGlobals->gameNetworkState = GAME_NETWORK_LOOKUP_FAILED; break; } - ipid = TCPIPLogin(myUserId, domainNameResolution.DNRIPaddress, NETWORK_SERVERPORT, 0, 64); + networkGlobals->gameNetworkState = GAME_NETWORK_TCP_UNCONNECTED; + break; + + case GAME_NETWORK_TCP_UNCONNECTED: + if ((hasGlobalHighScores) && + (globalScoreAge > 0) && + (!networkGlobals->hasHighScoreToSend)) + break; + + networkGlobals->ipid = TCPIPLogin(myUserId, networkGlobals->domainNameResolution.DNRIPaddress, NETWORK_SERVERPORT, 0, 64); if (toolerror()) { - gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR; break; } - if (TCPIPOpenTCP(ipid) != tcperrOK) { - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + if (TCPIPOpenTCP(networkGlobals->ipid) != tcperrOK) { + TCPIPLogout(networkGlobals->ipid); + networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR; break; } - gameNetworkState = GAME_NETWORK_WAITING_FOR_TCP; + networkGlobals->gameNetworkState = GAME_NETWORK_WAITING_FOR_TCP; break; case GAME_NETWORK_WAITING_FOR_TCP: - if (TCPIPStatusTCP(ipid, &tcpStatus) != tcperrOK) { - TCPIPAbortTCP(ipid); - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + if (TCPIPStatusTCP(networkGlobals->ipid, &(networkGlobals->tcpStatus)) != tcperrOK) { + TCPIPAbortTCP(networkGlobals->ipid); + TCPIPLogout(networkGlobals->ipid); + networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + break; } - if ((tcpStatus.srState == TCPSSYNSENT) || - (tcpStatus.srState == TCPSSYNRCVD)) + if ((networkGlobals->tcpStatus.srState == TCPSSYNSENT) || + (networkGlobals->tcpStatus.srState == TCPSSYNRCVD)) break; - if (tcpStatus.srState != TCPSESTABLISHED) { - TCPIPAbortTCP(ipid); - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + if (networkGlobals->tcpStatus.srState != TCPSESTABLISHED) { + TCPIPAbortTCP(networkGlobals->ipid); + TCPIPLogout(networkGlobals->ipid); + networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + break; } - gameNetworkState = GAME_NETWORK_WAITING_FOR_HELLO; - bytesRead = 0; + + networkGlobals->gameNetworkState = GAME_NETWORK_WAITING_FOR_HELLO; + networkGlobals->bytesRead = 0; break; case GAME_NETWORK_WAITING_FOR_HELLO: - if (TCPIPReadTCP(ipid, 0, ((uint32_t)(&helloResponse)) + bytesRead, sizeof(helloResponse) - bytesRead, &readResponseBuf) != tcperrOK) { - TCPIPAbortTCP(ipid); - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + if (TCPIPReadTCP(networkGlobals->ipid, 0, + ((uint32_t)(&(networkGlobals->helloResponse))) + networkGlobals->bytesRead, + sizeof(networkGlobals->helloResponse) - networkGlobals->bytesRead, + &(networkGlobals->readResponseBuf)) != tcperrOK) { + TCPIPAbortTCP(networkGlobals->ipid); + TCPIPLogout(networkGlobals->ipid); + networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + break; } - bytesRead += readResponseBuf.rrBuffCount; - if (bytesRead < sizeof(helloResponse)) + networkGlobals->bytesRead += networkGlobals->readResponseBuf.rrBuffCount; + if (networkGlobals->bytesRead < sizeof(networkGlobals->helloResponse)) break; - if (bytesRead > sizeof(helloResponse)) { - TCPIPAbortTCP(ipid); - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; + if (networkGlobals->bytesRead > sizeof(networkGlobals->helloResponse)) { + networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; + break; } - if (helloResponse.responseType != RESPONSE_TYPE_HELLO) { - TCPIPAbortTCP(ipid); - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; + if (networkGlobals->helloResponse.responseType != RESPONSE_TYPE_HELLO) { + networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; + break; } - gameNetworkState = GAME_NETWORK_REQUEST_SCORES; - secrets[2] = helloResponse.nonce; - + networkGlobals->secrets[2] = networkGlobals->helloResponse.nonce; + if (networkGlobals->hasHighScoreToSend) { + networkGlobals->gameNetworkState = GAME_NETWORK_SET_HIGH_SCORE; + } else if ((!hasGlobalHighScores) || + (globalScoreAge == 0)) { + networkGlobals->gameNetworkState = GAME_NETWORK_REQUEST_SCORES; + } else { + networkGlobals->gameNetworkState = GAME_NETWORK_CLOSING_TCP; + } break; case GAME_NETWORK_REQUEST_SCORES: - highScoreRequest.highScoreRequest.requestType = REQUEST_TYPE_GET_HIGH_SCORES; + networkGlobals->highScoreRequest.highScoreRequest.requestType = REQUEST_TYPE_GET_HIGH_SCORES; - md5Init(&hashWorkBlock); - md5Append(&hashWorkBlock, (Pointer)&secrets, sizeof(secrets)); - md5Append(&hashWorkBlock, (Pointer)&(highScoreRequest.highScoreRequest), sizeof(highScoreRequest.highScoreRequest)); - md5Finish(&hashWorkBlock, &(highScoreRequest.md5Digest[0])); + md5Init(&(networkGlobals->hashWorkBlock)); + md5Append(&(networkGlobals->hashWorkBlock), (Pointer)&(networkGlobals->secrets), sizeof(networkGlobals->secrets)); + md5Append(&(networkGlobals->hashWorkBlock), (Pointer)&(networkGlobals->highScoreRequest.highScoreRequest), sizeof(networkGlobals->highScoreRequest.highScoreRequest)); + md5Finish(&(networkGlobals->hashWorkBlock), &(networkGlobals->highScoreRequest.md5Digest[0])); - if (TCPIPWriteTCP(ipid, (Pointer)&highScoreRequest, sizeof(highScoreRequest), FALSE, FALSE) != tcperrOK) { - TCPIPAbortTCP(ipid); - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + if (TCPIPWriteTCP(networkGlobals->ipid, (Pointer)&(networkGlobals->highScoreRequest), sizeof(networkGlobals->highScoreRequest), FALSE, FALSE) != tcperrOK) { + TCPIPAbortTCP(networkGlobals->ipid); + TCPIPLogout(networkGlobals->ipid); + networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + break; } - gameNetworkState = GAME_NETWORK_WAITING_FOR_SCORES; - bytesRead = 0; + networkGlobals->gameNetworkState = GAME_NETWORK_WAITING_FOR_SCORES; + networkGlobals->bytesRead = 0; break; case GAME_NETWORK_WAITING_FOR_SCORES: - if (TCPIPReadTCP(ipid, 0, ((uint32_t)(&highScoreResponse)) + bytesRead, sizeof(highScoreResponse) - bytesRead, &readResponseBuf) != tcperrOK) { - TCPIPAbortTCP(ipid); - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + if (TCPIPReadTCP(networkGlobals->ipid, 0, + ((uint32_t)(&highScoreResponse)) + networkGlobals->bytesRead, + sizeof(highScoreResponse) - networkGlobals->bytesRead, + &(networkGlobals->readResponseBuf)) != tcperrOK) { + TCPIPAbortTCP(networkGlobals->ipid); + TCPIPLogout(networkGlobals->ipid); + networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + break; } - bytesRead += readResponseBuf.rrBuffCount; - if (bytesRead < sizeof(highScoreResponse)) + networkGlobals->bytesRead += networkGlobals->readResponseBuf.rrBuffCount; + if (networkGlobals->bytesRead < sizeof(highScoreResponse)) break; - if (bytesRead > sizeof(highScoreResponse)) { - TCPIPAbortTCP(ipid); - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; + if (networkGlobals->bytesRead > sizeof(highScoreResponse)) { + networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; + break; } if (highScoreResponse.responseType != RESPONSE_TYPE_SCORES) { - TCPIPAbortTCP(ipid); - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; + networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; + break; } globalScoreAge = GLOBAL_SCORE_REFRESH_TIME; - gameNetworkState = GAME_NETWORK_SCORES_RETRIEVED; hasGlobalHighScores = TRUE; + + TCPIPCloseTCP(networkGlobals->ipid); + networkGlobals->gameNetworkState = GAME_NETWORK_CLOSING_TCP; + break; + + case GAME_NETWORK_SET_HIGH_SCORE: + setHighScoreRequest.setHighScoreRequest.requestType = REQUEST_TYPE_SET_SCORE; + setHighScoreRequest.setHighScoreRequest.who[3] = '\0'; + + md5Init(&(networkGlobals->hashWorkBlock)); + md5Append(&(networkGlobals->hashWorkBlock), (Pointer)&(networkGlobals->secrets), sizeof(networkGlobals->secrets)); + md5Append(&(networkGlobals->hashWorkBlock), (Pointer)&(setHighScoreRequest.setHighScoreRequest), sizeof(setHighScoreRequest.setHighScoreRequest)); + md5Finish(&(networkGlobals->hashWorkBlock), &(setHighScoreRequest.md5Digest[0])); + + if (TCPIPWriteTCP(networkGlobals->ipid, (Pointer)&setHighScoreRequest, sizeof(setHighScoreRequest), FALSE, FALSE) != tcperrOK) { + TCPIPAbortTCP(networkGlobals->ipid); + TCPIPLogout(networkGlobals->ipid); + networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + break; + } + + networkGlobals->gameNetworkState = GAME_NETWORK_WAITING_FOR_SCORE_ACK; + networkGlobals->bytesRead = 0; break; case GAME_NETWORK_WAITING_FOR_SCORE_ACK: - if (TCPIPReadTCP(ipid, 0, ((uint32_t)(&setHighScoreResponse)) + bytesRead, sizeof(setHighScoreResponse) - bytesRead, &readResponseBuf) != tcperrOK) { - TCPIPAbortTCP(ipid); - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + if (TCPIPReadTCP(networkGlobals->ipid, 0, + ((uint32_t)(&(networkGlobals->setHighScoreResponse))) + networkGlobals->bytesRead, + sizeof(networkGlobals->setHighScoreResponse) - networkGlobals->bytesRead, + &(networkGlobals->readResponseBuf)) != tcperrOK) { + TCPIPAbortTCP(networkGlobals->ipid); + TCPIPLogout(networkGlobals->ipid); + networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + break; } - bytesRead += readResponseBuf.rrBuffCount; - if (bytesRead < sizeof(setHighScoreResponse)) + networkGlobals->bytesRead += networkGlobals->readResponseBuf.rrBuffCount; + if (networkGlobals->bytesRead < sizeof(networkGlobals->setHighScoreResponse)) break; - if (bytesRead > sizeof(setHighScoreResponse)) { - TCPIPAbortTCP(ipid); - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; + if (networkGlobals->bytesRead > sizeof(networkGlobals->setHighScoreResponse)) { + networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; + break; } - if (setHighScoreResponse.responseType != RESPONSE_TYPE_STATUS) { - TCPIPAbortTCP(ipid); - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; + if (networkGlobals->setHighScoreResponse.responseType != RESPONSE_TYPE_STATUS) { + networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; + break; } - if (!setHighScoreResponse.success) { - TCPIPAbortTCP(ipid); - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; + if (!networkGlobals->setHighScoreResponse.success) { + networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; + break; } + networkGlobals->hasHighScoreToSend = FALSE; globalScoreAge = 0; - gameNetworkState = GAME_NETWORK_REQUEST_SCORES; + networkGlobals->gameNetworkState = GAME_NETWORK_REQUEST_SCORES; break; - case GAME_NETWORK_SCORES_RETRIEVED: - if (globalScoreAge == 0) - gameNetworkState = GAME_NETWORK_REQUEST_SCORES; + case GAME_NETWORK_CLOSING_TCP: + if (TCPIPStatusTCP(networkGlobals->ipid, &(networkGlobals->tcpStatus)) != tcperrOK) { + TCPIPAbortTCP(networkGlobals->ipid); + TCPIPLogout(networkGlobals->ipid); + networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR; + break; + } + if ((networkGlobals->tcpStatus.srState != TCPSCLOSED) && + (networkGlobals->tcpStatus.srState != TCPSTIMEWAIT)) + break; + + TCPIPLogout(networkGlobals->ipid); + networkGlobals->gameNetworkState = GAME_NETWORK_TCP_UNCONNECTED; break; } } @@ -438,46 +561,21 @@ void pollNetwork(void) void sendHighScore(void) { - int timeout = 10*60; + int timeout = SET_SCORE_TIMEOUT; - if (!networkToolsStarted) + if (networkGlobals == NULL) return; - if (gameNetworkState < GAME_NETWORK_WAITING_FOR_TCP) + if (networkGlobals->gameNetworkState < GAME_NETWORK_TCP_UNCONNECTED) return; - while (gameNetworkState != GAME_NETWORK_SCORES_RETRIEVED) { + networkGlobals->hasHighScoreToSend = TRUE; + + do { waitForVbl(); pollNetwork(); timeout--; if (timeout == 0) return; - } - - setHighScoreRequest.setHighScoreRequest.requestType = REQUEST_TYPE_SET_SCORE; - setHighScoreRequest.setHighScoreRequest.who[3] = '\0'; - - md5Init(&hashWorkBlock); - md5Append(&hashWorkBlock, (Pointer)&secrets, sizeof(secrets)); - md5Append(&hashWorkBlock, (Pointer)&(setHighScoreRequest.setHighScoreRequest), sizeof(setHighScoreRequest.setHighScoreRequest)); - md5Finish(&hashWorkBlock, &(setHighScoreRequest.md5Digest[0])); - - if (TCPIPWriteTCP(ipid, (Pointer)&setHighScoreRequest, sizeof(setHighScoreRequest), FALSE, FALSE) != tcperrOK) { - TCPIPAbortTCP(ipid); - TCPIPLogout(ipid); - gameNetworkState = GAME_NETWORK_SOCKET_ERROR; - return; - } - - gameNetworkState = GAME_NETWORK_WAITING_FOR_SCORE_ACK; - bytesRead = 0; - - timeout = 10*60; - while (gameNetworkState != GAME_NETWORK_REQUEST_SCORES) { - waitForVbl(); - pollNetwork(); - timeout--; - if (timeout == 0) - return; - } + } while (networkGlobals->gameNetworkState > GAME_NETWORK_TCP_UNCONNECTED); } diff --git a/BuGS/loadSounds.c b/BuGS/loadSounds.c new file mode 100644 index 0000000..94bff3d --- /dev/null +++ b/BuGS/loadSounds.c @@ -0,0 +1,92 @@ +/* + * loadSounds.c + * BuGS + * + * Created by Jeremy Rand on 2021-06-03. + * Copyright © 2021 Jeremy Rand. All rights reserved. + */ + +#include +#include +#include + +#include "loadSounds.h" +#include "main.h" + + +segment "loadSounds"; + +static void loadSound(Word addr, Word soundNum) +{ + Handle handle = LoadResource(rRawSound, soundNum); + HLock(handle); + WriteRamBlock(*handle, addr, GetHandleSize(handle)); + HUnlock(handle); +} + +void loadSpiderSound(Word addr) +{ + loadSound(addr, SPIDER_SOUND); +} + + +void loadDeathSound(word addr) +{ + loadSound(addr, DEATH_SOUND); +} + + +void loadSegmentsSound(word addr) +{ + loadSound(addr, SEGMENTS_SOUND); +} + + +void loadBonusSound(word addr) +{ + loadSound(addr, BONUS_SOUND); +} + + +void loadKillSound(word addr) +{ + loadSound(addr, KILL_SOUND); +} + + +void loadFireSound(word addr) +{ + loadSound(addr, FIRE_SOUND); +} + + +void loadExtraLifeSound(word addr) +{ + loadSound(addr, EXTRA_LIFE_SOUND); +} + + +void loadFleaSound(word addr) +{ + loadSound(addr, FLEA_SOUND); +} + + +void loadScorpionSound(word addr) +{ + loadSound(addr, SCORPION_SOUND); +} + + +void preloadSound(void) +{ + LoadResource(rRawSound, SPIDER_SOUND); + LoadResource(rRawSound, DEATH_SOUND); + LoadResource(rRawSound, SEGMENTS_SOUND); + LoadResource(rRawSound, BONUS_SOUND); + LoadResource(rRawSound, KILL_SOUND); + LoadResource(rRawSound, FIRE_SOUND); + LoadResource(rRawSound, EXTRA_LIFE_SOUND); + LoadResource(rRawSound, FLEA_SOUND); + LoadResource(rRawSound, SCORPION_SOUND); +} diff --git a/BuGS/loadSounds.h b/BuGS/loadSounds.h new file mode 100644 index 0000000..ca04dc0 --- /dev/null +++ b/BuGS/loadSounds.h @@ -0,0 +1,29 @@ +/* + * loadSounds.h + * BuGS + * + * Created by Jeremy Rand on 2021-06-03. + * Copyright © 2021 Jeremy Rand. All rights reserved. + */ + +#ifndef _GUARD_PROJECTBuGS_FILEloadSounds_ +#define _GUARD_PROJECTBuGS_FILEloadSounds_ + + +#include + + +extern void loadSpiderSound(Word addr); +extern void loadDeathSound(word addr); +extern void loadSegmentsSound(word addr); +extern void loadBonusSound(word addr); +extern void loadKillSound(word addr); +extern void loadFireSound(word addr); +extern void loadExtraLifeSound(word addr); +extern void loadFleaSound(word addr); +extern void loadScorpionSound(word addr); + +extern void preloadSound(void); + + +#endif /* define _GUARD_PROJECTBuGS_FILEloadSounds_ */ diff --git a/BuGS/main.c b/BuGS/main.c index aa60e44..33a022b 100644 --- a/BuGS/main.c +++ b/BuGS/main.c @@ -9,14 +9,10 @@ #include -#include -#include #include #include #include -#include -#include #include "main.h" #include "game.h" @@ -29,56 +25,16 @@ #define TOOLFAIL(string) \ if (toolerror()) SysFailMgr(toolerror(), "\p" string "\n\r Error Code -> $"); -#define SETTINGS_FILENAME "@:BuGS.settings" - - -/* Typedefs */ - -typedef struct tSettingsData -{ - char magic[4]; - int version; - Boolean swapStereo; - tHighScore highScores[NUM_HIGH_SCORES]; -} tSettingsData; - /* Globals */ unsigned int myUserId; unsigned int randomSeed; -tSettingsData settings = { - { 'B', 'u', 'G', 'S' }, - 0, - FALSE, - { - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, - { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0} - } -}; -RefNumRecGS closeRec; -OpenRecGS openRec; -IORecGS readRec; -CreateRecGS createRec; -IORecGS writeRec; -NameRecGS destroyRec; -Handle filenameHandle = NULL; /* Implementation */ -extern void swapStereoChannels(void); - - word randomMushroomOffset(void) { /* We do not put mushrooms in the bottom tile so we subtract the width here to find @@ -86,216 +42,6 @@ word randomMushroomOffset(void) return (rand() % (NUM_GAME_TILES - GAME_NUM_TILES_WIDE)) * SIZEOF_TILE_INFO; } -void loadSound(Word addr, Word soundNum) -{ - Handle handle = LoadResource(rRawSound, soundNum); - HLock(handle); - WriteRamBlock(*handle, addr, GetHandleSize(handle)); - HUnlock(handle); -} - -void loadSpiderSound(Word addr) -{ - loadSound(addr, SPIDER_SOUND); -} - - -void loadDeathSound(word addr) -{ - loadSound(addr, DEATH_SOUND); -} - - -void loadSegmentsSound(word addr) -{ - loadSound(addr, SEGMENTS_SOUND); -} - - -void loadBonusSound(word addr) -{ - loadSound(addr, BONUS_SOUND); -} - - -void loadKillSound(word addr) -{ - loadSound(addr, KILL_SOUND); -} - - -void loadFireSound(word addr) -{ - loadSound(addr, FIRE_SOUND); -} - - -void loadExtraLifeSound(word addr) -{ - loadSound(addr, EXTRA_LIFE_SOUND); -} - - -void loadFleaSound(word addr) -{ - loadSound(addr, FLEA_SOUND); -} - - -void loadScorpionSound(word addr) -{ - loadSound(addr, SCORPION_SOUND); -} - - -void preloadSound(void) -{ - LoadResource(rRawSound, SPIDER_SOUND); - LoadResource(rRawSound, DEATH_SOUND); - LoadResource(rRawSound, SEGMENTS_SOUND); - LoadResource(rRawSound, BONUS_SOUND); - LoadResource(rRawSound, KILL_SOUND); - LoadResource(rRawSound, FIRE_SOUND); - LoadResource(rRawSound, EXTRA_LIFE_SOUND); - LoadResource(rRawSound, FLEA_SOUND); - LoadResource(rRawSound, SCORPION_SOUND); -} - - -void allocateFilenameHandle(void) -{ - if (filenameHandle == NULL) - { - GSString255Ptr filenamePtr; - - filenameHandle = NewHandle(sizeof(GSString255), myUserId, 0x8000, NULL); - HLock(filenameHandle); - filenamePtr = (GSString255Ptr)(*filenameHandle); - filenamePtr->length = strlen(SETTINGS_FILENAME); - strcpy(filenamePtr->text, SETTINGS_FILENAME); - HUnlock(filenameHandle); - } -} - - -void deleteSettings(void) -{ - allocateFilenameHandle(); - HLock(filenameHandle); - - destroyRec.pCount = 1; - destroyRec.pathname = (GSString255Ptr)(*filenameHandle); - DestroyGS(&destroyRec); - - HUnlock(filenameHandle); -} - - -void saveSettings(void) -{ - BOOLEAN success = false; - - deleteSettings(); - - allocateFilenameHandle(); - HLock(filenameHandle); - - createRec.pCount = 5; - createRec.pathname = (GSString255Ptr)(*filenameHandle); - createRec.access = destroyEnable | renameEnable | readWriteEnable; - createRec.fileType = 0x06; - createRec.auxType = 0x2000; - createRec.storageType = seedling; - CreateGS(&createRec); - if (toolerror() != 0) - { - HUnlock(filenameHandle); - return; - } - - openRec.pCount = 3; - openRec.pathname = (GSString255Ptr)(*filenameHandle); - openRec.requestAccess = writeEnable; - OpenGS(&openRec); - if (toolerror() == 0) - { - writeRec.pCount = 4; - writeRec.refNum = openRec.refNum; - writeRec.dataBuffer = (Pointer) &settings; - writeRec.requestCount = sizeof(settings); - WriteGS(&writeRec); - success = (toolerror() == 0); - - closeRec.pCount = 1; - closeRec.refNum = openRec.refNum; - CloseGS(&closeRec); - } - - HUnlock(filenameHandle); - - if (!success) - deleteSettings(); -} - - -BOOLEAN loadSettings(void) -{ - BOOLEAN success = FALSE; - - allocateFilenameHandle(); - HLock(filenameHandle); - - openRec.pCount = 12; - openRec.requestAccess = readEnable; - openRec.resourceNumber = 0; - openRec.pathname = (GSString255Ptr)(*filenameHandle); - OpenGS(&openRec); - if (toolerror() == 0) - { - if (openRec.eof == sizeof(settings)) - { - readRec.pCount = 4; - readRec.refNum = openRec.refNum; - readRec.dataBuffer = (Pointer)&settings; - readRec.requestCount = sizeof(tSettingsData); - ReadGS(&readRec); - success = (toolerror() == 0); - } - - closeRec.pCount = 1; - closeRec.refNum = openRec.refNum; - CloseGS(&closeRec); - } - HUnlock(filenameHandle); - - if (success) - { - if ((settings.magic[0] != 'B') || - (settings.magic[1] != 'u') || - (settings.magic[2] != 'G') || - (settings.magic[3] != 'S') || - (settings.version != 0)) - success = FALSE; - } - - if (success) - { - if (settings.swapStereo) - { - swapStereoChannels(); - } - } - - return success; -} - - -void swapStereoSettings(void) -{ - swapStereoChannels(); - settings.swapStereo = !settings.swapStereo; - saveSettings(); -} int main(void) diff --git a/BuGS/settings.c b/BuGS/settings.c new file mode 100644 index 0000000..8dac7c5 --- /dev/null +++ b/BuGS/settings.c @@ -0,0 +1,201 @@ +/* + * settings.c + * BuGS + * + * Created by Jeremy Rand on 2021-06-03. + * Copyright © 2021 Jeremy Rand. All rights reserved. + */ + + +#include +#include + +#include "globalScores.h" +#include "settings.h" +#include "tileData.h" + + +// Defines + +#define SETTINGS_FILENAME "@:BuGS.settings" + + +/* Typedefs */ + +typedef struct tSettingsData +{ + char magic[4]; + int version; + Boolean swapStereo; + tHighScore highScores[NUM_HIGH_SCORES]; +} tSettingsData; + + +// Globals + +tSettingsData settings = { + { 'B', 'u', 'G', 'S' }, + 0, + FALSE, + { + { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, + { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, + { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, + { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, + { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, + { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, + { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, + { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, + { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0}, + { { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0'}, { 'A', 'A', 'A' }, 0} + } +}; + +static RefNumRecGS closeRec; +static OpenRecGS openRec; +static IORecGS readRec; +static CreateRecGS createRec; +static IORecGS writeRec; +static NameRecGS destroyRec; +static Handle filenameHandle = NULL; + + +// Implementation + +segment "settings"; + + +static void allocateFilenameHandle(void) +{ + if (filenameHandle == NULL) + { + GSString255Ptr filenamePtr; + + filenameHandle = NewHandle(sizeof(GSString255), myUserId, 0x8000, NULL); + HLock(filenameHandle); + filenamePtr = (GSString255Ptr)(*filenameHandle); + filenamePtr->length = strlen(SETTINGS_FILENAME); + strcpy(filenamePtr->text, SETTINGS_FILENAME); + HUnlock(filenameHandle); + } +} + + +static void deleteSettings(void) +{ + allocateFilenameHandle(); + HLock(filenameHandle); + + destroyRec.pCount = 1; + destroyRec.pathname = (GSString255Ptr)(*filenameHandle); + DestroyGS(&destroyRec); + + HUnlock(filenameHandle); +} + + +void saveSettings(void) +{ + BOOLEAN success = false; + + deleteSettings(); + + allocateFilenameHandle(); + HLock(filenameHandle); + + createRec.pCount = 5; + createRec.pathname = (GSString255Ptr)(*filenameHandle); + createRec.access = destroyEnable | renameEnable | readWriteEnable; + createRec.fileType = 0x06; + createRec.auxType = 0x2000; + createRec.storageType = seedling; + CreateGS(&createRec); + if (toolerror() != 0) + { + HUnlock(filenameHandle); + return; + } + + openRec.pCount = 3; + openRec.pathname = (GSString255Ptr)(*filenameHandle); + openRec.requestAccess = writeEnable; + OpenGS(&openRec); + if (toolerror() == 0) + { + writeRec.pCount = 4; + writeRec.refNum = openRec.refNum; + writeRec.dataBuffer = (Pointer) &settings; + writeRec.requestCount = sizeof(settings); + WriteGS(&writeRec); + success = (toolerror() == 0); + + closeRec.pCount = 1; + closeRec.refNum = openRec.refNum; + CloseGS(&closeRec); + } + + HUnlock(filenameHandle); + + if (!success) + deleteSettings(); +} + + +BOOLEAN loadSettings(void) +{ + BOOLEAN success = FALSE; + + allocateFilenameHandle(); + HLock(filenameHandle); + + openRec.pCount = 12; + openRec.requestAccess = readEnable; + openRec.resourceNumber = 0; + openRec.pathname = (GSString255Ptr)(*filenameHandle); + OpenGS(&openRec); + if (toolerror() == 0) + { + if (openRec.eof == sizeof(settings)) + { + readRec.pCount = 4; + readRec.refNum = openRec.refNum; + readRec.dataBuffer = (Pointer)&settings; + readRec.requestCount = sizeof(tSettingsData); + ReadGS(&readRec); + success = (toolerror() == 0); + } + + closeRec.pCount = 1; + closeRec.refNum = openRec.refNum; + CloseGS(&closeRec); + } + HUnlock(filenameHandle); + + if (success) + { + if ((settings.magic[0] != 'B') || + (settings.magic[1] != 'u') || + (settings.magic[2] != 'G') || + (settings.magic[3] != 'S') || + (settings.version != 0)) + success = FALSE; + } + + if (success) + { + if (settings.swapStereo) + { + swapStereoChannels(); + } + } + + return success; +} + + +void swapStereoSettings(void) +{ + swapStereoChannels(); + settings.swapStereo = !settings.swapStereo; + saveSettings(); +} diff --git a/BuGS/settings.h b/BuGS/settings.h new file mode 100644 index 0000000..e954b8e --- /dev/null +++ b/BuGS/settings.h @@ -0,0 +1,20 @@ +/* + * settings.h + * BuGS + * + * Created by Jeremy Rand on 2021-06-03. + * Copyright © 2021 Jeremy Rand. All rights reserved. + */ + +#ifndef _GUARD_PROJECTBuGS_FILEsettings_ +#define _GUARD_PROJECTBuGS_FILEsettings_ + +#include + + +extern void saveSettings(void); +BOOLEAN loadSettings(void); +extern void swapStereoSettings(void); + + +#endif /* define _GUARD_PROJECTBuGS_FILEsettings_ */