/* decodeCaves.c */ /* This program decodes the Boulder Dash I cave data from the Commodore 64 implementation into human-readable instructions (with minimal HTML formatting) and an ASCII diagram. Boulder Dash I by: Peter Liepa This program written by: Jeff Bevis Peter Broadribb 28 Aug 1995 01 Oct 1995: Fixed bug in NextRandom(); I have now checked the output of NextRandom() against the output of the original 6510 code, and they appear to be generating the same numbers. However, the cave data, although _almost_ correct, doesn't seem exactly right. I'm puzzled. [PB] */ #include #include /* #define NDEBUG /* Uncomment if you don't want assertion */ #include /* **************************************** */ /* Compilation flags */ #define INCLUDE_HUMAN_READABLE 1 /* To include human-readable stuff in the output */ /* **************************************** */ /* Types */ typedef unsigned char UBYTE; typedef short objectType; /* **************************************** */ /* Global data */ /* Coded cave data */ #include "cavedata.h" /* Creature code conversion table */ /* Converts the C64 BoulderDash codes into the codes used by Jeff Bevis's Amiga implementation of BoulderDash. */ UBYTE creatureCode[64]= { ' ', '.', 'w', 'm', 'P', 'P', '?', 'W', 'q', 'o', 'Q', 'O', 'q', 'o', 'Q', 'O', 'r', 'r', 'r', 'r', 'd', 'd', 'd', 'd', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '3', 'X', '5', '6', '?', '?', '?', '?', '?', '?', '?', '?', 'B', 'c', 'b', 'C', 'B', 'c', 'b', 'C', '7', '8', 'a', 'a', '?', '?', '?', '?' }; /* Human-readable names for the 64 different C64 object codes */ char *objects[]= { "zSpace", "zDirt", "zBrick", "zMagic", "zPreOut", "zOutBox", "*", "zSteel", "zFFly1", "zFFly2", "zFFly3", "zFFly4", "zFFly1M", "zFFly2M", "zFFly3M", "zFFly4M", "zBouS", "zBouSM", "zBouF", "zBouFM", "zDiaS", "zDiaSM", "zDiaF", "zDiaFM", "", "", "", "zExp1S", "zExp2S", "zExp3S", "zExp4S", "zExp5S", "zExp1D", "zExp2D", "zExp3D", "zExp4D", "zExp5D", "zPRFd1", "zPRFd2", "zPRFd3", "zPRFd4", "", "", "", "", "", "", "", "zBFly1", "zBFly2", "zBFly3", "zBFly4", "zBFly1M", "zBFly2M", "zBFly3M", "zBFly4M", "zRFd", "zRFdM", "zAmoe", "zAmoeM", "", "", "*", "*" }; /* DrawLine data */ /* When drawing lines, you can draw in all eight directions. This table gives the offsets needed to move in each of the 8 directions. */ int ldx[8]={ 0, 1, 1, 1, 0, -1, -1, -1}; int ldy[8]={-1, -1, 0, 1, 1, 1, 0, -1}; /* Human-readable versions of the 8 directions */ char *directions[]= { "up","up/right","right","down/right","down","down/left","left","up/left" }; /* C64 color names */ char *colors[]= { "Black", "White", "Red", "Cyan", "Purple", "Green", "Blue", "Yellow", "Orange", "Brown", "Light red", "Gray 1", "Gray 2", "Light green", "Light blue", "Gray 3" }; char *caveNames[]= { "Intro", "Rooms", "Maze", "Butterflies", "Guards", "Firefly dens", "Amoeba", "Enchanted wall", "Greed", "Tracks", "Crowd", "Walls", "Apocalypse", "Zigzag", "Funnel", "Enchanted boxes" }; char *caveDescriptions[]= { "Pick up jewels and exit before time is up", "Pick up jewels, but you must move boulders to get all jewels", "Pick up jewels. You must get every jewel to exit", "Drop boulders on butterflies to create jewels", "The jewels are there for grapping, but they are guarded by the deadly fireflies", "Each firefly is guarding a jewel", "Surround the amoeba with boulders, so it can't grow anymore. Pick up jewels that are created when it suffocates", "Activate the enchanted wall and create as many jewels as you can", "You have to get a lot of jewels here, lucky there are so many", "Get the jewels, avoid the fireflies", "You must move a lot of boulders around in some tight spaces", "You must blast hrough walls to get at some of the jewels. Drop a boulder on a firefly at the right time and place to do this", "Bring the butterflies and amoeba together and watch the jewels fly", "Magically transform the butterflies into jewels, but don't waste any boulders and watch out the fireflies", "There is an enchanted wall at the bottom of the rock tunnel", "The top of each square room is an enchanted wall, but you'll have to blast your way inside" }; /* Output file */ FILE *theFile; /* Global storage for the decoded cave data */ UBYTE caveData[40][24]; /* **************************************** */ /* Prototypes */ void main(void); void DecodeCave(UBYTE *aCaveData); void StoreObject(int x, int y, objectType anObject); void DrawLine(objectType anObject, int x, int y, int aLength, int aDirection); void DrawFilledRect(objectType anObject, int x, int y, int aWidth, int aHeight, objectType aFillObject); void DrawRect(objectType anObject, int x, int y, int aWidth, int aHeight); void NextRandom(int *RandSeed1, int *RandSeed2); /* **************************************** */ void main(void) { #if 0 int RandSeed1, RandSeed2; int count; theFile = fopen("random-c","w"); RandSeed1 = 0; RandSeed2 = 10; for (count = 0; count<1000; count++) { fprintf(theFile, "%d %d\n", RandSeed1, RandSeed2); NextRandom(&RandSeed1, &RandSeed2); } fclose(theFile); return; #endif theFile = fopen("decodedCaves.html","w"); fprintf(theFile, "\n"); fprintf(theFile, "BoulderDash Decoded cave data\n"); fprintf(theFile, "\n"); fprintf(theFile, "\n"); fprintf(theFile, "\n"); fprintf(theFile, "

BoulderDash Decoded cave data

\n"); fprintf(theFile, "\n"); fprintf(theFile, "\n"); fprintf(theFile, "\n"); fprintf(theFile, "Decoded cave data in human-readable text, including ascii representation. Warning: There may be mistakes in the decode. The C source code to generate the following decodes are available in the files "); fprintf(theFile, "decodecaves.c and cavedata.h). Note that when decoding the cave, the random objects are placed first, then a bounding rectangle of steel wall "); fprintf(theFile, "is put in (effectively an implicit DrawRect(0,2,40,22) before the cave data), then the cave data is decoded. For each cave, a graphical representation is given for difficulty level 1. The characters "); fprintf(theFile, "used in the graphical representation are the ones used by a implementation of BoulderDash on the Amiga.\n"); DecodeCave(cave1); DecodeCave(cave2); DecodeCave(cave3); DecodeCave(cave4); DecodeCave(cave5); DecodeCave(cave6); DecodeCave(cave7); DecodeCave(cave8); DecodeCave(cave9); DecodeCave(cave10); DecodeCave(cave11); DecodeCave(cave12); DecodeCave(cave13); DecodeCave(cave14); DecodeCave(cave15); DecodeCave(cave16); DecodeCave(cave17); DecodeCave(cave18); DecodeCave(cave19); DecodeCave(cave20); fprintf(theFile, "\n"); fprintf(theFile, "\n"); fprintf(theFile, "
\n"); fprintf(theFile, "
Web page design by Peter Broadribb
\n"); fprintf(theFile, "\n"); fprintf(theFile, "\n"); fclose(theFile); } /* **************************************** */ void DecodeCave(UBYTE *aCaveData) { int RandSeed1, RandSeed2; int theWidth, theHeight, theFill, theLength, theDirection; int x, y; int caveDataIndex; objectType theObject; int theCode; RandSeed1 = 0; RandSeed2 = aCaveData[4]; /* Output human-readable version of the first 0x20 bytes of cave data. */ #if INCLUDE_HUMAN_READABLE if (aCaveData[0] <= 16) { fprintf(theFile,"\n\n

Cave %d (Cave %c: %s)

\n%s\n",aCaveData[0],aCaveData[0]+'A'-1,aCaveData[0]+'A'-1,caveNames[aCaveData[0]-1],caveDescriptions[aCaveData[0]-1]); } else { fprintf(theFile,"\n\n

Cave %d (Intermission %d)

\n",aCaveData[0],aCaveData[0]-16,aCaveData[0]-16); } fprintf(theFile,"
%.2X                  Cave %.2d\n",aCaveData[0],aCaveData[0]);
        fprintf(theFile,"%.2X                  Magic wall/amoeba slow growth for: %d seconds\n",aCaveData[1],aCaveData[1]);
        fprintf(theFile,"%.2X                  Diamonds worth: %d points\n",aCaveData[2],aCaveData[2]);
        fprintf(theFile,"%.2X                  Extra diamonds worth: %d points\n",aCaveData[3],aCaveData[3]);
        fprintf(theFile,"%.2X %.2X %.2X %.2X %.2X      Randomiser seed values for difficulty levels 1-5\n",aCaveData[4],aCaveData[5],aCaveData[6],aCaveData[7],aCaveData[8]);
        fprintf(theFile,"%.2X %.2X %.2X %.2X %.2X      Diamonds needed: %d, %d, %d, %d, %d (for difficulty levels 1-5)\n",aCaveData[9],aCaveData[10],aCaveData[11],aCaveData[12],aCaveData[13],aCaveData[9],aCaveData[10],aCaveData[11],aCaveData[12],aCaveData[13]);
        fprintf(theFile,"%.2X %.2X %.2X %.2X %.2X      Cave time: %d, %d, %d, %d, %d seconds\n",aCaveData[14],aCaveData[15],aCaveData[16],aCaveData[17],aCaveData[18],aCaveData[14],aCaveData[15],aCaveData[16],aCaveData[17],aCaveData[18]);
        fprintf(theFile,"%.2X                  Background color 1: %s\n",aCaveData[19],colors[aCaveData[19]]);
        fprintf(theFile,"%.2X                  Background color 2: %s\n",aCaveData[20],colors[aCaveData[20]]);
        fprintf(theFile,"%.2X                  Foreground color: %s\n",aCaveData[21],colors[aCaveData[21]]);
        fprintf(theFile,"%.2X %.2X               Unused\n",aCaveData[22],aCaveData[23]);
        fprintf(theFile,"%.2X %.2X %.2X %.2X         Random objects:\n",aCaveData[24],aCaveData[25],aCaveData[26],aCaveData[27]);
        if (aCaveData[28]) {
            fprintf(theFile,"%.2X %.2X %.2X %.2X             %-7s: %3d/256 = %2d%%\n",aCaveData[28],aCaveData[29],aCaveData[30],aCaveData[31], objects[aCaveData[24]],aCaveData[28],(int)aCaveData[28]*100/256);
        } else {
            fprintf(theFile,"%.2X %.2X %.2X %.2X             first code unused (0%%)\n",aCaveData[28],aCaveData[29],aCaveData[30],aCaveData[31]);
        }
        if (aCaveData[29]) {
            fprintf(theFile,"                        %-7s: %3d/256 = %2d%%\n", objects[aCaveData[25]],aCaveData[29],(int)aCaveData[29]*100/256);
        } else {
            fprintf(theFile,"                        second code unused (0%%)\n");
        }
        if (aCaveData[30]) {
            fprintf(theFile,"                        %-7s: %3d/256 = %2d%%\n", objects[aCaveData[26]],aCaveData[30],(int)aCaveData[30]*100/256);
        } else {
            fprintf(theFile,"                        third code unused (0%%)\n");
        }
        if (aCaveData[31]) {
            fprintf(theFile,"                        %-7s: %3d/256 = %2d%%\n", objects[aCaveData[27]],aCaveData[31],(int)aCaveData[31]*100/256);
        } else {
            fprintf(theFile,"                        fourth code unused (0%%)\n");
        }
        fprintf(theFile,"Cave layout:\n");
    #endif

/* Clear out the cave data to a null value */
    for(x = 0; x < 40; x++) {
        for (y = 0; y <= 23; y++) {
            StoreObject(x, y, 0x07);
        }
    }

/* Decode the random cave data */
    for(y = 3; y <= 23; y++) {
        for(x = 0; x <= 39; x++) {
            theObject = 1;  /* Dirt */
            NextRandom(&RandSeed1, &RandSeed2);
            for (caveDataIndex = 0; caveDataIndex <= 3; caveDataIndex++) {
                if (RandSeed1 < aCaveData[0x1C + caveDataIndex]) {
                    theObject = aCaveData[0x18 + caveDataIndex];
                }
            }
            StoreObject(x, y, theObject);
        }     
    }  

/* Decode the explicit cave data */
    for (caveDataIndex = 0x20; aCaveData[caveDataIndex] != 0xFF; caveDataIndex++) {
        theCode = aCaveData[caveDataIndex];
        theObject = theCode & 0x3F;

        switch(3 & (aCaveData[caveDataIndex] >> 6)) {
        case 0: /* PLOT */
            x = aCaveData[++caveDataIndex];
            y = aCaveData[++caveDataIndex];
            #if INCLUDE_HUMAN_READABLE
                fprintf(theFile,"%.2X %.2X %.2X            StoreChar %s at (%2d,%2d)\n",theCode,x,y,objects[theCode & 0x3f],x,y);
            #endif
            StoreObject(x, y, theObject);
            break;

        case 1: /* LINE */
            x = aCaveData[++caveDataIndex];
            y = aCaveData[++caveDataIndex];
            theLength = aCaveData[++caveDataIndex];
            theDirection = aCaveData[++caveDataIndex];
            #if INCLUDE_HUMAN_READABLE
                fprintf(theFile,"%.2X %.2X %.2X %.2X %.2X      Line of %s from (%2d,%2d); length = %d; direction = %s\n",theCode,x,y,theLength,theDirection,objects[theCode & 0x3f],x,y,theLength,directions[theDirection]);
            #endif
            DrawLine(theObject, x, y, theLength, theDirection);
            break;

        case 2: /* FILLED RECTANGLE */
            x = aCaveData[++caveDataIndex];
            y = aCaveData[++caveDataIndex];
            theWidth = aCaveData[++caveDataIndex];
            theHeight = aCaveData[++caveDataIndex];
            theFill = aCaveData[++caveDataIndex];
            #if INCLUDE_HUMAN_READABLE
                fprintf(theFile,"%.2X %.2X %.2X %.2X %.2X %.2X   FilledRect of %s from (%2d,%2d); length = %d; height = %d; fill = %s\n",theCode,x,y,theWidth,theHeight,aCaveData[caveDataIndex],objects[theCode & 0x3f],x,y,theWidth,theHeight,objects[aCaveData[caveDataIndex]]);
            #endif
            DrawFilledRect(theObject, x, y, theWidth, theHeight, theFill);
            break;

        case 3: /* OPEN RECTANGLE */
            x = aCaveData[++caveDataIndex];
            y = aCaveData[++caveDataIndex];
            theWidth = aCaveData[++caveDataIndex];
            theHeight = aCaveData[++caveDataIndex];
            #if INCLUDE_HUMAN_READABLE
                fprintf(theFile,"%.2X %.2X %.2X %.2X %.2X      Rect of %s from (%2d,%2d); length = %d; height = %d\n",theCode,x,y,theWidth,theHeight,objects[theCode & 0x3f],x,y,theWidth,theHeight);
            #endif
            DrawRect(theObject, x, y, theWidth, theHeight);
            break;
        }
    }

/* SteelBounds */
    DrawRect(0x07, 0, 2, 40, 22);

/* Print out the resulting aCaveData */
    #if INCLUDE_HUMAN_READABLE
        fprintf(theFile,"FF                  End of cave data.");
    #endif
    fprintf(theFile,"\n\nMap #%d\n",aCaveData[0]);
    for (y = 2; y <= 23; y++) {
        fprintf(theFile, "%03d ", y);
        for(x = 0; x <= 39; x++) {
            fprintf(theFile, "%c", caveData[x][y]); 
        }
        if (y == 23) {
            fprintf(theFile, "
"); } fprintf(theFile, "\n"); } } /* **************************************** */ void StoreObject(int x, int y, objectType anObject) { assert(((x >= 0) && (x <= 39))); assert(((y >= 0) && (y <= 23))); assert(((anObject >= 0) && (anObject <= 63))); caveData[x][y] = creatureCode[anObject]; } /* **************************************** */ void DrawLine(objectType anObject, int x, int y, int aLength, int aDirection) { /* Local variables */ int counter; assert(((anObject >= 0) && (anObject <= 63))); assert(((x >= 0) && (x <= 39))); assert(((y >= 0) && (y <= 23))); assert(((aLength >= 1) && (aLength <= 40))); assert(((aDirection >= 0) && (aDirection <= 7))); for(counter = 1; counter <= aLength; counter++) { StoreObject(x, y, anObject); x += ldx[aDirection]; y += ldy[aDirection]; } } /* **************************************** */ void DrawFilledRect(objectType anObject, int x, int y, int aWidth, int aHeight, objectType aFillObject) { /* Local variables */ int counter1; int counter2; assert(((anObject >= 0) && (anObject <= 63))); assert(((x >= 0) && (x <= 39))); assert(((y >= 0) && (y <= 23))); assert(((aWidth >= 1) && (aWidth <= 40))); assert(((aHeight >= 1) && (aHeight <= 24))); assert(((aFillObject >= 0) && (aFillObject <= 63))); for(counter1 = 0; counter1 < aWidth; counter1++) { for(counter2 = 0; counter2 < aHeight; counter2++) { if ((counter2 == 0) || (counter2 == aHeight-1)) { StoreObject(x+counter1, y+counter2, anObject); } else { StoreObject(x+counter1, y+counter2, aFillObject); } } StoreObject(x+counter1, y, anObject); StoreObject(x+counter1, y+aHeight-1, anObject); } } /* **************************************** */ void DrawRect(objectType anObject, int x, int y, int aWidth, int aHeight) { /* Local variables */ int counter1; assert(((anObject >= 0) && (anObject <= 63))); assert(((x >= 0) && (x <= 39))); assert(((y >= 0) && (y <= 23))); assert(((aWidth >= 1) && (aWidth <= 40))); assert(((aHeight >= 1) && (aHeight <= 24))); for(counter1 = 0; counter1 < aWidth; counter1++) { StoreObject(x + counter1, y, anObject); StoreObject(x + counter1, y + aHeight-1, anObject); } for(counter1 = 0; counter1 < aHeight; counter1++) { StoreObject(x, y + counter1, anObject); StoreObject(x + aWidth-1, y + counter1, anObject); } } /* **************************************** */ void NextRandom(int *RandSeed1, int *RandSeed2) /* This is the mathematical random number generator from the Commodore 64 implementation of Boulder Dash I. The 6510 disassembly is given in comments, and the C translation follows. */ { short TempRand1; short TempRand2; short carry; short result; assert(((*RandSeed1 >= 0x00) && (*RandSeed1 <= 0xFF))); assert(((*RandSeed2 >= 0x00) && (*RandSeed2 <= 0xFF))); /* 7085 NextRandom.00 LDA RandSeed1 7087 ROR 7088 ROR 7089 AND #$80 708B STA TempRand1 Note: ROR on the 6510 works like this: 7-->6-->5-->4-->3-->2-->1-->0-->C ^ | |_______________________________| In other words, it's a nine-bit rotate. Thus it takes two RORs to shift the low bit (bit zero) into the high bit (bit 7). */ TempRand1 = (*RandSeed1 & 0x0001) * 0x0080; /* Bugfix! */ /* 708E LDA RandSeed2 7090 ROR 7091 AND #$7F 7093 STA TempRand2 */ TempRand2 = (*RandSeed2 >> 1) & 0x007F; /* 7096 LDA RandSeed2 7098 ROR 7099 ROR 709A AND #$80 709C CLC 709D ADC RandSeed2 */ result = (*RandSeed2) + (*RandSeed2 & 0x0001) * 0x0080; carry = (result > 0x00FF); result = result & 0x00FF; /* 709F ADC #$13 70A1 STA RandSeed2 */ result = result + carry + 0x13; carry = (result > 0x00FF); *RandSeed2 = result & 0x00FF; /* 70A3 LDA RandSeed1 70A5 ADC TempRand1 */ result = *RandSeed1 + carry + TempRand1; carry = (result > 0x00FF); result = result & 0x00FF; /* 70A8 ADC TempRand2 70AB STA RandSeed1 70AD RTS */ result = result + carry + TempRand2; *RandSeed1 = result & 0x00FF; assert(((*RandSeed1 >= 0x00) && (*RandSeed1 <= 0xFF))); assert(((*RandSeed2 >= 0x00) && (*RandSeed2 <= 0xFF))); } /* **************************************** */