18f19343aSMarek Sokolowski //===-- ResourceFileWriter.cpp --------------------------------*- C++-*-===//
28f19343aSMarek Sokolowski //
38f19343aSMarek Sokolowski //                     The LLVM Compiler Infrastructure
48f19343aSMarek Sokolowski //
58f19343aSMarek Sokolowski // This file is distributed under the University of Illinois Open Source
68f19343aSMarek Sokolowski // License. See LICENSE.TXT for details.
78f19343aSMarek Sokolowski //
88f19343aSMarek Sokolowski //===---------------------------------------------------------------------===//
98f19343aSMarek Sokolowski //
108f19343aSMarek Sokolowski // This implements the visitor serializing resources to a .res stream.
118f19343aSMarek Sokolowski //
128f19343aSMarek Sokolowski //===---------------------------------------------------------------------===//
138f19343aSMarek Sokolowski 
148f19343aSMarek Sokolowski #include "ResourceFileWriter.h"
158f19343aSMarek Sokolowski 
168f19343aSMarek Sokolowski #include "llvm/Object/WindowsResource.h"
178f19343aSMarek Sokolowski #include "llvm/Support/ConvertUTF.h"
188f19343aSMarek Sokolowski #include "llvm/Support/Endian.h"
198f19343aSMarek Sokolowski #include "llvm/Support/EndianStream.h"
20bd159d32SZachary Turner #include "llvm/Support/MemoryBuffer.h"
21fa0ca6cbSZachary Turner #include "llvm/Support/Path.h"
22fa0ca6cbSZachary Turner #include "llvm/Support/Process.h"
238f19343aSMarek Sokolowski 
248f19343aSMarek Sokolowski using namespace llvm::support;
258f19343aSMarek Sokolowski 
268f19343aSMarek Sokolowski // Take an expression returning llvm::Error and forward the error if it exists.
278f19343aSMarek Sokolowski #define RETURN_IF_ERROR(Expr)                                                  \
288f19343aSMarek Sokolowski   if (auto Err = (Expr))                                                       \
298f19343aSMarek Sokolowski     return Err;
308f19343aSMarek Sokolowski 
318f19343aSMarek Sokolowski namespace llvm {
328f19343aSMarek Sokolowski namespace rc {
338f19343aSMarek Sokolowski 
3407bc04ffSZachary Turner // Class that employs RAII to save the current FileWriter object state
3522fccd64SMarek Sokolowski // and revert to it as soon as we leave the scope. This is useful if resources
3622fccd64SMarek Sokolowski // declare their own resource-local statements.
3722fccd64SMarek Sokolowski class ContextKeeper {
3822fccd64SMarek Sokolowski   ResourceFileWriter *FileWriter;
3922fccd64SMarek Sokolowski   ResourceFileWriter::ObjectInfo SavedInfo;
4022fccd64SMarek Sokolowski 
4122fccd64SMarek Sokolowski public:
4222fccd64SMarek Sokolowski   ContextKeeper(ResourceFileWriter *V)
4322fccd64SMarek Sokolowski       : FileWriter(V), SavedInfo(V->ObjectData) {}
4422fccd64SMarek Sokolowski   ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; }
4522fccd64SMarek Sokolowski };
4622fccd64SMarek Sokolowski 
47fa0ca6cbSZachary Turner static Error createError(const Twine &Message,
488f19343aSMarek Sokolowski                          std::errc Type = std::errc::invalid_argument) {
498f19343aSMarek Sokolowski   return make_error<StringError>(Message, std::make_error_code(Type));
508f19343aSMarek Sokolowski }
518f19343aSMarek Sokolowski 
52fa0ca6cbSZachary Turner static Error checkNumberFits(uint32_t Number, size_t MaxBits,
53fa0ca6cbSZachary Turner                              const Twine &FieldName) {
548f19343aSMarek Sokolowski   assert(1 <= MaxBits && MaxBits <= 32);
558f19343aSMarek Sokolowski   if (!(Number >> MaxBits))
568f19343aSMarek Sokolowski     return Error::success();
578f19343aSMarek Sokolowski   return createError(FieldName + " (" + Twine(Number) + ") does not fit in " +
588f19343aSMarek Sokolowski                          Twine(MaxBits) + " bits.",
598f19343aSMarek Sokolowski                      std::errc::value_too_large);
608f19343aSMarek Sokolowski }
618f19343aSMarek Sokolowski 
628f19343aSMarek Sokolowski template <typename FitType>
63fa0ca6cbSZachary Turner static Error checkNumberFits(uint32_t Number, const Twine &FieldName) {
648f19343aSMarek Sokolowski   return checkNumberFits(Number, sizeof(FitType) * 8, FieldName);
658f19343aSMarek Sokolowski }
668f19343aSMarek Sokolowski 
677f7745c0SMarek Sokolowski // A similar function for signed integers.
687f7745c0SMarek Sokolowski template <typename FitType>
69fa0ca6cbSZachary Turner static Error checkSignedNumberFits(uint32_t Number, const Twine &FieldName,
707f7745c0SMarek Sokolowski                                    bool CanBeNegative) {
717f7745c0SMarek Sokolowski   int32_t SignedNum = Number;
727f7745c0SMarek Sokolowski   if (SignedNum < std::numeric_limits<FitType>::min() ||
737f7745c0SMarek Sokolowski       SignedNum > std::numeric_limits<FitType>::max())
747f7745c0SMarek Sokolowski     return createError(FieldName + " (" + Twine(SignedNum) +
757f7745c0SMarek Sokolowski                            ") does not fit in " + Twine(sizeof(FitType) * 8) +
767f7745c0SMarek Sokolowski                            "-bit signed integer type.",
777f7745c0SMarek Sokolowski                        std::errc::value_too_large);
787f7745c0SMarek Sokolowski 
797f7745c0SMarek Sokolowski   if (!CanBeNegative && SignedNum < 0)
807f7745c0SMarek Sokolowski     return createError(FieldName + " (" + Twine(SignedNum) +
817f7745c0SMarek Sokolowski                        ") cannot be negative.");
827f7745c0SMarek Sokolowski 
837f7745c0SMarek Sokolowski   return Error::success();
847f7745c0SMarek Sokolowski }
857f7745c0SMarek Sokolowski 
86fa0ca6cbSZachary Turner static Error checkRCInt(RCInt Number, const Twine &FieldName) {
8707bc04ffSZachary Turner   if (Number.isLong())
8807bc04ffSZachary Turner     return Error::success();
8907bc04ffSZachary Turner   return checkNumberFits<uint16_t>(Number, FieldName);
9007bc04ffSZachary Turner }
9107bc04ffSZachary Turner 
92fa0ca6cbSZachary Turner static Error checkIntOrString(IntOrString Value, const Twine &FieldName) {
938f19343aSMarek Sokolowski   if (!Value.isInt())
948f19343aSMarek Sokolowski     return Error::success();
958f19343aSMarek Sokolowski   return checkNumberFits<uint16_t>(Value.getInt(), FieldName);
968f19343aSMarek Sokolowski }
978f19343aSMarek Sokolowski 
988f19343aSMarek Sokolowski static bool stripQuotes(StringRef &Str, bool &IsLongString) {
998f19343aSMarek Sokolowski   if (!Str.contains('"'))
1008f19343aSMarek Sokolowski     return false;
1018f19343aSMarek Sokolowski 
1028f19343aSMarek Sokolowski   // Just take the contents of the string, checking if it's been marked long.
1038f19343aSMarek Sokolowski   IsLongString = Str.startswith_lower("L");
1048f19343aSMarek Sokolowski   if (IsLongString)
1058f19343aSMarek Sokolowski     Str = Str.drop_front();
1068f19343aSMarek Sokolowski 
1078f19343aSMarek Sokolowski   bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\"");
1088f19343aSMarek Sokolowski   (void)StripSuccess;
1098f19343aSMarek Sokolowski   assert(StripSuccess && "Strings should be enclosed in quotes.");
1108f19343aSMarek Sokolowski   return true;
1118f19343aSMarek Sokolowski }
1128f19343aSMarek Sokolowski 
113d1d046aaSMartin Storsjo static UTF16 cp1252ToUnicode(unsigned char C) {
114d1d046aaSMartin Storsjo   static const UTF16 Map80[] = {
115d1d046aaSMartin Storsjo       0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
116d1d046aaSMartin Storsjo       0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
117d1d046aaSMartin Storsjo       0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
118d1d046aaSMartin Storsjo       0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178,
119d1d046aaSMartin Storsjo   };
120d1d046aaSMartin Storsjo   if (C >= 0x80 && C <= 0x9F)
121d1d046aaSMartin Storsjo     return Map80[C - 0x80];
122d1d046aaSMartin Storsjo   return C;
123d1d046aaSMartin Storsjo }
124d1d046aaSMartin Storsjo 
1258f19343aSMarek Sokolowski // Describes a way to handle '\0' characters when processing the string.
1268f19343aSMarek Sokolowski // rc.exe tool sometimes behaves in a weird way in postprocessing.
1278f19343aSMarek Sokolowski // If the string to be output is equivalent to a C-string (e.g. in MENU
1288f19343aSMarek Sokolowski // titles), string is (predictably) truncated after first 0-byte.
1298f19343aSMarek Sokolowski // When outputting a string table, the behavior is equivalent to appending
1308f19343aSMarek Sokolowski // '\0\0' at the end of the string, and then stripping the string
1318f19343aSMarek Sokolowski // before the first '\0\0' occurrence.
1328f19343aSMarek Sokolowski // Finally, when handling strings in user-defined resources, 0-bytes
1338f19343aSMarek Sokolowski // aren't stripped, nor do they terminate the string.
1348f19343aSMarek Sokolowski 
1358f19343aSMarek Sokolowski enum class NullHandlingMethod {
1368f19343aSMarek Sokolowski   UserResource,   // Don't terminate string on '\0'.
1378f19343aSMarek Sokolowski   CutAtNull,      // Terminate string on '\0'.
1388f19343aSMarek Sokolowski   CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'.
1398f19343aSMarek Sokolowski };
1408f19343aSMarek Sokolowski 
141a92eb33aSZachary Turner // Parses an identifier or string and returns a processed version of it:
142a92eb33aSZachary Turner //   * String the string boundary quotes.
1438f19343aSMarek Sokolowski //   * Squash "" to a single ".
1448f19343aSMarek Sokolowski //   * Replace the escape sequences with their processed version.
1458f19343aSMarek Sokolowski // For identifiers, this is no-op.
1468f19343aSMarek Sokolowski static Error processString(StringRef Str, NullHandlingMethod NullHandler,
147d1d046aaSMartin Storsjo                            bool &IsLongString, SmallVectorImpl<UTF16> &Result,
148d1d046aaSMartin Storsjo                            int CodePage) {
1498f19343aSMarek Sokolowski   bool IsString = stripQuotes(Str, IsLongString);
150a92eb33aSZachary Turner   SmallVector<UTF16, 128> Chars;
151d1d046aaSMartin Storsjo 
152d1d046aaSMartin Storsjo   // Convert the input bytes according to the chosen codepage.
153d1d046aaSMartin Storsjo   if (CodePage == CpUtf8) {
154a92eb33aSZachary Turner     convertUTF8ToUTF16String(Str, Chars);
155d1d046aaSMartin Storsjo   } else if (CodePage == CpWin1252) {
156d1d046aaSMartin Storsjo     for (char C : Str)
157d1d046aaSMartin Storsjo       Chars.push_back(cp1252ToUnicode((unsigned char)C));
158d1d046aaSMartin Storsjo   } else {
159d1d046aaSMartin Storsjo     // For other, unknown codepages, only allow plain ASCII input.
160d1d046aaSMartin Storsjo     for (char C : Str) {
161d1d046aaSMartin Storsjo       if ((unsigned char)C > 0x7F)
162d1d046aaSMartin Storsjo         return createError("Non-ASCII 8-bit codepoint (" + Twine(C) +
163d1d046aaSMartin Storsjo                            ") can't be interpreted in the current codepage");
164d1d046aaSMartin Storsjo       Chars.push_back((unsigned char)C);
165d1d046aaSMartin Storsjo     }
166d1d046aaSMartin Storsjo   }
1678f19343aSMarek Sokolowski 
1688f19343aSMarek Sokolowski   if (!IsString) {
1698f19343aSMarek Sokolowski     // It's an identifier if it's not a string. Make all characters uppercase.
170a92eb33aSZachary Turner     for (UTF16 &Ch : Chars) {
1718f19343aSMarek Sokolowski       assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII");
1728f19343aSMarek Sokolowski       Ch = toupper(Ch);
1738f19343aSMarek Sokolowski     }
174a92eb33aSZachary Turner     Result.swap(Chars);
1758f19343aSMarek Sokolowski     return Error::success();
1768f19343aSMarek Sokolowski   }
177a92eb33aSZachary Turner   Result.reserve(Chars.size());
178a92eb33aSZachary Turner   size_t Pos = 0;
179a92eb33aSZachary Turner 
180a92eb33aSZachary Turner   auto AddRes = [&Result, NullHandler, IsLongString](UTF16 Char) -> Error {
181a92eb33aSZachary Turner     if (!IsLongString) {
182a92eb33aSZachary Turner       if (NullHandler == NullHandlingMethod::UserResource) {
183a92eb33aSZachary Turner         // Narrow strings in user-defined resources are *not* output in
184a92eb33aSZachary Turner         // UTF-16 format.
185a92eb33aSZachary Turner         if (Char > 0xFF)
186a92eb33aSZachary Turner           return createError("Non-8-bit codepoint (" + Twine(Char) +
187a92eb33aSZachary Turner                              ") can't occur in a user-defined narrow string");
188d1d046aaSMartin Storsjo       }
189d1d046aaSMartin Storsjo     }
190a92eb33aSZachary Turner 
191d1d046aaSMartin Storsjo     Result.push_back(Char);
192d1d046aaSMartin Storsjo     return Error::success();
193d1d046aaSMartin Storsjo   };
194d1d046aaSMartin Storsjo   auto AddEscapedChar = [AddRes, IsLongString, CodePage](UTF16 Char) -> Error {
195d1d046aaSMartin Storsjo     if (!IsLongString) {
196d1d046aaSMartin Storsjo       // Escaped chars in narrow strings have to be interpreted according to
197d1d046aaSMartin Storsjo       // the chosen code page.
198d1d046aaSMartin Storsjo       if (Char > 0xFF)
199d1d046aaSMartin Storsjo         return createError("Non-8-bit escaped char (" + Twine(Char) +
200d1d046aaSMartin Storsjo                            ") can't occur in narrow string");
201d1d046aaSMartin Storsjo       if (CodePage == CpUtf8) {
202d1d046aaSMartin Storsjo         if (Char >= 0x80)
203d1d046aaSMartin Storsjo           return createError("Unable to interpret single byte (" + Twine(Char) +
204d1d046aaSMartin Storsjo                              ") as UTF-8");
205d1d046aaSMartin Storsjo       } else if (CodePage == CpWin1252) {
206d1d046aaSMartin Storsjo         Char = cp1252ToUnicode(Char);
207a92eb33aSZachary Turner       } else {
208d1d046aaSMartin Storsjo         // Unknown/unsupported codepage, only allow ASCII input.
209d1d046aaSMartin Storsjo         if (Char > 0x7F)
210a92eb33aSZachary Turner           return createError("Non-ASCII 8-bit codepoint (" + Twine(Char) +
211a92eb33aSZachary Turner                              ") can't "
212a92eb33aSZachary Turner                              "occur in a non-Unicode string");
213a92eb33aSZachary Turner       }
214a92eb33aSZachary Turner     }
215a92eb33aSZachary Turner 
216d1d046aaSMartin Storsjo     return AddRes(Char);
217a92eb33aSZachary Turner   };
218a92eb33aSZachary Turner 
219a92eb33aSZachary Turner   while (Pos < Chars.size()) {
220a92eb33aSZachary Turner     UTF16 CurChar = Chars[Pos];
221a92eb33aSZachary Turner     ++Pos;
222a92eb33aSZachary Turner 
223a92eb33aSZachary Turner     // Strip double "".
224a92eb33aSZachary Turner     if (CurChar == '"') {
225a92eb33aSZachary Turner       if (Pos == Chars.size() || Chars[Pos] != '"')
226a92eb33aSZachary Turner         return createError("Expected \"\"");
227a92eb33aSZachary Turner       ++Pos;
228a92eb33aSZachary Turner       RETURN_IF_ERROR(AddRes('"'));
229a92eb33aSZachary Turner       continue;
230a92eb33aSZachary Turner     }
231a92eb33aSZachary Turner 
232a92eb33aSZachary Turner     if (CurChar == '\\') {
233a92eb33aSZachary Turner       UTF16 TypeChar = Chars[Pos];
234a92eb33aSZachary Turner       ++Pos;
235a92eb33aSZachary Turner 
236a92eb33aSZachary Turner       if (TypeChar == 'x' || TypeChar == 'X') {
237a92eb33aSZachary Turner         // Read a hex number. Max number of characters to read differs between
238a92eb33aSZachary Turner         // narrow and wide strings.
239a92eb33aSZachary Turner         UTF16 ReadInt = 0;
240a92eb33aSZachary Turner         size_t RemainingChars = IsLongString ? 4 : 2;
241a92eb33aSZachary Turner         // We don't want to read non-ASCII hex digits. std:: functions past
242a92eb33aSZachary Turner         // 0xFF invoke UB.
243a92eb33aSZachary Turner         //
244a92eb33aSZachary Turner         // FIXME: actually, Microsoft version probably doesn't check this
245a92eb33aSZachary Turner         // condition and uses their Unicode version of 'isxdigit'. However,
246a92eb33aSZachary Turner         // there are some hex-digit Unicode character outside of ASCII, and
247a92eb33aSZachary Turner         // some of these are actually accepted by rc.exe, the notable example
248a92eb33aSZachary Turner         // being fullwidth forms (U+FF10..U+FF19 etc.) These can be written
249a92eb33aSZachary Turner         // instead of ASCII digits in \x... escape sequence and get accepted.
250a92eb33aSZachary Turner         // However, the resulting hexcodes seem totally unpredictable.
251a92eb33aSZachary Turner         // We think it's infeasible to try to reproduce this behavior, nor to
252a92eb33aSZachary Turner         // put effort in order to detect it.
253a92eb33aSZachary Turner         while (RemainingChars && Pos < Chars.size() && Chars[Pos] < 0x80) {
254a92eb33aSZachary Turner           if (!isxdigit(Chars[Pos]))
255a92eb33aSZachary Turner             break;
256a92eb33aSZachary Turner           char Digit = tolower(Chars[Pos]);
257a92eb33aSZachary Turner           ++Pos;
258a92eb33aSZachary Turner 
259a92eb33aSZachary Turner           ReadInt <<= 4;
260a92eb33aSZachary Turner           if (isdigit(Digit))
261a92eb33aSZachary Turner             ReadInt |= Digit - '0';
262a92eb33aSZachary Turner           else
263a92eb33aSZachary Turner             ReadInt |= Digit - 'a' + 10;
264a92eb33aSZachary Turner 
265a92eb33aSZachary Turner           --RemainingChars;
266a92eb33aSZachary Turner         }
267a92eb33aSZachary Turner 
268d1d046aaSMartin Storsjo         RETURN_IF_ERROR(AddEscapedChar(ReadInt));
269a92eb33aSZachary Turner         continue;
270a92eb33aSZachary Turner       }
271a92eb33aSZachary Turner 
272a92eb33aSZachary Turner       if (TypeChar >= '0' && TypeChar < '8') {
273a92eb33aSZachary Turner         // Read an octal number. Note that we've already read the first digit.
274a92eb33aSZachary Turner         UTF16 ReadInt = TypeChar - '0';
275a92eb33aSZachary Turner         size_t RemainingChars = IsLongString ? 6 : 2;
276a92eb33aSZachary Turner 
277a92eb33aSZachary Turner         while (RemainingChars && Pos < Chars.size() && Chars[Pos] >= '0' &&
278a92eb33aSZachary Turner                Chars[Pos] < '8') {
279a92eb33aSZachary Turner           ReadInt <<= 3;
280a92eb33aSZachary Turner           ReadInt |= Chars[Pos] - '0';
281a92eb33aSZachary Turner           --RemainingChars;
282a92eb33aSZachary Turner           ++Pos;
283a92eb33aSZachary Turner         }
284a92eb33aSZachary Turner 
285d1d046aaSMartin Storsjo         RETURN_IF_ERROR(AddEscapedChar(ReadInt));
286a92eb33aSZachary Turner 
287a92eb33aSZachary Turner         continue;
288a92eb33aSZachary Turner       }
289a92eb33aSZachary Turner 
290a92eb33aSZachary Turner       switch (TypeChar) {
291a92eb33aSZachary Turner       case 'A':
292a92eb33aSZachary Turner       case 'a':
293a92eb33aSZachary Turner         // Windows '\a' translates into '\b' (Backspace).
294a92eb33aSZachary Turner         RETURN_IF_ERROR(AddRes('\b'));
295a92eb33aSZachary Turner         break;
296a92eb33aSZachary Turner 
297a92eb33aSZachary Turner       case 'n': // Somehow, RC doesn't recognize '\N' and '\R'.
298a92eb33aSZachary Turner         RETURN_IF_ERROR(AddRes('\n'));
299a92eb33aSZachary Turner         break;
300a92eb33aSZachary Turner 
301a92eb33aSZachary Turner       case 'r':
302a92eb33aSZachary Turner         RETURN_IF_ERROR(AddRes('\r'));
303a92eb33aSZachary Turner         break;
304a92eb33aSZachary Turner 
305a92eb33aSZachary Turner       case 'T':
306a92eb33aSZachary Turner       case 't':
307a92eb33aSZachary Turner         RETURN_IF_ERROR(AddRes('\t'));
308a92eb33aSZachary Turner         break;
309a92eb33aSZachary Turner 
310a92eb33aSZachary Turner       case '\\':
311a92eb33aSZachary Turner         RETURN_IF_ERROR(AddRes('\\'));
312a92eb33aSZachary Turner         break;
313a92eb33aSZachary Turner 
314a92eb33aSZachary Turner       case '"':
315a92eb33aSZachary Turner         // RC accepts \" only if another " comes afterwards; then, \"" means
316a92eb33aSZachary Turner         // a single ".
317a92eb33aSZachary Turner         if (Pos == Chars.size() || Chars[Pos] != '"')
318a92eb33aSZachary Turner           return createError("Expected \\\"\"");
319a92eb33aSZachary Turner         ++Pos;
320a92eb33aSZachary Turner         RETURN_IF_ERROR(AddRes('"'));
321a92eb33aSZachary Turner         break;
322a92eb33aSZachary Turner 
323a92eb33aSZachary Turner       default:
324a92eb33aSZachary Turner         // If TypeChar means nothing, \ is should be output to stdout with
325a92eb33aSZachary Turner         // following char. However, rc.exe consumes these characters when
326a92eb33aSZachary Turner         // dealing with wide strings.
327a92eb33aSZachary Turner         if (!IsLongString) {
328a92eb33aSZachary Turner           RETURN_IF_ERROR(AddRes('\\'));
329a92eb33aSZachary Turner           RETURN_IF_ERROR(AddRes(TypeChar));
330a92eb33aSZachary Turner         }
331a92eb33aSZachary Turner         break;
332a92eb33aSZachary Turner       }
333a92eb33aSZachary Turner 
334a92eb33aSZachary Turner       continue;
335a92eb33aSZachary Turner     }
336a92eb33aSZachary Turner 
337a92eb33aSZachary Turner     // If nothing interesting happens, just output the character.
338a92eb33aSZachary Turner     RETURN_IF_ERROR(AddRes(CurChar));
339a92eb33aSZachary Turner   }
3408f19343aSMarek Sokolowski 
341da366693SZachary Turner   switch (NullHandler) {
342da366693SZachary Turner   case NullHandlingMethod::CutAtNull:
3438f19343aSMarek Sokolowski     for (size_t Pos = 0; Pos < Result.size(); ++Pos)
3448f19343aSMarek Sokolowski       if (Result[Pos] == '\0')
3458f19343aSMarek Sokolowski         Result.resize(Pos);
346da366693SZachary Turner     break;
347da366693SZachary Turner 
348da366693SZachary Turner   case NullHandlingMethod::CutAtDoubleNull:
349da366693SZachary Turner     for (size_t Pos = 0; Pos + 1 < Result.size(); ++Pos)
350da366693SZachary Turner       if (Result[Pos] == '\0' && Result[Pos + 1] == '\0')
351da366693SZachary Turner         Result.resize(Pos);
352da366693SZachary Turner     if (Result.size() > 0 && Result.back() == '\0')
353da366693SZachary Turner       Result.pop_back();
354da366693SZachary Turner     break;
355da366693SZachary Turner 
356da366693SZachary Turner   case NullHandlingMethod::UserResource:
357da366693SZachary Turner     break;
358da366693SZachary Turner   }
3598f19343aSMarek Sokolowski 
3608f19343aSMarek Sokolowski   return Error::success();
3618f19343aSMarek Sokolowski }
3628f19343aSMarek Sokolowski 
3638f19343aSMarek Sokolowski uint64_t ResourceFileWriter::writeObject(const ArrayRef<uint8_t> Data) {
3648f19343aSMarek Sokolowski   uint64_t Result = tell();
3658f19343aSMarek Sokolowski   FS->write((const char *)Data.begin(), Data.size());
3668f19343aSMarek Sokolowski   return Result;
3678f19343aSMarek Sokolowski }
3688f19343aSMarek Sokolowski 
3698f19343aSMarek Sokolowski Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) {
3708f19343aSMarek Sokolowski   SmallVector<UTF16, 128> ProcessedString;
3718f19343aSMarek Sokolowski   bool IsLongString;
3728f19343aSMarek Sokolowski   RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull,
373d1d046aaSMartin Storsjo                                 IsLongString, ProcessedString,
374d1d046aaSMartin Storsjo                                 Params.CodePage));
3758f19343aSMarek Sokolowski   for (auto Ch : ProcessedString)
3768f19343aSMarek Sokolowski     writeInt<uint16_t>(Ch);
3778f19343aSMarek Sokolowski   if (WriteTerminator)
3788f19343aSMarek Sokolowski     writeInt<uint16_t>(0);
3798f19343aSMarek Sokolowski   return Error::success();
3808f19343aSMarek Sokolowski }
3818f19343aSMarek Sokolowski 
3828f19343aSMarek Sokolowski Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) {
3838f19343aSMarek Sokolowski   return writeIntOrString(Ident);
3848f19343aSMarek Sokolowski }
3858f19343aSMarek Sokolowski 
3868f19343aSMarek Sokolowski Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) {
3878f19343aSMarek Sokolowski   if (!Value.isInt())
3888f19343aSMarek Sokolowski     return writeCString(Value.getString());
3898f19343aSMarek Sokolowski 
3908f19343aSMarek Sokolowski   writeInt<uint16_t>(0xFFFF);
3918f19343aSMarek Sokolowski   writeInt<uint16_t>(Value.getInt());
3928f19343aSMarek Sokolowski   return Error::success();
3938f19343aSMarek Sokolowski }
3948f19343aSMarek Sokolowski 
39507bc04ffSZachary Turner void ResourceFileWriter::writeRCInt(RCInt Value) {
39607bc04ffSZachary Turner   if (Value.isLong())
397b12792a4SZachary Turner     writeInt<uint32_t>(Value);
39807bc04ffSZachary Turner   else
399b12792a4SZachary Turner     writeInt<uint16_t>(Value);
40007bc04ffSZachary Turner }
40107bc04ffSZachary Turner 
4028f19343aSMarek Sokolowski Error ResourceFileWriter::appendFile(StringRef Filename) {
4038f19343aSMarek Sokolowski   bool IsLong;
4048f19343aSMarek Sokolowski   stripQuotes(Filename, IsLong);
4058f19343aSMarek Sokolowski 
406fa0ca6cbSZachary Turner   auto File = loadFile(Filename);
4078f19343aSMarek Sokolowski   if (!File)
408fa0ca6cbSZachary Turner     return File.takeError();
409fa0ca6cbSZachary Turner 
4108f19343aSMarek Sokolowski   *FS << (*File)->getBuffer();
4118f19343aSMarek Sokolowski   return Error::success();
4128f19343aSMarek Sokolowski }
4138f19343aSMarek Sokolowski 
4148f19343aSMarek Sokolowski void ResourceFileWriter::padStream(uint64_t Length) {
4158f19343aSMarek Sokolowski   assert(Length > 0);
4168f19343aSMarek Sokolowski   uint64_t Location = tell();
4178f19343aSMarek Sokolowski   Location %= Length;
4188f19343aSMarek Sokolowski   uint64_t Pad = (Length - Location) % Length;
4198f19343aSMarek Sokolowski   for (uint64_t i = 0; i < Pad; ++i)
4208f19343aSMarek Sokolowski     writeInt<uint8_t>(0);
4218f19343aSMarek Sokolowski }
4228f19343aSMarek Sokolowski 
423514b7105SZachary Turner Error ResourceFileWriter::handleError(Error Err, const RCResource *Res) {
4248f19343aSMarek Sokolowski   if (Err)
4258f19343aSMarek Sokolowski     return joinErrors(createError("Error in " + Res->getResourceTypeName() +
4268f19343aSMarek Sokolowski                                   " statement (ID " + Twine(Res->ResName) +
4278f19343aSMarek Sokolowski                                   "): "),
4288f19343aSMarek Sokolowski                       std::move(Err));
4298f19343aSMarek Sokolowski   return Error::success();
4308f19343aSMarek Sokolowski }
4318f19343aSMarek Sokolowski 
4328f19343aSMarek Sokolowski Error ResourceFileWriter::visitNullResource(const RCResource *Res) {
4338f19343aSMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeNullBody);
4348f19343aSMarek Sokolowski }
4358f19343aSMarek Sokolowski 
43622fccd64SMarek Sokolowski Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) {
43722fccd64SMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody);
43822fccd64SMarek Sokolowski }
43922fccd64SMarek Sokolowski 
440c3ab013aSZachary Turner Error ResourceFileWriter::visitCursorResource(const RCResource *Res) {
441c3ab013aSZachary Turner   return handleError(visitIconOrCursorResource(Res), Res);
442c3ab013aSZachary Turner }
443c3ab013aSZachary Turner 
4447f7745c0SMarek Sokolowski Error ResourceFileWriter::visitDialogResource(const RCResource *Res) {
4457f7745c0SMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeDialogBody);
4467f7745c0SMarek Sokolowski }
4477f7745c0SMarek Sokolowski 
448c3ab013aSZachary Turner Error ResourceFileWriter::visitIconResource(const RCResource *Res) {
449c3ab013aSZachary Turner   return handleError(visitIconOrCursorResource(Res), Res);
450c3ab013aSZachary Turner }
451c3ab013aSZachary Turner 
452420090afSZachary Turner Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) {
453420090afSZachary Turner   ObjectData.Caption = Stmt->Value;
454420090afSZachary Turner   return Error::success();
455420090afSZachary Turner }
456420090afSZachary Turner 
4578f19343aSMarek Sokolowski Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
4588f19343aSMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
4598f19343aSMarek Sokolowski }
4608f19343aSMarek Sokolowski 
46142f494d6SMarek Sokolowski Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
46242f494d6SMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeMenuBody);
46342f494d6SMarek Sokolowski }
46442f494d6SMarek Sokolowski 
465da366693SZachary Turner Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) {
466da366693SZachary Turner   const auto *Res = cast<StringTableResource>(Base);
467da366693SZachary Turner 
468da366693SZachary Turner   ContextKeeper RAII(this);
469da366693SZachary Turner   RETURN_IF_ERROR(Res->applyStmts(this));
470da366693SZachary Turner 
471da366693SZachary Turner   for (auto &String : Res->Table) {
472da366693SZachary Turner     RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID"));
473da366693SZachary Turner     uint16_t BundleID = String.first >> 4;
474da366693SZachary Turner     StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo);
475da366693SZachary Turner     auto &BundleData = StringTableData.BundleData;
476da366693SZachary Turner     auto Iter = BundleData.find(Key);
477da366693SZachary Turner 
478da366693SZachary Turner     if (Iter == BundleData.end()) {
479da366693SZachary Turner       // Need to create a bundle.
480da366693SZachary Turner       StringTableData.BundleList.push_back(Key);
481da366693SZachary Turner       auto EmplaceResult =
482da366693SZachary Turner           BundleData.emplace(Key, StringTableInfo::Bundle(ObjectData));
483da366693SZachary Turner       assert(EmplaceResult.second && "Could not create a bundle");
484da366693SZachary Turner       Iter = EmplaceResult.first;
485da366693SZachary Turner     }
486da366693SZachary Turner 
487da366693SZachary Turner     RETURN_IF_ERROR(
488da366693SZachary Turner         insertStringIntoBundle(Iter->second, String.first, String.second));
489da366693SZachary Turner   }
490da366693SZachary Turner 
491da366693SZachary Turner   return Error::success();
492da366693SZachary Turner }
493da366693SZachary Turner 
4949d8b358aSZachary Turner Error ResourceFileWriter::visitUserDefinedResource(const RCResource *Res) {
4959d8b358aSZachary Turner   return writeResource(Res, &ResourceFileWriter::writeUserDefinedBody);
4969d8b358aSZachary Turner }
4979d8b358aSZachary Turner 
49807bc04ffSZachary Turner Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) {
49907bc04ffSZachary Turner   return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody);
50007bc04ffSZachary Turner }
50107bc04ffSZachary Turner 
50222fccd64SMarek Sokolowski Error ResourceFileWriter::visitCharacteristicsStmt(
50322fccd64SMarek Sokolowski     const CharacteristicsStmt *Stmt) {
50422fccd64SMarek Sokolowski   ObjectData.Characteristics = Stmt->Value;
50522fccd64SMarek Sokolowski   return Error::success();
50622fccd64SMarek Sokolowski }
50722fccd64SMarek Sokolowski 
508420090afSZachary Turner Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) {
509420090afSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size"));
510420090afSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight"));
511420090afSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset"));
512420090afSZachary Turner   ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic,
513420090afSZachary Turner                             Stmt->Charset};
514420090afSZachary Turner   ObjectData.Font.emplace(Font);
515420090afSZachary Turner   return Error::success();
516420090afSZachary Turner }
517420090afSZachary Turner 
5188f19343aSMarek Sokolowski Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
5198f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID"));
5208f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"));
5218f19343aSMarek Sokolowski   ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10);
5228f19343aSMarek Sokolowski   return Error::success();
5238f19343aSMarek Sokolowski }
5248f19343aSMarek Sokolowski 
525420090afSZachary Turner Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) {
526420090afSZachary Turner   ObjectData.Style = Stmt->Value;
527420090afSZachary Turner   return Error::success();
528420090afSZachary Turner }
529420090afSZachary Turner 
53022fccd64SMarek Sokolowski Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) {
53122fccd64SMarek Sokolowski   ObjectData.VersionInfo = Stmt->Value;
53222fccd64SMarek Sokolowski   return Error::success();
53322fccd64SMarek Sokolowski }
53422fccd64SMarek Sokolowski 
5358f19343aSMarek Sokolowski Error ResourceFileWriter::writeResource(
5368f19343aSMarek Sokolowski     const RCResource *Res,
5378f19343aSMarek Sokolowski     Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) {
5388f19343aSMarek Sokolowski   // We don't know the sizes yet.
5398f19343aSMarek Sokolowski   object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)};
5408f19343aSMarek Sokolowski   uint64_t HeaderLoc = writeObject(HeaderPrefix);
5418f19343aSMarek Sokolowski 
5428f19343aSMarek Sokolowski   auto ResType = Res->getResourceType();
5438f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type"));
5448f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID"));
5458f19343aSMarek Sokolowski   RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res));
5468f19343aSMarek Sokolowski   RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res));
5478f19343aSMarek Sokolowski 
54822fccd64SMarek Sokolowski   // Apply the resource-local optional statements.
54922fccd64SMarek Sokolowski   ContextKeeper RAII(this);
55022fccd64SMarek Sokolowski   RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res));
55122fccd64SMarek Sokolowski 
5528f19343aSMarek Sokolowski   padStream(sizeof(uint32_t));
5538f19343aSMarek Sokolowski   object::WinResHeaderSuffix HeaderSuffix{
5548f19343aSMarek Sokolowski       ulittle32_t(0), // DataVersion; seems to always be 0
5558f19343aSMarek Sokolowski       ulittle16_t(Res->getMemoryFlags()), ulittle16_t(ObjectData.LanguageInfo),
55622fccd64SMarek Sokolowski       ulittle32_t(ObjectData.VersionInfo),
55722fccd64SMarek Sokolowski       ulittle32_t(ObjectData.Characteristics)};
5588f19343aSMarek Sokolowski   writeObject(HeaderSuffix);
5598f19343aSMarek Sokolowski 
5608f19343aSMarek Sokolowski   uint64_t DataLoc = tell();
5618f19343aSMarek Sokolowski   RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res));
5628f19343aSMarek Sokolowski   // RETURN_IF_ERROR(handleError(dumpResource(Ctx)));
5638f19343aSMarek Sokolowski 
5648f19343aSMarek Sokolowski   // Update the sizes.
5658f19343aSMarek Sokolowski   HeaderPrefix.DataSize = tell() - DataLoc;
5668f19343aSMarek Sokolowski   HeaderPrefix.HeaderSize = DataLoc - HeaderLoc;
5678f19343aSMarek Sokolowski   writeObjectAt(HeaderPrefix, HeaderLoc);
5688f19343aSMarek Sokolowski   padStream(sizeof(uint32_t));
5698f19343aSMarek Sokolowski 
5708f19343aSMarek Sokolowski   return Error::success();
5718f19343aSMarek Sokolowski }
5728f19343aSMarek Sokolowski 
57322fccd64SMarek Sokolowski // --- NullResource helpers. --- //
57422fccd64SMarek Sokolowski 
5758f19343aSMarek Sokolowski Error ResourceFileWriter::writeNullBody(const RCResource *) {
5768f19343aSMarek Sokolowski   return Error::success();
5778f19343aSMarek Sokolowski }
5788f19343aSMarek Sokolowski 
57922fccd64SMarek Sokolowski // --- AcceleratorsResource helpers. --- //
58022fccd64SMarek Sokolowski 
58122fccd64SMarek Sokolowski Error ResourceFileWriter::writeSingleAccelerator(
58222fccd64SMarek Sokolowski     const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) {
58322fccd64SMarek Sokolowski   using Accelerator = AcceleratorsResource::Accelerator;
58422fccd64SMarek Sokolowski   using Opt = Accelerator::Options;
58522fccd64SMarek Sokolowski 
58622fccd64SMarek Sokolowski   struct AccelTableEntry {
58722fccd64SMarek Sokolowski     ulittle16_t Flags;
58822fccd64SMarek Sokolowski     ulittle16_t ANSICode;
58922fccd64SMarek Sokolowski     ulittle16_t Id;
59022fccd64SMarek Sokolowski     uint16_t Padding;
59122fccd64SMarek Sokolowski   } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0};
59222fccd64SMarek Sokolowski 
59322fccd64SMarek Sokolowski   bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY;
59422fccd64SMarek Sokolowski 
59522fccd64SMarek Sokolowski   // Remove ASCII flags (which doesn't occur in .res files).
59622fccd64SMarek Sokolowski   Entry.Flags = Obj.Flags & ~Opt::ASCII;
59722fccd64SMarek Sokolowski 
59822fccd64SMarek Sokolowski   if (IsLastItem)
59922fccd64SMarek Sokolowski     Entry.Flags |= 0x80;
60022fccd64SMarek Sokolowski 
60122fccd64SMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"));
60222fccd64SMarek Sokolowski   Entry.Id = ulittle16_t(Obj.Id);
60322fccd64SMarek Sokolowski 
60422fccd64SMarek Sokolowski   auto createAccError = [&Obj](const char *Msg) {
60522fccd64SMarek Sokolowski     return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg);
60622fccd64SMarek Sokolowski   };
60722fccd64SMarek Sokolowski 
60822fccd64SMarek Sokolowski   if (IsASCII && IsVirtKey)
60922fccd64SMarek Sokolowski     return createAccError("Accelerator can't be both ASCII and VIRTKEY");
61022fccd64SMarek Sokolowski 
61122fccd64SMarek Sokolowski   if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL)))
61222fccd64SMarek Sokolowski     return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY"
61322fccd64SMarek Sokolowski                           " accelerators");
61422fccd64SMarek Sokolowski 
61522fccd64SMarek Sokolowski   if (Obj.Event.isInt()) {
61622fccd64SMarek Sokolowski     if (!IsASCII && !IsVirtKey)
61722fccd64SMarek Sokolowski       return createAccError(
61822fccd64SMarek Sokolowski           "Accelerator with a numeric event must be either ASCII"
61922fccd64SMarek Sokolowski           " or VIRTKEY");
62022fccd64SMarek Sokolowski 
62122fccd64SMarek Sokolowski     uint32_t EventVal = Obj.Event.getInt();
62222fccd64SMarek Sokolowski     RETURN_IF_ERROR(
62322fccd64SMarek Sokolowski         checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"));
62422fccd64SMarek Sokolowski     Entry.ANSICode = ulittle16_t(EventVal);
62522fccd64SMarek Sokolowski     writeObject(Entry);
62622fccd64SMarek Sokolowski     return Error::success();
62722fccd64SMarek Sokolowski   }
62822fccd64SMarek Sokolowski 
62922fccd64SMarek Sokolowski   StringRef Str = Obj.Event.getString();
63022fccd64SMarek Sokolowski   bool IsWide;
63122fccd64SMarek Sokolowski   stripQuotes(Str, IsWide);
63222fccd64SMarek Sokolowski 
63322fccd64SMarek Sokolowski   if (Str.size() == 0 || Str.size() > 2)
63422fccd64SMarek Sokolowski     return createAccError(
63522fccd64SMarek Sokolowski         "Accelerator string events should have length 1 or 2");
63622fccd64SMarek Sokolowski 
63722fccd64SMarek Sokolowski   if (Str[0] == '^') {
63822fccd64SMarek Sokolowski     if (Str.size() == 1)
63922fccd64SMarek Sokolowski       return createAccError("No character following '^' in accelerator event");
64022fccd64SMarek Sokolowski     if (IsVirtKey)
64122fccd64SMarek Sokolowski       return createAccError(
64222fccd64SMarek Sokolowski           "VIRTKEY accelerator events can't be preceded by '^'");
64322fccd64SMarek Sokolowski 
64422fccd64SMarek Sokolowski     char Ch = Str[1];
64522fccd64SMarek Sokolowski     if (Ch >= 'a' && Ch <= 'z')
64622fccd64SMarek Sokolowski       Entry.ANSICode = ulittle16_t(Ch - 'a' + 1);
64722fccd64SMarek Sokolowski     else if (Ch >= 'A' && Ch <= 'Z')
64822fccd64SMarek Sokolowski       Entry.ANSICode = ulittle16_t(Ch - 'A' + 1);
64922fccd64SMarek Sokolowski     else
65022fccd64SMarek Sokolowski       return createAccError("Control character accelerator event should be"
65122fccd64SMarek Sokolowski                             " alphabetic");
65222fccd64SMarek Sokolowski 
65322fccd64SMarek Sokolowski     writeObject(Entry);
65422fccd64SMarek Sokolowski     return Error::success();
65522fccd64SMarek Sokolowski   }
65622fccd64SMarek Sokolowski 
65722fccd64SMarek Sokolowski   if (Str.size() == 2)
65822fccd64SMarek Sokolowski     return createAccError("Event string should be one-character, possibly"
65922fccd64SMarek Sokolowski                           " preceded by '^'");
66022fccd64SMarek Sokolowski 
66122fccd64SMarek Sokolowski   uint8_t EventCh = Str[0];
66222fccd64SMarek Sokolowski   // The original tool just warns in this situation. We chose to fail.
66322fccd64SMarek Sokolowski   if (IsVirtKey && !isalnum(EventCh))
66422fccd64SMarek Sokolowski     return createAccError("Non-alphanumeric characters cannot describe virtual"
66522fccd64SMarek Sokolowski                           " keys");
66622fccd64SMarek Sokolowski   if (EventCh > 0x7F)
66722fccd64SMarek Sokolowski     return createAccError("Non-ASCII description of accelerator");
66822fccd64SMarek Sokolowski 
66922fccd64SMarek Sokolowski   if (IsVirtKey)
67022fccd64SMarek Sokolowski     EventCh = toupper(EventCh);
67122fccd64SMarek Sokolowski   Entry.ANSICode = ulittle16_t(EventCh);
67222fccd64SMarek Sokolowski   writeObject(Entry);
67322fccd64SMarek Sokolowski   return Error::success();
67422fccd64SMarek Sokolowski }
67522fccd64SMarek Sokolowski 
67622fccd64SMarek Sokolowski Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
67722fccd64SMarek Sokolowski   auto *Res = cast<AcceleratorsResource>(Base);
67822fccd64SMarek Sokolowski   size_t AcceleratorId = 0;
67922fccd64SMarek Sokolowski   for (auto &Acc : Res->Accelerators) {
68022fccd64SMarek Sokolowski     ++AcceleratorId;
68122fccd64SMarek Sokolowski     RETURN_IF_ERROR(
68222fccd64SMarek Sokolowski         writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size()));
68322fccd64SMarek Sokolowski   }
68422fccd64SMarek Sokolowski   return Error::success();
68522fccd64SMarek Sokolowski }
68622fccd64SMarek Sokolowski 
687c3ab013aSZachary Turner // --- CursorResource and IconResource helpers. --- //
688c3ab013aSZachary Turner 
689c3ab013aSZachary Turner // ICONRESDIR structure. Describes a single icon in resouce group.
690c3ab013aSZachary Turner //
691c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx
692c3ab013aSZachary Turner struct IconResDir {
693c3ab013aSZachary Turner   uint8_t Width;
694c3ab013aSZachary Turner   uint8_t Height;
695c3ab013aSZachary Turner   uint8_t ColorCount;
696c3ab013aSZachary Turner   uint8_t Reserved;
697c3ab013aSZachary Turner };
698c3ab013aSZachary Turner 
699c3ab013aSZachary Turner // CURSORDIR structure. Describes a single cursor in resource group.
700c3ab013aSZachary Turner //
701c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx
702c3ab013aSZachary Turner struct CursorDir {
703c3ab013aSZachary Turner   ulittle16_t Width;
704c3ab013aSZachary Turner   ulittle16_t Height;
705c3ab013aSZachary Turner };
706c3ab013aSZachary Turner 
707c3ab013aSZachary Turner // RESDIRENTRY structure, stripped from the last item. Stripping made
708c3ab013aSZachary Turner // for compatibility with RESDIR.
709c3ab013aSZachary Turner //
710c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx
711c3ab013aSZachary Turner struct ResourceDirEntryStart {
712c3ab013aSZachary Turner   union {
713c3ab013aSZachary Turner     CursorDir Cursor; // Used in CURSOR resources.
714c3ab013aSZachary Turner     IconResDir Icon;  // Used in .ico and .cur files, and ICON resources.
715c3ab013aSZachary Turner   };
716c3ab013aSZachary Turner   ulittle16_t Planes;   // HotspotX (.cur files but not CURSOR resource).
717c3ab013aSZachary Turner   ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource).
718c3ab013aSZachary Turner   ulittle32_t Size;
719c3ab013aSZachary Turner   // ulittle32_t ImageOffset;  // Offset to image data (ICONDIRENTRY only).
720c3ab013aSZachary Turner   // ulittle16_t IconID;       // Resource icon ID (RESDIR only).
721c3ab013aSZachary Turner };
722c3ab013aSZachary Turner 
723c3ab013aSZachary Turner // BITMAPINFOHEADER structure. Describes basic information about the bitmap
724c3ab013aSZachary Turner // being read.
725c3ab013aSZachary Turner //
726c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
727c3ab013aSZachary Turner struct BitmapInfoHeader {
728c3ab013aSZachary Turner   ulittle32_t Size;
729c3ab013aSZachary Turner   ulittle32_t Width;
730c3ab013aSZachary Turner   ulittle32_t Height;
731c3ab013aSZachary Turner   ulittle16_t Planes;
732c3ab013aSZachary Turner   ulittle16_t BitCount;
733c3ab013aSZachary Turner   ulittle32_t Compression;
734c3ab013aSZachary Turner   ulittle32_t SizeImage;
735c3ab013aSZachary Turner   ulittle32_t XPelsPerMeter;
736c3ab013aSZachary Turner   ulittle32_t YPelsPerMeter;
737c3ab013aSZachary Turner   ulittle32_t ClrUsed;
738c3ab013aSZachary Turner   ulittle32_t ClrImportant;
739c3ab013aSZachary Turner };
740c3ab013aSZachary Turner 
741c3ab013aSZachary Turner // Group icon directory header. Called ICONDIR in .ico/.cur files and
742c3ab013aSZachary Turner // NEWHEADER in .res files.
743c3ab013aSZachary Turner //
744c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx
745c3ab013aSZachary Turner struct GroupIconDir {
746c3ab013aSZachary Turner   ulittle16_t Reserved; // Always 0.
747c3ab013aSZachary Turner   ulittle16_t ResType;  // 1 for icons, 2 for cursors.
748c3ab013aSZachary Turner   ulittle16_t ResCount; // Number of items.
749c3ab013aSZachary Turner };
750c3ab013aSZachary Turner 
751c3ab013aSZachary Turner enum class IconCursorGroupType { Icon, Cursor };
752c3ab013aSZachary Turner 
753c3ab013aSZachary Turner class SingleIconCursorResource : public RCResource {
754c3ab013aSZachary Turner public:
755c3ab013aSZachary Turner   IconCursorGroupType Type;
756c3ab013aSZachary Turner   const ResourceDirEntryStart &Header;
757c3ab013aSZachary Turner   ArrayRef<uint8_t> Image;
758c3ab013aSZachary Turner 
759c3ab013aSZachary Turner   SingleIconCursorResource(IconCursorGroupType ResourceType,
760c3ab013aSZachary Turner                            const ResourceDirEntryStart &HeaderEntry,
761c3ab013aSZachary Turner                            ArrayRef<uint8_t> ImageData)
762c3ab013aSZachary Turner       : Type(ResourceType), Header(HeaderEntry), Image(ImageData) {}
763c3ab013aSZachary Turner 
764c3ab013aSZachary Turner   Twine getResourceTypeName() const override { return "Icon/cursor image"; }
765c3ab013aSZachary Turner   IntOrString getResourceType() const override {
766c3ab013aSZachary Turner     return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor;
767c3ab013aSZachary Turner   }
768c3ab013aSZachary Turner   uint16_t getMemoryFlags() const override {
769c3ab013aSZachary Turner     return MfDiscardable | MfMoveable;
770c3ab013aSZachary Turner   }
771c3ab013aSZachary Turner   ResourceKind getKind() const override { return RkSingleCursorOrIconRes; }
772c3ab013aSZachary Turner   static bool classof(const RCResource *Res) {
773c3ab013aSZachary Turner     return Res->getKind() == RkSingleCursorOrIconRes;
774c3ab013aSZachary Turner   }
775c3ab013aSZachary Turner };
776c3ab013aSZachary Turner 
777c3ab013aSZachary Turner class IconCursorGroupResource : public RCResource {
778c3ab013aSZachary Turner public:
779c3ab013aSZachary Turner   IconCursorGroupType Type;
780c3ab013aSZachary Turner   GroupIconDir Header;
781c3ab013aSZachary Turner   std::vector<ResourceDirEntryStart> ItemEntries;
782c3ab013aSZachary Turner 
783c3ab013aSZachary Turner   IconCursorGroupResource(IconCursorGroupType ResourceType,
784c3ab013aSZachary Turner                           const GroupIconDir &HeaderData,
785c3ab013aSZachary Turner                           std::vector<ResourceDirEntryStart> &&Entries)
786c3ab013aSZachary Turner       : Type(ResourceType), Header(HeaderData),
787c3ab013aSZachary Turner         ItemEntries(std::move(Entries)) {}
788c3ab013aSZachary Turner 
789c3ab013aSZachary Turner   Twine getResourceTypeName() const override { return "Icon/cursor group"; }
790c3ab013aSZachary Turner   IntOrString getResourceType() const override {
791c3ab013aSZachary Turner     return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup;
792c3ab013aSZachary Turner   }
793c3ab013aSZachary Turner   ResourceKind getKind() const override { return RkCursorOrIconGroupRes; }
794c3ab013aSZachary Turner   static bool classof(const RCResource *Res) {
795c3ab013aSZachary Turner     return Res->getKind() == RkCursorOrIconGroupRes;
796c3ab013aSZachary Turner   }
797c3ab013aSZachary Turner };
798c3ab013aSZachary Turner 
799c3ab013aSZachary Turner Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) {
800c3ab013aSZachary Turner   auto *Res = cast<SingleIconCursorResource>(Base);
801c3ab013aSZachary Turner   if (Res->Type == IconCursorGroupType::Cursor) {
802c3ab013aSZachary Turner     // In case of cursors, two WORDS are appended to the beginning
803c3ab013aSZachary Turner     // of the resource: HotspotX (Planes in RESDIRENTRY),
804c3ab013aSZachary Turner     // and HotspotY (BitCount).
805c3ab013aSZachary Turner     //
806c3ab013aSZachary Turner     // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx
807c3ab013aSZachary Turner     //  (Remarks section).
808c3ab013aSZachary Turner     writeObject(Res->Header.Planes);
809c3ab013aSZachary Turner     writeObject(Res->Header.BitCount);
810c3ab013aSZachary Turner   }
811c3ab013aSZachary Turner 
812c3ab013aSZachary Turner   writeObject(Res->Image);
813c3ab013aSZachary Turner   return Error::success();
814c3ab013aSZachary Turner }
815c3ab013aSZachary Turner 
816c3ab013aSZachary Turner Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) {
817c3ab013aSZachary Turner   auto *Res = cast<IconCursorGroupResource>(Base);
818c3ab013aSZachary Turner   writeObject(Res->Header);
819c3ab013aSZachary Turner   for (auto Item : Res->ItemEntries) {
820c3ab013aSZachary Turner     writeObject(Item);
821b12792a4SZachary Turner     writeInt(IconCursorID++);
822c3ab013aSZachary Turner   }
823c3ab013aSZachary Turner   return Error::success();
824c3ab013aSZachary Turner }
825c3ab013aSZachary Turner 
826c3ab013aSZachary Turner Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) {
827c3ab013aSZachary Turner   return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody);
828c3ab013aSZachary Turner }
829c3ab013aSZachary Turner 
830c3ab013aSZachary Turner Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) {
831c3ab013aSZachary Turner   return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody);
832c3ab013aSZachary Turner }
833c3ab013aSZachary Turner 
834c3ab013aSZachary Turner Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) {
835c3ab013aSZachary Turner   IconCursorGroupType Type;
836c3ab013aSZachary Turner   StringRef FileStr;
837c3ab013aSZachary Turner   IntOrString ResName = Base->ResName;
838c3ab013aSZachary Turner 
839c3ab013aSZachary Turner   if (auto *IconRes = dyn_cast<IconResource>(Base)) {
840c3ab013aSZachary Turner     FileStr = IconRes->IconLoc;
841c3ab013aSZachary Turner     Type = IconCursorGroupType::Icon;
842c3ab013aSZachary Turner   } else {
843c3ab013aSZachary Turner     auto *CursorRes = dyn_cast<CursorResource>(Base);
844c3ab013aSZachary Turner     FileStr = CursorRes->CursorLoc;
845c3ab013aSZachary Turner     Type = IconCursorGroupType::Cursor;
846c3ab013aSZachary Turner   }
847c3ab013aSZachary Turner 
848c3ab013aSZachary Turner   bool IsLong;
849c3ab013aSZachary Turner   stripQuotes(FileStr, IsLong);
850fa0ca6cbSZachary Turner   auto File = loadFile(FileStr);
851c3ab013aSZachary Turner 
852c3ab013aSZachary Turner   if (!File)
853fa0ca6cbSZachary Turner     return File.takeError();
854c3ab013aSZachary Turner 
855c3ab013aSZachary Turner   BinaryStreamReader Reader((*File)->getBuffer(), support::little);
856c3ab013aSZachary Turner 
857c3ab013aSZachary Turner   // Read the file headers.
858c3ab013aSZachary Turner   //   - At the beginning, ICONDIR/NEWHEADER header.
859c3ab013aSZachary Turner   //   - Then, a number of RESDIR headers follow. These contain offsets
860c3ab013aSZachary Turner   //       to data.
861c3ab013aSZachary Turner   const GroupIconDir *Header;
862c3ab013aSZachary Turner 
863c3ab013aSZachary Turner   RETURN_IF_ERROR(Reader.readObject(Header));
864c3ab013aSZachary Turner   if (Header->Reserved != 0)
865c3ab013aSZachary Turner     return createError("Incorrect icon/cursor Reserved field; should be 0.");
866c3ab013aSZachary Turner   uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2;
867c3ab013aSZachary Turner   if (Header->ResType != NeededType)
868c3ab013aSZachary Turner     return createError("Incorrect icon/cursor ResType field; should be " +
869c3ab013aSZachary Turner                        Twine(NeededType) + ".");
870c3ab013aSZachary Turner 
871c3ab013aSZachary Turner   uint16_t NumItems = Header->ResCount;
872c3ab013aSZachary Turner 
873c3ab013aSZachary Turner   // Read single ico/cur headers.
874c3ab013aSZachary Turner   std::vector<ResourceDirEntryStart> ItemEntries;
875c3ab013aSZachary Turner   ItemEntries.reserve(NumItems);
876c3ab013aSZachary Turner   std::vector<uint32_t> ItemOffsets(NumItems);
877c3ab013aSZachary Turner   for (size_t ID = 0; ID < NumItems; ++ID) {
878c3ab013aSZachary Turner     const ResourceDirEntryStart *Object;
879c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readObject(Object));
880c3ab013aSZachary Turner     ItemEntries.push_back(*Object);
881c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID]));
882c3ab013aSZachary Turner   }
883c3ab013aSZachary Turner 
884c3ab013aSZachary Turner   // Now write each icon/cursors one by one. At first, all the contents
885c3ab013aSZachary Turner   // without ICO/CUR header. This is described by SingleIconCursorResource.
886c3ab013aSZachary Turner   for (size_t ID = 0; ID < NumItems; ++ID) {
887c3ab013aSZachary Turner     // Load the fragment of file.
888c3ab013aSZachary Turner     Reader.setOffset(ItemOffsets[ID]);
889c3ab013aSZachary Turner     ArrayRef<uint8_t> Image;
890c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size));
891c3ab013aSZachary Turner     SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image);
892c3ab013aSZachary Turner     SingleRes.setName(IconCursorID + ID);
893c3ab013aSZachary Turner     RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes));
894c3ab013aSZachary Turner   }
895c3ab013aSZachary Turner 
896c3ab013aSZachary Turner   // Now, write all the headers concatenated into a separate resource.
897c3ab013aSZachary Turner   for (size_t ID = 0; ID < NumItems; ++ID) {
898c3ab013aSZachary Turner     if (Type == IconCursorGroupType::Icon) {
899c3ab013aSZachary Turner       // rc.exe seems to always set NumPlanes to 1. No idea why it happens.
900c3ab013aSZachary Turner       ItemEntries[ID].Planes = 1;
901c3ab013aSZachary Turner       continue;
902c3ab013aSZachary Turner     }
903c3ab013aSZachary Turner 
904c3ab013aSZachary Turner     // We need to rewrite the cursor headers.
905c3ab013aSZachary Turner     const auto &OldHeader = ItemEntries[ID];
906c3ab013aSZachary Turner     ResourceDirEntryStart NewHeader;
907c3ab013aSZachary Turner     NewHeader.Cursor.Width = OldHeader.Icon.Width;
908c3ab013aSZachary Turner     // Each cursor in fact stores two bitmaps, one under another.
909c3ab013aSZachary Turner     // Height provided in cursor definition describes the height of the
910c3ab013aSZachary Turner     // cursor, whereas the value existing in resource definition describes
911c3ab013aSZachary Turner     // the height of the bitmap. Therefore, we need to double this height.
912c3ab013aSZachary Turner     NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
913c3ab013aSZachary Turner 
914c3ab013aSZachary Turner     // Now, we actually need to read the bitmap header to find
915c3ab013aSZachary Turner     // the number of planes and the number of bits per pixel.
916c3ab013aSZachary Turner     Reader.setOffset(ItemOffsets[ID]);
917c3ab013aSZachary Turner     const BitmapInfoHeader *BMPHeader;
918c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readObject(BMPHeader));
919c3ab013aSZachary Turner     NewHeader.Planes = BMPHeader->Planes;
920c3ab013aSZachary Turner     NewHeader.BitCount = BMPHeader->BitCount;
921c3ab013aSZachary Turner 
922c3ab013aSZachary Turner     // Two WORDs were written at the beginning of the resource (hotspot
923c3ab013aSZachary Turner     // location). This is reflected in Size field.
924c3ab013aSZachary Turner     NewHeader.Size = OldHeader.Size + 2 * sizeof(uint16_t);
925c3ab013aSZachary Turner 
926c3ab013aSZachary Turner     ItemEntries[ID] = NewHeader;
927c3ab013aSZachary Turner   }
928c3ab013aSZachary Turner 
929c3ab013aSZachary Turner   IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries));
930c3ab013aSZachary Turner   HeaderRes.setName(ResName);
931c3ab013aSZachary Turner   RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes));
932c3ab013aSZachary Turner 
933c3ab013aSZachary Turner   return Error::success();
934c3ab013aSZachary Turner }
935c3ab013aSZachary Turner 
9367f7745c0SMarek Sokolowski // --- DialogResource helpers. --- //
93722fccd64SMarek Sokolowski 
9387f7745c0SMarek Sokolowski Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
9397f7745c0SMarek Sokolowski                                                    bool IsExtended) {
9407f7745c0SMarek Sokolowski   // Each control should be aligned to DWORD.
9417f7745c0SMarek Sokolowski   padStream(sizeof(uint32_t));
9427f7745c0SMarek Sokolowski 
9437f7745c0SMarek Sokolowski   auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
9447f7745c0SMarek Sokolowski   uint32_t CtlStyle = TypeInfo.Style | Ctl.Style.getValueOr(0);
9457f7745c0SMarek Sokolowski   uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
9467f7745c0SMarek Sokolowski 
9477f7745c0SMarek Sokolowski   // DIALOG(EX) item header prefix.
9487f7745c0SMarek Sokolowski   if (!IsExtended) {
9497f7745c0SMarek Sokolowski     struct {
9507f7745c0SMarek Sokolowski       ulittle32_t Style;
9517f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
9527f7745c0SMarek Sokolowski     } Prefix{ulittle32_t(CtlStyle), ulittle32_t(CtlExtStyle)};
9537f7745c0SMarek Sokolowski     writeObject(Prefix);
9547f7745c0SMarek Sokolowski   } else {
9557f7745c0SMarek Sokolowski     struct {
9567f7745c0SMarek Sokolowski       ulittle32_t HelpID;
9577f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
9587f7745c0SMarek Sokolowski       ulittle32_t Style;
9597f7745c0SMarek Sokolowski     } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
9607f7745c0SMarek Sokolowski              ulittle32_t(CtlStyle)};
9617f7745c0SMarek Sokolowski     writeObject(Prefix);
9627f7745c0SMarek Sokolowski   }
9637f7745c0SMarek Sokolowski 
9647f7745c0SMarek Sokolowski   // Common fixed-length part.
9657f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
9667f7745c0SMarek Sokolowski       Ctl.X, "Dialog control x-coordinate", true));
9677f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
9687f7745c0SMarek Sokolowski       Ctl.Y, "Dialog control y-coordinate", true));
9697f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
9707f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false));
9717f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
9727f7745c0SMarek Sokolowski       Ctl.Height, "Dialog control height", false));
9737f7745c0SMarek Sokolowski   struct {
9747f7745c0SMarek Sokolowski     ulittle16_t X;
9757f7745c0SMarek Sokolowski     ulittle16_t Y;
9767f7745c0SMarek Sokolowski     ulittle16_t Width;
9777f7745c0SMarek Sokolowski     ulittle16_t Height;
9787f7745c0SMarek Sokolowski   } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
9797f7745c0SMarek Sokolowski            ulittle16_t(Ctl.Height)};
9807f7745c0SMarek Sokolowski   writeObject(Middle);
9817f7745c0SMarek Sokolowski 
9827f7745c0SMarek Sokolowski   // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
9837f7745c0SMarek Sokolowski   if (!IsExtended) {
9847f7745c0SMarek Sokolowski     RETURN_IF_ERROR(checkNumberFits<uint16_t>(
9857f7745c0SMarek Sokolowski         Ctl.ID, "Control ID in simple DIALOG resource"));
9867f7745c0SMarek Sokolowski     writeInt<uint16_t>(Ctl.ID);
9877f7745c0SMarek Sokolowski   } else {
9887f7745c0SMarek Sokolowski     writeInt<uint32_t>(Ctl.ID);
9897f7745c0SMarek Sokolowski   }
9907f7745c0SMarek Sokolowski 
9917f7745c0SMarek Sokolowski   // Window class - either 0xFFFF + 16-bit integer or a string.
9927f7745c0SMarek Sokolowski   RETURN_IF_ERROR(writeIntOrString(IntOrString(TypeInfo.CtlClass)));
9937f7745c0SMarek Sokolowski 
9947f7745c0SMarek Sokolowski   // Element caption/reference ID. ID is preceded by 0xFFFF.
9957f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"));
9967f7745c0SMarek Sokolowski   RETURN_IF_ERROR(writeIntOrString(Ctl.Title));
9977f7745c0SMarek Sokolowski 
9987f7745c0SMarek Sokolowski   // # bytes of extra creation data count. Don't pass any.
9997f7745c0SMarek Sokolowski   writeInt<uint16_t>(0);
10007f7745c0SMarek Sokolowski 
10017f7745c0SMarek Sokolowski   return Error::success();
10027f7745c0SMarek Sokolowski }
10037f7745c0SMarek Sokolowski 
10047f7745c0SMarek Sokolowski Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
10057f7745c0SMarek Sokolowski   auto *Res = cast<DialogResource>(Base);
10067f7745c0SMarek Sokolowski 
10077f7745c0SMarek Sokolowski   // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
1008420090afSZachary Turner   const uint32_t DefaultStyle = 0x80880000;
1009420090afSZachary Turner   const uint32_t StyleFontFlag = 0x40;
1010420090afSZachary Turner   const uint32_t StyleCaptionFlag = 0x00C00000;
1011420090afSZachary Turner 
1012420090afSZachary Turner   uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle);
1013420090afSZachary Turner   if (ObjectData.Font)
1014420090afSZachary Turner     UsedStyle |= StyleFontFlag;
1015420090afSZachary Turner   else
1016420090afSZachary Turner     UsedStyle &= ~StyleFontFlag;
1017420090afSZachary Turner 
1018420090afSZachary Turner   // Actually, in case of empty (but existent) caption, the examined field
1019420090afSZachary Turner   // is equal to "\"\"". That's why empty captions are still noticed.
1020420090afSZachary Turner   if (ObjectData.Caption != "")
1021420090afSZachary Turner     UsedStyle |= StyleCaptionFlag;
1022420090afSZachary Turner 
1023420090afSZachary Turner   const uint16_t DialogExMagic = 0xFFFF;
10247f7745c0SMarek Sokolowski 
10257f7745c0SMarek Sokolowski   // Write DIALOG(EX) header prefix. These are pretty different.
10267f7745c0SMarek Sokolowski   if (!Res->IsExtended) {
1027420090afSZachary Turner     // We cannot let the higher word of DefaultStyle be equal to 0xFFFF.
1028420090afSZachary Turner     // In such a case, whole object (in .res file) is equivalent to a
1029420090afSZachary Turner     // DIALOGEX. It might lead to access violation/segmentation fault in
1030420090afSZachary Turner     // resource readers. For example,
1031420090afSZachary Turner     //   1 DIALOG 0, 0, 0, 65432
1032420090afSZachary Turner     //   STYLE 0xFFFF0001 {}
1033420090afSZachary Turner     // would be compiled to a DIALOGEX with 65432 controls.
1034420090afSZachary Turner     if ((UsedStyle >> 16) == DialogExMagic)
1035420090afSZachary Turner       return createError("16 higher bits of DIALOG resource style cannot be"
1036420090afSZachary Turner                          " equal to 0xFFFF");
1037420090afSZachary Turner 
10387f7745c0SMarek Sokolowski     struct {
10397f7745c0SMarek Sokolowski       ulittle32_t Style;
10407f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
10417f7745c0SMarek Sokolowski     } Prefix{ulittle32_t(UsedStyle),
10427f7745c0SMarek Sokolowski              ulittle32_t(0)}; // As of now, we don't keep EXSTYLE.
10437f7745c0SMarek Sokolowski 
10447f7745c0SMarek Sokolowski     writeObject(Prefix);
10457f7745c0SMarek Sokolowski   } else {
10467f7745c0SMarek Sokolowski     struct {
10477f7745c0SMarek Sokolowski       ulittle16_t Version;
10487f7745c0SMarek Sokolowski       ulittle16_t Magic;
10497f7745c0SMarek Sokolowski       ulittle32_t HelpID;
10507f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
10517f7745c0SMarek Sokolowski       ulittle32_t Style;
10527f7745c0SMarek Sokolowski     } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
10537f7745c0SMarek Sokolowski              ulittle32_t(Res->HelpID), ulittle32_t(0), ulittle32_t(UsedStyle)};
10547f7745c0SMarek Sokolowski 
10557f7745c0SMarek Sokolowski     writeObject(Prefix);
10567f7745c0SMarek Sokolowski   }
10577f7745c0SMarek Sokolowski 
10587f7745c0SMarek Sokolowski   // Now, a common part. First, fixed-length fields.
10597f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),
10607f7745c0SMarek Sokolowski                                             "Number of dialog controls"));
10617f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
10627f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true));
10637f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
10647f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true));
10657f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
10667f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false));
10677f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
10687f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false));
10697f7745c0SMarek Sokolowski   struct {
10707f7745c0SMarek Sokolowski     ulittle16_t Count;
10717f7745c0SMarek Sokolowski     ulittle16_t PosX;
10727f7745c0SMarek Sokolowski     ulittle16_t PosY;
10737f7745c0SMarek Sokolowski     ulittle16_t DialogWidth;
10747f7745c0SMarek Sokolowski     ulittle16_t DialogHeight;
10757f7745c0SMarek Sokolowski   } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
10767f7745c0SMarek Sokolowski            ulittle16_t(Res->Y), ulittle16_t(Res->Width),
10777f7745c0SMarek Sokolowski            ulittle16_t(Res->Height)};
10787f7745c0SMarek Sokolowski   writeObject(Middle);
10797f7745c0SMarek Sokolowski 
10807f7745c0SMarek Sokolowski   // MENU field. As of now, we don't keep them in the state and can peacefully
10817f7745c0SMarek Sokolowski   // think there is no menu attached to the dialog.
10827f7745c0SMarek Sokolowski   writeInt<uint16_t>(0);
10837f7745c0SMarek Sokolowski 
10847f7745c0SMarek Sokolowski   // Window CLASS field. Not kept here.
10857f7745c0SMarek Sokolowski   writeInt<uint16_t>(0);
10867f7745c0SMarek Sokolowski 
1087420090afSZachary Turner   // Window title or a single word equal to 0.
1088420090afSZachary Turner   RETURN_IF_ERROR(writeCString(ObjectData.Caption));
1089420090afSZachary Turner 
1090420090afSZachary Turner   // If there *is* a window font declared, output its data.
1091420090afSZachary Turner   auto &Font = ObjectData.Font;
1092420090afSZachary Turner   if (Font) {
1093420090afSZachary Turner     writeInt<uint16_t>(Font->Size);
1094420090afSZachary Turner     // Additional description occurs only in DIALOGEX.
1095420090afSZachary Turner     if (Res->IsExtended) {
1096420090afSZachary Turner       writeInt<uint16_t>(Font->Weight);
1097420090afSZachary Turner       writeInt<uint8_t>(Font->IsItalic);
1098420090afSZachary Turner       writeInt<uint8_t>(Font->Charset);
1099420090afSZachary Turner     }
1100420090afSZachary Turner     RETURN_IF_ERROR(writeCString(Font->Typeface));
1101420090afSZachary Turner   }
11027f7745c0SMarek Sokolowski 
11037f7745c0SMarek Sokolowski   auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
11047f7745c0SMarek Sokolowski     if (!Err)
11057f7745c0SMarek Sokolowski       return Error::success();
11067f7745c0SMarek Sokolowski     return joinErrors(createError("Error in " + Twine(Ctl.Type) +
11077f7745c0SMarek Sokolowski                                   " control  (ID " + Twine(Ctl.ID) + "):"),
11087f7745c0SMarek Sokolowski                       std::move(Err));
11097f7745c0SMarek Sokolowski   };
11107f7745c0SMarek Sokolowski 
11117f7745c0SMarek Sokolowski   for (auto &Ctl : Res->Controls)
11127f7745c0SMarek Sokolowski     RETURN_IF_ERROR(
11137f7745c0SMarek Sokolowski         handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl));
11147f7745c0SMarek Sokolowski 
11157f7745c0SMarek Sokolowski   return Error::success();
11167f7745c0SMarek Sokolowski }
11177f7745c0SMarek Sokolowski 
11187f7745c0SMarek Sokolowski // --- HTMLResource helpers. --- //
111922fccd64SMarek Sokolowski 
11208f19343aSMarek Sokolowski Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
11218f19343aSMarek Sokolowski   return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
11228f19343aSMarek Sokolowski }
11238f19343aSMarek Sokolowski 
112442f494d6SMarek Sokolowski // --- MenuResource helpers. --- //
112542f494d6SMarek Sokolowski 
112642f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuDefinition(
112742f494d6SMarek Sokolowski     const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
112842f494d6SMarek Sokolowski   assert(Def);
112942f494d6SMarek Sokolowski   const MenuDefinition *DefPtr = Def.get();
113042f494d6SMarek Sokolowski 
113142f494d6SMarek Sokolowski   if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
113242f494d6SMarek Sokolowski     writeInt<uint16_t>(Flags);
113342f494d6SMarek Sokolowski     RETURN_IF_ERROR(
113442f494d6SMarek Sokolowski         checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"));
113542f494d6SMarek Sokolowski     writeInt<uint16_t>(MenuItemPtr->Id);
113642f494d6SMarek Sokolowski     RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
113742f494d6SMarek Sokolowski     return Error::success();
113842f494d6SMarek Sokolowski   }
113942f494d6SMarek Sokolowski 
114042f494d6SMarek Sokolowski   if (isa<MenuSeparator>(DefPtr)) {
114142f494d6SMarek Sokolowski     writeInt<uint16_t>(Flags);
114242f494d6SMarek Sokolowski     writeInt<uint32_t>(0);
114342f494d6SMarek Sokolowski     return Error::success();
114442f494d6SMarek Sokolowski   }
114542f494d6SMarek Sokolowski 
114642f494d6SMarek Sokolowski   auto *PopupPtr = cast<PopupItem>(DefPtr);
114742f494d6SMarek Sokolowski   writeInt<uint16_t>(Flags);
114842f494d6SMarek Sokolowski   RETURN_IF_ERROR(writeCString(PopupPtr->Name));
114942f494d6SMarek Sokolowski   return writeMenuDefinitionList(PopupPtr->SubItems);
115042f494d6SMarek Sokolowski }
115142f494d6SMarek Sokolowski 
115242f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuDefinitionList(
115342f494d6SMarek Sokolowski     const MenuDefinitionList &List) {
115442f494d6SMarek Sokolowski   for (auto &Def : List.Definitions) {
115542f494d6SMarek Sokolowski     uint16_t Flags = Def->getResFlags();
115642f494d6SMarek Sokolowski     // Last element receives an additional 0x80 flag.
115742f494d6SMarek Sokolowski     const uint16_t LastElementFlag = 0x0080;
115842f494d6SMarek Sokolowski     if (&Def == &List.Definitions.back())
115942f494d6SMarek Sokolowski       Flags |= LastElementFlag;
116042f494d6SMarek Sokolowski 
116142f494d6SMarek Sokolowski     RETURN_IF_ERROR(writeMenuDefinition(Def, Flags));
116242f494d6SMarek Sokolowski   }
116342f494d6SMarek Sokolowski   return Error::success();
116442f494d6SMarek Sokolowski }
116542f494d6SMarek Sokolowski 
116642f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
116742f494d6SMarek Sokolowski   // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
116842f494d6SMarek Sokolowski   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
11691e2c8b11SZachary Turner   writeInt<uint32_t>(0);
117042f494d6SMarek Sokolowski 
117142f494d6SMarek Sokolowski   return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
117242f494d6SMarek Sokolowski }
117342f494d6SMarek Sokolowski 
1174da366693SZachary Turner // --- StringTableResource helpers. --- //
1175da366693SZachary Turner 
1176da366693SZachary Turner class BundleResource : public RCResource {
1177da366693SZachary Turner public:
1178da366693SZachary Turner   using BundleType = ResourceFileWriter::StringTableInfo::Bundle;
1179da366693SZachary Turner   BundleType Bundle;
1180da366693SZachary Turner 
1181da366693SZachary Turner   BundleResource(const BundleType &StrBundle) : Bundle(StrBundle) {}
1182da366693SZachary Turner   IntOrString getResourceType() const override { return 6; }
1183da366693SZachary Turner 
1184da366693SZachary Turner   ResourceKind getKind() const override { return RkStringTableBundle; }
1185da366693SZachary Turner   static bool classof(const RCResource *Res) {
1186da366693SZachary Turner     return Res->getKind() == RkStringTableBundle;
1187da366693SZachary Turner   }
1188d1d046aaSMartin Storsjo   Twine getResourceTypeName() const override { return "STRINGTABLE"; }
1189da366693SZachary Turner };
1190da366693SZachary Turner 
1191da366693SZachary Turner Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) {
1192da366693SZachary Turner   return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody);
1193da366693SZachary Turner }
1194da366693SZachary Turner 
1195da366693SZachary Turner Error ResourceFileWriter::insertStringIntoBundle(
1196da366693SZachary Turner     StringTableInfo::Bundle &Bundle, uint16_t StringID, StringRef String) {
1197da366693SZachary Turner   uint16_t StringLoc = StringID & 15;
1198da366693SZachary Turner   if (Bundle.Data[StringLoc])
1199da366693SZachary Turner     return createError("Multiple STRINGTABLE strings located under ID " +
1200da366693SZachary Turner                        Twine(StringID));
1201da366693SZachary Turner   Bundle.Data[StringLoc] = String;
1202da366693SZachary Turner   return Error::success();
1203da366693SZachary Turner }
1204da366693SZachary Turner 
1205da366693SZachary Turner Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) {
1206da366693SZachary Turner   auto *Res = cast<BundleResource>(Base);
1207da366693SZachary Turner   for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) {
1208da366693SZachary Turner     // The string format is a tiny bit different here. We
1209da366693SZachary Turner     // first output the size of the string, and then the string itself
1210da366693SZachary Turner     // (which is not null-terminated).
1211da366693SZachary Turner     bool IsLongString;
1212da366693SZachary Turner     SmallVector<UTF16, 128> Data;
1213da366693SZachary Turner     RETURN_IF_ERROR(processString(Res->Bundle.Data[ID].getValueOr(StringRef()),
1214da366693SZachary Turner                                   NullHandlingMethod::CutAtDoubleNull,
1215d1d046aaSMartin Storsjo                                   IsLongString, Data, Params.CodePage));
1216da366693SZachary Turner     if (AppendNull && Res->Bundle.Data[ID])
1217da366693SZachary Turner       Data.push_back('\0');
1218da366693SZachary Turner     RETURN_IF_ERROR(
1219da366693SZachary Turner         checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"));
1220b12792a4SZachary Turner     writeInt<uint16_t>(Data.size());
1221da366693SZachary Turner     for (auto Char : Data)
1222b12792a4SZachary Turner       writeInt(Char);
1223da366693SZachary Turner   }
1224da366693SZachary Turner   return Error::success();
1225da366693SZachary Turner }
1226da366693SZachary Turner 
1227da366693SZachary Turner Error ResourceFileWriter::dumpAllStringTables() {
1228da366693SZachary Turner   for (auto Key : StringTableData.BundleList) {
1229da366693SZachary Turner     auto Iter = StringTableData.BundleData.find(Key);
1230da366693SZachary Turner     assert(Iter != StringTableData.BundleData.end());
1231da366693SZachary Turner 
1232da366693SZachary Turner     // For a moment, revert the context info to moment of bundle declaration.
1233da366693SZachary Turner     ContextKeeper RAII(this);
1234da366693SZachary Turner     ObjectData = Iter->second.DeclTimeInfo;
1235da366693SZachary Turner 
1236da366693SZachary Turner     BundleResource Res(Iter->second);
1237da366693SZachary Turner     // Bundle #(k+1) contains keys [16k, 16k + 15].
1238da366693SZachary Turner     Res.setName(Key.first + 1);
1239da366693SZachary Turner     RETURN_IF_ERROR(visitStringTableBundle(&Res));
1240da366693SZachary Turner   }
1241da366693SZachary Turner   return Error::success();
1242da366693SZachary Turner }
1243da366693SZachary Turner 
12449d8b358aSZachary Turner // --- UserDefinedResource helpers. --- //
12459d8b358aSZachary Turner 
12469d8b358aSZachary Turner Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) {
12479d8b358aSZachary Turner   auto *Res = cast<UserDefinedResource>(Base);
12489d8b358aSZachary Turner 
12499d8b358aSZachary Turner   if (Res->IsFileResource)
12509d8b358aSZachary Turner     return appendFile(Res->FileLoc);
12519d8b358aSZachary Turner 
12529d8b358aSZachary Turner   for (auto &Elem : Res->Contents) {
12539d8b358aSZachary Turner     if (Elem.isInt()) {
12549d8b358aSZachary Turner       RETURN_IF_ERROR(
12559d8b358aSZachary Turner           checkRCInt(Elem.getInt(), "Number in user-defined resource"));
12569d8b358aSZachary Turner       writeRCInt(Elem.getInt());
12579d8b358aSZachary Turner       continue;
12589d8b358aSZachary Turner     }
12599d8b358aSZachary Turner 
12609d8b358aSZachary Turner     SmallVector<UTF16, 128> ProcessedString;
12619d8b358aSZachary Turner     bool IsLongString;
1262d1d046aaSMartin Storsjo     RETURN_IF_ERROR(
1263d1d046aaSMartin Storsjo         processString(Elem.getString(), NullHandlingMethod::UserResource,
1264d1d046aaSMartin Storsjo                       IsLongString, ProcessedString, Params.CodePage));
12659d8b358aSZachary Turner 
12669d8b358aSZachary Turner     for (auto Ch : ProcessedString) {
12679d8b358aSZachary Turner       if (IsLongString) {
1268b12792a4SZachary Turner         writeInt(Ch);
12699d8b358aSZachary Turner         continue;
12709d8b358aSZachary Turner       }
12719d8b358aSZachary Turner 
12729d8b358aSZachary Turner       RETURN_IF_ERROR(checkNumberFits<uint8_t>(
127375f9ae2bSSimon Dardis           Ch, "Character in narrow string in user-defined resource"));
1274b12792a4SZachary Turner       writeInt<uint8_t>(Ch);
12759d8b358aSZachary Turner     }
12769d8b358aSZachary Turner   }
12779d8b358aSZachary Turner 
12789d8b358aSZachary Turner   return Error::success();
12799d8b358aSZachary Turner }
12809d8b358aSZachary Turner 
128107bc04ffSZachary Turner // --- VersionInfoResourceResource helpers. --- //
128207bc04ffSZachary Turner 
128307bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
128407bc04ffSZachary Turner   // Output the header if the block has name.
128507bc04ffSZachary Turner   bool OutputHeader = Blk.Name != "";
128607bc04ffSZachary Turner   uint64_t LengthLoc;
128707bc04ffSZachary Turner 
1288*28ae894aSMartin Storsjo   padStream(sizeof(uint32_t));
128907bc04ffSZachary Turner   if (OutputHeader) {
12901e2c8b11SZachary Turner     LengthLoc = writeInt<uint16_t>(0);
12911e2c8b11SZachary Turner     writeInt<uint16_t>(0);
12921e2c8b11SZachary Turner     writeInt<uint16_t>(1); // true
129307bc04ffSZachary Turner     RETURN_IF_ERROR(writeCString(Blk.Name));
129407bc04ffSZachary Turner     padStream(sizeof(uint32_t));
129507bc04ffSZachary Turner   }
129607bc04ffSZachary Turner 
129707bc04ffSZachary Turner   for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) {
129807bc04ffSZachary Turner     VersionInfoStmt *ItemPtr = Item.get();
129907bc04ffSZachary Turner 
130007bc04ffSZachary Turner     if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) {
130107bc04ffSZachary Turner       RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr));
130207bc04ffSZachary Turner       continue;
130307bc04ffSZachary Turner     }
130407bc04ffSZachary Turner 
130507bc04ffSZachary Turner     auto *ValuePtr = cast<VersionInfoValue>(ItemPtr);
130607bc04ffSZachary Turner     RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr));
130707bc04ffSZachary Turner   }
130807bc04ffSZachary Turner 
130907bc04ffSZachary Turner   if (OutputHeader) {
131007bc04ffSZachary Turner     uint64_t CurLoc = tell();
131107bc04ffSZachary Turner     writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
131207bc04ffSZachary Turner   }
131307bc04ffSZachary Turner 
131407bc04ffSZachary Turner   return Error::success();
131507bc04ffSZachary Turner }
131607bc04ffSZachary Turner 
131707bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
131807bc04ffSZachary Turner   // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE
131907bc04ffSZachary Turner   // is a mapping from the key (string) to the value (a sequence of ints or
132007bc04ffSZachary Turner   // a sequence of strings).
132107bc04ffSZachary Turner   //
132207bc04ffSZachary Turner   // If integers are to be written: width of each integer written depends on
132307bc04ffSZachary Turner   // whether it's been declared 'long' (it's DWORD then) or not (it's WORD).
132407bc04ffSZachary Turner   // ValueLength defined in structure referenced below is then the total
132507bc04ffSZachary Turner   // number of bytes taken by these integers.
132607bc04ffSZachary Turner   //
132707bc04ffSZachary Turner   // If strings are to be written: characters are always WORDs.
132807bc04ffSZachary Turner   // Moreover, '\0' character is written after the last string, and between
132907bc04ffSZachary Turner   // every two strings separated by comma (if strings are not comma-separated,
133007bc04ffSZachary Turner   // they're simply concatenated). ValueLength is equal to the number of WORDs
133107bc04ffSZachary Turner   // written (that is, half of the bytes written).
133207bc04ffSZachary Turner   //
133307bc04ffSZachary Turner   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx
133407bc04ffSZachary Turner   bool HasStrings = false, HasInts = false;
133507bc04ffSZachary Turner   for (auto &Item : Val.Values)
133607bc04ffSZachary Turner     (Item.isInt() ? HasInts : HasStrings) = true;
133707bc04ffSZachary Turner 
133807bc04ffSZachary Turner   assert((HasStrings || HasInts) && "VALUE must have at least one argument");
133907bc04ffSZachary Turner   if (HasStrings && HasInts)
134007bc04ffSZachary Turner     return createError(Twine("VALUE ") + Val.Key +
134107bc04ffSZachary Turner                        " cannot contain both strings and integers");
134207bc04ffSZachary Turner 
1343*28ae894aSMartin Storsjo   padStream(sizeof(uint32_t));
13441e2c8b11SZachary Turner   auto LengthLoc = writeInt<uint16_t>(0);
13451e2c8b11SZachary Turner   auto ValLengthLoc = writeInt<uint16_t>(0);
13461e2c8b11SZachary Turner   writeInt<uint16_t>(HasStrings);
134707bc04ffSZachary Turner   RETURN_IF_ERROR(writeCString(Val.Key));
134807bc04ffSZachary Turner   padStream(sizeof(uint32_t));
134907bc04ffSZachary Turner 
135007bc04ffSZachary Turner   auto DataLoc = tell();
135107bc04ffSZachary Turner   for (size_t Id = 0; Id < Val.Values.size(); ++Id) {
135207bc04ffSZachary Turner     auto &Item = Val.Values[Id];
135307bc04ffSZachary Turner     if (Item.isInt()) {
135407bc04ffSZachary Turner       auto Value = Item.getInt();
135507bc04ffSZachary Turner       RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value"));
135607bc04ffSZachary Turner       writeRCInt(Value);
135707bc04ffSZachary Turner       continue;
135807bc04ffSZachary Turner     }
135907bc04ffSZachary Turner 
136007bc04ffSZachary Turner     bool WriteTerminator =
136107bc04ffSZachary Turner         Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1];
136207bc04ffSZachary Turner     RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator));
136307bc04ffSZachary Turner   }
136407bc04ffSZachary Turner 
136507bc04ffSZachary Turner   auto CurLoc = tell();
136607bc04ffSZachary Turner   auto ValueLength = CurLoc - DataLoc;
136707bc04ffSZachary Turner   if (HasStrings) {
136807bc04ffSZachary Turner     assert(ValueLength % 2 == 0);
136907bc04ffSZachary Turner     ValueLength /= 2;
137007bc04ffSZachary Turner   }
137107bc04ffSZachary Turner   writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
137207bc04ffSZachary Turner   writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
137307bc04ffSZachary Turner   return Error::success();
137407bc04ffSZachary Turner }
137507bc04ffSZachary Turner 
137607bc04ffSZachary Turner template <typename Ty>
137707bc04ffSZachary Turner static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key,
137807bc04ffSZachary Turner                          const Ty &Default) {
137907bc04ffSZachary Turner   auto Iter = Map.find(Key);
138007bc04ffSZachary Turner   if (Iter != Map.end())
138107bc04ffSZachary Turner     return Iter->getValue();
138207bc04ffSZachary Turner   return Default;
138307bc04ffSZachary Turner }
138407bc04ffSZachary Turner 
138507bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) {
138607bc04ffSZachary Turner   auto *Res = cast<VersionInfoResource>(Base);
138707bc04ffSZachary Turner 
138807bc04ffSZachary Turner   const auto &FixedData = Res->FixedData;
138907bc04ffSZachary Turner 
139007bc04ffSZachary Turner   struct /* VS_FIXEDFILEINFO */ {
139107bc04ffSZachary Turner     ulittle32_t Signature = ulittle32_t(0xFEEF04BD);
139207bc04ffSZachary Turner     ulittle32_t StructVersion = ulittle32_t(0x10000);
139307bc04ffSZachary Turner     // It's weird to have most-significant DWORD first on the little-endian
139407bc04ffSZachary Turner     // machines, but let it be this way.
139507bc04ffSZachary Turner     ulittle32_t FileVersionMS;
139607bc04ffSZachary Turner     ulittle32_t FileVersionLS;
139707bc04ffSZachary Turner     ulittle32_t ProductVersionMS;
139807bc04ffSZachary Turner     ulittle32_t ProductVersionLS;
139907bc04ffSZachary Turner     ulittle32_t FileFlagsMask;
140007bc04ffSZachary Turner     ulittle32_t FileFlags;
140107bc04ffSZachary Turner     ulittle32_t FileOS;
140207bc04ffSZachary Turner     ulittle32_t FileType;
140307bc04ffSZachary Turner     ulittle32_t FileSubtype;
140407bc04ffSZachary Turner     // MS implementation seems to always set these fields to 0.
140507bc04ffSZachary Turner     ulittle32_t FileDateMS = ulittle32_t(0);
140607bc04ffSZachary Turner     ulittle32_t FileDateLS = ulittle32_t(0);
140707bc04ffSZachary Turner   } FixedInfo;
140807bc04ffSZachary Turner 
140907bc04ffSZachary Turner   // First, VS_VERSIONINFO.
1410b12792a4SZachary Turner   auto LengthLoc = writeInt<uint16_t>(0);
1411b12792a4SZachary Turner   writeInt<uint16_t>(sizeof(FixedInfo));
1412b12792a4SZachary Turner   writeInt<uint16_t>(0);
141307bc04ffSZachary Turner   cantFail(writeCString("VS_VERSION_INFO"));
141407bc04ffSZachary Turner   padStream(sizeof(uint32_t));
141507bc04ffSZachary Turner 
141607bc04ffSZachary Turner   using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
141707bc04ffSZachary Turner   auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) {
141807bc04ffSZachary Turner     static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0};
141907bc04ffSZachary Turner     if (!FixedData.IsTypePresent[(int)Type])
142007bc04ffSZachary Turner       return DefaultOut;
142107bc04ffSZachary Turner     return FixedData.FixedInfo[(int)Type];
142207bc04ffSZachary Turner   };
142307bc04ffSZachary Turner 
142407bc04ffSZachary Turner   auto FileVer = GetField(VersionInfoFixed::FtFileVersion);
142507bc04ffSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint16_t>(
142607bc04ffSZachary Turner       *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields"));
142707bc04ffSZachary Turner   FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1];
142807bc04ffSZachary Turner   FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3];
142907bc04ffSZachary Turner 
143007bc04ffSZachary Turner   auto ProdVer = GetField(VersionInfoFixed::FtProductVersion);
143107bc04ffSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint16_t>(
143207bc04ffSZachary Turner       *std::max_element(ProdVer.begin(), ProdVer.end()),
143307bc04ffSZachary Turner       "PRODUCTVERSION fields"));
143407bc04ffSZachary Turner   FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1];
143507bc04ffSZachary Turner   FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3];
143607bc04ffSZachary Turner 
143707bc04ffSZachary Turner   FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0];
143807bc04ffSZachary Turner   FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0];
143907bc04ffSZachary Turner   FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0];
144007bc04ffSZachary Turner   FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0];
144107bc04ffSZachary Turner   FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0];
144207bc04ffSZachary Turner 
144307bc04ffSZachary Turner   writeObject(FixedInfo);
144407bc04ffSZachary Turner   padStream(sizeof(uint32_t));
144507bc04ffSZachary Turner 
144607bc04ffSZachary Turner   RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock));
144707bc04ffSZachary Turner 
144807bc04ffSZachary Turner   // FIXME: check overflow?
144907bc04ffSZachary Turner   writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc);
145007bc04ffSZachary Turner 
145107bc04ffSZachary Turner   return Error::success();
145207bc04ffSZachary Turner }
145307bc04ffSZachary Turner 
1454fa0ca6cbSZachary Turner Expected<std::unique_ptr<MemoryBuffer>>
1455fa0ca6cbSZachary Turner ResourceFileWriter::loadFile(StringRef File) const {
1456fa0ca6cbSZachary Turner   SmallString<128> Path;
1457fa0ca6cbSZachary Turner   SmallString<128> Cwd;
1458fa0ca6cbSZachary Turner   std::unique_ptr<MemoryBuffer> Result;
1459fa0ca6cbSZachary Turner 
1460fa0ca6cbSZachary Turner   // 1. The current working directory.
1461fa0ca6cbSZachary Turner   sys::fs::current_path(Cwd);
1462fa0ca6cbSZachary Turner   Path.assign(Cwd.begin(), Cwd.end());
1463fa0ca6cbSZachary Turner   sys::path::append(Path, File);
1464fa0ca6cbSZachary Turner   if (sys::fs::exists(Path))
1465f22a25e6SZachary Turner     return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1466fa0ca6cbSZachary Turner 
1467fa0ca6cbSZachary Turner   // 2. The directory of the input resource file, if it is different from the
1468fa0ca6cbSZachary Turner   // current
1469fa0ca6cbSZachary Turner   //    working directory.
1470fa0ca6cbSZachary Turner   StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath);
1471fa0ca6cbSZachary Turner   Path.assign(InputFileDir.begin(), InputFileDir.end());
1472fa0ca6cbSZachary Turner   sys::path::append(Path, File);
1473fa0ca6cbSZachary Turner   if (sys::fs::exists(Path))
1474f22a25e6SZachary Turner     return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1475fa0ca6cbSZachary Turner 
1476fa0ca6cbSZachary Turner   // 3. All of the include directories specified on the command line.
1477fa0ca6cbSZachary Turner   for (StringRef ForceInclude : Params.Include) {
1478fa0ca6cbSZachary Turner     Path.assign(ForceInclude.begin(), ForceInclude.end());
1479fa0ca6cbSZachary Turner     sys::path::append(Path, File);
1480fa0ca6cbSZachary Turner     if (sys::fs::exists(Path))
1481f22a25e6SZachary Turner       return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1482fa0ca6cbSZachary Turner   }
1483fa0ca6cbSZachary Turner 
1484fa0ca6cbSZachary Turner   if (auto Result =
1485fa0ca6cbSZachary Turner           llvm::sys::Process::FindInEnvPath("INCLUDE", File, Params.NoInclude))
1486f22a25e6SZachary Turner     return errorOrToExpected(MemoryBuffer::getFile(*Result, -1, false));
1487fa0ca6cbSZachary Turner 
1488fa0ca6cbSZachary Turner   return make_error<StringError>("error : file not found : " + Twine(File),
1489fa0ca6cbSZachary Turner                                  inconvertibleErrorCode());
1490fa0ca6cbSZachary Turner }
1491fa0ca6cbSZachary Turner 
14928f19343aSMarek Sokolowski } // namespace rc
14938f19343aSMarek Sokolowski } // namespace llvm
1494