diff --git a/PowerAnalyse/main.cpp b/PowerAnalyse/main.cpp
index 94917cb..9b63971 100644
--- a/PowerAnalyse/main.cpp
+++ b/PowerAnalyse/main.cpp
@@ -6,16 +6,335 @@
 #include <print>
 #include <xbox.h>
 
+#define SWITCH_ABSOLUTE 0
+#define SWITCH_COMPUTED 1
+#define SWITCH_BYTEOFFSET 2
+#define SWITCH_SHORTOFFSET 3
+
+struct SwitchTable
+{
+    std::vector<size_t> labels{};
+    size_t base{};
+    size_t defaultLabel{};
+    uint32_t r{};
+    uint32_t type{};
+};
+
+void ReadTable(Image& image, SwitchTable& table)
+{
+    uint32_t pOffset;
+    ppc_insn insn;
+    auto* code = (uint32_t*)image.Find(table.base);
+    ppc::Disassemble(code, table.base, insn);
+    pOffset = insn.operands[1] << 16;
+
+    ppc::Disassemble(code + 1, table.base + 4, insn);
+    pOffset += insn.operands[2];
+
+    if (table.type == SWITCH_ABSOLUTE)
+    {
+        const auto* offsets = (be<uint32_t>*)image.Find(pOffset);
+        for (size_t i = 0; i < table.labels.size(); i++)
+        {
+            table.labels[i] = offsets[i];
+        }
+    }
+    else if (table.type == SWITCH_COMPUTED)
+    {
+        uint32_t base;
+        uint32_t shift;
+        const auto* offsets = (uint8_t*)image.Find(pOffset);
+
+        ppc::Disassemble(code + 4, table.base + 0x10, insn);
+        base = insn.operands[1] << 16;
+
+        ppc::Disassemble(code + 5, table.base + 0x14, insn);
+        base += insn.operands[2];
+
+        ppc::Disassemble(code + 3, table.base + 0x0C, insn);
+        shift = insn.operands[2];
+
+        for (size_t i = 0; i < table.labels.size(); i++)
+        {
+            table.labels[i] = base + (offsets[i] << shift);
+        }
+    }
+    else if (table.type == SWITCH_BYTEOFFSET || table.type == SWITCH_SHORTOFFSET)
+    {
+        if (table.type == SWITCH_BYTEOFFSET)
+        {
+            const auto* offsets = (uint8_t*)image.Find(pOffset);
+            uint32_t base;
+
+            ppc::Disassemble(code + 3, table.base + 0x0C, insn);
+            base = insn.operands[1] << 16;
+
+            ppc::Disassemble(code + 4, table.base + 0x10, insn);
+            base += insn.operands[2];
+
+            for (size_t i = 0; i < table.labels.size(); i++)
+            {
+                table.labels[i] = base + offsets[i];
+            }
+        }
+        else if (table.type == SWITCH_SHORTOFFSET)
+        {
+            const auto* offsets = (be<uint16_t>*)image.Find(pOffset);
+            uint32_t base;
+
+            ppc::Disassemble(code + 4, table.base + 0x10, insn);
+            base = insn.operands[1] << 16;
+
+            ppc::Disassemble(code + 5, table.base + 0x14, insn);
+            base += insn.operands[2];
+
+            for (size_t i = 0; i < table.labels.size(); i++)
+            {
+                table.labels[i] = base + offsets[i];
+            }
+        }
+    }
+    else
+    {
+        assert(false);
+    }
+}
+
+void ScanTable(const uint32_t* code, size_t base, SwitchTable& table)
+{
+    ppc_insn insn;
+    uint32_t cr{ (uint32_t)-1 };
+    for (int i = 0; i < 32; i++)
+    {
+        ppc::Disassemble(&code[-i], base - (4 * i), insn);
+        if (insn.opcode == nullptr)
+        {
+            continue;
+        }
+
+        if (cr == -1 && (insn.opcode->id == PPC_INST_BGT || insn.opcode->id == PPC_INST_BGTLR || insn.opcode->id == PPC_INST_BLE || insn.opcode->id == PPC_INST_BLELR))
+        {
+            cr = insn.operands[0];
+            if (insn.opcode->operands[1] != 0)
+            {
+                table.defaultLabel = insn.operands[1] + 1;
+            }
+        }
+        else if (cr != -1)
+        {
+            if (insn.opcode->id == PPC_INST_CMPLWI && insn.operands[0] == cr)
+            {
+                table.r = insn.operands[1];
+                table.labels.resize(insn.operands[2]);
+                table.base = base;
+                break;
+            }
+        }
+    }
+}
+
+void MakeMask(const uint32_t* instructions, size_t count)
+{
+    ppc_insn insn;
+    for (size_t i = 0; i < count; i++)
+    {
+        ppc::Disassemble(&instructions[i], 0, insn);
+        std::println("0x{:X}, // {}", std::byteswap(insn.opcode->opcode | (insn.instruction & insn.opcode->mask)), insn.opcode->name);
+    }
+}
+
+void* SearchMask(const void* source, const uint32_t* compare, size_t compareCount, size_t size)
+{
+    assert(size % 4 == 0);
+    uint32_t* src = (uint32_t*)source;
+    size_t count = size / 4;
+    ppc_insn insn;
+
+    for (size_t i = 0; i < count; i++)
+    {
+        size_t c = 0;
+        for (c = 0; c < compareCount; c++)
+        {
+            ppc::Disassemble(&src[i + c], 0, insn);
+            if (insn.opcode == nullptr || insn.opcode->id != compare[c])
+            {
+                break;
+            }
+        }
+
+        if (c == compareCount)
+        {
+            return &src[i];
+        }
+    }
+
+    return nullptr;
+}
+
 int main()
 {
     const auto file = LoadFile("private/default.xex").value();
     auto image = Image::ParseImage(file.data(), file.size()).value();
 
+    std::string out;
+    auto println = [&]<class... Args>(std::format_string<Args...> fmt, Args&&... args)
+    {
+        std::vformat_to(std::back_inserter(out), fmt.get(), std::make_format_args(args...));
+        out += '\n';
+    };
     //for (const auto& section : image.sections)
     //{
     //    image.symbols.emplace(section.name, section.base, section.size, Symbol_Section);
     //}
 
+    // MakeMask((uint32_t*)image.Find(0x82C40D84), 6);
+
+    //auto data = "\x4D\x99\x00\x20";
+    //auto data2 = std::byteswap((2129));
+    //ppc_insn insn;
+    //ppc_insn insn2;
+    //ppc::Disassemble(data, 0, insn);
+    //ppc::Disassemble(&data2, 0, insn2);
+    //auto op = PPC_OP(insn.instruction);
+    //auto xop = PPC_XOP(insn.instruction);
+
+    auto printTable = [&](const SwitchTable& table)
+        {
+            println("[[switch]]");
+            println("base = 0x{:X}", table.base);
+            println("r = {}", table.r);
+            println("default = 0x{:X}", table.defaultLabel);
+            println("labels = [");
+            for (const auto& label : table.labels)
+            {
+                println("    0x{:X},", label);
+            }
+
+            println("]");
+            println("");
+        };
+
+    std::vector<SwitchTable> switches{};
+
+    auto insertTable = [&](size_t base, size_t defaultLabel, size_t r, size_t nLabels, uint32_t type)
+        {
+            auto& sw = switches.emplace_back();
+            sw.base = base;
+            sw.defaultLabel = defaultLabel;
+            sw.r = r;
+            sw.labels.resize(nLabels);
+            sw.type = type;
+        };
+
+    println("#");
+    insertTable(0x830ADAD8, 0x830ADB28, 11, 0x1B, SWITCH_COMPUTED);
+    insertTable(0x830AE1B0, 0x830AE21C, 11, 0x1B, SWITCH_BYTEOFFSET);
+    insertTable(0x82CFE120, 0x82CFDE68, 11, 0x10, SWITCH_SHORTOFFSET);
+
+    println("# ---- MANUAL JUMPTABLE ----");
+    for (auto& table : switches)
+    {
+        ReadTable(image, table);
+        printTable(table);
+    }
+
+    auto scanPattern = [&](uint32_t* pattern, size_t count, size_t type)
+        {
+            for (const auto& section : image.sections)
+            {
+                if (!(section.flags & SectionFlags_Code))
+                {
+                    continue;
+                }
+
+                size_t base = section.base;
+                uint8_t* data = section.data;
+                uint8_t* dataStart = section.data;
+                uint8_t* dataEnd = section.data + section.size;
+                while (data < dataEnd && data != nullptr)
+                {
+                    data = (uint8_t*)SearchMask(data, pattern, count, dataEnd - data);
+
+                    if (data != nullptr)
+                    {
+                        SwitchTable table{};
+                        table.type = type;
+                        ScanTable((uint32_t*)data, base + (data - dataStart), table);
+
+                        // std::println("{:X} ; jmptable - {}", base + (data - dataStart), table.labels.size());
+                        if (table.defaultLabel != 0)
+                        {
+                            ReadTable(image, table);
+                            printTable(table);
+                            switches.emplace_back(std::move(table));
+                        }
+
+                        data += 4;
+                    }
+                    continue;
+                }
+            }
+        };
+
+    uint32_t absoluteSwitch[] =
+    {
+        PPC_INST_LIS,
+        PPC_INST_ADDI,
+        PPC_INST_RLWINM,
+        PPC_INST_LWZX,
+        PPC_INST_MTCTR,
+        PPC_INST_BCTR,
+    };
+
+    uint32_t computedSwitch[] =
+    {
+        PPC_INST_LIS,
+        PPC_INST_ADDI,
+        PPC_INST_LBZX,
+        PPC_INST_RLWINM,
+        PPC_INST_LIS,
+        PPC_INST_ADDI,
+        PPC_INST_ADD,
+        PPC_INST_MTCTR,
+    };
+
+    uint32_t offsetSwitch[] =
+    {
+        PPC_INST_LIS,
+        PPC_INST_ADDI,
+        PPC_INST_LBZX,
+        PPC_INST_LIS,
+        PPC_INST_ADDI,
+        PPC_INST_ADD,
+        PPC_INST_MTCTR,
+    };
+
+    uint32_t wordOffsetSwitch[] =
+    {
+        PPC_INST_LIS,
+        PPC_INST_ADDI,
+        PPC_INST_RLWINM,
+        PPC_INST_LHZX,
+        PPC_INST_LIS,
+        PPC_INST_ADDI,
+        PPC_INST_ADD,
+        PPC_INST_MTCTR,
+    };
+
+    println("# ---- ABSOLUTE JUMPTABLE ----");
+    scanPattern(absoluteSwitch, std::size(absoluteSwitch), SWITCH_ABSOLUTE);
+
+    println("# ---- COMPUTED JUMPTABLE ----");
+    scanPattern(computedSwitch, std::size(computedSwitch), SWITCH_COMPUTED);
+
+    println("# ---- OFFSETED JUMPTABLE ----");
+    scanPattern(offsetSwitch, std::size(offsetSwitch), SWITCH_BYTEOFFSET);
+    scanPattern(wordOffsetSwitch, std::size(wordOffsetSwitch), SWITCH_SHORTOFFSET);
+
+    FILE* f = fopen("out/switches.toml", "w");
+    fwrite(out.data(), 1, out.size(), f);
+    fclose(f);
+
     uint32_t cxxFrameHandler = std::byteswap(0x831B1C90);
     uint32_t cSpecificFrameHandler = std::byteswap(0x8324B3BC);
     image.symbols.emplace("__CxxFrameHandler", 0x831B1C90, 0x38, Symbol_Function);
@@ -26,7 +345,7 @@ int main()
 
     image.symbols.emplace(std::format("sub_{:X}", 0x82EF5D78), 0x82EF5D78, 0x3F8, Symbol_Function);
 
-    auto fnd = Function::Analyze(image.Find(0x831B1358), image.size, 0x831B1358);
+    // auto fnd = Function::Analyze(image.Find(0x82C40D58), image.size, 0x82C40D58);
 
     std::vector<Function> functions;
     auto& pdata = *image.Find(".pdata");
@@ -45,7 +364,7 @@ int main()
         image.symbols.emplace(std::format("sub_{:X}", f.base), f.base, f.size, Symbol_Function);
     }
 
-    // auto sym = image.symbols.find(0x822C0000);
+    auto sym = image.symbols.find(0x82C40D58);
 
     std::vector<Function> missingFunctions;
     for (const auto& section : image.sections) 
@@ -98,32 +417,7 @@ int main()
     //uint8_t c[4] = { 0x10, 0x00, 0x59, 0xC3 };
     //ppc::Disassemble(c, 0x831D6C64, insn);
     //std::println("{:20}{}", insn.opcode->name, insn.op_str);
-    //for (const auto& section : image.sections)
-    //{
-    //    if (!(section.flags & SectionFlags_Code))
-    //    {
-    //        continue;
-    //    }
 
-    //    size_t base = section.base;
-    //    uint8_t* data = section.data;
-    //    uint8_t* dataEnd = section.data + section.size;
-    //    while (data < dataEnd)
-    //    {
-    //        if (*(uint32_t*)data == 0)
-    //        {
-    //            data += 4;
-    //            base += 4;
-    //            continue;
-    //        }
-
-    //        const auto& fn = functions.emplace_back(Function::Analyze(data, dataEnd - data, base));
-    //        data += fn.size;
-    //        base += fn.size;
-
-    //        image.symbols.emplace(std::format("sub_{:X}", fn.base), fn.base, fn.size, Symbol_Function);
-    //    }
-    //}
 
     const auto entrySymbol = image.symbols.find(image.entry_point);
     assert(entrySymbol != image.symbols.end());
diff --git a/PowerUtils/disasm.cpp b/PowerUtils/disasm.cpp
index b404bfb..4610b9b 100644
--- a/PowerUtils/disasm.cpp
+++ b/PowerUtils/disasm.cpp
@@ -22,3 +22,12 @@ int ppc::DisassemblerEngine::Disassemble(const void* code, size_t size, uint64_t
     info.buffer_length = size;
     return decode_insn_ppc(base, &info, &out);
 }
+
+int ppc::Disassemble(const void* code, uint64_t base, ppc_insn* out, size_t nOut)
+{
+    for (size_t i = 0; i < nOut; i++)
+    {
+        Disassemble(static_cast<const uint32_t*>(code) + i, base, out[i]);
+    }
+    return static_cast<int>(nOut) * 4;
+}
diff --git a/PowerUtils/disasm.h b/PowerUtils/disasm.h
index 67d8c2f..195bc85 100644
--- a/PowerUtils/disasm.h
+++ b/PowerUtils/disasm.h
@@ -30,4 +30,6 @@ namespace ppc
     {
         return Disassemble(code, 4, base, out);
     }
+
+    static int Disassemble(const void* code, uint64_t base, ppc_insn* out, size_t nOut);
 }