forked from OSchip/llvm-project
Adding parsing ability for .res file.
Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D33566 llvm-svn: 304225
This commit is contained in:
parent
4cec434a1b
commit
72fb6a87fb
|
@ -30,11 +30,18 @@
|
|||
#define LLVM_INCLUDE_LLVM_OBJECT_RESFILE_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Object/Binary.h"
|
||||
#include "llvm/Object/Error.h"
|
||||
#include "llvm/Support/BinaryByteStream.h"
|
||||
#include "llvm/Support/BinaryStreamReader.h"
|
||||
#include "llvm/Support/COFF.h"
|
||||
#include "llvm/Support/ConvertUTF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/ScopedPrinter.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace llvm {
|
||||
namespace object {
|
||||
|
@ -44,23 +51,44 @@ class WindowsResource;
|
|||
class ResourceEntryRef {
|
||||
public:
|
||||
Error moveNext(bool &End);
|
||||
bool checkTypeString() const { return IsStringType; }
|
||||
ArrayRef<UTF16> getTypeString() const { return Type; }
|
||||
uint16_t getTypeID() const { return TypeID; }
|
||||
bool checkNameString() const { return IsStringName; }
|
||||
ArrayRef<UTF16> getNameString() const { return Name; }
|
||||
uint16_t getNameID() const { return NameID; }
|
||||
uint16_t getLanguage() const { return Suffix->Language; }
|
||||
|
||||
private:
|
||||
friend class WindowsResource;
|
||||
|
||||
ResourceEntryRef(BinaryStreamRef Ref, const WindowsResource *Owner,
|
||||
Error &Err);
|
||||
|
||||
Error loadNext();
|
||||
|
||||
struct HeaderSuffix {
|
||||
support::ulittle32_t DataVersion;
|
||||
support::ulittle16_t MemoryFlags;
|
||||
support::ulittle16_t Language;
|
||||
support::ulittle32_t Version;
|
||||
support::ulittle32_t Characteristics;
|
||||
};
|
||||
|
||||
BinaryStreamReader Reader;
|
||||
BinaryStreamRef HeaderBytes;
|
||||
BinaryStreamRef DataBytes;
|
||||
bool IsStringType;
|
||||
ArrayRef<UTF16> Type;
|
||||
uint16_t TypeID;
|
||||
bool IsStringName;
|
||||
ArrayRef<UTF16> Name;
|
||||
uint16_t NameID;
|
||||
const HeaderSuffix *Suffix = nullptr;
|
||||
ArrayRef<uint8_t> Data;
|
||||
const WindowsResource *OwningRes = nullptr;
|
||||
};
|
||||
|
||||
class WindowsResource : public Binary {
|
||||
public:
|
||||
~WindowsResource() override;
|
||||
Expected<ResourceEntryRef> getHeadEntry();
|
||||
|
||||
static bool classof(const Binary *V) { return V->isWinRes(); }
|
||||
|
@ -76,6 +104,38 @@ private:
|
|||
BinaryByteStream BBS;
|
||||
};
|
||||
|
||||
class WindowsResourceParser {
|
||||
public:
|
||||
WindowsResourceParser();
|
||||
|
||||
Error parse(WindowsResource *WR);
|
||||
|
||||
void printTree() const;
|
||||
|
||||
private:
|
||||
class TreeNode {
|
||||
public:
|
||||
TreeNode() = default;
|
||||
explicit TreeNode(uint32_t ID);
|
||||
explicit TreeNode(ArrayRef<UTF16> Ref);
|
||||
void addEntry(const ResourceEntryRef &Entry);
|
||||
void print(ScopedPrinter &Writer, StringRef Name) const;
|
||||
|
||||
private:
|
||||
TreeNode &addTypeNode(const ResourceEntryRef &Entry);
|
||||
TreeNode &addNameNode(const ResourceEntryRef &Entry);
|
||||
TreeNode &addLanguageNode(const ResourceEntryRef &Entry);
|
||||
TreeNode &addChild(uint32_t ID);
|
||||
TreeNode &addChild(ArrayRef<UTF16> NameRef);
|
||||
uint16_t ID;
|
||||
std::vector<UTF16> Name;
|
||||
std::map<uint32_t, std::unique_ptr<TreeNode>> IDChildren;
|
||||
std::map<std::string, std::unique_ptr<TreeNode>> StringChildren;
|
||||
};
|
||||
|
||||
TreeNode Root;
|
||||
};
|
||||
|
||||
} // namespace object
|
||||
} // namespace llvm
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Support/BinaryStreamArray.h"
|
||||
#include "llvm/Support/BinaryStreamRef.h"
|
||||
#include "llvm/Support/ConvertUTF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/type_traits.h"
|
||||
|
@ -104,6 +105,13 @@ public:
|
|||
/// returns an appropriate error code.
|
||||
Error readCString(StringRef &Dest);
|
||||
|
||||
/// Similar to readCString, however read a null-terminated UTF16 string
|
||||
/// instead.
|
||||
///
|
||||
/// \returns a success error code if the data was successfully read, otherwise
|
||||
/// returns an appropriate error code.
|
||||
Error readWideString(ArrayRef<UTF16> &Dest);
|
||||
|
||||
/// Read a \p Length byte string into \p Dest. Whether a copy occurs depends
|
||||
/// on the implementation of the underlying stream. Updates the stream's
|
||||
/// offset to point after the newly read data.
|
||||
|
|
|
@ -12,20 +12,22 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Object/WindowsResource.h"
|
||||
#include "llvm/Object/Error.h"
|
||||
#include "llvm/Support/COFF.h"
|
||||
#include <system_error>
|
||||
|
||||
namespace llvm {
|
||||
namespace object {
|
||||
|
||||
#define RETURN_IF_ERROR(X) \
|
||||
if (auto EC = X) \
|
||||
return std::move(EC);
|
||||
|
||||
const uint32_t MIN_HEADER_SIZE = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t);
|
||||
|
||||
static const size_t ResourceMagicSize = 16;
|
||||
|
||||
static const size_t NullEntrySize = 16;
|
||||
|
||||
#define RETURN_IF_ERROR(X) \
|
||||
if (auto EC = X) \
|
||||
return EC;
|
||||
|
||||
WindowsResource::WindowsResource(MemoryBufferRef Source)
|
||||
: Binary(Binary::ID_WinRes, Source) {
|
||||
size_t LeadingSize = ResourceMagicSize + NullEntrySize;
|
||||
|
@ -33,8 +35,6 @@ WindowsResource::WindowsResource(MemoryBufferRef Source)
|
|||
support::little);
|
||||
}
|
||||
|
||||
WindowsResource::~WindowsResource() = default;
|
||||
|
||||
Expected<std::unique_ptr<WindowsResource>>
|
||||
WindowsResource::createWindowsResource(MemoryBufferRef Source) {
|
||||
if (Source.getBufferSize() < ResourceMagicSize + NullEntrySize)
|
||||
|
@ -72,19 +72,152 @@ Error ResourceEntryRef::moveNext(bool &End) {
|
|||
return Error::success();
|
||||
}
|
||||
|
||||
static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID,
|
||||
ArrayRef<UTF16> &Str, bool &IsString) {
|
||||
uint16_t IDFlag;
|
||||
RETURN_IF_ERROR(Reader.readInteger(IDFlag));
|
||||
IsString = IDFlag != 0xffff;
|
||||
|
||||
if (IsString) {
|
||||
Reader.setOffset(
|
||||
Reader.getOffset() -
|
||||
sizeof(uint16_t)); // Re-read the bytes which we used to check the flag.
|
||||
RETURN_IF_ERROR(Reader.readWideString(Str));
|
||||
} else
|
||||
RETURN_IF_ERROR(Reader.readInteger(ID));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error ResourceEntryRef::loadNext() {
|
||||
uint32_t DataSize;
|
||||
RETURN_IF_ERROR(Reader.readInteger(DataSize));
|
||||
uint32_t HeaderSize;
|
||||
RETURN_IF_ERROR(Reader.readInteger(HeaderSize));
|
||||
// The data and header size ints are themselves part of the header, so we must
|
||||
// subtract them from the size.
|
||||
RETURN_IF_ERROR(
|
||||
Reader.readStreamRef(HeaderBytes, HeaderSize - 2 * sizeof(uint32_t)));
|
||||
RETURN_IF_ERROR(Reader.readStreamRef(DataBytes, DataSize));
|
||||
|
||||
if (HeaderSize < MIN_HEADER_SIZE)
|
||||
return make_error<GenericBinaryError>("Header size is too small.",
|
||||
object_error::parse_failed);
|
||||
|
||||
RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType));
|
||||
|
||||
RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName));
|
||||
|
||||
RETURN_IF_ERROR(Reader.padToAlignment(sizeof(uint32_t)));
|
||||
|
||||
RETURN_IF_ERROR(Reader.readObject(Suffix));
|
||||
|
||||
RETURN_IF_ERROR(Reader.readArray(Data, DataSize));
|
||||
|
||||
RETURN_IF_ERROR(Reader.padToAlignment(sizeof(uint32_t)));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
WindowsResourceParser::WindowsResourceParser() {}
|
||||
|
||||
Error WindowsResourceParser::parse(WindowsResource *WR) {
|
||||
auto EntryOrErr = WR->getHeadEntry();
|
||||
if (!EntryOrErr)
|
||||
return EntryOrErr.takeError();
|
||||
|
||||
ResourceEntryRef Entry = EntryOrErr.get();
|
||||
bool End = false;
|
||||
|
||||
while (!End) {
|
||||
|
||||
Root.addEntry(Entry);
|
||||
|
||||
RETURN_IF_ERROR(Entry.moveNext(End));
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
void WindowsResourceParser::printTree() const {
|
||||
ScopedPrinter Writer(outs());
|
||||
Root.print(Writer, "Resource Tree");
|
||||
}
|
||||
|
||||
void WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef &Entry) {
|
||||
TreeNode &TypeNode = addTypeNode(Entry);
|
||||
TreeNode &NameNode = TypeNode.addNameNode(Entry);
|
||||
NameNode.addLanguageNode(Entry);
|
||||
}
|
||||
|
||||
WindowsResourceParser::TreeNode::TreeNode(uint32_t ID) : ID(ID) {}
|
||||
|
||||
WindowsResourceParser::TreeNode::TreeNode(ArrayRef<UTF16> NameRef)
|
||||
: Name(NameRef) {}
|
||||
|
||||
WindowsResourceParser::TreeNode &
|
||||
WindowsResourceParser::TreeNode::addTypeNode(const ResourceEntryRef &Entry) {
|
||||
if (Entry.checkTypeString())
|
||||
return addChild(Entry.getTypeString());
|
||||
else
|
||||
return addChild(Entry.getTypeID());
|
||||
}
|
||||
|
||||
WindowsResourceParser::TreeNode &
|
||||
WindowsResourceParser::TreeNode::addNameNode(const ResourceEntryRef &Entry) {
|
||||
if (Entry.checkNameString())
|
||||
return addChild(Entry.getNameString());
|
||||
else
|
||||
return addChild(Entry.getNameID());
|
||||
}
|
||||
|
||||
WindowsResourceParser::TreeNode &
|
||||
WindowsResourceParser::TreeNode::addLanguageNode(
|
||||
const ResourceEntryRef &Entry) {
|
||||
return addChild(Entry.getLanguage());
|
||||
}
|
||||
|
||||
WindowsResourceParser::TreeNode &
|
||||
WindowsResourceParser::TreeNode::addChild(uint32_t ID) {
|
||||
auto Child = IDChildren.find(ID);
|
||||
if (Child == IDChildren.end()) {
|
||||
auto NewChild = llvm::make_unique<WindowsResourceParser::TreeNode>(ID);
|
||||
WindowsResourceParser::TreeNode &Node = *NewChild;
|
||||
IDChildren.emplace(ID, std::move(NewChild));
|
||||
return Node;
|
||||
} else
|
||||
return *(Child->second);
|
||||
}
|
||||
|
||||
WindowsResourceParser::TreeNode &
|
||||
WindowsResourceParser::TreeNode::addChild(ArrayRef<UTF16> NameRef) {
|
||||
std::string NameString;
|
||||
ArrayRef<UTF16> CorrectedName;
|
||||
if (llvm::sys::IsBigEndianHost) {
|
||||
std::vector<UTF16> EndianCorrectedName;
|
||||
EndianCorrectedName.resize(NameRef.size() + 1);
|
||||
std::copy(NameRef.begin(), NameRef.end(), EndianCorrectedName.begin() + 1);
|
||||
EndianCorrectedName[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED;
|
||||
CorrectedName = makeArrayRef(EndianCorrectedName);
|
||||
} else
|
||||
CorrectedName = NameRef;
|
||||
llvm::convertUTF16ToUTF8String(CorrectedName, NameString);
|
||||
|
||||
auto Child = StringChildren.find(NameString);
|
||||
if (Child == StringChildren.end()) {
|
||||
auto NewChild = llvm::make_unique<WindowsResourceParser::TreeNode>(NameRef);
|
||||
WindowsResourceParser::TreeNode &Node = *NewChild;
|
||||
StringChildren.emplace(NameString, std::move(NewChild));
|
||||
return Node;
|
||||
} else
|
||||
return *(Child->second);
|
||||
}
|
||||
|
||||
void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer,
|
||||
StringRef Name) const {
|
||||
ListScope NodeScope(Writer, Name);
|
||||
for (auto const &Child : StringChildren) {
|
||||
Child.second->print(Writer, Child.first);
|
||||
}
|
||||
for (auto const &Child : IDChildren) {
|
||||
Child.second->print(Writer, std::to_string(Child.first));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace object
|
||||
} // namespace llvm
|
||||
|
|
|
@ -69,6 +69,26 @@ Error BinaryStreamReader::readCString(StringRef &Dest) {
|
|||
return Error::success();
|
||||
}
|
||||
|
||||
Error BinaryStreamReader::readWideString(ArrayRef<UTF16> &Dest) {
|
||||
uint32_t Length = 0;
|
||||
uint32_t OriginalOffset = getOffset();
|
||||
const UTF16 *C;
|
||||
while (true) {
|
||||
if (auto EC = readObject(C))
|
||||
return EC;
|
||||
if (*C == 0x0000)
|
||||
break;
|
||||
++Length;
|
||||
}
|
||||
uint32_t NewOffset = getOffset();
|
||||
setOffset(OriginalOffset);
|
||||
|
||||
if (auto EC = readArray(Dest, Length))
|
||||
return EC;
|
||||
setOffset(NewOffset);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error BinaryStreamReader::readFixedString(StringRef &Dest, uint32_t Length) {
|
||||
ArrayRef<uint8_t> Bytes;
|
||||
if (auto EC = readBytes(Bytes, Length))
|
||||
|
|
|
@ -42,3 +42,9 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS
|
|||
MENUITEM "salad", 101
|
||||
MENUITEM "duck", 102
|
||||
}
|
||||
|
||||
|
||||
myresource stringarray {
|
||||
"this is a user defined resource\0",
|
||||
"it contains many strings\0",
|
||||
}
|
Binary file not shown.
|
@ -4,4 +4,48 @@
|
|||
|
||||
RUN: llvm-cvtres %p/Inputs/test_resource.res | FileCheck %s
|
||||
|
||||
CHECK: Number of resources: 7
|
||||
CHECK: Number of resources: 8
|
||||
CHECK-NEXT: Resource Tree [
|
||||
CHECK-NEXT: STRINGARRAY [
|
||||
CHECK-NEXT: MYRESOURCE [
|
||||
CHECK-NEXT: 1033 [
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: 2 [
|
||||
CHECK-NEXT: CURSOR [
|
||||
CHECK-NEXT: 1033 [
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: OKAY [
|
||||
CHECK-NEXT: 1033 [
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: 4 [
|
||||
CHECK-NEXT: "EAT" [
|
||||
CHECK-NEXT: 3081 [
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: 14432 [
|
||||
CHECK-NEXT: 2052 [
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: 5 [
|
||||
CHECK-NEXT: TESTDIALOG [
|
||||
CHECK-NEXT: 1033 [
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: 9 [
|
||||
CHECK-NEXT: MYACCELERATORS [
|
||||
CHECK-NEXT: 1033 [
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: 12 [
|
||||
CHECK-NEXT: 1033 [
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: ]
|
||||
CHECK-NEXT: ]
|
||||
|
|
|
@ -131,7 +131,7 @@ int main(int argc_, const char *argv_[]) {
|
|||
std::vector<std::string> InputFiles = InputArgs.getAllArgValues(OPT_INPUT);
|
||||
|
||||
if (InputFiles.size() == 0) {
|
||||
reportError("No input file specified");
|
||||
reportError("No input file specified.\n");
|
||||
}
|
||||
|
||||
SmallString<128> OutputFile;
|
||||
|
@ -143,6 +143,20 @@ int main(int argc_, const char *argv_[]) {
|
|||
llvm::sys::path::replace_extension(OutputFile, ".obj");
|
||||
}
|
||||
|
||||
outs() << "Machine: ";
|
||||
switch (Machine) {
|
||||
case machine::ARM:
|
||||
outs() << "ARM\n";
|
||||
break;
|
||||
case machine::X86:
|
||||
outs() << "X86\n";
|
||||
break;
|
||||
default:
|
||||
outs() << "X64\n";
|
||||
}
|
||||
|
||||
WindowsResourceParser Parser;
|
||||
|
||||
for (const auto &File : InputFiles) {
|
||||
Expected<object::OwningBinary<object::Binary>> BinaryOrErr =
|
||||
object::createBinary(File);
|
||||
|
@ -166,17 +180,11 @@ int main(int argc_, const char *argv_[]) {
|
|||
EntryNumber++;
|
||||
}
|
||||
outs() << "Number of resources: " << EntryNumber << "\n";
|
||||
|
||||
error(Parser.parse(RF));
|
||||
}
|
||||
outs() << "Machine: ";
|
||||
switch (Machine) {
|
||||
case machine::ARM:
|
||||
outs() << "ARM\n";
|
||||
break;
|
||||
case machine::X86:
|
||||
outs() << "X86\n";
|
||||
break;
|
||||
default:
|
||||
outs() << "X64\n";
|
||||
}
|
||||
|
||||
Parser.printTree();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue