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