Newer
Older
ddhx / src / searcher.d
/**
 * Search module.
 * 
 * As crappy as it may be written, this is actually "sort of" of an extension
 * to the menu module, being an extension of the ddhx module.
 */
module searcher;

import std.stdio;
import std.encoding : transcode;
import core.bitop; // NOTE: byteswap was only in 2.092
import ddhx;
import utils;

private ushort bswap16(ushort v) pure {
	return cast(ushort)((v << 8) | (v >> 8));
}
	
align(1) private struct search_settings_t {
	align(1) struct data_t {
		align(1) union {
			void *p;	/// Data void pointer
			ubyte *pu8;	/// Data 8-bit pointer
			ushort *pu16;	/// Data 16-bit pointer
			uint *pu32;	/// Data 32-bit pointer
			ulong *pu64;	/// Data 64-bit pointer
		}
		void* _ptr;
		size_t len;
	} data_t data;
	align(1) struct sample_t {
		align(1) union {
			ubyte u8;	/// 8-bit sample data
			ushort u16;	/// 16-bit sample data
			uint u32;	/// 32-bit sample data
			ulong u64;	/// 64-bit sample data
		}
		uint size;	/// Sample size
		bool same;	/// Is sample size the same as input data?
	} sample_t sample;
}

/**
 * Search an UTF-8/ASCII string
 * Params: v = utf-8 string
 */
void search_utf8(immutable(char)[] v) {
	search_internal(cast(void*)v.ptr, v.length, "utf-8 string");
}

/**
 * Search an UTF-16 string
 * Params:
 *   v = utf-16 string
 */
void search_utf16(immutable(char)[] v) {
	wstring ws;
	transcode(v, ws);
	search_internal(cast(void*)ws.ptr, ws.length, "utf-16 string");
}

/**
 * Search an UTF-32 string
 * Params:
 *   v = utf-32 string
 */
void search_utf32(const char[] v) {
	dstring ds;
	transcode(v, ds);
	search_internal(cast(void*)ds.ptr, ds.length, "utf-32 string");
}

/**
 * Search a byte
 * Params: v = ubyte
 */
void search_u8(string v) {
	long l = void;
	if (unformat(v, l) == false) {
		ddhx_msglow("Could not parse number");
		return;
	}
	if (l < byte.min || l > ubyte.max) {
		ddhx_msglow("Integer too large for a byte");
		return;
	}
	byte data = cast(byte)l;
	search_internal(&data, 1, "u8");
}

/**
 * Search for a 16-bit value.
 * Params:
 *   input = Input
 *   invert = Invert endianness
 */
void search_u16(string input, bool invert = false) {
	long l = void;
	if (unformat(input, l) == false) {
		ddhx_msglow("Could not parse number");
		return;
	}
	if (l < short.min || l > ushort.max) {
		ddhx_msglow("Integer too large for a u16 value");
		return;
	}
	short data = cast(short)l;
	if (invert)
		data = bswap16(data);
	search_internal(&data, 2, "u16");
}

/**
 * Search for a 32-bit value.
 * Params:
 *   input = Input
 *   invert = Invert endianness
 */
void search_u32(string input, bool invert = false) {
	long l = void;
	if (unformat(input, l) == false) {
		ddhx_msglow("Could not parse number");
		return;
	}
	if (l < int.min || l > uint.max) {
		ddhx_msglow("Integer too large for a u16 value");
		return;
	}
	int data = cast(int)l;
	if (invert)
		data = bswap(data);
	search_internal(&data, 4, "u32");
}

/**
 * Search for a 64-bit value.
 * Params:
 *   input = Input
 *   invert = Invert endianness
 */
void search_u64(string input, bool invert = false) {
	long l = void;
	if (unformat(input, l) == false) {
		ddhx_msglow("Could not parse number");
		return;
	}
	if (invert)
		l = bswap(l);
	search_internal(&l, 8, "u64");
}

/**
 * Search using raw array of byte data.
 * Params: v = Byte array
 */
void search_array(ubyte[] v) {
	search_internal(v.ptr, v.length, "u8 array");
}

private void search_sample(ref search_settings_t s, void *data, ulong len) {
	s.data.p = data;
	s.data.len = len;
	
	version (D_LP64)
	if (len >= 8) {	// ulong.sizeof
		s.sample.size = 8;
		s.sample.same = len == 8;
		s.sample.u64  = *s.data.pu64;
		return;
	}
	if (len >= 4) {	// uint.sizeof
		s.sample.size = 4;
		s.sample.same = len == 4;
		s.sample.u32  = *s.data.pu32;
	} else if (len >= 2) {	// ushort.sizeof
		s.sample.size = 2;
		s.sample.same = len == 2;
		s.sample.u16  = *s.data.pu16;
	} else {
		s.sample.size = 1;
		s.sample.same = len == 1;
		s.sample.u8   = *s.data.pu8;
	}
}

private void search_internal(void *data, ulong len, string type) {
	import core.stdc.string : memcmp;
	
	if (len == 0) {
		ddhx_msglow(" Empty input, cancelled");
		return;
	}
	
	search_settings_t s = void;
	search_sample(s, data, len);
	
	ddhx_msglow(" Searching %s...", type);
	
	//TODO: Use D_SIMD
	
	long i = g_fpos + 1;
	ubyte[] a = cast(ubyte[])g_fhandle[g_fpos + 1..$];
	ubyte * b = a.ptr;
	const ulong alen = a.length;
	ulong o;	/// File offset
	
	for (; o < alen; ++o, ++i) {
		if (a[o] == s.sample.u8) {
			if (i + s.data.len >= g_fsize)
				goto L_BOUND;
			if (memcmp(b + o, s.data.p, s.data.len) == 0) {
				ddhx_seek(i);
				return;
			}
		}
	}

L_BOUND:
	ddhx_msglow(" Not found (%s)", type);
}