diff --git a/.gitignore b/.gitignore index fd8eb11..541ec7e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +*~ +*.swp 2048* *.o *.core +hello.efi +hello.so +tags diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..5d7dfa6 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,40 @@ +ARCH = $(shell uname -m | sed s,i[3456789]86,ia32,) + +C_FILES := $(wildcard *.c) +MERGE_FILE := merge_std.c +FILTERED_C_FILES := $(filter-out gfx%.c merge%.c highscore%.c, $(C_FILES)) $(MERGE_FILE) gfx_uefi.c highscore_uefi.c + +OBJS = $(FILTERED_C_FILES:.c=.o) +TARGET = 2048.efi + +EFIINC = /usr/include/efi +EFIINCS = -I$(EFIINC) -I$(EFIINC)/$(ARCH) -I$(EFIINC)/protocol +LIB = /usr/lib +EFILIB = /usr/lib +EFI_CRT_OBJS = $(EFILIB)/crt0-efi-$(ARCH).o +EFI_LDS = $(EFILIB)/elf_$(ARCH)_efi.lds + +#CFLAGS = -nostdinc $(EFIINCS) -fno-stack-protector -fpic +CFLAGS = $(EFIINCS) -fno-stack-protector -fpic \ + -fshort-wchar -mno-red-zone -Wall +CFLAGS += -DINVERT_COLORS -O2 + +ifeq ($(ARCH),x86_64) + CFLAGS += -DEFI_FUNCTION_WRAPPER +endif + +LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared \ + -Bsymbolic -L $(EFILIB) -L $(LIB) $(EFI_CRT_OBJS) + +all: $(TARGET) + +2048.so: $(OBJS) + ld $(LDFLAGS) $(OBJS) -o $@ -lefi -lgnuefi + +%.efi: %.so + objcopy -j .text -j .sdata -j .data -j .dynamic \ + -j .dynsym -j .rel -j .rela -j .reloc \ + --target=efi-app-$(ARCH) $^ $@ + +clean: + rm -f *.o 2048.efi 2048.so diff --git a/src/Rand.c b/src/Rand.c new file mode 100644 index 0000000..7447494 --- /dev/null +++ b/src/Rand.c @@ -0,0 +1,68 @@ +/*- + * Portions Copyright (c) 2010, Intel Corporation. All rights reserved.
+ * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +//__FBSDID("$FreeBSD: src/lib/libc/stdlib/rand.c,v 1.17.2.1.2.1 2009/10/25 01:10:29 kensmith Exp $"); +//#include +#include + +#include + +static UINT32 next = 1; + +/** Compute a pseudo-random number. + * + * Compute x = (7^5 * x) mod (2^31 - 1) + * without overflowing 31 bits: + * (2^31 - 1) = 127773 * (7^5) + 2836 + * From "Random number generators: good ones are hard to find", + * Park and Miller, Communications of the ACM, vol. 31, no. 10, + * October 1988, p. 1195. +**/ +int +rand() +{ + INT32 hi, lo, x; + + /* Can't be initialized with 0, so use another value. */ + if (next == 0) + next = 123459876; + hi = next / 127773; + lo = next % 127773; + x = 16807 * lo - 2836 * hi; + if (x < 0) + x += 0x7fffffff; + return ((next = x) % ((UINT32)RAND_MAX + 1)); +} + +void +srand(unsigned int seed) +{ + next = (UINT32)seed; +} diff --git a/src/engine.c b/src/engine.c index 0afc777..8a2d7c2 100644 --- a/src/engine.c +++ b/src/engine.c @@ -5,6 +5,9 @@ #include "engine.h" #include "highscore.h" +#include +#include + /* Utilize block counter to improve some of the functions so they can run * quicker */ @@ -213,23 +216,26 @@ static int digits_ceiling(unsigned int n) /* Return NULL if we couldn't allocate space for the gamestate. initializating the * gamestate will parse the options internally, so any caller should pass argc and argv * through this function */ -struct gamestate* gamestate_init(int argc, char **argv) +struct gamestate* gamestate_init(int argc, CHAR16 **argv) { + EFI_TIME Time; + struct gameoptions *opt = gameoptions_default(); if (!opt) return NULL; if (argc != 0) parse_options(opt, argc, argv); - srand(time(NULL)); + uefi_call_wrapper(ST->RuntimeServices->GetTime, 2, &Time, NULL); + srand(Time.Day*24*3600+Time.Hour*3600+Time.Minute*60+Time.Second+Time.Nanosecond); - struct gamestate *g = malloc(sizeof(struct gamestate)); + struct gamestate *g = AllocatePool(sizeof(struct gamestate)); if (!g) goto gamestate_alloc_fail; g->gridsize = opt->grid_width * opt->grid_height; - g->grid_data_ptr = calloc(g->gridsize, sizeof(int)); + g->grid_data_ptr = AllocateZeroPool(g->gridsize*sizeof(int)); if (!g->grid_data_ptr) goto grid_data_alloc_fail; - g->grid = malloc(opt->grid_height * sizeof(int*)); + g->grid = AllocatePool(opt->grid_height * sizeof(int*)); if (!g->grid) goto grid_alloc_fail; /* Switch to two allocation version */ @@ -260,9 +266,9 @@ struct gamestate* gamestate_init(int argc, char **argv) return g; grid_alloc_fail: - free(g->grid_data_ptr); + FreePool(g->grid_data_ptr); grid_data_alloc_fail: - free(g); + FreePool(g); gamestate_alloc_fail: return NULL; } @@ -285,7 +291,7 @@ void gamestate_clear(struct gamestate *g) { highscore_save(g); gameoptions_destroy(g->opts); - free(g->grid_data_ptr); /* Free grid data */ - free(g->grid); /* Free pointers to data slots */ - free(g); + FreePool(g->grid_data_ptr); /* Free grid data */ + FreePool(g->grid); /* Free pointers to data slots */ + FreePool(g); } diff --git a/src/engine.h b/src/engine.h index 0d50fad..5f589e3 100644 --- a/src/engine.h +++ b/src/engine.h @@ -40,6 +40,6 @@ int gamestate_end_condition(struct gamestate*); void gamestate_new_block(struct gamestate*); int gamestate_tick(struct gfx_state*, struct gamestate*, int, void (*callback)(struct gfx_state*, struct gamestate*)); void gamestate_clear(struct gamestate*); -struct gamestate* gamestate_init(int argc, char **argv); +struct gamestate* gamestate_init(int argc, CHAR16 **argv); #endif diff --git a/src/gfx_uefi.c b/src/gfx_uefi.c new file mode 100644 index 0000000..1115f86 --- /dev/null +++ b/src/gfx_uefi.c @@ -0,0 +1,193 @@ +#include "gfx.h" +#include "merge.h" + +#include +#include + +#define NUMBER_OF_COLORS 7 + +#define iterate(n, expression)\ + do {\ + int i;\ + for (i = 0; i < n; ++i) { expression; }\ + } while (0) + +static void print_pos(INTN Row, INTN Column, CHAR16 *str) +{ + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, Column, Row); + uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, str); +} + +struct gfx_state { + INTN window_height, window_width; +}; + +INTN uefi_color_array[] = { +#ifdef INVERT_COLORS + EFI_BLACK | EFI_BACKGROUND_RED, + EFI_BLACK | EFI_BACKGROUND_GREEN, + EFI_BLACK | EFI_BACKGROUND_BROWN, + EFI_BLACK | EFI_BACKGROUND_BLUE, + EFI_BLACK | EFI_BACKGROUND_MAGENTA, + EFI_BLACK | EFI_BACKGROUND_CYAN, + EFI_BLACK | EFI_BACKGROUND_LIGHTGRAY, +#else + EFI_RED | EFI_BACKGROUND_BLACK, + EFI_GREEN | EFI_BACKGROUND_BLACK, + EFI_YELLOW | EFI_BACKGROUND_BLACK, + EFI_BLUE | EFI_BACKGROUND_BLACK, + EFI_MAGENTA | EFI_BACKGROUND_BLACK, + EFI_CYAN | EFI_BACKGROUND_BLACK, + EFI_WHITE | EFI_BACKGROUND_BLACK, +#endif +}; + +struct gfx_state* gfx_init(struct gamestate *g) +{ + struct gfx_state *s = AllocatePool(sizeof(struct gfx_state)); + if (!s) return NULL; + + uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); + uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &s->window_width, &s->window_height); + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, 0); + uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, false); + + s->window_height = g->opts->grid_height * (g->print_width + 2) + 3; + s->window_width = g->opts->grid_width * (g->print_width + 2) + 1; + + return s; +} + +void gfx_draw(struct gfx_state *s, struct gamestate *g) +{ + if (g->score_last) { + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, 0); + Print(L"Score: %ld (+%ld) \n", g->score, g->score_last); + } else { + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, 0); + Print(L"Score: %ld \n", g->score); + } + + if (g->score >= g->score_high) + g->score_high = g->score; + + Print(L" Hi: %ld\n", g->score_high); + + //wattron(s->window, A_DIM); + Print(L"%c", BOXDRAW_DOWN_RIGHT); + iterate(g->opts->grid_width * (g->print_width + 2) + 1 - 2, Print(L"%c", (i+g->print_width+3) % (g->print_width+2) ? BOXDRAW_HORIZONTAL : BOXDRAW_DOWN_HORIZONTAL)); + Print(L"%c", BOXDRAW_DOWN_LEFT); + //wattroff(s->window, A_DIM); + + int x, y, + xpos = 0, + ypos = 3; + + for (y = 0; y < g->opts->grid_height; ++y, ++ypos, xpos = 0) { + //wattron(s->window, A_DIM); + print_pos(ypos, xpos++, L""); + Print(L"%c", BOXDRAW_VERTICAL); + //wattroff(s->window, A_DIM); + + for (x = 0; x < g->opts->grid_width; ++x) { + if (g->grid[x][y]) { + if (g->opts->enable_color) { + UINTN attr = uefi_color_array[g->grid[x][y] % NUMBER_OF_COLORS]; + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, attr); + } + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, xpos, ypos); + Print(L"%-*ld", g->print_width, merge_value(g->grid[x][y])); + print_pos(ypos, xpos + g->print_width, L" "); + if (g->opts->enable_color) { + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK); + } + + //wattron(s->window, A_DIM); + print_pos(ypos, xpos + g->print_width + 1, L""); + Print(L"%c", BOXDRAW_VERTICAL); + //wattroff(s->window, A_DIM); + } + else { + //wattron(s->window, A_DIM); + iterate(g->print_width + 1, Print(L" ")); + Print(L"%c", BOXDRAW_VERTICAL); + //wattroff(s->window, A_DIM); + } + + xpos += (g->print_width + 2); + } + } + + Print(L"\n"); + //wattron(s->window, A_DIM); + Print(L"%c", BOXDRAW_UP_RIGHT); + iterate(g->opts->grid_width * (g->print_width + 2) + 1 - 2, Print(L"%c", (i+g->print_width+3) % (g->print_width+2) ? BOXDRAW_HORIZONTAL : BOXDRAW_UP_HORIZONTAL)); + Print(L"%c", BOXDRAW_UP_LEFT); + //wattroff(s->window, A_DIM); + Print(L"\n"); +} + +int gfx_getch(struct gfx_state *s) +{ + EFI_INPUT_KEY Key; + + Pause(); + uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &Key); + + /* Flush buffer */ + //nodelay(s->window, TRUE); + //while (wgetch(s->window) != ERR); + //nodelay(s->window, FALSE); + + switch (Key.ScanCode) + { + case SCAN_UP: + return INPUT_UP; + break; + case SCAN_DOWN: + return INPUT_DOWN; + break; + case SCAN_RIGHT: + return INPUT_RIGHT; + break; + case SCAN_LEFT: + return INPUT_LEFT; + break; + + default: + return Key.UnicodeChar; + break; + + } +} + +#define EFI_TIMER_PERIOD_MILLISECONDS(Milliseconds) MultU64x32((UINT64)(Milliseconds), 10000) + +void gfx_sleep(int ms) +{ + EFI_STATUS Status; + EFI_EVENT TimerEvent; + UINTN Index; + + Status = uefi_call_wrapper(BS->CreateEvent, 5, EFI_EVENT_TIMER, 0, NULL, NULL, &TimerEvent); + if(EFI_ERROR(Status)) { + return; + } + + Status = uefi_call_wrapper(BS->SetTimer, 3, TimerEvent, TimerRelative, EFI_TIMER_PERIOD_MILLISECONDS(ms)); + if(EFI_ERROR(Status)) { + return; + } + + Status = uefi_call_wrapper(BS->WaitForEvent, 3, 1, &TimerEvent, &Index); + if(EFI_ERROR(Status)) { + return; + } + Status = uefi_call_wrapper(BS->CloseEvent, TimerEvent); +} + +void gfx_destroy(struct gfx_state *s) +{ + FreePool(s); + uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, true); +} diff --git a/src/highscore_uefi.c b/src/highscore_uefi.c new file mode 100644 index 0000000..aa46830 --- /dev/null +++ b/src/highscore_uefi.c @@ -0,0 +1,69 @@ +#include "engine.h" + +#include +#include + +EFI_GUID EFI_2048_GUID = + { 0x6F633F38, 0x2169, 0x42FF, {0xA2, 0x1D, 0x66, 0x34, 0x65, 0xCF, 0x3C, 0x71} }; +CHAR16 *hs_file_name = L"highscore"; + +void highscore_reset(void) +{ + EFI_INPUT_KEY Key; + EFI_STATUS Status; + + Print(L"Are you sure you want to reset your scores? Y(es) or N(o)\n"); + + while (1) { + Pause(); + Status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &Key); + if (EFI_ERROR(Status)) + return; + + if (Key.ScanCode == 0x00) { + if (Key.UnicodeChar == 'y' || Key.UnicodeChar == 'Y') { + goto reset_scores; + } else if (Key.UnicodeChar == 'n' || Key.UnicodeChar == 'N') { + return; + } + } + + Print(L"Please enter Y or N\n"); + } + +reset_scores:; + LibDeleteVariable(hs_file_name, &EFI_2048_GUID); +} + +long highscore_load(struct gamestate *g) +{ + long result = 0; + long *result_ptr; + size_t result_size; + + result_ptr = LibGetVariableAndSize(hs_file_name, &EFI_2048_GUID, &result_size); + if (result_ptr != NULL) { + if(result_size == sizeof(result)) { + result = *result_ptr; + } + FreePool(result_ptr); + } + + if (g) g->score_high = result; + return result; +} + +void highscore_save(struct gamestate *g) +{ + EFI_STATUS Status; + + /* Someone could make their own merge rules for highscores and this could be meaningless, + * howeverhighscores are in plaintext, so that isn't that much of a concern */ + if (g->score < g->score_high || g->opts->grid_width != 4 || + g->opts->grid_height != 4 || g->opts->ai == true) + return; + + Status = LibSetNVVariable(hs_file_name, &EFI_2048_GUID, sizeof(g->score), &g->score); + if (EFI_ERROR(Status)) + Print(L"save: Failed to write highscore file\n"); +} diff --git a/src/main.c b/src/main.c index 794f1cf..147239b 100644 --- a/src/main.c +++ b/src/main.c @@ -1,12 +1,12 @@ -#include #include #include "ai.h" #include "engine.h" #include "gfx.h" -#include -#include #include +#include +#include + void draw_then_sleep(struct gfx_state *s, struct gamestate *g) { gfx_draw(s, g); @@ -14,6 +14,7 @@ void draw_then_sleep(struct gfx_state *s, struct gamestate *g) gfx_sleep(160 / g->opts->grid_width); } +#if 0 char *targetDir(char *env, char *path) { char *dir; @@ -24,14 +25,21 @@ char *targetDir(char *env, char *path) strcat(dir,path); return dir; } +#endif -int main(int argc, char **argv) +EFI_STATUS +EFIAPI +efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) { + INTN argc; + CHAR16 **argv; + InitializeLib(ImageHandle, SystemTable); + argc = GetShellArgcArgv(ImageHandle, &argv); - setlocale (LC_ALL, ""); - bindtextdomain ("gfx_terminal", targetDir("PWD","/18n/")); - textdomain ("gfx_terminal"); + //setlocale (LC_ALL, ""); + //bindtextdomain ("gfx_terminal", targetDir("PWD","/18n/")); + //textdomain ("gfx_terminal"); struct gamestate *g = gamestate_init(argc, argv); if (!g) { @@ -108,7 +116,7 @@ get_new_key:; gfx_destroy(s); } - printf("%ld\n", g->score); + Print(L"%ld\n", g->score); gamestate_clear(g); - return 0; + return EFI_SUCCESS; } diff --git a/src/options.c b/src/options.c index 053f498..37ec64f 100644 --- a/src/options.c +++ b/src/options.c @@ -1,19 +1,20 @@ -#include -#include #include #include "highscore.h" #include "options.h" +#include +#include + void print_usage(void) { - printf("usage: 2048 [-cCaAiIrh] [-s SIZE] [-b RATE]\n"); + Print(L"usage: 2048 [-c] [-C] [-a] [-A] [-i] [-I] [-r] [-h] [-H] [-s SIZE] [-b RATE]\n"); } /* Initial game options */ struct gameoptions* gameoptions_default(void) { - struct gameoptions *opt = malloc(sizeof(struct gameoptions)); + struct gameoptions *opt = AllocatePool(sizeof(struct gameoptions)); if (!opt) return NULL; opt->grid_height = DEFAULT_GRID_HEIGHT; @@ -30,14 +31,18 @@ struct gameoptions* gameoptions_default(void) void gameoptions_destroy(struct gameoptions *opt) { - free(opt); + FreePool(opt); } -struct gameoptions* parse_options(struct gameoptions *opt, int argc, char **argv) +struct gameoptions* parse_options(struct gameoptions *opt, int argc, CHAR16 **argv) { - int c; - while ((c = getopt(argc, argv, "aArcCiIhHs:b:")) != -1) { - switch (c) { + argc--; argv++; /* skip program name */ + while (argc > 0) { + if(argv[0][0] != '-' || argv[0][1] == '\0' || argv[0][2] != '\0') { + print_usage(); + Exit(EFI_INVALID_PARAMETER, 0, NULL); + } + switch (argv[0][1]) { case 'a': opt->animate = true; break; @@ -60,25 +65,36 @@ struct gameoptions* parse_options(struct gameoptions *opt, int argc, char **argv break; case 's':; /* Stick with square for now */ - int optint = strtol(optarg, NULL, 10); + if(argc < 2) { + print_usage(); + Exit(EFI_INVALID_PARAMETER, 0, NULL); + } + int optint = Atoi(argv[1]); if (optint < CONSTRAINT_GRID_MAX && optint > CONSTRAINT_GRID_MIN) { opt->grid_height = optint; opt->grid_width = optint; } + argc--; argv++; break; case 'b': - opt->spawn_rate = strtol(optarg, NULL, 10); + if(argc < 2) { + print_usage(); + Exit(EFI_INVALID_PARAMETER, 0, NULL); + } + opt->spawn_rate = Atoi(argv[1]); + argc--; argv++; break; case 'r': highscore_reset(); - exit(0); + Exit(EFI_SUCCESS, 0, NULL); case 'h': print_usage(); - exit(0); + Exit(EFI_SUCCESS, 0, NULL); case 'H': - printf("%ld\n", highscore_load(NULL)); - exit(0); + Print(L"%ld\n", highscore_load(NULL)); + Exit(EFI_SUCCESS, 0, NULL); } + argc--; argv++; } return opt; diff --git a/src/options.h b/src/options.h index 2126cd3..ecbd732 100644 --- a/src/options.h +++ b/src/options.h @@ -3,6 +3,7 @@ #include #include +#include #define CONSTRAINT_GRID_MIN 4 #define CONSTRAINT_GRID_MAX 20 @@ -27,7 +28,7 @@ struct gameoptions { }; void print_usage(void); -struct gameoptions* parse_options(struct gameoptions*, int, char**); +struct gameoptions* parse_options(struct gameoptions*, int, CHAR16**); struct gameoptions* gameoptions_default(void); void gameoptions_destroy(struct gameoptions *opt); diff --git a/src/qemu.sh b/src/qemu.sh new file mode 100755 index 0000000..06c645e --- /dev/null +++ b/src/qemu.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +if [ ! -e OVMF_VARS.fd ]; then + cp /usr/share/OVMF/OVMF_VARS.fd . +fi +#qemu-system-x86_64 -bios /usr/share/qemu/OVMF.fd -net none -hda fat:rw:. +#qemu-system-x86_64 -pflash ~/devel/edk2/Build/OvmfX64/DEBUG_GCC5/FV/OVMF.fd -net none -hda fat:rw:. +qemu-system-x86_64 -drive if=pflash,format=raw,readonly,file=/usr/share/OVMF/OVMF_CODE.fd \ + -drive if=pflash,format=raw,file=OVMF_VARS.fd \ + -net none -hda fat:rw:. diff --git a/src/startup.nsh b/src/startup.nsh new file mode 100644 index 0000000..b5db7f9 --- /dev/null +++ b/src/startup.nsh @@ -0,0 +1,2 @@ +fs0: +2048 -c