// SPDX-License-Identifier: LGPL-3.0-only

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <rz_types.h>
#include <rz_util.h>
#include <rz_lib.h>
#include <rz_bin.h>
#include "nxo.h"

static char *readString(RzBuffer *buf, int off) {
	char symbol[128]; // assume 128 as max symbol name length
	int left = rz_buf_read_at(buf, off, (ut8 *)symbol, sizeof(symbol));
	if (left < 1) {
		return NULL;
	}
	symbol[sizeof(symbol) - 1] = 0;
	return strdup(symbol);
}

const char *fileType(const ut8 *buf) {
	if (!memcmp(buf, "NRO0", 4)) {
		return "nro0";
	}
	if (!memcmp(buf, "NRR0", 4)) {
		return "nrr0";
	}
	if (!memcmp(buf, "MOD0", 4)) {
		return "mod0";
	}
	if (!memcmp(buf, "NSO0", 4)) {
		return "nso0";
	}
	return NULL;
}

static void walkSymbols(RzBuffer *buf, RzBinNXOObj *bin, ut64 symtab, ut64 strtab, ut64 strtab_size, ut64 relplt, ut64 baddr) {
	int i, import = 0;
	RzBinSymbol *sym;
	RzBinImport *imp;
	for (i = 8; i < 99999; i++) {
		ut64 addr = rz_buf_read_le64_at(buf, symtab + i);
		ut64 size = rz_buf_read_le64_at(buf, symtab + i + 8);
		i += 16; // NULL, NULL
		ut64 name = rz_buf_read_le32_at(buf, symtab + i);
		//ut64 type = rz_buf_read_le32_at (buf, symtab + i + 4);
		char *symName = readString(buf, strtab + name);
		if (!symName) {
			break;
		}
		sym = RZ_NEW0(RzBinSymbol);
		if (!sym) {
			free(symName);
			break;
		}
		sym->type = RZ_BIN_TYPE_FUNC_STR;
		sym->bind = "NONE";
		sym->size = size;

		if (addr == 0) {
			import++;
			ut64 pltSym = rz_buf_read_le64_at(buf, relplt + (import * 24));
			imp = RZ_NEW0(RzBinImport);
			if (!imp) {
				RZ_FREE(sym);
				free(symName);
				break;
			}
			imp->name = symName;
			if (!imp->name) {
				goto out_walk_symbol;
			}
			imp->type = "FUNC";
			if (!imp->type) {
				goto out_walk_symbol;
			}
			imp->bind = "NONE";
			if (!imp->bind) {
				goto out_walk_symbol;
			}
			imp->ordinal = bin->imports_list->length;
			rz_list_append(bin->imports_list, imp);
			sym->is_imported = true;
			sym->name = strdup(symName);
			if (!sym->name) {
				goto out_walk_symbol;
			}
			sym->paddr = pltSym - 8;
			sym->vaddr = sym->paddr + baddr;
			eprintf("f sym.imp.%s = 0x%" PFMT64x "\n", symName, pltSym - 8);
		} else {
			sym->name = symName;
			if (!sym->name) {
				RZ_FREE(sym);
				break;
			}
			sym->paddr = addr;
			sym->vaddr = sym->paddr + baddr;
			eprintf("f sym.%s %" PFMT64u "0x%" PFMT64x "\n", symName, size, addr);
		}
		rz_list_append(bin->methods_list, sym);
		i += 8 - 1;
	}
	return;

out_walk_symbol:
	RZ_FREE(sym);
	RZ_FREE(imp);
	return;
}

void parseMod(RzBuffer *buf, RzBinNXOObj *bin, ut32 mod0, ut64 baddr) {
	ut32 ptr = rz_buf_read_le32_at(buf, mod0);
	eprintf("magic %x at 0x%x\n", ptr, mod0);
	if (ptr == 0x30444f4d) { // MOD0
		eprintf("is mode0\n");
		MODHeader mh = {
			.magic = rz_buf_read_le32_at(buf, mod0),
			.dynamic = rz_buf_read_le32_at(buf, mod0 + 4),
			.bss_start = rz_buf_read_le32_at(buf, mod0 + 8),
			.bss_end = rz_buf_read_le32_at(buf, mod0 + 12),
			.unwind_start = rz_buf_read_le32_at(buf, mod0 + 16),
			.unwind_end = rz_buf_read_le32_at(buf, mod0 + 20),
			.mod_object = rz_buf_read_le32_at(buf, mod0 + 24),
		};
		mh.mod_object += mod0;
		eprintf("magic 0x%x\n", mh.magic);
		eprintf("dynamic 0x%x\n", mh.dynamic);
		eprintf("bss 0x%x 0x%x\n", mh.bss_start, mh.bss_end);
		eprintf("unwind 0x%x 0x%x\n", mh.unwind_start, mh.unwind_end);
		eprintf("-------------\n");
		eprintf("mod 0x%x\n", mh.mod_object);
#define MO_(x) rz_buf_read_le64_at(buf, mh.mod_object + rz_offsetof(MODObject, x))
		MODObject mo = {
			.next = MO_(next),
			.prev = MO_(prev),
			.relplt = MO_(relplt),
			.reldyn = MO_(reldyn),
			.base = MO_(base),
			.dynamic = MO_(dynamic),
			.is_rela = MO_(is_rela),
			.relplt_size = MO_(relplt_size),
			.init = MO_(init),
			.fini = MO_(fini),
			.bucket = MO_(bucket),
			.chain = MO_(chain),
			.strtab = MO_(strtab),
			.symtab = MO_(symtab),
			.strtab_size = MO_(strtab_size)
		};
		eprintf("next 0x%" PFMT64x "\n", mo.next);
		eprintf("prev 0x%" PFMT64x "\n", mo.prev);
		eprintf("base 0x%" PFMT64x "\n", mo.base);
		eprintf("init 0x%" PFMT64x "\n", mo.init);
		eprintf("fini 0x%" PFMT64x "\n", mo.fini);
		eprintf("relplt 0x%" PFMT64x "\n", mo.relplt - mo.base);
		eprintf("symtab = 0x%" PFMT64x "\n", mo.symtab - mo.base);
		eprintf("strtab = 0x%" PFMT64x "\n", mo.strtab - mo.base);
		eprintf("strtabsz = 0x%" PFMT64x "\n", mo.strtab_size);
		//ut32 modo = mh.mod_object;
		ut64 strtab = mo.strtab - mo.base;
		ut64 symtab = mo.symtab - mo.base;
		walkSymbols(buf, bin, symtab, strtab, mo.strtab_size, mo.relplt - mo.base, baddr);
	}
}
