Advertisement
Faschz

Majora's Mask - .bmp to pictograph

Apr 12th, 2017
151
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 3.55 KB | None | 0 0
  1. --Script to be used with Bizhawk and Majora's Mask (USA)
  2. --Image must be 8-bit grayscale .bmp with width divisible by 8 and no larger than 160x112
  3.  
  4. --Function converts the ASCII String to its decimal equivalent value
  5. function str2dec(str, bytes)
  6.   local sum = 0
  7.   for i=1, bytes do
  8.     sum = sum + math.pow(256,i-1)*string.byte(str,i)
  9.   end
  10.   return sum
  11. end
  12.  
  13. --Function creates a 2D array with the given dimensions
  14. function createPixelArray(height, width)
  15.   local pixelArray = { }
  16.   for y = 1, height do
  17.     pixelArray[y] = { }
  18.     for x = 1, width do
  19.       pixelArray[y][x] = 0
  20.     end
  21.   end
  22.   return pixelArray
  23. end
  24.  
  25. --Reference: https://en.wikipedia.org/wiki/BMP_file_format
  26. local f = assert(io.open("input.bmp", "rb"))
  27. f:seek ("set", 10)
  28. local offset = str2dec(f:read(4), 4)
  29.  
  30. --TODO: Check header to insure the width and height are 4 bytes
  31. --in the wild they typically are
  32. f:seek ("set", 18)
  33. local width = str2dec(f:read(4), 4)
  34. local height = str2dec(f:read(4), 4)
  35. pixelArray = createPixelArray(height, width)
  36.  
  37. --Catches if the image won't fit into the pictograph image (160x112)
  38. if(width > 160 or height > 112) then
  39.   print("Invalid Dimensions: ("..width..", "..height..")")
  40.   f:close()
  41.   do return end
  42. end
  43.  
  44. --TODO: Pad the final image
  45. --Catches if the output needs padding (Also catches if the pixel array has padding)
  46. if(width%8 > 0) then
  47.   print("Width is not divisible by 8!!")
  48.   local buffer = 8 - width%8
  49.   f:close()
  50.   do return end
  51. end
  52.  
  53. f:seek ("set", offset) --Jumps right to the pixel array
  54.  
  55. --TODO: Catch if there are multiple color channels and convert to grayscale
  56. --TODO: Catch if the image is padded (width%4 ~= 0)
  57. --Pixel Array as it is in the image
  58. for y = height, 1, -1 do
  59.   for x = 1, width do
  60.     pixelArray[y][x] = str2dec(f:read(1), 1)
  61.   end
  62. end
  63.  
  64. f:close()
  65.  
  66.  
  67. --Converts from 8-bit to 5-bit
  68. for y = 1, height do
  69.   for x = 1, width do
  70.     pixelArray[y][x] = bit.rshift(pixelArray[y][x], 3)
  71.   end
  72. end
  73.  
  74.  
  75. --Create a new array that will hold the output
  76. local newHeight = height
  77. local newWidth = math.ceil(width*5/8) --Ceiling isn't really required since it breaks earlier in the check if the width was divisible by 8
  78. output = createPixelArray(newHeight, newWidth)
  79.  
  80. --Fills the output array
  81. for y = 1, newHeight do
  82.   for x = 1, newWidth/5 do
  83.     --index for arrays
  84.     old = (x-1)*8
  85.     new = (x-1)*5
  86.  
  87.     --New array has 5 bytes for every 8 bytes in the old array
  88.     --So here is a little bit of shifting
  89.     --most significant bits = previous pixel data
  90.     output[y][new+1] = bit.band(bit.lshift(pixelArray[y][old+1], 3) + bit.rshift(pixelArray[y][old+2], 2), 0xFF) --5 + top3
  91.     output[y][new+2] = bit.band(bit.lshift(pixelArray[y][old+2], 6) + bit.lshift(pixelArray[y][old+3], 1) + bit.rshift(pixelArray[y][old+4], 4), 0xFF) -- bottom2 + 5 + top1
  92.     output[y][new+3] = bit.band(bit.lshift(pixelArray[y][old+4], 4) + bit.rshift(pixelArray[y][old+5], 1), 0xFF) -- bottom4 + top4
  93.     output[y][new+4] = bit.band(bit.lshift(pixelArray[y][old+5], 7) + bit.lshift(pixelArray[y][old+6], 2) + bit.rshift(pixelArray[y][old+7], 3), 0xFF) -- bottom1 + 5 + top2
  94.     output[y][new+5] = bit.band(bit.lshift(pixelArray[y][old+7], 5) + pixelArray[y][old+8], 0xFF) -- bottom3 + 5
  95.   end
  96. end
  97.  
  98.  
  99. --Center the image
  100. offsetY = math.floor((112 - newHeight)/2)
  101. offsetX = math.floor((100 - newWidth)/2)
  102.  
  103. local pictographAddress = 0x1F0750 + offsetX + 100*offsetY
  104. --Write to game (Pictograph images are 5-bit grayscale)
  105. for y = 1, newHeight do
  106.   for x = 1, newWidth do
  107.     mainmemory.writebyte(pictographAddress + (x-1) + (y-1)*100, output[y][x])
  108.   end
  109. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement