summaryrefslogtreecommitdiff
path: root/tools/llvm-rc/ResourceFileWriter.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2018-07-28 10:51:19 +0000
committerDimitry Andric <dim@FreeBSD.org>2018-07-28 10:51:19 +0000
commiteb11fae6d08f479c0799db45860a98af528fa6e7 (patch)
tree44d492a50c8c1a7eb8e2d17ea3360ec4d066f042 /tools/llvm-rc/ResourceFileWriter.cpp
parentb8a2042aa938069e862750553db0e4d82d25822c (diff)
Notes
Diffstat (limited to 'tools/llvm-rc/ResourceFileWriter.cpp')
-rw-r--r--tools/llvm-rc/ResourceFileWriter.cpp195
1 files changed, 142 insertions, 53 deletions
diff --git a/tools/llvm-rc/ResourceFileWriter.cpp b/tools/llvm-rc/ResourceFileWriter.cpp
index f141dc7e3564..4b561940d2ec 100644
--- a/tools/llvm-rc/ResourceFileWriter.cpp
+++ b/tools/llvm-rc/ResourceFileWriter.cpp
@@ -110,6 +110,18 @@ static bool stripQuotes(StringRef &Str, bool &IsLongString) {
return true;
}
+static UTF16 cp1252ToUnicode(unsigned char C) {
+ static const UTF16 Map80[] = {
+ 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
+ 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
+ 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
+ 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178,
+ };
+ if (C >= 0x80 && C <= 0x9F)
+ return Map80[C - 0x80];
+ return C;
+}
+
// Describes a way to handle '\0' characters when processing the string.
// rc.exe tool sometimes behaves in a weird way in postprocessing.
// If the string to be output is equivalent to a C-string (e.g. in MENU
@@ -132,10 +144,26 @@ enum class NullHandlingMethod {
// * Replace the escape sequences with their processed version.
// For identifiers, this is no-op.
static Error processString(StringRef Str, NullHandlingMethod NullHandler,
- bool &IsLongString, SmallVectorImpl<UTF16> &Result) {
+ bool &IsLongString, SmallVectorImpl<UTF16> &Result,
+ int CodePage) {
bool IsString = stripQuotes(Str, IsLongString);
SmallVector<UTF16, 128> Chars;
- convertUTF8ToUTF16String(Str, Chars);
+
+ // Convert the input bytes according to the chosen codepage.
+ if (CodePage == CpUtf8) {
+ convertUTF8ToUTF16String(Str, Chars);
+ } else if (CodePage == CpWin1252) {
+ for (char C : Str)
+ Chars.push_back(cp1252ToUnicode((unsigned char)C));
+ } else {
+ // For other, unknown codepages, only allow plain ASCII input.
+ for (char C : Str) {
+ if ((unsigned char)C > 0x7F)
+ return createError("Non-ASCII 8-bit codepoint (" + Twine(C) +
+ ") can't be interpreted in the current codepage");
+ Chars.push_back((unsigned char)C);
+ }
+ }
if (!IsString) {
// It's an identifier if it's not a string. Make all characters uppercase.
@@ -157,21 +185,35 @@ static Error processString(StringRef Str, NullHandlingMethod NullHandler,
if (Char > 0xFF)
return createError("Non-8-bit codepoint (" + Twine(Char) +
") can't occur in a user-defined narrow string");
+ }
+ }
+ Result.push_back(Char);
+ return Error::success();
+ };
+ auto AddEscapedChar = [AddRes, IsLongString, CodePage](UTF16 Char) -> Error {
+ if (!IsLongString) {
+ // Escaped chars in narrow strings have to be interpreted according to
+ // the chosen code page.
+ if (Char > 0xFF)
+ return createError("Non-8-bit escaped char (" + Twine(Char) +
+ ") can't occur in narrow string");
+ if (CodePage == CpUtf8) {
+ if (Char >= 0x80)
+ return createError("Unable to interpret single byte (" + Twine(Char) +
+ ") as UTF-8");
+ } else if (CodePage == CpWin1252) {
+ Char = cp1252ToUnicode(Char);
} else {
- // In case of narrow non-user strings, Windows RC converts
- // [0x80, 0xFF] chars according to the current codepage.
- // There is no 'codepage' concept settled in every supported platform,
- // so we should reject such inputs.
- if (Char > 0x7F && Char <= 0xFF)
+ // Unknown/unsupported codepage, only allow ASCII input.
+ if (Char > 0x7F)
return createError("Non-ASCII 8-bit codepoint (" + Twine(Char) +
") can't "
"occur in a non-Unicode string");
}
}
- Result.push_back(Char);
- return Error::success();
+ return AddRes(Char);
};
while (Pos < Chars.size()) {
@@ -223,7 +265,7 @@ static Error processString(StringRef Str, NullHandlingMethod NullHandler,
--RemainingChars;
}
- RETURN_IF_ERROR(AddRes(ReadInt));
+ RETURN_IF_ERROR(AddEscapedChar(ReadInt));
continue;
}
@@ -240,7 +282,7 @@ static Error processString(StringRef Str, NullHandlingMethod NullHandler,
++Pos;
}
- RETURN_IF_ERROR(AddRes(ReadInt));
+ RETURN_IF_ERROR(AddEscapedChar(ReadInt));
continue;
}
@@ -328,7 +370,8 @@ Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) {
SmallVector<UTF16, 128> ProcessedString;
bool IsLongString;
RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull,
- IsLongString, ProcessedString));
+ IsLongString, ProcessedString,
+ Params.CodePage));
for (auto Ch : ProcessedString)
writeInt<uint16_t>(Ch);
if (WriteTerminator)
@@ -394,6 +437,10 @@ Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) {
return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody);
}
+Error ResourceFileWriter::visitBitmapResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeBitmapBody);
+}
+
Error ResourceFileWriter::visitCursorResource(const RCResource *Res) {
return handleError(visitIconOrCursorResource(Res), Res);
}
@@ -411,6 +458,11 @@ Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) {
return Error::success();
}
+Error ResourceFileWriter::visitClassStmt(const ClassStmt *Stmt) {
+ ObjectData.Class = Stmt->Value;
+ return Error::success();
+}
+
Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
}
@@ -435,8 +487,8 @@ Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) {
if (Iter == BundleData.end()) {
// Need to create a bundle.
StringTableData.BundleList.push_back(Key);
- auto EmplaceResult =
- BundleData.emplace(Key, StringTableInfo::Bundle(ObjectData));
+ auto EmplaceResult = BundleData.emplace(
+ Key, StringTableInfo::Bundle(ObjectData, Res->MemoryFlags));
assert(EmplaceResult.second && "Could not create a bundle");
Iter = EmplaceResult.first;
}
@@ -509,7 +561,7 @@ Error ResourceFileWriter::writeResource(
padStream(sizeof(uint32_t));
object::WinResHeaderSuffix HeaderSuffix{
ulittle32_t(0), // DataVersion; seems to always be 0
- ulittle16_t(Res->getMemoryFlags()), ulittle16_t(ObjectData.LanguageInfo),
+ ulittle16_t(Res->MemoryFlags), ulittle16_t(ObjectData.LanguageInfo),
ulittle32_t(ObjectData.VersionInfo),
ulittle32_t(ObjectData.Characteristics)};
writeObject(HeaderSuffix);
@@ -641,6 +693,29 @@ Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
return Error::success();
}
+// --- BitmapResource helpers. --- //
+
+Error ResourceFileWriter::writeBitmapBody(const RCResource *Base) {
+ StringRef Filename = cast<BitmapResource>(Base)->BitmapLoc;
+ bool IsLong;
+ stripQuotes(Filename, IsLong);
+
+ auto File = loadFile(Filename);
+ if (!File)
+ return File.takeError();
+
+ StringRef Buffer = (*File)->getBuffer();
+
+ // Skip the 14 byte BITMAPFILEHEADER.
+ constexpr size_t BITMAPFILEHEADER_size = 14;
+ if (Buffer.size() < BITMAPFILEHEADER_size || Buffer[0] != 'B' ||
+ Buffer[1] != 'M')
+ return createError("Incorrect bitmap file.");
+
+ *FS << Buffer.substr(BITMAPFILEHEADER_size);
+ return Error::success();
+}
+
// --- CursorResource and IconResource helpers. --- //
// ICONRESDIR structure. Describes a single icon in resouce group.
@@ -715,16 +790,14 @@ public:
SingleIconCursorResource(IconCursorGroupType ResourceType,
const ResourceDirEntryStart &HeaderEntry,
- ArrayRef<uint8_t> ImageData)
- : Type(ResourceType), Header(HeaderEntry), Image(ImageData) {}
+ ArrayRef<uint8_t> ImageData, uint16_t Flags)
+ : RCResource(Flags), Type(ResourceType), Header(HeaderEntry),
+ Image(ImageData) {}
Twine getResourceTypeName() const override { return "Icon/cursor image"; }
IntOrString getResourceType() const override {
return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor;
}
- uint16_t getMemoryFlags() const override {
- return MfDiscardable | MfMoveable;
- }
ResourceKind getKind() const override { return RkSingleCursorOrIconRes; }
static bool classof(const RCResource *Res) {
return Res->getKind() == RkSingleCursorOrIconRes;
@@ -845,46 +918,57 @@ Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) {
Reader.setOffset(ItemOffsets[ID]);
ArrayRef<uint8_t> Image;
RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size));
- SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image);
+ SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image,
+ Base->MemoryFlags);
SingleRes.setName(IconCursorID + ID);
RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes));
}
// Now, write all the headers concatenated into a separate resource.
for (size_t ID = 0; ID < NumItems; ++ID) {
- if (Type == IconCursorGroupType::Icon) {
- // rc.exe seems to always set NumPlanes to 1. No idea why it happens.
- ItemEntries[ID].Planes = 1;
- continue;
- }
-
- // We need to rewrite the cursor headers.
+ // We need to rewrite the cursor headers, and fetch actual values
+ // for Planes/BitCount.
const auto &OldHeader = ItemEntries[ID];
- ResourceDirEntryStart NewHeader;
- NewHeader.Cursor.Width = OldHeader.Icon.Width;
- // Each cursor in fact stores two bitmaps, one under another.
- // Height provided in cursor definition describes the height of the
- // cursor, whereas the value existing in resource definition describes
- // the height of the bitmap. Therefore, we need to double this height.
- NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
+ ResourceDirEntryStart NewHeader = OldHeader;
+
+ if (Type == IconCursorGroupType::Cursor) {
+ NewHeader.Cursor.Width = OldHeader.Icon.Width;
+ // Each cursor in fact stores two bitmaps, one under another.
+ // Height provided in cursor definition describes the height of the
+ // cursor, whereas the value existing in resource definition describes
+ // the height of the bitmap. Therefore, we need to double this height.
+ NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
+
+ // Two WORDs were written at the beginning of the resource (hotspot
+ // location). This is reflected in Size field.
+ NewHeader.Size += 2 * sizeof(uint16_t);
+ }
// Now, we actually need to read the bitmap header to find
// the number of planes and the number of bits per pixel.
Reader.setOffset(ItemOffsets[ID]);
const BitmapInfoHeader *BMPHeader;
RETURN_IF_ERROR(Reader.readObject(BMPHeader));
- NewHeader.Planes = BMPHeader->Planes;
- NewHeader.BitCount = BMPHeader->BitCount;
-
- // Two WORDs were written at the beginning of the resource (hotspot
- // location). This is reflected in Size field.
- NewHeader.Size = OldHeader.Size + 2 * sizeof(uint16_t);
+ if (BMPHeader->Size == sizeof(BitmapInfoHeader)) {
+ NewHeader.Planes = BMPHeader->Planes;
+ NewHeader.BitCount = BMPHeader->BitCount;
+ } else {
+ // A PNG .ico file.
+ // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473
+ // "The image must be in 32bpp"
+ NewHeader.Planes = 1;
+ NewHeader.BitCount = 32;
+ }
ItemEntries[ID] = NewHeader;
}
IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries));
HeaderRes.setName(ResName);
+ if (Base->MemoryFlags & MfPreload) {
+ HeaderRes.MemoryFlags |= MfPreload;
+ HeaderRes.MemoryFlags &= ~MfPure;
+ }
RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes));
return Error::success();
@@ -938,15 +1022,18 @@ Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
// ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
if (!IsExtended) {
- RETURN_IF_ERROR(checkNumberFits<uint16_t>(
- Ctl.ID, "Control ID in simple DIALOG resource"));
+ // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't
+ // want to refer to later.
+ if (Ctl.ID != static_cast<uint32_t>(-1))
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(
+ Ctl.ID, "Control ID in simple DIALOG resource"));
writeInt<uint16_t>(Ctl.ID);
} else {
writeInt<uint32_t>(Ctl.ID);
}
// Window class - either 0xFFFF + 16-bit integer or a string.
- RETURN_IF_ERROR(writeIntOrString(IntOrString(TypeInfo.CtlClass)));
+ RETURN_IF_ERROR(writeIntOrString(Ctl.Class));
// Element caption/reference ID. ID is preceded by 0xFFFF.
RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"));
@@ -1038,8 +1125,8 @@ Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
// think there is no menu attached to the dialog.
writeInt<uint16_t>(0);
- // Window CLASS field. Not kept here.
- writeInt<uint16_t>(0);
+ // Window CLASS field.
+ RETURN_IF_ERROR(writeIntOrString(ObjectData.Class));
// Window title or a single word equal to 0.
RETURN_IF_ERROR(writeCString(ObjectData.Caption));
@@ -1135,13 +1222,15 @@ public:
using BundleType = ResourceFileWriter::StringTableInfo::Bundle;
BundleType Bundle;
- BundleResource(const BundleType &StrBundle) : Bundle(StrBundle) {}
+ BundleResource(const BundleType &StrBundle)
+ : RCResource(StrBundle.MemoryFlags), Bundle(StrBundle) {}
IntOrString getResourceType() const override { return 6; }
ResourceKind getKind() const override { return RkStringTableBundle; }
static bool classof(const RCResource *Res) {
return Res->getKind() == RkStringTableBundle;
}
+ Twine getResourceTypeName() const override { return "STRINGTABLE"; }
};
Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) {
@@ -1168,7 +1257,7 @@ Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) {
SmallVector<UTF16, 128> Data;
RETURN_IF_ERROR(processString(Res->Bundle.Data[ID].getValueOr(StringRef()),
NullHandlingMethod::CutAtDoubleNull,
- IsLongString, Data));
+ IsLongString, Data, Params.CodePage));
if (AppendNull && Res->Bundle.Data[ID])
Data.push_back('\0');
RETURN_IF_ERROR(
@@ -1215,9 +1304,9 @@ Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) {
SmallVector<UTF16, 128> ProcessedString;
bool IsLongString;
- RETURN_IF_ERROR(processString(Elem.getString(),
- NullHandlingMethod::UserResource,
- IsLongString, ProcessedString));
+ RETURN_IF_ERROR(
+ processString(Elem.getString(), NullHandlingMethod::UserResource,
+ IsLongString, ProcessedString, Params.CodePage));
for (auto Ch : ProcessedString) {
if (IsLongString) {
@@ -1241,6 +1330,7 @@ Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
bool OutputHeader = Blk.Name != "";
uint64_t LengthLoc;
+ padStream(sizeof(uint32_t));
if (OutputHeader) {
LengthLoc = writeInt<uint16_t>(0);
writeInt<uint16_t>(0);
@@ -1266,7 +1356,6 @@ Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
}
- padStream(sizeof(uint32_t));
return Error::success();
}
@@ -1296,6 +1385,7 @@ Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
return createError(Twine("VALUE ") + Val.Key +
" cannot contain both strings and integers");
+ padStream(sizeof(uint32_t));
auto LengthLoc = writeInt<uint16_t>(0);
auto ValLengthLoc = writeInt<uint16_t>(0);
writeInt<uint16_t>(HasStrings);
@@ -1325,7 +1415,6 @@ Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
}
writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
- padStream(sizeof(uint32_t));
return Error::success();
}