1use crate::scripts::base::*;
2use crate::types::*;
3use crate::utils::encoding::*;
4use crate::utils::escape::*;
5use anyhow::Result;
6use std::collections::HashSet;
7use std::ops::{Deref, DerefMut};
8use std::sync::Arc;
9use unicode_segmentation::UnicodeSegmentation;
10
11#[derive(Debug, Clone)]
12pub struct TxtBuilder {}
14
15impl TxtBuilder {
16 pub const fn new() -> Self {
18 Self {}
19 }
20}
21
22impl ScriptBuilder for TxtBuilder {
23 fn default_encoding(&self) -> Encoding {
24 Encoding::Utf8
25 }
26
27 fn build_script(
28 &self,
29 buf: Vec<u8>,
30 _filename: &str,
31 encoding: Encoding,
32 _archive_encoding: Encoding,
33 config: &ExtraConfig,
34 _archive: Option<&Box<dyn Script>>,
35 ) -> Result<Box<dyn Script>> {
36 Ok(Box::new(TxtScript::new(buf, encoding, config)?))
37 }
38
39 fn extensions(&self) -> &'static [&'static str] {
40 &["txt"]
41 }
42
43 fn script_type(&self) -> &'static ScriptType {
44 &ScriptType::ArtemisTxt
45 }
46}
47
48pub trait Node {
50 fn serialize(&self) -> String;
52}
53
54#[derive(Debug, Clone, PartialEq)]
55pub struct CommentNode(pub String);
57
58impl Node for CommentNode {
59 fn serialize(&self) -> String {
60 format!("//{}", self.0)
61 }
62}
63
64#[derive(Clone, Debug, PartialEq)]
65pub struct EmptyLineNode;
67
68impl Node for EmptyLineNode {
69 fn serialize(&self) -> String {
70 String::new()
71 }
72}
73
74#[derive(Debug, Clone, PartialEq)]
75pub struct LabelNode(pub String);
77
78impl Node for LabelNode {
79 fn serialize(&self) -> String {
80 format!("*{}", self.0)
81 }
82}
83
84#[derive(Debug, Clone, PartialEq)]
85pub struct TagNode {
87 pub name: String,
89 pub attributes: Vec<(String, Option<String>)>,
91}
92
93impl Node for TagNode {
94 fn serialize(&self) -> String {
95 let attributes = self
96 .attributes
97 .iter()
98 .map(|(key, value)| {
99 if let Some(val) = value {
100 format!("{}=\"{}\"", key, val)
101 } else {
102 key.clone()
103 }
104 })
105 .collect::<Vec<_>>()
106 .join(" ");
107 if attributes.is_empty() {
108 format!("[{}]", self.name)
109 } else {
110 format!("[{} {}]", self.name, attributes)
111 }
112 }
113}
114
115impl TagNode {
116 fn ser_attributes_xml(&self) -> String {
117 let mut parts = Vec::new();
118 for (key, value) in self.attributes.iter() {
119 match value {
120 None => {
121 parts.push(key.clone());
122 }
123 Some(val) => {
124 parts.push(format!("{}=\"{}\"", key, escape_xml_attr_value(val)));
125 }
126 }
127 }
128 parts.join(" ")
129 }
130
131 pub fn get_attr(&self, attr: &str) -> Option<&str> {
133 self.attributes
134 .iter()
135 .find(|(key, _)| key == attr)
136 .and_then(|(_, value)| value.as_deref())
137 }
138
139 pub fn is_blocked_name(&self, set: &HashSet<String>) -> bool {
141 set.contains(&self.name)
142 }
143
144 pub fn has_attr(&self, attr: &str) -> bool {
146 self.attributes.iter().any(|(key, _)| key == attr)
147 }
148
149 pub fn set_attr(&mut self, attr: &str, value: Option<String>) {
151 if let Some(pos) = self.attributes.iter().position(|(key, _)| key == attr) {
152 self.attributes[pos].1 = value;
153 } else {
154 self.attributes.push((attr.to_string(), value));
155 }
156 }
157
158 pub fn to_xml(&self) -> String {
160 let attributes = self.ser_attributes_xml();
161 if attributes.is_empty() {
162 format!("<{}>", self.name)
163 } else {
164 format!("<{} {}>", self.name, attributes)
165 }
166 }
167}
168
169#[derive(Debug, Clone, PartialEq)]
170pub struct TextNode(pub String);
172
173#[derive(Debug, Clone, PartialEq)]
174pub enum TxtLineNode {
176 Comment(CommentNode),
177 Tag(TagNode),
178 Text(TextNode),
179}
180
181impl TxtLineNode {
182 pub fn is_tag(&self, tag: &str) -> bool {
186 matches!(self, TxtLineNode::Tag(node) if node.name == tag)
187 }
188
189 pub fn is_tag_blocked_name(&self, set: &HashSet<String>) -> bool {
191 if let TxtLineNode::Tag(node) = self {
192 node.is_blocked_name(set)
193 } else {
194 false
195 }
196 }
197
198 pub fn tag_attr_keys<'a>(&'a self) -> Box<dyn Iterator<Item = &'a str> + 'a> {
200 if let TxtLineNode::Tag(node) = self {
201 Box::new(node.attributes.iter().map(|(key, _)| key.as_str()))
202 } else {
203 Box::new(std::iter::empty())
204 }
205 }
206
207 pub fn tag_get_attr<'a>(&'a self, attr: &str) -> Option<&'a str> {
208 if let TxtLineNode::Tag(node) = self {
209 node.get_attr(attr)
210 } else {
211 None
212 }
213 }
214
215 pub fn tag_has_attr(&self, attr: &str) -> bool {
217 if let TxtLineNode::Tag(node) = self {
218 node.attributes.iter().any(|(key, _)| key == attr)
219 } else {
220 false
221 }
222 }
223
224 pub fn tag_set_attr(&mut self, attr: &str, value: Option<String>) {
225 if let TxtLineNode::Tag(node) = self {
226 node.set_attr(attr, value);
227 }
228 }
229
230 pub fn to_xml(&self) -> String {
232 match self {
233 TxtLineNode::Comment(_) => String::new(), TxtLineNode::Tag(n) => {
235 if (n.name == "rt2" || n.name == "ret2") && n.attributes.is_empty() {
236 "\n".to_string()
237 } else {
238 n.to_xml()
239 }
240 }
241 TxtLineNode::Text(n) => escape_xml_text_value(&n.0),
242 }
243 }
244}
245
246impl Node for TxtLineNode {
247 fn serialize(&self) -> String {
248 match self {
249 TxtLineNode::Comment(node) => node.serialize(),
250 TxtLineNode::Tag(node) => node.serialize(),
251 TxtLineNode::Text(node) => node.0.clone(),
252 }
253 }
254}
255
256#[derive(Debug, Clone, PartialEq)]
257pub struct TxtLine(pub Vec<TxtLineNode>);
259
260impl Deref for TxtLine {
261 type Target = Vec<TxtLineNode>;
262
263 fn deref(&self) -> &Self::Target {
264 &self.0
265 }
266}
267
268impl DerefMut for TxtLine {
269 fn deref_mut(&mut self) -> &mut Self::Target {
270 &mut self.0
271 }
272}
273
274impl Node for TxtLine {
275 fn serialize(&self) -> String {
276 self.0
277 .iter()
278 .map(|node| node.serialize())
279 .collect::<Vec<_>>()
280 .join("")
281 }
282}
283
284impl TxtLine {
285 pub fn to_xml(&self) -> String {
287 self.0
288 .iter()
289 .map(|node| node.to_xml())
290 .collect::<Vec<_>>()
291 .join("")
292 }
293}
294
295#[derive(Debug, Clone, PartialEq)]
296pub enum ParsedLine {
298 Empty(EmptyLineNode),
300 Comment(CommentNode),
302 Label(LabelNode),
304 Line(TxtLine),
306}
307
308impl Node for ParsedLine {
309 fn serialize(&self) -> String {
310 match self {
311 ParsedLine::Empty(node) => node.serialize(),
312 ParsedLine::Comment(node) => node.serialize(),
313 ParsedLine::Label(node) => node.serialize(),
314 ParsedLine::Line(line) => line.serialize(),
315 }
316 }
317}
318
319impl ParsedLine {
320 pub fn len(&self) -> usize {
322 match self {
323 ParsedLine::Empty(_) => 0,
324 ParsedLine::Comment(_) => 0,
325 ParsedLine::Label(_) => 0,
326 ParsedLine::Line(line) => line.len(),
327 }
328 }
329
330 pub fn push(&mut self, node: TxtLineNode) {
332 if let ParsedLine::Line(line) = self {
333 line.push(node);
334 } else {
335 }
337 }
338
339 pub fn insert(&mut self, index: usize, node: TxtLineNode) {
341 if let ParsedLine::Line(line) = self {
342 line.insert(index, node);
343 } else {
344 }
346 }
347
348 pub fn remove(&mut self, index: usize) -> Option<TxtLineNode> {
350 if let ParsedLine::Line(line) = self {
351 if index < line.len() {
352 Some(line.remove(index))
353 } else {
354 None
355 }
356 } else {
357 None
359 }
360 }
361}
362
363#[derive(Debug, Clone)]
364pub struct ParsedScript(pub Vec<ParsedLine>);
366
367impl Deref for ParsedScript {
368 type Target = Vec<ParsedLine>;
369
370 fn deref(&self) -> &Self::Target {
371 &self.0
372 }
373}
374
375impl DerefMut for ParsedScript {
376 fn deref_mut(&mut self) -> &mut Self::Target {
377 &mut self.0
378 }
379}
380
381impl Node for ParsedScript {
382 fn serialize(&self) -> String {
383 self.0
384 .iter()
385 .map(|line| line.serialize())
386 .collect::<Vec<_>>()
387 .join("\n")
388 }
389}
390
391pub struct Parser {
393 lines: Vec<String>,
394}
395
396impl Parser {
397 pub fn new<S: AsRef<str> + ?Sized>(script: &S) -> Self {
398 let lines = script.as_ref().lines().map(|s| s.to_string()).collect();
399 Self { lines }
400 }
401
402 pub fn parse(&self, preserve_empty_lines: bool) -> Result<ParsedScript> {
403 let mut parsed_script = Vec::new();
404 let mut i = 0;
405 let line_count = self.lines.len();
406 while i < line_count {
407 let line = self.lines[i].trim();
408 i += 1;
409 if line.is_empty() {
410 if preserve_empty_lines {
411 parsed_script.push(ParsedLine::Empty(EmptyLineNode));
412 }
413 continue;
414 }
415 if line.starts_with("//") {
416 parsed_script.push(ParsedLine::Comment(CommentNode(line[2..].to_string())));
417 continue;
418 }
419 if line.starts_with("*") {
420 let label = line[1..].trim().to_string();
421 parsed_script.push(ParsedLine::Label(LabelNode(label)));
422 continue;
423 }
424 let mut temp = String::new();
425 let mut nodes = Vec::new();
426 let mut line_graphs = line.graphemes(true).collect::<Vec<_>>();
427 let mut line_pos = 0;
428 let mut is_comment = false;
429 while line_pos < line_graphs.len() {
430 let graph = line_graphs[line_pos];
431 line_pos += 1;
432 temp.push_str(graph);
433 if is_comment {
434 continue;
435 }
436 if !is_comment && temp.ends_with("//") && temp.len() > 2 {
437 nodes.push(TxtLineNode::Text(TextNode(
438 temp[..temp.len() - 2].to_string(),
439 )));
440 temp.clear();
441 is_comment = true;
442 continue;
443 }
444 if graph == "[" {
445 if !temp.trim_end_matches("[").is_empty() {
446 nodes.push(TxtLineNode::Text(TextNode(
447 temp.trim_end_matches("[").to_string(),
448 )));
449 }
450 while !line_graphs[line_pos..].contains(&"]") {
452 if i < line_count {
453 let nline = self.lines[i].trim();
454 i += 1;
455 line_graphs.push("\n");
457 line_graphs.extend(nline.graphemes(true));
458 } else {
459 break;
460 }
461 }
462 let (tag, nextpos) = TagParser {
463 graphs: &line_graphs,
464 pos: line_pos,
465 }
466 .parse()?;
467 line_pos = nextpos;
468 nodes.push(TxtLineNode::Tag(tag));
469 temp.clear();
470 continue;
471 }
472 }
473 if is_comment {
474 nodes.push(TxtLineNode::Comment(CommentNode(temp)));
475 } else {
476 if !temp.is_empty() {
477 nodes.push(TxtLineNode::Text(TextNode(temp)));
478 }
479 }
480 parsed_script.push(ParsedLine::Line(TxtLine(nodes)));
481 }
482 Ok(ParsedScript(parsed_script))
483 }
484}
485
486struct TagParser<'a> {
487 graphs: &'a [&'a str],
488 pos: usize,
489}
490
491impl<'a> TagParser<'a> {
492 fn peek(&self) -> Option<&'a str> {
493 self.graphs.get(self.pos).cloned()
494 }
495
496 fn eat(&mut self) {
497 if self.pos < self.graphs.len() {
498 self.pos += 1;
499 }
500 }
501
502 fn next(&mut self) -> Option<&'a str> {
503 if self.pos < self.graphs.len() {
504 let graph = self.graphs[self.pos];
505 self.pos += 1;
506 Some(graph)
507 } else {
508 None
509 }
510 }
511
512 fn is_indent(&self, indent: &str) -> bool {
513 let mut pos = self.pos;
514 for ident in indent.graphemes(true) {
515 if pos >= self.graphs.len() || self.graphs[pos] != ident {
516 return false;
517 }
518 pos += 1;
519 }
520 true
521 }
522
523 fn eat_all_equal(&mut self) {
524 while let Some(graph) = self.peek() {
525 if graph == "=" {
526 self.eat();
527 } else {
528 break;
529 }
530 }
531 }
532
533 fn parse(&mut self) -> Result<(TagNode, usize)> {
534 let name = self.parse_tag()?;
535 self.erase_whitespace();
536 let mut attributes = Vec::new();
537 loop {
538 let graph = match self.peek() {
539 Some(g) => g,
540 None => {
541 return Err(anyhow::anyhow!(
542 "Unexpected end of tag parsing: {}",
543 self.graphs.join("")
544 ));
545 }
546 };
547 if graph == "]" {
548 self.eat();
549 break;
550 }
551 if graph == " " || graph == "\t" {
552 self.eat();
553 continue;
554 }
555 if graph == "=" {
556 return Err(anyhow::anyhow!("Unexpected '=' without attribute name"));
557 }
558 let attr_name = self.parse_attr_name()?;
559 self.erase_whitespace();
560 let graph = match self.peek() {
561 Some(g) => g,
562 None => {
563 return Err(anyhow::anyhow!(
564 "Unexpected end of tag parsing: {}",
565 self.graphs.join("")
566 ));
567 }
568 };
569 if graph == "]" {
570 self.eat();
571 attributes.push((attr_name, None));
572 break;
573 }
574 if graph == "=" {
575 self.eat_all_equal();
579 self.erase_whitespace();
580 let value = self.parse_attr_value()?;
581 attributes.push((attr_name, Some(value)));
582 self.erase_whitespace();
583 } else {
584 attributes.push((attr_name, None));
585 self.erase_whitespace();
586 continue;
587 }
588 }
589 return Ok((TagNode { name, attributes }, self.pos));
590 }
591
592 fn erase_whitespace(&mut self) {
593 while let Some(graph) = self.peek() {
594 if graph == " " || graph == "\t" {
595 self.eat();
596 } else {
597 break;
598 }
599 }
600 }
601
602 fn parse_attr_name(&mut self) -> Result<String> {
603 let mut attr_name = String::new();
604 while let Some(graph) = self.peek() {
605 if graph == "=" || graph == " " || graph == "\t" || graph == "]" {
606 break;
607 }
608 attr_name.push_str(graph);
609 self.eat();
610 }
611 if attr_name.is_empty() {
612 return Err(anyhow::anyhow!("Empty attribute name found"));
613 }
614 Ok(attr_name)
615 }
616
617 fn parse_attr_value(&mut self) -> Result<String> {
618 let mut value = String::new();
619 if !self.is_indent("\"") {
620 return Err(anyhow::anyhow!(
621 "Expected attribute value to start with a quote: {}",
622 self.graphs.join("")
623 ));
624 }
625 self.eat(); while let Some(graph) = self.next() {
627 if graph == "\"" {
628 break; }
630 value.push_str(graph);
631 }
632 Ok(value)
633 }
634
635 fn parse_tag(&mut self) -> Result<String> {
636 let mut tag = String::new();
637 while let Some(graph) = self.peek() {
638 if graph == " " || graph == "\t" || graph == "]" {
639 break;
640 }
641 tag.push_str(graph);
642 self.eat();
643 }
644 if tag.is_empty() {
645 return Err(anyhow::anyhow!("Empty tag found"));
646 }
647 Ok(tag)
648 }
649}
650
651struct XMLTextParser<'a> {
652 str: &'a str,
653 lang: &'a str,
654 pos: usize,
655}
656
657impl<'a> XMLTextParser<'a> {
658 pub fn new(text: &'a str, lang: &'a str) -> Self {
659 Self {
660 str: text,
661 lang,
662 pos: 0,
663 }
664 }
665
666 fn parse_tag(&mut self) -> Result<TagNode> {
667 let mut name = String::new();
668 let mut attributes = Vec::new();
669 let mut is_name = true;
670 let mut is_key = false;
671 let mut is_value = false;
672 let mut is_in_quote = false;
673 let mut key = String::new();
674 let mut value = String::new();
675 while let Some(c) = self.next() {
676 match c {
677 '>' => {
678 if !name.is_empty() {
679 return Ok(TagNode { name, attributes });
680 } else {
681 return Err(anyhow::anyhow!("Empty tag name"));
682 }
683 }
684 ' ' | '\t' => {
685 if is_name {
686 is_name = false;
687 is_key = true;
688 } else if is_key {
689 if !key.is_empty() {
690 attributes.push((key.clone(), None));
691 key.clear();
692 }
693 } else if is_value {
694 if is_in_quote {
695 value.push(c);
696 } else {
697 if !value.is_empty() {
698 attributes.push((key.clone(), Some(unescape_xml(&value))));
699 key.clear();
700 value.clear();
701 }
702 is_key = true;
703 is_value = false;
704 }
705 }
706 }
707 '"' => {
708 if is_in_quote {
709 is_in_quote = false;
710 if !value.is_empty() {
711 attributes.push((key.clone(), Some(unescape_xml(&value))));
712 key.clear();
713 value.clear();
714 }
715 is_key = true;
716 } else {
717 is_in_quote = true;
718 }
719 }
720 '=' => {
721 if is_key {
722 is_key = false;
723 is_value = true;
724 }
725 }
726 _ => {
727 if is_name {
728 name.push(c);
729 } else if is_key {
730 key.push(c);
731 } else if is_value {
732 value.push(c);
733 } else {
734 return Err(anyhow::anyhow!("Unexpected character in tag: {}", c));
735 }
736 }
737 }
738 }
739 Err(anyhow::anyhow!("Unexpected end of input while parsing tag"))
740 }
741
742 pub fn parse(mut self) -> Result<Vec<ParsedLine>> {
743 let mut lines = Vec::new();
744 let mut current_line = Vec::new();
745 let mut text = String::new();
746 current_line.push(TxtLineNode::Tag(TagNode {
747 name: "lang".to_string(),
748 attributes: vec![(self.lang.to_string(), None)],
749 }));
750 while let Some(c) = self.next() {
751 match c {
752 '<' => {
753 if !text.is_empty() {
754 current_line.push(TxtLineNode::Text(TextNode(unescape_xml(&text))));
755 text.clear();
756 }
757 let tag = self.parse_tag()?;
758 let is_r = tag.name == "rt2" || tag.name == "ret2";
759 current_line.push(TxtLineNode::Tag(tag));
760 if is_r {
761 lines.push(ParsedLine::Line(TxtLine(current_line)));
762 current_line = Vec::new();
763 }
764 }
765 '\n' => {
766 if !text.is_empty() {
767 current_line.push(TxtLineNode::Text(TextNode(unescape_xml(&text))));
768 text.clear();
769 }
770 current_line.push(TxtLineNode::Tag(TagNode {
771 name: "rt2".to_string(),
772 attributes: Vec::new(),
773 }));
774 lines.push(ParsedLine::Line(TxtLine(current_line)));
775 current_line = Vec::new();
776 }
777 _ => text.push(c),
778 }
779 }
780 if !text.is_empty() {
781 current_line.push(TxtLineNode::Text(TextNode(unescape_xml(&text))));
782 }
783 current_line.push(TxtLineNode::Tag(TagNode {
784 name: "/lang".to_string(),
785 attributes: Vec::new(),
786 }));
787 lines.push(ParsedLine::Line(TxtLine(current_line)));
788 Ok(lines)
789 }
790
791 fn next(&mut self) -> Option<char> {
792 if self.pos < self.str.len() {
793 let c = self.str[self.pos..].chars().next()?;
794 self.pos += c.len_utf8();
795 Some(c)
796 } else {
797 None
798 }
799 }
800}
801
802#[derive(Debug)]
803pub struct TxtScript {
804 tree: ParsedScript,
805 blacklist_names: Arc<HashSet<String>>,
806 lang: Option<String>,
807}
808
809impl TxtScript {
810 pub fn new(buf: Vec<u8>, encoding: Encoding, config: &ExtraConfig) -> Result<Self> {
812 let script = decode_to_string(encoding, &buf, true)?;
813 let parser = Parser::new(&script);
814 let tree = parser.parse(true)?;
815 Ok(Self {
816 tree,
817 blacklist_names: config.artemis_txt_blacklist_names.clone(),
818 lang: config.artemis_txt_lang.clone(),
819 })
820 }
821}
822
823impl Script for TxtScript {
824 fn default_output_script_type(&self) -> OutputScriptType {
825 OutputScriptType::Json
826 }
827
828 fn default_format_type(&self) -> FormatOptions {
829 FormatOptions::None
830 }
831
832 fn extract_messages(&self) -> Result<Vec<Message>> {
833 let mut messages = Vec::new();
834 let mut i = 0;
835 let len = self.tree.len();
836 let mut last_tag_block: Option<TagNode> = None;
837 let mut lang = self.lang.as_ref().map(|s| s.as_str());
838 let mut message = TxtLine(Vec::new());
839 let mut in_lang_block = false;
840 let mut droped_lang_block = false;
841 let mut is_selectblk = false;
842 while i < len {
843 let line = &self.tree[i];
844 if let ParsedLine::Line(line) = line {
845 for node in line.iter() {
846 if node.is_tag("lang") {
847 let lan = match lang {
848 Some(l) => l,
849 None => {
850 for key in node.tag_attr_keys() {
851 lang = Some(key);
852 break;
853 }
854 match lang {
855 Some(l) => l,
856 None => {
857 return Err(anyhow::anyhow!(
858 "No language found in lang tag"
859 ));
860 }
861 }
862 }
863 };
864 if node.tag_has_attr(lan) {
865 in_lang_block = true;
866 } else {
867 droped_lang_block = true;
868 }
869 } else if node.is_tag("/lang") {
870 in_lang_block = false;
871 droped_lang_block = false;
872 } else if node.is_tag("printlang") {
873 let mes = message.to_xml();
874 message.clear();
875 if !mes.is_empty() {
876 let name = if mes.starts_with("「") {
877 match &last_tag_block {
878 Some(block) => {
879 Some(if let Some(name) = block.get_attr("name") {
880 name.to_string()
881 } else {
882 block.name.clone()
883 })
884 }
885 _ => {
886 eprintln!("Warn: Name block not found.");
887 crate::COUNTER.inc_warning();
888 None
889 }
890 }
891 } else {
892 None
893 };
894 messages.push(Message { name, message: mes });
895 }
896 last_tag_block = None;
897 } else if node.is_tag("selectbtn_init") {
898 is_selectblk = true;
899 } else if node.is_tag("selectbtn") {
900 let mut lan = match lang {
901 Some(l) => l,
902 None => {
903 for key in node.tag_attr_keys() {
904 if key == "label" || key == "call" {
905 continue;
906 }
907 lang = Some(key);
908 break;
909 }
910 match lang {
911 Some(l) => l,
912 None => {
913 return Err(anyhow::anyhow!(
914 "No language found in selectbtn tag"
915 ));
916 }
917 }
918 }
919 };
920 if !node.tag_has_attr(lan) {
921 for key in node.tag_attr_keys() {
922 if key == "label" || key == "call" {
923 continue;
924 }
925 lan = key;
926 break;
927 }
928 }
929 if let Some(t) = node.tag_get_attr(lan) {
930 messages.push(Message {
931 name: None,
932 message: t.to_string(),
933 });
934 }
935 } else if node.is_tag("/selectbtn") {
936 is_selectblk = false;
937 } else if in_lang_block {
938 message.push(node.clone());
939 } else if droped_lang_block {
940 } else if is_selectblk {
942 } else if let TxtLineNode::Tag(tag) = node {
944 if !tag.is_blocked_name(&self.blacklist_names) {
945 last_tag_block = Some(tag.clone());
946 }
947 }
948 }
949 }
950 i += 1;
951 }
952 Ok(messages)
953 }
954
955 fn import_messages<'a>(
956 &'a self,
957 messages: Vec<Message>,
958 mut file: Box<dyn WriteSeek + 'a>,
959 _filename: &str,
960 encoding: Encoding,
961 replacement: Option<&'a ReplacementTable>,
962 ) -> Result<()> {
963 let mut output = self.tree.clone();
964 let mut current_line = 0;
965 let mut last_tag_block_loc = None;
966 let mut lang = self.lang.clone();
967 let mut mes = messages.iter();
968 let mut mess = mes.next();
969 let mut lang_block_index = None;
970 let mut lang_end_block_index = None;
971 let mut in_lang_block = false;
972 let mut droped_lang_block = false;
973 let mut is_selectblk = false;
974 while current_line < output.len() {
975 let line = output[current_line].clone();
976 if let ParsedLine::Line(line) = &line {
977 for (i, node) in line.iter().enumerate() {
978 if node.is_tag("lang") {
979 let lan = match lang.as_ref() {
980 Some(l) => l.as_str(),
981 None => {
982 for key in node.tag_attr_keys() {
983 lang = Some(key.to_string());
984 break;
985 }
986 match lang.as_ref() {
987 Some(l) => l.as_str(),
988 None => {
989 return Err(anyhow::anyhow!(
990 "No language found in lang tag"
991 ));
992 }
993 }
994 }
995 };
996 if node.tag_has_attr(lan) {
997 in_lang_block = true;
998 lang_block_index = Some((current_line, i));
999 } else {
1000 droped_lang_block = true;
1001 }
1002 } else if node.is_tag("/lang") {
1003 if in_lang_block {
1004 lang_end_block_index = Some((current_line, i));
1005 }
1006 in_lang_block = false;
1007 droped_lang_block = false;
1008 } else if node.is_tag("printlang") {
1009 let lan = lang
1010 .as_ref()
1011 .map(|s| s.as_str())
1012 .ok_or(anyhow::anyhow!("No language specified."))?;
1013 let m = match mess {
1014 Some(m) => m,
1015 None => {
1016 return Err(anyhow::anyhow!("Not enough messages."));
1017 }
1018 };
1019 if let Some(name) = &m.name {
1020 let block_index: (usize, usize) = match last_tag_block_loc.take() {
1021 Some(data) => data,
1022 None => {
1023 return Err(anyhow::anyhow!(
1024 "No name tag block found before printlang.",
1025 ));
1026 }
1027 };
1028 let mut name = name.clone();
1029 if let Some(repl) = replacement {
1030 for (k, v) in &repl.map {
1031 name = name.replace(k, v);
1032 }
1033 }
1034 let mblock = &mut output[block_index.0];
1035 if let ParsedLine::Line(txt_line) = mblock {
1036 let block = txt_line[block_index.1].clone();
1037 if let TxtLineNode::Tag(mut tag) = block {
1038 tag.set_attr("name", Some(name));
1039 txt_line[block_index.1] = TxtLineNode::Tag(tag);
1040 } else {
1041 return Err(anyhow::anyhow!(
1042 "Last tag block is not a tag: {:?}",
1043 mblock
1044 ));
1045 }
1046 } else {
1047 return Err(anyhow::anyhow!(
1048 "Last tag block is not a line: {:?}",
1049 mblock
1050 ));
1051 }
1052 }
1053 let mut message = m.message.clone();
1054 if let Some(repl) = replacement {
1055 for (k, v) in &repl.map {
1056 message = message.replace(k, v);
1057 }
1058 }
1059 let mut nodes = XMLTextParser::new(&message, lan).parse()?;
1060 if lang_block_index.is_some() && lang_end_block_index.is_some() {
1061 let start_index = lang_block_index.unwrap();
1062 let end_index = lang_end_block_index.unwrap();
1063 if start_index.1 != 0 {
1064 let block = output[start_index.0].clone();
1065 if let ParsedLine::Line(txt_line) = block {
1066 for i in 0..start_index.1 {
1067 nodes[0].insert(i, txt_line[i].clone());
1068 }
1069 } else {
1070 return Err(anyhow::anyhow!(
1071 "Lang block start is not a line: {:?}",
1072 block
1073 ));
1074 }
1075 }
1076 if end_index.1 + 1 < output[end_index.0].len() {
1077 let block = output[end_index.0].clone();
1078 if let ParsedLine::Line(txt_line) = block {
1079 for i in end_index.1 + 1..txt_line.len() {
1080 nodes.last_mut().unwrap().push(txt_line[i].clone());
1081 }
1082 } else {
1083 return Err(anyhow::anyhow!(
1084 "Lang block end is not a line: {:?}",
1085 block
1086 ));
1087 }
1088 }
1089 let ori_len = (end_index.0 - start_index.0 + 1) as isize;
1090 let new_len = nodes.len() as isize;
1091 for _ in start_index.0..=end_index.0 {
1092 output.remove(start_index.0);
1093 }
1094 let mut start_index = start_index.0;
1095 for node in nodes {
1096 output.insert(start_index, node);
1097 start_index += 1;
1098 }
1099 current_line = (current_line as isize + new_len - ori_len) as usize;
1100 } else {
1101 for node in nodes {
1103 output.insert(current_line, node);
1104 current_line += 1;
1105 }
1106 }
1107 lang_block_index = None;
1108 lang_end_block_index = None;
1109 mess = mes.next();
1110 last_tag_block_loc = None;
1111 } else if node.is_tag("selectbtn_init") {
1112 is_selectblk = true;
1113 } else if node.is_tag("selectbtn") {
1114 let lan = match lang.as_ref() {
1115 Some(l) => l.as_str(),
1116 None => {
1117 for key in node.tag_attr_keys() {
1118 if key == "label" || key == "call" {
1119 continue;
1120 }
1121 lang = Some(key.to_string());
1122 break;
1123 }
1124 match lang.as_ref() {
1125 Some(l) => l.as_str(),
1126 None => {
1127 return Err(anyhow::anyhow!(
1128 "No language found in selectbtn tag"
1129 ));
1130 }
1131 }
1132 }
1133 };
1134 let m = match mess {
1135 Some(m) => m,
1136 None => {
1137 return Err(anyhow::anyhow!("Not enough messages."));
1138 }
1139 };
1140 let mut message = m.message.clone();
1141 if let Some(repl) = replacement {
1142 for (k, v) in &repl.map {
1143 message = message.replace(k, v);
1144 }
1145 }
1146 let mut node = node.clone();
1147 node.tag_set_attr(lan, Some(message));
1148 let block = &mut output[current_line];
1149 if let ParsedLine::Line(txt_line) = block {
1150 txt_line[i] = node;
1151 } else {
1152 return Err(anyhow::anyhow!("selectbtn tag not in line: {:?}", block));
1153 }
1154 mess = mes.next();
1155 } else if node.is_tag("/selectbtn") {
1156 is_selectblk = false;
1157 } else if in_lang_block {
1158 } else if droped_lang_block {
1160 } else if is_selectblk {
1162 } else if let TxtLineNode::Tag(tag) = node {
1164 if !tag.is_blocked_name(&self.blacklist_names) {
1165 last_tag_block_loc = Some((current_line, i));
1166 }
1167 }
1168 }
1169 }
1170 current_line += 1;
1171 }
1172 let s = output.serialize();
1173 let encoded = encode_string(encoding, &s, false)?;
1174 file.write_all(&encoded)?;
1175 file.flush()?;
1176 Ok(())
1177 }
1178}
1179
1180#[test]
1181fn test_xml_parser() {
1182 let data = "测试文本\nok<r a=\"b\">测试<b o=\"文本\n换行\">";
1183 let data = XMLTextParser::new(data, "en").parse().unwrap();
1184 assert_eq!(
1185 data,
1186 vec![
1187 ParsedLine::Line(TxtLine(vec![
1188 TxtLineNode::Tag(TagNode {
1189 name: "lang".to_string(),
1190 attributes: vec![("en".to_string(), None)],
1191 }),
1192 TxtLineNode::Text(TextNode("测试文本".to_string())),
1193 TxtLineNode::Tag(TagNode {
1194 name: "rt2".to_string(),
1195 attributes: vec![],
1196 }),
1197 ])),
1198 ParsedLine::Line(TxtLine(vec![
1199 TxtLineNode::Text(TextNode("ok".to_string())),
1200 TxtLineNode::Tag(TagNode {
1201 name: "r".to_string(),
1202 attributes: vec![("a".to_string(), Some("b".to_string()))],
1203 }),
1204 TxtLineNode::Text(TextNode("测试".to_string())),
1205 TxtLineNode::Tag(TagNode {
1206 name: "b".to_string(),
1207 attributes: vec![("o".to_string(), Some("文本\n换行".to_string()))],
1208 }),
1209 TxtLineNode::Tag(TagNode {
1210 name: "/lang".to_string(),
1211 attributes: vec![],
1212 }),
1213 ])),
1214 ],
1215 );
1216}