this is still work in progress. this revision is not exactly but similar to the one used for my encode.
to use it for smb3, you have to change the rom addresses
--save 128 frames of map palette,
local Image = {}
local ImageOld = {}
local first = 0
local black = 15
local ImageWidth = 2048
local ImageHeight = 1080
local OffsetLevelX = 0
local OffsetLevelY = 0
local StatusBarY = 432-9
local V1xDrawOffsetX = 256
local V2xDrawOffsetX = 512
local VStatusBarX = 1024
local VStatusBarY = 696
local VStatusBarY2 = 888
local VStatusBarSplit = 168
local drawFullLevel = false
function irq()
local irqTime = os.clock()
local loadBGPTTime = os.clock()
loadBGPT()
print("loadBGPT:", os.clock()-loadBGPTTime)
local loadBGPalTime = os.clock()
loadBGPal()
print("loadBGPal:", os.clock()-loadBGPalTime)
local cropLevelTime = os.clock()
cropLevel()
print("cropLevel:", os.clock()-cropLevelTime)
local copyLevelTime = os.clock()
copyLevel()
print("copyLevel:", os.clock()-copyLevelTime)
print("irq:", os.clock()-irqTime)
end
function init()
os.execute("mkdir map")
loadImagePal()
preWriteHeader()
for i=0,1080-1,1 do
Image[i] = {}
ImageOld[i] = {}
end
print("init completed")
end
function after()
if first > 1 then
exportImage()
end
first = first+1
print(emu.framecount()," ok")
end
local TileLayout_ByTileset = 0x3d126 --indexed by Tileset<<1
local TileLayoutPage_ByTileset = 0x3d1d6 --the bank
local Tileset = 0x70a
--TileLayout +0 +0x200
-- +0x100 +0x300
-- =Metatile
--16+TileLayoutPage_ByTileset*0x2000+TileLayout_ByTileset%0x2000
local PPU_BG_PAL = 0x3f00
local PPU_SPR_PAL = 0x3f10
--TileID & %11000000 => Palette
local Map_Tile_Layouts = 0x1859c
local World_Num = 0x729
local Level_7Vertical = 0x3ef
local Level_Width = 0x22
local Horz_Scroll_Hi = 0x12
local Vert_Scroll_Hi = 0x13
local Horz_Scroll = 0xfd
local Vert_Scroll = 0xfc
local Player_SpriteX = 0xab
local Raster_Effect = 0x101
local ImageRowSize = 0
local ImageSize = 0
local PalColours = 64
local PalSize = PalColours*4
local HeaderSize = 0x36
local FileTotalSize = 0
local OffsetImage = HeaderSize+PalSize
function preWriteHeader()
ImageRowSize = math.ceil(ImageWidth/4)*4
ImageSize = ImageRowSize * ImageHeight
FileTotalSize = ImageSize+HeaderSize+PalSize
OffsetImage = HeaderSize+PalSize
end
local scrollOld = {}
local scroll = {}
function dword(x)
local t = {}
for i=0,4-1,1 do
t[i] = bit.band(x,255)
x = bit.rshift(x,8)
end
return string.char(t[0],t[1],t[2],t[3])
end
function table.clone(org)
return {table.unpack(org)}
end
local Header = ""
function writeHeader()
Header = "BM"..dword(FileTotalSize)..string.char(0,0,0,0)..dword(OffsetImage)..string.char(40,0,0,0)..dword(ImageWidth)..dword(ImageHeight)..string.char(1,0,8,0,0,0,0,0)..dword(ImageSize)..string.char(0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0)
end
local Pal = ""
function loadImagePal()
io.input("FCEUX.pal")
for i=0,64-1,1 do
j = io.read(3)
Pal = Pal..j.sub(j,3,3)..j.sub(j,2,2)..j.sub(j,1,1)..string.char(255)
end
end
local PatternTable = {}
function loadBGPT()
for i=0,256-1,1 do --8 bytes
local Metatile = {}
for h=0,8-1,1 do --bytes
Metatile[h] = {}
local Byt = ppu.readbyte(h+i*16)
for j=8-1,0,-1 do --bits
Metatile[h][j] = Byt%2
Byt = math.floor(Byt/2)
end
end
for h=0,8-1,1 do --bytes
local Byt = ppu.readbyte(h+8+i*16)
for j=8-1,0,-1 do --bits
Metatile[h][j] = Metatile[h][j]+Byt%2*2
Byt = math.floor(Byt/2)
end
end
PatternTable[i] = Metatile
end
end
--repeat for sprites' pattern table
local BGPalette = {}
local SpritePalette = {}
function loadBGPal()
for i=0,4-1,1 do
BGPalette[i] = {}
for j=0,4-1,1 do
BGPalette[i][j] = ppu.readbyte(PPU_BG_PAL+i*4+j)
end
end
end
function loadSpritePal()
for i=0,4-1,1 do
SpritePalette[i] = {}
for j=0,4-1,1 do
SpritePalette[i][j] = ppu.readbyte(PPU_SPR_PAL+i*4+j)
end
end
end
local cropX = 0
local cropY = 0
local cropXOldT = 0
local cropYOldT = 0
local cropXOld = 0
local cropYOld = 0
local smooth = 384
function cropLevel()
if first > 0 then
cropXOld = cropXOldT
cropYOld = cropYOldT
cropXOldT = cropX
cropYOldT = cropY
if memory.readbyte(Level_7Vertical) == 0 then
if drawFullLevel then
for i=0,ImageHeight-1,1 do
for j=0,ImageWidth-1,1 do
ImageOld[i][j] = Image[i][j]
end
end
else
k = memory.readbyte(Player_SpriteX)
if k >= 128 then
smooth = smooth - 1
end
if k <= 112 then
smooth = smooth + 1
end
smooth = math.max(256,math.min(smooth,512))
print("smooth",smooth)
cropX = math.max(0,math.min(scrollX-smooth,(memory.readbyte(Level_Width)+1)*256-ImageWidth/2))--print("cropX",cropX,"scrollX-256",scrollX-256,"memory.readbyte(Level_Width)+1)*256-ImageWidth",(memory.readbyte(Level_Width)+1)*256-ImageWidth)
cropY = 0
for i=0,432-1,1 do
ImageOld[i] = {}
for j=0,1024-1,1 do
ImageOld[i][j] = Image[i+cropY][j+cropX]
end
end
end
else
cropX = 0
cropY = math.min(math.max(0,scrollY-512),(memory.readbyte(Level_Width)+1)*240-ImageHeight)
for i=0,1024-1,1 do
ImageOld[i] = {}
for j=0,256-1,1 do
ImageOld[i][V1xDrawOffsetX+j] = Image[i+cropY][j+cropX]
end
end
end
end
end
function copyLevel()
scrollXOld = scrollXOldT
scrollYOld = scrollYOldT
scrollXOldT = scrollX
scrollYOldT = scrollY
scrollX = memory.readbyte(Horz_Scroll_Hi)*256+memory.readbyte(Horz_Scroll)
scrollY = memory.readbyte(Vert_Scroll_Hi)*256+memory.readbyte(Vert_Scroll)
if memory.readbyte(Level_7Vertical) == 0 then
local LvScreens = memory.readbyte(Level_Width)+1
local LvW = LvScreens*16
local LvH = 27
for i=0,ImageHeight-1,1 do
Image[i] = {}
end
if drawFullLevel then
h = 0
g = LvScreens
ImageWidth = LvW*16
ImageHeight = LvH*16
else
h=math.min(math.max(0,math.floor(scrollX/256)-2),LvScreens-6)
g=6
end
for i=h,h+g-1,1 do
for j=0,LvH-1,1 do
for k=0,16-1,1 do
local tile = memory.readbyte(0x6000+(i*LvH+j)*16+k)
local colours = BGPalette[math.floor(tile/0x40)]
for l=0,2-1,1 do
for m=0,2-1,1 do
local Metatile = rom.readbyte(16+rom.readbyte(TileLayoutPage_ByTileset+memory.readbyte(Tileset))*0x2000+rom.readbyte(TileLayout_ByTileset+memory.readbyte(Tileset)*2)%0x2000+l*0x200+m*0x100+tile)
for n=0,8-1,1 do
for o=0,8-1,1 do--print(Metatile,colours[0],colours[1],colours[2],colours[3],2,PatternTable[Metatile][n][o],3)
Image[(j*2+m)*8+n][((i*16+k)*2+l)*8+o] = colours[PatternTable[Metatile][n][o]]
end
end
end
end
end
end
end
--print("copyLevel: h:",h)
else
local LvScreens = memory.readbyte(Level_Width)+1
local LvW = 16
local LvH = LvScreens*15
for i=0,ImageHeight-1,1 do
Image[i] = {}
end
h=math.min(math.max(0,memory.readbyte(Vert_Scroll_Hi)-32),LvH-68)
OffsetLevelY = h
for i=h,h+68-1,1 do
for j=0,LvW-1,1 do
local tile = memory.readbyte(0x6000+i*16+j)
local colours = BGPalette[math.floor(tile/0x40)]
for l=0,2-1,1 do
for m=0,2-1,1 do
local Metatile = rom.readbyte(16+rom.readbyte(TileLayoutPage_ByTileset+memory.readbyte(Tileset))*0x2000+rom.readbyte(TileLayout_ByTileset+memory.readbyte(Tileset)*2)%0x2000+l*0x200+m*0x100+tile)
for n=0,8-1,1 do
for o=0,8-1,1 do--print(Metatile,colours[0],colours[1],colours[2],colours[3],2,PatternTable[Metatile][n][o],3)
Image[(i*2+m)*8+n][(j*2+l)*8+o] = colours[PatternTable[Metatile][n][o]]
end
end
end
end
end
end
end
end
function exportImage()
preWriteHeader()
writeHeader()
file = io.open("map/frame"..emu.framecount()..".bmp","wb")
--io.output("map/frame"..emu.framecount()..".bmp")
--io.write(dword(FileTotalSize),dword(FileTotalSize),dword(FileTotalSize))
file:write(Header,Pal)
writeImage()
io.close()
end
function writeImage()
if memory.readbyte(Tileset) > 0 and memory.readbyte(Raster_Effect) == 0 then
if memory.readbyte(Level_7Vertical) == 0 then
if drawFullLevel then
addScreen()
writeLevelFull()
else
addScreen()
StatusBar3x()
writeLevel2x()
end
else
VaddScreen()
VStatusBar6x()
Vcopy2x()
VwriteLevel()
end
else
clearImageOld()
copyScreen()
VwriteLevel()
end
end
function clearImageOld()
for i=0,ImageHeight-1,1 do
ImageOld[i] = {}
end
end
function copyScreen()
local nClock = os.clock()
local t = 0
for i=0,240-1,1 do
for j=0,256-1,1 do
--k,k,k,ImageOld[60+i][512+j] = emu.getscreenpixel(math.floor(j/4),math.floor(i/4),true)
k,k,k,t = emu.getscreenpixel(j,i,true)
local a = 60+i*4
local b = 512+j*4
local c = a+4-1
local d = b+4-1
for m=a,c,1 do
for n=b,d,1 do
ImageOld[m][n] = t
end
end
end
end
print("copyScreen time:",os.clock()-nClock)
end
function writeLevelFull()
for i=ImageHeight-1,0,-1 do
for j=0,ImageWidth-1,1 do
file:write(string.char(ImageOld[i][j]))
end
end
end
function writeLevel2x()
local nClock = os.clock()
for i=ImageHeight/2-1,0,-1 do
local line = {}
local mf = math.floor
local sc = string.char
h=ImageWidth/2-1
for j=0,h,1 do
k = sc(ImageOld[mf(i/2)][j] or 15)
line[j*2+1] = k
line[j*2+2] = k
end
line = table.concat(line)
line = line .. line
file:write(line)
--print(string.len(line),#line)
end
print("writeLevel2x time:",os.clock()-nClock)
end
function VwriteLevel()
local nClock = os.clock()
for i=ImageHeight-1,0,-1 do
local line = {}
local sc = string.char
for j=0,ImageWidth-1,1 do
line[j+1] = sc(ImageOld[i][j] or 15)
end
file:write(table.concat(line))
end
print("VwriteLevel time:",os.clock()-nClock)
end
function addScreen()
local nClock = os.clock()
local b = true
for i=0,192-1,1 do
for j=8,248-1,1 do
local t = 15
--print(ImageOld[239][scrollXOld+j])
--print(scrollYOld,scrollXOld,i,j)
k,k,k,t = emu.getscreenpixel(j,i,true)
ImageOld[scrollYOld-cropY+i][scrollXOld-cropX+j] = t
b = b and (t == 15)
end
end
if b then
clearImageOld()
smooth = 384
end
print("addScreen time:",os.clock()-nClock)
end
function VaddScreen()
for i=0,192-1,1 do
for j=8,256-1,1 do
k,k,k,ImageOld[scrollYOld-OffsetLevelY+i][scrollXOld+j] = emu.getscreenpixel(j,i,true)
end
end
end
function StatusBar3x()
local nClock = os.clock()
local mf = math.floor
for i=3*3,48*3-1 do
for j=8*3,256*3-1,1 do
k,k,k,ImageOld[StatusBarY+i][j] = emu.getscreenpixel(mf(j/3),192+mf(i/3),true)
end
end
print("StatusBar3x time:",os.clock()-nClock)
end
function VStatusBar6x()
for i=0,32*6-1 do
for j=0,VStatusBarSplit*6-1,1 do
k,k,k,ImageOld[VStatusBarY+i][j] = emu.getscreenpixel(math.floor(j/6),192+math.floor(i/6),true)
end
end
end
function Vcopy2x()
h=math.min(math.max(0,ScrollYOld-256),(memory.readbyte(Level_Width)+1)*240-540)
for i=0,ImageHeight-1,1 do
for j=0,512-1,1 do
ImageOld[i][V2xDrawOffsetX-V1xDrawOffsetX+j] = ImageOld[h+math.floor(i/2)][math.floor(j/2)]
end
end
end
init()
memory.registerexecute(0xfad3, irq)
emu.registerafter(after)