-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathScenarioProcessing.cs
160 lines (137 loc) · 5.84 KB
/
ScenarioProcessing.cs
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using System;
using System.Collections.Generic;
using System.IO;
using HyoutaPluginBase;
using HyoutaTools.Tales.Graces.SCS;
using HyoutaUtils;
namespace ToGLocInject {
internal static class ScenarioProcessing {
public static (Stream newScenarioFileStream, string newScenarioFilePath, SCS wscsnew, List<List<int>> new_multidefined_widxs) MultiplyOutScenarioFile(List<(int widx, int jidx)> widx_with_multidefined_j, FileFetcher _fc, string f, SCS wscs, List<(int index, string entry)> j, List<(int index, string entry)> u) {
string sopath = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(f)), Path.GetFileNameWithoutExtension(f) + ".so").Replace('\\', '/');
HyoutaPluginBase.FileContainer.IFile wfile = _fc.TryGetFile(sopath, Version.W);
if (wfile == null) {
throw new Exception("failed to find script file for " + f);
}
List<(long pos, int number, long len)> parsedNumbers = ParseScriptfile(wfile.DataStream.Duplicate());
bool injected = false;
Stream ms = wfile.DataStream.CopyToMemory();
List<string> newscs = new List<string>(wscs.Entries);
List<List<int>> new_multidefined_widxs = new List<List<int>>();
foreach (var v in widx_with_multidefined_j) {
List<(long pos, int number, long len)> thisindex = new List<(long pos, int number, long len)>();
foreach (var t in parsedNumbers) {
if (t.number == v.widx) {
thisindex.Add(t);
}
}
if (thisindex.Count <= 1) {
continue;
}
List<int> wscsidxes = new List<int>();
wscsidxes.Add(v.widx);
for (int i = 1; i < thisindex.Count; ++i) {
int newnumber = newscs.Count;
// overwrite number in scriptfile
var d = thisindex[i];
string numstr = SCS.EncodeNumber(newnumber);
string resultstr = "\x1F(0," + numstr + ")";
if (resultstr.Length > d.len) {
if ((d.number == 31 || d.number == 32 || d.number == 33) && f.Contains("zoneR.cpk")) {
// these are harmless, just ignore them
continue;
}
if (d.number == 34 && f == "map1R.cpk/mapfile_lanR.cpk/map/sce/R/ja/e834_080.scs") {
// here a system string and a much later string match
// luckly, we have two early 1-scs-digit strings that are different in JP but match in EN, so we can hijack that
var all34 = parsedNumbers.FindAll(x => x.number == 34);
var all36 = parsedNumbers.FindAll(x => x.number == 36);
var all60 = parsedNumbers.FindAll(x => x.number == 60);
HyoutaTools.Util.Assert(all34.Count == 2 && all34[0].pos < all34[1].pos && all36.Count == 1 && all60.Count == 1);
HyoutaTools.Util.Assert(newscs[36] == newscs[60]);
ms.Position = all60[0].pos;
ms.WriteShiftJisNullterm("\x1F(0," + SCS.EncodeNumber(36) + ")");
ms.Position = all34[1].pos;
ms.WriteShiftJisNullterm("\x1F(0," + SCS.EncodeNumber(60) + ")");
newscs[60] = u.Find(x => x.index == 414).entry;
injected = true;
continue;
}
if (d.number == 60 && f == "map1R.cpk/mapfile_otheR.cpk/map/sce/R/ja/othe_t03.scs") {
// similar thing here, we have two 60s that need to be different, but the 36 matches the first 60, so remap that instead
var all36 = parsedNumbers.FindAll(x => x.number == 36);
var all60 = parsedNumbers.FindAll(x => x.number == 60);
HyoutaTools.Util.Assert(all36.Count == 1 && all60.Count == 2 && all60[0].pos < all60[1].pos);
HyoutaTools.Util.Assert(newscs[36] == newscs[60]);
ms.Position = all60[0].pos;
ms.WriteShiftJisNullterm("\x1F(0," + SCS.EncodeNumber(36) + ")");
newscs[60] = u.Find(x => x.index == 272).entry;
injected = true;
continue;
}
Console.WriteLine("don't know how to inject new number for " + d.number + " (" + newscs[v.widx] + ") in " + f);
continue;
}
ms.Position = d.pos;
ms.WriteShiftJisNullterm(resultstr);
// append new string in wscs
newscs.Add(newscs[v.widx]);
wscsidxes.Add(newnumber);
injected = true;
}
if (wscsidxes.Count > 1) {
new_multidefined_widxs.Add(wscsidxes);
}
}
if (injected) {
ms.Position = 0;
return (ms, sopath, new SCS(newscs), new_multidefined_widxs);
}
return (null, null, null, null);
}
private static bool MatchesTextFormat(string s) {
int idx = s.IndexOf("\x1F(0,");
if (idx < 0) {
return false;
}
int idx2 = s.IndexOf(")", idx);
if (idx2 < 0) {
return false;
}
if (idx2 + 1 != s.Length) {
return false;
}
return true;
}
public static List<(long pos, int number, long len)> ParseScriptfile(DuplicatableStream stream) {
stream.Position = 0;
Stream s = stream.CopyToMemory();
uint magic = s.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian);
uint codeStart = s.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian);
uint unknown3 = s.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian);
uint dataStart = s.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian);
uint unknown5 = s.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian);
uint codeLength = s.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian);
uint dataLength = s.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian);
uint unknown8 = s.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian);
List<(long pos, int number, long len)> strings = new List<(long pos, int number, long len)>();
s.Position = codeStart;
while (s.Position < (dataStart + dataLength)) {
int b = s.ReadByte();
if (b == 0x1F) {
long pos = s.Position;
uint v = s.PeekUInt24();
if (v == 0x2C3028) {
s.Position = pos - 1;
string str = s.ReadNulltermString(TextUtils.GameTextEncoding.ShiftJIS);
if (MatchesTextFormat(str)) {
int num = SCS.DecodeNumber(str.Substring(4, str.Length - 5));
strings.Add((pos - 1, num, str.Length));
}
s.Position = pos;
}
}
}
return strings;
}
}
}