|
2 | 2 | using System.Collections.Generic; |
3 | 3 | using System.IO; |
4 | 4 | using System.Text; |
| 5 | +using SabreTools.IO.Compression.zlib; |
5 | 6 | using SabreTools.IO.Extensions; |
6 | 7 | using SabreTools.Matching; |
7 | 8 | using SabreTools.Serialization.Interfaces; |
@@ -1084,20 +1085,148 @@ private void ParseDebugTable() |
1084 | 1085 | #region Extraction |
1085 | 1086 |
|
1086 | 1087 | /// <inheritdoc/> |
1087 | | - /// <remarks>This only extracts overlay and resource data</remarks> |
| 1088 | + /// <remarks> |
| 1089 | + /// This extracts the following data: |
| 1090 | + /// - Archives and executables in the overlay |
| 1091 | + /// - Archives and executables in resource data |
| 1092 | + /// - CExe-compressed resource data |
| 1093 | + /// </remarks> |
1088 | 1094 | public bool Extract(string outputDirectory, bool includeDebug) |
1089 | 1095 | { |
| 1096 | + bool cexe = ExtractCExe(outputDirectory, includeDebug); |
1090 | 1097 | bool overlay = ExtractFromOverlay(outputDirectory, includeDebug); |
1091 | 1098 | bool resources = ExtractFromResources(outputDirectory, includeDebug); |
1092 | | - return overlay || resources; |
| 1099 | + return cexe || overlay || resources; |
| 1100 | + } |
| 1101 | + |
| 1102 | + /// <summary> |
| 1103 | + /// Extract a CExe-compressed executable |
| 1104 | + /// </summary> |
| 1105 | + /// <param name="outputDirectory">Output directory to write to</param> |
| 1106 | + /// <param name="includeDebug">True to include debug data, false otherwise</param> |
| 1107 | + /// <returns>True if extraction succeeded, false otherwise</returns> |
| 1108 | + public bool ExtractCExe(string outputDirectory, bool includeDebug) |
| 1109 | + { |
| 1110 | + try |
| 1111 | + { |
| 1112 | + // Get all resources of type 99 with index 2 |
| 1113 | + var resources = FindResourceByNamedType("99, 2"); |
| 1114 | + if (resources == null || resources.Count == 0) |
| 1115 | + return false; |
| 1116 | + |
| 1117 | + // Get the first resource of type 99 with index 2 |
| 1118 | + var payload = resources[0]; |
| 1119 | + if (payload == null || payload.Length == 0) |
| 1120 | + return false; |
| 1121 | + |
| 1122 | + // Create the output data buffer |
| 1123 | + byte[]? data = []; |
| 1124 | + |
| 1125 | + // If we had the decompression DLL included, it's zlib |
| 1126 | + if (FindResourceByNamedType("99, 1").Count > 0) |
| 1127 | + data = ExtractCExeZlib(payload); |
| 1128 | + else |
| 1129 | + data = ExtractCExeLZ(payload); |
| 1130 | + |
| 1131 | + // If we have no data |
| 1132 | + if (data == null) |
| 1133 | + return false; |
| 1134 | + |
| 1135 | + // Create the temp filename |
| 1136 | + string tempFile = string.IsNullOrEmpty(Filename) ? "temp.sxe" : $"{Path.GetFileNameWithoutExtension(Filename)}.sxe"; |
| 1137 | + tempFile = Path.Combine(outputDirectory, tempFile); |
| 1138 | + var directoryName = Path.GetDirectoryName(tempFile); |
| 1139 | + if (directoryName != null && !Directory.Exists(directoryName)) |
| 1140 | + Directory.CreateDirectory(directoryName); |
| 1141 | + |
| 1142 | + // Write the file data to a temp file |
| 1143 | + var tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite); |
| 1144 | + tempStream.Write(data, 0, data.Length); |
| 1145 | + |
| 1146 | + return true; |
| 1147 | + } |
| 1148 | + catch (Exception ex) |
| 1149 | + { |
| 1150 | + if (includeDebug) Console.Error.WriteLine(ex); |
| 1151 | + return false; |
| 1152 | + } |
| 1153 | + } |
| 1154 | + |
| 1155 | + /// <summary> |
| 1156 | + /// Extract CExe data compressed with LZ |
| 1157 | + /// </summary> |
| 1158 | + /// <param name="resource">Resource data to inflate</param> |
| 1159 | + /// <returns>Inflated data on success, null otherwise</returns> |
| 1160 | + private byte[]? ExtractCExeLZ(byte[] resource) |
| 1161 | + { |
| 1162 | + try |
| 1163 | + { |
| 1164 | + var decompressor = IO.Compression.SZDD.Decompressor.CreateSZDD(resource); |
| 1165 | + var dataStream = new MemoryStream(); |
| 1166 | + decompressor.CopyTo(dataStream); |
| 1167 | + return dataStream.ToArray(); |
| 1168 | + } |
| 1169 | + catch |
| 1170 | + { |
| 1171 | + // Reset the data |
| 1172 | + return null; |
| 1173 | + } |
| 1174 | + } |
| 1175 | + |
| 1176 | + /// <summary> |
| 1177 | + /// Extract CExe data compressed with zlib |
| 1178 | + /// </summary> |
| 1179 | + /// <param name="resource">Resource data to inflate</param> |
| 1180 | + /// <returns>Inflated data on success, null otherwise</returns> |
| 1181 | + private byte[]? ExtractCExeZlib(byte[] resource) |
| 1182 | + { |
| 1183 | + try |
| 1184 | + { |
| 1185 | + // Inflate the data into the buffer |
| 1186 | + var zstream = new ZLib.z_stream_s(); |
| 1187 | + byte[] data = new byte[resource.Length * 4]; |
| 1188 | + unsafe |
| 1189 | + { |
| 1190 | + fixed (byte* payloadPtr = resource) |
| 1191 | + fixed (byte* dataPtr = data) |
| 1192 | + { |
| 1193 | + zstream.next_in = payloadPtr; |
| 1194 | + zstream.avail_in = (uint)resource.Length; |
| 1195 | + zstream.total_in = (uint)resource.Length; |
| 1196 | + zstream.next_out = dataPtr; |
| 1197 | + zstream.avail_out = (uint)data.Length; |
| 1198 | + zstream.total_out = 0; |
| 1199 | + |
| 1200 | + ZLib.inflateInit_(zstream, ZLib.zlibVersion(), resource.Length); |
| 1201 | + int zret = ZLib.inflate(zstream, 1); |
| 1202 | + ZLib.inflateEnd(zstream); |
| 1203 | + } |
| 1204 | + } |
| 1205 | + |
| 1206 | + // Trim the buffer to the proper size |
| 1207 | + uint read = zstream.total_out; |
| 1208 | +#if NETFRAMEWORK |
| 1209 | + var temp = new byte[read]; |
| 1210 | + Array.Copy(data, temp, read); |
| 1211 | + data = temp; |
| 1212 | +#else |
| 1213 | + data = new ReadOnlySpan<byte>(data, 0, (int)read).ToArray(); |
| 1214 | +#endif |
| 1215 | + return data; |
| 1216 | + } |
| 1217 | + catch |
| 1218 | + { |
| 1219 | + // Reset the data |
| 1220 | + return null; |
| 1221 | + } |
1093 | 1222 | } |
1094 | 1223 |
|
1095 | 1224 | /// <summary> |
1096 | 1225 | /// Extract data from the overlay |
1097 | 1226 | /// </summary> |
1098 | 1227 | /// <param name="outputDirectory">Output directory to write to</param> |
1099 | 1228 | /// <param name="includeDebug">True to include debug data, false otherwise</param> |
1100 | | - private bool ExtractFromOverlay(string outputDirectory, bool includeDebug) |
| 1229 | + public bool ExtractFromOverlay(string outputDirectory, bool includeDebug) |
1101 | 1230 | { |
1102 | 1231 | try |
1103 | 1232 | { |
@@ -1152,7 +1281,7 @@ private bool ExtractFromOverlay(string outputDirectory, bool includeDebug) |
1152 | 1281 | /// </summary> |
1153 | 1282 | /// <param name="outputDirectory">Output directory to write to</param> |
1154 | 1283 | /// <param name="includeDebug">True to include debug data, false otherwise</param> |
1155 | | - private bool ExtractFromResources(string outputDirectory, bool includeDebug) |
| 1284 | + public bool ExtractFromResources(string outputDirectory, bool includeDebug) |
1156 | 1285 | { |
1157 | 1286 | try |
1158 | 1287 | { |
|
0 commit comments