├── zhtools ├── __init__.py ├── xpinyin.py ├── test_langconv.py └── langconv.py ├── gbk2sjis.dat ├── inner.nst ├── ons ├── nsutils.py ├── __init__.py ├── handler.py └── saver.py ├── utils.py ├── readme.md ├── flag_struct.txt ├── portable.py ├── docopt.py ├── nsttest.txt ├── gbk2sjis.py ├── onssaver.py ├── nstemplate.py └── LICENSE.txt /zhtools/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gbk2sjis.dat: -------------------------------------------------------------------------------- 1 | 吧 巴 2 | 么 莫 3 | 飞 飛 4 | 她 他 5 | 发 發 6 | 啊 阿 7 | 跑 袍 8 | 吗 馬 9 | 还 還 10 | 哪 那 11 | 呢 尼 12 | 呐 肭 13 | 嗯 恩 14 | 你 尼 15 | 啦 拉 16 | 哎 艾 17 | 瞪 登 18 | 吓 下 19 | 够 勾 20 | 內 内 21 | 佈 布 22 | 腳 脚 23 | 值 値 24 | 增 増 25 | 踢 裼 26 | 咱 俺 27 | 哼 亨 28 | 呃 厄 29 | — 一 30 | - 一 31 | ~ ‾ 32 | " 」 -------------------------------------------------------------------------------- /inner.nst: -------------------------------------------------------------------------------- 1 | 2 | ; 我是被导入的段落 3 | @python 4 | @output = __ 5 | @__ = lambda s: output('; 证明我修改了输出: '+s) 6 | @__('我的名字是: {__current_name}') 7 | @__base += 500 8 | @__('我把基址增加了500, 我的基址是: {__base}') 9 | @## 单引号里 @{var} 和 {var} 都可以使用 10 | @__('之前的内嵌 python 脚本使用 for 循环,变量 name 的值是 @{name}') 11 | @name = '新名字' 12 | @__('我把它改成了 {name}') 13 | 14 | @def test2 %1 15 | 测试输出BASE @{__base} 16 | click 17 | return 18 | 19 | -------------------------------------------------------------------------------- /ons/nsutils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | from portable import chr, to_unicode, UEMPTY, py3k 5 | 6 | _fullwide_map = [chr(65248 + i) for i in range(128)] 7 | _fullwide_map[32] = to_unicode(' ') 8 | _fullwide_map = UEMPTY.join(_fullwide_map) 9 | 10 | 11 | def get_widechar_converter(excepts=None): 12 | ''' 13 | >>> f = get_widechar_converter(r'/\@') 14 | >>> s = 'wc是@厕所的意思.../' 15 | >>> print(f(s) if py3k else f(s.decode('utf-8')).encode('utf-8')) 16 | wc是@厕所的意思.../ 17 | ''' 18 | if excepts: 19 | fm = list(_fullwide_map) 20 | for char in excepts: 21 | fm[ord(char)] = char 22 | fm = UEMPTY.join(fm) 23 | else: 24 | fm = _fullwide_map 25 | return lambda s: s.translate(fm) 26 | 27 | 28 | if __name__ == '__main__': 29 | import doctest 30 | doctest.testmod() 31 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | 6 | def cd_once(directory, func, *args, **kwargs): 7 | curpath = os.path.abspath('.') 8 | os.chdir(directory) 9 | ret = func(*args, **kwargs) 10 | os.chdir(curpath) 11 | return ret 12 | 13 | 14 | class Struct(object): 15 | def __init__(self, str=None, **entries): 16 | if str and entries == {}: 17 | self.from_str(str) 18 | else: 19 | if str is not None: 20 | entries['str'] = str 21 | self.__dict__.update(entries) 22 | 23 | def to_str(self, format='json'): 24 | d = dict((key, value) for key, value in self.__dict__.items() 25 | if not key.startswith('_')) 26 | return json.dumps(d) 27 | 28 | def from_str(self, s, format='json'): 29 | d = json.loads(s) 30 | self.__dict__.update(d) 31 | return self 32 | -------------------------------------------------------------------------------- /ons/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Copyright (c) 2011-2012 Skydark. All rights reserved. 4 | 5 | Skydark Chen 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | ''' 21 | 22 | __author__ = 'Skydark Chen' 23 | __license__ = 'GPLv2' 24 | 25 | 26 | class ONScripterError(Exception): 27 | pass 28 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # My NScripter Tools 2 | 3 | 一些大概没有用了的与 NScript 有关的东西。 4 | 5 | License: GPLv2 6 | 7 | 但 nstemplate.py 和 portable.py 除外。 8 | 它们并不依赖任何 GPL 项目,并且可以单独运行。 9 | 这两者均是 Public Domain 的。 10 | 11 | * gbk2sjis.py 将简体 nscript.dat/00~99.txt 转换为日文编码。 12 | 13 | 对不支持 GBK 而仅支持日文编码的 ONS 模拟器,当运行简体移植的时候会乱码。 14 | 这个工具能将原脚本转换为日文编码。 15 | 16 | 由于很多汉字在日文中并不存在,故会进行简繁转换和一些字符替换。 17 | 部分无法自动处理的字符替换定义在 `gbk2sjis.dat` 中。 18 | 19 | 使用方法: 20 | 21 | 直接运行弹出 GUI 界面,选择要转换的脚本(自动判断是nscript.dat还是某个txt文件还是00~99.txt)。 22 | 23 | 或 24 | `python gbk2sjis.py [选项] 原始文件/目录 [输出文件]` 25 | 26 | 输出文件默认是当前目录下的 `out.txt` . 27 | 28 | 选项除了帮助(`-h`)外只有一个:`-m auto/manual`,当出现无法转换的字符时是自动选择还是手动输入。 29 | 自动选择是根据拼音选择的。 30 | 31 | * onssaver.py 当替换脚本时根据新旧脚本的差异修复存档。 32 | 33 | NScripter 脚本更新后原有的存档会出现问题。这个工具在新旧脚本差异不大时修复存档。 34 | 35 | TODO(and never do):改用 `difflib` 修复。 36 | 37 | 使用方法: 38 | 39 | `python onssaver.py 原始脚本目录 新脚本目录 [存档文件]` 40 | 41 | 如果不给出存档文件,默认为原始脚本目录中的所有 `save*.dat` 文件。 42 | 43 | 新生成的存档会保存在新脚本目录中。 44 | 45 | * nstemplate.py NS脚本模板。已抛弃。 46 | 47 | 使用方法: 48 | 49 | 直接执行 `python nstemplate.py` 获取帮助。 50 | 51 | 运行 `python nstemplate.py nsttest.txt out.txt`,对比 `nsttext.txt` 和 `out.txt`. 52 | -------------------------------------------------------------------------------- /flag_struct.txt: -------------------------------------------------------------------------------- 1 | ;用来存放flag的结构 2 | 3 | ; 初始化结构 4 | ; numalias Address, 100 5 | ; numalias Length, 5 6 | ; >>> flags_init Address, Length 7 | ; $Address = "00000" & %Address = 5 8 | @def flags_init %1, %2 9 | mov %%1, %2 10 | mov $%1, "" 11 | for %2 = %2 to 1 step -1 12 | add $%1, "0" 13 | next 14 | return 15 | 16 | ; 设置某个位置的flag 17 | ; 注意,flag的位置从1到Length, 并只能将该位置的值设置为0~9中的数值 18 | ; >>> flags_set Address, 3, 2 19 | ; $Address = "00200" 20 | @def flags_set %1, %2, %3 21 | if %3 > 9 mov %3, 9:skip 2 22 | if %3 < 0 mov %3, 0 23 | mid $1, $%1, %2, %%1-%2 24 | mid $%1, $%1, 0, %2-1 25 | itoa $2, %3 26 | add $%1, $2 27 | add $%1, $1 28 | return 29 | 30 | ; 取得某个位置的flag 31 | ; numalias Value, 1 32 | ; >>> flags_get %Value, Address, 3 33 | ; %Value = 2 34 | @def flags_get i%3, %1, %2 35 | mid $1, $%1, %2-1, 1 36 | atoi %%3, $1 37 | return 38 | 39 | ; 将某个位置的flag的值增加一定的值 40 | ; 最后一个参数是增加的值,用负数即可表示减少 41 | ; 注意,最终值仍然被限定在0~9之间 42 | ; >>> flags_change Address, 3, 4 43 | ; $Address = "00600" 44 | @def flags_change %1, %2, %3 45 | mid $1, $%1, %2, %%1-%2 46 | mid $2, $%1, %2-1, 1 47 | mid $%1, $%1, 0, %2-1 48 | atoi %2, $2 49 | add %3, %2 50 | if %3 > 9 mov %3, 9:skip 2 51 | if %3 < 0 mov %3, 0 52 | itoa $2, %3 53 | add $%1, $2 54 | add $%1, $1 55 | return 56 | -------------------------------------------------------------------------------- /portable.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sys 4 | 5 | py = sys.version_info 6 | py3k = py >= (3, 0, 0) 7 | 8 | default_encoding = sys.getfilesystemencoding() 9 | if default_encoding.lower() == 'ascii': 10 | default_encoding = 'utf-8' 11 | 12 | from codecs import open 13 | 14 | if py3k: 15 | is_bytes = lambda s: isinstance(s, bytes) 16 | is_unicode = lambda s: isinstance(s, str) 17 | is_str = lambda s: isinstance(s, (bytes, str)) 18 | encode = lambda s, encoding=None: bytes(s, encoding or default_encoding) 19 | unistr = str 20 | bytechr = lambda c: bytes(chr(c), 'charmap') 21 | chr = chr 22 | UEMPTY = '' 23 | b_ord = lambda c: c 24 | chr_join = lambda l: bytes(l) 25 | else: 26 | is_bytes = lambda s: isinstance(s, str) 27 | is_unicode = lambda s: isinstance(s, unicode) 28 | is_str = lambda s: isinstance(s, basestring) 29 | encode = lambda s, encoding=None: s.encode(encoding or default_encoding) 30 | unistr = unicode 31 | bytechr = chr 32 | chr = unichr 33 | UEMPTY = ''.decode('utf-8') 34 | b_ord = lambda c: ord(c) 35 | chr_join = lambda l: b''.join(bytechr(c) for c in l) 36 | 37 | decode = lambda s, encoding=None: s.decode(encoding or default_encoding) 38 | 39 | to_bytes = lambda s, encoding=None: \ 40 | encode(s, encoding) if is_unicode(s) else s 41 | to_unicode = lambda s, encoding=None: \ 42 | s if is_unicode(s) else decode(s, encoding) 43 | B = to_bytes 44 | U = lambda s: to_bytes(s).decode('raw_unicode_escape') 45 | 46 | 47 | if py3k: 48 | from urllib.request import urlopen, Request 49 | else: 50 | from urllib2 import urlopen, Request 51 | 52 | if py3k: 53 | from io import BytesIO as StringIO 54 | else: 55 | from cStringIO import StringIO 56 | 57 | if py3k: 58 | import tkinter 59 | import tkinter.filedialog as tkFileDialog 60 | import tkinter.messagebox as tkMessageBox 61 | else: 62 | import Tkinter as tkinter 63 | import tkFileDialog 64 | import tkMessageBox 65 | -------------------------------------------------------------------------------- /docopt.py: -------------------------------------------------------------------------------- 1 | from getopt import gnu_getopt, GetoptError 2 | from ast import literal_eval 3 | import sys 4 | import re 5 | 6 | 7 | class Option(object): 8 | 9 | def __init__(self, short=None, long=None, value=False, parse=None): 10 | self.is_flag = True 11 | if parse: 12 | split = parse.strip().split(' ') 13 | options = split[0].replace(',', ' ').replace('=', ' ') 14 | description = ''.join(split[1:]) 15 | for s in options.split(): 16 | if s.startswith('--'): 17 | long = s.lstrip('-') 18 | elif s.startswith('-'): 19 | short = s.lstrip('-') 20 | else: 21 | self.is_flag = False 22 | if not self.is_flag: 23 | matched = re.findall('\[default: (.*)\]', description) 24 | value = argument_eval(matched[0]) if matched else False 25 | short = short + ':' if short else None 26 | long = long + '=' if long else None 27 | self.short = short 28 | self.long = long 29 | self.value = value 30 | 31 | @property 32 | def name(self): 33 | s = self.long or self.short 34 | s = s.rstrip(':').rstrip('=') 35 | ret = s[0] if s[0].isalpha() else '_' 36 | for ch in s[1:]: 37 | ret += ch if ch.isalpha() or ch.isdigit() else '_' 38 | return ret 39 | 40 | @property 41 | def forms(self): 42 | if self.short: 43 | yield '-' + self.short.rstrip(':') 44 | if self.long: 45 | yield '--' + self.long.rstrip('=') 46 | 47 | def __repr__(self): 48 | return 'Option(%s, %s, %s)' % (repr(self.short), 49 | repr(self.long), 50 | repr(self.value)) 51 | 52 | def __eq__(self, other): 53 | return repr(self) == repr(other) 54 | 55 | def __ne__(self, other): 56 | return not self == other 57 | 58 | 59 | class Options(object): 60 | 61 | def __init__(self, **kw): 62 | self.__dict__ = kw 63 | 64 | def __eq__(self, other): 65 | return type(self) is type(other) and \ 66 | self.__dict__ == other.__dict__ 67 | 68 | def __ne__(self, other): 69 | return not self == other 70 | 71 | def __repr__(self): 72 | return 'Options(%s)' % ',\n '.join("%s=%s" % (kw, repr(a)) 73 | for kw, a in self.__dict__.items()) 74 | 75 | 76 | def argument_eval(s): 77 | try: 78 | return literal_eval(s) 79 | except (ValueError, SyntaxError): 80 | return s 81 | 82 | 83 | def docopt(doc, args=sys.argv[1:], help=True, version=None): 84 | docopts = [Option(parse='-' + s) for s in re.split('^ *-|\n *-', doc)[1:]] 85 | try: 86 | getopts, args = gnu_getopt(args, 87 | ''.join(d.short for d in docopts if d.short), 88 | [d.long for d in docopts if d.long]) 89 | except GetoptError as e: 90 | exit(e.msg) 91 | for k, v in getopts: 92 | for o in docopts: 93 | if k in o.forms: 94 | o.value = True if o.is_flag else argument_eval(v) 95 | if help and k in ('-h', '--help'): 96 | exit(doc.strip()) 97 | if version is not None and k == '--version': 98 | exit(version) 99 | return Options(**dict((o.name, o.value) for o in docopts)), args 100 | -------------------------------------------------------------------------------- /nsttest.txt: -------------------------------------------------------------------------------- 1 | @#; NSTemplate.py 是一个基于 python 的 NS 脚本模板工具 2 | @#; 作者: skydarkchen 3 | @#; 发布于公共领域。 4 | @## 直接不带命令行参数执行它可以得到使用帮助。 5 | @## 这篇文档主要介绍其语法特性。这篇文档本身可以被 NSTemplate.py 处理。 6 | @## 另外,这个工具将自动检测文件编码,并将文件转换为适合 NS 的形式, 7 | @## 因此可以用 utf-8 等编码撰写模板文件。 8 | 9 | @## -------------------------------------- 10 | @## 首先,以 @## 开头的行是注释,不会被输出。 11 | @## 而对于以 @# 开头,紧接着非 # 符号的行,将去掉@#后直接输出,不做其他处理。 12 | @## 下面一些行用 @#; 开头,是为了使得输出也是 NS 的注释。 13 | @#; -------------------------------------- 14 | @## 所有 NS 中的变量(%1, $2)等的地址(1, 2)在这里都是*相对*的。 15 | @## 通过指定 @{__base} (即命令行参数中的 --base)可以使所有的变量地址增加 @{__base} 16 | @## 如果命令行参数中不指定 --base,其默认值是1000. 17 | 18 | ; 示例变量地址偏移: 19 | mov %1, 0 20 | 21 | @## 注意: 双引号中包含的变量地址不会被处理 22 | mov $1, "%1" 23 | 24 | @#; -------------------------------------- 25 | @## 以 @def 开头的行用来定义函数头。 26 | @## 对应的 defsub 指令将在文件末尾自动生成,可以在 NS 的 *define 节用 gosub 调用。 27 | 28 | ; 示例定义函数 29 | @def test 30 | 测试无参数函数 31 | return 32 | 33 | ; 将字符串用$1补齐到至少%3位 34 | ; >>> atoaex $1, "xyza", 3, " " 35 | ; $1 = "xyza" 36 | ; >>> atoaex $1, "x", 3, " " 37 | ; $1 = " x" 38 | @def atoaex s%1, $2, %3, $1 39 | mov $%1, $2 40 | len %4, $%1 41 | if %4 >= %3 return 42 | sub %3, %4 43 | for %4 = 1 to %3 44 | mov $%1, $1+$%1 45 | next 46 | return 47 | 48 | @#; -------------------------------------- 49 | @## 行内使用 @{表达式} 可以表达一些 python 变量。 50 | @## 内嵌支持的 python 变量有: 51 | @## __base : 地址偏移基址,即命令行参数传递过去的 --base 参数。 52 | @## __max_var : 到当前位置为止读到的最大的变量地址。 53 | @## __current_name : 当前脚本的名称(默认是文件名去掉后缀)。 54 | @## 注意: 双引号中包含的内嵌表达式不会被处理 55 | 56 | ; 测试内嵌表达式 57 | mov $1, "@{__base}" 58 | 变量%1应该是%@{__base+1}。 59 | 60 | @#; -------------------------------------- 61 | @## 以 @python 开头的行,其之后连续的以@开头的段落将视为 python 脚本执行。 62 | @## 注: NS 变量的偏移仍然有效,但是注意它们不对双引号内的文本产生影响。 63 | @## @{}的内嵌语法不再有效。 64 | @## 除了上面列出的 python 变量外,还可以使用下面的函数: 65 | @## __(s) : 输出字符串 s. s 中可以使用 {变量名} 嵌入变量值。 66 | @## 注意: @{} 里可以是*表达式*,这里只是*变量名*。 67 | @## 如果需要表达式的值,直接在脚本中计算即可。 68 | @## __s(s) : 将逗号分隔的字符串 s 切分成数组。 69 | 70 | ; 测试内嵌 python 脚本 71 | @python 72 | @for name in __s('010203, 110212, 010303'): 73 | @ if name.startswith('0'): 74 | @ __('; if $1="*s{name}" goto *fateroute_controller') 75 | @ else: 76 | @ v = int(__import__('math').sqrt(int(str(__base+1).strip()))**2) 77 | @ __('; mov $1, "%1": mov ${v}, "{name}"') 78 | @__(';@{name}不会以@{{}}的形式解析,因此@依然留了下来') 79 | 80 | @#; -------------------------------------- 81 | @## 可以使用 @import 导入另一个脚本,其语法为: 82 | @## `@import 文件名` 或 `@import 文件名 as 名称` 83 | @## 如果不给名称,导入段落的名称默认是文件名(去掉扩展名)。 84 | 85 | ; 测试导入 86 | @import inner.nst 87 | 88 | @## 导入段落的变量是与当前变量分开的,可以如下调用其中的变量: 89 | ; 被导入的段落的基址是 @{inner.__base} 90 | @python 91 | @__('; 我的基址仍然是 {__base}, 我的 name 变量值仍然是 {name}') 92 | 93 | @#; -------------------------------------- 94 | @## 最后一小段测试。 95 | 96 | ; 分割字符串 97 | ; splits与原版split函数不同的是,分割后的右半边是完整的; 98 | ; 而rsplits则是从右到左分割的; 99 | ; >>> split "a,b,c", ",", $1, $2 100 | ; $1 = "a" & $2 = "b" 101 | ; >>> splits "a,b,c", ",", $1, $2 102 | ; $1 = "a" & $2 = "b,c" 103 | ; >>> rsplits "a,b,c", ",", $1, $2 104 | ; $1 = "a,b" & $2 = "c" 105 | 106 | @def splits $1, $2, s%1, s%2 107 | split $1, $2, $%1 108 | len %3, $%1:inc %3 109 | len %1, $1:sub %1,%3 110 | if %1<=0 mov $%2,"":return 111 | mid $%2, $1, %3, %1 112 | return 113 | 114 | @def rsplits $1, $2, s%1, s%2 115 | split $1, $2, $%1 116 | if $1 = $%1 mov $%2, "":return 117 | len %3, $%1 118 | mov %4, %3+1 119 | ; 120 | mid $3, $1, %4, 1 121 | if $3 = $2 mov %3, %4 122 | if $3 != "" inc %4:skip -3 123 | mid $%1, $1, 0, %3 124 | sub %4, %3+1 125 | mid $%2, $1, %3+1, %4 126 | return 127 | -------------------------------------------------------------------------------- /zhtools/xpinyin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Translate chinese hanzi to pinyin by python 5 | Created by Eric Lo on 2010-05-20. 6 | Copyright (c) 2010 __lxneng@gmail.com__. http://lxneng.com All rights reserved. 7 | """ 8 | 9 | """ 10 | Forked by skydarkchen 11 | """ 12 | 13 | import os.path 14 | 15 | try: 16 | chr = unichr 17 | except NameError: 18 | pass 19 | 20 | VERSION = '0.3a' 21 | 22 | 23 | class Pinyin(object): 24 | """translate chinese hanzi to pinyin by python, inspired by flyerhzm’s 25 | `chinese\_pinyin`_ gem 26 | .. _chinese\_pinyin: https://github.com/flyerhzm/chinese_pinyin 27 | 28 | usage(python3) 29 | ----- 30 | :: 31 | >>> p = Pinyin() 32 | >>> p.get_pinyin("上海") 33 | 'shanghai' 34 | >>> p.get_pinyin("上海", tone=True) 35 | 'shang4hai3' 36 | >>> p.get_initials("上") 37 | 'S' 38 | >>> print(''.join(p.py2hz('shang4'))) 39 | 丄上姠尙尚蠰銄鑜 40 | >>> print(''.join(p.py2hz('a'))) 41 | 吖腌錒锕阿嗄阿阿啊阿 42 | """ 43 | 44 | data_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), \ 45 | 'Mandarin.dat') 46 | 47 | def __init__(self): 48 | self.dict = {} 49 | self.revdict = {} 50 | for line in open(self.data_path): 51 | k, v = line.strip().split('\t') 52 | v = v.lower().split(' ') 53 | hz = chr(int('0x%s' % k, 16)) 54 | self.dict[hz] = v 55 | for vkey in v: 56 | self.revdict.setdefault(vkey, []) 57 | self.revdict[vkey].append(hz) 58 | 59 | def py2hz(self, pinyin): 60 | if pinyin == '': 61 | return [] 62 | pinyin = pinyin.lower() 63 | if pinyin[-1].isdigit(): 64 | return self.revdict.get(pinyin, []) 65 | ret = [] 66 | for i in range(1, 6): 67 | key = '%s%s' % (pinyin, i) 68 | ret += self.revdict.get(key, []) 69 | return ret 70 | 71 | def get_pinyin(self, chars='', splitter='', tone=False): 72 | result = [] 73 | for char in chars: 74 | v = self.dict.get(char, None) 75 | if v: 76 | v = v[0] 77 | if not tone and v[-1].isdigit(): 78 | v = v[:-1] 79 | else: 80 | v = char 81 | result.append(v) 82 | return splitter.join(result) 83 | 84 | def get_initials(self, char=''): 85 | if char == '': 86 | return '' 87 | return self.dict.get(char, [char])[0][0].upper() 88 | 89 | 90 | if __name__ == '__main__': 91 | import unittest 92 | 93 | class PinyinTestCase(unittest.TestCase): 94 | def setUp(self): 95 | import sys 96 | py = sys.version_info 97 | self.py3k = py >= (3, 0, 0) 98 | 99 | self.py = Pinyin() 100 | 101 | def to_unicode(self, s): 102 | if self.py3k: 103 | return s 104 | return s.decode('utf-8') 105 | 106 | def test_get_pinyin(self): ## test method names begin 'test*' 107 | s = self.to_unicode('上A2#海') 108 | a = self.to_unicode('shangA2#hai') 109 | aa = self.to_unicode('shang4A2#hai3') 110 | aaa = self.to_unicode('shang A 2 # hai') 111 | self.assertEqual(self.py.get_pinyin(s), a) 112 | self.assertEqual(self.py.get_pinyin(s, tone=True), aa) 113 | self.assertEqual(self.py.get_pinyin(s, splitter=' '), aaa) 114 | 115 | def test_get_initials(self): 116 | s = self.to_unicode('上') 117 | a = self.to_unicode('S') 118 | self.assertEqual(self.py.get_initials(s), a) 119 | 120 | def test_py2hz(self): 121 | s1 = self.to_unicode('shang4') 122 | s2 = self.to_unicode('a') 123 | a1 = self.to_unicode('丄上姠尙尚蠰銄鑜') 124 | a2 = self.to_unicode('吖腌錒锕阿嗄阿阿啊阿') 125 | self.assertEqual(''.join(self.py.py2hz(s1)), a1) 126 | self.assertEqual(''.join(self.py.py2hz(s2)), a2) 127 | 128 | unittest.main() 129 | -------------------------------------------------------------------------------- /gbk2sjis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ Convert GBK nscripter dat to SHIFT-JIS version 5 | Usage: gbk2sjis.py [options] origin_file output_file=['./out.txt'] 6 | or gbk2sjis.py [options] origin_directory output_file=['./out.txt'] 7 | 8 | Options: 9 | -h --help show this help 10 | -m --method METHOD set method for missing characters, auto/manual [default: auto] 11 | 12 | """ 13 | 14 | from os import path 15 | import sys 16 | import codecs 17 | import random 18 | 19 | from docopt import docopt 20 | 21 | from ons.handler import ONSHandler 22 | from portable import decode, encode, chr, UEMPTY, py3k 23 | 24 | 25 | def help(): 26 | exit(__doc__.strip()) 27 | 28 | 29 | def hzconvert(text, from_, to_, method='auto', chardict=None): 30 | assert from_ == 'gbk' and to_ == 'sjis' and method == 'auto' 31 | 32 | from zhtools import chconv, xpinyin 33 | cdict = chconv.Chinese2Kanji_Table 34 | for k, v in chardict.items(): 35 | try: 36 | encode(v, 'SHIFT-JIS') 37 | cdict[ord(k)] = ord(v) 38 | except UnicodeEncodeError: 39 | pass 40 | 41 | xpy = xpinyin.Pinyin() 42 | guess_chars = set() 43 | except_chars = set() 44 | 45 | def gbk_to_sjis(exc): 46 | if not isinstance(exc, UnicodeEncodeError): 47 | raise exc 48 | newpos = exc.end 49 | char = exc.object[exc.start:exc.end] 50 | c = ord(char) 51 | if c in cdict: 52 | # print('%s: %s matched!' %(char, cdict[c])) 53 | return chr(cdict[c]), newpos 54 | pinyin = xpy.get_pinyin(char) 55 | ok = [] 56 | if pinyin: 57 | for newchar in xpy.py2hz(pinyin): 58 | try: 59 | encode(newchar, 'SHIFT-JIS') 60 | ok.append(newchar) 61 | except UnicodeEncodeError: 62 | pass 63 | for newchar in xpy.py2hz(pinyin[:-1]): 64 | try: 65 | encode(newchar, 'SHIFT-JIS') 66 | ok.append(newchar) 67 | except UnicodeEncodeError: 68 | pass 69 | if ok: 70 | newchar = random.choice(ok) 71 | cdict[c] = ord(newchar) 72 | guess_chars.add(c) 73 | # print('%s: %s' %(char, ','.join(ok))) 74 | return newchar, newpos 75 | except_chars.add(c) 76 | # print('Can not encode %s, ignore' % char) 77 | return ' ' * (newpos - exc.start), newpos 78 | 79 | codecs.register_error('gbk_to_sjis', gbk_to_sjis) 80 | # from zhtools import langconv 81 | # text = langconv.Converter('zh-hant').convert(text) 82 | try: 83 | text = text.encode('SHIFT-JIS', errors='gbk_to_sjis') 84 | except UnicodeError as exc: 85 | char = exc.object[exc.start:exc.end] 86 | print(char) 87 | raise 88 | print('These chars cannot encode to shift-jis:') 89 | if py3k: 90 | print(''.join(chr(c) for c in except_chars)) 91 | else: 92 | print(encode(UEMPTY.join(chr(c) for c in except_chars))) 93 | print('These chars can be guessed by pinyin:') 94 | if py3k: 95 | print(''.join(chr(c) for c in guess_chars)) 96 | else: 97 | print(encode(UEMPTY.join(chr(c) for c in guess_chars))) 98 | return text 99 | 100 | 101 | def read_char_dict(data_path, encoding=None): 102 | chardict = {} 103 | try: 104 | buf = open(data_path, 'rb').read() 105 | buf = decode(buf, encoding) 106 | for data in buf.splitlines(): 107 | k, v = data.strip().split(' ', 1) 108 | chardict[k] = v 109 | except Exception as e: 110 | print(e) 111 | else: 112 | print('Success load char dict: %s, %s' % (data_path, len(chardict))) 113 | return chardict 114 | 115 | 116 | def main(args, gui=True): 117 | if len(args) <= 1 and gui: 118 | # Show GUI 119 | from portable import tkFileDialog 120 | 121 | def file_or_directory(p): 122 | filename = path.basename(p) 123 | dirname = path.dirname(p) 124 | if filename == 'nscript.dat': 125 | return dirname 126 | base, ext = path.splitext(filename) 127 | if ext != '.txt': 128 | return dirname 129 | if base.isdigit() and 0 <= int(base) < 100: 130 | return dirname 131 | return p 132 | 133 | in_path = tkFileDialog.askopenfilename(title='请选择脚本文件') 134 | in_path = file_or_directory(in_path) 135 | print('in_path: ' + in_path) 136 | if not in_path: 137 | exit() 138 | out_path = tkFileDialog.asksaveasfilename(title='请选择要保存的文件名') 139 | if not out_path: 140 | exit() 141 | method = 'auto' 142 | else: 143 | options, arguments = docopt(__doc__) 144 | if len(arguments) == 1: 145 | in_path = arguments[0] 146 | out_path = './out.txt' 147 | elif len(arguments) != 2: 148 | help() 149 | else: 150 | in_path = arguments[0] 151 | out_path = arguments[1] 152 | method = options.method 153 | if method != 'auto': 154 | exit('Not Implemented Method: %s' % method) 155 | 156 | try: 157 | data_path = path.join(path.dirname(path.abspath(__file__)), 'gbk2sjis.dat') 158 | chardict = read_char_dict(data_path) 159 | 160 | ons = ONSHandler(in_path) 161 | text = ons.get_script() 162 | text = hzconvert(text, 'gbk', 'sjis', method, chardict) 163 | 164 | open(out_path, 'wb').write(text) 165 | except Exception as e: 166 | if gui: 167 | from portable import tkMessageBox 168 | tkMessageBox.showerror(message=repr(e), title='Error!') 169 | exit() 170 | raise 171 | else: 172 | if gui: 173 | from portable import tkMessageBox 174 | tkMessageBox.showinfo(message='转换完成!', title='Finished!') 175 | 176 | 177 | if __name__ == '__main__': 178 | main(sys.argv) 179 | -------------------------------------------------------------------------------- /onssaver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | __author__ = 'Skydark Chen' 5 | __version__ = '0.03.dev' 6 | __license__ = 'GPLv2' 7 | 8 | import os 9 | import sys 10 | 11 | 12 | from ons import ONScripterError 13 | from ons.handler import ONSHandler, ONSHandlerError 14 | from ons.saver import ONSSaver, ONSSaverError 15 | 16 | 17 | class ONSDiffSaver(ONSSaver): 18 | ONS_NEST_SEARCH_AREA_LIMIT = 50 19 | 20 | def __init__(self, handler, newhandler): 21 | self.savehandler = newhandler 22 | super(ONSDiffSaver, self).__init__(handler) 23 | 24 | def save(self): 25 | handler = self.handler 26 | savehandler = self.savehandler 27 | if savehandler.global_variable_border != \ 28 | handler.global_variable_border: 29 | raise ONSSaverError( 30 | 'New script has a different global variable border!') 31 | 32 | stack_offset_begin = self.stack_offset 33 | stack_offset_end = stack_offset_begin + self.num_nest * 4 34 | current_offset_end = self.current_offset[0] 35 | current_offset_begin = current_offset_end - 8 36 | code = self.code 37 | 38 | plain_nest_info = b'' 39 | for type_, addr in self._match_nest_info(): 40 | if type_ == 'label': 41 | plain_nest_info += self.writeInt(addr) 42 | #try: 43 | # print(addr, savehandler.buf[addr:addr+20].decode('gbk')) 44 | #except UnicodeDecodeError: 45 | # print(addr) 46 | else: 47 | plain_nest_info += b''.join(self.writeInt(i) for i in type_[1]) 48 | plain_nest_info += self.writeInt(-addr) 49 | 50 | current_offset = self._match_current_offset() 51 | 52 | ret = b''.join([ 53 | code[:stack_offset_begin], 54 | plain_nest_info, 55 | code[stack_offset_end:current_offset_begin], 56 | current_offset, 57 | code[current_offset_end:], 58 | ]) 59 | return ret 60 | 61 | def _match_current_offset(self): 62 | handler = self.savehandler 63 | p, current_inline_offset = self.current_offset[:2] 64 | address = self._match_offset(*self.current_offset[2:]) 65 | current_lno = handler.getLineByAddress(address, relative=False) 66 | return self.writeInt(current_lno) + \ 67 | self.writeInt(current_inline_offset) 68 | 69 | def _match_nest_info(self): 70 | new_nest_info = [0] * len(self.nest_info) 71 | for j, nest_info in enumerate(self.nest_info): 72 | type_ = nest_info[0] 73 | new_nest_info[j] = (type_, self._match_offset(*nest_info[1:])) 74 | return new_nest_info 75 | 76 | def _match_offset(self, addr, label, line, line_offset, line_start_addr): 77 | # 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | ''' 21 | 22 | __author__ = 'Skydark Chen' 23 | __license__ = 'GPLv2' 24 | 25 | 26 | import os 27 | 28 | from ons import ONScripterError 29 | from utils import cd_once, Struct 30 | from portable import bytechr, py3k, decode, UEMPTY 31 | 32 | 33 | DEFAULT_SCRIPT_ENCODING = 'GBK' 34 | 35 | 36 | class ONScripterLabel(Struct): 37 | name = b'' 38 | start_line = -1 39 | start_address = -1 40 | 41 | 42 | class ONSHandlerError(ONScripterError): 43 | pass 44 | 45 | 46 | class ONSHandler(object): 47 | def __init__(self, path, encoding=None): 48 | self.encoding = encoding or DEFAULT_SCRIPT_ENCODING 49 | self.global_variable_border = 200 50 | self.path = path 51 | self.buf = '' 52 | self.mode = 640 53 | self.label_info = [] 54 | if os.path.isdir(path): 55 | cd_once(path, self.read) 56 | elif os.path.isfile(path): 57 | self.read(path) 58 | else: 59 | raise ONSHandlerError('Unknown path: %s' % path) 60 | 61 | def get_script(self): 62 | ret = UEMPTY 63 | try: 64 | ret = decode(self.buf, self.encoding) 65 | except UnicodeDecodeError: 66 | encodings = ['GB18030', 'SHIFT-JIS', 'UTF-8'] 67 | if os.path.isfile(self.path): 68 | encodings.extend(['UTF-16LE', 'BIG5']) 69 | if self.encoding in encodings: 70 | encodings.remove(self.encoding) 71 | for encoding in encodings: 72 | try: 73 | ret = decode(self.buf, encoding) 74 | self.encoding = encoding 75 | break 76 | except UnicodeDecodeError: 77 | pass 78 | if ret: 79 | return ret 80 | raise ONSHandlerError('Can not decode script') 81 | 82 | def read(self, file_=None): 83 | buf = '' 84 | if file_: 85 | buf = open(file_, 'rb').read() 86 | else: 87 | for start in ('0.txt', '00.txt', 'nscript.dat'): 88 | try: 89 | buf = open(start, 'rb').read() 90 | break 91 | except IOError: 92 | pass 93 | else: 94 | raise ONSHandlerError('No script found!') 95 | if start == 'nscript.dat': 96 | buf = b''.join(bytechr(ord(c) ^ 0x84) for c in buf) 97 | else: 98 | bufs = [buf] 99 | for i in range(1, 100): 100 | try: 101 | text = open('%d.txt' % i, 'rb').read() 102 | except IOError: 103 | if i >= 10: 104 | continue 105 | try: 106 | text = open('%02d.txt' % i, 'rb').read() 107 | except IOError: 108 | continue 109 | bufs.append(text) 110 | buf = b'\n'.join(bufs) 111 | buf = buf.replace(b'\r\n', b'\n').replace(b'\r', b'\n') 112 | 113 | # try: 114 | # buf = buf.decode(self.encoding) 115 | # except UnicodeDecodeError: 116 | # raise ONSHandlerError( 117 | # 'Decode script with %s failed!' % self.encoding) 118 | 119 | p = 1 120 | while buf[0:1] == b';': 121 | if buf[p:p+4] == b'mode': 122 | try: 123 | self.mode = int(buf[p+4:p+7]) 124 | except ValueError: 125 | pass 126 | p += 7 127 | elif buf[p:p+5] == b'value': 128 | p += 5 129 | while buf[p:p+1] in b'\t \n': 130 | p += 1 131 | q = p 132 | while buf[p:p+1].isdigit(): 133 | p += 1 134 | self.global_variable_border = int(buf[q:p]) 135 | else: 136 | break 137 | if buf[p:p+1] != b',': 138 | break 139 | p += 1 140 | 141 | self.buf = buf 142 | #open('resultbuf.txt', 'wb').write(buf) 143 | self.readLabel() 144 | 145 | def readLabel(self): 146 | state, label, lno = 'newline', b'', 0 147 | label_info = [] 148 | for i, c in enumerate(self.buf): 149 | if py3k: 150 | c = bytechr(c) 151 | if state == 'label': 152 | c = c.upper() 153 | if c.isalnum() or c == b'_': 154 | label += c 155 | else: 156 | assert self.buf[i-len(label)-1:i-len(label)] == b'*' 157 | state = 'newline' if c == b'\n' else 'ready' 158 | label_info.append(ONScripterLabel( 159 | name=label, 160 | start_line=lno, 161 | start_address=i-len(label)-1, 162 | )) 163 | label = b'' 164 | if c == b'\n': 165 | lno += 1 166 | continue 167 | elif state == 'newline': 168 | if c in b'\t ': 169 | continue 170 | if c == b'*': 171 | state, label = 'label', b'' 172 | continue 173 | state = 'ready' 174 | if c == b'\n': 175 | state, lno = 'newline', lno + 1 176 | self.label_info = label_info 177 | # print([c.name for c in label_info]) 178 | 179 | def getLineStringByAddress(self, addr): 180 | return self.buf[addr:self.buf.find(b'\n', addr)] 181 | 182 | def getLabelByLine(self, line): 183 | for i, label in enumerate(self.label_info): 184 | if label.start_line > line: 185 | return self.label_info[i-1] 186 | return self.label_info[-1] 187 | 188 | def getLabelByAddress(self, address): 189 | for i, label in enumerate(self.label_info): 190 | if label.start_address > address: 191 | return self.label_info[i-1] 192 | return self.label_info[-1] 193 | 194 | def getAddressByLine(self, line): 195 | label = self.getLabelByLine(line) 196 | l = line - label.start_line 197 | addr = label.start_address 198 | buf = self.buf 199 | length = len(buf) 200 | while l > 0: 201 | while addr < length and buf[addr:addr+1] != b'\n': 202 | addr += 1 203 | addr += 1 204 | l -= 1 205 | return addr 206 | 207 | def getLineByAddress(self, address, relative=True): 208 | label = self.getLabelByAddress(address) 209 | addr = label.start_address 210 | line = 0 if relative else label.start_line 211 | buf = self.buf 212 | if address >= len(buf): 213 | raise ONSHandlerError('Script address %s overflow!' % address) 214 | while address > addr: 215 | if buf[addr:addr+1] == b'\n': 216 | line += 1 217 | addr += 1 218 | return line 219 | 220 | def findLabel(self, name): 221 | name = name.upper() 222 | for i, label in enumerate(self.label_info): 223 | if label.name == name: 224 | return i, label 225 | raise ONSHandlerError('No label %s found!' % name) 226 | -------------------------------------------------------------------------------- /nstemplate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ Usage: nstemplate.py [options] input_file output_file 5 | 6 | Options: 7 | -h --help show this help 8 | -b --base BASE_NUM set base number [default: 1000] 9 | -e --encoding ENCODING set input file encoding [default: auto] 10 | 11 | """ 12 | 13 | # (DROPPED!) 14 | # 1. macro 15 | # 2. import with param 16 | # 3. kr_reader_macro 17 | # 4. (NS) kr_reader 18 | # 5. (NS) function rec call 19 | # 6. (NS) setwindow 20 | # 7. (NS) reflect lsp/csp/... 21 | # 8. (NS) mathlib 22 | # 10. (NS) OO 23 | # 13. (NS) text_utils(to_wide, int_to_chinese) 24 | # 14. (NS) queue 25 | # 15. (NS) button interface 26 | # 16. (NS) name/face 27 | 28 | import os 29 | import sys 30 | import re 31 | from codecs import open 32 | 33 | import chardet 34 | from docopt import docopt 35 | 36 | from utils import Struct 37 | 38 | 39 | DEFAULT_ENCODING = 'utf-8' 40 | 41 | py = sys.version_info 42 | py3k = py >= (3, 0, 0) 43 | 44 | if py3k: 45 | to_unicode = lambda s, encoding=DEFAULT_ENCODING:\ 46 | s.decode(encoding) if isinstance(s, bytes) else str(s) 47 | isstr = lambda s: isinstance(s, str) 48 | encode = lambda s, encoding: bytes(s, encoding) 49 | else: 50 | to_unicode = lambda s, encoding=DEFAULT_ENCODING:\ 51 | s if isinstance(s, unicode) else s.decode(encoding) 52 | isstr = lambda s: isinstance(s, basestring) 53 | encode = lambda s, encoding: s.encode(encoding) 54 | 55 | 56 | def debug(s): 57 | print('DEBUG: ' + str(s)) 58 | 59 | 60 | class NSTParser(object): 61 | _re_exps = { 62 | 'comment': r'^(\s*)@##(.*)$', 63 | 'raw': r'^(\s*)@#(|[^#].*)$', 64 | 'var': r'(%|\$)(\d+)', 65 | 'format': r'@\{([^}]+)\}', 66 | 'def': r'^(\s*)(@def)(\s+)(\w+)(\s*)(.*)', 67 | 'python': r'^(\s*)@python.*$', 68 | 'in_python': r'^(\s*)@(.*)$', 69 | 'import': r'^\s*@import\s+(?P[^\s]+)' + \ 70 | r'(\s+as\s+(?P[^\s]+))?\s*$', 71 | } 72 | _resubs = { 73 | 'raw': r'\1\2', 74 | 'in_python': r'\2', 75 | 'def_make': r'\tdefsub \4', 76 | } 77 | 78 | def __init__(self, **options): 79 | self.locals = { 80 | '__base': 1000, 81 | '__max_var': 0, 82 | '__encoding': 'auto', 83 | '__': lambda s: self.outlines.append( 84 | to_unicode(s.format(**self.locals))), 85 | '__s': lambda s: map(str.strip, s.split(',')), 86 | } 87 | if '__' in options: # can not bind '__': `self` 88 | del options['__'] 89 | self.locals.update(options) 90 | 91 | self.lines = [] 92 | self.outlines = [] 93 | self.defsubs = [] 94 | self.python_lines = [] 95 | 96 | for name, re_exp in self._re_exps.items(): 97 | setattr(self, '_re_' + name, re.compile(re_exp)) 98 | 99 | self.state = 'default' 100 | self._parse = dict((state, getattr(self, '_parse_' + state)) 101 | for state in ('python', 'default')) 102 | 103 | def get_name_by_filename(self, filename): 104 | name = os.path.splitext(os.path.basename(filename))[0] 105 | return name 106 | 107 | def read_lines(self, filename, encoding=None): 108 | if encoding is None or encoding == 'auto': 109 | source = open(filename, 'rb').read() 110 | encoding = chardet.detect(source)['encoding'] 111 | debug('Guess encoding for %s: %s' % (filename, encoding)) 112 | source = open(filename, 'rb', encoding=encoding).read() 113 | lines = source.splitlines() 114 | return lines 115 | 116 | def _pre_parse(self, line, lno): 117 | def rebase(m): 118 | groups = m.groups() 119 | var = int(groups[-1]) 120 | self.locals['__max_var'] = max(var, self.locals['__max_var']) 121 | return ''.join(groups[:-1]) + str(self.locals['__base'] + var) 122 | 123 | if self._re_comment.match(line): 124 | return None 125 | 126 | if self._re_raw.match(line): 127 | self.outlines.append(self._re_raw.sub(self._resubs['raw'], line)) 128 | return None 129 | 130 | line = '"'.join(self._re_var.sub(rebase, part) if i % 2 == 0 else part 131 | for i, part in enumerate(line.split('"'))) 132 | return line 133 | 134 | def _parse_default(self, line, lno): 135 | if self._re_python.match(line): 136 | self.state = 'python' 137 | return 138 | 139 | format = lambda m: str(eval(m.groups()[0], self.locals)) 140 | line = '"'.join(self._re_format.sub(format, part) 141 | if i % 2 == 0 else part 142 | for i, part in enumerate(line.split('"'))) 143 | 144 | if self._re_def.match(line): 145 | def re_def_sub(m): 146 | groups = m.groups() 147 | p = groups[0] + '\tget_param ' + groups[5] if groups[5] else '' 148 | return '{0}*{3}\r\n{p}'.format(*groups, p=p) 149 | 150 | re_def = self._re_def 151 | self.defsubs.append(re_def.sub(self._resubs['def_make'], line)) 152 | line = re_def.sub(re_def_sub, line) 153 | self.outlines.append(line) 154 | return 155 | 156 | m = self._re_import.match(line) 157 | if m: 158 | d = m.groupdict() 159 | filename = d['filename'] 160 | name = d['name'] or self.get_name_by_filename(filename) 161 | importer = self.__class__(**self.locals) 162 | lines = importer.read_lines(filename) 163 | importer.parse(name, lines) 164 | self.outlines.extend(importer.outlines) 165 | if name: 166 | self.locals[name] = Struct(**importer.locals) 167 | return 168 | 169 | self.outlines.append(line) 170 | 171 | def _parse_python(self, line, lno): 172 | if self._re_in_python.match(line): 173 | line = self._re_in_python.sub(self._resubs['in_python'], line) 174 | self.python_lines.append(line) 175 | else: 176 | exec('\n'.join(self.python_lines), self.locals) 177 | self.python_lines = [] 178 | self.state = 'default' 179 | return True 180 | 181 | def parse(self, name, lines=None): 182 | if lines is None: 183 | lines = name 184 | name = self.get_name_by_filename(name) 185 | if isstr(lines): 186 | lines = self.read_lines(lines, encoding=self.locals['__encoding']) 187 | self.locals['__current_name'] = to_unicode(name) 188 | self.lines = lines 189 | # debug((name, self.locals['__encoding'])) 190 | 191 | for lno, line in enumerate(lines): 192 | try: 193 | line = self._pre_parse(line, lno) 194 | if line is not None: 195 | while self._parse[self.state](line, lno): 196 | pass 197 | except: 198 | print('Error when parsing %s at line %s: %s'\ 199 | % (name, lno + 1, line)) 200 | raise 201 | 202 | self.make_info_lines() 203 | self.locals['__current_name'] = '' 204 | 205 | def make_info_lines(self): 206 | outlines = self.outlines 207 | outlines.append('\r\n*__nst_defsub_%s' % self.locals['__current_name']) 208 | outlines.extend(self.defsubs) 209 | outlines.append('\treturn\r\n') 210 | outlines.append('mov %%__nst_base, %s\r\n\r\n' 211 | % (self.locals['__base'] + self.locals['__max_var'] + 1)) 212 | 213 | def write(self, output): 214 | if isstr(output): 215 | output = open(output, 'wb', encoding='GBK') 216 | output.write('\r\n'.join(self.outlines)) 217 | 218 | 219 | def help(): 220 | exit(__doc__.strip()) 221 | 222 | 223 | def main(args): 224 | options, arguments = docopt(__doc__) 225 | if not(str(options.base).isdigit() and len(arguments) == 2): 226 | help() 227 | encoding = options.encoding 228 | src_name = arguments[0] 229 | dst_name = arguments[1] 230 | 231 | parser = NSTParser(__encoding=encoding, __base=int(options.base)) 232 | parser.parse(src_name) 233 | parser.write(dst_name) 234 | 235 | 236 | if __name__ == '__main__': 237 | main(sys.argv) 238 | -------------------------------------------------------------------------------- /zhtools/langconv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from copy import deepcopy 5 | import re 6 | 7 | try: 8 | import psyco 9 | psyco.full() 10 | except: 11 | pass 12 | 13 | try: 14 | from zh_wiki import zh2Hant, zh2Hans 15 | except ImportError: 16 | from zhtools.zh_wiki import zh2Hant, zh2Hans 17 | 18 | import sys 19 | py3k = sys.version_info >= (3, 0, 0) 20 | 21 | if py3k: 22 | UEMPTY = '' 23 | else: 24 | _zh2Hant, _zh2Hans = {}, {} 25 | for old, new in ((zh2Hant, _zh2Hant), (zh2Hans, _zh2Hans)): 26 | for k, v in old.items(): 27 | new[k.decode('utf8')] = v.decode('utf8') 28 | zh2Hant = _zh2Hant 29 | zh2Hans = _zh2Hans 30 | UEMPTY = ''.decode('utf8') 31 | 32 | # states 33 | (START, END, FAIL, WAIT_TAIL) = list(range(4)) 34 | # conditions 35 | (TAIL, ERROR, MATCHED_SWITCH, UNMATCHED_SWITCH, CONNECTOR) = list(range(5)) 36 | 37 | MAPS = {} 38 | 39 | class Node(object): 40 | def __init__(self, from_word, to_word=None, is_tail=True, 41 | have_child=False): 42 | self.from_word = from_word 43 | if to_word is None: 44 | self.to_word = from_word 45 | self.data = (is_tail, have_child, from_word) 46 | self.is_original = True 47 | else: 48 | self.to_word = to_word or from_word 49 | self.data = (is_tail, have_child, to_word) 50 | self.is_original = False 51 | self.is_tail = is_tail 52 | self.have_child = have_child 53 | 54 | def is_original_long_word(self): 55 | return self.is_original and len(self.from_word)>1 56 | 57 | def is_follow(self, chars): 58 | return chars != self.from_word[:-1] 59 | 60 | def __str__(self): 61 | return '' % (repr(self.from_word), 62 | repr(self.to_word), self.is_tail, self.have_child) 63 | 64 | __repr__ = __str__ 65 | 66 | class ConvertMap(object): 67 | def __init__(self, name, mapping=None): 68 | self.name = name 69 | self._map = {} 70 | if mapping: 71 | self.set_convert_map(mapping) 72 | 73 | def set_convert_map(self, mapping): 74 | convert_map = {} 75 | have_child = {} 76 | max_key_length = 0 77 | for key in sorted(mapping.keys()): 78 | if len(key)>1: 79 | for i in range(1, len(key)): 80 | parent_key = key[:i] 81 | have_child[parent_key] = True 82 | have_child[key] = False 83 | max_key_length = max(max_key_length, len(key)) 84 | for key in sorted(have_child.keys()): 85 | convert_map[key] = (key in mapping, have_child[key], 86 | mapping.get(key, UEMPTY)) 87 | self._map = convert_map 88 | self.max_key_length = max_key_length 89 | 90 | def __getitem__(self, k): 91 | try: 92 | is_tail, have_child, to_word = self._map[k] 93 | return Node(k, to_word, is_tail, have_child) 94 | except: 95 | return Node(k) 96 | 97 | def __contains__(self, k): 98 | return k in self._map 99 | 100 | def __len__(self): 101 | return len(self._map) 102 | 103 | class StatesMachineException(Exception): pass 104 | 105 | class StatesMachine(object): 106 | def __init__(self): 107 | self.state = START 108 | self.final = UEMPTY 109 | self.len = 0 110 | self.pool = UEMPTY 111 | 112 | def clone(self, pool): 113 | new = deepcopy(self) 114 | new.state = WAIT_TAIL 115 | new.pool = pool 116 | return new 117 | 118 | def feed(self, char, map): 119 | node = map[self.pool+char] 120 | 121 | if node.have_child: 122 | if node.is_tail: 123 | if node.is_original: 124 | cond = UNMATCHED_SWITCH 125 | else: 126 | cond = MATCHED_SWITCH 127 | else: 128 | cond = CONNECTOR 129 | else: 130 | if node.is_tail: 131 | cond = TAIL 132 | else: 133 | cond = ERROR 134 | 135 | new = None 136 | if cond == ERROR: 137 | self.state = FAIL 138 | elif cond == TAIL: 139 | if self.state == WAIT_TAIL and node.is_original_long_word(): 140 | self.state = FAIL 141 | else: 142 | self.final += node.to_word 143 | self.len += 1 144 | self.pool = UEMPTY 145 | self.state = END 146 | elif self.state == START or self.state == WAIT_TAIL: 147 | if cond == MATCHED_SWITCH: 148 | new = self.clone(node.from_word) 149 | self.final += node.to_word 150 | self.len += 1 151 | self.state = END 152 | self.pool = UEMPTY 153 | elif cond == UNMATCHED_SWITCH or cond == CONNECTOR: 154 | if self.state == START: 155 | new = self.clone(node.from_word) 156 | self.final += node.to_word 157 | self.len += 1 158 | self.state = END 159 | else: 160 | if node.is_follow(self.pool): 161 | self.state = FAIL 162 | else: 163 | self.pool = node.from_word 164 | elif self.state == END: 165 | # END is a new START 166 | self.state = START 167 | new = self.feed(char, map) 168 | elif self.state == FAIL: 169 | raise StatesMachineException('Translate States Machine ' 170 | 'have error with input data %s' % node) 171 | return new 172 | 173 | def __len__(self): 174 | return self.len + 1 175 | 176 | def __str__(self): 177 | return '' % ( 178 | id(self), self.pool, self.state, self.final) 179 | __repr__ = __str__ 180 | 181 | class Converter(object): 182 | def __init__(self, to_encoding): 183 | self.to_encoding = to_encoding 184 | self.map = MAPS[to_encoding] 185 | self.start() 186 | 187 | def feed(self, char): 188 | branches = [] 189 | for fsm in self.machines: 190 | new = fsm.feed(char, self.map) 191 | if new: 192 | branches.append(new) 193 | if branches: 194 | self.machines.extend(branches) 195 | self.machines = [fsm for fsm in self.machines if fsm.state != FAIL] 196 | all_ok = True 197 | for fsm in self.machines: 198 | if fsm.state != END: 199 | all_ok = False 200 | if all_ok: 201 | self._clean() 202 | return self.get_result() 203 | 204 | def _clean(self): 205 | if len(self.machines): 206 | self.machines.sort(key=lambda x: len(x)) 207 | # self.machines.sort(cmp=lambda x,y: cmp(len(x), len(y))) 208 | self.final += self.machines[0].final 209 | self.machines = [StatesMachine()] 210 | 211 | def start(self): 212 | self.machines = [StatesMachine()] 213 | self.final = UEMPTY 214 | 215 | def end(self): 216 | self.machines = [fsm for fsm in self.machines 217 | if fsm.state == FAIL or fsm.state == END] 218 | self._clean() 219 | 220 | def convert(self, string): 221 | self.start() 222 | for char in string: 223 | self.feed(char) 224 | self.end() 225 | return self.get_result() 226 | 227 | def get_result(self): 228 | return self.final 229 | 230 | 231 | def registery(name, mapping): 232 | global MAPS 233 | MAPS[name] = ConvertMap(name, mapping) 234 | 235 | registery('zh-hant', zh2Hant) 236 | registery('zh-hans', zh2Hans) 237 | del zh2Hant, zh2Hans 238 | 239 | 240 | def run(): 241 | import sys 242 | from optparse import OptionParser 243 | parser = OptionParser() 244 | parser.add_option('-e', type='string', dest='encoding', 245 | help='encoding') 246 | parser.add_option('-f', type='string', dest='file_in', 247 | help='input file (- for stdin)') 248 | parser.add_option('-t', type='string', dest='file_out', 249 | help='output file') 250 | (options, args) = parser.parse_args() 251 | if not options.encoding: 252 | parser.error('encoding must be set') 253 | if options.file_in: 254 | if options.file_in == '-': 255 | file_in = sys.stdin 256 | else: 257 | file_in = open(options.file_in) 258 | else: 259 | file_in = sys.stdin 260 | if options.file_out: 261 | if options.file_out == '-': 262 | file_out = sys.stdout 263 | else: 264 | file_out = open(options.file_out, 'wb') 265 | else: 266 | file_out = sys.stdout 267 | 268 | c = Converter(options.encoding) 269 | for line in file_in: 270 | # print >> file_out, c.convert(line.rstrip('\n').decode( 271 | file_out.write(c.convert(line.rstrip('\n').decode( 272 | 'utf8')).encode('utf8')) 273 | 274 | 275 | if __name__ == '__main__': 276 | run() 277 | 278 | -------------------------------------------------------------------------------- /ons/saver.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Copyright (c) 2011-2012 Skydark. All rights reserved. 4 | 5 | Skydark Chen 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | ''' 21 | 22 | __author__ = 'Skydark Chen' 23 | __license__ = 'GPLv2' 24 | 25 | 26 | import logging 27 | 28 | from ons import ONScripterError 29 | from portable import b_ord, chr_join 30 | 31 | ENCODING = 'gb18030' 32 | 33 | _logger = logging.getLogger('ons.saver') 34 | _logger.addHandler(logging.StreamHandler()) 35 | # _logger.setLevel(logging.DEBUG) 36 | _logger.setLevel(logging.ERROR) 37 | 38 | 39 | def printd(s): 40 | _logger.debug(s) 41 | 42 | 43 | class ONSSaverError(ONScripterError): 44 | pass 45 | 46 | 47 | class ONSSaver(object): 48 | MAX_SPRITE_NUM = 1000 49 | MAX_SPRITE2_NUM = 256 50 | MAX_PARAM_NUM = 100 51 | MAX_EFFECT_NUM = 256 52 | 53 | def __init__(self, handler): 54 | self.handler = handler 55 | self.code = b'' 56 | self.length = 0 57 | self.p = 0 58 | 59 | def reset(self): 60 | self.p = 0 61 | self.stack_offset = None 62 | self.num_nest = 0 63 | self.nest_info = [] 64 | self.current_offset = (None, ) 65 | 66 | def readInt(self, count=1): 67 | ret = [] 68 | p, code = self.p, self.code 69 | for i in range(count): 70 | n = b_ord(code[p]) + (b_ord(code[p+1])<<8) + \ 71 | (b_ord(code[p+2])<<16) + (b_ord(code[p+3])<<24) 72 | if b_ord(code[p+3]) > 8: 73 | n -= (2 << 31) 74 | ret.append(n) 75 | p += 4 76 | self.p = p 77 | return ret if len(ret) != 1 else ret[0] 78 | 79 | def readChar(self, count=1): 80 | cnt = 0 81 | ret = [] 82 | code, l, p = self.code, self.length, self.p 83 | while cnt < count and p < l: 84 | ret.append(code[p:p+1]) 85 | cnt += 1 86 | p += 1 87 | self.p = p 88 | return b''.join(ret) 89 | 90 | def readStr(self, count=1): 91 | ret = [] 92 | code = self.code 93 | for i in range(count): 94 | pos = code.find(b'\x00', self.p) 95 | assert pos >= 0 96 | ret.append(code[self.p:pos]) 97 | self.p = pos + 1 98 | return ret if len(ret) != 1 else ret[0] 99 | 100 | def readBool(self, count=1): 101 | b = self.readInt(count) 102 | if isinstance(b, list): 103 | assert all(x in (0, 1) for x in b) 104 | else: 105 | assert b in (0, 1) 106 | return b 107 | 108 | def load(self, savedata): 109 | self.reset() 110 | self.code = savedata 111 | self.length = len(savedata) 112 | readChar = self.readChar 113 | assert readChar(3) == b'ONS' 114 | file_version = ord(readChar()) * 100 + ord(readChar()) 115 | printd('Save version: %s' % file_version) 116 | self.doPreCheck(file_version) 117 | self.getStackOffset() 118 | self.doPostCheck(file_version) 119 | self.getCurrentLine() 120 | 121 | def doPreCheck(self, file_version): 122 | readInt, readChar, readStr, readBool =\ 123 | self.readInt, self.readChar, self.readStr, self.readBool 124 | assert readInt() == 1 125 | readBool(), readBool() # bold, shadow 126 | assert readInt() == 0 127 | readBool() # rmode 128 | readInt(3) # font rgb 129 | readStr(2) # cursor 130 | readInt(2), readStr() # window effect, effect_image 131 | readInt(8) # font 132 | readChar(3) # window color 133 | readChar() # font transparent 134 | readInt() # waittime 135 | readInt(4), readStr() # animation info, animation image 136 | readInt(2) # absflag 137 | readInt(4) # cursor info 138 | readStr() # bgimage 139 | readStr(3), readInt(3), readInt(3) # tachi image, tachi x, tachi y 140 | assert readInt(3) == [0] * 3 141 | if file_version >= 203: 142 | assert readInt(3) == [-1] * 3 143 | for i in range(self.MAX_SPRITE_NUM): 144 | #ai image, xy, visible, cell, trans 145 | sprite_info = [ 146 | readStr(), readInt(), readInt(), readBool(), readInt()] 147 | if file_version >= 203: 148 | sprite_info.append(readInt()) # trans 149 | # if sprite_info[0]: print i, sprite_info 150 | self.readVariables(0, self.handler.global_variable_border) 151 | 152 | def doPostCheck(self, file_version): 153 | readInt, readChar, readStr, readBool =\ 154 | self.readInt, self.readChar, self.readStr, self.readBool 155 | readBool(), readInt(3) # monocro & color 156 | readInt() # nega mode 157 | readStr(2) # midi, wave 158 | readInt() # cdtrack 159 | readBool(5) # playloop, waveloop, playonce, bgmloop, mp3loop 160 | readStr() # musicname 161 | readBool() # erase text window 162 | assert readInt() == 1 163 | for i in range(self.MAX_PARAM_NUM): 164 | j = readInt() 165 | if j: 166 | readInt(5) # ai:x,y,max_w,h,max_param 167 | readChar(3) # color 168 | assert ord(readChar()) == 0 169 | else: 170 | assert readInt(6) == [-1, 0, 0, 0, 0, 0] 171 | for i in range(self.MAX_PARAM_NUM): 172 | j = readInt() 173 | readInt(5) # FIXME 174 | assert readInt(3) == [1, 0, 1] 175 | readStr() # btndef image 176 | if file_version >= 202: 177 | self.readArrayVariable() # read array variables 178 | # 179 | return 180 | 181 | def readArrayVariable(self): 182 | pass 183 | 184 | def getStackOffset(self): 185 | readInt = self.readInt 186 | num_nest = readInt() 187 | nest_info = [] 188 | self.stack_offset = self.p 189 | if num_nest > 0: 190 | ons = self.handler 191 | printd('nested info: %s' % num_nest) 192 | self.p += (num_nest - 1) * 4 193 | while num_nest > 0: 194 | i = readInt() 195 | if i > 0: 196 | nest_info.append(('label', i)) 197 | self.p -= 8 198 | num_nest -= 1 199 | else: 200 | self.p -= 16 201 | # info_var_no, info_to, info_step = readInt(3) 202 | nest_info.append((('for', readInt(3)), -i, )) 203 | self.p -= 16 204 | num_nest -= 4 205 | num_nest = readInt() 206 | self.p += num_nest * 4 207 | self.num_nest = num_nest 208 | self.nest_info = [0] * len(nest_info) 209 | nest_info.reverse() 210 | for j, (type_, addr) in enumerate(nest_info): 211 | label = ons.getLabelByAddress(addr) 212 | line = ons.getLineByAddress(addr, relative=False) 213 | line_offset = line - label.start_line 214 | line_start_addr = ons.getAddressByLine(line) 215 | self.nest_info[j] = ( 216 | type_, addr, label, line, line_offset, line_start_addr) 217 | # try: 218 | # print(type_, addr, 219 | # ons.buf[addr:ons.buf.find('\n', addr+1)] 220 | # .decode(ENCODING)) 221 | # except UnicodeDecodeError: 222 | # print(type_, addr) 223 | # print(label.name) 224 | 225 | def getCurrentLine(self): 226 | p = self.length - 1 227 | code = self.code 228 | handler = self.handler 229 | if code[p:p+1] == b'*': 230 | assert code[p-1:p] == b'"' 231 | p -= 2 232 | while code[p:p+1] != b'"': 233 | p -= 1 234 | p -= 1 235 | self.p = p - 7 236 | line, current_inline_offset = self.readInt(2) 237 | label = handler.getLabelByLine(line) 238 | line_offset = line - label.start_line 239 | line_start_addr = handler.getAddressByLine(line) 240 | addr = line_start_addr 241 | for i in range(current_inline_offset): 242 | while handler.buf[addr:addr+1] != ':': 243 | addr += 1 244 | addr += 1 245 | self.current_offset = ( 246 | self.p, current_inline_offset, 247 | addr, label, line, line_offset, line_start_addr 248 | ) 249 | printd('savestr: %s' % code[self.p:].decode(ENCODING)) 250 | 251 | def readVariables(self, from_, to_): 252 | printd('Variable size: %s' % to_) 253 | for i in range(from_, to_): 254 | k, s = self.readInt(), self.readStr() 255 | if k != 0: 256 | printd('Integer variable #%s: %s' %(i, k)) 257 | if s: 258 | printd('String variable #%s: %s' %(i, s)) 259 | 260 | def writeInt(self, i): 261 | if i < 0: 262 | i += (2 << 31) 263 | a, b = i % 256, i // 256 264 | b, c = b % 256, b // 256 265 | c, d = c % 256, c // 256 266 | assert 0 <= d < 256 267 | return chr_join([a, b, c, d]) 268 | 269 | def save(self): 270 | raise NotImplementedError('Not Implemented for generating savedata!') 271 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | 5 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 6 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | Preamble 11 | 12 | The licenses for most software are designed to take away your 13 | freedom to share and change it. By contrast, the GNU General Public 14 | License is intended to guarantee your freedom to share and change free 15 | software--to make sure the software is free for all its users. This 16 | General Public License applies to most of the Free Software 17 | Foundation's software and to any other program whose authors commit to 18 | using it. (Some other Free Software Foundation software is covered by 19 | the GNU Library General Public License instead.) You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | this service if you wish), that you receive source code or can get it 26 | if you want it, that you can change the software or use pieces of it 27 | in new free programs; and that you know you can do these things. 28 | 29 | To protect your rights, we need to make restrictions that forbid 30 | anyone to deny you these rights or to ask you to surrender the rights. 31 | These restrictions translate to certain responsibilities for you if you 32 | distribute copies of the software, or if you modify it. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must give the recipients all the rights that 36 | you have. You must make sure that they, too, receive or can get the 37 | source code. And you must show them these terms so they know their 38 | rights. 39 | 40 | We protect your rights with two steps: (1) copyright the software, and 41 | (2) offer you this license which gives you legal permission to copy, 42 | distribute and/or modify the software. 43 | 44 | Also, for each author's protection and ours, we want to make certain 45 | that everyone understands that there is no warranty for this free 46 | software. If the software is modified by someone else and passed on, we 47 | want its recipients to know that what they have is not the original, so 48 | that any problems introduced by others will not reflect on the original 49 | authors' reputations. 50 | 51 | Finally, any free program is threatened constantly by software 52 | patents. We wish to avoid the danger that redistributors of a free 53 | program will individually obtain patent licenses, in effect making the 54 | program proprietary. To prevent this, we have made it clear that any 55 | patent must be licensed for everyone's free use or not licensed at all. 56 | 57 | The precise terms and conditions for copying, distribution and 58 | modification follow. 59 | 60 | GNU GENERAL PUBLIC LICENSE 61 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 62 | 63 | 0. This License applies to any program or other work which contains 64 | a notice placed by the copyright holder saying it may be distributed 65 | under the terms of this General Public License. The "Program", below, 66 | refers to any such program or work, and a "work based on the Program" 67 | means either the Program or any derivative work under copyright law: 68 | that is to say, a work containing the Program or a portion of it, 69 | either verbatim or with modifications and/or translated into another 70 | language. (Hereinafter, translation is included without limitation in 71 | the term "modification".) Each licensee is addressed as "you". 72 | 73 | Activities other than copying, distribution and modification are not 74 | covered by this License; they are outside its scope. The act of 75 | running the Program is not restricted, and the output from the Program 76 | is covered only if its contents constitute a work based on the 77 | Program (independent of having been made by running the Program). 78 | Whether that is true depends on what the Program does. 79 | 80 | 1. You may copy and distribute verbatim copies of the Program's 81 | source code as you receive it, in any medium, provided that you 82 | conspicuously and appropriately publish on each copy an appropriate 83 | copyright notice and disclaimer of warranty; keep intact all the 84 | notices that refer to this License and to the absence of any warranty; 85 | and give any other recipients of the Program a copy of this License 86 | along with the Program. 87 | 88 | You may charge a fee for the physical act of transferring a copy, and 89 | you may at your option offer warranty protection in exchange for a fee. 90 | 91 | 2. You may modify your copy or copies of the Program or any portion 92 | of it, thus forming a work based on the Program, and copy and 93 | distribute such modifications or work under the terms of Section 1 94 | above, provided that you also meet all of these conditions: 95 | 96 | a) You must cause the modified files to carry prominent notices 97 | stating that you changed the files and the date of any change. 98 | 99 | b) You must cause any work that you distribute or publish, that in 100 | whole or in part contains or is derived from the Program or any 101 | part thereof, to be licensed as a whole at no charge to all third 102 | parties under the terms of this License. 103 | 104 | c) If the modified program normally reads commands interactively 105 | when run, you must cause it, when started running for such 106 | interactive use in the most ordinary way, to print or display an 107 | announcement including an appropriate copyright notice and a 108 | notice that there is no warranty (or else, saying that you provide 109 | a warranty) and that users may redistribute the program under 110 | these conditions, and telling the user how to view a copy of this 111 | License. (Exception: if the Program itself is interactive but 112 | does not normally print such an announcement, your work based on 113 | the Program is not required to print an announcement.) 114 | 115 | These requirements apply to the modified work as a whole. If 116 | identifiable sections of that work are not derived from the Program, 117 | and can be reasonably considered independent and separate works in 118 | themselves, then this License, and its terms, do not apply to those 119 | sections when you distribute them as separate works. But when you 120 | distribute the same sections as part of a whole which is a work based 121 | on the Program, the distribution of the whole must be on the terms of 122 | this License, whose permissions for other licensees extend to the 123 | entire whole, and thus to each and every part regardless of who wrote it. 124 | 125 | Thus, it is not the intent of this section to claim rights or contest 126 | your rights to work written entirely by you; rather, the intent is to 127 | exercise the right to control the distribution of derivative or 128 | collective works based on the Program. 129 | 130 | In addition, mere aggregation of another work not based on the Program 131 | with the Program (or with a work based on the Program) on a volume of 132 | a storage or distribution medium does not bring the other work under 133 | the scope of this License. 134 | 135 | 3. You may copy and distribute the Program (or a work based on it, 136 | under Section 2) in object code or executable form under the terms of 137 | Sections 1 and 2 above provided that you also do one of the following: 138 | 139 | a) Accompany it with the complete corresponding machine-readable 140 | source code, which must be distributed under the terms of Sections 141 | 1 and 2 above on a medium customarily used for software interchange; or, 142 | 143 | b) Accompany it with a written offer, valid for at least three 144 | years, to give any third party, for a charge no more than your 145 | cost of physically performing source distribution, a complete 146 | machine-readable copy of the corresponding source code, to be 147 | distributed under the terms of Sections 1 and 2 above on a medium 148 | customarily used for software interchange; or, 149 | 150 | c) Accompany it with the information you received as to the offer 151 | to distribute corresponding source code. (This alternative is 152 | allowed only for noncommercial distribution and only if you 153 | received the program in object code or executable form with such 154 | an offer, in accord with Subsection b above.) 155 | 156 | The source code for a work means the preferred form of the work for 157 | making modifications to it. For an executable work, complete source 158 | code means all the source code for all modules it contains, plus any 159 | associated interface definition files, plus the scripts used to 160 | control compilation and installation of the executable. However, as a 161 | special exception, the source code distributed need not include 162 | anything that is normally distributed (in either source or binary 163 | form) with the major components (compiler, kernel, and so on) of the 164 | operating system on which the executable runs, unless that component 165 | itself accompanies the executable. 166 | 167 | If distribution of executable or object code is made by offering 168 | access to copy from a designated place, then offering equivalent 169 | access to copy the source code from the same place counts as 170 | distribution of the source code, even though third parties are not 171 | compelled to copy the source along with the object code. 172 | 173 | 4. You may not copy, modify, sublicense, or distribute the Program 174 | except as expressly provided under this License. Any attempt 175 | otherwise to copy, modify, sublicense or distribute the Program is 176 | void, and will automatically terminate your rights under this License. 177 | However, parties who have received copies, or rights, from you under 178 | this License will not have their licenses terminated so long as such 179 | parties remain in full compliance. 180 | 181 | 5. You are not required to accept this License, since you have not 182 | signed it. However, nothing else grants you permission to modify or 183 | distribute the Program or its derivative works. These actions are 184 | prohibited by law if you do not accept this License. Therefore, by 185 | modifying or distributing the Program (or any work based on the 186 | Program), you indicate your acceptance of this License to do so, and 187 | all its terms and conditions for copying, distributing or modifying 188 | the Program or works based on it. 189 | 190 | 6. Each time you redistribute the Program (or any work based on the 191 | Program), the recipient automatically receives a license from the 192 | original licensor to copy, distribute or modify the Program subject to 193 | these terms and conditions. You may not impose any further 194 | restrictions on the recipients' exercise of the rights granted herein. 195 | You are not responsible for enforcing compliance by third parties to 196 | this License. 197 | 198 | 7. If, as a consequence of a court judgment or allegation of patent 199 | infringement or for any other reason (not limited to patent issues), 200 | conditions are imposed on you (whether by court order, agreement or 201 | otherwise) that contradict the conditions of this License, they do not 202 | excuse you from the conditions of this License. If you cannot 203 | distribute so as to satisfy simultaneously your obligations under this 204 | License and any other pertinent obligations, then as a consequence you 205 | may not distribute the Program at all. For example, if a patent 206 | license would not permit royalty-free redistribution of the Program by 207 | all those who receive copies directly or indirectly through you, then 208 | the only way you could satisfy both it and this License would be to 209 | refrain entirely from distribution of the Program. 210 | 211 | If any portion of this section is held invalid or unenforceable under 212 | any particular circumstance, the balance of the section is intended to 213 | apply and the section as a whole is intended to apply in other 214 | circumstances. 215 | 216 | It is not the purpose of this section to induce you to infringe any 217 | patents or other property right claims or to contest validity of any 218 | such claims; this section has the sole purpose of protecting the 219 | integrity of the free software distribution system, which is 220 | implemented by public license practices. Many people have made 221 | generous contributions to the wide range of software distributed 222 | through that system in reliance on consistent application of that 223 | system; it is up to the author/donor to decide if he or she is willing 224 | to distribute software through any other system and a licensee cannot 225 | impose that choice. 226 | 227 | This section is intended to make thoroughly clear what is believed to 228 | be a consequence of the rest of this License. 229 | 230 | 8. If the distribution and/or use of the Program is restricted in 231 | certain countries either by patents or by copyrighted interfaces, the 232 | original copyright holder who places the Program under this License 233 | may add an explicit geographical distribution limitation excluding 234 | those countries, so that distribution is permitted only in or among 235 | countries not thus excluded. In such case, this License incorporates 236 | the limitation as if written in the body of this License. 237 | 238 | 9. The Free Software Foundation may publish revised and/or new versions 239 | of the General Public License from time to time. Such new versions will 240 | be similar in spirit to the present version, but may differ in detail to 241 | address new problems or concerns. 242 | 243 | Each version is given a distinguishing version number. If the Program 244 | specifies a version number of this License which applies to it and "any 245 | later version", you have the option of following the terms and conditions 246 | either of that version or of any later version published by the Free 247 | Software Foundation. If the Program does not specify a version number of 248 | this License, you may choose any version ever published by the Free Software 249 | Foundation. 250 | 251 | 10. If you wish to incorporate parts of the Program into other free 252 | programs whose distribution conditions are different, write to the author 253 | to ask for permission. For software which is copyrighted by the Free 254 | Software Foundation, write to the Free Software Foundation; we sometimes 255 | make exceptions for this. Our decision will be guided by the two goals 256 | of preserving the free status of all derivatives of our free software and 257 | of promoting the sharing and reuse of software generally. 258 | 259 | NO WARRANTY 260 | 261 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 262 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 263 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 264 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 265 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 266 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 267 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 268 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 269 | REPAIR OR CORRECTION. 270 | 271 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 272 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 273 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 274 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 275 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 276 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 277 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 278 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 279 | POSSIBILITY OF SUCH DAMAGES. 280 | 281 | END OF TERMS AND CONDITIONS 282 | 283 | How to Apply These Terms to Your New Programs 284 | 285 | If you develop a new program, and you want it to be of the greatest 286 | possible use to the public, the best way to achieve this is to make it 287 | free software which everyone can redistribute and change under these terms. 288 | 289 | To do so, attach the following notices to the program. It is safest 290 | to attach them to the start of each source file to most effectively 291 | convey the exclusion of warranty; and each file should have at least 292 | the "copyright" line and a pointer to where the full notice is found. 293 | 294 | 295 | Copyright (C) 296 | 297 | This program is free software; you can redistribute it and/or modify 298 | it under the terms of the GNU General Public License as published by 299 | the Free Software Foundation; either version 2 of the License, or 300 | (at your option) any later version. 301 | 302 | This program is distributed in the hope that it will be useful, 303 | but WITHOUT ANY WARRANTY; without even the implied warranty of 304 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 305 | GNU General Public License for more details. 306 | 307 | You should have received a copy of the GNU General Public License 308 | along with this program; if not, write to the Free Software 309 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 310 | 311 | 312 | Also add information on how to contact you by electronic and paper mail. 313 | 314 | If the program is interactive, make it output a short notice like this 315 | when it starts in an interactive mode: 316 | 317 | Gnomovision version 69, Copyright (C) year name of author 318 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 319 | This is free software, and you are welcome to redistribute it 320 | under certain conditions; type `show c' for details. 321 | 322 | The hypothetical commands `show w' and `show c' should show the appropriate 323 | parts of the General Public License. Of course, the commands you use may 324 | be called something other than `show w' and `show c'; they could even be 325 | mouse-clicks or menu items--whatever suits your program. 326 | 327 | You should also get your employer (if you work as a programmer) or your 328 | school, if any, to sign a "copyright disclaimer" for the program, if 329 | necessary. Here is a sample; alter the names: 330 | 331 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 332 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 333 | 334 | , 1 April 1989 335 | Ty Coon, President of Vice 336 | 337 | This General Public License does not permit incorporating your program into 338 | proprietary programs. If your program is a subroutine library, you may 339 | consider it more useful to permit linking proprietary applications with the 340 | library. If this is what you want to do, use the GNU Library General 341 | Public License instead of this License. 342 | --------------------------------------------------------------------------------