0
|
1 // This file is a part of the Relic Tools and is copyright 2006-2018 IBBoard.
|
|
2 //
|
|
3 // The file and the library/program it is in are licensed under the GNU GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
|
|
4 using System;
|
|
5 using System.Collections;
|
|
6 using System.IO;
|
|
7 using IBBoard.Graphics;
|
|
8 using IBBoard.Relic.RelicTools.Exceptions;
|
|
9 using IBBoard.Relic.RelicTools.Collections;
|
|
10
|
|
11 namespace IBBoard.Relic.RelicTools
|
|
12 {
|
|
13 /// <summary>
|
|
14 /// Summary description for WTPFile.
|
|
15 /// </summary>
|
|
16 public class WTPFile:RelicChunkyFile
|
|
17 {
|
|
18 int width = 0;
|
|
19 int height = 0;
|
|
20 ChunkyDataATTR attr = null;
|
|
21
|
|
22 public WTPFile(string filename, ChunkyFolder folder)
|
|
23 :this(filename, new ChunkyStructureCollection(new RelicChunkyStructure(folder))){}
|
|
24
|
|
25 public WTPFile(string filename, ChunkyCollection col)
|
|
26 :this(filename, new ChunkyStructureCollection(new RelicChunkyStructure(col))){}
|
|
27
|
|
28 public WTPFile(string filename, ChunkyStructureCollection col): base(filename, col)
|
|
29 {
|
|
30 ChunkyFolder folder = (ChunkyFolder)col[0].RootChunks[0];
|
|
31 int children = folder.Children.Count;
|
|
32 ChunkyChunk chunk = null;
|
|
33 ChunkyDataINFOTPAT info = null;
|
|
34
|
|
35 for (int i = 0; i<children; i++)
|
|
36 {
|
|
37 chunk = folder.Children[i];
|
|
38
|
|
39 if (chunk is ChunkyDataINFOTPAT)
|
|
40 {
|
|
41 info = (ChunkyDataINFOTPAT)chunk;
|
|
42 width = info.Width;
|
|
43 height = info.Height;
|
|
44 }
|
|
45 }
|
|
46 }
|
|
47
|
|
48 public void SaveParts(DirectoryInfo destination)
|
|
49 {
|
|
50 string filenamebase = filename.Substring(0, filename.LastIndexOf('.'));
|
|
51
|
|
52 saveFolder((ChunkyFolder)this.ChunkyStructures[0].RootChunks[0], destination, filenamebase);
|
|
53 }
|
|
54
|
|
55 private void saveFolder(ChunkyFolder folder, DirectoryInfo destination, string filenamebase)
|
|
56 {
|
|
57 int children = folder.Children.Count;
|
|
58 ChunkyChunk chunk = null;
|
|
59
|
|
60 for (int i = 0; i<children; i++)
|
|
61 {
|
|
62 chunk = folder.Children[i];
|
|
63
|
|
64 if (chunk is ChunkyData)
|
|
65 {
|
|
66 ((ChunkyData)chunk).Save(destination, filenamebase);
|
|
67 }
|
|
68 else if (chunk is ChunkyFolder)
|
|
69 {
|
|
70 this.saveFolder((ChunkyFolder)chunk, destination, filenamebase);
|
|
71 }
|
|
72 //else something is wrong!
|
|
73
|
|
74 /*else if (chunk is ChunkyDataDATAIMAG)
|
|
75 {
|
|
76 ((ChunkyDataDATAIMAG)chunk).Save(destination, filenamebase);
|
|
77 }
|
|
78 else if (chunk is ChunkyDataPTBD)
|
|
79 {
|
|
80 ((ChunkyDataPTBD)chunk).Save(destination, filenamebase);
|
|
81 }
|
|
82 else if (chunk is ChunkyDataPTBN)
|
|
83 {
|
|
84 ((ChunkyDataPTBN)chunk).Save(destination, filenamebase);
|
|
85 }*/
|
|
86 }
|
|
87 }
|
|
88
|
|
89 public static WTPFile Create(string filepath)
|
|
90 {
|
|
91 string baseFileName = filepath.Substring(filepath.LastIndexOf(Path.DirectorySeparatorChar)+1);
|
|
92 string baseFileNameLower = baseFileName.ToLower();
|
|
93
|
|
94 string directory = "";
|
|
95
|
|
96 if (filepath.IndexOf(Path.DirectorySeparatorChar)!=-1)
|
|
97 {
|
|
98 directory = filepath.Substring(0, filepath.LastIndexOf(Path.DirectorySeparatorChar))+Path.DirectorySeparatorChar;
|
|
99 }
|
|
100
|
|
101 if (baseFileNameLower.EndsWith(".wtp"))
|
|
102 {
|
|
103 baseFileName = baseFileName.Substring(0, baseFileName.Length-4);
|
|
104 }
|
|
105 else if(baseFileNameLower.EndsWith(".tga"))
|
|
106 {
|
|
107
|
|
108 if (baseFileNameLower.EndsWith("_primary.tga"))
|
|
109 {
|
|
110 baseFileName = baseFileName.Substring(0, baseFileName.Length-12);
|
|
111 }
|
|
112 else if (baseFileNameLower.EndsWith("_secondary.tga"))
|
|
113 {
|
|
114 baseFileName = baseFileName.Substring(0, baseFileName.Length-14);
|
|
115 }
|
|
116 else if (baseFileNameLower.EndsWith("_weapon.tga")||baseFileNameLower.EndsWith("_banner.tga"))
|
|
117 {
|
|
118 baseFileName = baseFileName.Substring(0, baseFileName.Length-11);
|
|
119 }
|
|
120 else if (baseFileNameLower.EndsWith("_badge.tga"))
|
|
121 {
|
|
122 baseFileName = baseFileName.Substring(0, baseFileName.Length-10);
|
|
123 }
|
|
124 else if (baseFileNameLower.EndsWith("_eyes.tga") || baseFileNameLower.EndsWith("_trim.tga") || baseFileNameLower.EndsWith("_dirt.tga"))
|
|
125 {
|
|
126 baseFileName = baseFileName.Substring(0, baseFileName.Length-9);
|
|
127 }
|
|
128 else
|
|
129 {
|
|
130 baseFileName = baseFileName.Substring(0, baseFileName.Length-4);
|
|
131 }
|
|
132 }
|
|
133 else
|
|
134 {
|
|
135 throw new InvalidFileException("File path specified is not valid for a WTP file or one of its components");
|
|
136 }
|
|
137
|
|
138 string skinName = baseFileName.Substring(baseFileName.LastIndexOf('_')+1);//make it forwards compatible on the off-chance Relic introduce multiple textures
|
|
139
|
|
140 ChunkyDataDATA defaultData = null;
|
|
141 ChunkyData attr = null;
|
|
142 FileStream fs = null;
|
|
143 BinaryReader br = null;
|
|
144 FileInfo file = null;
|
|
145 byte [] data;
|
|
146
|
|
147 int width = 0;
|
|
148 int height = 0;
|
|
149
|
|
150 if (File.Exists(directory+baseFileName+".tga"))
|
|
151 {
|
|
152 CompilationEvent("Reading "+baseFileName+".tga");
|
|
153 file = new FileInfo(directory+baseFileName+".tga");
|
|
154 fs = file.OpenRead();
|
|
155 br = new BinaryReader(fs);
|
|
156 br.BaseStream.Seek(12,SeekOrigin.Begin);
|
|
157 width = br.ReadInt16();
|
|
158 height = br.ReadInt16();
|
|
159 br.BaseStream.Seek(0, SeekOrigin.Begin);
|
|
160 data = br.ReadBytes((int)br.BaseStream.Length);
|
|
161
|
|
162 defaultData = ChunkyDataDATAIMAG.CreateFromTGA(2, "", data);
|
|
163
|
|
164 br.Close();
|
|
165 data = new byte[]{0x0, 0x0, 0x0, 0x0, (byte)width, (byte)(width>>8), (byte)(width>>16), (byte)(width>>24), (byte)(height), (byte)(height>>8), (byte)(height>>16), (byte)(height>>24), 0x1, 0x0, 0x0, 0x0};
|
|
166 attr = new ChunkyDataUnknown("ATTR", 2, "", data);
|
|
167 }
|
|
168 else
|
|
169 {
|
|
170 throw new RelicTools.Exceptions.FileNotFoundException("WTP files must have a 32bit layer e.g. _default.tga layer");
|
|
171 }
|
|
172
|
|
173 string dirt_name = directory+baseFileName+"_Dirt.tga";
|
|
174
|
|
175 ChunkyFolder defaultFolder = new ChunkyFolder("IMAG", 1, "TOOL:"+dirt_name);
|
|
176
|
|
177 defaultFolder.Children.Add(attr);
|
|
178 defaultFolder.Children.Add(defaultData);
|
|
179
|
|
180 ChunkyDataPTLD primary = null;
|
|
181 ChunkyDataPTLD secondary = null;
|
|
182 ChunkyDataPTLD trim = null;
|
|
183 ChunkyDataPTLD weapon = null;
|
|
184 ChunkyDataPTLD eyes = null;
|
|
185 ChunkyDataPTLD dirt = null;
|
|
186 ChunkyDataPTBD badge = null;
|
|
187 ChunkyDataPTBN banner = null;
|
|
188 string primaryname = directory+baseFileName+"_Primary.tga";
|
|
189 string secondaryname = directory+baseFileName+"_Secondary.tga";
|
|
190 string trimname = directory+baseFileName+"_Trim.tga";
|
|
191 string weaponname = directory+baseFileName+"_Weapon.tga";
|
|
192 string eyesname = directory+baseFileName+"_Eyes.tga";
|
|
193 string badgename = directory+baseFileName+"_Badge.tga";
|
|
194 string bannername = directory+baseFileName+"_Banner.tga";
|
|
195
|
|
196 //int byteRead = width*height+18;//length of TGA data plus header
|
|
197
|
|
198 if (File.Exists(dirt_name))
|
|
199 {
|
|
200 CompilationEvent("Reading "+baseFileName+"_Dirt.tga");
|
|
201 file = new FileInfo(dirt_name);
|
|
202 fs = file.OpenRead();
|
|
203 br = new BinaryReader(fs);
|
|
204 br.BaseStream.Seek(0, SeekOrigin.Begin);
|
|
205
|
|
206 data = br.ReadBytes((int)br.BaseStream.Length);
|
|
207 br.Close();
|
|
208
|
|
209 dirt = ChunkyDataPTLD.CreateFromTGA(PTLD_Layers.Dirt, 1, "", data);
|
|
210 }
|
|
211 else
|
|
212 {
|
|
213 throw new RelicTools.Exceptions.FileNotFoundException("WTP Files must contain a dirt layer");
|
|
214 }
|
|
215
|
|
216 data = new byte[8];
|
|
217 data[0] = (byte)(width);
|
|
218 data[1] = (byte)(width >> 8);
|
|
219 data[2] = (byte)(width >> 16);
|
|
220 data[3] = (byte)(width >> 24);
|
|
221 data[4] = (byte)(height);
|
|
222 data[5] = (byte)(height >> 8);
|
|
223 data[6] = (byte)(height >> 16);
|
|
224 data[7] = (byte)(height >> 24);
|
|
225
|
|
226 ChunkyData info = new ChunkyDataUnknown("INFO", 1, "", data);
|
|
227
|
|
228 if (File.Exists(primaryname))
|
|
229 {
|
|
230 CompilationEvent("Reading "+baseFileName+"_Primary.tga");
|
|
231 file = new FileInfo(primaryname);
|
|
232 fs = file.OpenRead();
|
|
233 br = new BinaryReader(fs);
|
|
234 br.BaseStream.Seek(0, SeekOrigin.Begin);
|
|
235
|
|
236 data = br.ReadBytes((int)br.BaseStream.Length);
|
|
237 br.Close();
|
|
238
|
|
239 primary = ChunkyDataPTLD.CreateFromTGA(PTLD_Layers.Primary, 1, "", data);
|
|
240
|
|
241 if (primary==null)
|
|
242 {
|
|
243 CompilationEvent("Skipped "+baseFileName+"_Primary.tga - no team colour data");
|
|
244 }
|
|
245 }
|
|
246
|
|
247 if (File.Exists(secondaryname))
|
|
248 {
|
|
249 CompilationEvent("Reading "+baseFileName+"_Secondary.tga");
|
|
250 file = new FileInfo(secondaryname);
|
|
251 fs = file.OpenRead();
|
|
252 br = new BinaryReader(fs);
|
|
253 br.BaseStream.Seek(0, SeekOrigin.Begin);
|
|
254
|
|
255 data = br.ReadBytes((int)br.BaseStream.Length);
|
|
256 br.Close();
|
|
257
|
|
258 secondary = ChunkyDataPTLD.CreateFromTGA(PTLD_Layers.Secondary, 1, "", data);
|
|
259
|
|
260 if (secondary==null)
|
|
261 {
|
|
262 CompilationEvent("Skipped "+baseFileName+"_Secondary.tga - no team colour data");
|
|
263 }
|
|
264 }
|
|
265
|
|
266 if (File.Exists(trimname))
|
|
267 {
|
|
268 CompilationEvent("Reading "+baseFileName+"_Trim.tga");
|
|
269 file = new FileInfo(trimname);
|
|
270 fs = file.OpenRead();
|
|
271 br = new BinaryReader(fs);
|
|
272 br.BaseStream.Seek(0, SeekOrigin.Begin);
|
|
273
|
|
274 data = br.ReadBytes((int)br.BaseStream.Length);
|
|
275 br.Close();
|
|
276
|
|
277 trim = ChunkyDataPTLD.CreateFromTGA(PTLD_Layers.Trim, 1, "", data);
|
|
278
|
|
279 if (trim==null)
|
|
280 {
|
|
281 CompilationEvent("Skipped "+baseFileName+"_Trim.tga - no team colour data");
|
|
282 }
|
|
283 }
|
|
284
|
|
285 if (File.Exists(weaponname))
|
|
286 {
|
|
287 CompilationEvent("Reading "+baseFileName+"_Weapon.tga");
|
|
288 file = new FileInfo(weaponname);
|
|
289 fs = file.OpenRead();
|
|
290 br = new BinaryReader(fs);
|
|
291 br.BaseStream.Seek(0, SeekOrigin.Begin);
|
|
292
|
|
293 data = br.ReadBytes((int)br.BaseStream.Length);
|
|
294 br.Close();
|
|
295
|
|
296 weapon = ChunkyDataPTLD.CreateFromTGA(PTLD_Layers.Weapon, 1, "", data);
|
|
297
|
|
298 if (weapon==null)
|
|
299 {
|
|
300 CompilationEvent("Skipped "+baseFileName+"_Weapon.tga - no team colour data");
|
|
301 }
|
|
302 }
|
|
303
|
|
304 if (File.Exists(eyesname))
|
|
305 {
|
|
306 CompilationEvent("Reading "+baseFileName+"_Eyes.tga");
|
|
307 file = new FileInfo(eyesname);
|
|
308 fs = file.OpenRead();
|
|
309 br = new BinaryReader(fs);
|
|
310 br.BaseStream.Seek(0, SeekOrigin.Begin);
|
|
311
|
|
312 data = br.ReadBytes((int)br.BaseStream.Length);
|
|
313 br.Close();
|
|
314
|
|
315 eyes = ChunkyDataPTLD.CreateFromTGA(PTLD_Layers.Eyes, 1, "", data);
|
|
316
|
|
317 if (eyes==null)
|
|
318 {
|
|
319 CompilationEvent("Skipped "+baseFileName+"_Eyes.tga - no team colour data");
|
|
320 }
|
|
321 }
|
|
322
|
|
323 if (File.Exists(badgename))
|
|
324 {
|
|
325 CompilationEvent("Reading "+baseFileName+"_Badge.tga");
|
|
326 file = new FileInfo(badgename);
|
|
327 fs = file.OpenRead();
|
|
328 br = new BinaryReader(fs);
|
|
329 br.BaseStream.Seek(0, SeekOrigin.Begin);
|
|
330
|
|
331 data = br.ReadBytes((int)br.BaseStream.Length);
|
|
332 br.Close();
|
|
333
|
|
334 badge = ChunkyDataPTBD.CreateFromTGA(1, "", data);
|
|
335 }
|
|
336
|
|
337 if (File.Exists(bannername))
|
|
338 {
|
|
339 CompilationEvent("Reading "+baseFileName+"_Banner.tga");
|
|
340 file = new FileInfo(bannername);
|
|
341 fs = file.OpenRead();
|
|
342 br = new BinaryReader(fs);
|
|
343 br.BaseStream.Seek(0, SeekOrigin.Begin);
|
|
344
|
|
345 data = br.ReadBytes((int)br.BaseStream.Length);
|
|
346 br.Close();
|
|
347
|
|
348 banner = ChunkyDataPTBN.CreateFromTGA(1, "", data);
|
|
349 }
|
|
350
|
|
351 if (badge!=null && banner!=null)
|
|
352 {
|
|
353 if (((badge.Pos_x<=banner.Pos_x && (badge.Pos_x+badge.Width)>=banner.Pos_x)||
|
|
354 (badge.Pos_x<=(banner.Pos_x+banner.Width) && (badge.Pos_x+badge.Width)>=(banner.Pos_x+banner.Width)))&&
|
|
355 ((badge.Pos_y<=banner.Pos_y && (badge.Pos_y+badge.Height)>=banner.Pos_y)||
|
|
356 (badge.Pos_y<=(banner.Pos_y+banner.Height) && (badge.Pos_y+badge.Height)>=(banner.Pos_y+banner.Height))))
|
|
357 {
|
|
358 throw new InvalidFileException("Badge and banner position overlap");
|
|
359 }
|
|
360 }
|
|
361
|
|
362
|
|
363 CompilationEvent("Compiling WTP File");
|
|
364 ChunkyFolder tpat = new ChunkyFolder("TPAT", 3, skinName);
|
|
365 tpat.Children.Add(info);
|
|
366 tpat.Children.Add(primary);
|
|
367 tpat.Children.Add(secondary);
|
|
368 tpat.Children.Add(trim);
|
|
369 tpat.Children.Add(weapon);
|
|
370 tpat.Children.Add(eyes);
|
|
371 tpat.Children.Add(dirt);
|
|
372 tpat.Children.Add(defaultFolder);
|
|
373 tpat.Children.Add(banner);
|
|
374 tpat.Children.Add(badge);
|
|
375
|
|
376 return new WTPFile(baseFileName+".wtp", tpat);
|
|
377
|
|
378 //RelicChunkyFile.SaveChunky(directory+baseFileName+".wtp", tpat.GetBytes());
|
|
379 }
|
|
380
|
|
381 public byte[] CreateCompositeTGABytes(LayerCollection colours, string badgePath, string bannerPath)
|
|
382 {
|
|
383 if (colours[PTLD_Layers.Dirt].Length!=3)
|
|
384 {
|
|
385 throw new InvalidOperationException("LayerCollection 'colours' must contain RGB colour data in a 3-byte array");
|
|
386 }
|
|
387
|
|
388 FileInfo file = null;
|
|
389 BinaryReader br = null;
|
|
390 byte[] badge = new byte[64*64*4];
|
|
391 byte[] banner = new byte[64*96*4];
|
|
392 byte[] temp;
|
|
393
|
|
394 CompilationEvent("Loading badge");
|
|
395 if (File.Exists(badgePath))
|
|
396 {
|
|
397 file = new FileInfo(badgePath);
|
|
398 br = new BinaryReader(file.OpenRead());
|
|
399 temp = br.ReadBytes((int)file.Length);
|
|
400 br.Close();
|
|
401
|
|
402 if ((temp[12]+(temp[13]<<8))!=64 ||(temp[14]+(temp[15]<<8))!=64)
|
|
403 {
|
|
404 CompilationEvent("Invalid badge image - badge must be a 64x64 pixel 24/32-bit TGA image", true);
|
|
405 }
|
|
406 else if (!((temp[16]==32 || temp[16]==24) && temp[2]==2))
|
|
407 {
|
|
408 CompilationEvent("Invalid badge image - badge must be a 64x64 pixel 24/32-bit TGA image", true);
|
|
409 }
|
|
410 else
|
|
411 {
|
|
412 Buffer.BlockCopy(ImageConverter.TGAto32bitTGA(temp), 18, badge, 0, badge.Length);
|
|
413 }
|
|
414 }
|
|
415 else
|
|
416 {
|
|
417 CompilationEvent("Invalid badge path - file must exist. Using blank badge.", true);
|
|
418 }
|
|
419
|
|
420 CompilationEvent("Loading banner");
|
|
421 if (File.Exists(bannerPath))
|
|
422 {
|
|
423 file = new FileInfo(bannerPath);
|
|
424 br = new BinaryReader(file.OpenRead());
|
|
425 temp = br.ReadBytes((int)file.Length);
|
|
426 br.Close();
|
|
427
|
|
428
|
|
429 if ((temp[12]+(temp[13]<<8))!=64 ||(temp[14]+(temp[15]<<8))!=96)
|
|
430 {
|
|
431 CompilationEvent("Invalid banner image - banner must be a 64x96 pixel 24/32-bit TGA image", true);
|
|
432 }
|
|
433 else if (!((temp[16]==32 || temp[16]==24) && temp[2]==2))
|
|
434 {
|
|
435 CompilationEvent("Invalid banner image - banner must be a 64x64 pixel 24/32-bit TGA image", true);
|
|
436 }
|
|
437 else
|
|
438 {
|
|
439 Buffer.BlockCopy(ImageConverter.TGAto32bitTGA(temp), 18, banner, 0, banner.Length);
|
|
440 }
|
|
441 }
|
|
442 else
|
|
443 {
|
|
444 CompilationEvent("Invalid banner path - file must exist. Using blank badge.", true);
|
|
445 }
|
|
446
|
|
447 ChunkyFolder root = (ChunkyFolder)this.ChunkyStructures[0].RootChunks[0];
|
|
448
|
|
449 ChunkyChunk chunk = null;
|
|
450 ChunkyDataPTLD ptld = null;
|
|
451 ChunkyDataPTBN bannerLayer = null;
|
|
452 ChunkyDataPTBD badgeLayer = null;
|
|
453 ChunkyDataINFOTPAT info = (ChunkyDataINFOTPAT)root.Children[0];
|
|
454 LayerCollection layers = new LayerCollection(info.Width*info.Height);
|
|
455 byte[] mainImage = new byte[info.Width*info.Height];
|
|
456
|
|
457 int badge_bottom = int.MaxValue;
|
|
458 int badge_top = int.MaxValue;
|
|
459 int badge_left = int.MaxValue;
|
|
460 int badge_right = int.MaxValue;
|
|
461
|
|
462 int banner_bottom = int.MaxValue;
|
|
463 int banner_top = int.MaxValue;
|
|
464 int banner_left = int.MaxValue;
|
|
465 int banner_right = int.MaxValue;
|
|
466
|
|
467 CompilationEvent("Loading colour layers");
|
|
468 for (int i = 0; i<root.Children.Count; i++)
|
|
469 {
|
|
470 chunk = root.Children[i];
|
|
471
|
|
472 if (chunk is ChunkyDataPTLD)
|
|
473 {
|
|
474 ptld = (ChunkyDataPTLD)root.Children[i];
|
|
475 layers[(int)ptld.Layer] = ptld.GetColourBytes();
|
|
476 }
|
|
477 else if (chunk is ChunkyDataPTBD)
|
|
478 {
|
|
479 badgeLayer = (ChunkyDataPTBD)chunk;
|
|
480 badge_bottom = (int)badgeLayer.Pos_y;
|
|
481 badge_top = badge_bottom+(int)badgeLayer.Height;
|
|
482 badge_left = (int)badgeLayer.Pos_x;
|
|
483 badge_right = badge_left+(int)badgeLayer.Width;
|
|
484 }
|
|
485 else if (chunk is ChunkyDataPTBN)
|
|
486 {
|
|
487 bannerLayer = (ChunkyDataPTBN)chunk;
|
|
488 banner_bottom = (int)bannerLayer.Pos_y;
|
|
489 banner_top = banner_bottom+(int)bannerLayer.Height;
|
|
490 banner_left = (int)bannerLayer.Pos_x;
|
|
491 banner_right = banner_left+(int)bannerLayer.Width;
|
|
492 }
|
|
493 else if (chunk is ChunkyFolder)
|
|
494 {
|
|
495 mainImage = ((ChunkyData)((ChunkyFolder)chunk).Children[1]).GetDataBytes();
|
|
496 }
|
|
497 }
|
|
498
|
|
499 int bytePos = 0;
|
|
500 int bytePos32bit = 0;
|
|
501 int bytePosBadge = 0;
|
|
502 int bytePosBanner = 0;
|
|
503 byte[] badgeByte;
|
|
504 byte[] bannerByte;
|
|
505 double ratio;
|
|
506 double ratio2;
|
|
507 double ratio3;
|
|
508 double ratioTC;
|
|
509 double tempByte = 0;
|
|
510 double extra = 0;
|
|
511 int badge_width = 0;
|
|
512
|
|
513 if (badgeLayer != null)
|
|
514 {
|
|
515 badge_width = (int)badgeLayer.Width;
|
|
516 }
|
|
517
|
|
518 int banner_width = 0;
|
|
519
|
|
520 if (bannerLayer!=null)
|
|
521 {
|
|
522 banner_width = (int)bannerLayer.Width;
|
|
523 }
|
|
524
|
|
525 CompilationEvent("Compiling TGA");
|
|
526 for (int i = 0; i<info.Height; i++)
|
|
527 {
|
|
528 for (int j = 0; j<info.Width; j++)
|
|
529 {
|
|
530 bytePos = (i*info.Width)+j;
|
|
531 bytePos32bit = bytePos*4;
|
|
532 ratio = layers[PTLD_Layers.Dirt][bytePos]/255.0;
|
|
533
|
|
534 if (badge_left<=j && badge_right>j && badge_bottom<=i && badge_top>i)
|
|
535 {
|
|
536 bytePosBadge = ((i-badge_bottom)*badge_width+(j-badge_left))*4;
|
|
537
|
|
538 ratio2 = (badge[bytePosBadge+3]/255.0);
|
|
539 badgeByte = new byte[]{badge[bytePosBadge], badge[bytePosBadge+1], badge[bytePosBadge+2]};
|
|
540 }
|
|
541 else
|
|
542 {
|
|
543 ratio2 = 0;
|
|
544 badgeByte = new byte[3];
|
|
545 }
|
|
546
|
|
547 if (banner_left<=j && banner_right>j && banner_bottom<=i && banner_top>i)
|
|
548 {
|
|
549 bytePosBanner = ((i-banner_bottom)*banner_width+(j-banner_left))*4;
|
|
550
|
|
551 ratio3 = (banner[bytePosBanner+3]/255.0);
|
|
552 bannerByte = new byte[]{banner[bytePosBanner], banner[bytePosBanner+1], banner[bytePosBanner+2]};
|
|
553 }
|
|
554 else
|
|
555 {
|
|
556 ratio3 = 0;
|
|
557 bannerByte = new byte[3];
|
|
558 }
|
|
559
|
|
560 ratioTC = 1-ratio;
|
|
561
|
|
562 ratio2 = ratioTC*ratio2;
|
|
563 ratio3 = ratioTC*ratio3;
|
|
564 ratioTC = (ratioTC-ratio2-ratio3)*2;
|
|
565 extra = 0;
|
|
566
|
|
567 //TGA files run in reverse - little-endian ARGB, which ends up as BGRA in the file
|
|
568
|
|
569 //red
|
|
570 tempByte = (ratio*mainImage[bytePos32bit+2]) + (ratioTC)*((colours[PTLD_Layers.Primary][0]*layers[PTLD_Layers.Primary][bytePos]/255.0) +
|
|
571 (colours[PTLD_Layers.Secondary][0]*layers[PTLD_Layers.Secondary][bytePos]/255.0) +
|
|
572 (colours[PTLD_Layers.Weapon][0]*layers[PTLD_Layers.Weapon][bytePos]/255.0) +
|
|
573 (colours[PTLD_Layers.Trim][0]*layers[PTLD_Layers.Trim][bytePos]/255.0) +
|
|
574 (colours[PTLD_Layers.Eyes][0]*layers[PTLD_Layers.Eyes][bytePos]/255.0)) + ratio2*badgeByte[2] + ratio3*bannerByte[2];
|
|
575
|
|
576 if (tempByte>byte.MaxValue)
|
|
577 {
|
|
578 //CompilationEvent("Colour overflow at X:"+j.ToString()+", Y:"+i.ToString()+". Colour will show as Magenta or other bright colour with some colour schemes.", true);
|
|
579 mainImage[bytePos32bit+2] = byte.MaxValue;
|
|
580 extra = (tempByte-byte.MaxValue);
|
|
581 }
|
|
582 else
|
|
583 {
|
|
584 mainImage[bytePos32bit+2] = (byte)tempByte;
|
|
585 }
|
|
586
|
|
587 //green
|
|
588 tempByte = (ratio*mainImage[bytePos32bit+1]) + (ratioTC)*((colours[PTLD_Layers.Primary][1]*layers[PTLD_Layers.Primary][bytePos]/255.0) +
|
|
589 (colours[PTLD_Layers.Secondary][1]*layers[PTLD_Layers.Secondary][bytePos]/255.0) +
|
|
590 (colours[PTLD_Layers.Weapon][1]*layers[PTLD_Layers.Weapon][bytePos]/255.0) +
|
|
591 (colours[PTLD_Layers.Trim][1]*layers[PTLD_Layers.Trim][bytePos]/255.0) +
|
|
592 (colours[PTLD_Layers.Eyes][1]*layers[PTLD_Layers.Eyes][bytePos]/255.0)) + extra + ratio2*badgeByte[1] + ratio3*bannerByte[1];
|
|
593
|
|
594 if (tempByte>byte.MaxValue)
|
|
595 {
|
|
596 //CompilationEvent("Colour overflow at X:"+j.ToString()+", Y:"+i.ToString()+". Colour will show as Magenta or other bright colour with some colour schemes.", true);
|
|
597 mainImage[bytePos32bit+1] = byte.MaxValue;
|
|
598 extra = extra + tempByte-byte.MaxValue;
|
|
599 tempByte = mainImage[bytePos32bit+2]+extra;
|
|
600 if (tempByte>byte.MaxValue)
|
|
601 {
|
|
602 tempByte = byte.MaxValue;
|
|
603 }
|
|
604 mainImage[bytePos32bit+2] = (byte)tempByte;
|
|
605 }
|
|
606 else
|
|
607 {
|
|
608 mainImage[bytePos32bit+1] = (byte)tempByte;
|
|
609 }
|
|
610
|
|
611 //blue
|
|
612 tempByte = (ratio*mainImage[bytePos32bit]) + (ratioTC)*((colours[PTLD_Layers.Primary][2]*layers[PTLD_Layers.Primary][bytePos]/255.0) +
|
|
613 (colours[PTLD_Layers.Secondary][2]*layers[PTLD_Layers.Secondary][bytePos]/255.0) +
|
|
614 (colours[PTLD_Layers.Weapon][2]*layers[PTLD_Layers.Weapon][bytePos]/255.0) +
|
|
615 (colours[PTLD_Layers.Trim][2]*layers[PTLD_Layers.Trim][bytePos]/255.0) +
|
|
616 (colours[PTLD_Layers.Eyes][2]*layers[PTLD_Layers.Eyes][bytePos]/255.0)) + extra + ratio2*badgeByte[0] + ratio3*bannerByte[0];
|
|
617
|
|
618 if (tempByte>byte.MaxValue)
|
|
619 {
|
|
620 //CompilationEvent("Colour overflow at X:"+j.ToString()+", Y:"+i.ToString()+". Colour will show as Magenta or other bright colour with some colour schemes.", true);
|
|
621 mainImage[bytePos32bit] = byte.MaxValue;
|
|
622 extra = extra + tempByte - byte.MaxValue;
|
|
623 tempByte = mainImage[bytePos32bit+2]+extra;
|
|
624 if (tempByte>byte.MaxValue)
|
|
625 {
|
|
626 tempByte = byte.MaxValue;
|
|
627 }
|
|
628 mainImage[bytePos32bit+2] = (byte)tempByte;
|
|
629 tempByte = mainImage[bytePos32bit+1]+extra;
|
|
630 if (tempByte>byte.MaxValue)
|
|
631 {
|
|
632 tempByte = byte.MaxValue;
|
|
633 }
|
|
634 mainImage[bytePos32bit+1] = (byte)tempByte;
|
|
635 }
|
|
636 else
|
|
637 {
|
|
638 mainImage[bytePos32bit] = (byte)tempByte;
|
|
639 }
|
|
640 }
|
|
641 }
|
|
642
|
|
643 return mainImage;
|
|
644 }
|
|
645
|
|
646 public void SaveCompositeTGA(DirectoryInfo destination, LayerCollection colours, string badgePath, string bannerPath)
|
|
647 {
|
|
648 byte[] tga = this.CreateCompositeTGABytes(colours, badgePath, bannerPath);
|
|
649
|
|
650 ChunkyDataATTR attr = findFirstDataAttributes();
|
|
651
|
|
652 ChunkyDataDATAIMAG data = new ChunkyDataDATAIMAG(2, "", tga);
|
|
653 data.Attributes = attr;
|
|
654 CompilationEvent("Saving compiled TGA");
|
|
655 data.Save(destination, filename.Substring(0, filename.LastIndexOf('_')));
|
|
656 CompilationEvent("Compilation Complete\r\n");
|
|
657 }
|
|
658
|
|
659 private ChunkyDataATTR findFirstDataAttributes()
|
|
660 {
|
|
661 if (attr == null)
|
|
662 {
|
|
663 ChunkyFolder root = (ChunkyFolder)this.ChunkyStructures[0].RootChunks[0];
|
|
664
|
|
665 for (int i = 0; i<root.Children.Count; i++)
|
|
666 {
|
|
667 if (root.Children[i] is ChunkyFolder)
|
|
668 {
|
|
669 root = (ChunkyFolder)root.Children[i];
|
|
670 break;
|
|
671 }
|
|
672 }
|
|
673
|
|
674 if (root.Children[0] is ChunkyDataATTR)
|
|
675 {
|
|
676 attr = (ChunkyDataATTR)root.Children[0];
|
|
677 }
|
|
678 }
|
|
679
|
|
680 return attr;
|
|
681 }
|
|
682 }
|
|
683 } |