thorium 0.4.0

Lokathor does stuff, ium
Documentation
/*
// The custom global alloc thing is feature gated
#![cfg_attr(feature = "sdl2_alloc", feature(alloc))]
// if we're going no_std we need to specify our own OOM handler
#![cfg_attr(all(feature = "avoid_std", not(test)), feature(alloc_error_handler))]
// Be no_std, upon request
#![cfg_attr(all(feature = "avoid_std", not(test)), no_std)]
*/
#![warn(missing_docs)]
#![allow(clippy::cast_lossless)]
#![allow(clippy::mixed_case_hex_literals)]
// dev configs, do not ship like this (it'll ship like this)
#![allow(dead_code)]
#![allow(unused_imports)]

//! Lokathor makes stuff.
//!
//! Major features of the library so far:
//!
//! * [Game Boy](https://en.wikipedia.org/wiki/Game_Boy) style 4-color renderer.

// Note(Lokathor): We won't use anything from std, but on Win32 we must still
// link with the CRT or we'll get link errors.
#[cfg(all(feature = "avoid_std", not(test), windows))]
extern crate std;

#[cfg(all(feature = "just_a_no_std_lib", not(test), not(windows)))]
unsafe fn sdl_quit_and_exit_failure() -> ! {
  sdl2_sys::SDL_Quit();
  libc::exit(libc::EXIT_FAILURE);
}

#[cfg(all(feature = "just_a_no_std_lib", not(test), not(windows)))]
#[panic_handler]
fn panic_exit(_info: &core::panic::PanicInfo) -> ! {
  unsafe { sdl_quit_and_exit_failure() }
}

#[cfg(all(feature = "just_a_no_std_lib", not(test), not(windows)))]
#[alloc_error_handler]
fn alloc_exit(_layout: core::alloc::Layout) -> ! {
  unsafe { sdl_quit_and_exit_failure() }
}

/*
// Note(Lokathor): The `alloc` crate is magic, we must write the `extern crate`
// even when using Rust 2018.
#[cfg(feature = "sdl2_alloc")]
#[macro_use]
extern crate alloc;
*/

/*
#[cfg(feature = "sdl2_alloc")]
mod sdl2_global_alloc {
  use alloc::alloc::Layout;

  pub(super) struct SDLAlloc;

  unsafe impl alloc::alloc::GlobalAlloc for SDLAlloc {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
      fermium::SDL_malloc(layout.size()) as *mut u8
    }

    unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
      fermium::SDL_free(ptr as *mut _)
    }

    unsafe fn realloc(&self, ptr: *mut u8, _layout: Layout, new_size: usize) -> *mut u8 {
      fermium::SDL_realloc(ptr as *mut _, new_size) as *mut u8
    }
  }
}

#[cfg(feature = "sdl2_alloc")]
#[global_allocator]
static GLOBAL: sdl2_global_alloc::SDLAlloc = sdl2_global_alloc::SDLAlloc;

#[cfg(feature = "avoid_std")]
pub(crate) use alloc::vec::*;
*/

pub mod interchange;
use interchange::*;

pub mod color;
use color::*;

pub mod dmg_render;
use dmg_render::*;

pub mod dmg_apu;
use dmg_apu::*;

#[allow(dead_code)]
const UPDATE_AND_RENDER_CHECK: GameUpdateAndRenderFn = game_update_and_render;
#[allow(dead_code)]
const GET_SOUND_SAMPLES_CHECK: GameGetSoundSamplesFn = game_get_sound_samples;

/// A shim to the real game update, so that the ABI is correct based on link mode.
#[cfg(feature = "dynamic_link")]
#[no_mangle]
pub extern "C" fn game_update_and_render(
  memory: &mut GameMemory, input: &GameInput, bitmap: &mut Bitmap,
) {
  game_update_and_render_inner(memory, input, bitmap)
}
/// A shim to the real game update, so that the ABI is correct based on link mode.
#[cfg(not(feature = "dynamic_link"))]
pub fn game_update_and_render(memory: &mut GameMemory, input: &GameInput, bitmap: &mut Bitmap) {
  game_update_and_render_inner(memory, input, bitmap)
}

fn game_update_and_render_inner(memory: &mut GameMemory, input: &GameInput, bitmap: &mut Bitmap) {
  /*
  match 3 {
    0 => {
      // shitty triangle
      memory.apu.wave.wave_ram = [
        0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0xF, 0xE,
        0xD, 0xC, 0xB, 0xA, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0,
      ];
    }
    1 => {
      // sawtooth
      memory.apu.wave.wave_ram = [
        0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x3, 0x3, 0x4, 0x4, 0x5, 0x5, 0x6, 0x6, 0x7, 0x7, 0x8, 0x8,
        0x9, 0x9, 0xA, 0xA, 0xB, 0xB, 0xC, 0xC, 0xD, 0xD, 0xE, 0xE, 0xF, 0xF,
      ];
    }
    2 => {
      // shitty sine
      memory.apu.wave.wave_ram = [
        0x8, 0x8, 0x9, 0x9, 0xA, 0xB, 0xB, 0xC, 0xC, 0xC, 0xB, 0xB, 0xA, 0x9, 0x9, 0x8, 0x8, 0x7,
        0x6, 0x6, 0x5, 0x4, 0x4, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x6, 0x6, 0x7,
      ];
    }
    3 => {
      // weird demo wave
      memory.apu.wave.wave_ram = [
        0x7, 0xA, 0xA, 0x8, 0x6, 0x4, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0xB, 0xA, 0x3, 0x1,
        0x0, 0x0, 0x3, 0x6, 0x9, 0xB, 0xA, 0x9, 0x7, 0x6, 0x5, 0x6, 0x6, 0x7,
      ];
    }
    _ => panic!("must select a valid wave"),
  }
  */

  let config = &mut memory.config;
  let controller = &input.gamepads[0];
  // arrows
  if controller.arrow_right {
    memory.px = memory.px.wrapping_add(1);
  }
  if controller.arrow_left {
    memory.px = memory.px.wrapping_sub(1);
  }
  if controller.arrow_up {
    memory.py = memory.py.wrapping_sub(1);
  }
  if controller.arrow_down {
    memory.py = memory.py.wrapping_add(1);
  }
  /*
  if controller.l && !memory.l_was_down {
    memory.pal_index = memory.pal_index.wrapping_sub(1);
    dump!(memory.pal_index);
  }
  if controller.r && !memory.r_was_down {
    memory.pal_index = memory.pal_index.wrapping_add(1);
    dump!(memory.pal_index);
  }
  */
  //
  if controller.l && !memory.l_was_down {
    memory.apu.pulse_a.play_samples_remaining = core::usize::MAX;
    memory.apu.pulse_a.sweep = FrequencySweep::new(260, 5, 7, 48000);
    memory.apu.pulse_a.envelope = LevelEnvelope::new(0, 1.0, 48000);
    memory.apu.pulse_a.duty = 0.5;
  }
  //
  if controller.r && !memory.r_was_down {
    memory.apu.pulse_b.play_samples_remaining = core::usize::MAX;
    memory.apu.pulse_b.sweep = FrequencySweep::new(360, -3, 1, 48000);
    memory.apu.pulse_b.envelope = LevelEnvelope::new(0, 1.0, 48000);
    memory.apu.pulse_b.duty = 0.5;
  }
  //
  if controller.east && !memory.east_was_down {
    memory.apu.wave.play_samples_remaining = 48000;
    memory.apu.wave.sweep = FrequencySweep::new(360, -3, 1, 48000);
    memory.apu.wave.wave = [
      0x7, 0xA, 0xA, 0x8, 0x6, 0x4, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0xB, 0xA, 0x3, 0x1,
      0x0, 0x0, 0x3, 0x6, 0x9, 0xB, 0xA, 0x9, 0x7, 0x6, 0x5, 0x6, 0x6, 0x7,
    ]
    .iter()
    .cloned()
    .map(|u| u as f32 / 15.0)
    .collect();
  }
  //
  if controller.south && !memory.south_was_down {
    memory.apu.wave.play_samples_remaining = 12000;
    memory.apu.wave.sweep = FrequencySweep::new(200, 0, 0, 48000);
    memory.apu.wave.wave = [
      0x7, 0xA, 0xA, 0x8, 0x6, 0x4, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3, 0xB, 0xA, 0x3, 0x1,
      0x0, 0x0, 0x3, 0x6, 0x9, 0xB, 0xA, 0x9, 0x7, 0x6, 0x5, 0x6, 0x6, 0x7,
    ]
    .iter()
    .cloned()
    .map(|u| u as f32 / 15.0)
    .collect();
  }
  //
  if controller.north && !memory.north_was_down {
    memory.apu.noise.play_samples_remaining = 48000;
    memory.apu.noise.level = LevelEnvelope::new(0, 1.0, 48000);
    memory.apu.noise.frequency = NoiseVoice::frequency_for(3, 3);
    memory.apu.noise.counter = NoiseCounter::default();
  }
  //
  memory.l_was_down = controller.l;
  memory.r_was_down = controller.r;
  memory.east_was_down = controller.east;
  memory.south_was_down = controller.south;
  memory.north_was_down = controller.north;
  //
  render_weird_gradient(bitmap, 0, 0);
  let mut tile_memory = DMGTileMemory::default();
  dmg_inflate_tile_data(&mut tile_memory.tiles, TERRAIN_TILES);
  dmg_inflate_tile_data(
    &mut tile_memory.tiles[DMGTileMemory::INFLATED_BYTES_PER_TILE
      * (TERRAIN_TILES.len() / DMGTileMemory::COMPRESSED_BYTES_PER_TILE)..],
    CREATURE_TILES,
  );
  let mut bg_screenblock = DMGScreenblock::default();
  bg_screenblock
    .entries
    .iter_mut()
    .for_each(|e_mut| *e_mut = 4);
  let row_col = |row: usize, col: usize| col + row * 32;
  for row in 0..32 {
    for col in 0..32 {
      if row == 0 || row == 31 || col == 0 || col == 31 {
        bg_screenblock.entries[row_col(row, col)] = 8;
      } else {
        bg_screenblock.entries[row_col(row, col)] = 4;
      }
    }
  }
  let mut win_screenblock = DMGScreenblock::default();
  win_screenblock
    .entries
    .iter_mut()
    .for_each(|e_mut| *e_mut = b'.');
  for n in 0..32 {
    win_screenblock.entries[n] = b'0' + n as u8;
  }
  config.window_enabled = true;
  config.window_xp7 = 7;
  config.window_y = 17 * 8;
  config.objects_enabled = true;
  config.objects_are_tall = true;
  let mut oam = vec![
    DMGOAMEntry {
      xm8: memory.px.wrapping_add(8),
      ym16: memory.py.wrapping_add(16 + 16),
      tile_id: 128,
      display_behind_bg_win_13: false,
      vflip: false,
      hflip: false,
      use_high_palette: false,
    },
    DMGOAMEntry {
      xm8: memory.px.wrapping_add(8 + 8),
      ym16: memory.py.wrapping_add(16 + 16),
      tile_id: 130,
      display_behind_bg_win_13: false,
      vflip: false,
      hflip: false,
      use_high_palette: false,
    },
  ];
  /*
  dmg_basic_render(
    bitmap,
    &config,
    &tile_memory,
    &[&bg_screenblock, &win_screenblock],
    &oam,
    &[0xFF_ffffb5, 0xFF_7bc67b, 0xFF_6b8c42, 0xFF_5a3921],
  );
  */
  let saved_xp7 = config.window_xp7;
  dmg_hblank_render(
    bitmap,
    config,
    &mut tile_memory,
    &mut [&mut bg_screenblock, &mut win_screenblock],
    &mut oam,
    &FOUR_COLOR_PALETTES[memory.pal_index % FOUR_COLOR_PALETTES.len()],
    |config: &mut DMGConfig,

     _: &mut DMGTileMemory,
     _: &mut [&mut DMGScreenblock],
     _: &mut [DMGOAMEntry]| {
      config.window_xp7 = config.window_xp7.wrapping_add(0);
    },
  );
  config.window_xp7 = saved_xp7;
}

/// A shim to the real sound output, so that the ABI is correct based on link mode.
#[cfg(feature = "dynamic_link")]
#[no_mangle]
pub extern "C" fn game_get_sound_samples(
  memory: &mut GameMemory, samples_per_second: i32, buf: &mut [StereoI16],
) {
  game_get_sound_samples_inner(memory, samples_per_second, buf)
}
/// A shim to the real sound output, so that the ABI is correct based on link mode.
#[cfg(not(feature = "dynamic_link"))]
pub fn game_get_sound_samples(
  memory: &mut GameMemory, samples_per_second: i32, buf: &mut [StereoI16],
) {
  game_get_sound_samples_inner(memory, samples_per_second, buf)
}

/// The actual sound computation function.
#[allow(unused)]
fn game_get_sound_samples_inner(
  memory: &mut GameMemory, samples_per_second: i32, buf: &mut [StereoI16],
) {
  buf
    .iter_mut()
    .for_each(|s| *s = StereoI16 { left: 0, right: 0 });
  memory.apu.fill_sound_buffer(samples_per_second, buf);
}

/// Renders the weird gradient into the bitmap.
pub fn render_weird_gradient(bitmap: &mut Bitmap, blue_offset: i32, green_offset: i32) {
  let bitmap_memory = bitmap.memory;
  let width = bitmap.width;
  let height = bitmap.height;
  let pitch = bitmap.pitch;
  let mut row_start = bitmap_memory;
  for y in 0..height {
    #[allow(clippy::cast_ptr_alignment)]
    let mut pixel = row_start as *mut u32;
    for x in 0..width {
      // Note(Lokathor): Windows uses "BGRA" bitmaps, when written as a
      // little-endian `u32` the bytes end up being ordered as `0xAA_RR_GG_BB`.
      let blue = (x + blue_offset) as u8 as u32;
      let green = (y + green_offset) as u8 as u32;
      unsafe {
        pixel.write_unaligned(blue | (green << 8));
        //pixel.write_unaligned(0);
        pixel = pixel.offset(1);
      }
    }
    row_start = unsafe { row_start.offset(pitch) };
  }
}

const TERRAIN_TILES: &[u8] = &[
  0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x24, 0x00, 0x18, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x28, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x68, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x14, 0x00, 0x28, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x20, 0x00, 0x00, 0x00,
  0x80, 0x00, 0xC0, 0x00, 0xE2, 0x00, 0x66, 0x00, 0x6E, 0x00, 0x2C, 0x00, 0x0C, 0x00, 0x08, 0x00,
  0x00, 0x00, 0x41, 0x00, 0x61, 0x00, 0x63, 0x00, 0x36, 0x00, 0x36, 0x00, 0x16, 0x00, 0x14, 0x00,
  0x80, 0x00, 0xC0, 0x00, 0xC2, 0x00, 0x46, 0x00, 0x6E, 0x00, 0x2C, 0x00, 0x2C, 0x00, 0x28, 0x00,
  0x1C, 0x1C, 0x36, 0x2A, 0x43, 0x7D, 0xC1, 0xBF, 0x81, 0xFF, 0x81, 0xFF, 0x4E, 0x7E, 0x30, 0x30,
  0x18, 0x18, 0x34, 0x2C, 0x26, 0x3A, 0x43, 0x7D, 0x81, 0xFF, 0x81, 0xFF, 0x62, 0x7E, 0x1C, 0x1C,
  0x48, 0x48, 0xBC, 0xF4, 0x84, 0xFC, 0x42, 0x7E, 0x43, 0x7D, 0x41, 0x7F, 0x46, 0x7E, 0x38, 0x38,
  0x00, 0x00, 0x38, 0x38, 0x6E, 0x56, 0x83, 0xFD, 0x81, 0xFF, 0x81, 0xFF, 0x42, 0x7E, 0x3C, 0x3C,
  0x00, 0x07, 0x05, 0x18, 0x0A, 0x20, 0x15, 0x40, 0x2A, 0x80, 0x55, 0x80, 0x6B, 0x80, 0x7F, 0x80,
  0x7F, 0x80, 0x7F, 0x40, 0x3F, 0x20, 0x1F, 0x10, 0x0F, 0x0F, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03,
  0x00, 0xE0, 0x40, 0x18, 0xA8, 0x04, 0x54, 0x02, 0xAA, 0x01, 0x7E, 0x01, 0xFE, 0x01, 0xFE, 0x01,
  0xFE, 0x01, 0xFE, 0x02, 0xFC, 0x04, 0xF8, 0x08, 0xF0, 0xF0, 0xC0, 0x40, 0x40, 0xC0, 0xC0, 0x40,
  0x00, 0x00, 0x7F, 0x7F, 0x40, 0x7F, 0x44, 0x7B, 0x50, 0x6F, 0x40, 0x7F, 0x43, 0x7F, 0x42, 0x7E,
  0x42, 0x7E, 0x43, 0x7F, 0x40, 0x7F, 0x50, 0x6F, 0x42, 0x7D, 0x40, 0x7F, 0x7F, 0x7F, 0x00, 0x00,
  0x00, 0x00, 0xFE, 0xFE, 0x02, 0xFE, 0x22, 0xDE, 0x02, 0xFE, 0x0A, 0xF6, 0xC2, 0xFE, 0x42, 0x7E,
  0x42, 0x7E, 0xC2, 0xFE, 0x22, 0xDE, 0x0A, 0xF6, 0x02, 0xFE, 0x02, 0xFE, 0xFE, 0xFE, 0x00, 0x00,
  0x00, 0x00, 0x7E, 0x7E, 0x42, 0x7E, 0x4A, 0x76, 0x42, 0x7E, 0x42, 0x7E, 0x42, 0x7E, 0x62, 0x5E,
  0x42, 0x7E, 0x42, 0x7E, 0x42, 0x7E, 0x4A, 0x76, 0x42, 0x7E, 0x42, 0x7E, 0x7E, 0x7E, 0x00, 0x00,
  0x00, 0x00, 0xFE, 0xFE, 0x02, 0xFE, 0x22, 0xDE, 0x0A, 0xF6, 0x02, 0xFE, 0xFE, 0xFE, 0x00, 0x00,
  0x00, 0x00, 0x7F, 0x7F, 0x40, 0x7F, 0x50, 0x6F, 0x42, 0x7D, 0x40, 0x7F, 0x7F, 0x7F, 0x00, 0x00,
  0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x40, 0xBF, 0x04, 0xFB, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
  0x42, 0x7E, 0x4A, 0x76, 0x42, 0x7E, 0x42, 0x7E, 0x42, 0x7E, 0x52, 0x6E, 0x42, 0x7E, 0x42, 0x7E,
  0x00, 0x00, 0x7E, 0x7E, 0x42, 0x7E, 0x42, 0x7E, 0x42, 0x7E, 0x42, 0x7E, 0x7E, 0x7E, 0x00, 0x00,
  0x00, 0xFF, 0x02, 0xFF, 0x40, 0xBF, 0x00, 0xFF, 0x00, 0xFF, 0x20, 0xFF, 0x01, 0xFE, 0x00, 0xFF,
  0x00, 0xFF, 0x20, 0xFF, 0x02, 0xFD, 0x00, 0xFF, 0x00, 0xFF, 0x40, 0xBF, 0x00, 0xFF, 0x08, 0xFF,
  0x00, 0xFF, 0x10, 0xEF, 0x00, 0xFF, 0x02, 0xFF, 0x00, 0xFF, 0x20, 0xFF, 0x02, 0xFD, 0x00, 0xFF,
  0x00, 0xFF, 0x21, 0xFE, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x21, 0xDF, 0x00, 0xFF, 0x00, 0xFF,
  0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xB5, 0xFF, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0x08, 0x08, 0x08, 0x08,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10,
  0x00, 0x00, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x24, 0x24, 0x24, 0x24, 0x7E, 0x7E, 0x24, 0x24, 0x7E, 0x7E, 0x24, 0x24, 0x24, 0x24,
  0x08, 0x08, 0x3E, 0x3E, 0x40, 0x40, 0x3C, 0x3C, 0x02, 0x02, 0x7C, 0x7C, 0x10, 0x10, 0x00, 0x00,
  0x20, 0x20, 0x52, 0x52, 0x24, 0x24, 0x08, 0x08, 0x10, 0x10, 0x24, 0x24, 0x4A, 0x4A, 0x04, 0x04,
  0x38, 0x38, 0x44, 0x44, 0x40, 0x40, 0x20, 0x20, 0x54, 0x54, 0x48, 0x48, 0x44, 0x44, 0x3A, 0x3A,
  0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08,
  0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10,
  0x00, 0x00, 0x10, 0x10, 0x54, 0x54, 0x38, 0x38, 0x54, 0x54, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x7C, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30,
  0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20,
  0x00, 0x00, 0x08, 0x08, 0x3C, 0x3C, 0x7E, 0x7E, 0x3C, 0x3C, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x1E, 0x1E, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00,
  0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04,
  0x1C, 0x1C, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, 0x78, 0x78, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08,
  0x00, 0x00, 0x1E, 0x1E, 0x48, 0x48, 0x24, 0x24, 0x10, 0x10, 0x08, 0x08, 0x44, 0x44, 0x38, 0x38,
  0x3C, 0x3C, 0x42, 0x42, 0x42, 0x42, 0x40, 0x40, 0x78, 0x78, 0x20, 0x20, 0x38, 0x38, 0x10, 0x10,
  0x00, 0x00, 0x80, 0x80, 0x42, 0x42, 0x24, 0x24, 0x38, 0x38, 0x24, 0x24, 0x24, 0x24, 0x04, 0x04,
  0x02, 0x02, 0x44, 0x44, 0x48, 0x48, 0xA8, 0xA8, 0x18, 0x18, 0x28, 0x28, 0x04, 0x04, 0x02, 0x02,
  0x00, 0x00, 0x81, 0x81, 0x5A, 0x5A, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x5A, 0x5A, 0x99, 0x99,
  0x10, 0x10, 0x38, 0x38, 0x54, 0x54, 0x92, 0x92, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x38,
  0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30,
  0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20,
  0x00, 0x00, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x00, 0x00, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20,
  0x38, 0x38, 0x44, 0x44, 0x44, 0x44, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10,
  0x3C, 0x3C, 0x42, 0x42, 0x99, 0x99, 0xA5, 0xA5, 0xA5, 0xA5, 0x9A, 0x9A, 0x40, 0x40, 0x3C, 0x3C,
  0x38, 0x38, 0x44, 0x44, 0x82, 0x82, 0x82, 0x82, 0xFE, 0xFE, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
  0x70, 0x70, 0x48, 0x48, 0x44, 0x44, 0x44, 0x44, 0x78, 0x78, 0x44, 0x44, 0x44, 0x44, 0x78, 0x78,
  0x38, 0x38, 0x44, 0x44, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x82, 0x82, 0x44, 0x44, 0x38, 0x38,
  0x78, 0x78, 0x44, 0x44, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x44, 0x44, 0x78, 0x78,
  0x7C, 0x7C, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x78, 0x78, 0x40, 0x40, 0x40, 0x40, 0x7C, 0x7C,
  0x7E, 0x7E, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x78, 0x78, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
  0x38, 0x38, 0x44, 0x44, 0x82, 0x82, 0x80, 0x80, 0x9C, 0x9C, 0x82, 0x82, 0x44, 0x44, 0x38, 0x38,
  0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x7C, 0x7C, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
  0x7C, 0x7C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x7C,
  0x3E, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x48, 0x48, 0x48, 0x48, 0x30, 0x30,
  0x44, 0x44, 0x48, 0x48, 0x50, 0x50, 0x60, 0x60, 0x60, 0x60, 0x50, 0x50, 0x48, 0x48, 0x44, 0x44,
  0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7C, 0x7C,
  0x82, 0x82, 0xC6, 0xC6, 0xAA, 0xAA, 0x92, 0x92, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
  0x82, 0x82, 0xC2, 0xC2, 0xA2, 0xA2, 0x92, 0x92, 0x8A, 0x8A, 0x86, 0x86, 0x82, 0x82, 0x82, 0x82,
  0x3C, 0x3C, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x3C,
  0x78, 0x78, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x78, 0x78, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
  0x38, 0x38, 0x44, 0x44, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x8A, 0x8A, 0x44, 0x44, 0x3A, 0x3A,
  0x78, 0x78, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x78, 0x78, 0x50, 0x50, 0x48, 0x48, 0x44, 0x44,
  0x3C, 0x3C, 0x42, 0x42, 0x42, 0x42, 0x40, 0x40, 0x3C, 0x3C, 0x02, 0x02, 0x42, 0x42, 0x3C, 0x3C,
  0xFE, 0xFE, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
  0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x42, 0x42, 0x3C, 0x3C,
  0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x44, 0x44, 0x44, 0x44, 0x28, 0x28, 0x28, 0x28, 0x10, 0x10,
  0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x28, 0x28, 0x28, 0x28,
  0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x28, 0x28, 0x10, 0x10, 0x28, 0x28, 0x44, 0x44, 0x44, 0x44,
  0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x28, 0x28, 0x28, 0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
  0x00, 0x00, 0x7E, 0x7E, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x7E, 0x7E,
  0x38, 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x38,
  0x20, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
  0x1C, 0x1C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x1C, 0x1C,
  0x10, 0x10, 0x28, 0x28, 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E,
  0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x1E, 0x1E, 0x20, 0x20, 0x20, 0x20, 0x78, 0x78, 0x40, 0x40, 0x80, 0x80, 0x7E, 0x7E,
  0x00, 0x00, 0x06, 0x06, 0x28, 0x28, 0x70, 0x70, 0x28, 0x28, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04,
  0x00, 0x00, 0x22, 0x22, 0x52, 0x52, 0x8A, 0x8A, 0x06, 0x06, 0x02, 0x02, 0x04, 0x04, 0x38, 0x38,
  0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x12, 0x12, 0x22, 0x22, 0x42, 0x42, 0x24, 0x24, 0x10, 0x10,
  0x00, 0x00, 0x7E, 0x7E, 0x7E, 0x7E, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x20, 0x20,
  0xFE, 0xFE, 0x71, 0x71, 0x31, 0x31, 0x12, 0x12, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, 0x30, 0x30,
  0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x3E, 0x3E, 0x40, 0x40,
  0x38, 0x38, 0x6C, 0x6C, 0x44, 0x44, 0x44, 0x44, 0x28, 0x28, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04,
  0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x28, 0x28, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04,
  0x00, 0x00, 0x40, 0x40, 0xBE, 0xBE, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x44, 0x44, 0x80, 0x80,
  0x00, 0x00, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08,
  0x10, 0x10, 0x28, 0x28, 0x24, 0x24, 0x42, 0x42, 0x40, 0x40, 0x80, 0x80, 0xF0, 0xF0, 0x10, 0x10,
  0x38, 0x38, 0x7C, 0x7C, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0x60, 0x60, 0x10, 0x10, 0x08, 0x08,
  0x38, 0x38, 0x44, 0x44, 0x82, 0x82, 0x80, 0x80, 0x78, 0x78, 0x20, 0x20, 0x10, 0x10, 0x0C, 0x0C,
  0x00, 0x00, 0xFE, 0xFE, 0x3A, 0x3A, 0x1C, 0x1C, 0x0C, 0x0C, 0x08, 0x08, 0x10, 0x10, 0x08, 0x08,
  0x00, 0x00, 0x10, 0x10, 0x20, 0x20, 0x58, 0x58, 0x04, 0x04, 0x04, 0x04, 0x48, 0x48, 0x30, 0x30,
  0x00, 0x00, 0x06, 0x06, 0x18, 0x18, 0x20, 0x20, 0x70, 0x70, 0x08, 0x08, 0x04, 0x04, 0x08, 0x08,
  0x00, 0x00, 0xFE, 0xFE, 0x78, 0x78, 0x3A, 0x3A, 0x1C, 0x1C, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20,
  0x44, 0x44, 0x38, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x4C, 0x4C, 0x00, 0x00,
  0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x22, 0x22, 0x26, 0x26, 0x4E, 0x4E, 0x70, 0x70, 0x80, 0x80,
  0x00, 0x00, 0x04, 0x04, 0x0C, 0x0C, 0x14, 0x14, 0x20, 0x20, 0x42, 0x42, 0x7E, 0x7E, 0x80, 0x80,
  0x01, 0x01, 0x02, 0x02, 0x44, 0x44, 0x7C, 0x7C, 0x7C, 0x7C, 0x44, 0x44, 0x82, 0x82, 0x02, 0x02,
  0x00, 0x00, 0x00, 0x00, 0x7C, 0x7C, 0x40, 0x40, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04,
  0x26, 0x26, 0x52, 0x52, 0x92, 0x92, 0x14, 0x14, 0x18, 0x18, 0x30, 0x30, 0x18, 0x18, 0x0C, 0x0C,
  0x00, 0x00, 0x3E, 0x3E, 0x64, 0x64, 0x64, 0x64, 0x7C, 0x7C, 0x38, 0x38, 0x18, 0x18, 0x04, 0x04,
  0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x0C, 0x0C, 0x10, 0x10, 0x10, 0x10, 0x60, 0x60, 0x10, 0x10, 0x10, 0x10, 0x0C, 0x0C,
  0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
  0x00, 0x00, 0x30, 0x30, 0x08, 0x08, 0x08, 0x08, 0x06, 0x06, 0x08, 0x08, 0x08, 0x08, 0x30, 0x30,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x32, 0x4C, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xFF, 0xFF, 0xC3, 0xFF, 0xA5, 0xFF, 0x99, 0xFF, 0x99, 0xFF, 0xA5, 0xFF, 0xC3, 0xFF, 0xFF, 0xFF,
];

const CREATURE_TILES: &[u8] = &[
  0x00, 0x00, 0x08, 0x08, 0x0C, 0x0C, 0x0A, 0x0E, 0x09, 0x0F, 0x08, 0x0F, 0x0A, 0x0F, 0x0A, 0x0F,
  0x08, 0x0F, 0x48, 0x4F, 0x38, 0x3F, 0x08, 0x0F, 0x34, 0x37, 0x43, 0x42, 0x01, 0x01, 0x00, 0x00,
  0x00, 0x00, 0x10, 0x10, 0x30, 0x30, 0x50, 0x70, 0x90, 0xF0, 0x10, 0xF0, 0x50, 0xF0, 0x50, 0xF0,
  0x10, 0xF0, 0x12, 0xF2, 0x1C, 0xFC, 0x10, 0xF0, 0x2C, 0xEC, 0xC2, 0x42, 0x80, 0x80, 0x00, 0x00,
  0x48, 0x48, 0x6C, 0x6C, 0x5A, 0x7E, 0x69, 0x5F, 0x74, 0x4F, 0x5A, 0x67, 0x41, 0x7F, 0x40, 0x7F,
  0x41, 0x7F, 0x40, 0x7F, 0x40, 0x7F, 0x40, 0x7F, 0x40, 0x7F, 0x20, 0x3F, 0x1F, 0x1F, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x40, 0xC0, 0xE0, 0xE0, 0x10, 0xF0,
  0x88, 0xF8, 0xC4, 0xFC, 0x02, 0xFE, 0x04, 0xFC, 0x08, 0xF8, 0x10, 0xF0, 0xE0, 0xE0, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x02, 0x0F, 0x0C, 0x1D, 0x12, 0x3F, 0x21,
  0x7F, 0x7E, 0x7F, 0x40, 0x7F, 0x40, 0x77, 0x48, 0x7F, 0x40, 0x7F, 0x40, 0x7F, 0x7F, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xC0, 0x40, 0xE0, 0x20, 0x70, 0x90, 0xF8, 0x18, 0xFC, 0xE4,
  0xFC, 0x04, 0xEC, 0x14, 0x7C, 0x84, 0xFC, 0x04, 0xDC, 0x24, 0xFC, 0x04, 0xFC, 0xFC, 0x00, 0x00,
];