OSDN Git Service

[llvm-rc] Add support for all missing dialog controls
[android-x86/external-llvm.git] / tools / llvm-rc / ResourceFileWriter.cpp
1 //===-- ResourceFileWriter.cpp --------------------------------*- C++-*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===---------------------------------------------------------------------===//
9 //
10 // This implements the visitor serializing resources to a .res stream.
11 //
12 //===---------------------------------------------------------------------===//
13
14 #include "ResourceFileWriter.h"
15
16 #include "llvm/Object/WindowsResource.h"
17 #include "llvm/Support/ConvertUTF.h"
18 #include "llvm/Support/Endian.h"
19 #include "llvm/Support/EndianStream.h"
20 #include "llvm/Support/MemoryBuffer.h"
21 #include "llvm/Support/Path.h"
22 #include "llvm/Support/Process.h"
23
24 using namespace llvm::support;
25
26 // Take an expression returning llvm::Error and forward the error if it exists.
27 #define RETURN_IF_ERROR(Expr)                                                  \
28   if (auto Err = (Expr))                                                       \
29     return Err;
30
31 namespace llvm {
32 namespace rc {
33
34 // Class that employs RAII to save the current FileWriter object state
35 // and revert to it as soon as we leave the scope. This is useful if resources
36 // declare their own resource-local statements.
37 class ContextKeeper {
38   ResourceFileWriter *FileWriter;
39   ResourceFileWriter::ObjectInfo SavedInfo;
40
41 public:
42   ContextKeeper(ResourceFileWriter *V)
43       : FileWriter(V), SavedInfo(V->ObjectData) {}
44   ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; }
45 };
46
47 static Error createError(const Twine &Message,
48                          std::errc Type = std::errc::invalid_argument) {
49   return make_error<StringError>(Message, std::make_error_code(Type));
50 }
51
52 static Error checkNumberFits(uint32_t Number, size_t MaxBits,
53                              const Twine &FieldName) {
54   assert(1 <= MaxBits && MaxBits <= 32);
55   if (!(Number >> MaxBits))
56     return Error::success();
57   return createError(FieldName + " (" + Twine(Number) + ") does not fit in " +
58                          Twine(MaxBits) + " bits.",
59                      std::errc::value_too_large);
60 }
61
62 template <typename FitType>
63 static Error checkNumberFits(uint32_t Number, const Twine &FieldName) {
64   return checkNumberFits(Number, sizeof(FitType) * 8, FieldName);
65 }
66
67 // A similar function for signed integers.
68 template <typename FitType>
69 static Error checkSignedNumberFits(uint32_t Number, const Twine &FieldName,
70                                    bool CanBeNegative) {
71   int32_t SignedNum = Number;
72   if (SignedNum < std::numeric_limits<FitType>::min() ||
73       SignedNum > std::numeric_limits<FitType>::max())
74     return createError(FieldName + " (" + Twine(SignedNum) +
75                            ") does not fit in " + Twine(sizeof(FitType) * 8) +
76                            "-bit signed integer type.",
77                        std::errc::value_too_large);
78
79   if (!CanBeNegative && SignedNum < 0)
80     return createError(FieldName + " (" + Twine(SignedNum) +
81                        ") cannot be negative.");
82
83   return Error::success();
84 }
85
86 static Error checkRCInt(RCInt Number, const Twine &FieldName) {
87   if (Number.isLong())
88     return Error::success();
89   return checkNumberFits<uint16_t>(Number, FieldName);
90 }
91
92 static Error checkIntOrString(IntOrString Value, const Twine &FieldName) {
93   if (!Value.isInt())
94     return Error::success();
95   return checkNumberFits<uint16_t>(Value.getInt(), FieldName);
96 }
97
98 static bool stripQuotes(StringRef &Str, bool &IsLongString) {
99   if (!Str.contains('"'))
100     return false;
101
102   // Just take the contents of the string, checking if it's been marked long.
103   IsLongString = Str.startswith_lower("L");
104   if (IsLongString)
105     Str = Str.drop_front();
106
107   bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\"");
108   (void)StripSuccess;
109   assert(StripSuccess && "Strings should be enclosed in quotes.");
110   return true;
111 }
112
113 static UTF16 cp1252ToUnicode(unsigned char C) {
114   static const UTF16 Map80[] = {
115       0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
116       0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
117       0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
118       0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178,
119   };
120   if (C >= 0x80 && C <= 0x9F)
121     return Map80[C - 0x80];
122   return C;
123 }
124
125 // Describes a way to handle '\0' characters when processing the string.
126 // rc.exe tool sometimes behaves in a weird way in postprocessing.
127 // If the string to be output is equivalent to a C-string (e.g. in MENU
128 // titles), string is (predictably) truncated after first 0-byte.
129 // When outputting a string table, the behavior is equivalent to appending
130 // '\0\0' at the end of the string, and then stripping the string
131 // before the first '\0\0' occurrence.
132 // Finally, when handling strings in user-defined resources, 0-bytes
133 // aren't stripped, nor do they terminate the string.
134
135 enum class NullHandlingMethod {
136   UserResource,   // Don't terminate string on '\0'.
137   CutAtNull,      // Terminate string on '\0'.
138   CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'.
139 };
140
141 // Parses an identifier or string and returns a processed version of it:
142 //   * String the string boundary quotes.
143 //   * Squash "" to a single ".
144 //   * Replace the escape sequences with their processed version.
145 // For identifiers, this is no-op.
146 static Error processString(StringRef Str, NullHandlingMethod NullHandler,
147                            bool &IsLongString, SmallVectorImpl<UTF16> &Result,
148                            int CodePage) {
149   bool IsString = stripQuotes(Str, IsLongString);
150   SmallVector<UTF16, 128> Chars;
151
152   // Convert the input bytes according to the chosen codepage.
153   if (CodePage == CpUtf8) {
154     convertUTF8ToUTF16String(Str, Chars);
155   } else if (CodePage == CpWin1252) {
156     for (char C : Str)
157       Chars.push_back(cp1252ToUnicode((unsigned char)C));
158   } else {
159     // For other, unknown codepages, only allow plain ASCII input.
160     for (char C : Str) {
161       if ((unsigned char)C > 0x7F)
162         return createError("Non-ASCII 8-bit codepoint (" + Twine(C) +
163                            ") can't be interpreted in the current codepage");
164       Chars.push_back((unsigned char)C);
165     }
166   }
167
168   if (!IsString) {
169     // It's an identifier if it's not a string. Make all characters uppercase.
170     for (UTF16 &Ch : Chars) {
171       assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII");
172       Ch = toupper(Ch);
173     }
174     Result.swap(Chars);
175     return Error::success();
176   }
177   Result.reserve(Chars.size());
178   size_t Pos = 0;
179
180   auto AddRes = [&Result, NullHandler, IsLongString](UTF16 Char) -> Error {
181     if (!IsLongString) {
182       if (NullHandler == NullHandlingMethod::UserResource) {
183         // Narrow strings in user-defined resources are *not* output in
184         // UTF-16 format.
185         if (Char > 0xFF)
186           return createError("Non-8-bit codepoint (" + Twine(Char) +
187                              ") can't occur in a user-defined narrow string");
188       }
189     }
190
191     Result.push_back(Char);
192     return Error::success();
193   };
194   auto AddEscapedChar = [AddRes, IsLongString, CodePage](UTF16 Char) -> Error {
195     if (!IsLongString) {
196       // Escaped chars in narrow strings have to be interpreted according to
197       // the chosen code page.
198       if (Char > 0xFF)
199         return createError("Non-8-bit escaped char (" + Twine(Char) +
200                            ") can't occur in narrow string");
201       if (CodePage == CpUtf8) {
202         if (Char >= 0x80)
203           return createError("Unable to interpret single byte (" + Twine(Char) +
204                              ") as UTF-8");
205       } else if (CodePage == CpWin1252) {
206         Char = cp1252ToUnicode(Char);
207       } else {
208         // Unknown/unsupported codepage, only allow ASCII input.
209         if (Char > 0x7F)
210           return createError("Non-ASCII 8-bit codepoint (" + Twine(Char) +
211                              ") can't "
212                              "occur in a non-Unicode string");
213       }
214     }
215
216     return AddRes(Char);
217   };
218
219   while (Pos < Chars.size()) {
220     UTF16 CurChar = Chars[Pos];
221     ++Pos;
222
223     // Strip double "".
224     if (CurChar == '"') {
225       if (Pos == Chars.size() || Chars[Pos] != '"')
226         return createError("Expected \"\"");
227       ++Pos;
228       RETURN_IF_ERROR(AddRes('"'));
229       continue;
230     }
231
232     if (CurChar == '\\') {
233       UTF16 TypeChar = Chars[Pos];
234       ++Pos;
235
236       if (TypeChar == 'x' || TypeChar == 'X') {
237         // Read a hex number. Max number of characters to read differs between
238         // narrow and wide strings.
239         UTF16 ReadInt = 0;
240         size_t RemainingChars = IsLongString ? 4 : 2;
241         // We don't want to read non-ASCII hex digits. std:: functions past
242         // 0xFF invoke UB.
243         //
244         // FIXME: actually, Microsoft version probably doesn't check this
245         // condition and uses their Unicode version of 'isxdigit'. However,
246         // there are some hex-digit Unicode character outside of ASCII, and
247         // some of these are actually accepted by rc.exe, the notable example
248         // being fullwidth forms (U+FF10..U+FF19 etc.) These can be written
249         // instead of ASCII digits in \x... escape sequence and get accepted.
250         // However, the resulting hexcodes seem totally unpredictable.
251         // We think it's infeasible to try to reproduce this behavior, nor to
252         // put effort in order to detect it.
253         while (RemainingChars && Pos < Chars.size() && Chars[Pos] < 0x80) {
254           if (!isxdigit(Chars[Pos]))
255             break;
256           char Digit = tolower(Chars[Pos]);
257           ++Pos;
258
259           ReadInt <<= 4;
260           if (isdigit(Digit))
261             ReadInt |= Digit - '0';
262           else
263             ReadInt |= Digit - 'a' + 10;
264
265           --RemainingChars;
266         }
267
268         RETURN_IF_ERROR(AddEscapedChar(ReadInt));
269         continue;
270       }
271
272       if (TypeChar >= '0' && TypeChar < '8') {
273         // Read an octal number. Note that we've already read the first digit.
274         UTF16 ReadInt = TypeChar - '0';
275         size_t RemainingChars = IsLongString ? 6 : 2;
276
277         while (RemainingChars && Pos < Chars.size() && Chars[Pos] >= '0' &&
278                Chars[Pos] < '8') {
279           ReadInt <<= 3;
280           ReadInt |= Chars[Pos] - '0';
281           --RemainingChars;
282           ++Pos;
283         }
284
285         RETURN_IF_ERROR(AddEscapedChar(ReadInt));
286
287         continue;
288       }
289
290       switch (TypeChar) {
291       case 'A':
292       case 'a':
293         // Windows '\a' translates into '\b' (Backspace).
294         RETURN_IF_ERROR(AddRes('\b'));
295         break;
296
297       case 'n': // Somehow, RC doesn't recognize '\N' and '\R'.
298         RETURN_IF_ERROR(AddRes('\n'));
299         break;
300
301       case 'r':
302         RETURN_IF_ERROR(AddRes('\r'));
303         break;
304
305       case 'T':
306       case 't':
307         RETURN_IF_ERROR(AddRes('\t'));
308         break;
309
310       case '\\':
311         RETURN_IF_ERROR(AddRes('\\'));
312         break;
313
314       case '"':
315         // RC accepts \" only if another " comes afterwards; then, \"" means
316         // a single ".
317         if (Pos == Chars.size() || Chars[Pos] != '"')
318           return createError("Expected \\\"\"");
319         ++Pos;
320         RETURN_IF_ERROR(AddRes('"'));
321         break;
322
323       default:
324         // If TypeChar means nothing, \ is should be output to stdout with
325         // following char. However, rc.exe consumes these characters when
326         // dealing with wide strings.
327         if (!IsLongString) {
328           RETURN_IF_ERROR(AddRes('\\'));
329           RETURN_IF_ERROR(AddRes(TypeChar));
330         }
331         break;
332       }
333
334       continue;
335     }
336
337     // If nothing interesting happens, just output the character.
338     RETURN_IF_ERROR(AddRes(CurChar));
339   }
340
341   switch (NullHandler) {
342   case NullHandlingMethod::CutAtNull:
343     for (size_t Pos = 0; Pos < Result.size(); ++Pos)
344       if (Result[Pos] == '\0')
345         Result.resize(Pos);
346     break;
347
348   case NullHandlingMethod::CutAtDoubleNull:
349     for (size_t Pos = 0; Pos + 1 < Result.size(); ++Pos)
350       if (Result[Pos] == '\0' && Result[Pos + 1] == '\0')
351         Result.resize(Pos);
352     if (Result.size() > 0 && Result.back() == '\0')
353       Result.pop_back();
354     break;
355
356   case NullHandlingMethod::UserResource:
357     break;
358   }
359
360   return Error::success();
361 }
362
363 uint64_t ResourceFileWriter::writeObject(const ArrayRef<uint8_t> Data) {
364   uint64_t Result = tell();
365   FS->write((const char *)Data.begin(), Data.size());
366   return Result;
367 }
368
369 Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) {
370   SmallVector<UTF16, 128> ProcessedString;
371   bool IsLongString;
372   RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull,
373                                 IsLongString, ProcessedString,
374                                 Params.CodePage));
375   for (auto Ch : ProcessedString)
376     writeInt<uint16_t>(Ch);
377   if (WriteTerminator)
378     writeInt<uint16_t>(0);
379   return Error::success();
380 }
381
382 Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) {
383   return writeIntOrString(Ident);
384 }
385
386 Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) {
387   if (!Value.isInt())
388     return writeCString(Value.getString());
389
390   writeInt<uint16_t>(0xFFFF);
391   writeInt<uint16_t>(Value.getInt());
392   return Error::success();
393 }
394
395 void ResourceFileWriter::writeRCInt(RCInt Value) {
396   if (Value.isLong())
397     writeInt<uint32_t>(Value);
398   else
399     writeInt<uint16_t>(Value);
400 }
401
402 Error ResourceFileWriter::appendFile(StringRef Filename) {
403   bool IsLong;
404   stripQuotes(Filename, IsLong);
405
406   auto File = loadFile(Filename);
407   if (!File)
408     return File.takeError();
409
410   *FS << (*File)->getBuffer();
411   return Error::success();
412 }
413
414 void ResourceFileWriter::padStream(uint64_t Length) {
415   assert(Length > 0);
416   uint64_t Location = tell();
417   Location %= Length;
418   uint64_t Pad = (Length - Location) % Length;
419   for (uint64_t i = 0; i < Pad; ++i)
420     writeInt<uint8_t>(0);
421 }
422
423 Error ResourceFileWriter::handleError(Error Err, const RCResource *Res) {
424   if (Err)
425     return joinErrors(createError("Error in " + Res->getResourceTypeName() +
426                                   " statement (ID " + Twine(Res->ResName) +
427                                   "): "),
428                       std::move(Err));
429   return Error::success();
430 }
431
432 Error ResourceFileWriter::visitNullResource(const RCResource *Res) {
433   return writeResource(Res, &ResourceFileWriter::writeNullBody);
434 }
435
436 Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) {
437   return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody);
438 }
439
440 Error ResourceFileWriter::visitBitmapResource(const RCResource *Res) {
441   return writeResource(Res, &ResourceFileWriter::writeBitmapBody);
442 }
443
444 Error ResourceFileWriter::visitCursorResource(const RCResource *Res) {
445   return handleError(visitIconOrCursorResource(Res), Res);
446 }
447
448 Error ResourceFileWriter::visitDialogResource(const RCResource *Res) {
449   return writeResource(Res, &ResourceFileWriter::writeDialogBody);
450 }
451
452 Error ResourceFileWriter::visitIconResource(const RCResource *Res) {
453   return handleError(visitIconOrCursorResource(Res), Res);
454 }
455
456 Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) {
457   ObjectData.Caption = Stmt->Value;
458   return Error::success();
459 }
460
461 Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
462   return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
463 }
464
465 Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
466   return writeResource(Res, &ResourceFileWriter::writeMenuBody);
467 }
468
469 Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) {
470   const auto *Res = cast<StringTableResource>(Base);
471
472   ContextKeeper RAII(this);
473   RETURN_IF_ERROR(Res->applyStmts(this));
474
475   for (auto &String : Res->Table) {
476     RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID"));
477     uint16_t BundleID = String.first >> 4;
478     StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo);
479     auto &BundleData = StringTableData.BundleData;
480     auto Iter = BundleData.find(Key);
481
482     if (Iter == BundleData.end()) {
483       // Need to create a bundle.
484       StringTableData.BundleList.push_back(Key);
485       auto EmplaceResult =
486           BundleData.emplace(Key, StringTableInfo::Bundle(ObjectData));
487       assert(EmplaceResult.second && "Could not create a bundle");
488       Iter = EmplaceResult.first;
489     }
490
491     RETURN_IF_ERROR(
492         insertStringIntoBundle(Iter->second, String.first, String.second));
493   }
494
495   return Error::success();
496 }
497
498 Error ResourceFileWriter::visitUserDefinedResource(const RCResource *Res) {
499   return writeResource(Res, &ResourceFileWriter::writeUserDefinedBody);
500 }
501
502 Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) {
503   return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody);
504 }
505
506 Error ResourceFileWriter::visitCharacteristicsStmt(
507     const CharacteristicsStmt *Stmt) {
508   ObjectData.Characteristics = Stmt->Value;
509   return Error::success();
510 }
511
512 Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) {
513   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size"));
514   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight"));
515   RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset"));
516   ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic,
517                             Stmt->Charset};
518   ObjectData.Font.emplace(Font);
519   return Error::success();
520 }
521
522 Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
523   RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID"));
524   RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"));
525   ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10);
526   return Error::success();
527 }
528
529 Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) {
530   ObjectData.Style = Stmt->Value;
531   return Error::success();
532 }
533
534 Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) {
535   ObjectData.VersionInfo = Stmt->Value;
536   return Error::success();
537 }
538
539 Error ResourceFileWriter::writeResource(
540     const RCResource *Res,
541     Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) {
542   // We don't know the sizes yet.
543   object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)};
544   uint64_t HeaderLoc = writeObject(HeaderPrefix);
545
546   auto ResType = Res->getResourceType();
547   RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type"));
548   RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID"));
549   RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res));
550   RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res));
551
552   // Apply the resource-local optional statements.
553   ContextKeeper RAII(this);
554   RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res));
555
556   padStream(sizeof(uint32_t));
557   object::WinResHeaderSuffix HeaderSuffix{
558       ulittle32_t(0), // DataVersion; seems to always be 0
559       ulittle16_t(Res->getMemoryFlags()), ulittle16_t(ObjectData.LanguageInfo),
560       ulittle32_t(ObjectData.VersionInfo),
561       ulittle32_t(ObjectData.Characteristics)};
562   writeObject(HeaderSuffix);
563
564   uint64_t DataLoc = tell();
565   RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res));
566   // RETURN_IF_ERROR(handleError(dumpResource(Ctx)));
567
568   // Update the sizes.
569   HeaderPrefix.DataSize = tell() - DataLoc;
570   HeaderPrefix.HeaderSize = DataLoc - HeaderLoc;
571   writeObjectAt(HeaderPrefix, HeaderLoc);
572   padStream(sizeof(uint32_t));
573
574   return Error::success();
575 }
576
577 // --- NullResource helpers. --- //
578
579 Error ResourceFileWriter::writeNullBody(const RCResource *) {
580   return Error::success();
581 }
582
583 // --- AcceleratorsResource helpers. --- //
584
585 Error ResourceFileWriter::writeSingleAccelerator(
586     const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) {
587   using Accelerator = AcceleratorsResource::Accelerator;
588   using Opt = Accelerator::Options;
589
590   struct AccelTableEntry {
591     ulittle16_t Flags;
592     ulittle16_t ANSICode;
593     ulittle16_t Id;
594     uint16_t Padding;
595   } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0};
596
597   bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY;
598
599   // Remove ASCII flags (which doesn't occur in .res files).
600   Entry.Flags = Obj.Flags & ~Opt::ASCII;
601
602   if (IsLastItem)
603     Entry.Flags |= 0x80;
604
605   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"));
606   Entry.Id = ulittle16_t(Obj.Id);
607
608   auto createAccError = [&Obj](const char *Msg) {
609     return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg);
610   };
611
612   if (IsASCII && IsVirtKey)
613     return createAccError("Accelerator can't be both ASCII and VIRTKEY");
614
615   if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL)))
616     return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY"
617                           " accelerators");
618
619   if (Obj.Event.isInt()) {
620     if (!IsASCII && !IsVirtKey)
621       return createAccError(
622           "Accelerator with a numeric event must be either ASCII"
623           " or VIRTKEY");
624
625     uint32_t EventVal = Obj.Event.getInt();
626     RETURN_IF_ERROR(
627         checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"));
628     Entry.ANSICode = ulittle16_t(EventVal);
629     writeObject(Entry);
630     return Error::success();
631   }
632
633   StringRef Str = Obj.Event.getString();
634   bool IsWide;
635   stripQuotes(Str, IsWide);
636
637   if (Str.size() == 0 || Str.size() > 2)
638     return createAccError(
639         "Accelerator string events should have length 1 or 2");
640
641   if (Str[0] == '^') {
642     if (Str.size() == 1)
643       return createAccError("No character following '^' in accelerator event");
644     if (IsVirtKey)
645       return createAccError(
646           "VIRTKEY accelerator events can't be preceded by '^'");
647
648     char Ch = Str[1];
649     if (Ch >= 'a' && Ch <= 'z')
650       Entry.ANSICode = ulittle16_t(Ch - 'a' + 1);
651     else if (Ch >= 'A' && Ch <= 'Z')
652       Entry.ANSICode = ulittle16_t(Ch - 'A' + 1);
653     else
654       return createAccError("Control character accelerator event should be"
655                             " alphabetic");
656
657     writeObject(Entry);
658     return Error::success();
659   }
660
661   if (Str.size() == 2)
662     return createAccError("Event string should be one-character, possibly"
663                           " preceded by '^'");
664
665   uint8_t EventCh = Str[0];
666   // The original tool just warns in this situation. We chose to fail.
667   if (IsVirtKey && !isalnum(EventCh))
668     return createAccError("Non-alphanumeric characters cannot describe virtual"
669                           " keys");
670   if (EventCh > 0x7F)
671     return createAccError("Non-ASCII description of accelerator");
672
673   if (IsVirtKey)
674     EventCh = toupper(EventCh);
675   Entry.ANSICode = ulittle16_t(EventCh);
676   writeObject(Entry);
677   return Error::success();
678 }
679
680 Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
681   auto *Res = cast<AcceleratorsResource>(Base);
682   size_t AcceleratorId = 0;
683   for (auto &Acc : Res->Accelerators) {
684     ++AcceleratorId;
685     RETURN_IF_ERROR(
686         writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size()));
687   }
688   return Error::success();
689 }
690
691 // --- BitmapResource helpers. --- //
692
693 Error ResourceFileWriter::writeBitmapBody(const RCResource *Base) {
694   StringRef Filename = cast<BitmapResource>(Base)->BitmapLoc;
695   bool IsLong;
696   stripQuotes(Filename, IsLong);
697
698   auto File = loadFile(Filename);
699   if (!File)
700     return File.takeError();
701
702   StringRef Buffer = (*File)->getBuffer();
703
704   // Skip the 14 byte BITMAPFILEHEADER.
705   constexpr size_t BITMAPFILEHEADER_size = 14;
706   if (Buffer.size() < BITMAPFILEHEADER_size || Buffer[0] != 'B' ||
707       Buffer[1] != 'M')
708     return createError("Incorrect bitmap file.");
709
710   *FS << Buffer.substr(BITMAPFILEHEADER_size);
711   return Error::success();
712 }
713
714 // --- CursorResource and IconResource helpers. --- //
715
716 // ICONRESDIR structure. Describes a single icon in resouce group.
717 //
718 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx
719 struct IconResDir {
720   uint8_t Width;
721   uint8_t Height;
722   uint8_t ColorCount;
723   uint8_t Reserved;
724 };
725
726 // CURSORDIR structure. Describes a single cursor in resource group.
727 //
728 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx
729 struct CursorDir {
730   ulittle16_t Width;
731   ulittle16_t Height;
732 };
733
734 // RESDIRENTRY structure, stripped from the last item. Stripping made
735 // for compatibility with RESDIR.
736 //
737 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx
738 struct ResourceDirEntryStart {
739   union {
740     CursorDir Cursor; // Used in CURSOR resources.
741     IconResDir Icon;  // Used in .ico and .cur files, and ICON resources.
742   };
743   ulittle16_t Planes;   // HotspotX (.cur files but not CURSOR resource).
744   ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource).
745   ulittle32_t Size;
746   // ulittle32_t ImageOffset;  // Offset to image data (ICONDIRENTRY only).
747   // ulittle16_t IconID;       // Resource icon ID (RESDIR only).
748 };
749
750 // BITMAPINFOHEADER structure. Describes basic information about the bitmap
751 // being read.
752 //
753 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
754 struct BitmapInfoHeader {
755   ulittle32_t Size;
756   ulittle32_t Width;
757   ulittle32_t Height;
758   ulittle16_t Planes;
759   ulittle16_t BitCount;
760   ulittle32_t Compression;
761   ulittle32_t SizeImage;
762   ulittle32_t XPelsPerMeter;
763   ulittle32_t YPelsPerMeter;
764   ulittle32_t ClrUsed;
765   ulittle32_t ClrImportant;
766 };
767
768 // Group icon directory header. Called ICONDIR in .ico/.cur files and
769 // NEWHEADER in .res files.
770 //
771 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx
772 struct GroupIconDir {
773   ulittle16_t Reserved; // Always 0.
774   ulittle16_t ResType;  // 1 for icons, 2 for cursors.
775   ulittle16_t ResCount; // Number of items.
776 };
777
778 enum class IconCursorGroupType { Icon, Cursor };
779
780 class SingleIconCursorResource : public RCResource {
781 public:
782   IconCursorGroupType Type;
783   const ResourceDirEntryStart &Header;
784   ArrayRef<uint8_t> Image;
785
786   SingleIconCursorResource(IconCursorGroupType ResourceType,
787                            const ResourceDirEntryStart &HeaderEntry,
788                            ArrayRef<uint8_t> ImageData)
789       : Type(ResourceType), Header(HeaderEntry), Image(ImageData) {}
790
791   Twine getResourceTypeName() const override { return "Icon/cursor image"; }
792   IntOrString getResourceType() const override {
793     return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor;
794   }
795   uint16_t getMemoryFlags() const override {
796     return MfDiscardable | MfMoveable;
797   }
798   ResourceKind getKind() const override { return RkSingleCursorOrIconRes; }
799   static bool classof(const RCResource *Res) {
800     return Res->getKind() == RkSingleCursorOrIconRes;
801   }
802 };
803
804 class IconCursorGroupResource : public RCResource {
805 public:
806   IconCursorGroupType Type;
807   GroupIconDir Header;
808   std::vector<ResourceDirEntryStart> ItemEntries;
809
810   IconCursorGroupResource(IconCursorGroupType ResourceType,
811                           const GroupIconDir &HeaderData,
812                           std::vector<ResourceDirEntryStart> &&Entries)
813       : Type(ResourceType), Header(HeaderData),
814         ItemEntries(std::move(Entries)) {}
815
816   Twine getResourceTypeName() const override { return "Icon/cursor group"; }
817   IntOrString getResourceType() const override {
818     return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup;
819   }
820   ResourceKind getKind() const override { return RkCursorOrIconGroupRes; }
821   static bool classof(const RCResource *Res) {
822     return Res->getKind() == RkCursorOrIconGroupRes;
823   }
824 };
825
826 Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) {
827   auto *Res = cast<SingleIconCursorResource>(Base);
828   if (Res->Type == IconCursorGroupType::Cursor) {
829     // In case of cursors, two WORDS are appended to the beginning
830     // of the resource: HotspotX (Planes in RESDIRENTRY),
831     // and HotspotY (BitCount).
832     //
833     // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx
834     //  (Remarks section).
835     writeObject(Res->Header.Planes);
836     writeObject(Res->Header.BitCount);
837   }
838
839   writeObject(Res->Image);
840   return Error::success();
841 }
842
843 Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) {
844   auto *Res = cast<IconCursorGroupResource>(Base);
845   writeObject(Res->Header);
846   for (auto Item : Res->ItemEntries) {
847     writeObject(Item);
848     writeInt(IconCursorID++);
849   }
850   return Error::success();
851 }
852
853 Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) {
854   return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody);
855 }
856
857 Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) {
858   return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody);
859 }
860
861 Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) {
862   IconCursorGroupType Type;
863   StringRef FileStr;
864   IntOrString ResName = Base->ResName;
865
866   if (auto *IconRes = dyn_cast<IconResource>(Base)) {
867     FileStr = IconRes->IconLoc;
868     Type = IconCursorGroupType::Icon;
869   } else {
870     auto *CursorRes = dyn_cast<CursorResource>(Base);
871     FileStr = CursorRes->CursorLoc;
872     Type = IconCursorGroupType::Cursor;
873   }
874
875   bool IsLong;
876   stripQuotes(FileStr, IsLong);
877   auto File = loadFile(FileStr);
878
879   if (!File)
880     return File.takeError();
881
882   BinaryStreamReader Reader((*File)->getBuffer(), support::little);
883
884   // Read the file headers.
885   //   - At the beginning, ICONDIR/NEWHEADER header.
886   //   - Then, a number of RESDIR headers follow. These contain offsets
887   //       to data.
888   const GroupIconDir *Header;
889
890   RETURN_IF_ERROR(Reader.readObject(Header));
891   if (Header->Reserved != 0)
892     return createError("Incorrect icon/cursor Reserved field; should be 0.");
893   uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2;
894   if (Header->ResType != NeededType)
895     return createError("Incorrect icon/cursor ResType field; should be " +
896                        Twine(NeededType) + ".");
897
898   uint16_t NumItems = Header->ResCount;
899
900   // Read single ico/cur headers.
901   std::vector<ResourceDirEntryStart> ItemEntries;
902   ItemEntries.reserve(NumItems);
903   std::vector<uint32_t> ItemOffsets(NumItems);
904   for (size_t ID = 0; ID < NumItems; ++ID) {
905     const ResourceDirEntryStart *Object;
906     RETURN_IF_ERROR(Reader.readObject(Object));
907     ItemEntries.push_back(*Object);
908     RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID]));
909   }
910
911   // Now write each icon/cursors one by one. At first, all the contents
912   // without ICO/CUR header. This is described by SingleIconCursorResource.
913   for (size_t ID = 0; ID < NumItems; ++ID) {
914     // Load the fragment of file.
915     Reader.setOffset(ItemOffsets[ID]);
916     ArrayRef<uint8_t> Image;
917     RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size));
918     SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image);
919     SingleRes.setName(IconCursorID + ID);
920     RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes));
921   }
922
923   // Now, write all the headers concatenated into a separate resource.
924   for (size_t ID = 0; ID < NumItems; ++ID) {
925     if (Type == IconCursorGroupType::Icon) {
926       // rc.exe seems to always set NumPlanes to 1. No idea why it happens.
927       ItemEntries[ID].Planes = 1;
928       continue;
929     }
930
931     // We need to rewrite the cursor headers.
932     const auto &OldHeader = ItemEntries[ID];
933     ResourceDirEntryStart NewHeader;
934     NewHeader.Cursor.Width = OldHeader.Icon.Width;
935     // Each cursor in fact stores two bitmaps, one under another.
936     // Height provided in cursor definition describes the height of the
937     // cursor, whereas the value existing in resource definition describes
938     // the height of the bitmap. Therefore, we need to double this height.
939     NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
940
941     // Now, we actually need to read the bitmap header to find
942     // the number of planes and the number of bits per pixel.
943     Reader.setOffset(ItemOffsets[ID]);
944     const BitmapInfoHeader *BMPHeader;
945     RETURN_IF_ERROR(Reader.readObject(BMPHeader));
946     NewHeader.Planes = BMPHeader->Planes;
947     NewHeader.BitCount = BMPHeader->BitCount;
948
949     // Two WORDs were written at the beginning of the resource (hotspot
950     // location). This is reflected in Size field.
951     NewHeader.Size = OldHeader.Size + 2 * sizeof(uint16_t);
952
953     ItemEntries[ID] = NewHeader;
954   }
955
956   IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries));
957   HeaderRes.setName(ResName);
958   RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes));
959
960   return Error::success();
961 }
962
963 // --- DialogResource helpers. --- //
964
965 Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
966                                                    bool IsExtended) {
967   // Each control should be aligned to DWORD.
968   padStream(sizeof(uint32_t));
969
970   auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
971   uint32_t CtlStyle = TypeInfo.Style | Ctl.Style.getValueOr(0);
972   uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
973
974   // DIALOG(EX) item header prefix.
975   if (!IsExtended) {
976     struct {
977       ulittle32_t Style;
978       ulittle32_t ExtStyle;
979     } Prefix{ulittle32_t(CtlStyle), ulittle32_t(CtlExtStyle)};
980     writeObject(Prefix);
981   } else {
982     struct {
983       ulittle32_t HelpID;
984       ulittle32_t ExtStyle;
985       ulittle32_t Style;
986     } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
987              ulittle32_t(CtlStyle)};
988     writeObject(Prefix);
989   }
990
991   // Common fixed-length part.
992   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
993       Ctl.X, "Dialog control x-coordinate", true));
994   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
995       Ctl.Y, "Dialog control y-coordinate", true));
996   RETURN_IF_ERROR(
997       checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false));
998   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
999       Ctl.Height, "Dialog control height", false));
1000   struct {
1001     ulittle16_t X;
1002     ulittle16_t Y;
1003     ulittle16_t Width;
1004     ulittle16_t Height;
1005   } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
1006            ulittle16_t(Ctl.Height)};
1007   writeObject(Middle);
1008
1009   // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
1010   if (!IsExtended) {
1011     RETURN_IF_ERROR(checkNumberFits<uint16_t>(
1012         Ctl.ID, "Control ID in simple DIALOG resource"));
1013     writeInt<uint16_t>(Ctl.ID);
1014   } else {
1015     writeInt<uint32_t>(Ctl.ID);
1016   }
1017
1018   // Window class - either 0xFFFF + 16-bit integer or a string.
1019   RETURN_IF_ERROR(writeIntOrString(Ctl.Class));
1020
1021   // Element caption/reference ID. ID is preceded by 0xFFFF.
1022   RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"));
1023   RETURN_IF_ERROR(writeIntOrString(Ctl.Title));
1024
1025   // # bytes of extra creation data count. Don't pass any.
1026   writeInt<uint16_t>(0);
1027
1028   return Error::success();
1029 }
1030
1031 Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
1032   auto *Res = cast<DialogResource>(Base);
1033
1034   // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
1035   const uint32_t DefaultStyle = 0x80880000;
1036   const uint32_t StyleFontFlag = 0x40;
1037   const uint32_t StyleCaptionFlag = 0x00C00000;
1038
1039   uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle);
1040   if (ObjectData.Font)
1041     UsedStyle |= StyleFontFlag;
1042   else
1043     UsedStyle &= ~StyleFontFlag;
1044
1045   // Actually, in case of empty (but existent) caption, the examined field
1046   // is equal to "\"\"". That's why empty captions are still noticed.
1047   if (ObjectData.Caption != "")
1048     UsedStyle |= StyleCaptionFlag;
1049
1050   const uint16_t DialogExMagic = 0xFFFF;
1051
1052   // Write DIALOG(EX) header prefix. These are pretty different.
1053   if (!Res->IsExtended) {
1054     // We cannot let the higher word of DefaultStyle be equal to 0xFFFF.
1055     // In such a case, whole object (in .res file) is equivalent to a
1056     // DIALOGEX. It might lead to access violation/segmentation fault in
1057     // resource readers. For example,
1058     //   1 DIALOG 0, 0, 0, 65432
1059     //   STYLE 0xFFFF0001 {}
1060     // would be compiled to a DIALOGEX with 65432 controls.
1061     if ((UsedStyle >> 16) == DialogExMagic)
1062       return createError("16 higher bits of DIALOG resource style cannot be"
1063                          " equal to 0xFFFF");
1064
1065     struct {
1066       ulittle32_t Style;
1067       ulittle32_t ExtStyle;
1068     } Prefix{ulittle32_t(UsedStyle),
1069              ulittle32_t(0)}; // As of now, we don't keep EXSTYLE.
1070
1071     writeObject(Prefix);
1072   } else {
1073     struct {
1074       ulittle16_t Version;
1075       ulittle16_t Magic;
1076       ulittle32_t HelpID;
1077       ulittle32_t ExtStyle;
1078       ulittle32_t Style;
1079     } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
1080              ulittle32_t(Res->HelpID), ulittle32_t(0), ulittle32_t(UsedStyle)};
1081
1082     writeObject(Prefix);
1083   }
1084
1085   // Now, a common part. First, fixed-length fields.
1086   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),
1087                                             "Number of dialog controls"));
1088   RETURN_IF_ERROR(
1089       checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true));
1090   RETURN_IF_ERROR(
1091       checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true));
1092   RETURN_IF_ERROR(
1093       checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false));
1094   RETURN_IF_ERROR(
1095       checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false));
1096   struct {
1097     ulittle16_t Count;
1098     ulittle16_t PosX;
1099     ulittle16_t PosY;
1100     ulittle16_t DialogWidth;
1101     ulittle16_t DialogHeight;
1102   } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
1103            ulittle16_t(Res->Y), ulittle16_t(Res->Width),
1104            ulittle16_t(Res->Height)};
1105   writeObject(Middle);
1106
1107   // MENU field. As of now, we don't keep them in the state and can peacefully
1108   // think there is no menu attached to the dialog.
1109   writeInt<uint16_t>(0);
1110
1111   // Window CLASS field. Not kept here.
1112   writeInt<uint16_t>(0);
1113
1114   // Window title or a single word equal to 0.
1115   RETURN_IF_ERROR(writeCString(ObjectData.Caption));
1116
1117   // If there *is* a window font declared, output its data.
1118   auto &Font = ObjectData.Font;
1119   if (Font) {
1120     writeInt<uint16_t>(Font->Size);
1121     // Additional description occurs only in DIALOGEX.
1122     if (Res->IsExtended) {
1123       writeInt<uint16_t>(Font->Weight);
1124       writeInt<uint8_t>(Font->IsItalic);
1125       writeInt<uint8_t>(Font->Charset);
1126     }
1127     RETURN_IF_ERROR(writeCString(Font->Typeface));
1128   }
1129
1130   auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
1131     if (!Err)
1132       return Error::success();
1133     return joinErrors(createError("Error in " + Twine(Ctl.Type) +
1134                                   " control  (ID " + Twine(Ctl.ID) + "):"),
1135                       std::move(Err));
1136   };
1137
1138   for (auto &Ctl : Res->Controls)
1139     RETURN_IF_ERROR(
1140         handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl));
1141
1142   return Error::success();
1143 }
1144
1145 // --- HTMLResource helpers. --- //
1146
1147 Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
1148   return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
1149 }
1150
1151 // --- MenuResource helpers. --- //
1152
1153 Error ResourceFileWriter::writeMenuDefinition(
1154     const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
1155   assert(Def);
1156   const MenuDefinition *DefPtr = Def.get();
1157
1158   if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
1159     writeInt<uint16_t>(Flags);
1160     RETURN_IF_ERROR(
1161         checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"));
1162     writeInt<uint16_t>(MenuItemPtr->Id);
1163     RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
1164     return Error::success();
1165   }
1166
1167   if (isa<MenuSeparator>(DefPtr)) {
1168     writeInt<uint16_t>(Flags);
1169     writeInt<uint32_t>(0);
1170     return Error::success();
1171   }
1172
1173   auto *PopupPtr = cast<PopupItem>(DefPtr);
1174   writeInt<uint16_t>(Flags);
1175   RETURN_IF_ERROR(writeCString(PopupPtr->Name));
1176   return writeMenuDefinitionList(PopupPtr->SubItems);
1177 }
1178
1179 Error ResourceFileWriter::writeMenuDefinitionList(
1180     const MenuDefinitionList &List) {
1181   for (auto &Def : List.Definitions) {
1182     uint16_t Flags = Def->getResFlags();
1183     // Last element receives an additional 0x80 flag.
1184     const uint16_t LastElementFlag = 0x0080;
1185     if (&Def == &List.Definitions.back())
1186       Flags |= LastElementFlag;
1187
1188     RETURN_IF_ERROR(writeMenuDefinition(Def, Flags));
1189   }
1190   return Error::success();
1191 }
1192
1193 Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
1194   // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
1195   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
1196   writeInt<uint32_t>(0);
1197
1198   return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
1199 }
1200
1201 // --- StringTableResource helpers. --- //
1202
1203 class BundleResource : public RCResource {
1204 public:
1205   using BundleType = ResourceFileWriter::StringTableInfo::Bundle;
1206   BundleType Bundle;
1207
1208   BundleResource(const BundleType &StrBundle) : Bundle(StrBundle) {}
1209   IntOrString getResourceType() const override { return 6; }
1210
1211   ResourceKind getKind() const override { return RkStringTableBundle; }
1212   static bool classof(const RCResource *Res) {
1213     return Res->getKind() == RkStringTableBundle;
1214   }
1215   Twine getResourceTypeName() const override { return "STRINGTABLE"; }
1216 };
1217
1218 Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) {
1219   return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody);
1220 }
1221
1222 Error ResourceFileWriter::insertStringIntoBundle(
1223     StringTableInfo::Bundle &Bundle, uint16_t StringID, StringRef String) {
1224   uint16_t StringLoc = StringID & 15;
1225   if (Bundle.Data[StringLoc])
1226     return createError("Multiple STRINGTABLE strings located under ID " +
1227                        Twine(StringID));
1228   Bundle.Data[StringLoc] = String;
1229   return Error::success();
1230 }
1231
1232 Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) {
1233   auto *Res = cast<BundleResource>(Base);
1234   for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) {
1235     // The string format is a tiny bit different here. We
1236     // first output the size of the string, and then the string itself
1237     // (which is not null-terminated).
1238     bool IsLongString;
1239     SmallVector<UTF16, 128> Data;
1240     RETURN_IF_ERROR(processString(Res->Bundle.Data[ID].getValueOr(StringRef()),
1241                                   NullHandlingMethod::CutAtDoubleNull,
1242                                   IsLongString, Data, Params.CodePage));
1243     if (AppendNull && Res->Bundle.Data[ID])
1244       Data.push_back('\0');
1245     RETURN_IF_ERROR(
1246         checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"));
1247     writeInt<uint16_t>(Data.size());
1248     for (auto Char : Data)
1249       writeInt(Char);
1250   }
1251   return Error::success();
1252 }
1253
1254 Error ResourceFileWriter::dumpAllStringTables() {
1255   for (auto Key : StringTableData.BundleList) {
1256     auto Iter = StringTableData.BundleData.find(Key);
1257     assert(Iter != StringTableData.BundleData.end());
1258
1259     // For a moment, revert the context info to moment of bundle declaration.
1260     ContextKeeper RAII(this);
1261     ObjectData = Iter->second.DeclTimeInfo;
1262
1263     BundleResource Res(Iter->second);
1264     // Bundle #(k+1) contains keys [16k, 16k + 15].
1265     Res.setName(Key.first + 1);
1266     RETURN_IF_ERROR(visitStringTableBundle(&Res));
1267   }
1268   return Error::success();
1269 }
1270
1271 // --- UserDefinedResource helpers. --- //
1272
1273 Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) {
1274   auto *Res = cast<UserDefinedResource>(Base);
1275
1276   if (Res->IsFileResource)
1277     return appendFile(Res->FileLoc);
1278
1279   for (auto &Elem : Res->Contents) {
1280     if (Elem.isInt()) {
1281       RETURN_IF_ERROR(
1282           checkRCInt(Elem.getInt(), "Number in user-defined resource"));
1283       writeRCInt(Elem.getInt());
1284       continue;
1285     }
1286
1287     SmallVector<UTF16, 128> ProcessedString;
1288     bool IsLongString;
1289     RETURN_IF_ERROR(
1290         processString(Elem.getString(), NullHandlingMethod::UserResource,
1291                       IsLongString, ProcessedString, Params.CodePage));
1292
1293     for (auto Ch : ProcessedString) {
1294       if (IsLongString) {
1295         writeInt(Ch);
1296         continue;
1297       }
1298
1299       RETURN_IF_ERROR(checkNumberFits<uint8_t>(
1300           Ch, "Character in narrow string in user-defined resource"));
1301       writeInt<uint8_t>(Ch);
1302     }
1303   }
1304
1305   return Error::success();
1306 }
1307
1308 // --- VersionInfoResourceResource helpers. --- //
1309
1310 Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
1311   // Output the header if the block has name.
1312   bool OutputHeader = Blk.Name != "";
1313   uint64_t LengthLoc;
1314
1315   padStream(sizeof(uint32_t));
1316   if (OutputHeader) {
1317     LengthLoc = writeInt<uint16_t>(0);
1318     writeInt<uint16_t>(0);
1319     writeInt<uint16_t>(1); // true
1320     RETURN_IF_ERROR(writeCString(Blk.Name));
1321     padStream(sizeof(uint32_t));
1322   }
1323
1324   for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) {
1325     VersionInfoStmt *ItemPtr = Item.get();
1326
1327     if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) {
1328       RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr));
1329       continue;
1330     }
1331
1332     auto *ValuePtr = cast<VersionInfoValue>(ItemPtr);
1333     RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr));
1334   }
1335
1336   if (OutputHeader) {
1337     uint64_t CurLoc = tell();
1338     writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
1339   }
1340
1341   return Error::success();
1342 }
1343
1344 Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
1345   // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE
1346   // is a mapping from the key (string) to the value (a sequence of ints or
1347   // a sequence of strings).
1348   //
1349   // If integers are to be written: width of each integer written depends on
1350   // whether it's been declared 'long' (it's DWORD then) or not (it's WORD).
1351   // ValueLength defined in structure referenced below is then the total
1352   // number of bytes taken by these integers.
1353   //
1354   // If strings are to be written: characters are always WORDs.
1355   // Moreover, '\0' character is written after the last string, and between
1356   // every two strings separated by comma (if strings are not comma-separated,
1357   // they're simply concatenated). ValueLength is equal to the number of WORDs
1358   // written (that is, half of the bytes written).
1359   //
1360   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx
1361   bool HasStrings = false, HasInts = false;
1362   for (auto &Item : Val.Values)
1363     (Item.isInt() ? HasInts : HasStrings) = true;
1364
1365   assert((HasStrings || HasInts) && "VALUE must have at least one argument");
1366   if (HasStrings && HasInts)
1367     return createError(Twine("VALUE ") + Val.Key +
1368                        " cannot contain both strings and integers");
1369
1370   padStream(sizeof(uint32_t));
1371   auto LengthLoc = writeInt<uint16_t>(0);
1372   auto ValLengthLoc = writeInt<uint16_t>(0);
1373   writeInt<uint16_t>(HasStrings);
1374   RETURN_IF_ERROR(writeCString(Val.Key));
1375   padStream(sizeof(uint32_t));
1376
1377   auto DataLoc = tell();
1378   for (size_t Id = 0; Id < Val.Values.size(); ++Id) {
1379     auto &Item = Val.Values[Id];
1380     if (Item.isInt()) {
1381       auto Value = Item.getInt();
1382       RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value"));
1383       writeRCInt(Value);
1384       continue;
1385     }
1386
1387     bool WriteTerminator =
1388         Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1];
1389     RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator));
1390   }
1391
1392   auto CurLoc = tell();
1393   auto ValueLength = CurLoc - DataLoc;
1394   if (HasStrings) {
1395     assert(ValueLength % 2 == 0);
1396     ValueLength /= 2;
1397   }
1398   writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
1399   writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
1400   return Error::success();
1401 }
1402
1403 template <typename Ty>
1404 static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key,
1405                          const Ty &Default) {
1406   auto Iter = Map.find(Key);
1407   if (Iter != Map.end())
1408     return Iter->getValue();
1409   return Default;
1410 }
1411
1412 Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) {
1413   auto *Res = cast<VersionInfoResource>(Base);
1414
1415   const auto &FixedData = Res->FixedData;
1416
1417   struct /* VS_FIXEDFILEINFO */ {
1418     ulittle32_t Signature = ulittle32_t(0xFEEF04BD);
1419     ulittle32_t StructVersion = ulittle32_t(0x10000);
1420     // It's weird to have most-significant DWORD first on the little-endian
1421     // machines, but let it be this way.
1422     ulittle32_t FileVersionMS;
1423     ulittle32_t FileVersionLS;
1424     ulittle32_t ProductVersionMS;
1425     ulittle32_t ProductVersionLS;
1426     ulittle32_t FileFlagsMask;
1427     ulittle32_t FileFlags;
1428     ulittle32_t FileOS;
1429     ulittle32_t FileType;
1430     ulittle32_t FileSubtype;
1431     // MS implementation seems to always set these fields to 0.
1432     ulittle32_t FileDateMS = ulittle32_t(0);
1433     ulittle32_t FileDateLS = ulittle32_t(0);
1434   } FixedInfo;
1435
1436   // First, VS_VERSIONINFO.
1437   auto LengthLoc = writeInt<uint16_t>(0);
1438   writeInt<uint16_t>(sizeof(FixedInfo));
1439   writeInt<uint16_t>(0);
1440   cantFail(writeCString("VS_VERSION_INFO"));
1441   padStream(sizeof(uint32_t));
1442
1443   using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
1444   auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) {
1445     static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0};
1446     if (!FixedData.IsTypePresent[(int)Type])
1447       return DefaultOut;
1448     return FixedData.FixedInfo[(int)Type];
1449   };
1450
1451   auto FileVer = GetField(VersionInfoFixed::FtFileVersion);
1452   RETURN_IF_ERROR(checkNumberFits<uint16_t>(
1453       *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields"));
1454   FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1];
1455   FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3];
1456
1457   auto ProdVer = GetField(VersionInfoFixed::FtProductVersion);
1458   RETURN_IF_ERROR(checkNumberFits<uint16_t>(
1459       *std::max_element(ProdVer.begin(), ProdVer.end()),
1460       "PRODUCTVERSION fields"));
1461   FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1];
1462   FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3];
1463
1464   FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0];
1465   FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0];
1466   FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0];
1467   FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0];
1468   FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0];
1469
1470   writeObject(FixedInfo);
1471   padStream(sizeof(uint32_t));
1472
1473   RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock));
1474
1475   // FIXME: check overflow?
1476   writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc);
1477
1478   return Error::success();
1479 }
1480
1481 Expected<std::unique_ptr<MemoryBuffer>>
1482 ResourceFileWriter::loadFile(StringRef File) const {
1483   SmallString<128> Path;
1484   SmallString<128> Cwd;
1485   std::unique_ptr<MemoryBuffer> Result;
1486
1487   // 1. The current working directory.
1488   sys::fs::current_path(Cwd);
1489   Path.assign(Cwd.begin(), Cwd.end());
1490   sys::path::append(Path, File);
1491   if (sys::fs::exists(Path))
1492     return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1493
1494   // 2. The directory of the input resource file, if it is different from the
1495   // current
1496   //    working directory.
1497   StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath);
1498   Path.assign(InputFileDir.begin(), InputFileDir.end());
1499   sys::path::append(Path, File);
1500   if (sys::fs::exists(Path))
1501     return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1502
1503   // 3. All of the include directories specified on the command line.
1504   for (StringRef ForceInclude : Params.Include) {
1505     Path.assign(ForceInclude.begin(), ForceInclude.end());
1506     sys::path::append(Path, File);
1507     if (sys::fs::exists(Path))
1508       return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1509   }
1510
1511   if (auto Result =
1512           llvm::sys::Process::FindInEnvPath("INCLUDE", File, Params.NoInclude))
1513     return errorOrToExpected(MemoryBuffer::getFile(*Result, -1, false));
1514
1515   return make_error<StringError>("error : file not found : " + Twine(File),
1516                                  inconvertibleErrorCode());
1517 }
1518
1519 } // namespace rc
1520 } // namespace llvm