diff --git a/cffdump/CMakeLists.txt b/cffdump/CMakeLists.txt
index c2e82210b24126b821ae16fb9479c4bfbff795d1..0f60882b967278afe1f54ec51bfc7cb47daccd66 100644
--- a/cffdump/CMakeLists.txt
+++ b/cffdump/CMakeLists.txt
@@ -4,9 +4,12 @@ cmake_minimum_required(VERSION 2.6)
 include_directories(${PROJECT_BINARY_DIR})
 
 find_package(PkgConfig)
-pkg_check_modules(LUA lua52)
+pkg_check_modules(LUA lua53)
 if (NOT "${LUA_FOUND}")
-  pkg_check_modules(LUA lua)
+  pkg_check_modules(LUA lua52)
+  if (NOT "${LUA_FOUND}")
+    pkg_check_modules(LUA lua)
+  endif()
 endif()
 pkg_check_modules(LIBARCHIVE REQUIRED libarchive)
 
diff --git a/cffdump/script.c b/cffdump/script.c
index 11085c568ce337dc360361fa38dc9193d392f56c..611099a617183f51fa5579ca4067eb6997d85ede 100644
--- a/cffdump/script.c
+++ b/cffdump/script.c
@@ -27,6 +27,7 @@
  */
 
 #define _GNU_SOURCE
+#define LUA_COMPAT_APIINTCASTS
 
 #include <stdio.h>
 #include <string.h>
@@ -569,6 +570,12 @@ static const struct luaL_Reg l_bos[] = {
 	{NULL, NULL}  /* sentinel */
 };
 
+static void openlib(const char *lib, const luaL_Reg *reg)
+{
+  lua_newtable(L);
+  luaL_setfuncs(L, reg, 0);
+  lua_setglobal(L, lib);
+}
 
 /* called at start to load the script: */
 int script_load(const char *file)
@@ -579,9 +586,9 @@ int script_load(const char *file)
 
 	L = luaL_newstate();
 	luaL_openlibs(L);
-	luaL_openlib(L, "bos", l_bos, 0);
-	luaL_openlib(L, "regs", l_regs, 0);
-	luaL_openlib(L, "rnn", l_rnn, 0);
+	openlib("bos", l_bos);
+	openlib("regs", l_regs);
+	openlib("rnn", l_rnn);
 
 	ret = luaL_loadfile(L, file);
 	if (ret)
diff --git a/cffdump/scripts/texturator-to-unit-test.lua b/cffdump/scripts/texturator-to-unit-test.lua
new file mode 100644
index 0000000000000000000000000000000000000000..f91e8dc19c14a609550a933eb96b2bf95dafa756
--- /dev/null
+++ b/cffdump/scripts/texturator-to-unit-test.lua
@@ -0,0 +1,186 @@
+-- Parse logs from https://github.com/freedreno/freedreno/
+-- test-texturator.c to generate a src/freedreno/fdl/fd6_layout_test.c
+-- block.  We figure out the offsets from blits, but there may be some
+-- unrelated blits.  So just save all of them until we find the
+-- texture state.  This gives us the base address, and the miplevel #0
+-- width/height/depth.  Then work backwards from there finding the
+-- blits to the same dst buffer and deducing the miplevel from the
+-- minified dimensions
+
+local posix = require "posix"
+
+io.write("Analyzing Data...\n")
+
+local allblits = {}
+local nallblits = 0
+local r = rnn.init("a630")
+local found_tex = 0
+
+function minify(val, lvls)
+  val = val >> lvls
+  if val < 1 then
+    return 1
+  end
+  return val
+end
+
+function printf(fmt, ...)
+  return io.write(string.format(fmt, ...))
+end
+
+function start_cmdstream(name)
+  io.write("Parsing " .. name .. "\n")
+  allblits = {}
+  nallblits = 0
+end
+
+function draw(primtype, nindx)
+  if primtype ~= "BLIT_OP_SCALE" then
+    return
+  end
+
+  -- Just in case, filter out anything that isn't starting
+  -- at 0,0
+  if r.GRAS_2D_DST_TL.X ~= 0 or r.GRAS_2D_DST_TL.Y ~= 0 then
+    return
+  end
+
+  local blit = {}
+
+  blit.width   = r.GRAS_2D_DST_BR.X + 1
+  blit.height  = r.GRAS_2D_DST_BR.Y + 1
+  blit.pitch   = r.RB_2D_DST_SIZE.PITCH
+  blit.addr    = r.RB_2D_DST_LO | (r.RB_2D_DST_HI << 32)
+  blit.base    = bos.base(blit.addr)
+  blit.ubwc_addr = r.RB_2D_DST_FLAGS_LO | (r.RB_2D_DST_FLAGS_HI << 32)
+  blit.ubwc_base = bos.base(blit.uwbc_addr)
+  blit.ubwc_pitch = r.RB_2D_DST_FLAGS_PITCH.PITCH
+  blit.endaddr = 0  -- filled in later
+  printf("Found blit: 0x%x (0x%x) %dx%d\n", blit.addr, blit.base, blit.width, blit.height)
+
+  allblits[nallblits] = blit
+  nallblits = nallblits + 1
+end
+
+function A6XX_TEX_CONST(pkt, size)
+  -- ignore any texture state w/ DEPTH=1, these aren't the 3d tex state we
+  -- are looking for
+
+  local base = pkt[4].BASE_LO | (pkt[5].BASE_HI << 32)
+  local ubwc_base = pkt[7].FLAG_LO | (pkt[8].FLAG_HI << 32)
+  local width0  = pkt[1].WIDTH
+  local height0 = pkt[1].HEIGHT
+  local depth0  = pkt[5].DEPTH
+
+  if (found_tex ~= 0) then
+    return
+  end
+  found_tex = 1
+
+  printf("Found texture state:\n  %ux%ux%u (%s, %s, MIN_LAYERSZ=0x%x, TILE_ALL=%s, UBWC=%s FLAG_LOG2=%ux%u)\n",
+         width0, height0, depth0, pkt[0].FMT, pkt[0].TILE_MODE, pkt[3].MIN_LAYERSZ, tostring(pkt[3].TILE_ALL), tostring(pkt[3].FLAG), pkt[10].FLAG_BUFFER_LOGW, pkt[10].FLAG_BUFFER_LOGH)
+
+  -- Note that in some case the texture has some extra page or so
+  -- at the beginning:
+  local basebase = bos.base(base)
+  printf("base: 0x%x (0x%x)\n", base, basebase)
+  printf("ubwcbase: 0x%x (0x%x)\n", ubwc_base, bos.base(ubwc_base))
+
+  -- see if we can find the associated blits..  The blob always seems to
+  -- start from the lower (larger) mipmap levels and layers, so we don't
+  -- need to sort by dst address.  Also, while we are at it, fill in the
+  -- end-addr (at least for everything but the last blit)
+  local blits = {}
+  local nblits = 0
+  local lastblit = nil
+  for n = 0,nallblits-1 do
+    local blit = allblits[n]
+    --printf("blit addr: 0x%x (0x%x)\n", blit.addr, blit.base)
+    if blit.base == basebase and blit.addr >= base then
+      blits[nblits] = blit
+      nblits = nblits + 1
+      if lastblit then
+        lastblit.endaddr = blit.addr
+      end
+      lastblit = blit
+    end
+  end
+
+  -- now go thru the relevant blits and print out interesting details
+  local level = 0
+  local layer = 0
+  local w = width0   -- track current width/height to detect changing
+  local h = height0  -- mipmap level
+
+  printf("	{\n")
+  printf("		.format = %s,\n", pkt[0].FMT)
+
+  printf("		.layout = {\n")
+  printf("			.tile_mode = %s,\n", pkt[0].TILE_MODE)
+  printf("			.ubwc = %s,\n", tostring(pkt[3].FLAG))
+  printf("			.width0 = %d, .height0 = %d,\n", width0, height0)
+  printf("			.slices = {\n")
+  for n = 0,nblits-1 do
+    local blit = blits[n]
+    --printf("%u: %ux%u, addr=%x\n", n, blit.width, blit.height, blit.addr)
+    if w ~= blit.width or h ~= blit.height then
+      level = level + 1
+      layer = 0
+
+
+      if blit.width ~= minify(w, 1) or blit.height ~= minify(h, 1) then
+        printf("I am confused! %ux%u vs %ux%u\n", blit.width, blit.height, minify(w, 1), minify(h, 1))
+	printf("addr=%x\n", blit.addr)
+        --return
+      end
+
+      w = blit.width
+      h = blit.height
+    end
+
+    printf("				{ .offset = %d, .pitch = %u },\n",
+        blit.addr - base,
+        blit.pitch
+    );
+
+    layer = layer + 1
+  end
+  printf("			},\n")
+
+  layer = 0
+  level = 0
+  w = width0
+  h = height0
+
+  printf("			.ubwc_slices = {\n")
+  for n = 0,nblits-1 do
+    local blit = blits[n]
+    --printf("%u: %ux%u, addr=%x\n", n, blit.width, blit.height, blit.addr)
+    if w ~= blit.width or h ~= blit.height then
+      level = level + 1
+      layer = 0
+
+      if blit.width ~= minify(w, 1) or blit.height ~= minify(h, 1) then
+        printf("I am confused! %ux%u vs %ux%u\n", blit.width, blit.height, minify(w, 1), minify(h, 1))
+	printf("addr=%x\n", blit.addr)
+        --return
+      end
+
+      w = blit.width
+      h = blit.height
+    end
+
+    printf("				{ .offset = %d, .pitch = %u },\n",
+        blit.ubwc_addr - ubwc_base,
+        blit.ubwc_pitch
+    );
+
+    layer = layer + 1
+  end
+
+  printf("			},\n")
+  printf("		},\n")
+  printf("	},\n")
+  printf("\n\n")
+end
+