LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1671|回复: 1

stardict 的安装及其它, 与大家分享一下 (http://blog.csdn.net/jixiuffff/archive/2

[复制链接]
发表于 2009-12-2 21:44:15 | 显示全部楼层 |阅读模式
(转载+部分原创)星际译王在gentoo gnome (xfce4 上也可以)上的安装(并可以实现发音,)OtdRealPeopleTTS 不用非得把mp3 转化为wav  收藏

http://forum.ubuntu.org.cn/viewtopic.php?f=137&p=1515096  (原文)

今天要用到StarDict的真人发音库,发现有WyabdcRealPeopleTTS 和OtdRealPeopleTTS
两个库。WyabdcRealPeopleTTS的音质太差,不用。那就只有用OtdRealPeopleTTS了,
但OtdRealPeopleTTS是mp3格式的,StarDict只能放wav格式的,还要把mp3转为wav,
本来转换一下不是问题,但转换后150M 的文件变成3G的文件,就算磁盘空间充足也不
是这样浪费的吧!何况这都可以存放很多部片子了~

由于我已经装了mplayer以及mp3解码器,以为在音效设置里面把play改为mplayer,
语音库路径改为/usr/share/OtdRealPeopleTTS就可以了。但修改后发现还是没发声。

/etc/portage/package.use

加入下面一行

app-dicts/stardict -gnome spell   pronounce

# ebuild    stardict-3.0.1-r2.ebuild  fetch   //下载所需的软件,

决定下开源码来看一下,打开/stardict-3.0.1/src/readword.cpp一看,发现了问题所在

一、
bool ReadWord::RealTts_canRead(const gchar *word)
{
bool return_val = false;
if (!ttspath.empty() && word && g_ascii_isalpha(word[0])) {
std::string lowerword;
const gchar *p = word;
while (*p) {
if (*p!=' ')
lowerword+=g_ascii_tolower(*p);
p++;
}
std::string filename;
std::list<std::string>::const_iterator it;
for (it=ttspath.begin(); it!=ttspath.end(); ++it) {
filename = *it + G_DIR_SEPARATOR_S + lowerword[0] + G_DIR_SEPARATOR_S + lowerword + ".wav"; #1
return_val = g_file_test(filename.c_str(), G_FILE_TEST_EXISTS); #2
if (return_val)
break;
}
}
return return_val;
}

红色标记第一句是找到wav文件的全路径,第二句是判断文件是否存在。由于是硬编定为wav,
所以前面修改音效设置是没有用的。

改为:
bool ReadWord::RealTts_canRead(const gchar *word)
{
bool return_val = false;
if (!ttspath.empty() && word && g_ascii_isalpha(word[0])) {
std::string lowerword;
const gchar *p = word;
while (*p) {
if (*p!=' ')
lowerword+=g_ascii_tolower(*p);
p++;
}
std::string filename;
std::list<std::string>::const_iterator it;
for (it=ttspath.begin(); it!=ttspath.end(); ++it) {
std::string suffix, aword;
aword = *it + G_DIR_SEPARATOR_S + "a" + G_DIR_SEPARATOR_S + "a.mp3";
if (g_file_test(aword.c_str(), G_FILE_TEST_EXISTS))
suffix = ".mp3";
else
suffix = ".wav";
filename = *it + G_DIR_SEPARATOR_S + lowerword[0] + G_DIR_SEPARATOR_S + lowerword + suffix;
return_val = g_file_test(filename.c_str(), G_FILE_TEST_EXISTS);
if (return_val)
break;
}
}
return return_val;
}

在/stardict-3.0.1/src/readword.cpp中有两个这样的地方,另一个在

void ReadWord::RealTts_read(const gchar *word)
bool ReadWord::RealTts_canRead(const gchar *word)

void ReadWord::Command_read(const gchar *word)这个函数中,都这样改。

(大家在此文件中搜一搜wav 这个词就可以知道有几 处)

改好之后,保存文件

tar -jcf stardict-3.0.1.tar.bz2  /tmp/ stardict-3.0.1

将其放到/usr/portage/distfiles 目录下(将好将原来的另保存一份)

此文件为我改好的readword.cpp 文件内容

view plaincopy to clipboardprint?

   1. #ifdef HAVE_CONFIG_H  
   2. #  include "config.h"  
   3. #endif  
   4. #include <cstring>  
   5. #include <string>  
   6. #include <glib/gi18n.h>  
   7. #include "conf.h"  
   8. #include "desktop.hpp"  
   9. #include "utils.h"  
  10. #include "stardict.h"  
  11. #include "readword.h"  
  12. ReadWord::ReadWord()  
  13. {  
  14.         const std::string &path = conf->get_string_at("dictionary/tts_path");  
  15.         LoadRealTtsPath(path.c_str());  
  16.         use_command_tts = conf->get_bool("/apps/stardict/preferences/dictionary/use_tts_program");  
  17.         tts_program_cmdline = conf->get_string("/apps/stardict/preferences/dictionary/tts_program_cmdline");  
  18. }  
  19. ReadWordType ReadWord::canRead(const gchar *word)  
  20. {  
  21.         if (RealTts_canRead(word))  
  22.                 return READWORD_REALTTS;  
  23.         if (gpAppFrame->oStarDictPlugins->TtsPlugins.nplugins() > 0)  
  24.                 return READWORD_TTS;  
  25.         if (use_command_tts && !tts_program_cmdline.empty())  
  26.                 return READWORD_COMMAND;  
  27.         return READWORD_CANNOT;  
  28. }  
  29. void ReadWord::read(const gchar *word, ReadWordType type)  
  30. {  
  31.         if (type == READWORD_REALTTS) {  
  32.                 RealTts_read(word);  
  33.         } else if (type == READWORD_TTS) {  
  34.                 gpAppFrame->oStarDictPlugins->TtsPlugins.saytext(0, word);  
  35.         } else if (type == READWORD_COMMAND) {  
  36.                 Command_read(word);  
  37.         }  
  38. }  
  39. void ReadWord:oadRealTtsPath(const gchar *path)  
  40. {  
  41.         std::list<std::string> paths;  
  42.         std::string str;  
  43.         const gchar *p, *p1;  
  44.         p = path;  
  45.         do {  
  46.                 p1 = strchr(p, '\n');  
  47.                 if (p1) {  
  48.                         str.assign(p, p1-p);  
  49.                         if (!str.empty())  
  50.                                 paths.push_back(str);  
  51.                         p = p1+1;  
  52.                 }  
  53.         } while (p1);  
  54.         str = p;  
  55.         if (!str.empty())  
  56.                 paths.push_back(str);  
  57.         ttspath.clear();  
  58.         std::list<std::string>::const_iterator it;  
  59.         for (it=paths.begin(); it!=paths.end(); ++it) {  
  60. #ifdef _WIN32  
  61.                 if (it->length()>1 && (*it)[1]==':') {  
  62.                         if (g_file_test(it->c_str(), G_FILE_TEST_EXISTS))  
  63.                                 ttspath.push_back(*it);  
  64.                 } else {  
  65.                         str = gStarDictDataDir + G_DIR_SEPARATOR_S + *it;  
  66.                         if (g_file_test(str.c_str(), G_FILE_TEST_EXISTS))  
  67.                                 ttspath.push_back(str);  
  68.                 }  
  69. #else  
  70.                 if (g_file_test(it->c_str(), G_FILE_TEST_EXISTS))  
  71.                         ttspath.push_back(*it);  
  72. #endif  
  73.         }  
  74. }  
  75. bool ReadWord::RealTts_canRead(const gchar *word)  
  76. {  
  77.         bool return_val = false;  
  78.         if (!ttspath.empty() && word && g_ascii_isalpha(word[0])) {  
  79.                 std::string lowerword;  
  80.                 const gchar *p = word;  
  81.                 while (*p) {  
  82.                         if (*p!=' ')  
  83.                                 lowerword+=g_ascii_tolower(*p);  
  84.                         p++;  
  85.                 }  
  86.                 std::string filename;  
  87.                 std::list<std::string>::const_iterator it;  
  88.                 for (it=ttspath.begin(); it!=ttspath.end(); ++it) {  
  89.                         std::string suffix, aword;  
  90.                         aword = *it + G_DIR_SEPARATOR_S + "a" + G_DIR_SEPARATOR_S + "a.mp3";  
  91.                         if (g_file_test(aword.c_str(), G_FILE_TEST_EXISTS))  
  92.                                 suffix = ".mp3";  
  93.                         else  
  94.                                 suffix = ".wav";  
  95.                         filename = *it + G_DIR_SEPARATOR_S + lowerword[0] + G_DIR_SEPARATOR_S + lowerword + suffix;  
  96.                         return_val = g_file_test(filename.c_str(), G_FILE_TEST_EXISTS);  
  97.                         if (return_val)  
  98.                                 break;  
  99.                 }  
100.         }  
101.         return return_val;  
102. }  
103. void ReadWord::Command_read(const gchar *word)  
104. {  
105.         gchar *eword = g_shell_quote(word);  
106.         gchar *command = g_strdup_printf(tts_program_cmdline.c_str(), eword);  
107.         g_free(eword);  
108.         system(command);  
109.         g_free(command);  
110. }  
111. void ReadWord::RealTts_read(const gchar *word)  
112. {  
113.         if (!ttspath.empty() && word && g_ascii_isalpha(word[0])) {  
114.                 std::string lowerword;  
115.                 const gchar *p = word;  
116.                 while (*p) {  
117.                         if (*p!=' ')  
118.                                 lowerword+=g_ascii_tolower(*p);  
119.                         p++;  
120.                 }  
121.                 std::string filename;  
122.                 std::list<std::string>::const_iterator it;  
123.                 for (it=ttspath.begin(); it!=ttspath.end(); ++it) {  
124.                         std::string suffix, aword;  
125.                         aword = *it + G_DIR_SEPARATOR_S + "a" + G_DIR_SEPARATOR_S + "a.mp3";  
126.                         if (g_file_test(aword.c_str(), G_FILE_TEST_EXISTS))  
127.                                 suffix = ".mp3";  
128.                         else  
129.                                 suffix = ".wav";  
130.                         filename = *it + G_DIR_SEPARATOR_S + lowerword[0] + G_DIR_SEPARATOR_S + lowerword + suffix;  
131.                         if (g_file_test(filename.c_str(), G_FILE_TEST_EXISTS)) {  
132.                                 play_wav_file(filename);  
133.                                 break;  
134.                         }  
135.                 }  
136.         }  
137. }  
138. const int Engine_RealTTS = 0;  
139. const int Engine_Command = 1;  
140. const int Engine_VirtualTTS_Base = 2;  
141. std::list<std::pair<std::string, int> > ReadWord::GetEngineList()  
142. {  
143.         std::list<std::pair<std::string, int> > engine_list;  
144.         if (!ttspath.empty()) {  
145.                 engine_list.push_back(std::pair<std::string, int>(_("Real People TTS"), Engine_RealTTS));  
146.         }  
147.         size_t n = gpAppFrame->oStarDictPlugins->TtsPlugins.nplugins();  
148.         for (size_t i = 0; i < n; i++) {  
149.                 engine_list.push_back(std::pair<std::string, int>(gpAppFrame->oStarDictPlugins->TtsPlugins.tts_name(i), Engine_VirtualTTS_Base + i));  
150.         }  
151.         if (use_command_tts && !tts_program_cmdline.empty()) {  
152.                 engine_list.push_back(std::pair<std::string, int>(_("Command TTS"), Engine_Command));  
153.         }  
154.         return engine_list;  
155. }  
156. void ReadWord::ReadByEngine(const gchar *word, int engine_index)  
157. {  
158.         if (engine_index == Engine_RealTTS) {  
159.                 RealTts_read(word);  
160.         } else if (engine_index == Engine_Command) {  
161.                 Command_read(word);  
162.         } else {  
163.                 gpAppFrame->oStarDictPlugins->TtsPlugins.saytext(engine_index - Engine_VirtualTTS_Base, word);  
164.         }  
165. }  

#ifdef HAVE_CONFIG_H # include "config.h" #endif #include <cstring> #include <string> #include <glib/gi18n.h> #include "conf.h" #include "desktop.hpp" #include "utils.h" #include "stardict.h" #include "readword.h" ReadWord::ReadWord() { const std::string &path = conf->get_string_at("dictionary/tts_path"); LoadRealTtsPath(path.c_str()); use_command_tts = conf->get_bool("/apps/stardict/preferences/dictionary/use_tts_program"); tts_program_cmdline = conf->get_string("/apps/stardict/preferences/dictionary/tts_program_cmdline"); } ReadWordType ReadWord::canRead(const gchar *word) { if (RealTts_canRead(word)) return READWORD_REALTTS; if (gpAppFrame->oStarDictPlugins->TtsPlugins.nplugins() > 0) return READWORD_TTS; if (use_command_tts && !tts_program_cmdline.empty()) return READWORD_COMMAND; return READWORD_CANNOT; } void ReadWord::read(const gchar *word, ReadWordType type) { if (type == READWORD_REALTTS) { RealTts_read(word); } else if (type == READWORD_TTS) { gpAppFrame->oStarDictPlugins->TtsPlugins.saytext(0, word); } else if (type == READWORD_COMMAND) { Command_read(word); } } void ReadWord:oadRealTtsPath(const gchar *path) { std::list<std::string> paths; std::string str; const gchar *p, *p1; p = path; do { p1 = strchr(p, '\n'); if (p1) { str.assign(p, p1-p); if (!str.empty()) paths.push_back(str); p = p1+1; } } while (p1); str = p; if (!str.empty()) paths.push_back(str); ttspath.clear(); std::list<std::string>::const_iterator it; for (it=paths.begin(); it!=paths.end(); ++it) { #ifdef _WIN32 if (it->length()>1 && (*it)[1]==':') { if (g_file_test(it->c_str(), G_FILE_TEST_EXISTS)) ttspath.push_back(*it); } else { str = gStarDictDataDir + G_DIR_SEPARATOR_S + *it; if (g_file_test(str.c_str(), G_FILE_TEST_EXISTS)) ttspath.push_back(str); } #else if (g_file_test(it->c_str(), G_FILE_TEST_EXISTS)) ttspath.push_back(*it); #endif } } bool ReadWord::RealTts_canRead(const gchar *word) { bool return_val = false; if (!ttspath.empty() && word && g_ascii_isalpha(word[0])) { std::string lowerword; const gchar *p = word; while (*p) { if (*p!=' ') lowerword+=g_ascii_tolower(*p); p++; } std::string filename; std::list<std::string>::const_iterator it; for (it=ttspath.begin(); it!=ttspath.end(); ++it) { std::string suffix, aword; aword = *it + G_DIR_SEPARATOR_S + "a" + G_DIR_SEPARATOR_S + "a.mp3"; if (g_file_test(aword.c_str(), G_FILE_TEST_EXISTS)) suffix = ".mp3"; else suffix = ".wav"; filename = *it + G_DIR_SEPARATOR_S + lowerword[0] + G_DIR_SEPARATOR_S + lowerword + suffix; return_val = g_file_test(filename.c_str(), G_FILE_TEST_EXISTS); if (return_val) break; } } return return_val; } void ReadWord::Command_read(const gchar *word) { gchar *eword = g_shell_quote(word); gchar *command = g_strdup_printf(tts_program_cmdline.c_str(), eword); g_free(eword); system(command); g_free(command); } void ReadWord::RealTts_read(const gchar *word) { if (!ttspath.empty() && word && g_ascii_isalpha(word[0])) { std::string lowerword; const gchar *p = word; while (*p) { if (*p!=' ') lowerword+=g_ascii_tolower(*p); p++; } std::string filename; std::list<std::string>::const_iterator it; for (it=ttspath.begin(); it!=ttspath.end(); ++it) { std::string suffix, aword; aword = *it + G_DIR_SEPARATOR_S + "a" + G_DIR_SEPARATOR_S + "a.mp3"; if (g_file_test(aword.c_str(), G_FILE_TEST_EXISTS)) suffix = ".mp3"; else suffix = ".wav"; filename = *it + G_DIR_SEPARATOR_S + lowerword[0] + G_DIR_SEPARATOR_S + lowerword + suffix; if (g_file_test(filename.c_str(), G_FILE_TEST_EXISTS)) { play_wav_file(filename); break; } } } } const int Engine_RealTTS = 0; const int Engine_Command = 1; const int Engine_VirtualTTS_Base = 2; std::list<std::pair<std::string, int> > ReadWord::GetEngineList() { std::list<std::pair<std::string, int> > engine_list; if (!ttspath.empty()) { engine_list.push_back(std::pair<std::string, int>(_("Real People TTS"), Engine_RealTTS)); } size_t n = gpAppFrame->oStarDictPlugins->TtsPlugins.nplugins(); for (size_t i = 0; i < n; i++) { engine_list.push_back(std::pair<std::string, int>(gpAppFrame->oStarDictPlugins->TtsPlugins.tts_name(i), Engine_VirtualTTS_Base + i)); } if (use_command_tts && !tts_program_cmdline.empty()) { engine_list.push_back(std::pair<std::string, int>(_("Command TTS"), Engine_Command)); } return engine_list; } void ReadWord::ReadByEngine(const gchar *word, int engine_index) { if (engine_index == Engine_RealTTS) { RealTts_read(word); } else if (engine_index == Engine_Command) { Command_read(word); } else { gpAppFrame->oStarDictPlugins->TtsPlugins.saytext(engine_index - Engine_VirtualTTS_Base, word); } }



(此处不必在此时改可以在运行软件的界面上修改)

在/stardict-3.0.1/src/conf.cpp中
#if defined(CONFIG_GTK) || defined (CONFIG_GPE)
add_entry("/apps/stardict/preferences/dictionary/play_command", std::string("play "));
#endif

把play改为mplayer。这个其实可以在安装运行以后再手动设置。

cd /usr/portage/app-dicts/stardict

#ebuild  --help
  --skip-manifest       skip all manifest check(这个选项可以跳过md5 检查)


# ebuild   --skip-manifest stardict-3.0.1-r2.ebuild install  //将软件安装到 /var/tmp/portage/app-dicts/stardict-3.0.1-r2 /image/   

ebuild   --skip-manifest stardict-3.0.1-r2.ebuild  qmerge 将软件真正安装到系统   ( install 与qmerge 步骤不能交换)

此三步相当于emerge stardict 步骤

把发音库拷到/usr/share/OtdRealPeopleTTS下面,

tar -jxf OtdRealPeopleTTS.tar.gz2 -C /usr/share
运行,还是没有声音~
从上面的代码我们可以看到,StarDict只能读到/usr/share/OtdRealPeopleTTS下面路径和文件名都是小写的
声音文件。
这么多文件要一个个手工修改?当然不是了~
我这里有个shell脚本,修改这么多文件就靠它了~
运行它后,再运行StarDict应该就可以了

OtdRealPeopleTTS真人发音库下载地址:http://ubuntu:ubuntuftp@ftp.ubun ... ealPeopleTTS.tar.gz
修改好的源码:http://myfilestorage.googlecode.com/files/stardict-3.0.1.tar.bz2
convert脚本(打开可以看到使用方法):http://myfilestorage.googlecode.com/files/convert

角本内容 笔录如下,你可将其保存为名为convert 的文件 照其注释运行

./convert  -l /usr/share/OtdRealPeopleTTS/

如此/usr/share/OtdRealPeopleTTS/ 下的所有文件都会被转化为小写,包括OtdRealPeopleTTS ,你需要手动将其改回大小写混用的,或者在软件界面理改成与其对应的路径

view plaincopy to clipboardprint?

   1. #!/bin/bash  
   2. ############################################################  
   3. # 把目录下的所有文件和文件夹从大写转换到小写,并把文件名中的空格去掉  
   4. # 参数 convert -l ./mydir  
   5. # -u: 小写到大写  
   6. # -l: 大写到小写  
   7. # dir 目录  
   8. ############################################################  
   9. hint ()   
  10. {  
  11.     echo " Usage: $0 [-l|-u] DIR1 [DIR2 DIR3...]  
  12.     -l to lowcase  
  13.     -u to upcase"  
  14.     exit 1  
  15. }  
  16. if test $# -lt 2; then  
  17.     echo "Too few arguments."  
  18.     hint  
  19. fi  
  20. while [ "$1" ]; do  
  21.     case $1 in  
  22.         -l)  
  23.                 ACTION="lo"  
  24.                 shift 1  
  25.                 ;;  
  26.         -u)  
  27.                 ACTION="up"  
  28.                 shift 1  
  29.                 ;;  
  30.         *)  
  31.             if test -d $1; then  
  32.             DIR="$DIR $1"  
  33.             else  
  34.                 echo "no such directory --- $1"  
  35.                 hint  
  36.             fi  
  37.             shift  
  38.             ;;  
  39.     esac  
  40. done  
  41. # echo $ACTION  
  42. # echo $DIR  
  43. find $1 -name "* *" -print |  
  44. while read name; do  
  45. na=$(echo $name | sed 's/ //g')  
  46. echo $na  
  47. if [[ $name != $na ]]; then  
  48. mv "$name" $na  
  49. fi  
  50. done  
  51. FOUND=`find $DIR | sort -r`  
  52. for i in $FOUND  
  53. do  
  54.     DN=`dirname $i`  
  55.     echo "$DN"  
  56.     BS=`basename $i`  
  57.     echo "$BS"  
  58.     loBS=`echo $BS | tr '[A-Z]' '[a-z]'`  
  59.     upBS=`echo $BS | tr '[a-z]' '[A-Z]'`  
  60.      
  61.     loDN=`echo $BS | tr '[A-Z]' '[a-z]'`  
  62.     upDN=`echo $BS | tr '[a-z]' '[A-Z]'`  
  63.      
  64.     NAME1="$DN/$BS"  
  65.     if [ "$ACTION" = "lo" ]; then  
  66.         NAME2="$DN/$loBS"  
  67.     elif [ "$ACTION" = "up" ]; then  
  68.         NAME2="$DN/$upBS"  
  69.     fi  
  70.     if [ "$NAME1" = "$NAME2" ]; then  
  71.         echo "****: $NAME1 ---x--- $NAME2 identical!"  
  72.     else  
  73.         echo "- renaming $NAME1 --> $NAME2"  
  74.         mv  "$NAME1" "$NAME2"  
  75.     fi  
  76. done   

#!/bin/bash ############################################################ # 把目录下的所有文件和文件夹从大写转换到小写,并把文件名中的空格去掉 # 参数 convert -l ./mydir # -u: 小写到大写 # -l: 大写到小写 # dir 目录 ############################################################ hint () { echo " Usage: $0 [-l|-u] DIR1 [DIR2 DIR3...] -l to lowcase -u to upcase" exit 1 } if test $# -lt 2; then echo "Too few arguments." hint fi while [ "$1" ]; do case $1 in -l) ACTION="lo" shift 1 ;; -u) ACTION="up" shift 1 ;; *) if test -d $1; then DIR="$DIR $1" else echo "no such directory --- $1" hint fi shift ;; esac done # echo $ACTION # echo $DIR find $1 -name "* *" -print | while read name; do na=$(echo $name | sed 's/ //g') echo $na if [[ $name != $na ]]; then mv "$name" $na fi done FOUND=`find $DIR | sort -r` for i in $FOUND do DN=`dirname $i` echo "$DN" BS=`basename $i` echo "$BS" loBS=`echo $BS | tr '[A-Z]' '[a-z]'` upBS=`echo $BS | tr '[a-z]' '[A-Z]'` loDN=`echo $BS | tr '[A-Z]' '[a-z]'` upDN=`echo $BS | tr '[a-z]' '[A-Z]'` NAME1="$DN/$BS" if [ "$ACTION" = "lo" ]; then NAME2="$DN/$loBS" elif [ "$ACTION" = "up" ]; then NAME2="$DN/$upBS" fi if [ "$NAME1" = "$NAME2" ]; then echo "****: $NAME1 ---x--- $NAME2 identical!" else echo "- renaming $NAME1 --> $NAME2" mv "$NAME1" "$NAME2" fi done

最近stardict启动经常失败,不知道为什么。有没有高人也遇到过这种情况?

tensecor@~$ stardict
PowerWord data parsing plug-in loaded.
Wiki data parsing plug-in loaded.
XDXF data parsing plug-in loaded.
HTML data parsing plug-in loaded.
WordNet dict rendering plug-in loaded.
Error, no spellchecking dictionary available!
Load /usr/lib/stardict/plugins/stardict_spell.so failed !
Dict.cn plug-in loaded.
WordNet data parsing plug-in loaded.
Man plug-in loaded.
QQWry plug-in loaded.
Segmentation fault

解决办法
 楼主| 发表于 2009-12-2 21:45:28 | 显示全部楼层
这里格式有点不对,
到这个链接会好很多
http://blog.csdn.net/jixiuffff/archive/2009/10/04/4632290.aspx
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表