// ネトラジ等の httpストリーミングコンテンツの文字化けを修正するパッチ
//
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#define _WIN32_WINNT 0x500
#include 
#include 
#include 
#include "SDK/foobar2000.h"
#include "foobar2000_component_client/component_client.cpp"
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "foobar2000_SDK.lib")
#pragma comment(lib, "foobar2000_sdk_helpers.lib")
#pragma comment(lib, "shared.lib")
#pragma comment(lib, "pfc.lib")

DECLARE_COMPONENT_VERSION(
	"http SJIS patch",
	"1.0.1",
	""
);

#if 0
bool is_disp_char(unsigned n){
	return (n=='\r' || n=='\n' || (0x20<=n && n<=0x7e));
}
void dump_string(const char* title, const char* s, int len){
	fprintf(stdout, ">>----- %s -----\n", title);
	if(len<0){
		for(; *s; s++)
			fprintf(stdout, is_disp_char((BYTE)*s) ? "%c" : "\\%x", (BYTE)*s);
	}
	else{
		for(int i=0; i < len; i++)
			fprintf(stdout, is_disp_char((BYTE)s[i]) ? "%c" : "\\%x", (BYTE)s[i]);
	}
	fprintf(stdout, "\n<<----- %s -----\n", title);
	fflush(stdout);
}
#else
void dump_string(const char* title, const char* s, int len){}
#endif


// sjisかutf-8か自動判別
bool is_sjis(const char* _s, const char* _e){
	const BYTE *s, *s2 = (const BYTE*)_s, *e = (const BYTE*)_e;
	int n_sjis = 0;
	int n_utf8 = 0;
	for(s=s2; s 0 && n_sjis > n_utf8);
}


// winsock::recv()
// HTTPヘッダーをsjisからutf-8に変換してfoobar2000へ渡す
int WINAPI HOOK_recv(SOCKET sock, char* buf, int max, int flags){
	int len = recv(sock, buf, max, flags);
	if(len <= 0) return len;

	static SOCKET current_sock = 0;
	static bool   http_process = false;

	if(current_sock != sock){
		current_sock = sock;
		http_process = false;
		if(!strncmp(buf, "HTTP/1.0 200 OK\r\n", 17) || !strncmp(buf, "ICY 200 OK\r\n", 12)){
			http_process = true;//start
		}
	}
	if(http_process && current_sock == sock){
		char* tail = buf;
		char* end  = buf+len;
		for(; tail < end; tail++){
			if(tail+4 <= end && !strncmp(tail, "\r\n\r\n", 4)){
				tail += 4;
				http_process = false;//stop
				break;
			}
		}

		dump_string("http header", buf, tail-buf);

		if(is_sjis(buf,tail)){
			WCHAR ucs2 [1024];
			int ucs2len = MultiByteToWideChar(CP_ACP,0, buf, tail-buf, ucs2, 1023);
			if(ucs2len>0){
				char utf8 [2048];
				int utf8len = WideCharToMultiByte(CP_UTF8,0, ucs2, ucs2len, utf8, 2047,0,0);
				if(utf8len>0){
					int remain = (end-tail);
					if(utf8len+remain <= max){
						dump_string("utf8", utf8, utf8len);

						memmove(buf+utf8len, tail, remain);
						memcpy(buf, utf8, utf8len);
						len = utf8len + remain;
					}
				}
			}
		}
	}
	return len;
}

// icy-metaint (meta interval)の定期更新は iso8859-1固定扱いになっている模様。
// active codepageに変更してやる
int WINAPI HOOK_mb2wc(UINT codepage, UINT flags, LPCSTR abuf, int alen, LPWSTR wbuf, int wlen){
	if(codepage==28591){//iso-8859-1
		codepage=CP_ACP;
		//codepage=932;
		dump_string("mb2wc", abuf, alen);
	}
	return MultiByteToWideChar(codepage, flags, abuf, alen, wbuf, wlen);
}

//
void _install(void** ptr, void* func){
	DWORD old;
	if(VirtualProtect(ptr, sizeof(void*), PAGE_READWRITE, &old)){
		*ptr = func;
		VirtualProtect(ptr, sizeof(void*), old, &old);
	}
}

// foobar2000.exeのインポートテーブルを書き換え、MultiByteToWideChar()と winsock::recv()をフックする
// 他のdllがインポートしていても、そちらは変更しない。exeだけで良い。
BOOL startup(){
	DWORD dos = (DWORD) GetModuleHandle(0);//exe
	IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(dos + ((IMAGE_DOS_HEADER*)dos)->e_lfanew);

	void** p_imp_mb2wc = 0;
	void** p_imp_recv = 0;

	//import table - kernel32.dll MultiByteToWideChar()
	IMAGE_IMPORT_DESCRIPTOR* imp = (IMAGE_IMPORT_DESCRIPTOR*)
		(dos + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
	for(; imp->FirstThunk; imp++){
		const char* dll = (const char*)(dos+imp->Name);
		if(!_stricmp(dll, "kernel32.dll")){
			IMAGE_THUNK_DATA* pIAT = (IMAGE_THUNK_DATA*)(dos + imp->FirstThunk);
			IMAGE_THUNK_DATA* pINT = (IMAGE_THUNK_DATA*)(dos + imp->OriginalFirstThunk);
			for(; pINT->u1.Function; pINT++,pIAT++){
				if(! IMAGE_SNAP_BY_ORDINAL(pINT->u1.Ordinal)){
					const char* func = (const char*) ((IMAGE_IMPORT_BY_NAME*)(dos + pINT->u1.AddressOfData))->Name;
					if(!_stricmp(func, "MultiByteToWideChar")){
						p_imp_mb2wc = (void**) &pIAT->u1.Function;
					}
				}
			}
		}
	}

	//delay import talble - ws2_32.dll recv()
	struct ImgDelayDesc{
		DWORD grAttrs, szName, phmod, pIAT, pINT, pBoundIAT, pUnloadIAT, dwTimeStamp;
	} *delay = (ImgDelayDesc*)
		(dos + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress);
	for(; delay->pIAT; delay++){
		const char* dll = (const char*)(dos+delay->szName);
		if(!_stricmp(dll, "ws2_32.dll")){
			IMAGE_THUNK_DATA* pIAT = (IMAGE_THUNK_DATA*)(dos + delay->pIAT);
			IMAGE_THUNK_DATA* pINT = (IMAGE_THUNK_DATA*)(dos + delay->pINT);
			for(; pINT->u1.Function; pINT++,pIAT++){
				if(IMAGE_SNAP_BY_ORDINAL(pINT->u1.Ordinal)){
					if((pINT->u1.Ordinal & 0x7fffffff)==16){
						p_imp_recv = (void**) &pIAT->u1.Function;
					}
				}
			}
		}
	}

	if(!p_imp_mb2wc || !p_imp_recv)
		return FALSE;

	_install(p_imp_mb2wc, HOOK_mb2wc);
	_install(p_imp_recv, HOOK_recv);
	return TRUE;
}

extern "C" BOOL WINAPI DllMain(HMODULE module, DWORD reason, LPVOID lp){
	if(reason==DLL_PROCESS_ATTACH)
		return startup();
	return TRUE;
}

DLL 이 로드되는 시점에 MultiByteToWideChar, recv 를 IAT 훅하여 멀티바이트 캐릭터이면 wchar_t 으로 변경하고 다시 UTF8로 변경하는 방식으로 한글  깨지는 현상을 패치하는 방법.

나름 잘되네~ 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기