18f19343aSMarek Sokolowski //===-- ResourceFileWriter.cpp --------------------------------*- C++-*-===//
28f19343aSMarek Sokolowski //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68f19343aSMarek Sokolowski //
78f19343aSMarek Sokolowski //===---------------------------------------------------------------------===//
88f19343aSMarek Sokolowski //
98f19343aSMarek Sokolowski // This implements the visitor serializing resources to a .res stream.
108f19343aSMarek Sokolowski //
118f19343aSMarek Sokolowski //===---------------------------------------------------------------------===//
128f19343aSMarek Sokolowski 
138f19343aSMarek Sokolowski #include "ResourceFileWriter.h"
148f19343aSMarek Sokolowski #include "llvm/Object/WindowsResource.h"
158f19343aSMarek Sokolowski #include "llvm/Support/ConvertUTF.h"
168f19343aSMarek Sokolowski #include "llvm/Support/Endian.h"
178f19343aSMarek Sokolowski #include "llvm/Support/EndianStream.h"
180ea52537SSimon Pilgrim #include "llvm/Support/FileSystem.h"
19bd159d32SZachary Turner #include "llvm/Support/MemoryBuffer.h"
20fa0ca6cbSZachary Turner #include "llvm/Support/Path.h"
21fa0ca6cbSZachary Turner #include "llvm/Support/Process.h"
228f19343aSMarek Sokolowski 
238f19343aSMarek Sokolowski using namespace llvm::support;
248f19343aSMarek Sokolowski 
258f19343aSMarek Sokolowski // Take an expression returning llvm::Error and forward the error if it exists.
268f19343aSMarek Sokolowski #define RETURN_IF_ERROR(Expr)                                                  \
278f19343aSMarek Sokolowski   if (auto Err = (Expr))                                                       \
288f19343aSMarek Sokolowski     return Err;
298f19343aSMarek Sokolowski 
308f19343aSMarek Sokolowski namespace llvm {
318f19343aSMarek Sokolowski namespace rc {
328f19343aSMarek Sokolowski 
3307bc04ffSZachary Turner // Class that employs RAII to save the current FileWriter object state
3422fccd64SMarek Sokolowski // and revert to it as soon as we leave the scope. This is useful if resources
3522fccd64SMarek Sokolowski // declare their own resource-local statements.
3622fccd64SMarek Sokolowski class ContextKeeper {
3722fccd64SMarek Sokolowski   ResourceFileWriter *FileWriter;
3822fccd64SMarek Sokolowski   ResourceFileWriter::ObjectInfo SavedInfo;
3922fccd64SMarek Sokolowski 
4022fccd64SMarek Sokolowski public:
4122fccd64SMarek Sokolowski   ContextKeeper(ResourceFileWriter *V)
4222fccd64SMarek Sokolowski       : FileWriter(V), SavedInfo(V->ObjectData) {}
4322fccd64SMarek Sokolowski   ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; }
4422fccd64SMarek Sokolowski };
4522fccd64SMarek Sokolowski 
46fa0ca6cbSZachary Turner static Error createError(const Twine &Message,
478f19343aSMarek Sokolowski                          std::errc Type = std::errc::invalid_argument) {
488f19343aSMarek Sokolowski   return make_error<StringError>(Message, std::make_error_code(Type));
498f19343aSMarek Sokolowski }
508f19343aSMarek Sokolowski 
51fa0ca6cbSZachary Turner static Error checkNumberFits(uint32_t Number, size_t MaxBits,
52fa0ca6cbSZachary Turner                              const Twine &FieldName) {
538f19343aSMarek Sokolowski   assert(1 <= MaxBits && MaxBits <= 32);
548f19343aSMarek Sokolowski   if (!(Number >> MaxBits))
558f19343aSMarek Sokolowski     return Error::success();
568f19343aSMarek Sokolowski   return createError(FieldName + " (" + Twine(Number) + ") does not fit in " +
578f19343aSMarek Sokolowski                          Twine(MaxBits) + " bits.",
588f19343aSMarek Sokolowski                      std::errc::value_too_large);
598f19343aSMarek Sokolowski }
608f19343aSMarek Sokolowski 
618f19343aSMarek Sokolowski template <typename FitType>
62fa0ca6cbSZachary Turner static Error checkNumberFits(uint32_t Number, const Twine &FieldName) {
638f19343aSMarek Sokolowski   return checkNumberFits(Number, sizeof(FitType) * 8, FieldName);
648f19343aSMarek Sokolowski }
658f19343aSMarek Sokolowski 
667f7745c0SMarek Sokolowski // A similar function for signed integers.
677f7745c0SMarek Sokolowski template <typename FitType>
68fa0ca6cbSZachary Turner static Error checkSignedNumberFits(uint32_t Number, const Twine &FieldName,
697f7745c0SMarek Sokolowski                                    bool CanBeNegative) {
707f7745c0SMarek Sokolowski   int32_t SignedNum = Number;
717f7745c0SMarek Sokolowski   if (SignedNum < std::numeric_limits<FitType>::min() ||
727f7745c0SMarek Sokolowski       SignedNum > std::numeric_limits<FitType>::max())
737f7745c0SMarek Sokolowski     return createError(FieldName + " (" + Twine(SignedNum) +
747f7745c0SMarek Sokolowski                            ") does not fit in " + Twine(sizeof(FitType) * 8) +
757f7745c0SMarek Sokolowski                            "-bit signed integer type.",
767f7745c0SMarek Sokolowski                        std::errc::value_too_large);
777f7745c0SMarek Sokolowski 
787f7745c0SMarek Sokolowski   if (!CanBeNegative && SignedNum < 0)
797f7745c0SMarek Sokolowski     return createError(FieldName + " (" + Twine(SignedNum) +
807f7745c0SMarek Sokolowski                        ") cannot be negative.");
817f7745c0SMarek Sokolowski 
827f7745c0SMarek Sokolowski   return Error::success();
837f7745c0SMarek Sokolowski }
847f7745c0SMarek Sokolowski 
85fa0ca6cbSZachary Turner static Error checkRCInt(RCInt Number, const Twine &FieldName) {
8607bc04ffSZachary Turner   if (Number.isLong())
8707bc04ffSZachary Turner     return Error::success();
8807bc04ffSZachary Turner   return checkNumberFits<uint16_t>(Number, FieldName);
8907bc04ffSZachary Turner }
9007bc04ffSZachary Turner 
91fa0ca6cbSZachary Turner static Error checkIntOrString(IntOrString Value, const Twine &FieldName) {
928f19343aSMarek Sokolowski   if (!Value.isInt())
938f19343aSMarek Sokolowski     return Error::success();
948f19343aSMarek Sokolowski   return checkNumberFits<uint16_t>(Value.getInt(), FieldName);
958f19343aSMarek Sokolowski }
968f19343aSMarek Sokolowski 
978f19343aSMarek Sokolowski static bool stripQuotes(StringRef &Str, bool &IsLongString) {
988f19343aSMarek Sokolowski   if (!Str.contains('"'))
998f19343aSMarek Sokolowski     return false;
1008f19343aSMarek Sokolowski 
1018f19343aSMarek Sokolowski   // Just take the contents of the string, checking if it's been marked long.
1028f19343aSMarek Sokolowski   IsLongString = Str.startswith_lower("L");
1038f19343aSMarek Sokolowski   if (IsLongString)
1048f19343aSMarek Sokolowski     Str = Str.drop_front();
1058f19343aSMarek Sokolowski 
1068f19343aSMarek Sokolowski   bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\"");
1078f19343aSMarek Sokolowski   (void)StripSuccess;
1088f19343aSMarek Sokolowski   assert(StripSuccess && "Strings should be enclosed in quotes.");
1098f19343aSMarek Sokolowski   return true;
1108f19343aSMarek Sokolowski }
1118f19343aSMarek Sokolowski 
112d1d046aaSMartin Storsjo static UTF16 cp1252ToUnicode(unsigned char C) {
113d1d046aaSMartin Storsjo   static const UTF16 Map80[] = {
114d1d046aaSMartin Storsjo       0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
115d1d046aaSMartin Storsjo       0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
116d1d046aaSMartin Storsjo       0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
117d1d046aaSMartin Storsjo       0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178,
118d1d046aaSMartin Storsjo   };
119d1d046aaSMartin Storsjo   if (C >= 0x80 && C <= 0x9F)
120d1d046aaSMartin Storsjo     return Map80[C - 0x80];
121d1d046aaSMartin Storsjo   return C;
122d1d046aaSMartin Storsjo }
123d1d046aaSMartin Storsjo 
1248f19343aSMarek Sokolowski // Describes a way to handle '\0' characters when processing the string.
1258f19343aSMarek Sokolowski // rc.exe tool sometimes behaves in a weird way in postprocessing.
1268f19343aSMarek Sokolowski // If the string to be output is equivalent to a C-string (e.g. in MENU
1278f19343aSMarek Sokolowski // titles), string is (predictably) truncated after first 0-byte.
1288f19343aSMarek Sokolowski // When outputting a string table, the behavior is equivalent to appending
1298f19343aSMarek Sokolowski // '\0\0' at the end of the string, and then stripping the string
1308f19343aSMarek Sokolowski // before the first '\0\0' occurrence.
1318f19343aSMarek Sokolowski // Finally, when handling strings in user-defined resources, 0-bytes
1328f19343aSMarek Sokolowski // aren't stripped, nor do they terminate the string.
1338f19343aSMarek Sokolowski 
1348f19343aSMarek Sokolowski enum class NullHandlingMethod {
1358f19343aSMarek Sokolowski   UserResource,   // Don't terminate string on '\0'.
1368f19343aSMarek Sokolowski   CutAtNull,      // Terminate string on '\0'.
1378f19343aSMarek Sokolowski   CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'.
1388f19343aSMarek Sokolowski };
1398f19343aSMarek Sokolowski 
140a92eb33aSZachary Turner // Parses an identifier or string and returns a processed version of it:
141a92eb33aSZachary Turner //   * String the string boundary quotes.
1428f19343aSMarek Sokolowski //   * Squash "" to a single ".
1438f19343aSMarek Sokolowski //   * Replace the escape sequences with their processed version.
1448f19343aSMarek Sokolowski // For identifiers, this is no-op.
1458f19343aSMarek Sokolowski static Error processString(StringRef Str, NullHandlingMethod NullHandler,
146d1d046aaSMartin Storsjo                            bool &IsLongString, SmallVectorImpl<UTF16> &Result,
147d1d046aaSMartin Storsjo                            int CodePage) {
1488f19343aSMarek Sokolowski   bool IsString = stripQuotes(Str, IsLongString);
149a92eb33aSZachary Turner   SmallVector<UTF16, 128> Chars;
150d1d046aaSMartin Storsjo 
151d1d046aaSMartin Storsjo   // Convert the input bytes according to the chosen codepage.
152d1d046aaSMartin Storsjo   if (CodePage == CpUtf8) {
153a92eb33aSZachary Turner     convertUTF8ToUTF16String(Str, Chars);
154d1d046aaSMartin Storsjo   } else if (CodePage == CpWin1252) {
155d1d046aaSMartin Storsjo     for (char C : Str)
156d1d046aaSMartin Storsjo       Chars.push_back(cp1252ToUnicode((unsigned char)C));
157d1d046aaSMartin Storsjo   } else {
158d1d046aaSMartin Storsjo     // For other, unknown codepages, only allow plain ASCII input.
159d1d046aaSMartin Storsjo     for (char C : Str) {
160d1d046aaSMartin Storsjo       if ((unsigned char)C > 0x7F)
161d1d046aaSMartin Storsjo         return createError("Non-ASCII 8-bit codepoint (" + Twine(C) +
162d1d046aaSMartin Storsjo                            ") can't be interpreted in the current codepage");
163d1d046aaSMartin Storsjo       Chars.push_back((unsigned char)C);
164d1d046aaSMartin Storsjo     }
165d1d046aaSMartin Storsjo   }
1668f19343aSMarek Sokolowski 
1678f19343aSMarek Sokolowski   if (!IsString) {
1688f19343aSMarek Sokolowski     // It's an identifier if it's not a string. Make all characters uppercase.
169a92eb33aSZachary Turner     for (UTF16 &Ch : Chars) {
1708f19343aSMarek Sokolowski       assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII");
1718f19343aSMarek Sokolowski       Ch = toupper(Ch);
1728f19343aSMarek Sokolowski     }
173a92eb33aSZachary Turner     Result.swap(Chars);
1748f19343aSMarek Sokolowski     return Error::success();
1758f19343aSMarek Sokolowski   }
176a92eb33aSZachary Turner   Result.reserve(Chars.size());
177a92eb33aSZachary Turner   size_t Pos = 0;
178a92eb33aSZachary Turner 
179a92eb33aSZachary Turner   auto AddRes = [&Result, NullHandler, IsLongString](UTF16 Char) -> Error {
180a92eb33aSZachary Turner     if (!IsLongString) {
181a92eb33aSZachary Turner       if (NullHandler == NullHandlingMethod::UserResource) {
182a92eb33aSZachary Turner         // Narrow strings in user-defined resources are *not* output in
183a92eb33aSZachary Turner         // UTF-16 format.
184a92eb33aSZachary Turner         if (Char > 0xFF)
185a92eb33aSZachary Turner           return createError("Non-8-bit codepoint (" + Twine(Char) +
186a92eb33aSZachary Turner                              ") can't occur in a user-defined narrow string");
187d1d046aaSMartin Storsjo       }
188d1d046aaSMartin Storsjo     }
189a92eb33aSZachary Turner 
190d1d046aaSMartin Storsjo     Result.push_back(Char);
191d1d046aaSMartin Storsjo     return Error::success();
192d1d046aaSMartin Storsjo   };
193d1d046aaSMartin Storsjo   auto AddEscapedChar = [AddRes, IsLongString, CodePage](UTF16 Char) -> Error {
194d1d046aaSMartin Storsjo     if (!IsLongString) {
195d1d046aaSMartin Storsjo       // Escaped chars in narrow strings have to be interpreted according to
196d1d046aaSMartin Storsjo       // the chosen code page.
197d1d046aaSMartin Storsjo       if (Char > 0xFF)
198d1d046aaSMartin Storsjo         return createError("Non-8-bit escaped char (" + Twine(Char) +
199d1d046aaSMartin Storsjo                            ") can't occur in narrow string");
200d1d046aaSMartin Storsjo       if (CodePage == CpUtf8) {
201d1d046aaSMartin Storsjo         if (Char >= 0x80)
202d1d046aaSMartin Storsjo           return createError("Unable to interpret single byte (" + Twine(Char) +
203d1d046aaSMartin Storsjo                              ") as UTF-8");
204d1d046aaSMartin Storsjo       } else if (CodePage == CpWin1252) {
205d1d046aaSMartin Storsjo         Char = cp1252ToUnicode(Char);
206a92eb33aSZachary Turner       } else {
207d1d046aaSMartin Storsjo         // Unknown/unsupported codepage, only allow ASCII input.
208d1d046aaSMartin Storsjo         if (Char > 0x7F)
209a92eb33aSZachary Turner           return createError("Non-ASCII 8-bit codepoint (" + Twine(Char) +
210a92eb33aSZachary Turner                              ") can't "
211a92eb33aSZachary Turner                              "occur in a non-Unicode string");
212a92eb33aSZachary Turner       }
213a92eb33aSZachary Turner     }
214a92eb33aSZachary Turner 
215d1d046aaSMartin Storsjo     return AddRes(Char);
216a92eb33aSZachary Turner   };
217a92eb33aSZachary Turner 
218a92eb33aSZachary Turner   while (Pos < Chars.size()) {
219a92eb33aSZachary Turner     UTF16 CurChar = Chars[Pos];
220a92eb33aSZachary Turner     ++Pos;
221a92eb33aSZachary Turner 
222a92eb33aSZachary Turner     // Strip double "".
223a92eb33aSZachary Turner     if (CurChar == '"') {
224a92eb33aSZachary Turner       if (Pos == Chars.size() || Chars[Pos] != '"')
225a92eb33aSZachary Turner         return createError("Expected \"\"");
226a92eb33aSZachary Turner       ++Pos;
227a92eb33aSZachary Turner       RETURN_IF_ERROR(AddRes('"'));
228a92eb33aSZachary Turner       continue;
229a92eb33aSZachary Turner     }
230a92eb33aSZachary Turner 
231a92eb33aSZachary Turner     if (CurChar == '\\') {
232a92eb33aSZachary Turner       UTF16 TypeChar = Chars[Pos];
233a92eb33aSZachary Turner       ++Pos;
234a92eb33aSZachary Turner 
235a92eb33aSZachary Turner       if (TypeChar == 'x' || TypeChar == 'X') {
236a92eb33aSZachary Turner         // Read a hex number. Max number of characters to read differs between
237a92eb33aSZachary Turner         // narrow and wide strings.
238a92eb33aSZachary Turner         UTF16 ReadInt = 0;
239a92eb33aSZachary Turner         size_t RemainingChars = IsLongString ? 4 : 2;
240a92eb33aSZachary Turner         // We don't want to read non-ASCII hex digits. std:: functions past
241a92eb33aSZachary Turner         // 0xFF invoke UB.
242a92eb33aSZachary Turner         //
243a92eb33aSZachary Turner         // FIXME: actually, Microsoft version probably doesn't check this
244a92eb33aSZachary Turner         // condition and uses their Unicode version of 'isxdigit'. However,
245a92eb33aSZachary Turner         // there are some hex-digit Unicode character outside of ASCII, and
246a92eb33aSZachary Turner         // some of these are actually accepted by rc.exe, the notable example
247a92eb33aSZachary Turner         // being fullwidth forms (U+FF10..U+FF19 etc.) These can be written
248a92eb33aSZachary Turner         // instead of ASCII digits in \x... escape sequence and get accepted.
249a92eb33aSZachary Turner         // However, the resulting hexcodes seem totally unpredictable.
250a92eb33aSZachary Turner         // We think it's infeasible to try to reproduce this behavior, nor to
251a92eb33aSZachary Turner         // put effort in order to detect it.
252a92eb33aSZachary Turner         while (RemainingChars && Pos < Chars.size() && Chars[Pos] < 0x80) {
253a92eb33aSZachary Turner           if (!isxdigit(Chars[Pos]))
254a92eb33aSZachary Turner             break;
255a92eb33aSZachary Turner           char Digit = tolower(Chars[Pos]);
256a92eb33aSZachary Turner           ++Pos;
257a92eb33aSZachary Turner 
258a92eb33aSZachary Turner           ReadInt <<= 4;
259a92eb33aSZachary Turner           if (isdigit(Digit))
260a92eb33aSZachary Turner             ReadInt |= Digit - '0';
261a92eb33aSZachary Turner           else
262a92eb33aSZachary Turner             ReadInt |= Digit - 'a' + 10;
263a92eb33aSZachary Turner 
264a92eb33aSZachary Turner           --RemainingChars;
265a92eb33aSZachary Turner         }
266a92eb33aSZachary Turner 
267d1d046aaSMartin Storsjo         RETURN_IF_ERROR(AddEscapedChar(ReadInt));
268a92eb33aSZachary Turner         continue;
269a92eb33aSZachary Turner       }
270a92eb33aSZachary Turner 
271a92eb33aSZachary Turner       if (TypeChar >= '0' && TypeChar < '8') {
272a92eb33aSZachary Turner         // Read an octal number. Note that we've already read the first digit.
273a92eb33aSZachary Turner         UTF16 ReadInt = TypeChar - '0';
274a92eb33aSZachary Turner         size_t RemainingChars = IsLongString ? 6 : 2;
275a92eb33aSZachary Turner 
276a92eb33aSZachary Turner         while (RemainingChars && Pos < Chars.size() && Chars[Pos] >= '0' &&
277a92eb33aSZachary Turner                Chars[Pos] < '8') {
278a92eb33aSZachary Turner           ReadInt <<= 3;
279a92eb33aSZachary Turner           ReadInt |= Chars[Pos] - '0';
280a92eb33aSZachary Turner           --RemainingChars;
281a92eb33aSZachary Turner           ++Pos;
282a92eb33aSZachary Turner         }
283a92eb33aSZachary Turner 
284d1d046aaSMartin Storsjo         RETURN_IF_ERROR(AddEscapedChar(ReadInt));
285a92eb33aSZachary Turner 
286a92eb33aSZachary Turner         continue;
287a92eb33aSZachary Turner       }
288a92eb33aSZachary Turner 
289a92eb33aSZachary Turner       switch (TypeChar) {
290a92eb33aSZachary Turner       case 'A':
291a92eb33aSZachary Turner       case 'a':
292a92eb33aSZachary Turner         // Windows '\a' translates into '\b' (Backspace).
293a92eb33aSZachary Turner         RETURN_IF_ERROR(AddRes('\b'));
294a92eb33aSZachary Turner         break;
295a92eb33aSZachary Turner 
296a92eb33aSZachary Turner       case 'n': // Somehow, RC doesn't recognize '\N' and '\R'.
297a92eb33aSZachary Turner         RETURN_IF_ERROR(AddRes('\n'));
298a92eb33aSZachary Turner         break;
299a92eb33aSZachary Turner 
300a92eb33aSZachary Turner       case 'r':
301a92eb33aSZachary Turner         RETURN_IF_ERROR(AddRes('\r'));
302a92eb33aSZachary Turner         break;
303a92eb33aSZachary Turner 
304a92eb33aSZachary Turner       case 'T':
305a92eb33aSZachary Turner       case 't':
306a92eb33aSZachary Turner         RETURN_IF_ERROR(AddRes('\t'));
307a92eb33aSZachary Turner         break;
308a92eb33aSZachary Turner 
309a92eb33aSZachary Turner       case '\\':
310a92eb33aSZachary Turner         RETURN_IF_ERROR(AddRes('\\'));
311a92eb33aSZachary Turner         break;
312a92eb33aSZachary Turner 
313a92eb33aSZachary Turner       case '"':
314a92eb33aSZachary Turner         // RC accepts \" only if another " comes afterwards; then, \"" means
315a92eb33aSZachary Turner         // a single ".
316a92eb33aSZachary Turner         if (Pos == Chars.size() || Chars[Pos] != '"')
317a92eb33aSZachary Turner           return createError("Expected \\\"\"");
318a92eb33aSZachary Turner         ++Pos;
319a92eb33aSZachary Turner         RETURN_IF_ERROR(AddRes('"'));
320a92eb33aSZachary Turner         break;
321a92eb33aSZachary Turner 
322a92eb33aSZachary Turner       default:
323a92eb33aSZachary Turner         // If TypeChar means nothing, \ is should be output to stdout with
324a92eb33aSZachary Turner         // following char. However, rc.exe consumes these characters when
325a92eb33aSZachary Turner         // dealing with wide strings.
326a92eb33aSZachary Turner         if (!IsLongString) {
327a92eb33aSZachary Turner           RETURN_IF_ERROR(AddRes('\\'));
328a92eb33aSZachary Turner           RETURN_IF_ERROR(AddRes(TypeChar));
329a92eb33aSZachary Turner         }
330a92eb33aSZachary Turner         break;
331a92eb33aSZachary Turner       }
332a92eb33aSZachary Turner 
333a92eb33aSZachary Turner       continue;
334a92eb33aSZachary Turner     }
335a92eb33aSZachary Turner 
336a92eb33aSZachary Turner     // If nothing interesting happens, just output the character.
337a92eb33aSZachary Turner     RETURN_IF_ERROR(AddRes(CurChar));
338a92eb33aSZachary Turner   }
3398f19343aSMarek Sokolowski 
340da366693SZachary Turner   switch (NullHandler) {
341da366693SZachary Turner   case NullHandlingMethod::CutAtNull:
3428f19343aSMarek Sokolowski     for (size_t Pos = 0; Pos < Result.size(); ++Pos)
3438f19343aSMarek Sokolowski       if (Result[Pos] == '\0')
3448f19343aSMarek Sokolowski         Result.resize(Pos);
345da366693SZachary Turner     break;
346da366693SZachary Turner 
347da366693SZachary Turner   case NullHandlingMethod::CutAtDoubleNull:
348da366693SZachary Turner     for (size_t Pos = 0; Pos + 1 < Result.size(); ++Pos)
349da366693SZachary Turner       if (Result[Pos] == '\0' && Result[Pos + 1] == '\0')
350da366693SZachary Turner         Result.resize(Pos);
351da366693SZachary Turner     if (Result.size() > 0 && Result.back() == '\0')
352da366693SZachary Turner       Result.pop_back();
353da366693SZachary Turner     break;
354da366693SZachary Turner 
355da366693SZachary Turner   case NullHandlingMethod::UserResource:
356da366693SZachary Turner     break;
357da366693SZachary Turner   }
3588f19343aSMarek Sokolowski 
3598f19343aSMarek Sokolowski   return Error::success();
3608f19343aSMarek Sokolowski }
3618f19343aSMarek Sokolowski 
3628f19343aSMarek Sokolowski uint64_t ResourceFileWriter::writeObject(const ArrayRef<uint8_t> Data) {
3638f19343aSMarek Sokolowski   uint64_t Result = tell();
3648f19343aSMarek Sokolowski   FS->write((const char *)Data.begin(), Data.size());
3658f19343aSMarek Sokolowski   return Result;
3668f19343aSMarek Sokolowski }
3678f19343aSMarek Sokolowski 
3688f19343aSMarek Sokolowski Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) {
3698f19343aSMarek Sokolowski   SmallVector<UTF16, 128> ProcessedString;
3708f19343aSMarek Sokolowski   bool IsLongString;
3718f19343aSMarek Sokolowski   RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull,
372d1d046aaSMartin Storsjo                                 IsLongString, ProcessedString,
373d1d046aaSMartin Storsjo                                 Params.CodePage));
3748f19343aSMarek Sokolowski   for (auto Ch : ProcessedString)
3758f19343aSMarek Sokolowski     writeInt<uint16_t>(Ch);
3768f19343aSMarek Sokolowski   if (WriteTerminator)
3778f19343aSMarek Sokolowski     writeInt<uint16_t>(0);
3788f19343aSMarek Sokolowski   return Error::success();
3798f19343aSMarek Sokolowski }
3808f19343aSMarek Sokolowski 
3818f19343aSMarek Sokolowski Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) {
3828f19343aSMarek Sokolowski   return writeIntOrString(Ident);
3838f19343aSMarek Sokolowski }
3848f19343aSMarek Sokolowski 
3858f19343aSMarek Sokolowski Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) {
3868f19343aSMarek Sokolowski   if (!Value.isInt())
3878f19343aSMarek Sokolowski     return writeCString(Value.getString());
3888f19343aSMarek Sokolowski 
3898f19343aSMarek Sokolowski   writeInt<uint16_t>(0xFFFF);
3908f19343aSMarek Sokolowski   writeInt<uint16_t>(Value.getInt());
3918f19343aSMarek Sokolowski   return Error::success();
3928f19343aSMarek Sokolowski }
3938f19343aSMarek Sokolowski 
39407bc04ffSZachary Turner void ResourceFileWriter::writeRCInt(RCInt Value) {
39507bc04ffSZachary Turner   if (Value.isLong())
396b12792a4SZachary Turner     writeInt<uint32_t>(Value);
39707bc04ffSZachary Turner   else
398b12792a4SZachary Turner     writeInt<uint16_t>(Value);
39907bc04ffSZachary Turner }
40007bc04ffSZachary Turner 
4018f19343aSMarek Sokolowski Error ResourceFileWriter::appendFile(StringRef Filename) {
4028f19343aSMarek Sokolowski   bool IsLong;
4038f19343aSMarek Sokolowski   stripQuotes(Filename, IsLong);
4048f19343aSMarek Sokolowski 
405fa0ca6cbSZachary Turner   auto File = loadFile(Filename);
4068f19343aSMarek Sokolowski   if (!File)
407fa0ca6cbSZachary Turner     return File.takeError();
408fa0ca6cbSZachary Turner 
4098f19343aSMarek Sokolowski   *FS << (*File)->getBuffer();
4108f19343aSMarek Sokolowski   return Error::success();
4118f19343aSMarek Sokolowski }
4128f19343aSMarek Sokolowski 
4138f19343aSMarek Sokolowski void ResourceFileWriter::padStream(uint64_t Length) {
4148f19343aSMarek Sokolowski   assert(Length > 0);
4158f19343aSMarek Sokolowski   uint64_t Location = tell();
4168f19343aSMarek Sokolowski   Location %= Length;
4178f19343aSMarek Sokolowski   uint64_t Pad = (Length - Location) % Length;
4188f19343aSMarek Sokolowski   for (uint64_t i = 0; i < Pad; ++i)
4198f19343aSMarek Sokolowski     writeInt<uint8_t>(0);
4208f19343aSMarek Sokolowski }
4218f19343aSMarek Sokolowski 
422514b7105SZachary Turner Error ResourceFileWriter::handleError(Error Err, const RCResource *Res) {
4238f19343aSMarek Sokolowski   if (Err)
4248f19343aSMarek Sokolowski     return joinErrors(createError("Error in " + Res->getResourceTypeName() +
4258f19343aSMarek Sokolowski                                   " statement (ID " + Twine(Res->ResName) +
4268f19343aSMarek Sokolowski                                   "): "),
4278f19343aSMarek Sokolowski                       std::move(Err));
4288f19343aSMarek Sokolowski   return Error::success();
4298f19343aSMarek Sokolowski }
4308f19343aSMarek Sokolowski 
4318f19343aSMarek Sokolowski Error ResourceFileWriter::visitNullResource(const RCResource *Res) {
4328f19343aSMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeNullBody);
4338f19343aSMarek Sokolowski }
4348f19343aSMarek Sokolowski 
43522fccd64SMarek Sokolowski Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) {
43622fccd64SMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody);
43722fccd64SMarek Sokolowski }
43822fccd64SMarek Sokolowski 
439577b9817SMartin Storsjo Error ResourceFileWriter::visitBitmapResource(const RCResource *Res) {
440577b9817SMartin Storsjo   return writeResource(Res, &ResourceFileWriter::writeBitmapBody);
441577b9817SMartin Storsjo }
442577b9817SMartin Storsjo 
443c3ab013aSZachary Turner Error ResourceFileWriter::visitCursorResource(const RCResource *Res) {
444c3ab013aSZachary Turner   return handleError(visitIconOrCursorResource(Res), Res);
445c3ab013aSZachary Turner }
446c3ab013aSZachary Turner 
4477f7745c0SMarek Sokolowski Error ResourceFileWriter::visitDialogResource(const RCResource *Res) {
4487f7745c0SMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeDialogBody);
4497f7745c0SMarek Sokolowski }
4507f7745c0SMarek Sokolowski 
451c3ab013aSZachary Turner Error ResourceFileWriter::visitIconResource(const RCResource *Res) {
452c3ab013aSZachary Turner   return handleError(visitIconOrCursorResource(Res), Res);
453c3ab013aSZachary Turner }
454c3ab013aSZachary Turner 
455420090afSZachary Turner Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) {
456420090afSZachary Turner   ObjectData.Caption = Stmt->Value;
457420090afSZachary Turner   return Error::success();
458420090afSZachary Turner }
459420090afSZachary Turner 
460e241ce6fSMartin Storsjo Error ResourceFileWriter::visitClassStmt(const ClassStmt *Stmt) {
461e241ce6fSMartin Storsjo   ObjectData.Class = Stmt->Value;
462e241ce6fSMartin Storsjo   return Error::success();
463e241ce6fSMartin Storsjo }
464e241ce6fSMartin Storsjo 
4658f19343aSMarek Sokolowski Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
4668f19343aSMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
4678f19343aSMarek Sokolowski }
4688f19343aSMarek Sokolowski 
46942f494d6SMarek Sokolowski Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
47042f494d6SMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeMenuBody);
47142f494d6SMarek Sokolowski }
47242f494d6SMarek Sokolowski 
473da366693SZachary Turner Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) {
474da366693SZachary Turner   const auto *Res = cast<StringTableResource>(Base);
475da366693SZachary Turner 
476da366693SZachary Turner   ContextKeeper RAII(this);
477da366693SZachary Turner   RETURN_IF_ERROR(Res->applyStmts(this));
478da366693SZachary Turner 
479da366693SZachary Turner   for (auto &String : Res->Table) {
480da366693SZachary Turner     RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID"));
481da366693SZachary Turner     uint16_t BundleID = String.first >> 4;
482da366693SZachary Turner     StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo);
483da366693SZachary Turner     auto &BundleData = StringTableData.BundleData;
484da366693SZachary Turner     auto Iter = BundleData.find(Key);
485da366693SZachary Turner 
486da366693SZachary Turner     if (Iter == BundleData.end()) {
487da366693SZachary Turner       // Need to create a bundle.
488da366693SZachary Turner       StringTableData.BundleList.push_back(Key);
48911adbacaSMartin Storsjo       auto EmplaceResult = BundleData.emplace(
49011adbacaSMartin Storsjo           Key, StringTableInfo::Bundle(ObjectData, Res->MemoryFlags));
491da366693SZachary Turner       assert(EmplaceResult.second && "Could not create a bundle");
492da366693SZachary Turner       Iter = EmplaceResult.first;
493da366693SZachary Turner     }
494da366693SZachary Turner 
495da366693SZachary Turner     RETURN_IF_ERROR(
496da366693SZachary Turner         insertStringIntoBundle(Iter->second, String.first, String.second));
497da366693SZachary Turner   }
498da366693SZachary Turner 
499da366693SZachary Turner   return Error::success();
500da366693SZachary Turner }
501da366693SZachary Turner 
5029d8b358aSZachary Turner Error ResourceFileWriter::visitUserDefinedResource(const RCResource *Res) {
5039d8b358aSZachary Turner   return writeResource(Res, &ResourceFileWriter::writeUserDefinedBody);
5049d8b358aSZachary Turner }
5059d8b358aSZachary Turner 
50607bc04ffSZachary Turner Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) {
50707bc04ffSZachary Turner   return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody);
50807bc04ffSZachary Turner }
50907bc04ffSZachary Turner 
51022fccd64SMarek Sokolowski Error ResourceFileWriter::visitCharacteristicsStmt(
51122fccd64SMarek Sokolowski     const CharacteristicsStmt *Stmt) {
51222fccd64SMarek Sokolowski   ObjectData.Characteristics = Stmt->Value;
51322fccd64SMarek Sokolowski   return Error::success();
51422fccd64SMarek Sokolowski }
51522fccd64SMarek Sokolowski 
516a876b5c0SMartin Storsjo Error ResourceFileWriter::visitExStyleStmt(const ExStyleStmt *Stmt) {
517a876b5c0SMartin Storsjo   ObjectData.ExStyle = Stmt->Value;
518a876b5c0SMartin Storsjo   return Error::success();
519a876b5c0SMartin Storsjo }
520a876b5c0SMartin Storsjo 
521420090afSZachary Turner Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) {
522420090afSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size"));
523420090afSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight"));
524420090afSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset"));
525420090afSZachary Turner   ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic,
526420090afSZachary Turner                             Stmt->Charset};
527420090afSZachary Turner   ObjectData.Font.emplace(Font);
528420090afSZachary Turner   return Error::success();
529420090afSZachary Turner }
530420090afSZachary Turner 
5318f19343aSMarek Sokolowski Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
5328f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID"));
5338f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"));
5348f19343aSMarek Sokolowski   ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10);
5358f19343aSMarek Sokolowski   return Error::success();
5368f19343aSMarek Sokolowski }
5378f19343aSMarek Sokolowski 
538420090afSZachary Turner Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) {
539420090afSZachary Turner   ObjectData.Style = Stmt->Value;
540420090afSZachary Turner   return Error::success();
541420090afSZachary Turner }
542420090afSZachary Turner 
54322fccd64SMarek Sokolowski Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) {
54422fccd64SMarek Sokolowski   ObjectData.VersionInfo = Stmt->Value;
54522fccd64SMarek Sokolowski   return Error::success();
54622fccd64SMarek Sokolowski }
54722fccd64SMarek Sokolowski 
5488f19343aSMarek Sokolowski Error ResourceFileWriter::writeResource(
5498f19343aSMarek Sokolowski     const RCResource *Res,
5508f19343aSMarek Sokolowski     Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) {
5518f19343aSMarek Sokolowski   // We don't know the sizes yet.
5528f19343aSMarek Sokolowski   object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)};
5538f19343aSMarek Sokolowski   uint64_t HeaderLoc = writeObject(HeaderPrefix);
5548f19343aSMarek Sokolowski 
5558f19343aSMarek Sokolowski   auto ResType = Res->getResourceType();
5568f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type"));
5578f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID"));
5588f19343aSMarek Sokolowski   RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res));
5598f19343aSMarek Sokolowski   RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res));
5608f19343aSMarek Sokolowski 
56122fccd64SMarek Sokolowski   // Apply the resource-local optional statements.
56222fccd64SMarek Sokolowski   ContextKeeper RAII(this);
56322fccd64SMarek Sokolowski   RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res));
56422fccd64SMarek Sokolowski 
5658f19343aSMarek Sokolowski   padStream(sizeof(uint32_t));
5668f19343aSMarek Sokolowski   object::WinResHeaderSuffix HeaderSuffix{
5678f19343aSMarek Sokolowski       ulittle32_t(0), // DataVersion; seems to always be 0
56811adbacaSMartin Storsjo       ulittle16_t(Res->MemoryFlags), ulittle16_t(ObjectData.LanguageInfo),
56922fccd64SMarek Sokolowski       ulittle32_t(ObjectData.VersionInfo),
57022fccd64SMarek Sokolowski       ulittle32_t(ObjectData.Characteristics)};
5718f19343aSMarek Sokolowski   writeObject(HeaderSuffix);
5728f19343aSMarek Sokolowski 
5738f19343aSMarek Sokolowski   uint64_t DataLoc = tell();
5748f19343aSMarek Sokolowski   RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res));
5758f19343aSMarek Sokolowski   // RETURN_IF_ERROR(handleError(dumpResource(Ctx)));
5768f19343aSMarek Sokolowski 
5778f19343aSMarek Sokolowski   // Update the sizes.
5788f19343aSMarek Sokolowski   HeaderPrefix.DataSize = tell() - DataLoc;
5798f19343aSMarek Sokolowski   HeaderPrefix.HeaderSize = DataLoc - HeaderLoc;
5808f19343aSMarek Sokolowski   writeObjectAt(HeaderPrefix, HeaderLoc);
5818f19343aSMarek Sokolowski   padStream(sizeof(uint32_t));
5828f19343aSMarek Sokolowski 
5838f19343aSMarek Sokolowski   return Error::success();
5848f19343aSMarek Sokolowski }
5858f19343aSMarek Sokolowski 
58622fccd64SMarek Sokolowski // --- NullResource helpers. --- //
58722fccd64SMarek Sokolowski 
5888f19343aSMarek Sokolowski Error ResourceFileWriter::writeNullBody(const RCResource *) {
5898f19343aSMarek Sokolowski   return Error::success();
5908f19343aSMarek Sokolowski }
5918f19343aSMarek Sokolowski 
59222fccd64SMarek Sokolowski // --- AcceleratorsResource helpers. --- //
59322fccd64SMarek Sokolowski 
59422fccd64SMarek Sokolowski Error ResourceFileWriter::writeSingleAccelerator(
59522fccd64SMarek Sokolowski     const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) {
59622fccd64SMarek Sokolowski   using Accelerator = AcceleratorsResource::Accelerator;
59722fccd64SMarek Sokolowski   using Opt = Accelerator::Options;
59822fccd64SMarek Sokolowski 
59922fccd64SMarek Sokolowski   struct AccelTableEntry {
60022fccd64SMarek Sokolowski     ulittle16_t Flags;
60122fccd64SMarek Sokolowski     ulittle16_t ANSICode;
60222fccd64SMarek Sokolowski     ulittle16_t Id;
60322fccd64SMarek Sokolowski     uint16_t Padding;
60422fccd64SMarek Sokolowski   } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0};
60522fccd64SMarek Sokolowski 
60622fccd64SMarek Sokolowski   bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY;
60722fccd64SMarek Sokolowski 
60822fccd64SMarek Sokolowski   // Remove ASCII flags (which doesn't occur in .res files).
60922fccd64SMarek Sokolowski   Entry.Flags = Obj.Flags & ~Opt::ASCII;
61022fccd64SMarek Sokolowski 
61122fccd64SMarek Sokolowski   if (IsLastItem)
61222fccd64SMarek Sokolowski     Entry.Flags |= 0x80;
61322fccd64SMarek Sokolowski 
61422fccd64SMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"));
61522fccd64SMarek Sokolowski   Entry.Id = ulittle16_t(Obj.Id);
61622fccd64SMarek Sokolowski 
61722fccd64SMarek Sokolowski   auto createAccError = [&Obj](const char *Msg) {
61822fccd64SMarek Sokolowski     return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg);
61922fccd64SMarek Sokolowski   };
62022fccd64SMarek Sokolowski 
62122fccd64SMarek Sokolowski   if (IsASCII && IsVirtKey)
62222fccd64SMarek Sokolowski     return createAccError("Accelerator can't be both ASCII and VIRTKEY");
62322fccd64SMarek Sokolowski 
62422fccd64SMarek Sokolowski   if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL)))
62522fccd64SMarek Sokolowski     return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY"
62622fccd64SMarek Sokolowski                           " accelerators");
62722fccd64SMarek Sokolowski 
62822fccd64SMarek Sokolowski   if (Obj.Event.isInt()) {
62922fccd64SMarek Sokolowski     if (!IsASCII && !IsVirtKey)
63022fccd64SMarek Sokolowski       return createAccError(
63122fccd64SMarek Sokolowski           "Accelerator with a numeric event must be either ASCII"
63222fccd64SMarek Sokolowski           " or VIRTKEY");
63322fccd64SMarek Sokolowski 
63422fccd64SMarek Sokolowski     uint32_t EventVal = Obj.Event.getInt();
63522fccd64SMarek Sokolowski     RETURN_IF_ERROR(
63622fccd64SMarek Sokolowski         checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"));
63722fccd64SMarek Sokolowski     Entry.ANSICode = ulittle16_t(EventVal);
63822fccd64SMarek Sokolowski     writeObject(Entry);
63922fccd64SMarek Sokolowski     return Error::success();
64022fccd64SMarek Sokolowski   }
64122fccd64SMarek Sokolowski 
64222fccd64SMarek Sokolowski   StringRef Str = Obj.Event.getString();
64322fccd64SMarek Sokolowski   bool IsWide;
64422fccd64SMarek Sokolowski   stripQuotes(Str, IsWide);
64522fccd64SMarek Sokolowski 
64622fccd64SMarek Sokolowski   if (Str.size() == 0 || Str.size() > 2)
64722fccd64SMarek Sokolowski     return createAccError(
64822fccd64SMarek Sokolowski         "Accelerator string events should have length 1 or 2");
64922fccd64SMarek Sokolowski 
65022fccd64SMarek Sokolowski   if (Str[0] == '^') {
65122fccd64SMarek Sokolowski     if (Str.size() == 1)
65222fccd64SMarek Sokolowski       return createAccError("No character following '^' in accelerator event");
65322fccd64SMarek Sokolowski     if (IsVirtKey)
65422fccd64SMarek Sokolowski       return createAccError(
65522fccd64SMarek Sokolowski           "VIRTKEY accelerator events can't be preceded by '^'");
65622fccd64SMarek Sokolowski 
65722fccd64SMarek Sokolowski     char Ch = Str[1];
65822fccd64SMarek Sokolowski     if (Ch >= 'a' && Ch <= 'z')
65922fccd64SMarek Sokolowski       Entry.ANSICode = ulittle16_t(Ch - 'a' + 1);
66022fccd64SMarek Sokolowski     else if (Ch >= 'A' && Ch <= 'Z')
66122fccd64SMarek Sokolowski       Entry.ANSICode = ulittle16_t(Ch - 'A' + 1);
66222fccd64SMarek Sokolowski     else
66322fccd64SMarek Sokolowski       return createAccError("Control character accelerator event should be"
66422fccd64SMarek Sokolowski                             " alphabetic");
66522fccd64SMarek Sokolowski 
66622fccd64SMarek Sokolowski     writeObject(Entry);
66722fccd64SMarek Sokolowski     return Error::success();
66822fccd64SMarek Sokolowski   }
66922fccd64SMarek Sokolowski 
67022fccd64SMarek Sokolowski   if (Str.size() == 2)
67122fccd64SMarek Sokolowski     return createAccError("Event string should be one-character, possibly"
67222fccd64SMarek Sokolowski                           " preceded by '^'");
67322fccd64SMarek Sokolowski 
67422fccd64SMarek Sokolowski   uint8_t EventCh = Str[0];
67522fccd64SMarek Sokolowski   // The original tool just warns in this situation. We chose to fail.
67622fccd64SMarek Sokolowski   if (IsVirtKey && !isalnum(EventCh))
67722fccd64SMarek Sokolowski     return createAccError("Non-alphanumeric characters cannot describe virtual"
67822fccd64SMarek Sokolowski                           " keys");
67922fccd64SMarek Sokolowski   if (EventCh > 0x7F)
68022fccd64SMarek Sokolowski     return createAccError("Non-ASCII description of accelerator");
68122fccd64SMarek Sokolowski 
68222fccd64SMarek Sokolowski   if (IsVirtKey)
68322fccd64SMarek Sokolowski     EventCh = toupper(EventCh);
68422fccd64SMarek Sokolowski   Entry.ANSICode = ulittle16_t(EventCh);
68522fccd64SMarek Sokolowski   writeObject(Entry);
68622fccd64SMarek Sokolowski   return Error::success();
68722fccd64SMarek Sokolowski }
68822fccd64SMarek Sokolowski 
68922fccd64SMarek Sokolowski Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
69022fccd64SMarek Sokolowski   auto *Res = cast<AcceleratorsResource>(Base);
69122fccd64SMarek Sokolowski   size_t AcceleratorId = 0;
69222fccd64SMarek Sokolowski   for (auto &Acc : Res->Accelerators) {
69322fccd64SMarek Sokolowski     ++AcceleratorId;
69422fccd64SMarek Sokolowski     RETURN_IF_ERROR(
69522fccd64SMarek Sokolowski         writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size()));
69622fccd64SMarek Sokolowski   }
69722fccd64SMarek Sokolowski   return Error::success();
69822fccd64SMarek Sokolowski }
69922fccd64SMarek Sokolowski 
700577b9817SMartin Storsjo // --- BitmapResource helpers. --- //
701577b9817SMartin Storsjo 
702577b9817SMartin Storsjo Error ResourceFileWriter::writeBitmapBody(const RCResource *Base) {
703577b9817SMartin Storsjo   StringRef Filename = cast<BitmapResource>(Base)->BitmapLoc;
704577b9817SMartin Storsjo   bool IsLong;
705577b9817SMartin Storsjo   stripQuotes(Filename, IsLong);
706577b9817SMartin Storsjo 
707577b9817SMartin Storsjo   auto File = loadFile(Filename);
708577b9817SMartin Storsjo   if (!File)
709577b9817SMartin Storsjo     return File.takeError();
710577b9817SMartin Storsjo 
711577b9817SMartin Storsjo   StringRef Buffer = (*File)->getBuffer();
712577b9817SMartin Storsjo 
713577b9817SMartin Storsjo   // Skip the 14 byte BITMAPFILEHEADER.
714577b9817SMartin Storsjo   constexpr size_t BITMAPFILEHEADER_size = 14;
715577b9817SMartin Storsjo   if (Buffer.size() < BITMAPFILEHEADER_size || Buffer[0] != 'B' ||
716577b9817SMartin Storsjo       Buffer[1] != 'M')
717577b9817SMartin Storsjo     return createError("Incorrect bitmap file.");
718577b9817SMartin Storsjo 
719577b9817SMartin Storsjo   *FS << Buffer.substr(BITMAPFILEHEADER_size);
720577b9817SMartin Storsjo   return Error::success();
721577b9817SMartin Storsjo }
722577b9817SMartin Storsjo 
723c3ab013aSZachary Turner // --- CursorResource and IconResource helpers. --- //
724c3ab013aSZachary Turner 
7252aa0bdebSFangrui Song // ICONRESDIR structure. Describes a single icon in resource group.
726c3ab013aSZachary Turner //
727c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx
728c3ab013aSZachary Turner struct IconResDir {
729c3ab013aSZachary Turner   uint8_t Width;
730c3ab013aSZachary Turner   uint8_t Height;
731c3ab013aSZachary Turner   uint8_t ColorCount;
732c3ab013aSZachary Turner   uint8_t Reserved;
733c3ab013aSZachary Turner };
734c3ab013aSZachary Turner 
735c3ab013aSZachary Turner // CURSORDIR structure. Describes a single cursor in resource group.
736c3ab013aSZachary Turner //
737c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx
738c3ab013aSZachary Turner struct CursorDir {
739c3ab013aSZachary Turner   ulittle16_t Width;
740c3ab013aSZachary Turner   ulittle16_t Height;
741c3ab013aSZachary Turner };
742c3ab013aSZachary Turner 
743c3ab013aSZachary Turner // RESDIRENTRY structure, stripped from the last item. Stripping made
744c3ab013aSZachary Turner // for compatibility with RESDIR.
745c3ab013aSZachary Turner //
746c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx
747c3ab013aSZachary Turner struct ResourceDirEntryStart {
748c3ab013aSZachary Turner   union {
749c3ab013aSZachary Turner     CursorDir Cursor; // Used in CURSOR resources.
750c3ab013aSZachary Turner     IconResDir Icon;  // Used in .ico and .cur files, and ICON resources.
751c3ab013aSZachary Turner   };
752c3ab013aSZachary Turner   ulittle16_t Planes;   // HotspotX (.cur files but not CURSOR resource).
753c3ab013aSZachary Turner   ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource).
754c3ab013aSZachary Turner   ulittle32_t Size;
755c3ab013aSZachary Turner   // ulittle32_t ImageOffset;  // Offset to image data (ICONDIRENTRY only).
756c3ab013aSZachary Turner   // ulittle16_t IconID;       // Resource icon ID (RESDIR only).
757c3ab013aSZachary Turner };
758c3ab013aSZachary Turner 
759c3ab013aSZachary Turner // BITMAPINFOHEADER structure. Describes basic information about the bitmap
760c3ab013aSZachary Turner // being read.
761c3ab013aSZachary Turner //
762c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
763c3ab013aSZachary Turner struct BitmapInfoHeader {
764c3ab013aSZachary Turner   ulittle32_t Size;
765c3ab013aSZachary Turner   ulittle32_t Width;
766c3ab013aSZachary Turner   ulittle32_t Height;
767c3ab013aSZachary Turner   ulittle16_t Planes;
768c3ab013aSZachary Turner   ulittle16_t BitCount;
769c3ab013aSZachary Turner   ulittle32_t Compression;
770c3ab013aSZachary Turner   ulittle32_t SizeImage;
771c3ab013aSZachary Turner   ulittle32_t XPelsPerMeter;
772c3ab013aSZachary Turner   ulittle32_t YPelsPerMeter;
773c3ab013aSZachary Turner   ulittle32_t ClrUsed;
774c3ab013aSZachary Turner   ulittle32_t ClrImportant;
775c3ab013aSZachary Turner };
776c3ab013aSZachary Turner 
777c3ab013aSZachary Turner // Group icon directory header. Called ICONDIR in .ico/.cur files and
778c3ab013aSZachary Turner // NEWHEADER in .res files.
779c3ab013aSZachary Turner //
780c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx
781c3ab013aSZachary Turner struct GroupIconDir {
782c3ab013aSZachary Turner   ulittle16_t Reserved; // Always 0.
783c3ab013aSZachary Turner   ulittle16_t ResType;  // 1 for icons, 2 for cursors.
784c3ab013aSZachary Turner   ulittle16_t ResCount; // Number of items.
785c3ab013aSZachary Turner };
786c3ab013aSZachary Turner 
787c3ab013aSZachary Turner enum class IconCursorGroupType { Icon, Cursor };
788c3ab013aSZachary Turner 
789c3ab013aSZachary Turner class SingleIconCursorResource : public RCResource {
790c3ab013aSZachary Turner public:
791c3ab013aSZachary Turner   IconCursorGroupType Type;
792c3ab013aSZachary Turner   const ResourceDirEntryStart &Header;
793c3ab013aSZachary Turner   ArrayRef<uint8_t> Image;
794c3ab013aSZachary Turner 
795c3ab013aSZachary Turner   SingleIconCursorResource(IconCursorGroupType ResourceType,
796c3ab013aSZachary Turner                            const ResourceDirEntryStart &HeaderEntry,
79711adbacaSMartin Storsjo                            ArrayRef<uint8_t> ImageData, uint16_t Flags)
79811adbacaSMartin Storsjo       : RCResource(Flags), Type(ResourceType), Header(HeaderEntry),
79911adbacaSMartin Storsjo         Image(ImageData) {}
800c3ab013aSZachary Turner 
801c3ab013aSZachary Turner   Twine getResourceTypeName() const override { return "Icon/cursor image"; }
802c3ab013aSZachary Turner   IntOrString getResourceType() const override {
803c3ab013aSZachary Turner     return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor;
804c3ab013aSZachary Turner   }
805c3ab013aSZachary Turner   ResourceKind getKind() const override { return RkSingleCursorOrIconRes; }
806c3ab013aSZachary Turner   static bool classof(const RCResource *Res) {
807c3ab013aSZachary Turner     return Res->getKind() == RkSingleCursorOrIconRes;
808c3ab013aSZachary Turner   }
809c3ab013aSZachary Turner };
810c3ab013aSZachary Turner 
811c3ab013aSZachary Turner class IconCursorGroupResource : public RCResource {
812c3ab013aSZachary Turner public:
813c3ab013aSZachary Turner   IconCursorGroupType Type;
814c3ab013aSZachary Turner   GroupIconDir Header;
815c3ab013aSZachary Turner   std::vector<ResourceDirEntryStart> ItemEntries;
816c3ab013aSZachary Turner 
817c3ab013aSZachary Turner   IconCursorGroupResource(IconCursorGroupType ResourceType,
818c3ab013aSZachary Turner                           const GroupIconDir &HeaderData,
819c3ab013aSZachary Turner                           std::vector<ResourceDirEntryStart> &&Entries)
820c3ab013aSZachary Turner       : Type(ResourceType), Header(HeaderData),
821c3ab013aSZachary Turner         ItemEntries(std::move(Entries)) {}
822c3ab013aSZachary Turner 
823c3ab013aSZachary Turner   Twine getResourceTypeName() const override { return "Icon/cursor group"; }
824c3ab013aSZachary Turner   IntOrString getResourceType() const override {
825c3ab013aSZachary Turner     return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup;
826c3ab013aSZachary Turner   }
827c3ab013aSZachary Turner   ResourceKind getKind() const override { return RkCursorOrIconGroupRes; }
828c3ab013aSZachary Turner   static bool classof(const RCResource *Res) {
829c3ab013aSZachary Turner     return Res->getKind() == RkCursorOrIconGroupRes;
830c3ab013aSZachary Turner   }
831c3ab013aSZachary Turner };
832c3ab013aSZachary Turner 
833c3ab013aSZachary Turner Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) {
834c3ab013aSZachary Turner   auto *Res = cast<SingleIconCursorResource>(Base);
835c3ab013aSZachary Turner   if (Res->Type == IconCursorGroupType::Cursor) {
836c3ab013aSZachary Turner     // In case of cursors, two WORDS are appended to the beginning
837c3ab013aSZachary Turner     // of the resource: HotspotX (Planes in RESDIRENTRY),
838c3ab013aSZachary Turner     // and HotspotY (BitCount).
839c3ab013aSZachary Turner     //
840c3ab013aSZachary Turner     // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx
841c3ab013aSZachary Turner     //  (Remarks section).
842c3ab013aSZachary Turner     writeObject(Res->Header.Planes);
843c3ab013aSZachary Turner     writeObject(Res->Header.BitCount);
844c3ab013aSZachary Turner   }
845c3ab013aSZachary Turner 
846c3ab013aSZachary Turner   writeObject(Res->Image);
847c3ab013aSZachary Turner   return Error::success();
848c3ab013aSZachary Turner }
849c3ab013aSZachary Turner 
850c3ab013aSZachary Turner Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) {
851c3ab013aSZachary Turner   auto *Res = cast<IconCursorGroupResource>(Base);
852c3ab013aSZachary Turner   writeObject(Res->Header);
853c3ab013aSZachary Turner   for (auto Item : Res->ItemEntries) {
854c3ab013aSZachary Turner     writeObject(Item);
855b12792a4SZachary Turner     writeInt(IconCursorID++);
856c3ab013aSZachary Turner   }
857c3ab013aSZachary Turner   return Error::success();
858c3ab013aSZachary Turner }
859c3ab013aSZachary Turner 
860c3ab013aSZachary Turner Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) {
861c3ab013aSZachary Turner   return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody);
862c3ab013aSZachary Turner }
863c3ab013aSZachary Turner 
864c3ab013aSZachary Turner Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) {
865c3ab013aSZachary Turner   return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody);
866c3ab013aSZachary Turner }
867c3ab013aSZachary Turner 
868c3ab013aSZachary Turner Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) {
869c3ab013aSZachary Turner   IconCursorGroupType Type;
870c3ab013aSZachary Turner   StringRef FileStr;
871c3ab013aSZachary Turner   IntOrString ResName = Base->ResName;
872c3ab013aSZachary Turner 
873c3ab013aSZachary Turner   if (auto *IconRes = dyn_cast<IconResource>(Base)) {
874c3ab013aSZachary Turner     FileStr = IconRes->IconLoc;
875c3ab013aSZachary Turner     Type = IconCursorGroupType::Icon;
876c3ab013aSZachary Turner   } else {
877c3ab013aSZachary Turner     auto *CursorRes = dyn_cast<CursorResource>(Base);
878c3ab013aSZachary Turner     FileStr = CursorRes->CursorLoc;
879c3ab013aSZachary Turner     Type = IconCursorGroupType::Cursor;
880c3ab013aSZachary Turner   }
881c3ab013aSZachary Turner 
882c3ab013aSZachary Turner   bool IsLong;
883c3ab013aSZachary Turner   stripQuotes(FileStr, IsLong);
884fa0ca6cbSZachary Turner   auto File = loadFile(FileStr);
885c3ab013aSZachary Turner 
886c3ab013aSZachary Turner   if (!File)
887fa0ca6cbSZachary Turner     return File.takeError();
888c3ab013aSZachary Turner 
889c3ab013aSZachary Turner   BinaryStreamReader Reader((*File)->getBuffer(), support::little);
890c3ab013aSZachary Turner 
891c3ab013aSZachary Turner   // Read the file headers.
892c3ab013aSZachary Turner   //   - At the beginning, ICONDIR/NEWHEADER header.
893c3ab013aSZachary Turner   //   - Then, a number of RESDIR headers follow. These contain offsets
894c3ab013aSZachary Turner   //       to data.
895c3ab013aSZachary Turner   const GroupIconDir *Header;
896c3ab013aSZachary Turner 
897c3ab013aSZachary Turner   RETURN_IF_ERROR(Reader.readObject(Header));
898c3ab013aSZachary Turner   if (Header->Reserved != 0)
899c3ab013aSZachary Turner     return createError("Incorrect icon/cursor Reserved field; should be 0.");
900c3ab013aSZachary Turner   uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2;
901c3ab013aSZachary Turner   if (Header->ResType != NeededType)
902c3ab013aSZachary Turner     return createError("Incorrect icon/cursor ResType field; should be " +
903c3ab013aSZachary Turner                        Twine(NeededType) + ".");
904c3ab013aSZachary Turner 
905c3ab013aSZachary Turner   uint16_t NumItems = Header->ResCount;
906c3ab013aSZachary Turner 
907c3ab013aSZachary Turner   // Read single ico/cur headers.
908c3ab013aSZachary Turner   std::vector<ResourceDirEntryStart> ItemEntries;
909c3ab013aSZachary Turner   ItemEntries.reserve(NumItems);
910c3ab013aSZachary Turner   std::vector<uint32_t> ItemOffsets(NumItems);
911c3ab013aSZachary Turner   for (size_t ID = 0; ID < NumItems; ++ID) {
912c3ab013aSZachary Turner     const ResourceDirEntryStart *Object;
913c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readObject(Object));
914c3ab013aSZachary Turner     ItemEntries.push_back(*Object);
915c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID]));
916c3ab013aSZachary Turner   }
917c3ab013aSZachary Turner 
918c3ab013aSZachary Turner   // Now write each icon/cursors one by one. At first, all the contents
919c3ab013aSZachary Turner   // without ICO/CUR header. This is described by SingleIconCursorResource.
920c3ab013aSZachary Turner   for (size_t ID = 0; ID < NumItems; ++ID) {
921c3ab013aSZachary Turner     // Load the fragment of file.
922c3ab013aSZachary Turner     Reader.setOffset(ItemOffsets[ID]);
923c3ab013aSZachary Turner     ArrayRef<uint8_t> Image;
924c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size));
92511adbacaSMartin Storsjo     SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image,
92611adbacaSMartin Storsjo                                        Base->MemoryFlags);
927c3ab013aSZachary Turner     SingleRes.setName(IconCursorID + ID);
928c3ab013aSZachary Turner     RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes));
929c3ab013aSZachary Turner   }
930c3ab013aSZachary Turner 
931c3ab013aSZachary Turner   // Now, write all the headers concatenated into a separate resource.
932c3ab013aSZachary Turner   for (size_t ID = 0; ID < NumItems; ++ID) {
933860e5fcdSMartin Storsjo     // We need to rewrite the cursor headers, and fetch actual values
934860e5fcdSMartin Storsjo     // for Planes/BitCount.
935c3ab013aSZachary Turner     const auto &OldHeader = ItemEntries[ID];
936860e5fcdSMartin Storsjo     ResourceDirEntryStart NewHeader = OldHeader;
937860e5fcdSMartin Storsjo 
938860e5fcdSMartin Storsjo     if (Type == IconCursorGroupType::Cursor) {
939c3ab013aSZachary Turner       NewHeader.Cursor.Width = OldHeader.Icon.Width;
940c3ab013aSZachary Turner       // Each cursor in fact stores two bitmaps, one under another.
941c3ab013aSZachary Turner       // Height provided in cursor definition describes the height of the
942c3ab013aSZachary Turner       // cursor, whereas the value existing in resource definition describes
943c3ab013aSZachary Turner       // the height of the bitmap. Therefore, we need to double this height.
944c3ab013aSZachary Turner       NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
945c3ab013aSZachary Turner 
946860e5fcdSMartin Storsjo       // Two WORDs were written at the beginning of the resource (hotspot
947860e5fcdSMartin Storsjo       // location). This is reflected in Size field.
948860e5fcdSMartin Storsjo       NewHeader.Size += 2 * sizeof(uint16_t);
949860e5fcdSMartin Storsjo     }
950860e5fcdSMartin Storsjo 
951c3ab013aSZachary Turner     // Now, we actually need to read the bitmap header to find
952c3ab013aSZachary Turner     // the number of planes and the number of bits per pixel.
953c3ab013aSZachary Turner     Reader.setOffset(ItemOffsets[ID]);
954c3ab013aSZachary Turner     const BitmapInfoHeader *BMPHeader;
955c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readObject(BMPHeader));
956860e5fcdSMartin Storsjo     if (BMPHeader->Size == sizeof(BitmapInfoHeader)) {
957c3ab013aSZachary Turner       NewHeader.Planes = BMPHeader->Planes;
958c3ab013aSZachary Turner       NewHeader.BitCount = BMPHeader->BitCount;
959860e5fcdSMartin Storsjo     } else {
960860e5fcdSMartin Storsjo       // A PNG .ico file.
961860e5fcdSMartin Storsjo       // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473
962860e5fcdSMartin Storsjo       // "The image must be in 32bpp"
963860e5fcdSMartin Storsjo       NewHeader.Planes = 1;
964860e5fcdSMartin Storsjo       NewHeader.BitCount = 32;
965860e5fcdSMartin Storsjo     }
966c3ab013aSZachary Turner 
967c3ab013aSZachary Turner     ItemEntries[ID] = NewHeader;
968c3ab013aSZachary Turner   }
969c3ab013aSZachary Turner 
970c3ab013aSZachary Turner   IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries));
971c3ab013aSZachary Turner   HeaderRes.setName(ResName);
97211adbacaSMartin Storsjo   if (Base->MemoryFlags & MfPreload) {
97311adbacaSMartin Storsjo     HeaderRes.MemoryFlags |= MfPreload;
97411adbacaSMartin Storsjo     HeaderRes.MemoryFlags &= ~MfPure;
97511adbacaSMartin Storsjo   }
976c3ab013aSZachary Turner   RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes));
977c3ab013aSZachary Turner 
978c3ab013aSZachary Turner   return Error::success();
979c3ab013aSZachary Turner }
980c3ab013aSZachary Turner 
9817f7745c0SMarek Sokolowski // --- DialogResource helpers. --- //
98222fccd64SMarek Sokolowski 
9837f7745c0SMarek Sokolowski Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
9847f7745c0SMarek Sokolowski                                                    bool IsExtended) {
9857f7745c0SMarek Sokolowski   // Each control should be aligned to DWORD.
9867f7745c0SMarek Sokolowski   padStream(sizeof(uint32_t));
9877f7745c0SMarek Sokolowski 
9887f7745c0SMarek Sokolowski   auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
989d0afe724SMartin Storsjo   IntWithNotMask CtlStyle(TypeInfo.Style);
990d0afe724SMartin Storsjo   CtlStyle |= Ctl.Style.getValueOr(RCInt(0));
9917f7745c0SMarek Sokolowski   uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
9927f7745c0SMarek Sokolowski 
9937f7745c0SMarek Sokolowski   // DIALOG(EX) item header prefix.
9947f7745c0SMarek Sokolowski   if (!IsExtended) {
9957f7745c0SMarek Sokolowski     struct {
9967f7745c0SMarek Sokolowski       ulittle32_t Style;
9977f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
998d0afe724SMartin Storsjo     } Prefix{ulittle32_t(CtlStyle.getValue()), ulittle32_t(CtlExtStyle)};
9997f7745c0SMarek Sokolowski     writeObject(Prefix);
10007f7745c0SMarek Sokolowski   } else {
10017f7745c0SMarek Sokolowski     struct {
10027f7745c0SMarek Sokolowski       ulittle32_t HelpID;
10037f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
10047f7745c0SMarek Sokolowski       ulittle32_t Style;
10057f7745c0SMarek Sokolowski     } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
1006d0afe724SMartin Storsjo              ulittle32_t(CtlStyle.getValue())};
10077f7745c0SMarek Sokolowski     writeObject(Prefix);
10087f7745c0SMarek Sokolowski   }
10097f7745c0SMarek Sokolowski 
10107f7745c0SMarek Sokolowski   // Common fixed-length part.
10117f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
10127f7745c0SMarek Sokolowski       Ctl.X, "Dialog control x-coordinate", true));
10137f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
10147f7745c0SMarek Sokolowski       Ctl.Y, "Dialog control y-coordinate", true));
10157f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
10167f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false));
10177f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
10187f7745c0SMarek Sokolowski       Ctl.Height, "Dialog control height", false));
10197f7745c0SMarek Sokolowski   struct {
10207f7745c0SMarek Sokolowski     ulittle16_t X;
10217f7745c0SMarek Sokolowski     ulittle16_t Y;
10227f7745c0SMarek Sokolowski     ulittle16_t Width;
10237f7745c0SMarek Sokolowski     ulittle16_t Height;
10247f7745c0SMarek Sokolowski   } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
10257f7745c0SMarek Sokolowski            ulittle16_t(Ctl.Height)};
10267f7745c0SMarek Sokolowski   writeObject(Middle);
10277f7745c0SMarek Sokolowski 
10287f7745c0SMarek Sokolowski   // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
10297f7745c0SMarek Sokolowski   if (!IsExtended) {
1030c1a67857SMartin Storsjo     // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't
1031c1a67857SMartin Storsjo     // want to refer to later.
1032c1a67857SMartin Storsjo     if (Ctl.ID != static_cast<uint32_t>(-1))
10337f7745c0SMarek Sokolowski       RETURN_IF_ERROR(checkNumberFits<uint16_t>(
10347f7745c0SMarek Sokolowski           Ctl.ID, "Control ID in simple DIALOG resource"));
10357f7745c0SMarek Sokolowski     writeInt<uint16_t>(Ctl.ID);
10367f7745c0SMarek Sokolowski   } else {
10377f7745c0SMarek Sokolowski     writeInt<uint32_t>(Ctl.ID);
10387f7745c0SMarek Sokolowski   }
10397f7745c0SMarek Sokolowski 
10407f7745c0SMarek Sokolowski   // Window class - either 0xFFFF + 16-bit integer or a string.
1041818bd568SMartin Storsjo   RETURN_IF_ERROR(writeIntOrString(Ctl.Class));
10427f7745c0SMarek Sokolowski 
10437f7745c0SMarek Sokolowski   // Element caption/reference ID. ID is preceded by 0xFFFF.
10447f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"));
10457f7745c0SMarek Sokolowski   RETURN_IF_ERROR(writeIntOrString(Ctl.Title));
10467f7745c0SMarek Sokolowski 
10477f7745c0SMarek Sokolowski   // # bytes of extra creation data count. Don't pass any.
10487f7745c0SMarek Sokolowski   writeInt<uint16_t>(0);
10497f7745c0SMarek Sokolowski 
10507f7745c0SMarek Sokolowski   return Error::success();
10517f7745c0SMarek Sokolowski }
10527f7745c0SMarek Sokolowski 
10537f7745c0SMarek Sokolowski Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
10547f7745c0SMarek Sokolowski   auto *Res = cast<DialogResource>(Base);
10557f7745c0SMarek Sokolowski 
10567f7745c0SMarek Sokolowski   // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
1057420090afSZachary Turner   const uint32_t DefaultStyle = 0x80880000;
1058420090afSZachary Turner   const uint32_t StyleFontFlag = 0x40;
1059420090afSZachary Turner   const uint32_t StyleCaptionFlag = 0x00C00000;
1060420090afSZachary Turner 
1061420090afSZachary Turner   uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle);
1062420090afSZachary Turner   if (ObjectData.Font)
1063420090afSZachary Turner     UsedStyle |= StyleFontFlag;
1064420090afSZachary Turner   else
1065420090afSZachary Turner     UsedStyle &= ~StyleFontFlag;
1066420090afSZachary Turner 
1067420090afSZachary Turner   // Actually, in case of empty (but existent) caption, the examined field
1068420090afSZachary Turner   // is equal to "\"\"". That's why empty captions are still noticed.
1069420090afSZachary Turner   if (ObjectData.Caption != "")
1070420090afSZachary Turner     UsedStyle |= StyleCaptionFlag;
1071420090afSZachary Turner 
1072420090afSZachary Turner   const uint16_t DialogExMagic = 0xFFFF;
1073a876b5c0SMartin Storsjo   uint32_t ExStyle = ObjectData.ExStyle.getValueOr(0);
10747f7745c0SMarek Sokolowski 
10757f7745c0SMarek Sokolowski   // Write DIALOG(EX) header prefix. These are pretty different.
10767f7745c0SMarek Sokolowski   if (!Res->IsExtended) {
1077420090afSZachary Turner     // We cannot let the higher word of DefaultStyle be equal to 0xFFFF.
1078420090afSZachary Turner     // In such a case, whole object (in .res file) is equivalent to a
1079420090afSZachary Turner     // DIALOGEX. It might lead to access violation/segmentation fault in
1080420090afSZachary Turner     // resource readers. For example,
1081420090afSZachary Turner     //   1 DIALOG 0, 0, 0, 65432
1082420090afSZachary Turner     //   STYLE 0xFFFF0001 {}
1083420090afSZachary Turner     // would be compiled to a DIALOGEX with 65432 controls.
1084420090afSZachary Turner     if ((UsedStyle >> 16) == DialogExMagic)
1085420090afSZachary Turner       return createError("16 higher bits of DIALOG resource style cannot be"
1086420090afSZachary Turner                          " equal to 0xFFFF");
1087420090afSZachary Turner 
10887f7745c0SMarek Sokolowski     struct {
10897f7745c0SMarek Sokolowski       ulittle32_t Style;
10907f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
10917f7745c0SMarek Sokolowski     } Prefix{ulittle32_t(UsedStyle),
1092a876b5c0SMartin Storsjo              ulittle32_t(ExStyle)};
10937f7745c0SMarek Sokolowski 
10947f7745c0SMarek Sokolowski     writeObject(Prefix);
10957f7745c0SMarek Sokolowski   } else {
10967f7745c0SMarek Sokolowski     struct {
10977f7745c0SMarek Sokolowski       ulittle16_t Version;
10987f7745c0SMarek Sokolowski       ulittle16_t Magic;
10997f7745c0SMarek Sokolowski       ulittle32_t HelpID;
11007f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
11017f7745c0SMarek Sokolowski       ulittle32_t Style;
11027f7745c0SMarek Sokolowski     } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
1103a876b5c0SMartin Storsjo              ulittle32_t(Res->HelpID), ulittle32_t(ExStyle), ulittle32_t(UsedStyle)};
11047f7745c0SMarek Sokolowski 
11057f7745c0SMarek Sokolowski     writeObject(Prefix);
11067f7745c0SMarek Sokolowski   }
11077f7745c0SMarek Sokolowski 
11087f7745c0SMarek Sokolowski   // Now, a common part. First, fixed-length fields.
11097f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),
11107f7745c0SMarek Sokolowski                                             "Number of dialog controls"));
11117f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
11127f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true));
11137f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
11147f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true));
11157f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
11167f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false));
11177f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
11187f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false));
11197f7745c0SMarek Sokolowski   struct {
11207f7745c0SMarek Sokolowski     ulittle16_t Count;
11217f7745c0SMarek Sokolowski     ulittle16_t PosX;
11227f7745c0SMarek Sokolowski     ulittle16_t PosY;
11237f7745c0SMarek Sokolowski     ulittle16_t DialogWidth;
11247f7745c0SMarek Sokolowski     ulittle16_t DialogHeight;
11257f7745c0SMarek Sokolowski   } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
11267f7745c0SMarek Sokolowski            ulittle16_t(Res->Y), ulittle16_t(Res->Width),
11277f7745c0SMarek Sokolowski            ulittle16_t(Res->Height)};
11287f7745c0SMarek Sokolowski   writeObject(Middle);
11297f7745c0SMarek Sokolowski 
11307f7745c0SMarek Sokolowski   // MENU field. As of now, we don't keep them in the state and can peacefully
11317f7745c0SMarek Sokolowski   // think there is no menu attached to the dialog.
11327f7745c0SMarek Sokolowski   writeInt<uint16_t>(0);
11337f7745c0SMarek Sokolowski 
1134e241ce6fSMartin Storsjo   // Window CLASS field.
1135e241ce6fSMartin Storsjo   RETURN_IF_ERROR(writeIntOrString(ObjectData.Class));
11367f7745c0SMarek Sokolowski 
1137420090afSZachary Turner   // Window title or a single word equal to 0.
1138420090afSZachary Turner   RETURN_IF_ERROR(writeCString(ObjectData.Caption));
1139420090afSZachary Turner 
1140420090afSZachary Turner   // If there *is* a window font declared, output its data.
1141420090afSZachary Turner   auto &Font = ObjectData.Font;
1142420090afSZachary Turner   if (Font) {
1143420090afSZachary Turner     writeInt<uint16_t>(Font->Size);
1144420090afSZachary Turner     // Additional description occurs only in DIALOGEX.
1145420090afSZachary Turner     if (Res->IsExtended) {
1146420090afSZachary Turner       writeInt<uint16_t>(Font->Weight);
1147420090afSZachary Turner       writeInt<uint8_t>(Font->IsItalic);
1148420090afSZachary Turner       writeInt<uint8_t>(Font->Charset);
1149420090afSZachary Turner     }
1150420090afSZachary Turner     RETURN_IF_ERROR(writeCString(Font->Typeface));
1151420090afSZachary Turner   }
11527f7745c0SMarek Sokolowski 
11537f7745c0SMarek Sokolowski   auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
11547f7745c0SMarek Sokolowski     if (!Err)
11557f7745c0SMarek Sokolowski       return Error::success();
11567f7745c0SMarek Sokolowski     return joinErrors(createError("Error in " + Twine(Ctl.Type) +
11577f7745c0SMarek Sokolowski                                   " control  (ID " + Twine(Ctl.ID) + "):"),
11587f7745c0SMarek Sokolowski                       std::move(Err));
11597f7745c0SMarek Sokolowski   };
11607f7745c0SMarek Sokolowski 
11617f7745c0SMarek Sokolowski   for (auto &Ctl : Res->Controls)
11627f7745c0SMarek Sokolowski     RETURN_IF_ERROR(
11637f7745c0SMarek Sokolowski         handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl));
11647f7745c0SMarek Sokolowski 
11657f7745c0SMarek Sokolowski   return Error::success();
11667f7745c0SMarek Sokolowski }
11677f7745c0SMarek Sokolowski 
11687f7745c0SMarek Sokolowski // --- HTMLResource helpers. --- //
116922fccd64SMarek Sokolowski 
11708f19343aSMarek Sokolowski Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
11718f19343aSMarek Sokolowski   return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
11728f19343aSMarek Sokolowski }
11738f19343aSMarek Sokolowski 
117442f494d6SMarek Sokolowski // --- MenuResource helpers. --- //
117542f494d6SMarek Sokolowski 
117642f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuDefinition(
117742f494d6SMarek Sokolowski     const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
117842f494d6SMarek Sokolowski   assert(Def);
117942f494d6SMarek Sokolowski   const MenuDefinition *DefPtr = Def.get();
118042f494d6SMarek Sokolowski 
118142f494d6SMarek Sokolowski   if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
118242f494d6SMarek Sokolowski     writeInt<uint16_t>(Flags);
11838330dcadSMartin Storsjö     // Some resource files use -1, i.e. UINT32_MAX, for empty menu items.
11848330dcadSMartin Storsjö     if (MenuItemPtr->Id != static_cast<uint32_t>(-1))
118542f494d6SMarek Sokolowski       RETURN_IF_ERROR(
118642f494d6SMarek Sokolowski           checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"));
118742f494d6SMarek Sokolowski     writeInt<uint16_t>(MenuItemPtr->Id);
118842f494d6SMarek Sokolowski     RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
118942f494d6SMarek Sokolowski     return Error::success();
119042f494d6SMarek Sokolowski   }
119142f494d6SMarek Sokolowski 
119242f494d6SMarek Sokolowski   if (isa<MenuSeparator>(DefPtr)) {
119342f494d6SMarek Sokolowski     writeInt<uint16_t>(Flags);
119442f494d6SMarek Sokolowski     writeInt<uint32_t>(0);
119542f494d6SMarek Sokolowski     return Error::success();
119642f494d6SMarek Sokolowski   }
119742f494d6SMarek Sokolowski 
119842f494d6SMarek Sokolowski   auto *PopupPtr = cast<PopupItem>(DefPtr);
119942f494d6SMarek Sokolowski   writeInt<uint16_t>(Flags);
120042f494d6SMarek Sokolowski   RETURN_IF_ERROR(writeCString(PopupPtr->Name));
120142f494d6SMarek Sokolowski   return writeMenuDefinitionList(PopupPtr->SubItems);
120242f494d6SMarek Sokolowski }
120342f494d6SMarek Sokolowski 
120442f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuDefinitionList(
120542f494d6SMarek Sokolowski     const MenuDefinitionList &List) {
120642f494d6SMarek Sokolowski   for (auto &Def : List.Definitions) {
120742f494d6SMarek Sokolowski     uint16_t Flags = Def->getResFlags();
120842f494d6SMarek Sokolowski     // Last element receives an additional 0x80 flag.
120942f494d6SMarek Sokolowski     const uint16_t LastElementFlag = 0x0080;
121042f494d6SMarek Sokolowski     if (&Def == &List.Definitions.back())
121142f494d6SMarek Sokolowski       Flags |= LastElementFlag;
121242f494d6SMarek Sokolowski 
121342f494d6SMarek Sokolowski     RETURN_IF_ERROR(writeMenuDefinition(Def, Flags));
121442f494d6SMarek Sokolowski   }
121542f494d6SMarek Sokolowski   return Error::success();
121642f494d6SMarek Sokolowski }
121742f494d6SMarek Sokolowski 
121842f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
121942f494d6SMarek Sokolowski   // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
122042f494d6SMarek Sokolowski   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
12211e2c8b11SZachary Turner   writeInt<uint32_t>(0);
122242f494d6SMarek Sokolowski 
122342f494d6SMarek Sokolowski   return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
122442f494d6SMarek Sokolowski }
122542f494d6SMarek Sokolowski 
1226da366693SZachary Turner // --- StringTableResource helpers. --- //
1227da366693SZachary Turner 
1228da366693SZachary Turner class BundleResource : public RCResource {
1229da366693SZachary Turner public:
1230da366693SZachary Turner   using BundleType = ResourceFileWriter::StringTableInfo::Bundle;
1231da366693SZachary Turner   BundleType Bundle;
1232da366693SZachary Turner 
123311adbacaSMartin Storsjo   BundleResource(const BundleType &StrBundle)
123411adbacaSMartin Storsjo       : RCResource(StrBundle.MemoryFlags), Bundle(StrBundle) {}
1235da366693SZachary Turner   IntOrString getResourceType() const override { return 6; }
1236da366693SZachary Turner 
1237da366693SZachary Turner   ResourceKind getKind() const override { return RkStringTableBundle; }
1238da366693SZachary Turner   static bool classof(const RCResource *Res) {
1239da366693SZachary Turner     return Res->getKind() == RkStringTableBundle;
1240da366693SZachary Turner   }
1241d1d046aaSMartin Storsjo   Twine getResourceTypeName() const override { return "STRINGTABLE"; }
1242da366693SZachary Turner };
1243da366693SZachary Turner 
1244da366693SZachary Turner Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) {
1245da366693SZachary Turner   return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody);
1246da366693SZachary Turner }
1247da366693SZachary Turner 
1248da366693SZachary Turner Error ResourceFileWriter::insertStringIntoBundle(
1249*b989fcbaSMartin Storsjö     StringTableInfo::Bundle &Bundle, uint16_t StringID,
1250*b989fcbaSMartin Storsjö     const std::vector<StringRef> &String) {
1251da366693SZachary Turner   uint16_t StringLoc = StringID & 15;
1252da366693SZachary Turner   if (Bundle.Data[StringLoc])
1253da366693SZachary Turner     return createError("Multiple STRINGTABLE strings located under ID " +
1254da366693SZachary Turner                        Twine(StringID));
1255da366693SZachary Turner   Bundle.Data[StringLoc] = String;
1256da366693SZachary Turner   return Error::success();
1257da366693SZachary Turner }
1258da366693SZachary Turner 
1259da366693SZachary Turner Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) {
1260da366693SZachary Turner   auto *Res = cast<BundleResource>(Base);
1261da366693SZachary Turner   for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) {
1262da366693SZachary Turner     // The string format is a tiny bit different here. We
1263da366693SZachary Turner     // first output the size of the string, and then the string itself
1264da366693SZachary Turner     // (which is not null-terminated).
1265da366693SZachary Turner     SmallVector<UTF16, 128> Data;
1266*b989fcbaSMartin Storsjö     if (Res->Bundle.Data[ID]) {
1267*b989fcbaSMartin Storsjö       bool IsLongString;
1268*b989fcbaSMartin Storsjö       for (StringRef S : *Res->Bundle.Data[ID])
1269*b989fcbaSMartin Storsjö         RETURN_IF_ERROR(processString(S, NullHandlingMethod::CutAtDoubleNull,
1270d1d046aaSMartin Storsjo                                       IsLongString, Data, Params.CodePage));
1271*b989fcbaSMartin Storsjö       if (AppendNull)
1272da366693SZachary Turner         Data.push_back('\0');
1273*b989fcbaSMartin Storsjö     }
1274da366693SZachary Turner     RETURN_IF_ERROR(
1275da366693SZachary Turner         checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"));
1276b12792a4SZachary Turner     writeInt<uint16_t>(Data.size());
1277da366693SZachary Turner     for (auto Char : Data)
1278b12792a4SZachary Turner       writeInt(Char);
1279da366693SZachary Turner   }
1280da366693SZachary Turner   return Error::success();
1281da366693SZachary Turner }
1282da366693SZachary Turner 
1283da366693SZachary Turner Error ResourceFileWriter::dumpAllStringTables() {
1284da366693SZachary Turner   for (auto Key : StringTableData.BundleList) {
1285da366693SZachary Turner     auto Iter = StringTableData.BundleData.find(Key);
1286da366693SZachary Turner     assert(Iter != StringTableData.BundleData.end());
1287da366693SZachary Turner 
1288da366693SZachary Turner     // For a moment, revert the context info to moment of bundle declaration.
1289da366693SZachary Turner     ContextKeeper RAII(this);
1290da366693SZachary Turner     ObjectData = Iter->second.DeclTimeInfo;
1291da366693SZachary Turner 
1292da366693SZachary Turner     BundleResource Res(Iter->second);
1293da366693SZachary Turner     // Bundle #(k+1) contains keys [16k, 16k + 15].
1294da366693SZachary Turner     Res.setName(Key.first + 1);
1295da366693SZachary Turner     RETURN_IF_ERROR(visitStringTableBundle(&Res));
1296da366693SZachary Turner   }
1297da366693SZachary Turner   return Error::success();
1298da366693SZachary Turner }
1299da366693SZachary Turner 
13009d8b358aSZachary Turner // --- UserDefinedResource helpers. --- //
13019d8b358aSZachary Turner 
13029d8b358aSZachary Turner Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) {
13039d8b358aSZachary Turner   auto *Res = cast<UserDefinedResource>(Base);
13049d8b358aSZachary Turner 
13059d8b358aSZachary Turner   if (Res->IsFileResource)
13069d8b358aSZachary Turner     return appendFile(Res->FileLoc);
13079d8b358aSZachary Turner 
13089d8b358aSZachary Turner   for (auto &Elem : Res->Contents) {
13099d8b358aSZachary Turner     if (Elem.isInt()) {
13109d8b358aSZachary Turner       RETURN_IF_ERROR(
13119d8b358aSZachary Turner           checkRCInt(Elem.getInt(), "Number in user-defined resource"));
13129d8b358aSZachary Turner       writeRCInt(Elem.getInt());
13139d8b358aSZachary Turner       continue;
13149d8b358aSZachary Turner     }
13159d8b358aSZachary Turner 
13169d8b358aSZachary Turner     SmallVector<UTF16, 128> ProcessedString;
13179d8b358aSZachary Turner     bool IsLongString;
1318d1d046aaSMartin Storsjo     RETURN_IF_ERROR(
1319d1d046aaSMartin Storsjo         processString(Elem.getString(), NullHandlingMethod::UserResource,
1320d1d046aaSMartin Storsjo                       IsLongString, ProcessedString, Params.CodePage));
13219d8b358aSZachary Turner 
13229d8b358aSZachary Turner     for (auto Ch : ProcessedString) {
13239d8b358aSZachary Turner       if (IsLongString) {
1324b12792a4SZachary Turner         writeInt(Ch);
13259d8b358aSZachary Turner         continue;
13269d8b358aSZachary Turner       }
13279d8b358aSZachary Turner 
13289d8b358aSZachary Turner       RETURN_IF_ERROR(checkNumberFits<uint8_t>(
132975f9ae2bSSimon Dardis           Ch, "Character in narrow string in user-defined resource"));
1330b12792a4SZachary Turner       writeInt<uint8_t>(Ch);
13319d8b358aSZachary Turner     }
13329d8b358aSZachary Turner   }
13339d8b358aSZachary Turner 
13349d8b358aSZachary Turner   return Error::success();
13359d8b358aSZachary Turner }
13369d8b358aSZachary Turner 
133707bc04ffSZachary Turner // --- VersionInfoResourceResource helpers. --- //
133807bc04ffSZachary Turner 
133907bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
134007bc04ffSZachary Turner   // Output the header if the block has name.
134107bc04ffSZachary Turner   bool OutputHeader = Blk.Name != "";
134207bc04ffSZachary Turner   uint64_t LengthLoc;
134307bc04ffSZachary Turner 
134428ae894aSMartin Storsjo   padStream(sizeof(uint32_t));
134507bc04ffSZachary Turner   if (OutputHeader) {
13461e2c8b11SZachary Turner     LengthLoc = writeInt<uint16_t>(0);
13471e2c8b11SZachary Turner     writeInt<uint16_t>(0);
13481e2c8b11SZachary Turner     writeInt<uint16_t>(1); // true
134907bc04ffSZachary Turner     RETURN_IF_ERROR(writeCString(Blk.Name));
135007bc04ffSZachary Turner     padStream(sizeof(uint32_t));
135107bc04ffSZachary Turner   }
135207bc04ffSZachary Turner 
135307bc04ffSZachary Turner   for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) {
135407bc04ffSZachary Turner     VersionInfoStmt *ItemPtr = Item.get();
135507bc04ffSZachary Turner 
135607bc04ffSZachary Turner     if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) {
135707bc04ffSZachary Turner       RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr));
135807bc04ffSZachary Turner       continue;
135907bc04ffSZachary Turner     }
136007bc04ffSZachary Turner 
136107bc04ffSZachary Turner     auto *ValuePtr = cast<VersionInfoValue>(ItemPtr);
136207bc04ffSZachary Turner     RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr));
136307bc04ffSZachary Turner   }
136407bc04ffSZachary Turner 
136507bc04ffSZachary Turner   if (OutputHeader) {
136607bc04ffSZachary Turner     uint64_t CurLoc = tell();
136707bc04ffSZachary Turner     writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
136807bc04ffSZachary Turner   }
136907bc04ffSZachary Turner 
137007bc04ffSZachary Turner   return Error::success();
137107bc04ffSZachary Turner }
137207bc04ffSZachary Turner 
137307bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
137407bc04ffSZachary Turner   // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE
137507bc04ffSZachary Turner   // is a mapping from the key (string) to the value (a sequence of ints or
137607bc04ffSZachary Turner   // a sequence of strings).
137707bc04ffSZachary Turner   //
137807bc04ffSZachary Turner   // If integers are to be written: width of each integer written depends on
137907bc04ffSZachary Turner   // whether it's been declared 'long' (it's DWORD then) or not (it's WORD).
138007bc04ffSZachary Turner   // ValueLength defined in structure referenced below is then the total
138107bc04ffSZachary Turner   // number of bytes taken by these integers.
138207bc04ffSZachary Turner   //
138307bc04ffSZachary Turner   // If strings are to be written: characters are always WORDs.
138407bc04ffSZachary Turner   // Moreover, '\0' character is written after the last string, and between
138507bc04ffSZachary Turner   // every two strings separated by comma (if strings are not comma-separated,
138607bc04ffSZachary Turner   // they're simply concatenated). ValueLength is equal to the number of WORDs
138707bc04ffSZachary Turner   // written (that is, half of the bytes written).
138807bc04ffSZachary Turner   //
138907bc04ffSZachary Turner   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx
139007bc04ffSZachary Turner   bool HasStrings = false, HasInts = false;
139107bc04ffSZachary Turner   for (auto &Item : Val.Values)
139207bc04ffSZachary Turner     (Item.isInt() ? HasInts : HasStrings) = true;
139307bc04ffSZachary Turner 
139407bc04ffSZachary Turner   assert((HasStrings || HasInts) && "VALUE must have at least one argument");
139507bc04ffSZachary Turner   if (HasStrings && HasInts)
139607bc04ffSZachary Turner     return createError(Twine("VALUE ") + Val.Key +
139707bc04ffSZachary Turner                        " cannot contain both strings and integers");
139807bc04ffSZachary Turner 
139928ae894aSMartin Storsjo   padStream(sizeof(uint32_t));
14001e2c8b11SZachary Turner   auto LengthLoc = writeInt<uint16_t>(0);
14011e2c8b11SZachary Turner   auto ValLengthLoc = writeInt<uint16_t>(0);
14021e2c8b11SZachary Turner   writeInt<uint16_t>(HasStrings);
140307bc04ffSZachary Turner   RETURN_IF_ERROR(writeCString(Val.Key));
140407bc04ffSZachary Turner   padStream(sizeof(uint32_t));
140507bc04ffSZachary Turner 
140607bc04ffSZachary Turner   auto DataLoc = tell();
140707bc04ffSZachary Turner   for (size_t Id = 0; Id < Val.Values.size(); ++Id) {
140807bc04ffSZachary Turner     auto &Item = Val.Values[Id];
140907bc04ffSZachary Turner     if (Item.isInt()) {
141007bc04ffSZachary Turner       auto Value = Item.getInt();
141107bc04ffSZachary Turner       RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value"));
141207bc04ffSZachary Turner       writeRCInt(Value);
141307bc04ffSZachary Turner       continue;
141407bc04ffSZachary Turner     }
141507bc04ffSZachary Turner 
141607bc04ffSZachary Turner     bool WriteTerminator =
141707bc04ffSZachary Turner         Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1];
141807bc04ffSZachary Turner     RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator));
141907bc04ffSZachary Turner   }
142007bc04ffSZachary Turner 
142107bc04ffSZachary Turner   auto CurLoc = tell();
142207bc04ffSZachary Turner   auto ValueLength = CurLoc - DataLoc;
142307bc04ffSZachary Turner   if (HasStrings) {
142407bc04ffSZachary Turner     assert(ValueLength % 2 == 0);
142507bc04ffSZachary Turner     ValueLength /= 2;
142607bc04ffSZachary Turner   }
142707bc04ffSZachary Turner   writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
142807bc04ffSZachary Turner   writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
142907bc04ffSZachary Turner   return Error::success();
143007bc04ffSZachary Turner }
143107bc04ffSZachary Turner 
143207bc04ffSZachary Turner template <typename Ty>
143307bc04ffSZachary Turner static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key,
143407bc04ffSZachary Turner                          const Ty &Default) {
143507bc04ffSZachary Turner   auto Iter = Map.find(Key);
143607bc04ffSZachary Turner   if (Iter != Map.end())
143707bc04ffSZachary Turner     return Iter->getValue();
143807bc04ffSZachary Turner   return Default;
143907bc04ffSZachary Turner }
144007bc04ffSZachary Turner 
144107bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) {
144207bc04ffSZachary Turner   auto *Res = cast<VersionInfoResource>(Base);
144307bc04ffSZachary Turner 
144407bc04ffSZachary Turner   const auto &FixedData = Res->FixedData;
144507bc04ffSZachary Turner 
144607bc04ffSZachary Turner   struct /* VS_FIXEDFILEINFO */ {
144707bc04ffSZachary Turner     ulittle32_t Signature = ulittle32_t(0xFEEF04BD);
144807bc04ffSZachary Turner     ulittle32_t StructVersion = ulittle32_t(0x10000);
144907bc04ffSZachary Turner     // It's weird to have most-significant DWORD first on the little-endian
145007bc04ffSZachary Turner     // machines, but let it be this way.
145107bc04ffSZachary Turner     ulittle32_t FileVersionMS;
145207bc04ffSZachary Turner     ulittle32_t FileVersionLS;
145307bc04ffSZachary Turner     ulittle32_t ProductVersionMS;
145407bc04ffSZachary Turner     ulittle32_t ProductVersionLS;
145507bc04ffSZachary Turner     ulittle32_t FileFlagsMask;
145607bc04ffSZachary Turner     ulittle32_t FileFlags;
145707bc04ffSZachary Turner     ulittle32_t FileOS;
145807bc04ffSZachary Turner     ulittle32_t FileType;
145907bc04ffSZachary Turner     ulittle32_t FileSubtype;
146007bc04ffSZachary Turner     // MS implementation seems to always set these fields to 0.
146107bc04ffSZachary Turner     ulittle32_t FileDateMS = ulittle32_t(0);
146207bc04ffSZachary Turner     ulittle32_t FileDateLS = ulittle32_t(0);
146307bc04ffSZachary Turner   } FixedInfo;
146407bc04ffSZachary Turner 
146507bc04ffSZachary Turner   // First, VS_VERSIONINFO.
1466b12792a4SZachary Turner   auto LengthLoc = writeInt<uint16_t>(0);
1467b12792a4SZachary Turner   writeInt<uint16_t>(sizeof(FixedInfo));
1468b12792a4SZachary Turner   writeInt<uint16_t>(0);
146907bc04ffSZachary Turner   cantFail(writeCString("VS_VERSION_INFO"));
147007bc04ffSZachary Turner   padStream(sizeof(uint32_t));
147107bc04ffSZachary Turner 
147207bc04ffSZachary Turner   using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
147307bc04ffSZachary Turner   auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) {
147407bc04ffSZachary Turner     static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0};
147507bc04ffSZachary Turner     if (!FixedData.IsTypePresent[(int)Type])
147607bc04ffSZachary Turner       return DefaultOut;
147707bc04ffSZachary Turner     return FixedData.FixedInfo[(int)Type];
147807bc04ffSZachary Turner   };
147907bc04ffSZachary Turner 
148007bc04ffSZachary Turner   auto FileVer = GetField(VersionInfoFixed::FtFileVersion);
148107bc04ffSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint16_t>(
148207bc04ffSZachary Turner       *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields"));
148307bc04ffSZachary Turner   FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1];
148407bc04ffSZachary Turner   FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3];
148507bc04ffSZachary Turner 
148607bc04ffSZachary Turner   auto ProdVer = GetField(VersionInfoFixed::FtProductVersion);
148707bc04ffSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint16_t>(
148807bc04ffSZachary Turner       *std::max_element(ProdVer.begin(), ProdVer.end()),
148907bc04ffSZachary Turner       "PRODUCTVERSION fields"));
149007bc04ffSZachary Turner   FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1];
149107bc04ffSZachary Turner   FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3];
149207bc04ffSZachary Turner 
149307bc04ffSZachary Turner   FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0];
149407bc04ffSZachary Turner   FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0];
149507bc04ffSZachary Turner   FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0];
149607bc04ffSZachary Turner   FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0];
149707bc04ffSZachary Turner   FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0];
149807bc04ffSZachary Turner 
149907bc04ffSZachary Turner   writeObject(FixedInfo);
150007bc04ffSZachary Turner   padStream(sizeof(uint32_t));
150107bc04ffSZachary Turner 
150207bc04ffSZachary Turner   RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock));
150307bc04ffSZachary Turner 
150407bc04ffSZachary Turner   // FIXME: check overflow?
150507bc04ffSZachary Turner   writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc);
150607bc04ffSZachary Turner 
150707bc04ffSZachary Turner   return Error::success();
150807bc04ffSZachary Turner }
150907bc04ffSZachary Turner 
1510fa0ca6cbSZachary Turner Expected<std::unique_ptr<MemoryBuffer>>
1511fa0ca6cbSZachary Turner ResourceFileWriter::loadFile(StringRef File) const {
1512fa0ca6cbSZachary Turner   SmallString<128> Path;
1513fa0ca6cbSZachary Turner   SmallString<128> Cwd;
1514fa0ca6cbSZachary Turner   std::unique_ptr<MemoryBuffer> Result;
1515fa0ca6cbSZachary Turner 
15161f597e6eSPetr Hosek   // 0. The file path is absolute and the file exists.
15171f597e6eSPetr Hosek   if (sys::path::is_absolute(File))
15181f597e6eSPetr Hosek     return errorOrToExpected(MemoryBuffer::getFile(File, -1, false));
15191f597e6eSPetr Hosek 
1520fa0ca6cbSZachary Turner   // 1. The current working directory.
1521fa0ca6cbSZachary Turner   sys::fs::current_path(Cwd);
1522fa0ca6cbSZachary Turner   Path.assign(Cwd.begin(), Cwd.end());
1523fa0ca6cbSZachary Turner   sys::path::append(Path, File);
1524fa0ca6cbSZachary Turner   if (sys::fs::exists(Path))
1525f22a25e6SZachary Turner     return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1526fa0ca6cbSZachary Turner 
1527fa0ca6cbSZachary Turner   // 2. The directory of the input resource file, if it is different from the
15281f597e6eSPetr Hosek   // current working directory.
1529fa0ca6cbSZachary Turner   StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath);
1530fa0ca6cbSZachary Turner   Path.assign(InputFileDir.begin(), InputFileDir.end());
1531fa0ca6cbSZachary Turner   sys::path::append(Path, File);
1532fa0ca6cbSZachary Turner   if (sys::fs::exists(Path))
1533f22a25e6SZachary Turner     return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1534fa0ca6cbSZachary Turner 
1535fa0ca6cbSZachary Turner   // 3. All of the include directories specified on the command line.
1536fa0ca6cbSZachary Turner   for (StringRef ForceInclude : Params.Include) {
1537fa0ca6cbSZachary Turner     Path.assign(ForceInclude.begin(), ForceInclude.end());
1538fa0ca6cbSZachary Turner     sys::path::append(Path, File);
1539fa0ca6cbSZachary Turner     if (sys::fs::exists(Path))
1540f22a25e6SZachary Turner       return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1541fa0ca6cbSZachary Turner   }
1542fa0ca6cbSZachary Turner 
1543fa0ca6cbSZachary Turner   if (auto Result =
1544fa0ca6cbSZachary Turner           llvm::sys::Process::FindInEnvPath("INCLUDE", File, Params.NoInclude))
1545f22a25e6SZachary Turner     return errorOrToExpected(MemoryBuffer::getFile(*Result, -1, false));
1546fa0ca6cbSZachary Turner 
1547fa0ca6cbSZachary Turner   return make_error<StringError>("error : file not found : " + Twine(File),
1548fa0ca6cbSZachary Turner                                  inconvertibleErrorCode());
1549fa0ca6cbSZachary Turner }
1550fa0ca6cbSZachary Turner 
15518f19343aSMarek Sokolowski } // namespace rc
15528f19343aSMarek Sokolowski } // namespace llvm
1553