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_ */