/* radare - LGPL - Copyright 2012-2015 - pancake */

#include <r_types.h>
#include <r_util.h>
#include <r_lib.h>
#include <r_bin.h>

#define RAR_CONST "\x00\x00\x00\x00\x20\x73\x74\x64\x6f\x75\x74\x20\x21\x55\x0c\xcd"
#define RARVMHDR "\x52\x61\x72\x21\x1a\x07\x00\xf9\x4e\x73\x00\x00\x0e\x00\x00\x00"

typedef struct r_bin_obj_rar_t {
	RBuffer *buf;
	ut64 loadaddr;
	Sdb *kv;
} RRarBinObj;

static bool check_bytes(const ut8 *buf, ut64 length) {
	if (buf && length > 16)
		if (!memcmp (buf, RARVMHDR, 16))
			return true;
	return false;
}

static bool check(RBinFile *arch) {
	const ut8 *bytes = arch ? r_buf_buffer (arch->buf) : NULL;
	ut64 sz = arch ? r_buf_size (arch->buf): 0;
	return check_bytes (bytes, sz);
}

static Sdb* get_sdb (RBinObject *o) {
	if (!o) return NULL;
	struct r_bin_obj_rar_t *bin = (struct r_bin_obj_rar_t *) o->bin_obj;
	if (bin->kv) return bin->kv;
	return NULL;
}

static void * load_bytes(RBinFile *arch, const ut8 *buf, ut64 sz, ut64 loadaddr, Sdb *sdb){
	RBuffer *tbuf = NULL;
	RRarBinObj *res = NULL;
	if (!buf || sz == 0 || sz == UT64_MAX) {
		return NULL;
	}
	res = R_NEW0 (RRarBinObj);
	tbuf = r_buf_new ();
	r_buf_set_bytes (tbuf, buf, sz);
	res->buf = tbuf;
	res->kv = sdb;
	res->loadaddr = loadaddr;
	return res;
}

static int load(RBinFile *arch) {
	return check (arch);
}

static int destroy (RBinFile *arch) {
	return true;
}

static ut64 baddr(RBinFile *arch) {
	return 0;
}

static RList* entries(RBinFile *arch) {
	RList* ret = r_list_new ();;
	RBinAddr *ptr = NULL;
	RRarBinObj *bin_obj = arch && arch->o ? arch->o->bin_obj : NULL;
	const ut8 *buf = bin_obj ? r_buf_buffer (bin_obj->buf) : NULL;
	ut64 sz = arch && bin_obj ? r_buf_size (bin_obj->buf) : 0;

	if (!ret) return NULL;
	ret->free = free;
	if (bin_obj && sz > 0x30 && !memcmp (buf+0x30, RAR_CONST, 16)) {
		if ((ptr = R_NEW (RBinAddr))) {
			ptr->vaddr = ptr->paddr = 0x9a;
			r_list_append (ret, ptr);
		}
	}
	return ret;
}

static RList* sections(RBinFile *arch) {
	RList *ret = NULL;
	RBinSection *ptr = NULL;
	RRarBinObj *bin_obj = arch && arch->o ? arch->o->bin_obj : NULL;
	const ut8 *buf = bin_obj ? r_buf_buffer (bin_obj->buf) : NULL;
	ut64 sz = 0;
	if (bin_obj)
		sz = r_buf_size (bin_obj->buf);

	if (!(ret = r_list_new ()))
		return NULL;
	ret->free = free;

	// TODO: return NULL here?
	if (!buf || sz < 0x30 || memcmp (buf+0x30, RAR_CONST, 16))
		return ret;

	// add text segment
	if (!(ptr = R_NEW0 (RBinSection)))
		return ret;
	strncpy (ptr->name, "header", R_BIN_SIZEOF_STRINGS);
	ptr->size = ptr->vsize = 0x9a;
	ptr->paddr = 0;
	ptr->vaddr = ptr->paddr;
	ptr->srwx = R_BIN_SCN_READABLE | R_BIN_SCN_MAP; // r--
	ptr->add = true;
	r_list_append (ret, ptr);

	/* rarvm code */
	if (!(ptr = R_NEW0 (RBinSection)))
		return ret;
	strncpy (ptr->name, "rarvm", R_BIN_SIZEOF_STRINGS);
	ptr->vsize = ptr->size = sz - 0x9a;
	ptr->vaddr = ptr->paddr = 0x9a;
	ptr->srwx = R_BIN_SCN_READABLE | R_BIN_SCN_EXECUTABLE | R_BIN_SCN_MAP; // r-x
	ptr->add = true;
	r_list_append (ret, ptr);
	return ret;
}

static RList* symbols(RBinFile *arch) {
	return NULL;
}

static RList* imports(RBinFile *arch) {
	return NULL;
}

static RList* libs(RBinFile *arch) {
	return NULL;
}

static RBinInfo* info(RBinFile *arch) {
	RBinInfo *ret = R_NEW0 (RBinInfo);
	RRarBinObj *bin_obj = arch && arch->o ? arch->o->bin_obj : NULL;
	const ut8 *buf = bin_obj ? r_buf_buffer (bin_obj->buf) : NULL;
	ut64 sz = arch && bin_obj ? r_buf_size (bin_obj->buf): 0;
	int bits = 32; // Default value

	if (!ret || !buf || sz < 0x30) {
		free (ret);
		return NULL;
	}

	ret->file = strdup (arch->file);
	ret->rclass = strdup ("rar");
	ret->os = strdup ("rar");
	ret->arch = strdup ("rar");
	ret->machine = strdup ("rarvm");
	if (!memcmp (buf+0x30, RAR_CONST, 16)) {
		ret->subsystem = strdup ("rarvm");
		ret->bclass = strdup ("program");
		ret->type = strdup ("EXEC (Compressed executable)");
	} else {
		ret->subsystem = strdup ("archive");
		ret->bclass = strdup ("archive");
		ret->type = strdup ("ARCHIVE (Compressed archive)");
	}
// TODO: specify if its compressed or executable
	ret->bits = bits;
	ret->has_va = true;
	ret->big_endian = true;
	ret->dbg_info = 0;
	ret->dbg_info = 0;
	return ret;
}

static ut64 size(RBinFile *arch) {
	// TODO: walk rar structures and guess size here...
	return 0x9a+128; // XXX
}

/* inspired in http://www.phreedom.org/solar/code/tinype/tiny.97/tiny.asm */
static RBuffer* create(RBin* bin, const ut8 *code, int codelen, const ut8 *data, int datalen) {
	RBuffer *buf = r_buf_new ();
	return buf;
}

RBinPlugin r_bin_plugin_rar = {
	.name = "rar",
	.desc = "rarvm bin plugin",
	.author = "pancake",
	.version = "0.1.0",
	.license = "LGPL3",
	.get_sdb = &get_sdb,
	.load = &load,
	.load_bytes = &load_bytes,
	.size = &size,
	.destroy = &destroy,
	.check = &check,
	.check_bytes = &check_bytes,
	.baddr = &baddr,
	.entries = &entries,
	.sections = &sections,
	.symbols = &symbols,
	.imports = &imports,
	.info = &info,
	.libs = &libs,
	.create = &create,
};

#ifndef CORELIB
RLibStruct radare_plugin = {
	.type = R_LIB_TYPE_BIN,
	.data = &r_bin_plugin_pe,
	.version = R2_VERSION
};
#endif
