Compare commits

...

46 Commits
1.0 ... master

Author SHA1 Message Date
Jeremy Rand 59dc11e516 Make the timeouts configurable with net scores. 2023-06-11 23:33:47 -04:00
Jeremy Rand 6afbf4eb85 Finish reworking the network high score code to be independent of BuGS itself so it can be put into its own library. 2023-06-11 22:51:21 -04:00
Jeremy Rand 2f53df9eb0 Update the project settings to eliminate some Xcode warnings. 2023-06-09 00:07:36 -04:00
Jeremy Rand 3660b47fd3 Rename globalScores to netScores to work towards moving it to NetScoresGS 2023-06-08 20:04:22 -04:00
Jeremy Rand 1be8d217f1 Cleanup parse errors which appear in the latest Xcode. 2023-06-08 01:15:54 -04:00
Jeremy Rand 0ae5fa1b54 More global high score rework towards making it generic. 2023-06-07 19:53:14 -04:00
Jeremy Rand 37faafb698 More work isolating the global high score code from the game so it can be made a generic library. 2022-05-25 22:58:53 -04:00
Jeremy Rand b8b9089aa2 Start trying to extract the global high scores code so it can be turned into a generic library. 2022-05-25 21:50:38 -04:00
Jeremy Rand f1a80c792a Update the Uthernet link layer used in testing from emulators to the latest. 2022-03-29 23:48:51 -04:00
Jeremy Rand a8bf46bcac Update bootable net image with the latest version of Marinetti. 2021-07-30 23:46:40 -04:00
Jeremy Rand e5b0432991 Change the polarity of the stereo default. I used to default to "bad stereo" which matched the behaviour of mame, GSPlus and the 4soniq card but was against the Apple standard. The mame code has since been fixed and it seems like all other stereo cards are correct (I am unaware of any others that are backwards). So, by default, BuGS will start in the Apple standard L/R mode. If you have saved your settings previously, your option will be preserved correctly. Also, the display considers Apple standard stereo to be L:R while the non-standard to be R:L in the UI. 2021-07-23 23:59:11 -04:00
Jeremy Rand a7aeb5c142 Fix the URLs for the download links. 2021-07-08 22:31:18 -04:00
Jeremy Rand c76cd3d100 Update the readme for the v2.0.1 release. 2021-07-08 22:30:08 -04:00
Jeremy Rand 25a8fbeacd Display a better message when a score upload fails so it is more clear what happened. 2021-07-08 00:13:26 -04:00
Jeremy Rand 40da3b3434 Do not consider a network connection problem a soft error. Bump the version number for an upcoming bug fix release. 2021-07-07 20:11:05 -04:00
Jeremy Rand bf35d817eb Update the build environment for this project to the latest. 2021-07-06 17:04:25 -04:00
Jeremy Rand 72a65f53a7 Fix links in the readme. 2021-07-04 23:35:08 -04:00
Jeremy Rand 913add72b0 Final change before shipping v2.0. 2021-07-04 23:30:56 -04:00
Jeremy Rand 2a43113f4b Add a timeout to the TCP connect state. Prior to this, it was possible to block (indefinitely?) if there is a network glitch when trying to upload a score. 2021-07-03 16:42:41 -04:00
Jeremy Rand 165d489832 Update the readme with some more information about emulators. Make the random number generator test less likely to result in a series of scorpions appearing one after another. 2021-07-02 22:33:38 -04:00
Jeremy Rand af16a35ea7 Minor changes to the build to move some stuff to the internal make files. 2021-06-30 01:09:46 -04:00
Jeremy Rand 9a0791d812 Add a link to the full readme from the project readme. 2021-06-30 00:25:35 -04:00
Jeremy Rand 408a670e09 Bump the version number and update the readme to describe the requirements for the networking feature. 2021-06-29 17:36:51 -04:00
Jeremy Rand 739d89add0 Display the position of the players score after upload. 2021-06-28 23:12:06 -04:00
Jeremy Rand 701585df1a Move non-game code out of the main segment to make more room for more high score code. 2021-06-28 22:33:01 -04:00
Jeremy Rand 08be64a61c Add some more prompts to be clearer when the network is busy. Allow the user to retry uploading their score. Even if things go wrong, add a retry mechanism so that we will try to get the network connection up after three minutes. 2021-06-28 00:03:32 -04:00
Jeremy Rand ea085174eb Also include the position of the score and the number of scores in the response to setting a score. Bump the version number. This will become a new beta build soon. 2021-06-27 15:48:57 -04:00
Jeremy Rand 265d9cbb2e Modify the build to translate the Read.Me.md file into a Teach file for the SW distribution. 2021-06-07 00:11:44 -04:00
Jeremy Rand ed9f53551c Add the hash toolset to the baseline disk image. Add md2teach so the build can turn a markdown file into a Teach text file at build time. Introduce the Read.Me from the distribution to the GitHub repository as a markdown file so it can be translated to Teach. 2021-06-06 23:49:36 -04:00
Jeremy Rand e84d6e97c2 Update the version to beta and remove online high scores from the todo list. I now have an implementation of that. 2021-06-06 00:24:35 -04:00
Jeremy Rand abe5f0c72b Uploading scores is working now. Add timeouts for reads and the close operation so we don't end up stuck in any one state forever. Improve the error handling and provide an error code for debugging purposes if there is a problem. 2021-06-05 23:34:30 -04:00
Jeremy Rand 2c511fe33a Add some todos for the networking code. 2021-06-04 00:42:39 -04:00
Jeremy Rand 4e456cab15 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.
2021-06-04 00:41:11 -04:00
Jeremy Rand fb36a28e98 With this commit, processing of the result of a score set request is working. The connection seems to drop during the game and I don't want to poll the network during the game so the next steps will be to close and re-open the connection between games. 2021-06-03 00:32:56 -04:00
Jeremy Rand 9a77919702 Update some TODOs in the code. 2021-06-02 00:39:05 -04:00
Jeremy Rand 14cbdbac2b Fix a problem where the players initials were not going to the correct place in the global high score request. Make the set high score request message a multiple of 2. It seems that the hash functions do not agree between the GS and the server when the request is an odd number of bytes. 2021-06-02 00:33:53 -04:00
Jeremy Rand 35930c72e2 Add the code to send high scores to the server, although it is being rejected right now due to a md5 hash problem in the request that I don't yet understand. 2021-06-01 23:53:52 -04:00
Jeremy Rand 5e26684fd2 Retrieve global high scores from the Internet and display them in the game. 2021-06-01 23:13:14 -04:00
Jeremy Rand 4b76293125 Add code to load Marinetti and the Hash tool if present and bring up the network if possible. 2021-06-01 00:35:47 -04:00
Jeremy Rand 8d95d0a674 Bump the version to 1.9 since this is work towards v2.0. Add the structs for the messages to a server side high score list. Add the hash tool to the boot image for network testing. The requests will use hashes to validate that they come from the game. 2021-05-28 00:03:04 -04:00
Jeremy Rand 701d9fd312 Add some infrastructure to be able to test networking code from within the emulator. 2021-05-20 23:49:14 -04:00
Jeremy Rand c3302102d7 Update the readme with some more feedback about trackballs. 2021-03-25 17:03:38 -04:00
Jeremy Rand dfcf6a66b6 Turns out I only have fixed the "magically appearing mushroom" bug. I fixed it for slow moving centipedes coming on screen but there was a second case of the same problem for fast moving centipedes. So fix that now also. Bump the version number to 1.0.1. I will do a patch update at some point but I don't think this bug deserves pushing out a quick release update. It is an annoying minor bug at most. 2021-03-03 22:20:22 -05:00
Jeremy Rand cb47d1ff0d Update the TODO with information about running on System 5. 2021-02-24 22:45:56 -05:00
Jeremy Rand 34d5523c19 Add a new TODO to try to fit a bootable image on a single 800K disk. 2021-02-24 17:14:51 -05:00
Jeremy Rand 6b7221ed23 Add links to the version 1.0 downloads in the readme. 2021-02-17 00:05:02 -05:00
35 changed files with 3544 additions and 1290 deletions

View File

@ -2,6 +2,7 @@ BUGS
=======
This is a list of the software bugs (as opposed to the bugs in the game that you shoot) that still need attention:
* Should detect when running on system 5 or lower and quit gracefully. Today, the game crashes.
FIXED

View File

@ -3,130 +3,173 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 53;
objects = {
/* Begin PBXBuildFile section */
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 */; };
9D1716972491C49300C83148 /* config.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9D1716962491C49300C83148 /* config.txt */; };
9D1716992491C49300C83148 /* createDiskImage in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9D1716982491C49300C83148 /* createDiskImage */; };
9D17169B2491C49300C83148 /* head.mk in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9D17169A2491C49300C83148 /* head.mk */; };
9D17169D2491C49300C83148 /* launchEmulator in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9D17169C2491C49300C83148 /* launchEmulator */; };
9D17169F2491C49300C83148 /* orca-asm in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9D17169E2491C49300C83148 /* orca-asm */; };
9D1716A12491C49300C83148 /* orca-cc in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9D1716A02491C49300C83148 /* orca-cc */; };
9D1716A32491C49300C83148 /* orca-rez in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9D1716A22491C49300C83148 /* orca-rez */; };
9D1716A52491C49300C83148 /* system601.2mg in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9D1716A42491C49300C83148 /* system601.2mg */; };
9D1716A72491C49300C83148 /* tail.mk in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9D1716A62491C49300C83148 /* tail.mk */; };
9D1716AA2491C49300C83148 /* BuGS.xcscheme in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9D1716A92491C49300C83148 /* BuGS.xcscheme */; };
9DE37AF72694E070005FC562 /* Makefile in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37AF62694E070005FC562 /* Makefile */; };
9DE37AFA2694E070005FC562 /* config.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9DE37AF92694E070005FC562 /* config.txt */; };
9DE37AFC2694E070005FC562 /* createDiskImage in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9DE37AFB2694E070005FC562 /* createDiskImage */; };
9DE37AFE2694E070005FC562 /* head.mk in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9DE37AFD2694E070005FC562 /* head.mk */; };
9DE37B002694E070005FC562 /* launchEmulator in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9DE37AFF2694E070005FC562 /* launchEmulator */; };
9DE37B022694E070005FC562 /* md2teach in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9DE37B012694E070005FC562 /* md2teach */; };
9DE37B042694E070005FC562 /* orca-asm in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9DE37B032694E070005FC562 /* orca-asm */; };
9DE37B062694E070005FC562 /* orca-cc in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9DE37B052694E070005FC562 /* orca-cc */; };
9DE37B082694E070005FC562 /* orca-rez in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9DE37B072694E070005FC562 /* orca-rez */; };
9DE37B0A2694E070005FC562 /* system601.2mg in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9DE37B092694E070005FC562 /* system601.2mg */; };
9DE37B0E2694E070005FC562 /* tar in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9DE37B0D2694E070005FC562 /* tar */; };
9DE37B102694E070005FC562 /* tail.mk in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9DE37B0F2694E070005FC562 /* tail.mk */; };
9DE37B132694E070005FC562 /* BuGS.xcscheme in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9DE37B122694E070005FC562 /* BuGS.xcscheme */; };
9DE37B742694E0B0005FC562 /* level.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B332694E0B0005FC562 /* level.s */; };
9DE37B752694E0B0005FC562 /* tileConvert.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B342694E0B0005FC562 /* tileConvert.s */; };
9DE37B762694E0B0005FC562 /* gameSpider.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B362694E0B0005FC562 /* gameSpider.s */; };
9DE37B772694E0B0005FC562 /* netScores.c in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B372694E0B0005FC562 /* netScores.c */; };
9DE37B782694E0B0005FC562 /* game.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B382694E0B0005FC562 /* game.s */; };
9DE37B792694E0B0005FC562 /* gameShot.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B3A2694E0B0005FC562 /* gameShot.s */; };
9DE37B7A2694E0B0005FC562 /* loadSounds.c in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B3B2694E0B0005FC562 /* loadSounds.c */; };
9DE37B7B2694E0B0005FC562 /* gameMushroom.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B3D2694E0B0005FC562 /* gameMushroom.s */; };
9DE37B7C2694E0B0005FC562 /* globals.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B3F2694E0B0005FC562 /* globals.s */; };
9DE37B7D2694E0B0005FC562 /* gameSegments.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B402694E0B0005FC562 /* gameSegments.s */; };
9DE37B7E2694E0B0005FC562 /* score.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B412694E0B0005FC562 /* score.s */; };
9DE37B7F2694E0B0005FC562 /* gamePlayer.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B422694E0B0005FC562 /* gamePlayer.s */; };
9DE37B802694E0B0005FC562 /* gameFlea.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B452694E0B0005FC562 /* gameFlea.s */; };
9DE37B812694E0B0005FC562 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B472694E0B0005FC562 /* main.c */; };
9DE37B822694E0B0005FC562 /* settings.c in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B482694E0B0005FC562 /* settings.c */; };
9DE37B832694E0B0005FC562 /* colour.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B492694E0B0005FC562 /* colour.s */; };
9DE37B842694E0B0005FC562 /* ship.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B4B2694E0B0005FC562 /* ship.s */; };
9DE37B852694E0B0005FC562 /* solid.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B4C2694E0B0005FC562 /* solid.s */; };
9DE37B862694E0B0005FC562 /* numbers.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B4D2694E0B0005FC562 /* numbers.s */; };
9DE37B872694E0B0005FC562 /* symbols.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B4E2694E0B0005FC562 /* symbols.s */; };
9DE37B882694E0B0005FC562 /* segments.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B502694E0B0005FC562 /* segments.s */; };
9DE37B892694E0B0005FC562 /* fleas.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B512694E0B0005FC562 /* fleas.s */; };
9DE37B8A2694E0B0005FC562 /* spiders.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B522694E0B0005FC562 /* spiders.s */; };
9DE37B8B2694E0B0005FC562 /* scores.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B532694E0B0005FC562 /* scores.s */; };
9DE37B8C2694E0B0005FC562 /* explosions.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B542694E0B0005FC562 /* explosions.s */; };
9DE37B8D2694E0B0005FC562 /* scorpions.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B552694E0B0005FC562 /* scorpions.s */; };
9DE37B8E2694E0B0005FC562 /* shot.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B562694E0B0005FC562 /* shot.s */; };
9DE37B8F2694E0B0005FC562 /* letters.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B582694E0B0005FC562 /* letters.s */; };
9DE37B902694E0B0005FC562 /* mushrooms.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B592694E0B0005FC562 /* mushrooms.s */; };
9DE37B912694E0B0005FC562 /* random.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B5A2694E0B0005FC562 /* random.s */; };
9DE37B922694E0B0005FC562 /* gameScorpion.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B5B2694E0B0005FC562 /* gameScorpion.s */; };
9DE37B932694E0B0005FC562 /* gameSound.s in Sources */ = {isa = PBXBuildFile; fileRef = 9DE37B722694E0B0005FC562 /* gameSound.s */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9D17168A2491C49300C83148 /* CopyFiles */ = {
9DE37AED2694E070005FC562 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
9D17169D2491C49300C83148 /* launchEmulator in CopyFiles */,
9D1716AA2491C49300C83148 /* BuGS.xcscheme in CopyFiles */,
9D1716A12491C49300C83148 /* orca-cc in CopyFiles */,
9D1716992491C49300C83148 /* createDiskImage in CopyFiles */,
9D1716A32491C49300C83148 /* orca-rez in CopyFiles */,
9D1716972491C49300C83148 /* config.txt in CopyFiles */,
9D1716A52491C49300C83148 /* system601.2mg in CopyFiles */,
9D17169F2491C49300C83148 /* orca-asm in CopyFiles */,
9D1716922491C49300C83148 /* main.rez in CopyFiles */,
9D17169B2491C49300C83148 /* head.mk in CopyFiles */,
9D1716A72491C49300C83148 /* tail.mk in CopyFiles */,
9DE37B022694E070005FC562 /* md2teach in CopyFiles */,
9DE37B082694E070005FC562 /* orca-rez in CopyFiles */,
9DE37B102694E070005FC562 /* tail.mk in CopyFiles */,
9DE37B002694E070005FC562 /* launchEmulator in CopyFiles */,
9DE37B042694E070005FC562 /* orca-asm in CopyFiles */,
9DE37B132694E070005FC562 /* BuGS.xcscheme in CopyFiles */,
9DE37AFC2694E070005FC562 /* createDiskImage in CopyFiles */,
9DE37B0E2694E070005FC562 /* tar in CopyFiles */,
9DE37AFA2694E070005FC562 /* config.txt in CopyFiles */,
9DE37AFE2694E070005FC562 /* head.mk in CopyFiles */,
9DE37B062694E070005FC562 /* orca-cc in CopyFiles */,
9DE37B0A2694E070005FC562 /* system601.2mg in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
9D0CF93D25B553430035D329 /* createRaw */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = createRaw; sourceTree = "<group>"; };
9D0CF93E25B553CB0035D329 /* segments.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = segments.wav; sourceTree = "<group>"; };
9D0CF93F25B553DE0035D329 /* bonus.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = bonus.wav; sourceTree = "<group>"; };
9D0CF94025B553FE0035D329 /* death.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = death.wav; sourceTree = "<group>"; };
9D0CF94125B554490035D329 /* fire.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = fire.wav; sourceTree = "<group>"; };
9D0CF94225B5547B0035D329 /* kill.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = kill.wav; sourceTree = "<group>"; };
9D0CF94325B554910035D329 /* spider.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = spider.wav; sourceTree = "<group>"; };
9D0CF94425B554A10035D329 /* extralife.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = extralife.wav; sourceTree = "<group>"; };
9D0CF94525B554BC0035D329 /* flea_loop_256b.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = flea_loop_256b.wav; sourceTree = "<group>"; };
9D0CF94625B554CE0035D329 /* scorpion.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = scorpion.wav; sourceTree = "<group>"; };
9D0DC8EF258C715E00DE9E87 /* extralife.raw */ = {isa = PBXFileReference; lastKnownFileType = text; path = extralife.raw; sourceTree = "<group>"; };
9D1553DE257ACA1800657188 /* ACKNOWLEDGEMENTS.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = ACKNOWLEDGEMENTS.md; sourceTree = "<group>"; };
9D1553E9257F3E5200657188 /* fire.raw */ = {isa = PBXFileReference; lastKnownFileType = file; path = fire.raw; sourceTree = "<group>"; };
9D1553EA257F3E5200657188 /* bonus.raw */ = {isa = PBXFileReference; lastKnownFileType = file; path = bonus.raw; sourceTree = "<group>"; };
9D1553EB257F3E5200657188 /* death.raw */ = {isa = PBXFileReference; lastKnownFileType = file; path = death.raw; sourceTree = "<group>"; };
9D1553EC257F3E5200657188 /* kill.raw */ = {isa = PBXFileReference; lastKnownFileType = text; path = kill.raw; sourceTree = "<group>"; };
9D1553ED257F3E5200657188 /* segments.raw */ = {isa = PBXFileReference; lastKnownFileType = text; path = segments.raw; sourceTree = "<group>"; };
9D1553EE257F3E5200657188 /* spider.raw */ = {isa = PBXFileReference; lastKnownFileType = text; path = spider.raw; sourceTree = "<group>"; };
9D159805258A6BCB00BA42DF /* gameSound.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = gameSound.s; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.asm.orcam; };
9D1716842491C49300C83148 /* BuGS */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BuGS; sourceTree = BUILT_PRODUCTS_DIR; };
9D1716872491C49300C83148 /* BuGS.2mg */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BuGS.2mg; sourceTree = BUILT_PRODUCTS_DIR; };
9D17168C2491C49300C83148 /* doNotBuild */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = doNotBuild; sourceTree = BUILT_PRODUCTS_DIR; };
9D17168E2491C49300C83148 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; };
9D1716902491C49300C83148 /* main.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = main.h; sourceTree = "<group>"; };
9D1716912491C49300C83148 /* main.rez */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.rez; path = main.rez; sourceTree = "<group>"; };
9D1716932491C49300C83148 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
9D1716962491C49300C83148 /* config.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = config.txt; sourceTree = "<group>"; };
9D1716982491C49300C83148 /* createDiskImage */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = createDiskImage; sourceTree = "<group>"; };
9D17169A2491C49300C83148 /* head.mk */ = {isa = PBXFileReference; lastKnownFileType = text; path = head.mk; sourceTree = "<group>"; };
9D17169C2491C49300C83148 /* launchEmulator */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = launchEmulator; sourceTree = "<group>"; };
9D17169E2491C49300C83148 /* orca-asm */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "orca-asm"; sourceTree = "<group>"; };
9D1716A02491C49300C83148 /* orca-cc */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "orca-cc"; sourceTree = "<group>"; };
9D1716A22491C49300C83148 /* orca-rez */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "orca-rez"; sourceTree = "<group>"; };
9D1716A42491C49300C83148 /* system601.2mg */ = {isa = PBXFileReference; lastKnownFileType = file; path = system601.2mg; sourceTree = "<group>"; };
9D1716A62491C49300C83148 /* tail.mk */ = {isa = PBXFileReference; lastKnownFileType = text; path = tail.mk; sourceTree = "<group>"; };
9D1716A92491C49300C83148 /* BuGS.xcscheme */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = BuGS.xcscheme; path = ../../BuGS.xcodeproj/xcshareddata/xcschemes/BuGS.xcscheme; sourceTree = "<group>"; };
9D2FF6DA24C4C79A000181E5 /* random.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = random.s; sourceTree = "<group>"; };
9D3396F424AECB1D003222B3 /* explosions.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = explosions.s; sourceTree = "<group>"; };
9D3396F524AECBB1003222B3 /* spiders.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = spiders.s; sourceTree = "<group>"; };
9D3396F624AECC40003222B3 /* scorpions.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = scorpions.s; sourceTree = "<group>"; };
9D3396F724AECCAC003222B3 /* scores.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = scores.s; sourceTree = "<group>"; };
9D3396F824AECD11003222B3 /* fleas.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = fleas.s; sourceTree = "<group>"; };
9D3396F924AECD7A003222B3 /* numbers.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = numbers.s; sourceTree = "<group>"; };
9D3396FA24AECDE9003222B3 /* solid.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = solid.s; sourceTree = "<group>"; };
9D3396FB24AECF7C003222B3 /* symbols.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = symbols.s; sourceTree = "<group>"; };
9D3396FC24AECFBB003222B3 /* letters.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = letters.s; sourceTree = "<group>"; };
9D3396FD24AECFF5003222B3 /* mushrooms.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = mushrooms.s; sourceTree = "<group>"; };
9D3396FF24AED09D003222B3 /* sprites.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = sprites.md; sourceTree = "<group>"; };
9D33970024AEFBF2003222B3 /* segments.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = segments.s; sourceTree = "<group>"; };
9D33970124AF9D55003222B3 /* sprites.macros */ = {isa = PBXFileReference; lastKnownFileType = text; path = sprites.macros; sourceTree = "<group>"; };
9D47CBE02547BEDB00CDA5CB /* gameMushroom.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = gameMushroom.s; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.asm.orcam; };
9D47CC14254A698900CDA5CB /* gamePlayer.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = gamePlayer.s; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.asm.orcam; };
9D47CCBA25525C1A00CDA5CB /* genData.pl */ = {isa = PBXFileReference; lastKnownFileType = text.script.perl; path = genData.pl; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.perl; };
9D4EF43E25D7006A001BEDB0 /* tar */ = {isa = PBXFileReference; lastKnownFileType = file; path = tar; sourceTree = "<group>"; };
9D4EF46925D9D171001BEDB0 /* BuGS.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = BuGS.png; sourceTree = "<group>"; };
9D53E5B32562320300E10169 /* gameShot.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = gameShot.s; sourceTree = "<group>"; };
9D53E6472565939300E10169 /* BUGS.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = BUGS.md; sourceTree = "<group>"; };
9D62AF3B249871A300348F45 /* colour.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = colour.s; sourceTree = "<group>"; };
9D62AF3F2499CD1E00348F45 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
9D62AF402499CD3A00348F45 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
9D6DB0AE2591A67700CDBF05 /* flea.raw */ = {isa = PBXFileReference; lastKnownFileType = file; path = flea.raw; sourceTree = "<group>"; };
9D6DB0B825932CA600CDBF05 /* scorpion.raw */ = {isa = PBXFileReference; lastKnownFileType = text; path = scorpion.raw; sourceTree = "<group>"; };
9D6DB164259D759C00CDBF05 /* tileConvert.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = tileConvert.s; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.asm.orcam; };
9D6DB18625A411DA00CDBF05 /* BuGS.2mg */ = {isa = PBXFileReference; lastKnownFileType = file; path = BuGS.2mg; sourceTree = "<group>"; };
9D8AF0B72535542400C10E3C /* level.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = level.s; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.asm.orcam; };
9D8AF0B82535543000C10E3C /* score.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = score.s; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.asm.orcam; };
9D8FFC602491CA28005C9327 /* game.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = game.s; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.asm.orcam; };
9D8FFC612491CAF0005C9327 /* game.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = game.h; sourceTree = "<group>"; };
9D9F07F92553AB3800875B29 /* TODO.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = TODO.md; sourceTree = "<group>"; };
9DB1505024C3801100558B87 /* gameFlea.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = gameFlea.s; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.asm.orcam; };
9DB1505124C6875C00558B87 /* gameScorpion.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = gameScorpion.s; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.asm.orcam; };
9DB1505224C7495400558B87 /* globals.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = globals.s; sourceTree = "<group>"; };
9DB1505324C9E54C00558B87 /* gameSpider.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = gameSpider.s; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.asm.orcam; };
9DB1505424D3BF6C00558B87 /* gameSegments.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = gameSegments.s; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.asm.orcam; };
9DB1505524D3BFCE00558B87 /* global.macros */ = {isa = PBXFileReference; lastKnownFileType = text; path = global.macros; sourceTree = "<group>"; };
9DC4D7BD24B7652100BACF4B /* ship.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = ship.s; sourceTree = "<group>"; };
9DC4D7BE24B80C9600BACF4B /* shot.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = shot.s; sourceTree = "<group>"; };
9DE37AE52694E070005FC562 /* BuGS */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BuGS; sourceTree = BUILT_PRODUCTS_DIR; };
9DE37AE82694E070005FC562 /* BuGS.2mg */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BuGS.2mg; sourceTree = BUILT_PRODUCTS_DIR; };
9DE37AEA2694E070005FC562 /* BuGS.shk */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BuGS.shk; sourceTree = BUILT_PRODUCTS_DIR; };
9DE37AEF2694E070005FC562 /* doNotBuild */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = doNotBuild; sourceTree = BUILT_PRODUCTS_DIR; };
9DE37AF62694E070005FC562 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
9DE37AF92694E070005FC562 /* config.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = config.txt; sourceTree = "<group>"; };
9DE37AFB2694E070005FC562 /* createDiskImage */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = createDiskImage; sourceTree = "<group>"; };
9DE37AFD2694E070005FC562 /* head.mk */ = {isa = PBXFileReference; lastKnownFileType = text; path = head.mk; sourceTree = "<group>"; };
9DE37AFF2694E070005FC562 /* launchEmulator */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = launchEmulator; sourceTree = "<group>"; };
9DE37B012694E070005FC562 /* md2teach */ = {isa = PBXFileReference; lastKnownFileType = file; path = md2teach; sourceTree = "<group>"; };
9DE37B032694E070005FC562 /* orca-asm */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "orca-asm"; sourceTree = "<group>"; };
9DE37B052694E070005FC562 /* orca-cc */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "orca-cc"; sourceTree = "<group>"; };
9DE37B072694E070005FC562 /* orca-rez */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "orca-rez"; sourceTree = "<group>"; };
9DE37B092694E070005FC562 /* system601.2mg */ = {isa = PBXFileReference; lastKnownFileType = file; path = system601.2mg; sourceTree = "<group>"; };
9DE37B0D2694E070005FC562 /* tar */ = {isa = PBXFileReference; lastKnownFileType = file; path = tar; sourceTree = "<group>"; };
9DE37B0F2694E070005FC562 /* tail.mk */ = {isa = PBXFileReference; lastKnownFileType = text; path = tail.mk; sourceTree = "<group>"; };
9DE37B122694E070005FC562 /* BuGS.xcscheme */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = BuGS.xcscheme; path = ../../BuGS.xcodeproj/xcshareddata/xcschemes/BuGS.xcscheme; sourceTree = "<group>"; };
9DE37B292694E088005FC562 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
9DE37B2A2694E088005FC562 /* BuGS.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = BuGS.png; sourceTree = "<group>"; };
9DE37B2B2694E088005FC562 /* BUGS.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = BUGS.md; sourceTree = "<group>"; };
9DE37B2C2694E088005FC562 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
9DE37B2D2694E088005FC562 /* TODO.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = TODO.md; sourceTree = "<group>"; };
9DE37B2E2694E088005FC562 /* ACKNOWLEDGEMENTS.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = ACKNOWLEDGEMENTS.md; sourceTree = "<group>"; };
9DE37B332694E0B0005FC562 /* level.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = level.s; sourceTree = "<group>"; };
9DE37B342694E0B0005FC562 /* tileConvert.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = tileConvert.s; sourceTree = "<group>"; };
9DE37B352694E0B0005FC562 /* main.rez */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.rez; path = main.rez; sourceTree = "<group>"; };
9DE37B362694E0B0005FC562 /* gameSpider.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = gameSpider.s; sourceTree = "<group>"; };
9DE37B372694E0B0005FC562 /* netScores.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = netScores.c; sourceTree = "<group>"; };
9DE37B382694E0B0005FC562 /* game.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = game.s; sourceTree = "<group>"; };
9DE37B392694E0B0005FC562 /* main.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = main.h; sourceTree = "<group>"; };
9DE37B3A2694E0B0005FC562 /* gameShot.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = gameShot.s; sourceTree = "<group>"; };
9DE37B3B2694E0B0005FC562 /* loadSounds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadSounds.c; sourceTree = "<group>"; };
9DE37B3C2694E0B0005FC562 /* loadSounds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = loadSounds.h; sourceTree = "<group>"; };
9DE37B3D2694E0B0005FC562 /* gameMushroom.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = gameMushroom.s; sourceTree = "<group>"; };
9DE37B3E2694E0B0005FC562 /* netScores.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = netScores.h; sourceTree = "<group>"; };
9DE37B3F2694E0B0005FC562 /* globals.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = globals.s; sourceTree = "<group>"; };
9DE37B402694E0B0005FC562 /* gameSegments.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = gameSegments.s; sourceTree = "<group>"; };
9DE37B412694E0B0005FC562 /* score.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = score.s; sourceTree = "<group>"; };
9DE37B422694E0B0005FC562 /* gamePlayer.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = gamePlayer.s; sourceTree = "<group>"; };
9DE37B432694E0B0005FC562 /* Read.Me.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Read.Me.md; sourceTree = "<group>"; };
9DE37B442694E0B0005FC562 /* global.macros */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = global.macros; sourceTree = "<group>"; };
9DE37B452694E0B0005FC562 /* gameFlea.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = gameFlea.s; sourceTree = "<group>"; };
9DE37B462694E0B0005FC562 /* settings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = settings.h; sourceTree = "<group>"; };
9DE37B472694E0B0005FC562 /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; };
9DE37B482694E0B0005FC562 /* settings.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = settings.c; sourceTree = "<group>"; };
9DE37B492694E0B0005FC562 /* colour.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = colour.s; sourceTree = "<group>"; };
9DE37B4B2694E0B0005FC562 /* ship.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = ship.s; sourceTree = "<group>"; };
9DE37B4C2694E0B0005FC562 /* solid.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = solid.s; sourceTree = "<group>"; };
9DE37B4D2694E0B0005FC562 /* numbers.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = numbers.s; sourceTree = "<group>"; };
9DE37B4E2694E0B0005FC562 /* symbols.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = symbols.s; sourceTree = "<group>"; };
9DE37B4F2694E0B0005FC562 /* sprites.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = sprites.md; sourceTree = "<group>"; };
9DE37B502694E0B0005FC562 /* segments.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = segments.s; sourceTree = "<group>"; };
9DE37B512694E0B0005FC562 /* fleas.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = fleas.s; sourceTree = "<group>"; };
9DE37B522694E0B0005FC562 /* spiders.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = spiders.s; sourceTree = "<group>"; };
9DE37B532694E0B0005FC562 /* scores.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = scores.s; sourceTree = "<group>"; };
9DE37B542694E0B0005FC562 /* explosions.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = explosions.s; sourceTree = "<group>"; };
9DE37B552694E0B0005FC562 /* scorpions.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = scorpions.s; sourceTree = "<group>"; };
9DE37B562694E0B0005FC562 /* shot.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = shot.s; sourceTree = "<group>"; };
9DE37B572694E0B0005FC562 /* sprites.macros */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = sprites.macros; sourceTree = "<group>"; };
9DE37B582694E0B0005FC562 /* letters.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = letters.s; sourceTree = "<group>"; };
9DE37B592694E0B0005FC562 /* mushrooms.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = mushrooms.s; sourceTree = "<group>"; };
9DE37B5A2694E0B0005FC562 /* random.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = random.s; sourceTree = "<group>"; };
9DE37B5B2694E0B0005FC562 /* gameScorpion.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = gameScorpion.s; sourceTree = "<group>"; };
9DE37B5D2694E0B0005FC562 /* flea.raw */ = {isa = PBXFileReference; lastKnownFileType = file; path = flea.raw; sourceTree = "<group>"; };
9DE37B5E2694E0B0005FC562 /* segments.raw */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = segments.raw; sourceTree = "<group>"; };
9DE37B5F2694E0B0005FC562 /* scorpion.raw */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = scorpion.raw; sourceTree = "<group>"; };
9DE37B602694E0B0005FC562 /* death.raw */ = {isa = PBXFileReference; lastKnownFileType = file; path = death.raw; sourceTree = "<group>"; };
9DE37B612694E0B0005FC562 /* kill.raw */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = kill.raw; sourceTree = "<group>"; };
9DE37B622694E0B0005FC562 /* spider.raw */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = spider.raw; sourceTree = "<group>"; };
9DE37B632694E0B0005FC562 /* fire.raw */ = {isa = PBXFileReference; lastKnownFileType = file; path = fire.raw; sourceTree = "<group>"; };
9DE37B652694E0B0005FC562 /* extralife.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = extralife.wav; sourceTree = "<group>"; };
9DE37B662694E0B0005FC562 /* bonus.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = bonus.wav; sourceTree = "<group>"; };
9DE37B672694E0B0005FC562 /* kill.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = kill.wav; sourceTree = "<group>"; };
9DE37B682694E0B0005FC562 /* spider.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = spider.wav; sourceTree = "<group>"; };
9DE37B692694E0B0005FC562 /* fire.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = fire.wav; sourceTree = "<group>"; };
9DE37B6A2694E0B0005FC562 /* segments.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = segments.wav; sourceTree = "<group>"; };
9DE37B6B2694E0B0005FC562 /* scorpion.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = scorpion.wav; sourceTree = "<group>"; };
9DE37B6C2694E0B0005FC562 /* createRaw */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = createRaw; sourceTree = "<group>"; };
9DE37B6D2694E0B0005FC562 /* death.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = death.wav; sourceTree = "<group>"; };
9DE37B6E2694E0B0005FC562 /* flea_loop_256b.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = flea_loop_256b.wav; sourceTree = "<group>"; };
9DE37B6F2694E0B0005FC562 /* extralife.raw */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = extralife.raw; sourceTree = "<group>"; };
9DE37B702694E0B0005FC562 /* bonus.raw */ = {isa = PBXFileReference; lastKnownFileType = file; path = bonus.raw; sourceTree = "<group>"; };
9DE37B712694E0B0005FC562 /* game.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = game.h; sourceTree = "<group>"; };
9DE37B722694E0B0005FC562 /* gameSound.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = gameSound.s; sourceTree = "<group>"; };
9DE37B732694E0B0005FC562 /* genData.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = genData.pl; sourceTree = "<group>"; };
9DE37B982694E0DF005FC562 /* system601_net.2mg */ = {isa = PBXFileReference; lastKnownFileType = file; path = system601_net.2mg; sourceTree = "<group>"; };
9DE37B9D2694E105005FC562 /* empty.2mg */ = {isa = PBXFileReference; lastKnownFileType = file; path = empty.2mg; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
9D1716892491C49300C83148 /* Frameworks */ = {
9DE37AEC2694E070005FC562 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
@ -136,152 +179,162 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9D0CF93C25B5531F0035D329 /* sources */ = {
9DE37ADE2694E070005FC562 = {
isa = PBXGroup;
children = (
9D0CF93E25B553CB0035D329 /* segments.wav */,
9D0CF93F25B553DE0035D329 /* bonus.wav */,
9D0CF94025B553FE0035D329 /* death.wav */,
9D0CF94125B554490035D329 /* fire.wav */,
9D0CF94225B5547B0035D329 /* kill.wav */,
9D0CF94325B554910035D329 /* spider.wav */,
9D0CF94425B554A10035D329 /* extralife.wav */,
9D0CF94525B554BC0035D329 /* flea_loop_256b.wav */,
9D0CF94625B554CE0035D329 /* scorpion.wav */,
9D0CF93D25B553430035D329 /* createRaw */,
);
path = sources;
sourceTree = "<group>";
};
9D1553E8257F3DC600657188 /* sound */ = {
isa = PBXGroup;
children = (
9D0CF93C25B5531F0035D329 /* sources */,
9D1553EA257F3E5200657188 /* bonus.raw */,
9D6DB0AE2591A67700CDBF05 /* flea.raw */,
9D0DC8EF258C715E00DE9E87 /* extralife.raw */,
9D1553EB257F3E5200657188 /* death.raw */,
9D6DB0B825932CA600CDBF05 /* scorpion.raw */,
9D1553E9257F3E5200657188 /* fire.raw */,
9D1553EC257F3E5200657188 /* kill.raw */,
9D1553ED257F3E5200657188 /* segments.raw */,
9D1553EE257F3E5200657188 /* spider.raw */,
);
path = sound;
sourceTree = "<group>";
};
9D17167D2491C49300C83148 = {
isa = PBXGroup;
children = (
9D62AF3F2499CD1E00348F45 /* LICENSE */,
9D62AF402499CD3A00348F45 /* README.md */,
9D9F07F92553AB3800875B29 /* TODO.md */,
9D53E6472565939300E10169 /* BUGS.md */,
9D4EF46925D9D171001BEDB0 /* BuGS.png */,
9D1553DE257ACA1800657188 /* ACKNOWLEDGEMENTS.md */,
9D17168D2491C49300C83148 /* BuGS */,
9D1716852491C49300C83148 /* Products */,
9DE37B2E2694E088005FC562 /* ACKNOWLEDGEMENTS.md */,
9DE37B2B2694E088005FC562 /* BUGS.md */,
9DE37B2A2694E088005FC562 /* BuGS.png */,
9DE37B292694E088005FC562 /* LICENSE */,
9DE37B2C2694E088005FC562 /* README.md */,
9DE37B2D2694E088005FC562 /* TODO.md */,
9DE37AF02694E070005FC562 /* BuGS */,
9DE37AE62694E070005FC562 /* Products */,
);
sourceTree = "<group>";
};
9D1716852491C49300C83148 /* Products */ = {
9DE37AE62694E070005FC562 /* Products */ = {
isa = PBXGroup;
children = (
9D1716842491C49300C83148 /* BuGS */,
9D1716872491C49300C83148 /* BuGS.2mg */,
9D17168C2491C49300C83148 /* doNotBuild */,
9DE37AE52694E070005FC562 /* BuGS */,
9DE37AE82694E070005FC562 /* BuGS.2mg */,
9DE37AEA2694E070005FC562 /* BuGS.shk */,
9DE37AEF2694E070005FC562 /* doNotBuild */,
);
name = Products;
sourceTree = "<group>";
};
9D17168D2491C49300C83148 /* BuGS */ = {
9DE37AF02694E070005FC562 /* BuGS */ = {
isa = PBXGroup;
children = (
9D17168E2491C49300C83148 /* main.c */,
9D1716902491C49300C83148 /* main.h */,
9D8FFC602491CA28005C9327 /* game.s */,
9D8FFC612491CAF0005C9327 /* game.h */,
9DB1505024C3801100558B87 /* gameFlea.s */,
9DB1505124C6875C00558B87 /* gameScorpion.s */,
9DB1505324C9E54C00558B87 /* gameSpider.s */,
9DB1505424D3BF6C00558B87 /* gameSegments.s */,
9D47CBE02547BEDB00CDA5CB /* gameMushroom.s */,
9D47CC14254A698900CDA5CB /* gamePlayer.s */,
9D53E5B32562320300E10169 /* gameShot.s */,
9D159805258A6BCB00BA42DF /* gameSound.s */,
9D6DB164259D759C00CDBF05 /* tileConvert.s */,
9D8AF0B72535542400C10E3C /* level.s */,
9D8AF0B82535543000C10E3C /* score.s */,
9D62AF3B249871A300348F45 /* colour.s */,
9D2FF6DA24C4C79A000181E5 /* random.s */,
9DB1505224C7495400558B87 /* globals.s */,
9DB1505524D3BFCE00558B87 /* global.macros */,
9D47CCBA25525C1A00CDA5CB /* genData.pl */,
9D1553E8257F3DC600657188 /* sound */,
9D3396F324AECACC003222B3 /* sprites */,
9D1716912491C49300C83148 /* main.rez */,
9D1716932491C49300C83148 /* Makefile */,
9D1716952491C49300C83148 /* make */,
9D1716A82491C49300C83148 /* Supporting Files */,
9DE37B492694E0B0005FC562 /* colour.s */,
9DE37B712694E0B0005FC562 /* game.h */,
9DE37B382694E0B0005FC562 /* game.s */,
9DE37B452694E0B0005FC562 /* gameFlea.s */,
9DE37B3D2694E0B0005FC562 /* gameMushroom.s */,
9DE37B422694E0B0005FC562 /* gamePlayer.s */,
9DE37B5B2694E0B0005FC562 /* gameScorpion.s */,
9DE37B402694E0B0005FC562 /* gameSegments.s */,
9DE37B3A2694E0B0005FC562 /* gameShot.s */,
9DE37B722694E0B0005FC562 /* gameSound.s */,
9DE37B362694E0B0005FC562 /* gameSpider.s */,
9DE37B732694E0B0005FC562 /* genData.pl */,
9DE37B442694E0B0005FC562 /* global.macros */,
9DE37B3F2694E0B0005FC562 /* globals.s */,
9DE37B372694E0B0005FC562 /* netScores.c */,
9DE37B3E2694E0B0005FC562 /* netScores.h */,
9DE37B332694E0B0005FC562 /* level.s */,
9DE37B3B2694E0B0005FC562 /* loadSounds.c */,
9DE37B3C2694E0B0005FC562 /* loadSounds.h */,
9DE37B472694E0B0005FC562 /* main.c */,
9DE37B392694E0B0005FC562 /* main.h */,
9DE37B352694E0B0005FC562 /* main.rez */,
9DE37B5A2694E0B0005FC562 /* random.s */,
9DE37B432694E0B0005FC562 /* Read.Me.md */,
9DE37B412694E0B0005FC562 /* score.s */,
9DE37B482694E0B0005FC562 /* settings.c */,
9DE37B462694E0B0005FC562 /* settings.h */,
9DE37B342694E0B0005FC562 /* tileConvert.s */,
9DE37B5C2694E0B0005FC562 /* sound */,
9DE37B4A2694E0B0005FC562 /* sprites */,
9DE37AF62694E070005FC562 /* Makefile */,
9DE37AF82694E070005FC562 /* make */,
9DE37B112694E070005FC562 /* Supporting Files */,
);
path = BuGS;
sourceTree = "<group>";
};
9D1716952491C49300C83148 /* make */ = {
9DE37AF82694E070005FC562 /* make */ = {
isa = PBXGroup;
children = (
9D1716962491C49300C83148 /* config.txt */,
9D1716982491C49300C83148 /* createDiskImage */,
9D17169A2491C49300C83148 /* head.mk */,
9D4EF43E25D7006A001BEDB0 /* tar */,
9D17169C2491C49300C83148 /* launchEmulator */,
9D6DB18625A411DA00CDBF05 /* BuGS.2mg */,
9D17169E2491C49300C83148 /* orca-asm */,
9D1716A02491C49300C83148 /* orca-cc */,
9D1716A22491C49300C83148 /* orca-rez */,
9D1716A42491C49300C83148 /* system601.2mg */,
9D1716A62491C49300C83148 /* tail.mk */,
9DE37AF92694E070005FC562 /* config.txt */,
9DE37AFB2694E070005FC562 /* createDiskImage */,
9DE37AFD2694E070005FC562 /* head.mk */,
9DE37AFF2694E070005FC562 /* launchEmulator */,
9DE37B012694E070005FC562 /* md2teach */,
9DE37B032694E070005FC562 /* orca-asm */,
9DE37B052694E070005FC562 /* orca-cc */,
9DE37B072694E070005FC562 /* orca-rez */,
9DE37B9D2694E105005FC562 /* empty.2mg */,
9DE37B092694E070005FC562 /* system601.2mg */,
9DE37B982694E0DF005FC562 /* system601_net.2mg */,
9DE37B0D2694E070005FC562 /* tar */,
9DE37B0F2694E070005FC562 /* tail.mk */,
);
path = make;
sourceTree = "<group>";
};
9D1716A82491C49300C83148 /* Supporting Files */ = {
9DE37B112694E070005FC562 /* Supporting Files */ = {
isa = PBXGroup;
children = (
9D1716A92491C49300C83148 /* BuGS.xcscheme */,
9DE37B122694E070005FC562 /* BuGS.xcscheme */,
);
path = "Supporting Files";
sourceTree = "<group>";
};
9D3396F324AECACC003222B3 /* sprites */ = {
9DE37B4A2694E0B0005FC562 /* sprites */ = {
isa = PBXGroup;
children = (
9D3396FF24AED09D003222B3 /* sprites.md */,
9D33970124AF9D55003222B3 /* sprites.macros */,
9D3396F424AECB1D003222B3 /* explosions.s */,
9D3396F524AECBB1003222B3 /* spiders.s */,
9D3396F624AECC40003222B3 /* scorpions.s */,
9D3396F724AECCAC003222B3 /* scores.s */,
9D3396F824AECD11003222B3 /* fleas.s */,
9D3396F924AECD7A003222B3 /* numbers.s */,
9D3396FA24AECDE9003222B3 /* solid.s */,
9D3396FB24AECF7C003222B3 /* symbols.s */,
9D3396FC24AECFBB003222B3 /* letters.s */,
9D3396FD24AECFF5003222B3 /* mushrooms.s */,
9D33970024AEFBF2003222B3 /* segments.s */,
9DC4D7BD24B7652100BACF4B /* ship.s */,
9DC4D7BE24B80C9600BACF4B /* shot.s */,
9DE37B4B2694E0B0005FC562 /* ship.s */,
9DE37B4C2694E0B0005FC562 /* solid.s */,
9DE37B4D2694E0B0005FC562 /* numbers.s */,
9DE37B4E2694E0B0005FC562 /* symbols.s */,
9DE37B4F2694E0B0005FC562 /* sprites.md */,
9DE37B502694E0B0005FC562 /* segments.s */,
9DE37B512694E0B0005FC562 /* fleas.s */,
9DE37B522694E0B0005FC562 /* spiders.s */,
9DE37B532694E0B0005FC562 /* scores.s */,
9DE37B542694E0B0005FC562 /* explosions.s */,
9DE37B552694E0B0005FC562 /* scorpions.s */,
9DE37B562694E0B0005FC562 /* shot.s */,
9DE37B572694E0B0005FC562 /* sprites.macros */,
9DE37B582694E0B0005FC562 /* letters.s */,
9DE37B592694E0B0005FC562 /* mushrooms.s */,
);
path = sprites;
sourceTree = "<group>";
};
9DE37B5C2694E0B0005FC562 /* sound */ = {
isa = PBXGroup;
children = (
9DE37B5D2694E0B0005FC562 /* flea.raw */,
9DE37B5E2694E0B0005FC562 /* segments.raw */,
9DE37B5F2694E0B0005FC562 /* scorpion.raw */,
9DE37B602694E0B0005FC562 /* death.raw */,
9DE37B612694E0B0005FC562 /* kill.raw */,
9DE37B622694E0B0005FC562 /* spider.raw */,
9DE37B632694E0B0005FC562 /* fire.raw */,
9DE37B642694E0B0005FC562 /* sources */,
9DE37B6F2694E0B0005FC562 /* extralife.raw */,
9DE37B702694E0B0005FC562 /* bonus.raw */,
);
path = sound;
sourceTree = "<group>";
};
9DE37B642694E0B0005FC562 /* sources */ = {
isa = PBXGroup;
children = (
9DE37B652694E0B0005FC562 /* extralife.wav */,
9DE37B662694E0B0005FC562 /* bonus.wav */,
9DE37B672694E0B0005FC562 /* kill.wav */,
9DE37B682694E0B0005FC562 /* spider.wav */,
9DE37B692694E0B0005FC562 /* fire.wav */,
9DE37B6A2694E0B0005FC562 /* segments.wav */,
9DE37B6B2694E0B0005FC562 /* scorpion.wav */,
9DE37B6C2694E0B0005FC562 /* createRaw */,
9DE37B6D2694E0B0005FC562 /* death.wav */,
9DE37B6E2694E0B0005FC562 /* flea_loop_256b.wav */,
);
path = sources;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXLegacyTarget section */
9D1716822491C49300C83148 /* BuGS */ = {
9DE37AE32694E070005FC562 /* BuGS */ = {
isa = PBXLegacyTarget;
buildArgumentsString = "-C BuGS $(ACTION)";
buildConfigurationList = 9D1716AD2491C49300C83148 /* Build configuration list for PBXLegacyTarget "BuGS" */;
buildConfigurationList = 9DE37B162694E070005FC562 /* Build configuration list for PBXLegacyTarget "BuGS" */;
buildPhases = (
);
buildToolPath = /usr/bin/make;
@ -294,9 +347,9 @@
/* End PBXLegacyTarget section */
/* Begin PBXNativeTarget section */
9D1716832491C49300C83148 /* Binary */ = {
9DE37AE42694E070005FC562 /* Binary */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9D1716B02491C49300C83148 /* Build configuration list for PBXNativeTarget "Binary" */;
buildConfigurationList = 9DE37B192694E070005FC562 /* Build configuration list for PBXNativeTarget "Binary" */;
buildPhases = (
);
buildRules = (
@ -305,12 +358,12 @@
);
name = Binary;
productName = Binary;
productReference = 9D1716842491C49300C83148 /* BuGS */;
productReference = 9DE37AE52694E070005FC562 /* BuGS */;
productType = "com.apple.product-type.tool";
};
9D1716862491C49300C83148 /* DiskImage */ = {
9DE37AE72694E070005FC562 /* DiskImage */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9D1716B32491C49300C83148 /* Build configuration list for PBXNativeTarget "DiskImage" */;
buildConfigurationList = 9DE37B1C2694E070005FC562 /* Build configuration list for PBXNativeTarget "DiskImage" */;
buildPhases = (
);
buildRules = (
@ -319,16 +372,30 @@
);
name = DiskImage;
productName = DiskImage;
productReference = 9D1716872491C49300C83148 /* BuGS.2mg */;
productReference = 9DE37AE82694E070005FC562 /* BuGS.2mg */;
productType = "com.apple.product-type.tool";
};
9D17168B2491C49300C83148 /* doNotBuild */ = {
9DE37AE92694E070005FC562 /* Archive */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9D1716B62491C49300C83148 /* Build configuration list for PBXNativeTarget "doNotBuild" */;
buildConfigurationList = 9DE37B1F2694E070005FC562 /* Build configuration list for PBXNativeTarget "Archive" */;
buildPhases = (
9D1716882491C49300C83148 /* Sources */,
9D1716892491C49300C83148 /* Frameworks */,
9D17168A2491C49300C83148 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = Archive;
productName = Archive;
productReference = 9DE37AEA2694E070005FC562 /* BuGS.shk */;
productType = "com.apple.product-type.tool";
};
9DE37AEE2694E070005FC562 /* doNotBuild */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9DE37B222694E070005FC562 /* Build configuration list for PBXNativeTarget "doNotBuild" */;
buildPhases = (
9DE37AEB2694E070005FC562 /* Sources */,
9DE37AEC2694E070005FC562 /* Frameworks */,
9DE37AED2694E070005FC562 /* CopyFiles */,
);
buildRules = (
);
@ -336,33 +403,36 @@
);
name = doNotBuild;
productName = doNotBuild;
productReference = 9D17168C2491C49300C83148 /* doNotBuild */;
productReference = 9DE37AEF2694E070005FC562 /* doNotBuild */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
9D17167E2491C49300C83148 /* Project object */ = {
9DE37ADF2694E070005FC562 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1130;
ORGANIZATIONNAME = "Jeremy Rand";
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1430;
TargetAttributes = {
9D1716822491C49300C83148 = {
CreatedOnToolsVersion = 11.3.1;
9DE37AE32694E070005FC562 = {
CreatedOnToolsVersion = 12.4;
};
9D1716832491C49300C83148 = {
CreatedOnToolsVersion = 11.3.1;
9DE37AE42694E070005FC562 = {
CreatedOnToolsVersion = 12.4;
};
9D1716862491C49300C83148 = {
CreatedOnToolsVersion = 11.3.1;
9DE37AE72694E070005FC562 = {
CreatedOnToolsVersion = 12.4;
};
9D17168B2491C49300C83148 = {
CreatedOnToolsVersion = 11.3.1;
9DE37AE92694E070005FC562 = {
CreatedOnToolsVersion = 12.4;
};
9DE37AEE2694E070005FC562 = {
CreatedOnToolsVersion = 12.4;
};
};
};
buildConfigurationList = 9D1716812491C49300C83148 /* Build configuration list for PBXProject "BuGS" */;
buildConfigurationList = 9DE37AE22694E070005FC562 /* Build configuration list for PBXProject "BuGS" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
@ -370,33 +440,65 @@
en,
Base,
);
mainGroup = 9D17167D2491C49300C83148;
productRefGroup = 9D1716852491C49300C83148 /* Products */;
mainGroup = 9DE37ADE2694E070005FC562;
productRefGroup = 9DE37AE62694E070005FC562 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
9D1716822491C49300C83148 /* BuGS */,
9D1716832491C49300C83148 /* Binary */,
9D1716862491C49300C83148 /* DiskImage */,
9D17168B2491C49300C83148 /* doNotBuild */,
9DE37AE32694E070005FC562 /* BuGS */,
9DE37AE42694E070005FC562 /* Binary */,
9DE37AE72694E070005FC562 /* DiskImage */,
9DE37AE92694E070005FC562 /* Archive */,
9DE37AEE2694E070005FC562 /* doNotBuild */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
9D1716882491C49300C83148 /* Sources */ = {
9DE37AEB2694E070005FC562 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9D1716942491C49300C83148 /* Makefile in Sources */,
9D17168F2491C49300C83148 /* main.c in Sources */,
9DE37B892694E0B0005FC562 /* fleas.s in Sources */,
9DE37B912694E0B0005FC562 /* random.s in Sources */,
9DE37B832694E0B0005FC562 /* colour.s in Sources */,
9DE37B842694E0B0005FC562 /* ship.s in Sources */,
9DE37B8F2694E0B0005FC562 /* letters.s in Sources */,
9DE37B7E2694E0B0005FC562 /* score.s in Sources */,
9DE37B8A2694E0B0005FC562 /* spiders.s in Sources */,
9DE37B8E2694E0B0005FC562 /* shot.s in Sources */,
9DE37B802694E0B0005FC562 /* gameFlea.s in Sources */,
9DE37B7F2694E0B0005FC562 /* gamePlayer.s in Sources */,
9DE37B882694E0B0005FC562 /* segments.s in Sources */,
9DE37B7B2694E0B0005FC562 /* gameMushroom.s in Sources */,
9DE37B742694E0B0005FC562 /* level.s in Sources */,
9DE37B812694E0B0005FC562 /* main.c in Sources */,
9DE37B7D2694E0B0005FC562 /* gameSegments.s in Sources */,
9DE37B7A2694E0B0005FC562 /* loadSounds.c in Sources */,
9DE37AF72694E070005FC562 /* Makefile in Sources */,
9DE37B852694E0B0005FC562 /* solid.s in Sources */,
9DE37B7C2694E0B0005FC562 /* globals.s in Sources */,
9DE37B872694E0B0005FC562 /* symbols.s in Sources */,
9DE37B772694E0B0005FC562 /* netScores.c in Sources */,
9DE37B8B2694E0B0005FC562 /* scores.s in Sources */,
9DE37B932694E0B0005FC562 /* gameSound.s in Sources */,
9DE37B922694E0B0005FC562 /* gameScorpion.s in Sources */,
9DE37B762694E0B0005FC562 /* gameSpider.s in Sources */,
9DE37B822694E0B0005FC562 /* settings.c in Sources */,
9DE37B902694E0B0005FC562 /* mushrooms.s in Sources */,
9DE37B8C2694E0B0005FC562 /* explosions.s in Sources */,
9DE37B8D2694E0B0005FC562 /* scorpions.s in Sources */,
9DE37B752694E0B0005FC562 /* tileConvert.s in Sources */,
9DE37B792694E0B0005FC562 /* gameShot.s in Sources */,
9DE37B862694E0B0005FC562 /* numbers.s in Sources */,
9DE37B782694E0B0005FC562 /* game.s in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
9D1716AB2491C49300C83148 /* Debug */ = {
9DE37B142694E070005FC562 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
@ -422,6 +524,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@ -429,6 +532,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@ -452,7 +556,7 @@
};
name = Debug;
};
9D1716AC2491C49300C83148 /* Release */ = {
9DE37B152694E070005FC562 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
@ -478,6 +582,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@ -485,6 +590,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@ -501,10 +607,11 @@
};
name = Release;
};
9D1716AE2491C49300C83148 /* Debug */ = {
9DE37B172694E070005FC562 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEBUGGING_SYMBOLS = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = VD9FGCW36C;
@ -516,10 +623,11 @@
};
name = Debug;
};
9D1716AF2491C49300C83148 /* Release */ = {
9DE37B182694E070005FC562 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = VD9FGCW36C;
OTHER_CFLAGS = "";
@ -528,50 +636,77 @@
};
name = Release;
};
9D1716B12491C49300C83148 /* Debug */ = {
9DE37B1A2694E070005FC562 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = VD9FGCW36C;
ENABLE_HARDENED_RUNTIME = YES;
PRODUCT_NAME = BuGS;
};
name = Debug;
};
9D1716B22491C49300C83148 /* Release */ = {
9DE37B1B2694E070005FC562 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = VD9FGCW36C;
ENABLE_HARDENED_RUNTIME = YES;
PRODUCT_NAME = BuGS;
};
name = Release;
};
9D1716B42491C49300C83148 /* Debug */ = {
9DE37B1D2694E070005FC562 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = VD9FGCW36C;
ENABLE_HARDENED_RUNTIME = YES;
PRODUCT_NAME = BuGS.2mg;
};
name = Debug;
};
9D1716B52491C49300C83148 /* Release */ = {
9DE37B1E2694E070005FC562 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = VD9FGCW36C;
ENABLE_HARDENED_RUNTIME = YES;
PRODUCT_NAME = BuGS.2mg;
};
name = Release;
};
9D1716B72491C49300C83148 /* Debug */ = {
9DE37B202694E070005FC562 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = VD9FGCW36C;
ENABLE_HARDENED_RUNTIME = YES;
PRODUCT_NAME = BuGS.shk;
};
name = Debug;
};
9DE37B212694E070005FC562 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = VD9FGCW36C;
ENABLE_HARDENED_RUNTIME = YES;
PRODUCT_NAME = BuGS.shk;
};
name = Release;
};
9DE37B232694E070005FC562 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = VD9FGCW36C;
ENABLE_HARDENED_RUNTIME = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
@ -590,10 +725,11 @@
};
name = Debug;
};
9D1716B82491C49300C83148 /* Release */ = {
9DE37B242694E070005FC562 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = VD9FGCW36C;
ENABLE_HARDENED_RUNTIME = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
@ -615,52 +751,61 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
9D1716812491C49300C83148 /* Build configuration list for PBXProject "BuGS" */ = {
9DE37AE22694E070005FC562 /* Build configuration list for PBXProject "BuGS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9D1716AB2491C49300C83148 /* Debug */,
9D1716AC2491C49300C83148 /* Release */,
9DE37B142694E070005FC562 /* Debug */,
9DE37B152694E070005FC562 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9D1716AD2491C49300C83148 /* Build configuration list for PBXLegacyTarget "BuGS" */ = {
9DE37B162694E070005FC562 /* Build configuration list for PBXLegacyTarget "BuGS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9D1716AE2491C49300C83148 /* Debug */,
9D1716AF2491C49300C83148 /* Release */,
9DE37B172694E070005FC562 /* Debug */,
9DE37B182694E070005FC562 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9D1716B02491C49300C83148 /* Build configuration list for PBXNativeTarget "Binary" */ = {
9DE37B192694E070005FC562 /* Build configuration list for PBXNativeTarget "Binary" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9D1716B12491C49300C83148 /* Debug */,
9D1716B22491C49300C83148 /* Release */,
9DE37B1A2694E070005FC562 /* Debug */,
9DE37B1B2694E070005FC562 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9D1716B32491C49300C83148 /* Build configuration list for PBXNativeTarget "DiskImage" */ = {
9DE37B1C2694E070005FC562 /* Build configuration list for PBXNativeTarget "DiskImage" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9D1716B42491C49300C83148 /* Debug */,
9D1716B52491C49300C83148 /* Release */,
9DE37B1D2694E070005FC562 /* Debug */,
9DE37B1E2694E070005FC562 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9D1716B62491C49300C83148 /* Build configuration list for PBXNativeTarget "doNotBuild" */ = {
9DE37B1F2694E070005FC562 /* Build configuration list for PBXNativeTarget "Archive" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9D1716B72491C49300C83148 /* Debug */,
9D1716B82491C49300C83148 /* Release */,
9DE37B202694E070005FC562 /* Debug */,
9DE37B212694E070005FC562 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9DE37B222694E070005FC562 /* Build configuration list for PBXNativeTarget "doNotBuild" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9DE37B232694E070005FC562 /* Debug */,
9DE37B242694E070005FC562 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 9D17167E2491C49300C83148 /* Project object */;
rootObject = 9DE37ADF2694E070005FC562 /* Project object */;
}

View File

@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:BuGS.xcodeproj">
location = "self:">
</FileRef>
</Workspace>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0830"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -14,7 +14,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9D1716822491C49300C83148"
BlueprintIdentifier = "9DE37AE32694E070005FC562"
BuildableName = "BuGS"
BlueprintName = "BuGS"
ReferencedContainer = "container:BuGS.xcodeproj">
@ -47,7 +47,7 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9D1716822491C49300C83148"
BlueprintIdentifier = "9DE37AE32694E070005FC562"
BuildableName = "BuGS"
BlueprintName = "BuGS"
ReferencedContainer = "container:BuGS.xcodeproj">
@ -59,7 +59,7 @@
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "$PROJECT_DIR/BuGS"
argument = "&quot;$PROJECT_DIR/BuGS&quot;"
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
@ -89,7 +89,7 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9D1716822491C49300C83148"
BlueprintIdentifier = "9DE37AE32694E070005FC562"
BuildableName = "BuGS"
BlueprintName = "BuGS"
ReferencedContainer = "container:BuGS.xcodeproj">

View File

@ -4,10 +4,15 @@
<dict>
<key>SchemeUserState</key>
<dict>
<key>Archive.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>3</integer>
</dict>
<key>Binary.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
<integer>1</integer>
</dict>
<key>BuGS.xcscheme_^#shared#^_</key>
<dict>
@ -17,12 +22,12 @@
<key>DiskImage.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>3</integer>
<integer>4</integer>
</dict>
<key>doNotBuild.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
<integer>2</integer>
</dict>
</dict>
</dict>

View File

@ -28,6 +28,22 @@ TARGETTYPE=desktop
# TARGETTYPE=nda
# TARGETTYPE=xcmd
# Uncomment one of the following lines to force a particular GS emulator.
# By default, specific paths are tested for these emulators in the below
# order and the first found is what will be used. You can overrride that
# here:
#
# EMULATOR=gsplus
EMULATOR=mame
# EMULATOR=gsport
# By default, the build provides a single boot environment to test with.
# You can put other .2mg files into the make directory and override this
# variable to change what boot disk you want to use when you launch the
# emulator. The disk image you specify must be found in the make directory:
#
BOOTIMAGE=system601_net.2mg
# Add any other directories where you are putting C or assembly source
# files to this list:
SRCDIRS+=sprites $(GENDIR)
@ -65,7 +81,14 @@ LDFLAGS+=
# Uncomment the following line if you want to build against the GNO libraries
# export ORCA=$(ORCA_BINDIR)/gno
# If you want to copy one or more files or directories to the target disk
# Add any markdown files to the MD_SRCS variable to specify which files to
# convert to a Teach text file format during the build. Using this, you can
# write your documentation in markdown in your project and at build time,
# that documentation is converted to a format that is good to package with
# your software and can be opened on a GS.
MD_SRCS=Read.Me.md
# If you want to copy one or more files or directories to the distribution disk
# image, add the root directory to this variable. Any directories under
# the source directory which don't exist in the target disk image will be
# created. All files will be copied from the source to the target using
@ -73,23 +96,61 @@ LDFLAGS+=
#
# For example, if you set COPYDIRS to dir and in your project you have
# the following files:
# dir/Icons/myIconFile
# dir/newDir/anotherFile
# Then, during the copy phase, myIconFile will be copied into the Icons
# folder and a folder newDir will be created and anotherFile will be copied
# into there.
COPYDIRS=
# If you want to copy one or more files or directories to the boot disk
# image, add the root directory to this variable. Any directories under
# the source directory which don't exist in the boot disk image will be
# created. All files will be copied from the source to the target using
# the same path from the source.
#
# For example, if you set COPYBOOTDIRS to dir and in your project you have
# the following files:
# dir/System/mySystemFile
# dir/newDir/anotherFile
# Then, during the copy phase, mySystemFile will be copied into the System
# folder and a folder newDir will be created and anotherFile will be copied
# into there.
COPYDIRS=
COPYBOOTDIRS=
# By default, the build expects that you have GSplus in the path:
# /Applications/GSplus.app/Contents/MacOS/gsplus
# If you have it in a different location, specify that here.
# GSPLUS=/Applications/GSplus.app/Contents/MacOS/gsplus
# By default, the build uses no arguments with GSplus. If you would like to
# use different arguments, specify that here.
# GSPLUSARGS=
# By default, the build expects that you have GSport in the path:
# /Applications/GSport/GSport.app/Contents/MacOS/GSport
# If you have it in a different location, specify that here.
# GSPORT=/Applications/GSport/GSport.app/Contents/MacOS/GSport
# By default, the build uses no arguments with GSport. If you would like to
# use different arguments, specify that here.
# GSPORTARGS=
# By default, the build expects that you have Ample/mame in the path:
# /Applications/Ample.app/Contents/MacOS/mame64
# If you have it in a different location, specify that here.
# MAME=/Applications/Ample.app/Contents/MacOS/mame64
# By default, the build expects that you have the mame ROMs/libs in the path:
# $(HOME)/Library/Application Support/Ample
# If you have it in a different location, specify that here.
# MAMELIB=$(HOME)/Library/Application Support/Ample
# By default, the build uses these arguments with mame:
# apple2gs -skip_gameinfo -mouse -window -resolution 1408x1056 -ramsize 4M -sl7 cffa202
# If you would like to use different arguments, specify that here.
MAMEARGS=apple2gs -skip_gameinfo -mouse -window -resolution 1408x1056 -ramsize 4M -sl3 uthernet -sl7 scsi
# For a desktop application, it can operate in 640x200 or 320x200
# resolution. This setting is used to define which horizontal
# resolution you want to use for a desktop application. Other

123
BuGS/Read.Me.md Normal file
View File

@ -0,0 +1,123 @@
# BuGS:
This game represents my attempt to reproduce my favourite arcade game from when I was growing up. It should work on any Apple //gs, with or without an accelerator and should run at a fixed 60 (or 50) frames per second. Starting with v2.0 of BuGS, if you have Internet access on your GS using an Uthernet II card or similar, then you can see the top scores by the community and upload your own high scores into the database.
See below for information about game play. There are also a number of caveats related to emulators, networking, trackballs and sound cards which are described in subsequent sections. Finally, there is a section of acknowledgements and information on how to contact me.
## Game Play:
Use a mouse or an ADB trackball to control your player on the screen. You can move up, down, left and right at the bottom 1/4th or so of the screen. Click the mouse button to fire or hold the mouse button down to fire repeatedly. Only one shot can be on screen at a time so if you shoot something at the top of the screen, it will take longer for you to be able to shoot again. You cannot pass through a mushroom in the play area but you can shoot mushrooms to destroy them. Do not let any of the bugs in the game touch you or your turn will be over. You start with 3 lives and every 12,000 points you accumulate, an extra life is added. The goal is to destroy all of the centipede segments in a level and when you do, the game goes to the next level where there are more centipedes or faster centipedes.
At the beginning of the game, the field will be populated with a random set of mushrooms and a centipede will enter the screen from the top an dmove down row by row towards you. A centipede will drop down one row and turn around when it reaches the side of the play area or if there is a mushroom in its way. When you shoot a segment of the centipede, the segment will be turned into a new mushroom. If you shoot the head or the tail of the centipede, then it will be one segment shorter. If you shoot the centipede in the middle, it splits into two centipedes. If one or more centipede segments reach the bottom of the screen, they will travel up and then down row by row and single centipede segments will be added randomly on the left and the right of the play area. These randomly added segments make it harder to clear the level so try to get all of the centipede segments before they reach the bottom of the screen. Shooting a body segment awards you 10 points but shooting a head segment awards 100 points.
From the first level, a spider will appear randomly on the right edge of the play area and move towards the left or vice versa. The spider moves jerkily up, down or on diagonals in an unpredictable way. Best to stay behind a spider because they never backtrack. You get 300, 600 or 900 points for shooting a spider depending on how close they were to you when you shot it. At higher scores, the spider will restrict its movement to fewer rows at the bottom of the screen making it more likely to collide with you. You do not need to shoot spiders to clear levels and you can choose to just avoid them.
From the second level, a flea may appear and drop from the top of the screen down to the bottom, depositing mushrooms randomly behind it. A flea appears if the number of mushrooms in the bottom part of the screen drops below a particular threshold and they will continue to drop until enough mushrooms exist there. If you shoot a flea once, it accelerates and drops at you extra quickly. You must shoot them twice to destroy them. At higher scores, the flea is always fast but only takes one shot to destroy. Also at higher scores, the number of mushrooms required at the bottom of the screen goes up leading to more fleas. A flea adds 200 points to your score but as with spiders, you can choose to just avoid them.
From the third level, a scorpion may appear on the right side of the screen and move to the left or vice versa. The scorpion only ever travels across a single row on the screen and always on the upper part of the game play area so you never need to worry about colliding with a scorpion. But a scorpion poisons any mushroom it passes over. A poisoned mushroom changes colour and whenever a centipede touches a poisoned mushroom, it will just drop straight down towards the bottom of the screen, passing through mushrooms which may in the way. In order to "heal" the centipede, you must shoot the head segment. If you do not, it will reach the bottom of the screen and then move up row by row and down again like a normal centipede. Note that a poisoned centipede that reaches the bottom of the screen does not trigger the addition of random single centipede segments. It is not a bad idea to try to destroy poisoned mushrooms. Shooting a scorpion awares 1000 points. At higher scores, the scorpion will sometimes cross the screen twice as quickly making it harder to hit.
## Emulators:
Unfortunately, not all GS emulators work well with BuGS. I recommend mame or KeGS based on my testing. Some notes about specific emulators are below:
**mame:** This emulator is not very friendly but is probably the best for playing BuGS. The mouse is handled well and the sound and graphics look good. If you are on a Mac, I highly recommend Kelvin Sherlock's Ample which makes running mame much easier. There was a problem with versions 0.231 and 0.232 of mame when playing BuGS. I recommend using a newer or older version of mame. Also, you can emulate an Uthernet card in mame and it is possible to get the network up and share your scores. You can find Ample at - http://github.com/ksherlock/ample
**KeGS:** Kent has released some updates for this emulator recently and v1.05 seems to work well with BuGS. You will want to press F8 to constrain the mouse to the emulator. You can find the latest version at kegs.sourceforge.net. I don't think there is any support for networking in KeGS today however.
**GSPlus:** This emulator is based on KeGS and I generally find it to be a good emulator but it does not work well with BuGS. The main problem is related to mouse handling. GSPlus tries to map the host's mouse position to the corresponding position on the emulator's screen. When they do this, the emulator often stops updating the mouse position when it reaches the side of the emulated screen. With a game like BuGS (or Arkanoid for a similar example), the mouse is used as an analog input to the game. So when the mouse reaches a screen edge, you will no longer be able to move the player in that direction. Other than this mouse input problem, everything else seems to work but I suspect you will find the game frustrating because of these "virtual barriers" due to mouse input.
**GSPort:** This is another KeGS based emulator. I can no longer test with this emulator on my Mac so I don't know how well BuGS works with GSPort. My guess is that the sound and graphics will probably be good but it may suffer from the same mouse input problem as GSPlus. Feedback welcome.
**Sweet16:** This is another emulator I cannot test any longer on my Mac. But I do have feedback from others who say it suffers from the same kind of mouse input problems as GSPlus. Worse, there are graphics problems related to handling vertical blanking. BuGS will not work well under Sweet16 unfortunately.
## Network Support:
With v2.0 of BuGS, network support has been added to the game. When the game is launched on a machine that has a network connection, a request is made to get the top 10 high scores from the online database. These are shown as "Global High Scores" in the game. And when a game is over, if your score gets onto your own local high score list, then it is also uploaded to the server. Even if you do not have a GS with a network connection, you can see the global high scores in a web browser here:
http://scores.rand-emonium.com/
If you have the necessary hardware to support a network connection on your GS, then make sure you have the latest version of Marinetti installed as available from here:
http://www.apple2.org/marinetti/
My testing was done with Marinetti v3.0b11. Also, make sure you have the correct link layer installed for your network connection. If you have network applications like Webber working, then networking should work with BuGS also.
Make sure that Tool128, the hash tool, is installed on your machine. Hashing is used to provide a bit of security to the online score database. This tool is distributed with BuGS. Drag it to your System folder and the Finder should offer to put it in the right place for you. You do *not* need to install Tool128 if you plan to play the game offline but installing it shouldn't cause a problem. But if you do not have both Marinetti and Tool128 installed when you launch BuGS, then no attempt will be made to make network requests from the game. If you do not see the global high score list, then that likely indicates that something isn't configured correctly.
If something goes wrong with the network connection, you may see a network error instead of the global high score list. Feel free to contact me if you are seeing errors and I will try to help find the problem.
Finally, the only scores that will be posted in the online database are those that get onto your local high score list. If you are upgrading to v2.0 of BuGS and already have 10 high scores that you cannot easily beat, then you may find the game doesn't send any of your new scores to the server. If you want to reset your local high score list to make it easier to submit high scores to the server, delete the BuGS.settings file. This will clear your local high score list and your next ten games will be guaranteed to make your local high score list and therefore get posted to the online database.
## Trackballs:
The original arcade game is played using a trackball so for the best experience, I would recommend using an ADB trackball if you have one. Below is a list of trackballs for which I have some feedback saying they work:
**Kensington Turbo Mouse Version 5.0 (Model Number 64210):** This is the trackball I am using on my GS and it works great for me.
**Kensington Turbo Mouse Version 3.0:** This trackball is known to work well with the GS and should work with BuGS but I have no direct feedback at this time.
**Logitech TrackMan Mouse (Model Number T-AA1-4MD):** This trackball was tested during WozFest in March 2021 and the feedback is that it worked with BuGS.
Some ADB trackballs will not work. The root problem seems to be whether it behaves like a regular ADB mouse or whether it might need some custom driver. Custom drivers were only written for the Mac for these trackballs so they probably don't work well with the GS, nor with BuGS. This is a list of the trackballs for which I have some feedback saying they do not work:
**Kensington Turbo Mouse Version 4.0:** Apparently Version 4.0 of the Turbo Mouse required a driver on the Mac and due to that, it is not likely to work well for BuGS. It has been tested and it did not work well.
I don't have any more recommendations for trackballs. If you have a trackball that does or does not work, let me know and we can continue to build a list of which are good and which to avoid.
## Sound Cards:
In BuGS, the sound effects are in stereo if you have a stereo sound card in your GS. When you shoot from the left side, the sound should come from the left speaker. When a spider enters the screen on the right side, the sound of the spider should come from the right speaker and then pan to the left as the spider travels across the screen.
However, it seems that there is some inconsistency between what channel maps to the left or right speaker. The documentation from Apple says one thing but emulators and recent sound cards like the 2Soniq and 4Soniq do the opposite. It may be that some sound cards follow Apple's documentation while other do not. By default, BuGS has adopted the "standard" that emulators have set. But if you find that the sound seems to be backwards, hit the "S" key from the main screen on BuGS to swap the stereo channels. Press "S" again to swap them back. This option will be saved so you shouldn't need to swap the stereo channels again the next time you play.
## Updates:
I do hope to improve BuGS so watch for updates. One feature I would like to implement is a global high score list. For people with Internet connectivity on their GS's, their high scores would be shared to a server and we could all compare our best scores amongst ourselves.
In order to make updating the game easy, I have adoped Speccie's "Versions". If you have the "Check File PIF" installed, you can select BuGS in the Finder, then select Extras -> Check File... and it will tell you whether there has been an update.
## Acknowledgements:
This project would not have been a success without help from the Apple // community, especially the appleiigs slack channel where I got tons of help and encouragement. I have to thank some people for their support, help and contributions of code and other materials that helped make BuGS possible. Please know that if your name is missing, that is my fault. I should have been keeping track of this early on but I find myself writing this in the later days of the project and I am almost certainly going to make a mistake. I apologize if I have left you off and I would encourage you to contact me and help me correct my mistake.
* @fatdog on slack who provided multiple sound samples for the game including the spider, flea and the beat sound. As well, he provided the wonderful icon for the game.
* Mark Collins who pointed me to a source of sound effects and information on how to convert digital sound into a format that would work on the GS.
* John Brooks who was instrumental in getting the animation algorithm performant. He described a series of optimizations that I could persue and sure enough, implementing a couple of them got me back to 60 frames per second. He also provided code for a fast random number generator and fast mouse polling.
* Antoine Vignau for his technical support, especially when it came time to look at sound. He often posted comments that warned me that the road I was going down was going to be a problem (like trying to use the toolbox for polling the mouse) and of course he was right.
* Brutal Deluxe in general for their incredible reference information about the Apple //gs, especially their page about Mr. Sprite. I very nearly used Mr. Sprite for this project but there were further assumptions and optimizations I could do because of the limited colour palette so I decided to hand code my sprites. But their ideas were key to my approach and I read those web pages multiple times.
* Dagen Brock for GSPlus and for his technical support and encouragement on slack. GSPlus was the emulator I used primarily and when I did a "build and run", GSPlus always launched reliably and quickly so I could test the latest code.
* Andrew Roughan for his work testing the beta builds and helping to make the release as good as possible.
* Mike Westerfield and The Byte Works for the ORCA tools which I have used for building this project.
* Kelvin Sherlock for GoldenGate and ProFuse which are fundamental tools I used for building this project.
* Ken Gagne and Juiced.GS for making tools like ORCA and GoldenGate available.
* Stephen Heumann for NetDisk which I used to transfer my latest build to my real GS for testing.
* Ewen Wannop (Speccie) for his great network tools, especially Versions which I am using to assist in pushing out fixes and new versions of BuGS. I had a problem with the Check File Finder extension but very quickly he found the problem. I very much appreciate his support getting that problem fixed.
* Stefan Wessels for his extensive testing leading up to v2.0 of BuGS. He identified a problem which could lead to waves of scorpions appearing over and over again due to the behaviour of the random number generator and also found a problem with timeouts not working when trying to upload a score. At the time of updating this document, he is trying very hard to beat my high score online and is very close to doing so (his scores are posted as SQW). Thanks so much for your help and enthusiasm and I am sure you will beat my score soon.
## Contact:
If you are having problems or have some feedback about the game, you can reach me at:
jeremy@rand-family.com
My Apple // projects are all available at:
http://rand-emonium.com/
My website also seems to work in Speccie's incredible "Webber" web browser for the GS so you can browse and download my projects there.
The latest high scores in BuGS posted to the online database can be found here:
http://scores.rand-emonium.com/
If you want to see how BuGS works, check out:
http://github.com/jeremysrand/BuGS
All of the source code is there. And you can find more of my Apple // projects here:
http://github.com/jeremysrand/
I hope you enjoy BuGS.
# Apple // Forever!

View File

@ -10,8 +10,22 @@
#define _GUARD_PROJECTBuGS_FILEgame_
#include "tileData.h"
// These are globals used from assembly.
extern char globalScoreInfo[GAME_NUM_TILES_WIDE + 1];
// These are assembly functions called from C.
extern void game(void);
extern void randInit(void);
extern void waitForVbl(void);
extern void uploadSpin1(void);
extern void uploadSpin2(void);
extern void uploadSpin3(void);
extern void displayConnectionString(void);
extern void displayScorePosition(void);
extern void swapStereoChannels(void);
#endif /* define _GUARD_PROJECTBuGS_FILEgame_ */

File diff suppressed because it is too large Load Diff

View File

@ -96,6 +96,11 @@ updateScorpion_playerOnscreen anop
blt updateScorpion_doNotAdd
jsl rand0_to_65534
and #$3ff
; This used to just do an AND and add a scorpion when zero. But because of the nature of the
; random number generator, if you get 0 once after the mask, chances are good you will get 0
; again on the next call to the random number generator. Better to just look for some non-zero
; value. I picked $20 which has a nice distribution when I checked the random number generator.
cmp #$0020
bne updateScorpion_doNotAdd
jmp addScorpion
updateScorpion_doNotAdd anop
@ -218,7 +223,6 @@ updateScorpion_explosionDone anop
setScorpionSpeed entry
; TODO - Call this code with each level to set the scorpion speed
cmp #SPRITE_SPEED_FAST
beq setScorpionSpeed_fast

View File

@ -1507,8 +1507,8 @@ addBodySegment_fast anop
; This is a bit weird but we pretend the body segment is in the bottom RHS tile while it is offscreen.
; We have to put in a valid game tile here and it doesn't matter if body segments "collide". But we
; don't want head segments to collide with a off-screen body segment. Since the head segments always
; appear at the top, it is safe to just tuck the body segments away at the bottom RHS.
lda #NUM_GAME_TILES-1
; appear at the top, it is safe to just tuck the body segments away at the top RHS.
lda #RHS_FIRST_TILE_OFFSET
sta segmentCurrentTile-6,y
sta segmentCurrentTile-4,y
sta segmentCurrentTile-2,y
@ -1606,7 +1606,7 @@ addBodySegment_slow anop
sta segmentTileOffsetsLR,y
sta segmentTileOffsetsLR,y
; See the comment above why we pretend the body segment is at the bottom RHS tile.
; See the comment above why we pretend the body segment is at the top RHS tile.
lda #RHS_FIRST_TILE_OFFSET
sta segmentCurrentTile-14,y
sta segmentCurrentTile-12,y

View File

@ -33,8 +33,11 @@ SOUND_ONE_SHOT_MODE equ 2
; $10 should be the left. But based on GSPlus and mame, I see the opposite. Still need
; to check on my HW. But in the end, it could be that some HW demultiplexed them backwards
; so there could be either out there in the wild.
SOUND_RIGHT_SPEAKER equ $10
SOUND_LEFT_SPEAKER equ $00
;
; Now I am changing my mind about this. mame has been fixed. I think the 4soniq and GSplus
; are the only ones which are wrong. So, going with the Apple standard here.
SOUND_RIGHT_SPEAKER equ $00
SOUND_LEFT_SPEAKER equ $10
; OSC 16 - 19 for L/R channels in swap mode
SPIDER_SOUND_ADDR equ $0000

View File

@ -8,7 +8,6 @@
use strict;
use integer;
# main
@ -86,6 +85,13 @@ $gEquates{"NUM_FLEA_FREQS"} = 120;
$gEquates{"NUM_HIGH_SCORES"} = 10;
our %gNetworkDefines;
$gNetworkDefines{'NETWORK_SERVER'} = "NULL";
$gNetworkDefines{'NETWORK_SERVERPORT'} = 0;
$gNetworkDefines{'NETWORK_SERVERSECRET1'} = 0;
$gNetworkDefines{'NETWORK_SERVERSECRET2'} = 0;
our @gTileDirty = ("TILE_STATE_CLEAN") x $gEquates{"TOTAL_NUM_TILES"};
our @gTileScreenOffset = (0) x $gEquates{"TOTAL_NUM_TILES"};
our @gTileType = ("TILE_EMPTY") x $gEquates{"TOTAL_NUM_TILES"};
@ -109,6 +115,18 @@ our @gMouseXTileRight = (0) x $gEquates{"MOUSE_MAX_X"};
our @gScreenToTileOffset = (0) x ($gEquates{"SCREEN_PIXELS_TALL"} * $gEquates{"SCREEN_BYTES_PER_ROW"} / $gEquates{"SIZEOF_TILE_INFO"});
sub loadNetworkSettings
{
open my $fh, "<", $ENV{'HOME'} . "/.BuGS_scores.json" or return;
while (<$fh>)
{
if (/^\s+\"(Server\w*)\"\s+:\s+([-\"a-zA-Z0-9.]+),\s*$/) {
$gNetworkDefines{'NETWORK_' . uc($1)} = $2;
}
}
}
sub printTileData
{
my ($fh, $symbol, @data) = @_;
@ -505,6 +523,7 @@ sub initFleaFreqs
}
loadNetworkSettings();
initTiles();
initNonGameTiles();
initFleaFreqs();
@ -612,6 +631,11 @@ foreach my $equate (sort keys %gEquates)
print $fh "#define $equate " . $gEquates{$equate} . "\n";
}
foreach my $equate (sort keys %gNetworkDefines)
{
print $fh "#define $equate " . $gNetworkDefines{$equate} . "\n";
}
$text = << "EOF";

View File

@ -23,12 +23,14 @@ _drawDirtyGameRow_wait&rowNum anop
; offset numbers.
;
; Also according to that technote, it looks like these numbers are different
; in PAL mode. TODO - Do I need something here to handle PAL correctly? I
; switched my GS to 50Hz mode (startup/reboot with option held down to get the
; menu) and I didnt't detect any graphics glitches at all. I did notice that
; the game is noticably easier because things run a bit slower. If I do an
; online score system, I should record whether a score was gotten at 50Hz vs
; 60Hz.
; in PAL mode. Do I need something here to handle PAL correctly? I switched
; my GS to 50Hz mode (startup/reboot with option held down to get the menu)
; and I didnt't detect any graphics glitches at all. I did notice that the
; game is noticably easier because things run a bit slower. If I do an online
; score system, I should record whether a score was gotten at 50Hz vs 60Hz.
; (and in fact, I have since implemented online high scores and record the
; frequency at which the game was played)
lda >VERTICAL_COUNTER ; load the counter value
and #$80ff ; mask out the VBL bits
asl a ; shift the word around
@ -137,14 +139,14 @@ _dirtyNonGameTile_skip&SYSCNT anop
macro
_overwriteGameTile &type
lda #&type
jsr overwriteGameTile
jsl overwriteGameTile
mend
macro
_setGameTile &type
lda #&type
jsr setGameTile
jsl setGameTile
mend
@ -153,70 +155,142 @@ _dirtyNonGameTile_skip&SYSCNT anop
ldy #SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+&nthScore*SETTINGS_HIGH_SCORE_SIZE
lda settings,y
jsl asciiToTileType
jsr setGameTile
jsl setGameTile
iny
lda settings,y
jsl asciiToTileType
jsr setGameTile
jsl setGameTile
iny
lda settings,y
jsl asciiToTileType
jsr setGameTile
jsl setGameTile
iny
lda settings,y
jsl asciiToTileType
jsr setGameTile
jsl setGameTile
iny
lda settings,y
jsl asciiToTileType
jsr setGameTile
jsl setGameTile
iny
lda settings,y
jsl asciiToTileType
jsr setGameTile
jsl setGameTile
iny
lda settings,y
jsl asciiToTileType
jsr setGameTile
jsl setGameTile
iny
lda settings,y
jsl asciiToTileType
jsr setGameTile
jsl setGameTile
iny
lda settings,y
jsl asciiToTileType
jsr setGameTile
jsl setGameTile
iny
lda settings,y
jsl asciiToTileType
jsr setGameTile
jsl setGameTile
lda #TILE_EMPTY
jsr setGameTile
jsl setGameTile
iny
lda settings,y
jsl asciiToTileType
jsr setGameTile
jsl setGameTile
iny
lda settings,y
jsl asciiToTileType
jsr setGameTile
jsl setGameTile
iny
lda settings,y
jsl asciiToTileType
jsr setGameTile
jsl setGameTile
mend
macro
_globalHighScoreRow &nthScore
ldy #&nthScore*SETTINGS_HIGH_SCORE_SIZE
lda globalHighScores,y
jsl asciiToTileType
jsl setGameTile
iny
lda globalHighScores,y
jsl asciiToTileType
jsl setGameTile
iny
lda globalHighScores,y
jsl asciiToTileType
jsl setGameTile
iny
lda globalHighScores,y
jsl asciiToTileType
jsl setGameTile
iny
lda globalHighScores,y
jsl asciiToTileType
jsl setGameTile
iny
lda globalHighScores,y
jsl asciiToTileType
jsl setGameTile
iny
lda globalHighScores,y
jsl asciiToTileType
jsl setGameTile
iny
lda globalHighScores,y
jsl asciiToTileType
jsl setGameTile
iny
lda globalHighScores,y
jsl asciiToTileType
jsl setGameTile
iny
lda globalHighScores,y
jsl asciiToTileType
jsl setGameTile
lda #TILE_EMPTY
jsl setGameTile
iny
lda globalHighScores,y
jsl asciiToTileType
jsl setGameTile
iny
lda globalHighScores,y
jsl asciiToTileType
jsl setGameTile
iny
lda globalHighScores,y
jsl asciiToTileType
jsl setGameTile
mend

View File

@ -193,6 +193,8 @@ backupStack dc i2'0'
collision dc i2'0'
collisionAddr dc i2'0'
numSegments dc i2'0'
displayGlobalHighScores dc i2'0'
shouldQuit dc i2'1'
tileJumpTable dc a4'solid0'

95
BuGS/loadSounds.c Normal file
View File

@ -0,0 +1,95 @@
/*
* loadSounds.c
* BuGS
*
* Created by Jeremy Rand on 2021-06-03.
* Copyright © 2021 Jeremy Rand. All rights reserved.
*/
#include <memory.h>
#include <resources.h>
#include <sound.h>
#include "loadSounds.h"
#include "main.h"
#if __ORCAC__
segment "loadSounds";
#endif
static void loadSound(Word addr, Word soundNum)
{
Handle handle = LoadResource(rRawSound, soundNum);
HLock(handle);
WriteRamBlock(*handle, addr, (Word)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);
}

29
BuGS/loadSounds.h Normal file
View File

@ -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 <TYPES.h>
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_ */

View File

@ -7,84 +7,52 @@
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <gsos.h>
#include <orca.h>
#include <Memory.h>
#include <Locator.h>
#include <MiscTool.h>
#include <Resources.h>
#include <Sound.h>
#include "main.h"
#include "game.h"
#include "netScores.h"
#include "settings.h"
#include "tileData.h"
/* Defines and macros */
#define GLOBAL_SCORE_REFRESH_TIME (15 * 60 * 60)
#define SHUTDOWN_NETWORK_TIMEOUT (2 * 60)
#define TCP_CONNECT_TIMEOUT (8 * 60)
#define READ_NETWORK_TIMEOUT (5 * 60)
#define NETWORK_RETRY_TIMEOUT (3 * 60 * 60)
#define TOOLFAIL(string) \
if (toolerror()) SysFailMgr(toolerror(), "\p" string "\n\r Error Code -> $");
#define SETTINGS_FILENAME "@:BuGS.settings"
/* Typedefs */
typedef struct tHighScore
{
char scoreText[10];
char who[4];
unsigned long score;
} tHighScore;
typedef struct tSettingsData
{
char magic[4];
int version;
Boolean swapStereo;
tHighScore highScores[NUM_HIGH_SCORES];
} tSettingsData;
if (toolerror()) SysFailMgr(toolerror(), (Pointer)"\p" string "\n\r Error Code -> $");
/* Globals */
unsigned int userid;
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;
// This symbol is used also from assembly directly so be careful with it...
char globalScoreInfo[GAME_NUM_TILES_WIDE + 1];
tNSGSHighScores globalHighScores;
Boolean hasGlobalHighScores = FALSE;
Word globalScoreAge = 0;
unsigned int scoreIndex = 0;
/* 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
@ -93,255 +61,174 @@ word randomMushroomOffset(void)
}
void loadSpiderSound(word addr)
static void uploadSpin(int val)
{
Handle handle = LoadResource(rRawSound, SPIDER_SOUND);
HLock(handle);
WriteRamBlock(*handle, addr, GetHandleSize(handle));
HUnlock(handle);
}
void loadDeathSound(word addr)
{
Handle handle = LoadResource(rRawSound, DEATH_SOUND);
HLock(handle);
WriteRamBlock(*handle, addr, GetHandleSize(handle));
HUnlock(handle);
}
void loadSegmentsSound(word addr)
{
Handle handle = LoadResource(rRawSound, SEGMENTS_SOUND);
HLock(handle);
WriteRamBlock(*handle, addr, GetHandleSize(handle));
HUnlock(handle);
}
void loadBonusSound(word addr)
{
Handle handle = LoadResource(rRawSound, BONUS_SOUND);
HLock(handle);
WriteRamBlock(*handle, addr, GetHandleSize(handle));
HUnlock(handle);
}
void loadKillSound(word addr)
{
Handle handle = LoadResource(rRawSound, KILL_SOUND);
HLock(handle);
WriteRamBlock(*handle, addr, GetHandleSize(handle));
HUnlock(handle);
}
void loadFireSound(word addr)
{
Handle handle = LoadResource(rRawSound, FIRE_SOUND);
HLock(handle);
WriteRamBlock(*handle, addr, GetHandleSize(handle));
HUnlock(handle);
}
void loadExtraLifeSound(word addr)
{
Handle handle = LoadResource(rRawSound, EXTRA_LIFE_SOUND);
HLock(handle);
WriteRamBlock(*handle, addr, GetHandleSize(handle));
HUnlock(handle);
}
void loadFleaSound(word addr)
{
Handle handle = LoadResource(rRawSound, FLEA_SOUND);
HLock(handle);
WriteRamBlock(*handle, addr, GetHandleSize(handle));
HUnlock(handle);
}
void loadScorpionSound(word addr)
{
Handle handle = LoadResource(rRawSound, SCORPION_SOUND);
HLock(handle);
WriteRamBlock(*handle, addr, GetHandleSize(handle));
HUnlock(handle);
}
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)
switch (val)
{
GSString255Ptr filenamePtr;
filenameHandle = NewHandle(sizeof(GSString255), userid, 0x8000, NULL);
HLock(filenameHandle);
filenamePtr = (GSString255Ptr)(*filenameHandle);
filenamePtr->length = strlen(SETTINGS_FILENAME);
strcpy(filenamePtr->text, SETTINGS_FILENAME);
HUnlock(filenameHandle);
case 0:
uploadSpin1();
break;
case 1:
uploadSpin2();
break;
case 2:
uploadSpin3();
break;
case 3:
uploadSpin2();
break;
}
}
void deleteSettings(void)
static void scorePosition(unsigned int position, unsigned int numberOfScores)
{
allocateFilenameHandle();
HLock(filenameHandle);
int i;
destroyRec.pCount = 1;
destroyRec.pathname = (GSString255Ptr)(*filenameHandle);
DestroyGS(&destroyRec);
sprintf(globalScoreInfo, " %u OF %u SCORES", position, numberOfScores);
for (i = (int)strlen(globalScoreInfo); i < sizeof(globalScoreInfo); i++) {
globalScoreInfo[i] = ' ';
}
globalScoreInfo[GAME_NUM_TILES_WIDE] = '\0';
displayScorePosition();
HUnlock(filenameHandle);
for (i = 6 * 60; i > 0; i--) {
waitForVbl();
}
globalScoreAge = 0;
}
void saveSettings(void)
static void showConnectionString(BOOLEAN display)
{
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();
if (display)
displayConnectionString();
}
BOOLEAN loadSettings(void)
static char hexDigitToAscii(Word digit)
{
BOOLEAN success = FALSE;
digit &= 0xf;
if (digit < 10)
return '0' + digit;
allocateFilenameHandle();
HLock(filenameHandle);
if (digit > 15)
return 'X';
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);
return 'A' + digit - 10;
}
static void displayString(Word row, char * string)
{
strcpy(&(globalHighScores.score[row].scoreText[2]), string);
}
static void displayNetworkError(char * line1, char * line2, unsigned int errorCode)
{
Word row;
Word column;
for (row = 0; row < sizeof(globalHighScores.score) / sizeof(globalHighScores.score[0]); row++) {
for (column = 0;
column < sizeof(globalHighScores.score[0].scoreText) / sizeof(globalHighScores.score[0].scoreText[0]);
column++) {
globalHighScores.score[row].scoreText[column] = ' ';
}
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();
for (column = 0;
column < sizeof(globalHighScores.score[0].who) / sizeof(globalHighScores.score[0].who[0]);
column++) {
globalHighScores.score[row].who[column] = ' ';
}
}
return success;
displayString(1, "NETWORK");
displayString(2, "PROBLEM");
displayString(4, line1);
displayString(5, line2);
globalHighScores.score[7].scoreText[0] = 'C';
globalHighScores.score[7].scoreText[1] = 'O';
globalHighScores.score[7].scoreText[2] = 'D';
globalHighScores.score[7].scoreText[3] = 'E';
globalHighScores.score[7].scoreText[4] = ':';
globalHighScores.score[7].scoreText[5] = ' ';
globalHighScores.score[7].scoreText[6] = hexDigitToAscii(errorCode >> 12);
globalHighScores.score[7].scoreText[7] = hexDigitToAscii(errorCode >> 8);
globalHighScores.score[7].scoreText[8] = hexDigitToAscii(errorCode >> 4);
globalHighScores.score[7].scoreText[9] = hexDigitToAscii(errorCode);
hasGlobalHighScores = TRUE;
globalScoreAge = 0;
}
static void displayError(tNSGSErrorType errorType, unsigned int errorCode)
{
switch (errorType) {
case NSGS_CONNECT_ERROR:
displayNetworkError("CONNECT", "FAILED", errorCode);
break;
case NSGS_LOOKUP_ERROR:
displayNetworkError("LOOKUP", "FAILED", errorCode);
break;
case NSGS_SOCKET_ERROR:
displayNetworkError("SOCKET", "ERROR", errorCode);
break;
case NSGS_PROTOCOL_ERROR:
displayNetworkError("PROTOCOL", "FAILED", errorCode);
break;
}
}
void swapStereoSettings(void)
static void setHighScores(const tNSGSHighScores * highScores)
{
swapStereoChannels();
settings.swapStereo = !settings.swapStereo;
saveSettings();
memcpy(&globalHighScores, highScores, sizeof(globalHighScores));
hasGlobalHighScores = TRUE;
globalScoreAge = GLOBAL_SCORE_REFRESH_TIME;
}
static BOOLEAN shouldRefreshHighScores(void)
{
return globalScoreAge == 0;
}
BOOLEAN sendHighScore(void)
{
const tNSGSHighScore * highScore = getHighScore(scoreIndex / sizeof(tNSGSHighScore));
return NSGS_SendHighScore(highScore->who, highScore->score);
}
int main(void)
{
static tNSGSHighScoreInitParams highScoreInitParams;
int event;
Ref toolStartupRef;
userid = MMStartUp();
myUserId = MMStartUp();
TOOLFAIL("Unable to start memory manager");
TLStartUp();
TOOLFAIL("Unable to start tool locator");
toolStartupRef = StartUpTools(userid, refIsResource, TOOL_STARTUP);
toolStartupRef = StartUpTools(myUserId, refIsResource, TOOL_STARTUP);
TOOLFAIL("Unable to start tools");
CompactMem();
/* Allocate $1000 extra before the SHR screen so I can write sprites above the start of the
screen without overwriting memory I do not own. */
NewHandle(0x9000, userid, attrLocked | attrFixed | attrAddr | attrBank, (Pointer)0x011000);
NewHandle(0x9000, myUserId, attrLocked | attrFixed | attrAddr | attrBank, (Pointer)0x011000);
TOOLFAIL("Unable to allocate SHR screen");
randomSeed = (int)time(NULL);
@ -353,17 +240,38 @@ int main(void)
InitMouse(0);
SetMouse(transparent);
highScoreInitParams.userId = myUserId;
highScoreInitParams.scoreServer = (const char *)"\p" NETWORK_SERVER;
highScoreInitParams.scorePort = NETWORK_SERVERPORT;
highScoreInitParams.secret1 = NETWORK_SERVERSECRET1;
highScoreInitParams.secret2 = NETWORK_SERVERSECRET2;
highScoreInitParams.shutdownTimeout = SHUTDOWN_NETWORK_TIMEOUT;
highScoreInitParams.connectTimeout = TCP_CONNECT_TIMEOUT;
highScoreInitParams.readTimeout = READ_NETWORK_TIMEOUT;
highScoreInitParams.retryTimeout = NETWORK_RETRY_TIMEOUT;
highScoreInitParams.displayConnectionString = showConnectionString;
highScoreInitParams.waitForVbl = waitForVbl;
highScoreInitParams.uploadSpin = uploadSpin;
highScoreInitParams.scorePosition = scorePosition;
highScoreInitParams.displayError = displayError;
highScoreInitParams.setHighScores = setHighScores;
NSGS_InitNetwork(&highScoreInitParams);
if (!loadSettings())
saveSettings();
game();
NSGS_ShutdownNetwork();
ShutDownTools(refIsHandle, toolStartupRef);
TOOLFAIL("Unable to shutdown tools");
TLShutDown();
TOOLFAIL("Unable to shutdown tool locator");
MMShutDown(userid);
MMShutDown(myUserId);
TOOLFAIL("Unable to shutdown memory manager");
}

View File

@ -40,7 +40,7 @@ resource rPString (DOWNLOAD_URL_STR, $0000) {
resource rPString (ARCHIVE_STR, $0000) {
"BuGS100.shk"
"BuGS201.shk"
};
@ -61,9 +61,9 @@ resource rUpdateInfo (UPDATE_INFO, $0000) {
/* Version resource */
resource rVersion (1) {
{
1, /* Major version number in BCD */
2, /* Major version number in BCD */
0, /* Minor version number in BCD */
0, /* Bug version number in BCD */
1, /* Bug version number in BCD */
release, /* Development phase */
0 /* Release number */
},

View File

@ -1,21 +1,22 @@
# GSplus configuration file version 0.14
# GSport configuration file version 0.31
s5d1 = /Users/jrand/Library/Developer/Xcode/DerivedData/BuGS-bffpexoblaghkzcbtjtzxeulnuto/Build/Products/Debug/BuGS.2mg
s5d1 =
s5d2 =
s6d1 =
s6d2 =
s7d1 = /Users/jrand/Coding/AppleCoding/Apple2GS/BuGS/BuGS/make/system601.2mg
s7d1 =
g_limit_speed = 3
g_joystick_type = 0
g_limit_speed = 0
bram1[00] = 00 00 00 01 00 00 0d 06 02 01 01 00 01 00 00 00
bram1[10] = 00 00 07 06 02 01 01 00 00 00 0f 06 06 00 05 06
bram1[20] = 01 00 00 00 00 00 00 01 00 00 00 00 03 02 02 02
bram1[30] = 00 00 00 01 00 00 00 00 00 00 01 02 03 04 05 06
bram1[40] = 07 00 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d
bram1[30] = 00 00 00 00 00 00 00 00 08 00 01 02 03 04 05 06
bram1[40] = 07 0a 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d
bram1[50] = 0e 0f ff ff ff ff ff ff ff 00 ff ff ff ff ff 81
bram1[60] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
bram1[70] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
@ -26,7 +27,7 @@ bram1[b0] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
bram1[c0] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
bram1[d0] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
bram1[e0] = ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
bram1[f0] = ff ff ff ff ff ff ff ff ff ff ff ff 41 f8 eb 52
bram1[f0] = ff ff ff ff ff ff ff ff ff ff ff ff 52 06 f8 ac
bram3[00] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
bram3[10] = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

View File

@ -2,136 +2,247 @@
if [ $# -lt 3 ]
then
echo USAGE: $0 diskimage file directory
echo USAGE: $0 diskimage bootimage file [bootdest]
exit 1
fi
DISKIMAGE="$1"
shift
DESTBOOTIMAGE="$1"
shift
FILE="$1"
shift
DISKIMAGEDEST="$1"
shift
COPYDIRS=$*
BOOTCOPYPATH="$1"
PROGRAM=`basename "$FILE"`
TMPDIR=/tmp/a2gs_mount.$$
MOUNTDIR="${TMPDIR}/$PROGRAM"
TMPDISKIMAGE=/tmp/a2gs_diskimage_$$.2mg
TMPBOOTIMAGE=/tmp/a2gs_bootimage_$$.2mg
TMPARCHIVE=/tmp/s2gs_archive_$$.shk
TEMPLATEDISKIMAGE="make/${PROGRAM}.2mg"
TEMPLATEDISKIMAGE="make/empty.2mg"
TEMPLATEBOOTIMAGE="make/$BOOTIMAGE"
ARCHIVE=`dirname "$DISKIMAGE"`/"${PROGRAM}.shk"
DEST="${MOUNTDIR}/${DISKIMAGEDEST}"
cleanupAndExit()
# It looks like on Linux, the cp command needs a special argument to preserve the resource fork. This isn't ideal
# but for now, if uname is Darwin, then this is MacOS and we don't need any cp args. If not Darwin, then assume
# this is Linux and ask for extended attributes to be preserved through the copy.
#
# Ultimately, it could be that other platforms (BSD, does Solaris still exist?) or even other Linux versions or
# distributions need different arguments for this rather special thing. If true, this may need to be a build time
# option which can be set.
if [ "`uname`" = Darwin ]
then
CPARGS=""
else
CPARGS="--preserve=xattr"
fi
printErrorAndExit()
{
echo "`pwd`/Makefile:0:0: error: $*" >&2
umount "$MOUNTDIR" 2> /dev/null
rm -f "$TMPDISKIMAGE" 2> /dev/null
rm -f "$TMPBOOTIMAGE" 2> /dev/null
rm -f "$TMPARCHIVE" 2> /dev/null
rm -f "$DISKIMAGE" 2> /dev/null
rmdir "$MOUNTDIR" 2> /dev/null
rm -f "$DESTBOOTIMAGE" 2> /dev/null
rm -rf "$TMPDIR" 2> /dev/null
exit 1
}
unmount()
{
RETRIES=0
while [ $RETRIES -lt 5 ]
do
umount "$1"
if [ $? -eq 0 ]
then
break
fi
RETRIES=`expr $RETRIES + 1`
sleep 1
done
if [ $RETRIES -ge 5 ]
then
printErrorAndExit "Unable to unmount the disk image."
fi
}
validateProDOSName()
{
NAME=`basename $1`
echo $NAME | egrep '^[a-zA-Z][a-zA-Z0-9.]{0,14}$' > /dev/null
}
mkdirProDOS()
{
validateProDOSName "$1"
if [ $? -ne 0 ]
then
printErrorAndExit "Invalid ProDOS name of directory `basename $1`. ProDOS names must be 1 to 15 characters, start with a letter and only letters, numbers and a period can be used in the name."
fi
mkdir -p "$1"
if [ $? -ne 0 ]
then
printErrorAndExit "Unable to create directory $1"
fi
}
cpProDOS()
{
validateProDOSName "$2"
if [ $? -ne 0 ]
then
printErrorAndExit "Invalid ProDOS name of file `basename $2`. ProDOS names must be 1 to 15 characters, start with a letter and only letters, numbers and a period can be used in the name."
fi
cp $CPARGS "$1" "$2"
if [ $? -ne 0 ]
then
printErrorAndExit "Unable to create directory $1"
fi
}
copyDirs()
{
OLDDIR=`pwd`
for COPYDIR in $*
do
cd "$COPYDIR"
if [ $? != 0 ]
then
printErrorAndExit "Unable to find $COPYDIR"
fi
find . -print | while read FILEORDIR
do
if [ "$FILEORDIR" = "." ]
then
continue
fi
if [ -d "$FILEORDIR" ]
then
mkdirProDOS "${MOUNTDIR}/$FILEORDIR"
elif [ -f "$FILEORDIR" ]
then
cpProDOS "$FILEORDIR" "${MOUNTDIR}/$FILEORDIR"
fi
done
cd "$OLDDIR"
done
}
if [ ! -f "$TEMPLATEDISKIMAGE" ]
then
echo Unable to find the template disk image, $TEMPLATEDISKIMAGE
cleanupAndExit
printErrorAndExit "Unable to find the template disk image, $TEMPLATEDISKIMAGE"
fi
if [ ! -f "$TEMPLATEBOOTIMAGE" ]
then
printErrorAndExit "Unable to find the template boot image, $TEMPLATEBOOTIMAGE"
fi
mkdir "$TMPDIR"
if [ $? != 0 ]
then
printErrorAndExit "Unable to create the mount directory."
fi
mkdir "$MOUNTDIR"
if [ $? != 0 ]
then
printErrorAndExit "Unable to create the mount directory."
fi
cp "$TEMPLATEBOOTIMAGE" "$TMPBOOTIMAGE"
if [ $? != 0 ]
then
printErrorAndExit "Unable to copy template boot image."
fi
if [ ! -z "$COPYBOOTDIRS" ] || [ ! -z "BOOTCOPYPATH" ]
then
profuse -orw "$TMPBOOTIMAGE" "$MOUNTDIR"
if [ $? != 0 ]
then
printErrorAndExit "Unable to mount the boot image."
fi
if [ ! -z "$BOOTCOPYPATH" ]
then
cpProDOS "$FILE" "$MOUNTDIR/$BOOTCOPYPATH"
if [ $? != 0 ]
then
printErrorAndExit "Unable to copy the file to the boot image."
fi
fi
copyDirs $COPYBOOTDIRS
unmount "$MOUNTDIR"
fi
cp "$TEMPLATEDISKIMAGE" "$TMPDISKIMAGE"
if [ $? != 0 ]
then
echo Unable to copy template disk image.
cleanupAndExit
fi
mkdir "$TMPDIR"
mkdir "$MOUNTDIR"
if [ $? != 0 ]
then
echo Unable to create the mount directory.
cleanupAndExit
printErrorAndExit "Unable to copy template disk image."
fi
profuse -orw "$TMPDISKIMAGE" "$MOUNTDIR"
if [ $? != 0 ]
then
echo Unable to mount the disk image.
cleanupAndExit
printErrorAndExit "Unable to mount the disk image."
fi
cp "$FILE" "$DEST"
cpProDOS "$FILE" "$MOUNTDIR"
if [ $? != 0 ]
then
echo Unable to copy the file to the disk image.
cleanupAndExit
printErrorAndExit "Unable to copy the file to the disk image."
fi
copyDirs $COPYDIRS
OLDDIR=`pwd`
for COPYDIR in $COPYDIRS
do
cd "$COPYDIR"
if [ $? != 0 ]
then
echo Unable to find $COPYDIR
cleanupAndExit
fi
find . -print | while read FILEORDIR
do
if [ -d "$FILEORDIR" ]
then
mkdir -p "${MOUNTDIR}/$FILEORDIR"
elif [ -f "$FILEORDIR" ]
then
cp "$FILEORDIR" "${MOUNTDIR}/$FILEORDIR"
fi
done
done
cd "$TMPDIR"
$ORCA "$OLDDIR/make/tar" cf "$TMPARCHIVE" "$PROGRAM"
if [ $? != 0 ]
then
echo Unable to create archive.
cleanupAndExit
printErrorAndExit "Unable to create archive."
fi
cd "$OLDDIR"
RETRIES=0
while [ $RETRIES -lt 5 ]
do
umount "$MOUNTDIR"
if [ $? -eq 0 ]
then
break
fi
RETRIES=`expr $RETRIES + 1`
sleep 1
done
if [ $RETRIES -ge 5 ]
then
echo Unable to unmount the disk image.
cleanupAndExit
fi
unmount "$MOUNTDIR"
cp "$TMPDISKIMAGE" "$DISKIMAGE"
if [ $? != 0 ]
then
echo Unable to copy the disk image to the destination.
cleanupAndExit
printErrorAndExit "Unable to copy the disk image to the destination."
fi
cp "$TMPBOOTIMAGE" "$DESTBOOTIMAGE"
if [ $? != 0 ]
then
printErrorAndExit "Unable to copy the boot image to the destination."
fi
cp "$TMPARCHIVE" "$ARCHIVE"
if [ $? != 0 ]
then
echo Unable to copy the archive to the destination.
cleanupAndExit
printErrorAndExit "Unable to copy the archive to the destination."
fi
# This is a special case for my personal build environment. If I can find my home directory
@ -144,6 +255,7 @@ then
fi
rm -f "$TMPDISKIMAGE"
rm -f "$TMPBOOTIMAGE"
rm -f "$TMPARCHIVE"
rm -rf "$TMPDIR"
exit 0

View File

@ -72,10 +72,40 @@ DESKTOP_RES_MODE=640
MESSAGE_CENTER=0
GSPLUS=/Applications/GSplus.app/Contents/MacOS/gsplus
GSPLUSARGS=
GSPORT=/Applications/GSport/GSport.app/Contents/MacOS/GSport
GSPORTARGS=
MAME=/Applications/Ample.app/Contents/MacOS/mame64
MAMELIB=$(HOME)/Library/Application Support/Ample
MAMEARGS=apple2gs -skip_gameinfo -speed 2 -mouse -window -resolution 1408x1056 -ramsize 4M -sl7 cffa202
export GSPLUS
export GSPLUSARGS
export GSPORT
export GSPORTARGS
export MAME
export MAMELIB
export MAMEARGS
ifneq (,$(wildcard $GSPLUS))
EMULATOR=gsplus
else ifneq (,$(wildcard $MAME))
EMULATOR=mame
else ifneq (,$(wildcard $GSPORT))
EMULATOR=gsport
else
EMULATOR=gsplus
endif
export EMULATOR
BOOTIMAGE=system601.2mg
export BOOTIMAGE
COPYDIRS=
export COPYDIRS
COPYBOOTDIRS=
export COPYBOOTDIRS
XCODE_PATH=/Applications/Xcode.app
XCODE_INFO=$(XCODE_PATH)/Contents/Info.plist
@ -90,6 +120,6 @@ XCODE_PLUGIN_COMPATIBILITY=DVTPlugInCompatibilityUUID
all:
$(MKDIR) $(OBJDIR) $(GENDIR) $(TARGETDIR)
@make gen
@make build
@make -f $(firstword $(MAKEFILE_LIST)) gen
@make -f $(firstword $(MAKEFILE_LIST)) build

View File

@ -1,48 +1,84 @@
#!/bin/sh
if [ ! -z "$GSPLUS" ] && [ -x "$GSPLUS" ]
then
EMULATORPATH="$GSPLUS"
elif [ ! -z "$GSPORT" ] && [ -x "$GSPORT" ]
then
EMULATORPATH="$GSPORT"
fi
TMPDIR="/tmp/a2gs_emulator.$$"
if [ -z "$EMULATORPATH" ]
cleanupAndExit()
{
cd
if [ ! -z $PID ]
then
kill $PID 2> /dev/null
fi
rm -rf $TMPDIR
exit 0
}
createConfigTxt()
{
mkdir "$TMPDIR"
sed "s:^s7d1 *=.*$:s7d1 = $BOOTIMAGE:
s:^s5d1 *=.*:s5d1 = $DISKIMAGE:" < make/config.txt > "$TMPDIR/config.txt"
}
if [ $# -ne 2 ]
then
echo Unable to find GSplus or GSport at these locations.
echo " GSPLUS=$GSPLUS"
echo " GSPORT=$GSPORT"
echo USAGE $0: diskimage bootimage
exit 1
fi
BOOTIMAGE="$1"
if echo $BOOTIMAGE | grep -v '^/' > /dev/null
then
BOOTIMAGE="$PWD/$BOOTIMAGE"
fi
DISKIMAGE="$2"
DISKIMAGE="$1"
if echo $DISKIMAGE | grep -v '^/' > /dev/null
then
DISKIMAGE="$PWD/$DISKIMAGE"
fi
# This magic ensures that clicking stop in Xcode results in the emulator terminating.
if true
BOOTIMAGE="$2"
if echo $BOOTIMAGE | grep -v '^/' > /dev/null
then
cd make
sed -i "" "s:^s7d1 *=.*$:s7d1 = $BOOTIMAGE:
s:^s5d1 *=.*:s5d1 = $DISKIMAGE:" config.txt
$EMULATORPATH -fullscreen -mem 1572864 &
PID=$!
else
cd "/Users/jrand/Library/Application Support/Ample"
/Applications/Ample.app/Contents/MacOS/mame64 apple2gs -skip_gameinfo -mouse -window -resolution 1408x1056 -ramsize 4M -sl7 cffa202 -hard1 "$BOOTIMAGE" -flop3 "$DISKIMAGE" &
PID=$!
BOOTIMAGE="$PWD/$BOOTIMAGE"
fi
trap 'kill $PID' SIGTERM SIGINT SIGHUP EXIT
wait
case "$EMULATOR" in
gsplus)
EMULATORPATH="$GSPLUS"
EMULATORARGS="$GSPLUSARGS"
EMULATORDIR="$TMPDIR"
createConfigTxt
;;
gsport)
EMULATORPATH="$GSPORT"
EMULATORARGS="$GSPORTARGS"
EMULATORDIR="$TMPDIR"
createConfigTxt
;;
mame)
EMULATORPATH="$MAME"
EMULATORARGS="$MAMEARGS -hard1 "$BOOTIMAGE" -flop3 "$DISKIMAGE""
EMULATORDIR="$MAMELIB"
;;
*)
echo Invalid emulator specified, \"$EMULATOR\"
exit 1
;;
esac
exit 0
if [ ! -x "$EMULATORPATH" ]
then
echo Unable to find $EMULATOR at $EMULATORPATH
exit 1
fi
if [ ! -d "$EMULATORDIR" ]
then
echo Unable to find $EMULATOR library at $EMULATORDIR
exit 1
fi
cd "$EMULATORDIR"
# This magic ensure that clicking stop in Xcode results in the emulator terminating.
"$EMULATORPATH" $EMULATORARGS &
PID=$!
trap cleanupAndExit SIGTERM SIGINT SIGHUP EXIT
wait

BIN
BuGS/make/md2teach Normal file

Binary file not shown.

Binary file not shown.

BIN
BuGS/make/system601_net.2mg Normal file

Binary file not shown.

View File

@ -7,9 +7,11 @@ export PATH := $(PATH):$(ORCA_BIN)
CWD=$(shell pwd)
DISKIMAGE=$(TARGETDIR)/$(PGM).2mg
ARCHIVE=$(TARGETDIR)/$(PGM).shk
DESTBOOTIMAGE=$(TARGETDIR)/$(BOOTIMAGE)
BUILDTARGET=$(DISKIMAGE)
EXECTARGET=executeGUI
DISKIMAGEDEST=.
BOOTCOPYPATH=
AUXTYPE=
CFLAGS+=-i$(GENDIR)
@ -31,18 +33,18 @@ else ifeq ($(TARGETTYPE),desktop)
REZFLAGS+=rez='-d MESSAGE_CENTER=$(MESSAGE_CENTER)'
else ifeq ($(TARGETTYPE),cda)
FILETYPE=cda
DISKIMAGEDEST=System/Desk.Accs
BOOTCOPYPATH=System/Desk.Accs
else ifeq ($(TARGETTYPE),cdev)
BINTARGET=$(TARGETDIR)/$(PGM).bin
FILETYPE=199
DISKIMAGEDEST=System/CDevs
BOOTCOPYPATH=System/CDevs
REZFLAGS+=rez='-d BINTARGET="$(BINTARGET)"'
else ifeq ($(TARGETTYPE),nba)
FILETYPE=exe
BUILDTARGET=$(TARGETDIR)/$(PGM)
else ifeq ($(TARGETTYPE),nda)
FILETYPE=nda
DISKIMAGEDEST=System/Desk.Accs
BOOTCOPYPATH=System/Desk.Accs
else ifeq ($(TARGETTYPE),xcmd)
FILETYPE=exe
BUILDTARGET=$(TARGETDIR)/$(PGM)
@ -71,17 +73,22 @@ REZ_SRCS=$(patsubst $(GENDIR)/%, %, $(patsubst ./%, %, $(wildcard $(addsuffix /*
REZ_DEPS=$(patsubst %.rez, $(OBJDIR)/%.rez.d, $(REZ_SRCS))
REZ_OBJS=$(patsubst %.rez, $(OBJDIR)/%.r, $(REZ_SRCS))
TEACH_FILES=$(patsubst %.md, $(GENDIR)/Teach/%, $(MD_SRCS))
ifneq ($(TEACH_FILES),)
COPYDIRS+=$(GENDIR)/Teach
endif
ifneq ($(firstword $(REZ_SRCS)), $(lastword $(REZ_SRCS)))
$(error Only a single resource file supported, found $(REZ_SRCS))
endif
BUILD_OBJS=$(C_ROOTS) $(C_OBJS) $(ASM_ROOTS)
BUILD_OBJS=$(C_ROOTS) $(C_OBJS) $(ASM_ROOTS) $(TEACH_FILES)
ifeq ($(BINTARGET),)
BUILD_OBJS+=$(REZ_OBJS)
endif
BUILD_OBJS_NOSUFFIX=$(C_ROOTS:.root=) $(C_OBJS:.a=) $(ASM_ROOTS:.ROOT=)
ALL_OBJS=$(C_ROOTS:.root=.a) $(C_OBJS) $(ASM_OBJS) $(REZ_OBJS)
ALL_OBJS=$(C_ROOTS:.root=.a) $(C_OBJS) $(ASM_OBJS) $(REZ_OBJS) $(TEACH_FILES)
ALL_ROOTS=$(C_ROOTS) $(C_OBJS:.a=.root) $(ASM_ROOTS)
ALL_DEPS=$(C_DEPS) $(ASM_DEPS) $(REZ_DEPS)
@ -96,7 +103,7 @@ build: $(BUILDTARGET)
gen: xcodefix
xcodefix:
defaults write "$(ORCAM_PLUGIN_INFO)" $(XCODE_PLUGIN_COMPATIBILITY)s -array `defaults read "$(XCODE_INFO)" $(XCODE_PLUGIN_COMPATIBILITY)` || true
[ "`uname`" = Darwin ] && defaults write "$(ORCAM_PLUGIN_INFO)" $(XCODE_PLUGIN_COMPATIBILITY)s -array `defaults read "$(XCODE_INFO)" $(XCODE_PLUGIN_COMPATIBILITY)` || true
clean: genclean
$(RM) "$(TARGETDIR)/$(PGM)" $(BINTARGET)
@ -105,6 +112,8 @@ clean: genclean
$(RM) $(ALL_DEPS)
$(RM) $(ASM_MACROS)
$(RM) "$(DISKIMAGE)"
$(RM) "$(DESTBOOTIMAGE)"
$(RM) "$(ARCHIVE)"
createPackage:
pkg/createPackage
@ -125,7 +134,7 @@ ifneq ($(REZ_OBJS),)
$(RM) $(TARGETDIR)/$(PGM)
$(CP) $(REZ_OBJS) $(TARGETDIR)/$(PGM)
endif
cd $(OBJDIR); $(LINK) $(LDFLAGS) $(patsubst $(OBJDIR)/%, %, $(BUILD_OBJS_NOSUFFIX)) --keep=$(TARGETDIR)/$(PGM)
cd $(OBJDIR); $(LINK) $(LDFLAGS) $(patsubst $(OBJDIR)/%, %, $(BUILD_OBJS_NOSUFFIX)) keep="$(abspath $(TARGETDIR)/$(PGM))"
$(CHTYP) -t $(FILETYPE) $(AUXTYPE) $(TARGETDIR)/$(PGM)
endif
@ -154,7 +163,7 @@ else
# resource compile will read the $(PGM).bin binary and load it into the
# resources also.
$(BINTARGET): $(BUILD_OBJS)
cd $(OBJDIR); $(LINK) $(LDFLAGS) $(patsubst $(OBJDIR)/%, %, $(BUILD_OBJS_NOSUFFIX)) --keep=$(BINTARGET)
cd $(OBJDIR); $(LINK) $(LDFLAGS) $(patsubst $(OBJDIR)/%, %, $(BUILD_OBJS_NOSUFFIX)) keep="$(abspath $(BINTARGET))"
endif
@ -178,13 +187,13 @@ $(TARGETDIR)/$(PGM): $(REZ_OBJS)
endif
$(DISKIMAGE): $(TARGETDIR)/$(PGM)
make/createDiskImage "$(DISKIMAGE)" "$(TARGETDIR)/$(PGM)" "$(DISKIMAGEDEST)" $(COPYDIRS)
$(DISKIMAGE): $(TARGETDIR)/$(PGM) make/empty.2mg make/$(BOOTIMAGE)
make/createDiskImage "$(DISKIMAGE)" $(DESTBOOTIMAGE) "$(TARGETDIR)/$(PGM)" $(BOOTCOPYPATH)
execute: $(EXECTARGET)
executeGUI: all
make/launchEmulator make/system601.2mg $(DISKIMAGE)
make/launchEmulator "$(DISKIMAGE)" "$(DESTBOOTIMAGE)"
executeShell: all
$(ORCA) $(TARGETDIR)/$(PGM)
@ -219,6 +228,10 @@ ifneq ($(RLINT_PATH),)
$(ORCA) $(RLINT_PATH) $@
endif
$(GENDIR)/Teach/%: %.md
$(MKDIR) "$(GENDIR)/Teach"
$(ORCA) make/md2teach "$<" "$@"
$(OBJS): Makefile
# Include the C and rez dependencies which were generated from the last build

745
BuGS/netScores.c Normal file
View File

@ -0,0 +1,745 @@
/*
* netScores.c
* NetScoresGS
*
* Created by Jeremy Rand on 2021-05-23.
* Copyright © 2021 Jeremy Rand. All rights reserved.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <Hash.h>
#include <locator.h>
#include <misctool.h>
#include <orca.h>
#include <tcpip.h>
#include "netScores.h"
// Defines
#define REQUEST_TYPE_GET_HIGH_SCORES 0
#define REQUEST_TYPE_SET_SCORE 1
#define RESPONSE_TYPE_HELLO 0
#define RESPONSE_TYPE_SCORES 1
#define RESPONSE_TYPE_STATUS 2
#define DEFAULT_SHUTDOWN_NETWORK_TIMEOUT (2 * 60)
#define DEFAULT_TCP_CONNECT_TIMEOUT (8 * 60)
#define DEFAULT_READ_NETWORK_TIMEOUT (5 * 60)
#define DEFAULT_NETWORK_RETRY_TIMEOUT (3 * 60 * 60)
// Types
typedef struct tSessionSecrets {
uint32_t secret;
uint32_t nonce;
} tSessionSecrets;
typedef struct tHighScoreRequest {
uint16_t requestType;
} tHighScoreRequest;
typedef struct tHighScoreRequestWithHash {
tHighScoreRequest highScoreRequest;
uint8_t md5Digest[16];
} tHighScoreRequestWithHash;
typedef struct tSetHighScoreRequest {
uint16_t requestType;
char who[4];
uint32_t score;
Word is60Hz;
} tSetHighScoreRequest;
typedef struct tSetHighScoreRequestWithHash {
tSetHighScoreRequest setHighScoreRequest;
uint8_t md5Digest[16];
} tSetHighScoreRequestWithHash;
typedef struct tHelloResponse {
uint16_t responseType;
uint32_t nonce;
} tHelloResponse;
typedef struct tScoresResponse {
uint16_t responseType;
tNSGSHighScores highScores;
} tScoresResponse;
typedef struct tStatusResponse {
uint16_t responseType;
Boolean success;
uint16_t position;
uint16_t numberOfScores;
} tStatusResponse;
typedef enum tGameNetworkState {
GAME_NETWORK_CONNECT_FAILED = 0,
GAME_NETWORK_UNCONNECTED,
/* All states below this should have Marinetti disconnected */
GAME_NETWORK_CONNECTED,
GAME_NETWORK_RESOLVING_NAME,
/* These are all of the error states. Marinetti is still connected but the
TCP connection is closed and ipid released. */
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. */
GAME_NETWORK_WAITING_FOR_TCP,
/* All of these states below this point have the TCP connection established
and the score protocol is exchanging information. */
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,
GAME_NETWORK_CLOSING_TCP,
NUM_GAME_NETWORK_STATES
} tGameNetworkState;
typedef void (*tGameNetworkStateHandler)(void);
typedef struct tGameNetworkGlobals {
Boolean networkStartedConnected;
tNSGSHighScoreInitParams initParams;
tGameNetworkState gameNetworkState;
dnrBuffer domainNameResolution;
srBuff tcpStatus;
Word ipid;
rrBuff readResponseBuf;
tHelloResponse helloResponse;
uint32_t bytesRead;
md5WorkBlock hashWorkBlock;
uint32_t secrets[3];
tHighScoreRequestWithHash highScoreRequest;
tScoresResponse highScoreResponse;
Boolean hasHighScoreToSend;
tSetHighScoreRequestWithHash setHighScoreRequest;
tStatusResponse setHighScoreResponse;
uint16_t errorCode;
uint16_t timeout;
Boolean hasGlobalHighScores;
} tGameNetworkGlobals;
// Forward declarations
static void handleConnectFailed(void);
static void handleUnconnected(void);
static void handleConnected(void);
static void handleResolvingName(void);
static void handleLookupFailed(void);
static void handleSocketError(void);
static void handleProtocolFailed(void);
static void handleFailure(void);
static void handleTcpUnconnected(void);
static void handleWaitingForTcp(void);
static void handleWaitingForHello(void);
static void handleRequestScores(void);
static void handleWaitingForScores(void);
static void handleSetHighScore(void);
static void handleWaitingForScoreAck(void);
static void handleClosingTcp(void);
// Globals
#if __ORCAC__
segment "highscores";
#endif
static tGameNetworkStateHandler handlers[NUM_GAME_NETWORK_STATES] = {
handleConnectFailed,
handleUnconnected,
handleConnected,
handleResolvingName,
handleLookupFailed,
handleSocketError,
handleProtocolFailed,
handleFailure,
handleTcpUnconnected,
handleWaitingForTcp,
handleWaitingForHello,
handleRequestScores,
handleWaitingForScores,
handleSetHighScore,
handleWaitingForScoreAck,
handleClosingTcp
};
// 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;
// Implementation
void NSGS_InitNetwork(tNSGSHighScoreInitParams * params)
{
networkGlobals = NULL;
if ((params->scoreServer == NULL) ||
(params->scorePort == 0) ||
(params->waitForVbl == NULL))
return;
LoadOneTool(54, 0x200); // Load Marinetti
if (toolerror())
return;
LoadOneTool(128, 0x103); // Load the Hash toolset
if (toolerror()) {
UnloadOneTool(54);
return;
}
TCPIPStartUp();
if (toolerror()) {
UnloadOneTool(128);
UnloadOneTool(54);
return;
}
hashStartUp();
if (toolerror()) {
TCPIPShutDown();
UnloadOneTool(128);
UnloadOneTool(54);
return;
}
networkGlobals = malloc(sizeof(tGameNetworkGlobals));
if (networkGlobals == NULL) {
hashShutDown();
TCPIPShutDown();
UnloadOneTool(128);
UnloadOneTool(54);
return;
}
memcpy(&(networkGlobals->initParams), params, sizeof(networkGlobals->initParams));
if (networkGlobals->initParams.shutdownTimeout == 0)
networkGlobals->initParams.shutdownTimeout = DEFAULT_SHUTDOWN_NETWORK_TIMEOUT;
if (networkGlobals->initParams.connectTimeout == 0)
networkGlobals->initParams.connectTimeout = DEFAULT_TCP_CONNECT_TIMEOUT;
if (networkGlobals->initParams.readTimeout == 0)
networkGlobals->initParams.readTimeout = DEFAULT_READ_NETWORK_TIMEOUT;
if (networkGlobals->initParams.retryTimeout == 0)
networkGlobals->initParams.retryTimeout = DEFAULT_NETWORK_RETRY_TIMEOUT;
networkGlobals->networkStartedConnected = TCPIPGetConnectStatus();
if (networkGlobals->networkStartedConnected) {
networkGlobals->gameNetworkState = GAME_NETWORK_CONNECTED;
} else {
networkGlobals->gameNetworkState = GAME_NETWORK_UNCONNECTED;
}
networkGlobals->secrets[0] = params->secret1;
networkGlobals->secrets[1] = params->secret2;
networkGlobals->hasHighScoreToSend = FALSE;
networkGlobals->hasGlobalHighScores = FALSE;
networkGlobals->setHighScoreRequest.setHighScoreRequest.is60Hz = !ReadBParam(hrtz50or60);
}
void NSGS_ShutdownNetwork(void)
{
if ((!networkGlobals->networkStartedConnected) &&
(networkGlobals->gameNetworkState > GAME_NETWORK_UNCONNECTED)) {
TCPIPDisconnect(TRUE, NULL);
}
free(networkGlobals);
hashShutDown();
TCPIPShutDown();
UnloadOneTool(128); // Unload the Hash toolset
UnloadOneTool(54); // Unload Marinetti
}
static void abortConnection(void)
{
TCPIPAbortTCP(networkGlobals->ipid);
TCPIPLogout(networkGlobals->ipid);
}
void NSGS_DisconnectNetwork(void)
{
if (networkGlobals == NULL)
return;
if (networkGlobals->gameNetworkState > GAME_NETWORK_TCP_UNCONNECTED) {
if (networkGlobals->gameNetworkState < GAME_NETWORK_CLOSING_TCP) {
TCPIPCloseTCP(networkGlobals->ipid);
networkGlobals->gameNetworkState = GAME_NETWORK_CLOSING_TCP;
networkGlobals->timeout = networkGlobals->initParams.shutdownTimeout;
}
}
while ((networkGlobals->gameNetworkState != GAME_NETWORK_TCP_UNCONNECTED) &&
(networkGlobals->gameNetworkState != GAME_NETWORK_FAILURE)) {
networkGlobals->initParams.waitForVbl();
NSGS_PollNetwork();
}
}
static void handleConnectFailed(void)
{
if (networkGlobals->initParams.displayError != NULL)
networkGlobals->initParams.displayError(NSGS_CONNECT_ERROR, networkGlobals->errorCode);
networkGlobals->gameNetworkState = GAME_NETWORK_FAILURE;
}
static void handleUnconnected(void)
{
if (networkGlobals->initParams.displayConnectionString != NULL)
networkGlobals->initParams.displayConnectionString(TRUE);
TCPIPConnect(NULL);
if ((!toolerror()) &&
(TCPIPGetConnectStatus())) {
networkGlobals->gameNetworkState = GAME_NETWORK_CONNECTED;
} else {
networkGlobals->gameNetworkState = GAME_NETWORK_CONNECT_FAILED;
networkGlobals->errorCode = toolerror();
}
if (networkGlobals->initParams.displayConnectionString != NULL)
networkGlobals->initParams.displayConnectionString(FALSE);
}
static void handleConnected(void)
{
TCPIPDNRNameToIP((char *)networkGlobals->initParams.scoreServer, &(networkGlobals->domainNameResolution));
if (toolerror()) {
networkGlobals->gameNetworkState = GAME_NETWORK_LOOKUP_FAILED;
networkGlobals->errorCode = toolerror();
} else
networkGlobals->gameNetworkState = GAME_NETWORK_RESOLVING_NAME;
}
static void handleResolvingName(void)
{
if (networkGlobals->domainNameResolution.DNRstatus == DNR_Pending)
return;
if (networkGlobals->domainNameResolution.DNRstatus != DNR_OK) {
networkGlobals->gameNetworkState = GAME_NETWORK_LOOKUP_FAILED;
networkGlobals->errorCode = networkGlobals->domainNameResolution.DNRstatus;
return;
}
networkGlobals->gameNetworkState = GAME_NETWORK_TCP_UNCONNECTED;
}
static void handleLookupFailed(void)
{
if (networkGlobals->initParams.displayError != NULL)
networkGlobals->initParams.displayError(NSGS_LOOKUP_ERROR, networkGlobals->errorCode);
networkGlobals->gameNetworkState = GAME_NETWORK_FAILURE;
}
static void handleSocketError(void)
{
if (networkGlobals->initParams.displayError != NULL)
networkGlobals->initParams.displayError(NSGS_SOCKET_ERROR, networkGlobals->errorCode);
networkGlobals->gameNetworkState = GAME_NETWORK_FAILURE;
networkGlobals->timeout = networkGlobals->initParams.retryTimeout;
}
static void handleProtocolFailed(void)
{
abortConnection();
if (networkGlobals->initParams.displayError != NULL)
networkGlobals->initParams.displayError(NSGS_PROTOCOL_ERROR, networkGlobals->errorCode);
networkGlobals->gameNetworkState = GAME_NETWORK_FAILURE;
networkGlobals->timeout = networkGlobals->initParams.retryTimeout;
}
static void handleFailure(void)
{
// All of the different failure modes except protocol failure above end up here ultimately. And the state
// machine stays here once it arrives here.
if (networkGlobals->timeout > 0) {
networkGlobals->timeout--;
if (networkGlobals->timeout == 0)
networkGlobals->gameNetworkState = GAME_NETWORK_TCP_UNCONNECTED;
}
}
static void handleTcpUnconnected(void)
{
if ((networkGlobals->hasGlobalHighScores) &&
(!networkGlobals->hasHighScoreToSend)) {
if (networkGlobals->initParams.shouldRefreshHighScores == NULL)
return;
if (!networkGlobals->initParams.shouldRefreshHighScores())
return;
}
networkGlobals->ipid = TCPIPLogin(networkGlobals->initParams.userId, networkGlobals->domainNameResolution.DNRIPaddress, networkGlobals->initParams.scorePort, 0, 64);
if (toolerror()) {
networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR;
networkGlobals->errorCode = toolerror();
return;
}
networkGlobals->errorCode = TCPIPOpenTCP(networkGlobals->ipid);
if (networkGlobals->errorCode != tcperrOK) {
TCPIPLogout(networkGlobals->ipid);
networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR;
return;
}
networkGlobals->gameNetworkState = GAME_NETWORK_WAITING_FOR_TCP;
networkGlobals->timeout = networkGlobals->initParams.connectTimeout;
}
static void handleWaitingForTcp(void)
{
networkGlobals->errorCode = TCPIPStatusTCP(networkGlobals->ipid, &(networkGlobals->tcpStatus));
if (networkGlobals->errorCode != tcperrOK) {
abortConnection();
networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR;
return;
}
if ((networkGlobals->tcpStatus.srState == TCPSSYNSENT) ||
(networkGlobals->tcpStatus.srState == TCPSSYNRCVD)) {
if (networkGlobals->timeout > 0) {
networkGlobals->timeout--;
} else {
networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED;
networkGlobals->errorCode = NSGS_TCP_CONNECT_TIMEOUT_ERROR;
}
return;
}
if (networkGlobals->tcpStatus.srState != TCPSESTABLISHED) {
abortConnection();
networkGlobals->errorCode = networkGlobals->tcpStatus.srState | 0x8000;
networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR;
return;
}
networkGlobals->gameNetworkState = GAME_NETWORK_WAITING_FOR_HELLO;
networkGlobals->timeout = networkGlobals->initParams.readTimeout;
networkGlobals->bytesRead = 0;
}
static void handleWaitingForHello(void)
{
networkGlobals->errorCode = TCPIPReadTCP(networkGlobals->ipid, 0,
((uint32_t)(&(networkGlobals->helloResponse))) + networkGlobals->bytesRead,
sizeof(networkGlobals->helloResponse) - networkGlobals->bytesRead,
&(networkGlobals->readResponseBuf));
if (networkGlobals->errorCode != tcperrOK) {
abortConnection();
networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR;
return;
}
networkGlobals->bytesRead += networkGlobals->readResponseBuf.rrBuffCount;
if (networkGlobals->bytesRead < sizeof(networkGlobals->helloResponse)) {
if (networkGlobals->timeout > 0) {
networkGlobals->timeout--;
} else {
networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED;
networkGlobals->errorCode = NSGS_HELLO_TIMEOUT_ERROR;
}
return;
}
if (networkGlobals->bytesRead > sizeof(networkGlobals->helloResponse)) {
networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED;
networkGlobals->errorCode = NSGS_HELLO_TOO_BIG_ERROR;
return;
}
if (networkGlobals->helloResponse.responseType != RESPONSE_TYPE_HELLO) {
networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED;
networkGlobals->errorCode = NSGS_HELLO_UNEXPECTED_RESPONSE_ERROR;
return;
}
networkGlobals->secrets[2] = networkGlobals->helloResponse.nonce;
if (networkGlobals->hasHighScoreToSend) {
networkGlobals->gameNetworkState = GAME_NETWORK_SET_HIGH_SCORE;
} else if ((!networkGlobals->hasGlobalHighScores) ||
((networkGlobals->initParams.shouldRefreshHighScores != NULL) &&
(networkGlobals->initParams.shouldRefreshHighScores()))) {
networkGlobals->gameNetworkState = GAME_NETWORK_REQUEST_SCORES;
} else {
TCPIPCloseTCP(networkGlobals->ipid);
networkGlobals->gameNetworkState = GAME_NETWORK_CLOSING_TCP;
networkGlobals->timeout = networkGlobals->initParams.shutdownTimeout;
}
}
static void handleRequestScores(void)
{
networkGlobals->highScoreRequest.highScoreRequest.requestType = REQUEST_TYPE_GET_HIGH_SCORES;
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), (Pointer)&(networkGlobals->highScoreRequest.md5Digest[0]));
networkGlobals->errorCode = TCPIPWriteTCP(networkGlobals->ipid, (Pointer)&(networkGlobals->highScoreRequest), sizeof(networkGlobals->highScoreRequest), FALSE, FALSE);
if (networkGlobals->errorCode != tcperrOK) {
abortConnection();
networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR;
return;
}
networkGlobals->gameNetworkState = GAME_NETWORK_WAITING_FOR_SCORES;
networkGlobals->timeout = networkGlobals->initParams.readTimeout;
networkGlobals->bytesRead = 0;
}
static void handleWaitingForScores(void)
{
networkGlobals->errorCode = TCPIPReadTCP(networkGlobals->ipid, 0,
((uint32_t)(&networkGlobals->highScoreResponse)) + networkGlobals->bytesRead,
sizeof(networkGlobals->highScoreResponse) - networkGlobals->bytesRead,
&(networkGlobals->readResponseBuf));
if (networkGlobals->errorCode != tcperrOK) {
abortConnection();
networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR;
return;
}
networkGlobals->bytesRead += networkGlobals->readResponseBuf.rrBuffCount;
if (networkGlobals->bytesRead < sizeof(networkGlobals->highScoreResponse)) {
if (networkGlobals->timeout > 0) {
networkGlobals->timeout--;
} else {
networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED;
networkGlobals->errorCode = NSGS_HIGH_SCORE_TIMEOUT_ERROR;
}
return;
}
if (networkGlobals->bytesRead > sizeof(networkGlobals->highScoreResponse)) {
networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED;
networkGlobals->errorCode = NSGS_HIGH_SCORE_TOO_BIG_ERROR;
return;
}
if (networkGlobals->highScoreResponse.responseType != RESPONSE_TYPE_SCORES) {
networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED;
networkGlobals->errorCode = NSGS_HIGH_SCORE_UNEXPECTED_RESPONSE_ERROR;
return;
}
networkGlobals->hasGlobalHighScores = TRUE;
if (networkGlobals->initParams.setHighScores != NULL)
networkGlobals->initParams.setHighScores(&(networkGlobals->highScoreResponse.highScores));
TCPIPCloseTCP(networkGlobals->ipid);
networkGlobals->gameNetworkState = GAME_NETWORK_CLOSING_TCP;
networkGlobals->timeout = networkGlobals->initParams.shutdownTimeout;
}
static void handleSetHighScore(void)
{
networkGlobals->setHighScoreRequest.setHighScoreRequest.requestType = REQUEST_TYPE_SET_SCORE;
networkGlobals->setHighScoreRequest.setHighScoreRequest.who[3] = '\0';
md5Init(&(networkGlobals->hashWorkBlock));
md5Append(&(networkGlobals->hashWorkBlock), (Pointer)&(networkGlobals->secrets), sizeof(networkGlobals->secrets));
md5Append(&(networkGlobals->hashWorkBlock), (Pointer)&(networkGlobals->setHighScoreRequest.setHighScoreRequest), sizeof(networkGlobals->setHighScoreRequest.setHighScoreRequest));
md5Finish(&(networkGlobals->hashWorkBlock), (Pointer)&(networkGlobals->setHighScoreRequest.md5Digest[0]));
networkGlobals->errorCode = TCPIPWriteTCP(networkGlobals->ipid, (Pointer)&(networkGlobals->setHighScoreRequest), sizeof(networkGlobals->setHighScoreRequest), FALSE, FALSE);
if (networkGlobals->errorCode != tcperrOK) {
abortConnection();
networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR;
return;
}
networkGlobals->gameNetworkState = GAME_NETWORK_WAITING_FOR_SCORE_ACK;
networkGlobals->timeout = networkGlobals->initParams.readTimeout;
networkGlobals->bytesRead = 0;
}
static void handleWaitingForScoreAck(void)
{
networkGlobals->errorCode = TCPIPReadTCP(networkGlobals->ipid, 0,
((uint32_t)(&(networkGlobals->setHighScoreResponse))) + networkGlobals->bytesRead,
sizeof(networkGlobals->setHighScoreResponse) - networkGlobals->bytesRead,
&(networkGlobals->readResponseBuf));
if (networkGlobals->errorCode != tcperrOK) {
abortConnection();
networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR;
return;
}
networkGlobals->bytesRead += networkGlobals->readResponseBuf.rrBuffCount;
if (networkGlobals->bytesRead < sizeof(networkGlobals->setHighScoreResponse)) {
if (networkGlobals->timeout > 0) {
networkGlobals->timeout--;
} else {
networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED;
networkGlobals->errorCode = NSGS_SET_SCORE_TIMEOUT_ERROR;
}
return;
}
if (networkGlobals->bytesRead > sizeof(networkGlobals->setHighScoreResponse)) {
networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED;
networkGlobals->errorCode = NSGS_SET_SCORE_TOO_BIG_ERROR;
return;
}
if (networkGlobals->setHighScoreResponse.responseType != RESPONSE_TYPE_STATUS) {
networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED;
networkGlobals->errorCode = NSGS_SET_SCORE_UNEXPECTED_RESPONSE_ERROR;
return;
}
if (!networkGlobals->setHighScoreResponse.success) {
networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED;
networkGlobals->errorCode = NSGS_SET_SCORE_FAILED_ERROR;
return;
}
networkGlobals->hasHighScoreToSend = FALSE;
networkGlobals->gameNetworkState = GAME_NETWORK_REQUEST_SCORES;
}
static void handleClosingTcp(void)
{
networkGlobals->errorCode = TCPIPStatusTCP(networkGlobals->ipid, &(networkGlobals->tcpStatus));
if ((networkGlobals->tcpStatus.srState != TCPSCLOSED) &&
(networkGlobals->tcpStatus.srState != TCPSTIMEWAIT)) {
if (networkGlobals->timeout > 0) {
networkGlobals->timeout--;
} else {
abortConnection();
networkGlobals->gameNetworkState = GAME_NETWORK_TCP_UNCONNECTED;
}
return;
}
TCPIPLogout(networkGlobals->ipid);
networkGlobals->gameNetworkState = GAME_NETWORK_TCP_UNCONNECTED;
}
void NSGS_PollNetwork(void)
{
if (networkGlobals == NULL)
return;
TCPIPPoll();
handlers[networkGlobals->gameNetworkState]();
}
BOOLEAN NSGS_CanSendHighScore(void)
{
if (networkGlobals == NULL)
return FALSE;
if (networkGlobals->gameNetworkState < GAME_NETWORK_TCP_UNCONNECTED) {
if ((networkGlobals->gameNetworkState == GAME_NETWORK_FAILURE) &&
(networkGlobals->timeout > 0))
return TRUE;
return FALSE;
}
return TRUE;
}
BOOLEAN NSGS_SendHighScore(const char * who, unsigned long score)
{
uint16_t cycleCount = 0;
if (strlen(who) != 3)
return FALSE;
networkGlobals->hasHighScoreToSend = TRUE;
memcpy(networkGlobals->setHighScoreRequest.setHighScoreRequest.who, who, sizeof(networkGlobals->setHighScoreRequest.setHighScoreRequest.who));
networkGlobals->setHighScoreRequest.setHighScoreRequest.score = score;
if (networkGlobals->gameNetworkState < GAME_NETWORK_TCP_UNCONNECTED)
networkGlobals->gameNetworkState = GAME_NETWORK_TCP_UNCONNECTED;
do {
networkGlobals->initParams.waitForVbl();
NSGS_PollNetwork();
cycleCount++;
if ((cycleCount & 0x7) == 0) {
switch (cycleCount & 0x18) {
case 0x00:
if (networkGlobals->initParams.uploadSpin != NULL)
networkGlobals->initParams.uploadSpin(0);
break;
case 0x08:
if (networkGlobals->initParams.uploadSpin != NULL)
networkGlobals->initParams.uploadSpin(1);
break;
case 0x10:
if (networkGlobals->initParams.uploadSpin != NULL)
networkGlobals->initParams.uploadSpin(2);
break;
case 0x18:
if (networkGlobals->initParams.uploadSpin != NULL)
networkGlobals->initParams.uploadSpin(3);
break;
}
}
} while (networkGlobals->gameNetworkState > GAME_NETWORK_TCP_UNCONNECTED);
if (networkGlobals->gameNetworkState != GAME_NETWORK_TCP_UNCONNECTED)
return FALSE;
if (networkGlobals->initParams.scorePosition != NULL)
networkGlobals->initParams.scorePosition(networkGlobals->setHighScoreResponse.position,
networkGlobals->setHighScoreResponse.numberOfScores);
return TRUE;
}

182
BuGS/netScores.h Normal file
View File

@ -0,0 +1,182 @@
/*
* netScores.h
* NetScoresGS
*
* Created by Jeremy Rand on 2021-05-23.
* Copyright © 2021 Jeremy Rand. All rights reserved.
*/
#ifndef _GUARD_PROJECTNetScoresGS_FILEnetScores_
#define _GUARD_PROJECTNetScoresGS_FILEnetScores_
#include <TYPES.h>
typedef enum tNSGSErrorType
{
NSGS_CONNECT_ERROR,
NSGS_LOOKUP_ERROR,
NSGS_SOCKET_ERROR,
NSGS_PROTOCOL_ERROR
} tNSGSErrorType;
typedef enum ttNSGSProtocolErrors {
NSGS_TCP_CONNECT_TIMEOUT_ERROR = 1,
NSGS_HELLO_TIMEOUT_ERROR,
NSGS_HELLO_TOO_BIG_ERROR,
NSGS_HELLO_UNEXPECTED_RESPONSE_ERROR,
NSGS_HIGH_SCORE_TIMEOUT_ERROR,
NSGS_HIGH_SCORE_TOO_BIG_ERROR,
NSGS_HIGH_SCORE_UNEXPECTED_RESPONSE_ERROR,
NSGS_SET_SCORE_TIMEOUT_ERROR,
NSGS_SET_SCORE_TOO_BIG_ERROR,
NSGS_SET_SCORE_UNEXPECTED_RESPONSE_ERROR,
NSGS_SET_SCORE_FAILED_ERROR,
} ttNSGSProtocolErrors;
typedef struct tNSGSHighScore
{
char scoreText[10];
char who[4];
unsigned long score;
} tNSGSHighScore;
typedef struct tNSGSHighScores
{
tNSGSHighScore score[10];
} tNSGSHighScores;
typedef struct tNSGSHighScoreInitParams
{
/* This structure is initialized by the game and passed by pointer to the high score code. It consists of
a series of data members and a series of callbacks which are provided by the game.
*/
/* This is the memory manager ID of the game. */
unsigned int userId;
/* This is a Pascal string (not a C string) of the hostname of the score server to connect to. */
const char * scoreServer;
/* This is the TCP port number of the score server to connect to. */
unsigned int scorePort;
/* These two 32-bit values are the shared secrets used by the connect between the game and the score server
which is used to try to reduce the amount of game score hacking.
*/
unsigned long secret1;
unsigned long secret2;
/* Each of these timeouts are measured in poll periods. So, if you call NSGS_PollNetwork() every 60th of a
second, then you would use 60 to set the timeout to 1 second. If these are zero, then the "good value"
in the comment will be used as a default.
The shutdown timeout controls how long we wait for a clean TCP disconnection before forcing an abort of
the connection. Two seconds is a good value for this timeout. */
unsigned int shutdownTimeout;
/* The connect timeout is the amount of time we wait for a TCP connection to come up before declaring a
timeout protocol error. Eight seconds is a good value for this timeout. */
unsigned int connectTimeout;
/* The read timeout is the amount of time we wait for a response from the server after we have made a
request of it, whether that is getting the high score list or setting a new high score. Five seconds
is a good value for this timeout. */
unsigned int readTimeout;
/* The retry timeout is the amount of time we wait in an error state before retrying. This only happens
for "soft" errors where a retry is worthwhile. Three minutes is a good value for this timeout. */
unsigned int retryTimeout;
/* This function should display a message to the user that the network is being brought up and they should
be patient when the argument is TRUE and when the argument is FALSE, it should clear that message. This
function shouldn't block and just put something on the screen to say that the connection is being brought
up or clear that message. It is called sometimes (rarely) by NSGS_PollNetwork(). It is called with argument
TRUE and will always be called with FALSE sometime after that.
*/
void (*displayConnectionString)(BOOLEAN display);
/* This function should wait for the next VBL and is used to poll the network and limit upload time for a
high score.
*/
void (*waitForVbl)(void);
/* This argument iterates over 0, 1, 2, 3 and then back to 0, 1, 2, etc and is intended to show some kind
of spinner to the user while uploading a high score to the server. This function shouldn't block for
any real amount of time and just cause something on the screen to change to make sure the player doesn't
think something has hung while the upload is in progress. It is called when NSGS_SendHighScore() is called
by the game.
*/
void (*uploadSpin)(int);
/* When a score is successfully uploaded to the server, this function will be called with the position
of this player's score among the total number of scores recorded for this game. This information
should be displayed to the user. The function can block while this information is being displayed
and that message should be cleaned up before the function returns to the caller. This function is
called by NSGS_SendHighScore().
*/
void (*scorePosition)(unsigned int position, unsigned int numberOfScores);
/* This function is only called from NSGS_PollNetwork() when something unexpected has occurred.
The meaning of the error code depends on the error type. For a protocol error, the error code
is one of tNSGSProtocolErrors. For other error codes, they come from Marinetti error codes.
if the error code > $8000, then the error code is the socket state or-ed with $8000. */
void (*displayError)(tNSGSErrorType errorType, unsigned int errorCode);
/* This function is only called from NSGS_PollNetwork() when new scores have been downloaded.
The scores passed should be copied because the pointer is not guaranteed to be valid after
the callback returns. */
void (*setHighScores)(const tNSGSHighScores * scores);
/* This function is called to ask if it is time to refresh the global high score list. This should
be based on watching the elapsed time and it should return true if say 5 minutes has elapsed
since high scores have been updated. */
BOOLEAN (*shouldRefreshHighScores)(void);
} tNSGSHighScoreInitParams;
/* Call this function once at launch. The pointer to the parameters is copied so the structure does not need
to remain valid after the call to this function.
*/
extern void NSGS_InitNetwork(tNSGSHighScoreInitParams * params);
/* Call this when a game is about to start. It will interrupt any network operation in progress and get ready for
a quiet period where polling will stop until the game is over. That way, all CPU time can be focused on the game.
This function may call the waitForVbl() callback.
*/
extern void NSGS_DisconnectNetwork(void);
/* Call this every frame refresh period when a game is _not_ in progress. This does any network operations required
to download high scores. During this function call, the displayConnectionString() callback may be called.
*/
extern void NSGS_PollNetwork(void);
/* Call this function once when the game is quitting. */
extern void NSGS_ShutdownNetwork(void);
/* Call this function when the player has a high score that should be recorded online. This function will return
TRUE if the network is up and a high score can be sent and if so, the game should call NSGS_SendHighScore() to send
the high score. If FALSE is returned, then the user is playing while offline and no attempt should be made to
send the high score.
*/
extern BOOLEAN NSGS_CanSendHighScore(void);
/* Assuming NSGS_CanSendHighScore() returned TRUE, the game can call this function to actually try to send the high score
to the server. If this function returns TRUE, then the score was successfully sent to the server. If FALSE
is returned, then an error has occurred. The game can offer the user the option to retry to the upload of the
score and if the user would like to retry, just call NSGS_SendHighScore() again. During this function call, the
waitForVbl(), uploadSpin() and scorePosition() callbacks may be called.
TODO - Pass the score as an argument rather than through globals.
*/
extern BOOLEAN NSGS_SendHighScore(const char * who, unsigned long score);
#endif /* define _GUARD_PROJECTNetScoresGS_FILEnetScores_ */

View File

@ -8,6 +8,7 @@
case on
datachk off
mcopy score.macros
keep score
@ -15,78 +16,6 @@ score start
using globalData
using tileData
updateHighScore entry
ldx #HIGH_SCORE_ONES_OFFSET
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+9
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+8
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+7
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+6
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+5
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+4
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+3
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+2
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+1
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
rtl
scoreStartGame entry
stz gameScore
@ -380,6 +309,90 @@ scoreAddOneThousand_skipZeroHundreds anop
jmp scoreAddOneToTile
end
scoreExtras start extraSeg
using globalData
using tileData
using playerData
updateHighScore entry
ldx #HIGH_SCORE_ONES_OFFSET
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+9
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+8
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+7
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+6
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+5
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+4
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+3
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+2
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+1
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
dex
dex
lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET
jsl asciiToTileType
sta tileType,x
_dirtyNonGameTile
rtl
checkHighScore entry
ldy #0
checkHighScore_loop anop
@ -427,7 +440,7 @@ checkHighScore_doneCopy anop
sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_OFFSET,y
lda gameScore+2,x
sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_OFFSET+2,y
lda playerNum
cmp #PLAYER_ONE
beq checkHighScore_isPlayer1
@ -439,61 +452,61 @@ checkHighScore_saveHighScore anop
lda tileType,x
jsl tileTypeToAscii
sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET,y
inx
inx
lda tileType,x
jsl tileTypeToAscii
sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+1,y
inx
inx
lda tileType,x
jsl tileTypeToAscii
sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+2,y
inx
inx
lda tileType,x
jsl tileTypeToAscii
sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+3,y
inx
inx
lda tileType,x
jsl tileTypeToAscii
sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+4,y
inx
inx
lda tileType,x
jsl tileTypeToAscii
sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+5,y
inx
inx
lda tileType,x
jsl tileTypeToAscii
sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+6,y
inx
inx
lda tileType,x
jsl tileTypeToAscii
sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+7,y
inx
inx
lda tileType,x
jsl tileTypeToAscii
sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+8,y
inx
inx
lda tileType,x
jsl tileTypeToAscii
sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_TEXT_OFFSET+9,y
ldx #GAME_NUM_TILES_WIDE*4+2
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
@ -516,7 +529,7 @@ checkHighScore_saveHighScore anop
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
ldx #GAME_NUM_TILES_WIDE*6+2
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
@ -539,7 +552,7 @@ checkHighScore_saveHighScore anop
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
ldx #GAME_NUM_TILES_WIDE*8+2
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
@ -568,7 +581,7 @@ checkHighScore_donePrintingPlayer anop
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
ldx #GAME_NUM_TILES_WIDE*10+2
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
@ -591,7 +604,7 @@ checkHighScore_donePrintingPlayer anop
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
ldx #GAME_NUM_TILES_WIDE*12+2
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
@ -614,7 +627,7 @@ checkHighScore_donePrintingPlayer anop
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
ldx #GAME_NUM_TILES_WIDE*14+2
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_LETTER_E
@ -637,7 +650,7 @@ checkHighScore_donePrintingPlayer anop
_overwriteGameTile TILE_LETTER_L
_overwriteGameTile TILE_LETTER_S
_overwriteGameTile TILE_EMPTY
ldx #GAME_NUM_TILES_WIDE*16+2
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
@ -660,7 +673,7 @@ checkHighScore_donePrintingPlayer anop
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
ldx #GAME_NUM_TILES_WIDE*18+2
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
@ -683,7 +696,7 @@ checkHighScore_donePrintingPlayer anop
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
ldx #GAME_NUM_TILES_WIDE*20+2
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
@ -706,7 +719,7 @@ checkHighScore_donePrintingPlayer anop
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
ldx #GAME_NUM_TILES_WIDE*18+20
checkHighScore_nextKey anop
jsl waitForKey
@ -731,14 +744,14 @@ checkHighScore_skipToUpperCase anop
blt checkHighScore_isInvalid
cmp #'Z'+1
bge checkHighScore_isInvalid
checkHighScore_isValid anop
cpx #GAME_NUM_TILES_WIDE*18+26
bge checkHighScore_isInvalid
sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_WHO_OFFSET,y
iny
jsl asciiToTileType
jsr overwriteGameTile
jsl overwriteGameTile
_overwriteGameTile TILE_SOLID2
dex
dex
@ -757,21 +770,197 @@ checkHighScore_backspace anop
dex
dex
bra checkHighScore_nextKey
checkHighScore_isEnter anop
cpx #GAME_NUM_TILES_WIDE*18+26
blt checkHighScore_isInvalid
bra checkHighScore_doneInitials
checkHighScore_isInvalid anop
bra checkHighScore_nextKey
checkHighScore_doneInitials anop
_overwriteGameTile TILE_EMPTY
jsl saveSettings
jsl NSGS_CanSendHighScore
bne checkHighScore_retry
brl checkHighScore_doneNetwork
checkHighScore_retry anop
ldx #GAME_NUM_TILES_WIDE*22+2
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_LETTER_U
_overwriteGameTile TILE_LETTER_P
_overwriteGameTile TILE_LETTER_L
_overwriteGameTile TILE_LETTER_O
_overwriteGameTile TILE_LETTER_A
_overwriteGameTile TILE_LETTER_D
_overwriteGameTile TILE_LETTER_I
_overwriteGameTile TILE_LETTER_N
_overwriteGameTile TILE_LETTER_G
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_LETTER_S
_overwriteGameTile TILE_LETTER_C
_overwriteGameTile TILE_LETTER_O
_overwriteGameTile TILE_LETTER_R
_overwriteGameTile TILE_LETTER_E
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_SOLID1
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
jsl sendHighScore
beq checkHighScore_retryPrompt
brl checkHighScore_doneNetwork
checkHighScore_retryPrompt anop
ldx #GAME_NUM_TILES_WIDE*22+2
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_LETTER_S
_overwriteGameTile TILE_LETTER_C
_overwriteGameTile TILE_LETTER_O
_overwriteGameTile TILE_LETTER_R
_overwriteGameTile TILE_LETTER_E
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_LETTER_U
_overwriteGameTile TILE_LETTER_P
_overwriteGameTile TILE_LETTER_L
_overwriteGameTile TILE_LETTER_O
_overwriteGameTile TILE_LETTER_A
_overwriteGameTile TILE_LETTER_D
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_LETTER_F
_overwriteGameTile TILE_LETTER_A
_overwriteGameTile TILE_LETTER_I
_overwriteGameTile TILE_LETTER_L
_overwriteGameTile TILE_LETTER_E
_overwriteGameTile TILE_LETTER_D
_overwriteGameTile TILE_EMPTY
ldx #GAME_NUM_TILES_WIDE*24+2
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_LETTER_R
_overwriteGameTile TILE_LETTER_E
_overwriteGameTile TILE_LETTER_T
_overwriteGameTile TILE_LETTER_R
_overwriteGameTile TILE_LETTER_Y
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_LETTER_U
_overwriteGameTile TILE_LETTER_P
_overwriteGameTile TILE_LETTER_L
_overwriteGameTile TILE_LETTER_O
_overwriteGameTile TILE_LETTER_A
_overwriteGameTile TILE_LETTER_D
_overwriteGameTile TILE_SYMBOL_COLON
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_LETTER_Y
_overwriteGameTile TILE_SYMBOL_COLON
_overwriteGameTile TILE_LETTER_N
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_SOLID2
_overwriteGameTile TILE_EMPTY
jsl waitForKey
and #$df
cmp #'N'
beq checkHighScore_doneNetwork
cmp #'Y'
beq checkHighScore_doRetry
brl checkHighScore_retryPrompt
checkHighScore_doneNetwork anop
jsl updateHighScore
sec
rtl
checkHighScore_doRetry anop
ldx #GAME_NUM_TILES_WIDE*24+2
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
brl checkHighScore_retry
uploadSpin1 entry
ldx #GAME_NUM_TILES_WIDE*22+36
_overwriteGameTile TILE_SOLID1
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
rtl
uploadSpin2 entry
ldx #GAME_NUM_TILES_WIDE*22+36
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_SOLID1
_overwriteGameTile TILE_EMPTY
rtl
uploadSpin3 entry
ldx #GAME_NUM_TILES_WIDE*22+36
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_SOLID1
rtl
scoreIndex dc i2'0'
end
displayScorePosition entry
ldx #GAME_NUM_TILES_WIDE*22
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_LETTER_Y
_overwriteGameTile TILE_LETTER_O
_overwriteGameTile TILE_LETTER_U
_overwriteGameTile TILE_LETTER_R
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_LETTER_S
_overwriteGameTile TILE_LETTER_C
_overwriteGameTile TILE_LETTER_O
_overwriteGameTile TILE_LETTER_R
_overwriteGameTile TILE_LETTER_E
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_LETTER_G
_overwriteGameTile TILE_LETTER_L
_overwriteGameTile TILE_LETTER_O
_overwriteGameTile TILE_LETTER_B
_overwriteGameTile TILE_LETTER_A
_overwriteGameTile TILE_LETTER_L
_overwriteGameTile TILE_LETTER_L
_overwriteGameTile TILE_LETTER_Y
_overwriteGameTile TILE_EMPTY
_overwriteGameTile TILE_LETTER_I
_overwriteGameTile TILE_LETTER_S
_overwriteGameTile TILE_SYMBOL_COLON
_overwriteGameTile TILE_EMPTY
ldx #GAME_NUM_TILES_WIDE*24
ldy #0
displayScorePosition_loop anop
lda globalScoreInfo,y
and #$ff
beq displayScorePosition_done
jsl asciiToTileType
jsl overwriteGameTile
iny
bra displayScorePosition_loop
displayScorePosition_done anop
rtl
end

213
BuGS/settings.c Normal file
View File

@ -0,0 +1,213 @@
/*
* settings.c
* BuGS
*
* Created by Jeremy Rand on 2021-06-03.
* Copyright © 2021 Jeremy Rand. All rights reserved.
*/
#include <string.h>
#include <gsos.h>
#include <orca.h>
#include <Memory.h>
#include "game.h"
#include "netScores.h"
#include "settings.h"
#include "tileData.h"
// Defines
#define SETTINGS_FILENAME "@:BuGS.settings"
/* Typedefs */
typedef struct tSettingsData
{
char magic[4];
int version;
Boolean stereoCorrect;
tNSGSHighScore highScores[NUM_HIGH_SCORES];
} tSettingsData;
// Globals
tSettingsData settings = {
{ 'B', 'u', 'G', 'S' },
0,
TRUE,
{
{ { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '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
#if __ORCAC__
segment "settings";
#endif
const tNSGSHighScore * getHighScore(unsigned int index)
{
if (index >= NUM_HIGH_SCORES)
return NULL;
return &(settings.highScores[index]);
}
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.stereoCorrect)
{
swapStereoChannels();
}
}
return success;
}
void swapStereoSettings(void)
{
swapStereoChannels();
settings.stereoCorrect = !settings.stereoCorrect;
saveSettings();
}

26
BuGS/settings.h Normal file
View File

@ -0,0 +1,26 @@
/*
* 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 <TYPES.h>
typedef struct tNSGSHighScore tNSGSHighScore;
extern unsigned int myUserId;
extern void saveSettings(void);
extern BOOLEAN loadSettings(void);
extern void swapStereoSettings(void);
extern const tNSGSHighScore * getHighScore(unsigned int index);
#endif /* define _GUARD_PROJECTBuGS_FILEsettings_ */

View File

@ -10,3 +10,9 @@ This game runs at 60 frames per second on a standard Apple //gs without an accel
[YouTube video of game on real HW](https://youtu.be/QcmpkEUQznM)
[Video of game run on mame](https://www.rand-emonium.com/wp-content/uploads/2021/02/BuGS_video.mp4)
[Download a disk image](https://github.com/jeremysrand/BuGS/releases/download/2.0.1/BuGS_201.2mg)
[Download a SHK archive](https://github.com/jeremysrand/BuGS/releases/download/2.0.1/BuGS201.shk)
[BuGS Readme from the SW distribution](/BuGS/Read.Me.md)

View File

@ -1,5 +1,7 @@
TODO
=======
* Look at supporting a global high score list for systems with an Internet connection
* Look at supporting System 5 which may allow a single disk bootable image. System 6 is probably too big to ever fit on a 800k floppy with the game.
* I tested it and BuGS crashes on launch if run from System 5.0.4. Something is assuming System 6 or better.
* Get the old boot disks from https://ftp.apple.asimov.net/images/gs/os/gsos/
* More ideas?