old_blog 《赤印》的文本提取

本文介绍如何从国产Galgame《赤印》中提取文本内容,包括使用crass-0.4.14.0-final拆解scenario.xp3文件,以及通过Java编程处理ks脚本文件,实现对话文本的提取。
  《赤印》是我很久之前玩过的一款国产galgame。与其他的galgame不同,该游戏没有选项,所以如果将它的文本放到手机里面当作小说看的话,也是非常方便的。(当然,游戏性与感染力就下降很多了。)在网上没有找到《赤印》的文本,十分不爽,自己动手吧。

  由于游戏是KiriKiri2开发的,我们用软件“crass-0.4.14.0-final”对游戏包文件scenario.xp3进行拆解。对拆解后的文件夹进行文件筛选,将有用的脚本留下,什么函数啊、宏定义啊删掉就行了。


  脚本文件如图所示:



  我们原本想采用编程的方法进行文本提取。但是这之前需要做一下简单的处理。
为什么要做处理呢?我们用winhex打开任意一个ks文件,如prologe.ks,显示如下:




  尼玛字符之间有ASCII为0的字符夹着。这个会让我们的程序运行出错。我们对ks文件先右击—打开方式—记事本。然后全选—复制。新建prologe.txt,粘贴即可。一个复制粘贴就将这种畸形文本编程纯文本了,之后我们的操作对该txt文件操作即可。
  我们研究一下脚本文件。我们发现一些共性:
  对话总是以符号“『”开头;
  对话开头有的时候有[porcessname text="Name"]标签,有点时候没有。
  如图所示:



  我们的目标如下:1.将对话前的姓名保留;2.如果对话前没有姓名,则给出符号提示(如------)。后期我们根据上下文补上说话者的姓名。
  这里我们用Eclipse编写一个java类:WordHandling
  简介一下这个类吧,它提供了两种文字处理方式——按行处理和按字符处理。这里我们选择按行处理。其次,它提供一些常见的处理方法,去除歌词文件lrc开头、是否包含中文、是否所有字符都是可打印字符、删除开头的空格等。输出方式有两种,输出至文件和标准输出至屏幕。这里我只码了第二种方式,即打印至屏幕。
  代码贴出来:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
public class WordHandling {
String address;
int method;
boolean addEnter=false;
boolean deleteMoreEL=false;
int tag=0;
final static int BYTE=0;
final static int LINE=1;
WordHandling()
{
this("",WordHandling.BYTE,false);
}
WordHandling(String address,int method)
{
this(address,method,false);
}
WordHandling(String address,int method,boolean addEnter)
{
this.address=address;
this.method=method;
this.addEnter=addEnter;
}
void setDeleteMoreEMPTYLINE(boolean set)
{
deleteMoreEL=set;
}
char byteHandling(char c)
{
return c;
}
String lineHandling(String s)
{
return s;
}
void setAddress(String address)
{
this.address=address;
}
void setAddEnter(boolean addEnter)
{
this.addEnter=addEnter;
}
void setMethod(int method)
{
this.method=method;
}
int runForPrint()
{
if(method==WordHandling.BYTE)
{
File file=new File(address);
Reader reader=null;
try
{
reader=new InputStreamReader(new FileInputStream(file));
int tempchar;
int cnt=0;
char ch=' ';
while((tempchar=reader.read())!=-1)
{
ch=(char)tempchar;
ch=byteHandling(ch);
if((int)ch!=0)
{
System.out.print(ch);
cnt++;
} 
}
if(addEnter) System.out.println();
reader.close();
return cnt;
}
catch (Exception e)
{
e.printStackTrace();
}
}
else
{
File file=new File(address);
BufferedReader reader = null;
try
{
reader=new BufferedReader(new FileReader(file));
String tempString=null;
int emptyline=0;
int line=0;
while((tempString=reader.readLine())!=null)
{
tempString=lineHandling(tempString);
if(tempString!=null)
{
if(deleteMoreEL=false)
{
System.out.println(tempString);
}
else
{
if(tempString.equals(""))
{
if(emptyline==0)
System.out.println(tempString);
emptyline++;
}
else
{
System.out.println(tempString);
emptyline=0;
}
}
line++;
}
}
reader.close();
return line;
}
catch(IOException e)
{
e.printStackTrace();
}
finally
{
if(reader!=null)
{
try
{
reader.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
return 0;
}
int runForFile(String saveAddress)
{
return 0;
}
static String removeLrcHead(String s)
{
int i,j;
for(i=0;i<s.length();i++)
if(s.charAt(i)==']') break;
if(i==s.length()-1) return "";
for(j=i;j<s.length();j++)
{
if(s.charAt(j)==' ')
{
String t=s.substring(i+1,j);
return t;
}
}
return null;
}
static boolean containsChinese(String s)
{
for(int i=0;i<s.length();i++)
{
int asc=(int)s.charAt(i);
if(asc>128||asc<-1)
return true;
}
return false;
}
static boolean containsAllPritableASCII(String s)
{
for(int i=0;i<s.length();i++)
{
int asc=(int)s.charAt(i);
if(asc>=32&&asc<128)
return true;
}
return false;
}
static String deleteHeadingSpace(String s)
{
int i;
for(i=0;i<s.length();i++)
if(s.charAt(i)!=' ')
break;
return s.substring(i,s.length());
}
}
//下面利用该类处理文本prologe.txt,我们需要继承之并复写lineHandling方法即可。贴出代码(有注释):
public class TestMain {
public static void main(String[] args) throws Exception
{
String path="E:\\scenario\\prologe.txt";
WordHandling wh=new WordHandling(path,WordHandling.LINE,false)
{
String lineHandling(String s)
{
//如果是注释或者无信息 则不输出
if(s.startsWith(";")||s.startsWith("*")||s.startsWith("@")||s.equals(""))
{
return null;
}
int a=0,b=0;
//处理processname text姓名栏
if(s.startsWith("[processname text"))
{
this.tag=1;
for(int i=0;i<s.length();i++)
{
if(a==0&&s.charAt(i)=='\"')
a=i;
if(a!=0&&s.charAt(i)=='\"')
b=i;
}
if(a!=b)
return s.substring(a+1,b)+":";
else
{
for(int i=0;i<s.length();i++)
{
if(a==0&&s.charAt(i)=='=')
a=i;
}
return WordHandling.deleteHeadingSpace(s.substring(a+1,s.length()-1))+":";
}

}
//去除配对的[...]字段
for(int i=0;i<s.length();i++)
{
if(s.charAt(i)=='[')
{
a=i;
for(int j=i+1;j<s.length();j++)
{
if(s.charAt(j)==']')
{
b=j;
break;
}
}
s=s.substring(0,a)+s.substring(b+1,s.length());
i--;
}
}
//处理会话部分 如果之前没有人名则给出信号
if(s.startsWith("『"))
{
if(this.tag==1)
this.tag=0;
else
{
s="-----------------\n"+s;
this.tag=0;
}
}
return s;
}
};
//置多行空格为一行
wh.setDeleteMoreEMPTYLINE(true);
wh.runForPrint();
}
}
  运行后的结果如下:



  复制到记事本文件中,根据上下文将符号“-----------------”改成对应的人名即可。
  秀一下成功之后的《赤印》文本。



1, 打开support_list.txt,用游戏名做关键字(注意关键字中不要夹杂标点,空格和符号),如果查找不到所要找的游戏,再顺次打开support_list_kirikiri2.txt、support_NScripter.txt和support_list_RealLive.txt等,重复查找。直到找到要找的游戏为止。如果还是没有找到,请转到4。 2, 找到要找的游戏后,找到对应的插件名(该组游戏最上面的名字),并转到4。 3, 打开documentation\cn\对应插件名的txt。如果有注意事项,一定要仔细阅读。如果发现有提取范例,就尽量模仿其中的命令来操作GUI(如果你擅长用命令行的话,直接套用示例命令就行了)。示例命令中常常包含很多以-开头的选项,你需要把这些选项后跟的内容和GUI里的选项对应起来:-p和-d分配对应的是GUI中的“指定源文件和目录”2个选项;-O对应“特殊参数”(要用显示高级参数点开);-l对应“导入索引文件”;-u对应“指定插件”。可以参照下面的例子实际看下怎么对应的。注意:千万不要把命令行中的选项本身也写到GUI中!至此,你应该会提取了。请直接看下面的提取实例。 4, 如果查找不到要找的游戏,这就要看运气了。基本还是有些黄金法则的:a,优先查找同公司的前作或用公司名做关键字,按照步骤2的方式找对应的插件名;b,尝试下只提取封包的目录能不能成功;c,如果还是提取不成,打开cui_info.txt,根据后缀名找对应插件名,并转到2;d,如果还是提取不成,就上报给汉公。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值