#!/usr/bin/perl
#  genData.pl
#  BuGS
#
#  Created by Jeremy Rand on 2020-11-03.
#  Copyright © 2020 Jeremy Rand. All rights reserved.

use strict;
use integer;

# main


die "$0: Expected one argument pointing to the gen directory" if ($#ARGV != 0);

my $gGenDir = $ARGV[0];
our %gEquates;

# These are global equates which will also become defines in the C header file.  Also, these will drive the other
# data that is generated by this script.
$gEquates{"SCREEN_ADDRESS"} = 0x2000;
$gEquates{"SCREEN_PIXELS_WIDE"} = 320;
$gEquates{"SCREEN_PIXELS_TALL"} = 200;
$gEquates{"SCREEN_PIXELS_PER_BYTE"} = 2;
$gEquates{"SCREEN_BYTES_PER_ROW"} = $gEquates{"SCREEN_PIXELS_WIDE"} / $gEquates{"SCREEN_PIXELS_PER_BYTE"};

$gEquates{"TILE_PIXEL_WIDTH"} = 8;
$gEquates{"TILE_PIXEL_HEIGHT"} = 8;
$gEquates{"TILE_BYTE_WIDTH"} = $gEquates{"TILE_PIXEL_WIDTH"} / $gEquates{"SCREEN_PIXELS_PER_BYTE"};

$gEquates{"TOTAL_TILES_WIDE"} = $gEquates{"SCREEN_PIXELS_WIDE"} / $gEquates{"TILE_PIXEL_WIDTH"};
$gEquates{"TOTAL_TILES_TALL"} = $gEquates{"SCREEN_PIXELS_TALL"} / $gEquates{"TILE_PIXEL_HEIGHT"};
$gEquates{"TOTAL_NUM_TILES"} = $gEquates{"TOTAL_TILES_WIDE"} * $gEquates{"TOTAL_TILES_TALL"};

$gEquates{"GAME_NUM_TILES_WIDE"} = 25;
$gEquates{"GAME_NUM_TILES_TALL"} = $gEquates{"TOTAL_TILES_TALL"};
$gEquates{"NUM_GAME_TILES"} = $gEquates{"GAME_NUM_TILES_WIDE"} * $gEquates{"GAME_NUM_TILES_TALL"};

$gEquates{"SIZEOF_TILE_INFO"} = 2;

$gEquates{"RHS_NUM_TILES_WIDE"} = 2;
$gEquates{"NUM_RHS_NON_GAMES_TILES"} = $gEquates{"RHS_NUM_TILES_WIDE"} * $gEquates{"TOTAL_TILES_TALL"};
$gEquates{"RHS_FIRST_TILE"} = $gEquates{"NUM_GAME_TILES"};
$gEquates{"RHS_FIRST_TILE_OFFSET"} = $gEquates{"RHS_FIRST_TILE"} * $gEquates{"SIZEOF_TILE_INFO"};

$gEquates{"LHS_NUM_TILES_WIDE"} = $gEquates{"TOTAL_TILES_WIDE"} - $gEquates{"GAME_NUM_TILES_WIDE"} - $gEquates{"RHS_NUM_TILES_WIDE"};
$gEquates{"NUM_LHS_NON_GAMES_TILES"} = $gEquates{"LHS_NUM_TILES_WIDE"} * $gEquates{"TOTAL_TILES_TALL"};
$gEquates{"LHS_FIRST_TILE"} = $gEquates{"RHS_FIRST_TILE"} + $gEquates{"NUM_RHS_NON_GAMES_TILES"};
$gEquates{"LHS_FIRST_TILE_OFFSET"} = $gEquates{"LHS_FIRST_TILE"} * $gEquates{"SIZEOF_TILE_INFO"};

$gEquates{"NUM_NON_GAME_TILES"} = $gEquates{"NUM_RHS_NON_GAMES_TILES"} + $gEquates{"NUM_LHS_NON_GAMES_TILES"};

$gEquates{"SPIDER_MAX_NUM_POSSIBLE_ROWS"} = 10;
$gEquates{"SPIDER_STARTING_TOP_ROW"} = $gEquates{"GAME_NUM_TILES_TALL"} - $gEquates{"SPIDER_MAX_NUM_POSSIBLE_ROWS"};
$gEquates{"SPIDER_STARTING_TOP_ROW_OFFSET"} = $gEquates{"SPIDER_STARTING_TOP_ROW"} * $gEquates{"GAME_NUM_TILES_WIDE"} * $gEquates{"SIZEOF_TILE_INFO"};
$gEquates{"SPIDER_DISTANCE_900"} = $gEquates{"SCREEN_BYTES_PER_ROW"} * $gEquates{"TILE_PIXEL_HEIGHT"} * 2;
$gEquates{"SPIDER_DISTANCE_600"} = $gEquates{"SCREEN_BYTES_PER_ROW"} * $gEquates{"TILE_PIXEL_HEIGHT"} * 5;

$gEquates{"PLAYER_TILES_HIGH"} = 7;
$gEquates{"MOUSE_MAX_X"} = (($gEquates{"GAME_NUM_TILES_WIDE"} - 1) * $gEquates{"TILE_PIXEL_WIDTH"}) + 1;
$gEquates{"MOUSE_MAX_Y"} = (($gEquates{"PLAYER_TILES_HIGH"} - 1) * $gEquates{"TILE_PIXEL_HEIGHT"}) + 1;
$gEquates{"STARTING_MOUSE_X"} = $gEquates{"MOUSE_MAX_X"} / 2;
$gEquates{"STARTING_MOUSE_Y"} = $gEquates{"MOUSE_MAX_Y"} - 1;

$gEquates{"P1_SCORE_ONES_OFFSET"} = ($gEquates{"LHS_FIRST_TILE"} + ($gEquates{"LHS_NUM_TILES_WIDE"} * 6) + ($gEquates{"LHS_NUM_TILES_WIDE"} - 2)) * $gEquates{"SIZEOF_TILE_INFO"};
$gEquates{"P1_SCORE_TENS_OFFSET"} = $gEquates{"P1_SCORE_ONES_OFFSET"} - $gEquates{"SIZEOF_TILE_INFO"};
$gEquates{"P1_SCORE_HUNDREDS_OFFSET"} = $gEquates{"P1_SCORE_TENS_OFFSET"} - $gEquates{"SIZEOF_TILE_INFO"};
$gEquates{"P1_SCORE_THOUSANDS_OFFSET"} = $gEquates{"P1_SCORE_HUNDREDS_OFFSET"} - $gEquates{"SIZEOF_TILE_INFO"};
$gEquates{"P1_SCORE_FIRST_OFFSET"} = ($gEquates{"LHS_FIRST_TILE"} + ($gEquates{"LHS_NUM_TILES_WIDE"} * 6)) * $gEquates{"SIZEOF_TILE_INFO"};

$gEquates{"P1_LIVES_OFFSET"} = ($gEquates{"LHS_FIRST_TILE"} + ($gEquates{"LHS_NUM_TILES_WIDE"} * 9) + ($gEquates{"LHS_NUM_TILES_WIDE"} - 2)) * $gEquates{"SIZEOF_TILE_INFO"};

$gEquates{"P2_SCORE_ONES_OFFSET"} = ($gEquates{"LHS_FIRST_TILE"} + ($gEquates{"LHS_NUM_TILES_WIDE"} * 19) + ($gEquates{"LHS_NUM_TILES_WIDE"} - 2)) * $gEquates{"SIZEOF_TILE_INFO"};
$gEquates{"P2_SCORE_TENS_OFFSET"} = $gEquates{"P2_SCORE_ONES_OFFSET"} - $gEquates{"SIZEOF_TILE_INFO"};
$gEquates{"P2_SCORE_HUNDREDS_OFFSET"} = $gEquates{"P2_SCORE_TENS_OFFSET"} - $gEquates{"SIZEOF_TILE_INFO"};
$gEquates{"P2_SCORE_THOUSANDS_OFFSET"} = $gEquates{"P2_SCORE_HUNDREDS_OFFSET"} - $gEquates{"SIZEOF_TILE_INFO"};
$gEquates{"P2_SCORE_FIRST_OFFSET"} = ($gEquates{"LHS_FIRST_TILE"} + ($gEquates{"LHS_NUM_TILES_WIDE"} * 19)) * $gEquates{"SIZEOF_TILE_INFO"};

$gEquates{"P2_LIVES_OFFSET"} = ($gEquates{"LHS_FIRST_TILE"} + ($gEquates{"LHS_NUM_TILES_WIDE"} * 22) + ($gEquates{"LHS_NUM_TILES_WIDE"} - 2)) * $gEquates{"SIZEOF_TILE_INFO"};

$gEquates{"HIGH_SCORE_ONES_OFFSET"} = ($gEquates{"LHS_FIRST_TILE"} + ($gEquates{"LHS_NUM_TILES_WIDE"} * 13) + ($gEquates{"LHS_NUM_TILES_WIDE"} - 2)) * $gEquates{"SIZEOF_TILE_INFO"};

$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"};
our @gTileAbove = ("INVALID_TILE_NUM") x $gEquates{"TOTAL_NUM_TILES"};
our @gTileBelow = ("INVALID_TILE_NUM") x $gEquates{"TOTAL_NUM_TILES"};
our @gTileLeft = ("INVALID_TILE_NUM") x $gEquates{"TOTAL_NUM_TILES"};
our @gTileRight = ("INVALID_TILE_NUM") x $gEquates{"TOTAL_NUM_TILES"};
our @gTileBitOffset = (0) x $gEquates{"NUM_GAME_TILES"};
our @gTileBitMask = (0) x $gEquates{"NUM_GAME_TILES"};
our @gTileRightVolume = (0) x $gEquates{"TOTAL_NUM_TILES"};
our @gDirtyNonGameTiles = ("INVALID_TILE_NUM") x $gEquates{"NUM_NON_GAME_TILES"};
our $gNumDirtyNonGameTiles = 0;

our @gFleaFreqs = (0) x $gEquates{"NUM_FLEA_FREQS"};

our @gMouseYAddress = (0) x $gEquates{"MOUSE_MAX_Y"};
our @gMouseYTileAbove = (0) x $gEquates{"MOUSE_MAX_Y"};
our @gMouseYTileBelow = (0) x $gEquates{"MOUSE_MAX_Y"};
our @gMouseXTileLeft = (0) x $gEquates{"MOUSE_MAX_X"};
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) = @_;

    print $fh "\n";
    print $fh "\n";
    print $fh "$symbol   anop\n";
    
    for my $index (0 .. $#data)
    {
        print $fh "        dc i2'$data[$index]'\t; Index $index\n";
    }
}


sub gameXYToTileOffset
{
    my ($x, $y) = @_;
    
    return (($y * $gEquates{"GAME_NUM_TILES_WIDE"}) + $x) * $gEquates{"SIZEOF_TILE_INFO"};
}


sub rhsXYToTileNum
{
    my ($x, $y) = @_;
    
    return $gEquates{"RHS_FIRST_TILE"} + ($y * $gEquates{"RHS_NUM_TILES_WIDE"}) + $x;
}


sub rhsXYToTileOffset
{
    my ($x, $y) = @_;
    
    return rhsXYToTileNum($x, $y) * $gEquates{"SIZEOF_TILE_INFO"};
}


sub lhsXYToTileNum
{
    my ($x, $y) = @_;
    
    return $gEquates{"LHS_FIRST_TILE"} + ($y * $gEquates{"LHS_NUM_TILES_WIDE"}) + $x;
}


sub lhsXYToTileOffset
{
    my ($x, $y) = @_;
    
    return lhsXYToTileNum($x, $y) * $gEquates{"SIZEOF_TILE_INFO"};
}


sub addDirtyNonGameTile
{
    my ($tileNum) = @_;
    
    if ($gTileDirty[$tileNum] eq "TILE_STATE_CLEAN")
    {
        my $tileOffset = $tileNum * $gEquates{"SIZEOF_TILE_INFO"};
        my $dirtyIndex = $gNumDirtyNonGameTiles / $gEquates{"SIZEOF_TILE_INFO"};
        
        $gTileDirty[$tileNum] = "TILE_STATE_DIRTY";
        $gDirtyNonGameTiles[$dirtyIndex] = $tileOffset;
        $gNumDirtyNonGameTiles += $gEquates{"SIZEOF_TILE_INFO"};
    }
}


sub addLhsGameTile
{
    my ($x, $y, $tileType) = @_;
    my $tileNum = lhsXYToTileNum($x, $y);
    $gTileType[$tileNum] = $tileType;
    addDirtyNonGameTile($tileNum);
}


sub addRhsGameTile
{
    my ($x, $y, $tileType) = @_;
    my $tileNum = rhsXYToTileNum($x, $y);
    $gTileType[$tileNum] = $tileType;
    addDirtyNonGameTile($tileNum);
}


sub addLhsGameString
{
    my ($x, $y, $string) = @_;
    
    foreach my $char (split('', $string)) {
        if ($char =~ /^[0-9]$/)
        {
            addLhsGameTile($x, $y, "TILE_NUMBER_$char");
        }
        elsif ($char eq ":")
        {
            addLhsGameTile($x, $y, "TILE_SYMBOL_COLON");
        }
        elsif ($char ne " ")
        {
            addLhsGameTile($x, $y, "TILE_LETTER_$char");
        }
        $x++;
    }
}


sub screenAddressForTileAtXY
{
    my ($x, $y) = @_;
    
    return $gEquates{"SCREEN_ADDRESS"} + ($gEquates{"SCREEN_BYTES_PER_ROW"} * $y) + ($x / $gEquates{"SCREEN_PIXELS_PER_BYTE"}) + 3;
}


sub initTiles
{
    my($tileX, $tileY, $lastOffset);
    my $tileIndex = 0;
    my $bitOffset = 0;
    my $bitMask = 1;
    my $rhsTileIndex = $gEquates{"RHS_FIRST_TILE"};
    my $lhsTileIndex = $gEquates{"LHS_FIRST_TILE"};
    my $screenOffset = 0;
    
    for ($tileY = 0; $tileY < $gEquates{"TOTAL_TILES_TALL"}; $tileY++)
    {
        $screenOffset = ($tileY * $gEquates{"TILE_PIXEL_HEIGHT"} * $gEquates{"SCREEN_BYTES_PER_ROW"} / $gEquates{"SIZEOF_TILE_INFO"});
        $lastOffset = screenAddressForTileAtXY(0, $tileY * $gEquates{"TILE_PIXEL_HEIGHT"});
        
        for ($tileX = 0; $tileX < $gEquates{"LHS_NUM_TILES_WIDE"}; $tileX++)
        {
            $gTileScreenOffset[$lhsTileIndex] = $lastOffset;
            
            for (my $word = 0; $word < ($gEquates{"TILE_BYTE_WIDTH"} / $gEquates{"SIZEOF_TILE_INFO"}); $word++)
            {
                for (my $y = 0; $y < $gEquates{"TILE_PIXEL_HEIGHT"}; $y++)
                {
                    $gScreenToTileOffset[$screenOffset + ($y * $gEquates{"SCREEN_BYTES_PER_ROW"} / $gEquates{"SIZEOF_TILE_INFO"})] = $lhsTileIndex * $gEquates{"SIZEOF_TILE_INFO"};
                }
                $screenOffset++;
            }
            
            if ($tileY != 0)
            {
                $gTileAbove[$lhsTileIndex] = lhsXYToTileOffset($tileX, $tileY - 1);
            }
            
            if ($tileY != $gEquates{"TOTAL_TILES_TALL"} - 1)
            {
                $gTileBelow[$lhsTileIndex] = lhsXYToTileOffset($tileX, $tileY + 1);
            }
            
            if ($tileX != 0)
            {
                $gTileLeft[$lhsTileIndex] = lhsXYToTileOffset($tileX - 1, $tileY);
            }
            
            if ($tileX == $gEquates{"LHS_NUM_TILES_WIDE"} - 1)
            {
                $gTileRight[$lhsTileIndex] = gameXYToTileOffset(0, $tileY);
            }
            else
            {
                $gTileRight[$lhsTileIndex] = lhsXYToTileOffset($tileX + 1, $tileY);
            }
            
            $gTileRightVolume[$lhsTileIndex] = 0;
            
            $lhsTileIndex++;
            
            $lastOffset += $gEquates{"TILE_BYTE_WIDTH"};
        }
        
        for ($tileX = 0; $tileX < $gEquates{"GAME_NUM_TILES_WIDE"}; $tileX++)
        {
            $gTileScreenOffset[$tileIndex] = $lastOffset;
            $gTileBitOffset[$tileIndex] = $bitOffset;
            $gTileBitMask[$tileIndex] = $bitMask;
            
            for (my $word = 0; $word < ($gEquates{"TILE_BYTE_WIDTH"} / $gEquates{"SIZEOF_TILE_INFO"}); $word++)
            {
                for (my $y = 0; $y < $gEquates{"TILE_PIXEL_HEIGHT"}; $y++)
                {
                    $gScreenToTileOffset[$screenOffset + ($y * $gEquates{"SCREEN_BYTES_PER_ROW"} / $gEquates{"SIZEOF_TILE_INFO"})] = $tileIndex * $gEquates{"SIZEOF_TILE_INFO"};
                }
                $screenOffset++;
            }
            
            if ($tileY != 0)
            {
                $gTileAbove[$tileIndex] = gameXYToTileOffset($tileX, $tileY - 1);
            }
            
            if ($tileY != $gEquates{"TOTAL_TILES_TALL"} - 1)
            {
                $gTileBelow[$tileIndex] = gameXYToTileOffset($tileX, $tileY + 1);
            }
            
            if ($tileX == 0)
            {
                $gTileLeft[$tileIndex] = lhsXYToTileOffset($gEquates{"LHS_NUM_TILES_WIDE"} - 1, $tileY);
            }
            else
            {
                $gTileLeft[$tileIndex] = gameXYToTileOffset($tileX - 1, $tileY);
            }
            
            if ($tileX == $gEquates{"GAME_NUM_TILES_WIDE"} - 1)
            {
                $gTileRight[$tileIndex] = rhsXYToTileOffset(0, $tileY);
            }
            else
            {
                $gTileRight[$tileIndex] = gameXYToTileOffset($tileX + 1, $tileY);
            }
            
            $gTileRightVolume[$tileIndex] = $tileX * 255 / ($gEquates{"GAME_NUM_TILES_WIDE"} - 1);
            
            $tileIndex++;
            
            if ($bitMask == 0x8000)
            {
                $bitOffset += $gEquates{"SIZEOF_TILE_INFO"} = 2;
                $bitMask = 1;
            }
            else
            {
                $bitMask <<= 1;
            }
            
            $lastOffset += $gEquates{"TILE_BYTE_WIDTH"};
        }
        
        for ($tileX = 0; $tileX < $gEquates{"RHS_NUM_TILES_WIDE"}; $tileX++)
        {
            $gTileScreenOffset[$rhsTileIndex] = $lastOffset;
            
            for (my $word = 0; $word < ($gEquates{"TILE_BYTE_WIDTH"} / $gEquates{"SIZEOF_TILE_INFO"}); $word++)
            {
                for (my $y = 0; $y < $gEquates{"TILE_PIXEL_HEIGHT"}; $y++)
                {
                    $gScreenToTileOffset[$screenOffset + ($y * $gEquates{"SCREEN_BYTES_PER_ROW"} / $gEquates{"SIZEOF_TILE_INFO"})] = $rhsTileIndex * $gEquates{"SIZEOF_TILE_INFO"};
                }
                $screenOffset++;
            }
            
            if ($tileY != 0)
            {
                $gTileAbove[$rhsTileIndex] = rhsXYToTileOffset($tileX, $tileY - 1);
            }
            
            if ($tileY != $gEquates{"TOTAL_TILES_TALL"} - 1)
            {
                $gTileBelow[$rhsTileIndex] = rhsXYToTileOffset($tileX, $tileY + 1);
            }
            
            if ($tileX == 0)
            {
                $gTileLeft[$rhsTileIndex] = gameXYToTileOffset($gEquates{"GAME_NUM_TILES_WIDE"} - 1, $tileY);
            }
            else
            {
                $gTileLeft[$rhsTileIndex] = rhsXYToTileOffset($tileX - 1, $tileY);
            }
            
            if ($tileX != $gEquates{"RHS_NUM_TILES_WIDE"} - 1)
            {
                $gTileRight[$rhsTileIndex] = rhsXYToTileOffset($tileX + 1, $tileY);
            }
            
            $gTileRightVolume[$rhsTileIndex] = 255;
            
            $rhsTileIndex++;
            
            $lastOffset += $gEquates{"TILE_BYTE_WIDTH"};
        }
    }
    
# Calculate the memory address of the 0th row of the player's mouse position.
    $lastOffset = $gEquates{"SCREEN_ADDRESS"} + ($gEquates{"LHS_NUM_TILES_WIDE"} * $gEquates{"TILE_BYTE_WIDTH"}) + (($gEquates{"GAME_NUM_TILES_TALL"} - $gEquates{"PLAYER_TILES_HIGH"}) * $gEquates{"TILE_PIXEL_HEIGHT"} * $gEquates{"SCREEN_BYTES_PER_ROW"});
    $tileX = 0;
    $tileY = $gEquates{"GAME_NUM_TILES_TALL"} - $gEquates{"PLAYER_TILES_HIGH"} - 1;
    for (my $y = 0; $y < $gEquates{"MOUSE_MAX_Y"}; $y++)
    {
        $gMouseYAddress[$y] = $lastOffset;
        $lastOffset += $gEquates{"SCREEN_BYTES_PER_ROW"};
        
        if (($y % $gEquates{"TILE_PIXEL_HEIGHT"}) == 0)
        {
            $tileY++;
        }
        
        $gMouseYTileAbove[$y] = gameXYToTileOffset($tileX, $tileY);
        if (($y % $gEquates{"TILE_PIXEL_HEIGHT"}) == 0)
        {
            $gMouseYTileBelow[$y] = gameXYToTileOffset($tileX, $tileY);
        }
        else
        {
            $gMouseYTileBelow[$y] = gameXYToTileOffset($tileX, $tileY + 1);
        }
    }
    
    $tileX = -1;
    $tileY = 0;
    for (my $x = 0; $x < $gEquates{"MOUSE_MAX_X"}; $x++)
    {
        if (($x % $gEquates{"TILE_PIXEL_WIDTH"}) == 0)
        {
            $tileX++;
        }
        
        $gMouseXTileLeft[$x] = gameXYToTileOffset($tileX, $tileY);
        if (($x % $gEquates{"TILE_PIXEL_WIDTH"}) == 0)
        {
            $gMouseXTileRight[$x] = gameXYToTileOffset($tileX, $tileY);
        }
        else
        {
            $gMouseXTileRight[$x] = gameXYToTileOffset($tileX + 1, $tileY);
        }
    }
}


sub initNonGameTiles
{
    my $x;
    my $y;
    
    $x = $gEquates{"LHS_NUM_TILES_WIDE"} / 2;
    $y = 0;
    addLhsGameTile($x, $y, "TILE_LETTER_B");
    $x++;
    addLhsGameTile($x, $y, "TILE_LETTER_WHITE_U");
    $x++;
    addLhsGameTile($x, $y, "TILE_LETTER_GREEN_G");
    $x++;
    addLhsGameTile($x, $y, "TILE_LETTER_GREEN_S");
    
    $x = 0;
    $y = 3;
    addLhsGameString($x, $y, "PLAYER 1");
    
    $x = 2;
    $y = 5;
    addLhsGameString($x, $y, "SCORE:");
    
    $x = 2;
    $y = 8;
    addLhsGameString($x, $y, "LIVES:");
    
    $x = 0;
    $y = 12;
    addLhsGameString($x, $y, "HIGH SCORE:");
    
    $x = $gEquates{"LHS_NUM_TILES_WIDE"} - 2;
    $y = 13;
    addLhsGameString($x, $y, "0");
    
    $x = 0;
    $y = 16;
    addLhsGameString($x, $y, "PLAYER 2");
    
    $x = 2;
    $y = 18;
    addLhsGameString($x, $y, "SCORE:");
    
    $x = 2;
    $y = 21;
    addLhsGameString($x, $y, "LIVES:");
    
    for (my $tileY = 0; $tileY < $gEquates{"TOTAL_TILES_TALL"}; $tileY++)
    {
        addLhsGameTile($gEquates{"LHS_NUM_TILES_WIDE"} - 1, $tileY, "TILE_RIGHT_BAR");
        addRhsGameTile(0, $tileY, "TILE_LEFT_BAR");
    }
}


sub initFleaFreqs
{
    $gFleaFreqs[0] = (197 * 256 * 32) / ( 2 * 1645);
    
    for (my $i = 1; $i < $gEquates{"NUM_FLEA_FREQS"}; $i++)
    {
        $gFleaFreqs[$i] = ($gFleaFreqs[$i - 1] * 100000000) / 100580970;
    }
}


loadNetworkSettings();
initTiles();
initNonGameTiles();
initFleaFreqs();

# Generate the tileData.s file
open my $fh, ">", "$gGenDir/tileData.s" or die "$0: Unable to open $gGenDir/tileData.s for writing, $!";
my $text = << "EOF";
        case on
        mcopy tilesData.macros
        keep tilesData

tileData data
        using globalData

EOF
print $fh $text;

foreach my $equate (sort keys %gEquates)
{
    print $fh "$equate\tgequ " . $gEquates{$equate} . "\n";
}

printTileData($fh, "tileDirty", @gTileDirty);
printTileData($fh, "tileScreenOffset", @gTileScreenOffset);
printTileData($fh, "tileType", @gTileType);
printTileData($fh, "tileAbove", @gTileAbove);
printTileData($fh, "tileBelow", @gTileBelow);
printTileData($fh, "tileLeft", @gTileLeft);
printTileData($fh, "tileRight", @gTileRight);
printTileData($fh, "tileBitOffset", @gTileBitOffset);
printTileData($fh, "tileBitMask", @gTileBitMask);
printTileData($fh, "tileRightVolume", @gTileRightVolume);
printTileData($fh, "dirtyNonGameTiles", @gDirtyNonGameTiles);
printTileData($fh, "fleaFreqs", @gFleaFreqs);
printTileData($fh, "mouseYAddress", @gMouseYAddress);
printTileData($fh, "mouseYTileAbove", @gMouseYTileAbove);
printTileData($fh, "mouseYTileBelow", @gMouseYTileBelow);
printTileData($fh, "mouseXTileLeft", @gMouseXTileLeft);
printTileData($fh, "mouseXTileRight", @gMouseXTileRight);


$text = << "EOF";


numDirtyNonGameTiles    dc i2'$gNumDirtyNonGameTiles'

        end
EOF
print $fh $text;
close($fh);

# Generate the screenData.s file

open $fh, ">", "$gGenDir/screenData.s" or die "$0: Unable to open $gGenDir/screenData.s for writing, $!";
$text = << "EOF";
        case on
        mcopy screenData.macros
        keep screenData

screenData data screenDataSeg

EOF
print $fh $text;

printTileData($fh, "screenToTileOffset", @gScreenToTileOffset);

$text = << "EOF";

        end
EOF
print $fh $text;
close($fh);

# Generate the playerData.s file
open $fh, ">", "$gGenDir/playerData.s" or die "$0: Unable to open $gGenDir/playerData.s for writing, $!";
my $dataSize = 2 * $gEquates{"NUM_GAME_TILES"};
$text = << "EOF";
        case on
        mcopy playerData.macros
        keep playerData

playerData data playerDataSeg

player1Tiles ds $dataSize
player2Tiles ds $dataSize

        end
EOF
print $fh $text;
close($fh);

# Generate the tileData.h file

open $fh, ">", "$gGenDir/tileData.h" or die "$0: Unable to open $gGenDir/tileData.h for writing, $!";
$text = << "EOF";

#ifndef _GUARD_PROJECTBuGS_FILEtileData_
#define _GUARD_PROJECTBuGS_FILEtileData_

EOF
print $fh $text;

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";

#endif /* define _GUARD_PROJECTBuGS_FILEtileData_ */
EOF
print $fh $text;
close($fh);
