A simple .NET coroutine framework

(测试Evernote同步到wordpress)

(测试Evernote同步到wordpress)

public static class Coroutine
{
    private static List< IEnumerator > coroutines = new List< IEnumerator >();

    private static bool Step( IEnumerator coroutine)
    {
        IEnumerator inner = coroutine.Current as IEnumerator;
        if (inner != null && Step(inner))
            return true ;
        return coroutine.MoveNext();
    }

    public static IEnumerator Wait( Func <bool > condition)
    {
        while (!condition()) yield return null ;
    }

    public static void Start( IEnumerator coroutine)
    {
        coroutines.Add(coroutine);
    }

    public static void Update()
    {
        for (int i = 0; i < coroutines.Count; ++i)
        {
            if (!Step(coroutines[i]))
                coroutines.RemoveAt(i--);
        }
    }
}

PHP小轮子计划

因为“PHP是世界上最好的语言”,所以大凡尝试过用原生PHP写网页的同学都会被要裸写一堆重复代码恶心到,不想恶心稍微偷懒一点的话又会被黑客分分钟sql注入之类的。

用框架是个好的选择,然而意味着学习成本提高、语言灵活性降低、程序执行效率降低。比如我显然没必要做去学习Zend然后给工作室写一个简单的学生管理系统这样大炮打蚊子的事。轻量级的框架固然也有,比如CI我就很喜欢,但也有着把简单问题复杂化以及某些功能用着不顺手的问题。有能力让框架比原生更简单的我觉得只有微软帝国,比如COM和.NET,设计上几乎找不到缺点。

于是萌生自己造个框架的想法。这个框架应该具有以下的特点:

框架应该极度简洁(强迫症发作),最好1000行以内搞定,以至于它其实只是个类库,接近原生开发。
不为了面向对象而面向对象,好好做单身狗(PHP-CGI作为单进程模型,本质为过程式编程,有些东西硬要去封装也必然会搞成singleton)。
在该框架基础上写代码清爽。
MVC,只做小项目的话也至少做到VC。
有一个URI router。
输入安全检查。
DB接口与安全,有需求可以做sql语句生成,但active record估计用不上。
session与cache存储。

20151028 Edit:

目前项目的设计架子已经基本成型,代码开始提交到github上,欢迎围观。
https://github.com/gmsj0001/x-php

ELAN触控板改手写板实验

原理:逆向了ETDDeviceInformation.exe中取内核原始PS2数据并计算出多点触摸坐标的部分,然后使用Microsoft.Ink墨迹库来做文字识别。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;

namespace InkTest
{
    class ETD
    {
        [DllImport("kernel32")]
        static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode, byte[] lpInBuffer, uint nInBufferSize, byte[] lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped);
        [DllImport("kernel32")]
        static extern IntPtr CreateFileA(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
        [DllImport("kernel32")]
        static extern IntPtr OpenEventA(uint dwDesiredAccess, bool bInheritHandle, string lpName);
        [DllImport("kernel32")]
        static extern bool ResetEvent(IntPtr hEvent);
        [DllImport("kernel32")]
        static extern uint WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);

        static public void Init()
        {
            var t = new Thread(ETDThread);
            t.Start();
        }
        static public int X;
        static public int Y;

        static void ETDThread()
        {
            IntPtr hDevice = CreateFileA(@"\\.\ETD", 0xC0000000, 0, IntPtr.Zero, 3, 0, IntPtr.Zero);
            IntPtr hEvent = OpenEventA(0x1f0003, false, @"Global\ETDOther_GetKernelData");            
            byte[] ioctlBuf = new byte[124814];
            uint nRet = (uint)ioctlBuf.Length;
            Array.Copy(BitConverter.GetBytes((short)0x608), ioctlBuf, 2);
            DeviceIoControl(hDevice, 0x9c412000, ioctlBuf, nRet, ioctlBuf, nRet, out nRet, IntPtr.Zero);
            int bufIndex = BitConverter.ToInt32(ioctlBuf, 0x406);
            byte[] data = new byte[6];
            while (true)
            {
                WaitForSingleObject(hEvent, -1);
                ResetEvent(hEvent);
                Array.Clear(ioctlBuf, 0, ioctlBuf.Length);
                nRet = (uint)ioctlBuf.Length;
                Array.Copy(BitConverter.GetBytes((short)0x708), ioctlBuf, 2);
                Array.Copy(BitConverter.GetBytes(bufIndex), 0, ioctlBuf, 2, 4);
                DeviceIoControl(hDevice, 0x9c412000, ioctlBuf, nRet, ioctlBuf, nRet, out nRet, IntPtr.Zero);
                int count = BitConverter.ToInt32(ioctlBuf, 10);
                bufIndex = BitConverter.ToInt32(ioctlBuf, 0x480e);

                for (int i = 0; i < count; ++i)
                {
                    byte d1 = ioctlBuf[14 + i * 18 + 4];
                    int d4 = BitConverter.ToInt32(ioctlBuf, 14 + i * 18 + 13);
                    byte d5 = ioctlBuf[14 + i * 18 + 17];
                    data[d4] = d5;
                    if (d4 == 5)
                    {
                        if (d1 == 16 && data[1] == 0) //release
                        {
                            X = Y = 0;
                        }
                        else if (d1 == 17)
                        {
                            X = (data[1] & 0xf) * 0x100 + data[2];
                            Y = (data[4] & 0xf) * 0x100 + data[5];
                        }
                    }
                }
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Ink;
using System.IO;
using System.Runtime.InteropServices;


namespace InkTest
{
    public partial class Form1 : Form
    {
        List<Point> mPoints = new List<Point>();
        Ink mInk = new Ink();
        int mLastX, mLastY;
        DateTime mLastTime = DateTime.MaxValue;
        [DllImport("user32")]
        static extern int GetKeyState(int nVirtKey);

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ETD.Init();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            //this.Hide();
            if (GetKeyState(0xA0) > 0)
                return;
            if (ETD.X == 0 && ETD.Y == 0)
            {
                if (mLastX != 0 && mLastY != 0)
                {

                    mInk.Strokes.Add(mInk.CreateStroke(mPoints.ToArray()));
                    mPoints.Clear();
                    mLastTime = DateTime.Now;
                }
                else if (DateTime.Now - mLastTime > TimeSpan.FromSeconds(0.5))
                {
                    string str = mInk.Strokes.ToString();
                    mInk.DeleteStrokes();
                    mLastTime = DateTime.MaxValue;
                    Clipboard.SetText(str);
                    SendKeys.Send("^V");
                }
            }
            else
            {
                mPoints.Add(new Point(ETD.X, -ETD.Y));
            }
            mLastX = ETD.X;
            mLastY = ETD.Y;
            this.Text = string.Format("{0},{1}", ETD.X, ETD.Y);
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            Environment.Exit(0);
        }
    }
}

小工具:释放TrueCrypt加密卷占用空间

有不少好电影(大雾)想加密存放,但是心疼硬盘肿么办,加密盘没有填满的空间都浪费了肿么办!

TrueCrypt支持创建文件大小动态增长的加密盘,其原理是基于NTFS稀疏文件,建立时为0字节大小,有数据写入时才真正往硬盘写文件。

但是这样还不够。如果删除了加密盘中的文件,已经分配的空间并不会自动释放。VHD倒是支持先进行碎片整理,然后压缩以释放浪费的空间,但这样显然就和加密无缘了。

能想到的方法很简单,读取加密盘的空间分配信息,然后将加密盘文件对应的区域释放掉。查阅MSDN发现可以对NTFS稀疏文件使用FSCTL_SET_ZERO_DATA将指定区域设为稀疏并释放占用。

简单写了一段小程序,风格很烂(懒),目前只支持标准512扇区磁盘、FAT32分区。NTFS处理太复杂,需要处理MFT、$Bitmap可能的分块,对于开启NTFS压缩的还需要考虑忽略零碎的簇,写起来起码要1000行。

#include <Windows.h>
#include <stdio.h>
#include <string.h>

void error(char* str)
{
	printf(str);
	printf("按回车键退出。");
	getchar(); getchar();
	exit(-1);
}

void main()
{
	printf("TrueCrypt稀疏文件释放工具v0.1 by gmsj0001\n");
	char buf[64];
	printf("输入加密卷挂载盘符,带冒号:");
	scanf("%s", buf);
	char file[64];
	sprintf(file, "\\\\.\\%s", buf);
	//打开分区,读取FAT表
	HANDLE hDevice = CreateFile(file, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
	if (hDevice == INVALID_HANDLE_VALUE)
		error("分区打开失败!");
	char boot[512];
	DWORD dwRead;
	ReadFile(hDevice, boot, 512, &dwRead, NULL);
	if (memcmp(&boot[0x52], "FAT32   ", 8))
		error("不是FAT32分区!");
	int fat_addr = *(short*)&boot[0xe] * 512;
	int fat_size = *(int*)&boot[0x24] * 512;
	int clu_num = (*(int*)&boot[0x20] - *(short*)&boot[0xe] - *(char*)&boot[0x10] * *(int*)&boot[0x24]) / *(char*)&boot[0xd];
	int data_addr = (*(short*)&boot[0xe] + *(char*)&boot[0x10] * *(int*)&boot[0x24]) * 512;
	int clu_size = *(char*)&boot[0xd] * 512;
	int* fat = (int*)malloc(fat_size);
	printf("读取FAT表。。。\n");
	SetFilePointer(hDevice, fat_addr, 0, FILE_BEGIN);	//操作磁盘设备必须是扇区对齐
	ReadFile(hDevice, fat, fat_size, &dwRead, NULL);
	CloseHandle(hDevice);
	//打开文件
	printf("请卸载加密卷,然后输入加密卷文件名:");
	scanf("%s", file);
	hDevice = CreateFile(file, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
	if (hDevice == INVALID_HANDLE_VALUE)
		error("打开文件失败!");
	printf("正在释放空间,请祈祷本软件没有bug。。。\n");
	FILE_ZERO_DATA_INFORMATION zero;
	int i;
	for (i = 0; i < clu_num; ++i)
	{
		if (fat[i + 2] == 0)
		{
			zero.FileOffset.QuadPart = 0x20000 + data_addr + (LONGLONG)i * clu_size;	//0x20000为TrueCrypt文件头的长度
			for (; i < clu_num; ++i)
				if (fat[i + 2] != 0)
					break;
			zero.BeyondFinalZero.QuadPart = 0x20000 + data_addr + (LONGLONG)i * clu_size;
			DeviceIoControl(hDevice, FSCTL_SET_ZERO_DATA, &zero, sizeof(zero), NULL, 0, &dwRead, 0);
		}
	}
	CloseHandle(hDevice);
	free(fat);
	printf("搞定,按回车键退出。");
	getchar(); getchar();
}

暂不提供二进制程序,因为我不完全保证此工具是完全bug free的,万一被转载到天涯海角出了问题我扶不起。

使用效果:

以后建立加密盘可以大胆地开空间(当然FAT32不能超过32G),再也不用担心填满后删除造成空间浪费了。

newifi mini纯净版OpenWrt兼容内核固件,集成rt2860v2、mt76x2e驱动

基于原生OpenWrt Barrier Breaker 14.07,使用SDK && ImageBuilder构建,与官方14.07内核兼容,可直接安装官方源任意软件包(包括内核模块)

尝试做这个事情主要是因为PandoraBox固件没有打包某些我需要的内核模块,因为都是3.10内核,有些ko是可以忽略警告强行安装的,但少部分ko因引用内核头文件config而无法二进制兼容(例如ipset,你懂的)。

友情提醒1:此固件不适合普通小白使用,需要对OpenWRT有一定的了解,具备初级的Linux折腾能力,至少要会用vim编辑network、wireless等配置文件,因为LuCI不支持配置rt2860v2驱动的无线参数。
友情提醒2:此固件是纯净版,不因任何用户喜好集成任何功能,需要各种功能的请自己opkg安装。

感谢泄漏驱动代码的有关单位以及lintel、dq等大神的贡献:https://github.com/qdk0901/openwrt-mt7620

已知问题:
1、无法通过LuCI配置无线。这是原版ralink驱动的硬伤,请参考下面的范例配置/etc/config/wireless。
2、5G驱动使用samba时会抽,PandoraBox也有这个问题,见http://www.right.com.cn/forum/thread-149292-1-1.html#pid936103

变更历史:

20141104 r2:
1、Port到14.07正式版(14.07-rc3无法正常使用USB)。
2、添加5G驱动kmod-mt76x2e。
3、无线配置脚本使用最新PandoraBox里的脚本。
4、恢复默认的USB软件包。
5、修复2.4G与5G的LED。

20141023 r1:
1、移除mac80211及rt2800相关的驱动。
2、添加rt2860v2驱动,修复了读取ROM的一处错误。
3、添加luci-i18n-chinese
4、移除ipv6相关软件包及uci初始化时写入的ipv6相关配置。
5、移除usb相关软件包(内核兼容用时再装)。
6、修正newifi mini的交换机接口定义。
7、初始ip地址为192.168.99.1(大多人买这个当从路由)
8、初始root密码为admin。

/etc/config/wireless范例:

config wifi-device 'ra0'
	option type 'rt2860v2'
	option mode '9'
	option channel '6'
	option txpower '100'
	option ht '20+40'
	option country 'US'
	option disabled '0'

config wifi-iface
	option device 'ra0'
	option network 'lan'
	option mode 'ap'
	option ssid 'NEWIFI'
	option encryption 'psk2'
	option key 'xxxxxxxx'

config wifi-iface
	option device 'ra0'
	option network 'wwan'
	option mode 'sta'
	option ssid 'TPLINK'
	option encryption 'psk2'
	option key 'xxxxxxxx'

config wifi-device  rai0
	option type     mt7612
	option mode 	14
	option channel  auto
	option txpower 100
	option ht 20+40+80
	option country US
	
config wifi-iface
	option device   rai0
	option network	lan
	option mode     ap
	option ssid     NEWIFI_5G
	option encryption psk2
	option key xxxxxxxx

下载地址:openwrt-newifi-y1-r2-20141104

315/433MHz智能遥控终端

项目地址:https://github.com/gmsj0001/rfunit

感谢2014年9月乌云首届安全峰会《智能家居,光环下的玄机》议题采用本项目方案制作演示DEMO。

RFUnit_20140729224017

====特点====

1、串口通信,纯字符界面命令行操作,上位机无需任何软件
2、标准2262协议,支持直接发送自定义键码
3、自适应不同震荡电阻和脉宽
4、32个预置存储
5、多任务并行设计,支持不限时间脉冲发送
6、极简主义设计,MCU仅8针脚,程序编译后仅2K字

====用法====

使用USB转TTL线连接上位机与本设备,使用任何串口调试工具(如minicom、putty)打开端口即可,波特率请设置为9600。Linux下还可以直接echo命令到串口设备中,方便编写bash脚本。

====TTL命令格式====

w 数据(16进制)
发送2262脉冲,持续0.2秒。预置设置状态下为手动录入预置

w 数据(16进制) 时间(分秒)
发送2262脉冲,持续指定时间,时间设为255时不自动停止

w 0
停止发送2262脉冲。预置设置状态下为清空预置

w 预置序号(10进制,范围1-32)
发送指定预置已存储的数据

r
进入/退出预置设置,监听到的数据显示到TTL控制台中

r 预置序号
进入预置设置,将监听到或手动录入的数据存入指定的预置

====数据格式====

每个按键码由四字节整数构成。最高字节使用低7位存储2262脉冲震荡周期,即2262说明手册中的a值,单位微秒。
剩余三字节共24位存储地址码与按键码,令4a低电平+12a高电平为0,12a高电平+4a低电平为1。
PT2262引脚有0、1、F三个状态,而本模块使用实际传输数据为准,换算方式为0=00、1=11、F=01。

====MCU接线====

5V 1|—-|8 GND
2262 IN 2|12F |7 TTL TX
LED 3|1822|6 TTL RX
N/A 4|—-|5 2262 OUT

H3C Lite轻量级校园网认证Linux客户端(For SHNU)

2014年7月15日Update:
代码已完全重构,使用纯C编写。
项目主页:github.com/gmsj0001/h3clite

7月18日Update:
添加OpenWRT的UCI与LuCI控制界面(基于netifd,适用于AA及以上版本,不适用Backfire),如图。详情见github。

H3C_LuCI_20140718202919

—————————————————————————————————————————-

H3C Lite 轻量级H3C 802.1x校园网认证Linux客户端(SHNU 2013协议版本)

====特点====

轻量精简,单文件,源代码5K,编译后仅10K。使用原生RAW socket,无需安装python(YaH3C与OH3C)、libpcap(njit-client)等任何支持库。方便阅读,同时非常方便部署到路由器中。

用法:h3clite username password [interface]

本程序只负责认证,认证成功后请运行dhclient或udhcpc手动获取IP地址。

====编译====

桌面环境:
gcc h3clite.c -std=c99 -o h3clite

OpenWRT:
1、下载所需版本与硬件的交叉编译工具链(以版本12.09,硬件RG100A为例):http://downloads.openwrt.org/attitude_adjustment/12.09/brcm63xx/generic/OpenWrt-Toolchain-brcm63xx-for-mips-gcc-4.6-linaro_uClibc-0.9.33.2.tar.bz2
2、./toolchain-mips_gcc-4.6-linaro_uClibc-0.9.33.2/bin/mips-openwrt-linux-gcc h3clite.c -std=c99 -o h3clite

====定制====

本项目适用于SHNU 2013版协议。欢迎有技术基础的朋友fork项目以定制自己学校的版本。一些要点:
1、大部分情况只需修改文件末尾的eap_handle_XXX函数。对于不同版本的iNode,请用wireshark抓包得到新的数据替换eap_handle_identity中的base64字串。这部分的算法可参考njit-client。
2、如代码所示,SHNU的MD5 Challenge实际上只是个XOR,需要MD5算法的可从njit-client中粘一个过来。
3、使用多播方式触发EAPOL请将s_broadcast_addr修改为01-80-c2-00-00-03。
4、某些协议会使用其它的Request type,如用编号7的ALLOCATED代替MD5,需要修改authentication中间相应部分。

VB6 HTMLForm

关于使用IE技术借助HTML来实现DirectUI的实验博主断断续续做了四年,期间就是因为这个实验烧坏了脑子才转行考了音乐的研究生。

这次成果是一个自认为调整得还算比较好用的VB6窗体类,用以显示HTML对话框。基本效果如图所示:

HTMLForm

能看出这是嵌入了一个IE么?当然,可以做得更好看一些,前端网页能做多漂亮这里就可以有多漂亮。

为什么使用VB6。第一,VB窗体本身实现了作为OLE容器的功能,如果用原生C++,除了需要自己处理窗口消息外,还需要非常熟悉COM、OLE自动化等知识,很显然我不想再烧坏一次脑子然后再去考个什么专业的研。

第二,VB支持弱类型的IDispatch后期绑定,可以非常方便地直接在代码中与HTML互操作。例如HTMLForm公开了一个Object属性指向DOM中的window对象,那么完全可以写出如下代码:
frm.Object.Text1.innerText = “hello world”
frm.Object.Button1.style.display = “none”
另一方面,也可以直接通过Dim WithEvents As HTMLDocument来监听HTML上的事件,并通过window.event.srcElement.id来确定哪个按钮被点击等,编程灵活性完全不输于VB Form。

更多功能请读者自己探索。以下贴代码。

'Module: HTMLForm.frm r140520
'Author: gmsj0001(http:lxf.me)

Option Explicit

Implements olelib.IOleClientSite
Implements olelib2.IOleInPlaceSite
Implements olelib.IDocHostUIHandler

Private Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long

Private WithEvents m_WebBrowser As SHDocVw.WebBrowser
Private m_ScriptSite As Object

Public Event DocumentComplete()

Private Sub IDocHostUIHandler_EnableModeless(ByVal fEnable As olelib.BOOL)
    Err.Raise E_NOTIMPL
End Sub

Private Function IDocHostUIHandler_FilterDataObject(ByVal pDO As olelib.IDataObject) As olelib.IDataObject
    Err.Raise E_NOTIMPL
End Function

Private Function IDocHostUIHandler_GetDropTarget(ByVal pDropTarget As olelib.IDropTarget) As olelib.IDropTarget
    Err.Raise E_NOTIMPL
End Function

Private Function IDocHostUIHandler_GetExternal() As Object
    Set IDocHostUIHandler_GetExternal = m_ScriptSite
End Function

Private Sub IDocHostUIHandler_GetHostInfo(pInfo As olelib.DOCHOSTUIINFO)
    pInfo.cbSize = LenB(pInfo)
    pInfo.dwFlags = DOCHOSTUIFLAG_DIALOG + DOCHOSTUIFLAG_NO3DBORDER + DOCHOSTUIFLAG_SCROLL_NO + &H40000 + &H200000
End Sub

Private Sub IDocHostUIHandler_GetOptionKeyPath(pOLESTRchKey As Long, ByVal dw As Long)
    Err.Raise E_NOTIMPL
End Sub

Private Sub IDocHostUIHandler_HideUI()
    Err.Raise E_NOTIMPL
End Sub

Private Sub IDocHostUIHandler_OnDocWindowActivate(ByVal fActivate As olelib.BOOL)
    Err.Raise E_NOTIMPL
End Sub

Private Sub IDocHostUIHandler_OnFrameWindowActivate(ByVal fActivate As olelib.BOOL)
    Err.Raise E_NOTIMPL
End Sub

Private Sub IDocHostUIHandler_ResizeBorder(prcBorder As olelib.RECT, ByVal pUIWindow As olelib.IOleInPlaceUIWindow, ByVal fRameWindow As olelib.BOOL)
    Err.Raise E_NOTIMPL
End Sub

Private Sub IDocHostUIHandler_ShowContextMenu(ByVal dwContext As olelib.ContextMenuTarget, pPOINT As olelib.POINT, ByVal pCommandTarget As olelib.IOleCommandTarget, ByVal HTMLTagElement As Object)
    Err.Raise E_NOTIMPL
End Sub

Private Sub IDocHostUIHandler_ShowUI(ByVal dwID As Long, ByVal pActiveObject As olelib.IOleInPlaceActiveObject, ByVal pCommandTarget As olelib.IOleCommandTarget, ByVal pFrame As olelib.IOleInPlaceFrame, ByVal pDoc As olelib.IOleInPlaceUIWindow)
    Err.Raise E_NOTIMPL
End Sub

Private Sub IDocHostUIHandler_TranslateAccelerator(lpmsg As olelib.MSG, pguidCmdGroup As olelib.UUID, ByVal nCmdID As Long)
    Err.Raise E_NOTIMPL
End Sub

Private Function IDocHostUIHandler_TranslateUrl(ByVal dwTranslate As Long, ByVal pchURLIn As Long) As Long
    Err.Raise E_NOTIMPL
End Function

Private Sub IDocHostUIHandler_UpdateUI()
    Err.Raise E_NOTIMPL
End Sub

Private Function IOleClientSite_GetContainer() As olelib.IOleContainer
    Err.Raise E_NOINTERFACE
End Function

Private Function IOleClientSite_GetMoniker(ByVal dwAssign As olelib.OLEGETMONIKER, ByVal dwWhichMoniker As olelib.OLEWHICHMK) As olelib.IMoniker
    Err.Raise E_NOTIMPL
End Function

Private Sub IOleClientSite_OnShowWindow(ByVal fShow As olelib.BOOL)
End Sub

Private Sub IOleClientSite_RequestNewObjectLayout()
    Err.Raise E_NOTIMPL
End Sub

Private Sub IOleClientSite_SaveObject()
    Err.Raise E_NOTIMPL
End Sub

Private Sub IOleClientSite_ShowObject()
End Sub

Private Sub IOleInPlaceSite_CanInPlaceActivate()
End Sub

Private Sub IOleInPlaceSite_ContextSensitiveHelp(ByVal fEnterMode As olelib.BOOL)
End Sub

Private Sub IOleInPlaceSite_DeactivateAndUndo()
End Sub

Private Sub IOleInPlaceSite_DiscardUndoState()
End Sub

Private Function IOleInPlaceSite_GetWindow() As Long
    IOleInPlaceSite_GetWindow = Me.hwnd
End Function

Private Sub IOleInPlaceSite_GetWindowContext(ppFrame As olelib.IOleInPlaceFrame, ppDoc As olelib.IOleInPlaceUIWindow, lprcPosRect As olelib.RECT, lprcClipRect As olelib.RECT, lpFrameInfo As olelib.OLEINPLACEFRAMEINFO)
    Err.Raise E_NOTIMPL
End Sub

Private Sub IOleInPlaceSite_OnInPlaceActivate()
End Sub

Private Sub IOleInPlaceSite_OnInPlaceDeactivate()
End Sub

Private Sub IOleInPlaceSite_OnPosRectChange(lprcPosRect As olelib.RECT)
End Sub

Private Sub IOleInPlaceSite_OnUIActivate()
End Sub

Private Sub IOleInPlaceSite_OnUIDeactivate(ByVal fUndoable As olelib.BOOL)
End Sub

Private Sub IOleInPlaceSite_Scroll(ByVal scrollX As Long, ByVal scrollY As Long)
End Sub

Public Sub Navigate(ByVal URL As String)
    m_WebBrowser.Navigate2 URL
End Sub

Public Sub SetScriptSite(pDisp As Object)
    Set m_ScriptSite = pDisp
End Sub

Public Property Get Object()
    Set Object = m_WebBrowser.Document.parentWindow
End Property

Public Property Get Browser()
    Set Browser = m_WebBrowser
End Property

Private Sub Form_Initialize()
    Load Me
End Sub

Private Sub Form_Load()
    Set m_WebBrowser = New WebBrowser
    Dim pOleObject As olelib.IOleObject
    Dim pRect As olelib.RECT
    Set pOleObject = m_WebBrowser
    pOleObject.SetClientSite Me
    pOleObject.DoVerb OLEIVERB_INPLACEACTIVATE, 0, Me, 0, Me.hwnd, pRect
    m_WebBrowser.Silent = True
End Sub

Private Sub Form_Resize()
    Dim pInPlaceObject As olelib.IOleInPlaceObject
    Dim rcClient As RECT
    GetClientRect Me.hwnd, rcClient
    Set pInPlaceObject = m_WebBrowser
    pInPlaceObject.SetObjectRects rcClient, rcClient
End Sub

Private Sub m_WebBrowser_DocumentComplete(ByVal pDisp As Object, URL As Variant)
    If URL = "" Then Exit Sub
    RaiseEvent DocumentComplete
End Sub

Private Sub m_WebBrowser_NewWindow2(ppDisp As Object, Cancel As Boolean)
    Dim pNewWindow As New HTMLForm
    Set ppDisp = pNewWindow.Browser
    pNewWindow.Show
End Sub

CHDEMU V2 LaTale Emulator Readme (English Version)

Part I: About this project

CHDEMU is a server emulator of the MMORPG La Tale, which is developed by Actoz Inc. from Korea. CHDEMU IS NOT A PRIVATE SERVER. It should be only used for self use and for study purpose.

The project is developed by gmsj0001, the author of this blogger.Further development may be canceled because of a public leak of La Tale official server occurred at Dec 2013. Thus the author decided to make this version partial open sourced in Public Domain. You can download, modify, complie, or re release this project freely for studying, adding feature, or fixing issues.

If you like this project, either the distribution or the source, you can consider making a donation to me. Thank you.

Part II: Features

This version of CHDEMU provides the following features.

O Tour around the game world
O Learn and cast skills
O Obtain and equip items

The emulator is tested on the 20130117 release of USA La Tale and compatible with any client from version 274 to 283. Suggest download links will be provided at the end of the article.

Part III: Simple Usage

Configures is located in chdemu.ini. Available settings are:

IP, Port: The IP address and the port for listening.
ResourceDir: Directory of the data files. Fill in your game client path.
Pack: Use SPF pack files or not.

First login will create the user. Please provide the correct username and password.

The following standard GM commands are available (GM commands start with double slashes):

portal PosX PosY [StageID] [MapGroupID]
giveitem ItemID [Count]

A client patch is provided to skip the XTrap loading and enable Non-English input methods.

Part IV: Advanced topics

chdemu.db is a SQLITE database. Advanced users can use related tools to edit the saved data.

There is a packet sniffer tool provided in the download links. You can use it for analyzing the La Tale packet structs.

Users who know about programming can visit github to checkout or fork the partial sources for building a custom server yourself.

Part V: Links

CHDEMU: http://lxf.me/www/project/latale/chdemu_v2.7z
Client patch: http://lxf.me/www/upload/latale/lataleusa_patch.7z
Packet sniffer: http://lxf.me/www/upload/latale/sniffer.7z
Source: http://github.com/gmsj0001/chdemu
USA client (20130117): http://www.gamefront.com/files/22901610
USA client (v9.0, 2012): http://download.cnet.com/La-Tale/3000-7540_4-10877348.html

CHDEMU V2 彩虹岛模拟器发布暨使用说明

(〇)愤怒的吐槽、部分开源计划

小鬼本来打算寒假回家再完善完善再加点诸如NPC购买什么的功能的,结果就有那么一个傻逼把服务端给公开了。所以首先让我们热烈祝贺我们热爱或曾经热爱的彩虹岛即将倒闭。

由于服务端的公开泄漏,小鬼五年半来从未停止努力的彩虹岛单机版或模拟器项目也就失去了其意义。现宣布CHDEMU项目于即日正式终结,作者未来将不对本项目进行任何的维护。源代码将以公共领域形式(Public Domain)部分开源,您可以自由地修改、编译、再发布本项目的代码用以学习、添加功能、修正BUG等目的。

如果您喜欢本项目的成果或认为本项目的源代码给您的学习带来很大帮助,您可以考虑向我捐助

(一)项目介绍、与“私服”的区别

CHDEMU是大型多人在线角色扮演游戏(MMORPG)La Tale(中文名:彩虹岛)的单机与局域网模拟器。网络游戏La Tale由韩国Actoz开发,中国大陆区于2007年至2012年由盛大网络代理运营,2013年以后由盛大游戏独立研发。以上组织视具体情况拥有该网络游戏的合法版权。

La Tale在2011年在台湾区由爱玩家运营时发生过运营商人为原因的服务端二进制程序泄漏,泄漏程序两年来在地下非公开传播并用于架设私服。2013年12月,该二进制程序在中国大陆被短时间公开到因特网上,目前尚不能评估该事件对全球La Tale运营的负面影响。

本项目由本人,即此博客博主、曾经的资深彩虹岛玩家、现上海师范大学音乐学院钢琴系研究生gmsj0001同学独立开发,初衷是当游戏停运或线上虚拟社会 发展不乐观时能够帮助老玩家在线下找回曾经美好的回忆。本项目最初于2008年下半年发起,所有代码均为自主研发。与泄漏的官方服务端没有任何关系。

即便如此,请勿架设以营利为目的的私服,不论是使用您有幸得到的官方服务端,或是利用本项目的部署或源代码。于道德上讲,架设私服会严重影响彩虹岛的可持续发展,于法律上讲,架设私服在各国均属于违法行为。作者将不对因擅自架设私服的行为引来的纠纷负责。

(二)功能与特性

本版本实现了以下功能:

1、上一版本的跑地图(小鬼版彩虹岛音乐播放器 = =!)
2、技能的学习与使用
3、物品的获取、装备的穿戴

总的来说,该版本的新特性是可以实现像模像样的纸娃娃系统,可以放喜欢的技能并截图。

协议版本采用“经典”的274版核心,兼容274-283范围的客户端版本。开发测试使用的是美服2013年1月的280709客户端。

(三)简单使用说明

客户端可以使用274-283之间的任何版本。汉语用户可考虑使用274版的私服客户端(繁体中文,大陆用户请使用乱码大师或AppLocale调整编码),英语用户可使用美服V7.0至20130117之间的任何版本。参考下载地址见文章末尾。客户端的命令行启动不再赘述。

使用美服版本的用户可使用作者提供的一个微型补丁绕过XTrap加载与开启输入法。

服务端配置文件为chdemu.ini,相关设置说明如下:

IP、Port:监听的IP地址与端口
ResourceDir:资源文件目录,可设置为客户端的目录
Pack:资源文件是否使用SPF打包

游戏登陆时会自动创建不存在的用户名,第一次登陆输入的密码即为设置的密码。结束游戏时请按ESC键点击结束游戏,请勿直接关闭模拟器程序,否则游戏将无法得到保存。

支持以下标准GM命令(GM命令以双斜杠开始):

传送:portal XPos YPos [StageID] [MapGroupID]
获取物品:giveitem ItemID [Count]

地图号请自行测试,物品号可以用搜索引擎找到,有时间我可能会整理一份发出来。

(四)高级使用说明

chdemu.db为SQLITE数据库,有技术基础的同学可使用相关工具打开编辑其中的数据。

下载链接里还提供了一个封包侦听工具,有兴趣的同学可用来分析协议,理论上对当前多个地区运营的版本有效,且不受NP限制。

技术基础更NB的同学可以前往github下载或fork部分开源的源代码,谨代表作者本人为可能比较糟糕的编码水平给读者带来的困惑致以最诚挚的歉意。

(五)相关下载地址

模拟器:http://lxf.me/www/upload/latale/chdemu_v2.7z
美服补丁:http://lxf.me/www/upload/latale/lataleusa_patch.7z
封包工具:http://lxf.me/www/upload/latale/sniffer.7z
源代码:http://github.com/gmsj0001/chdemu
274版私服客户端:http://pan.baidu.com/share/link?shareid=1552961789&uk=1261605962
美服20130117版:http://www.gamefront.com/files/22901610(国内用户需爬墙)
美服2012年9.0版:http://download.cnet.com/La-Tale/3000-7540_4-10877348.html