aboutsummaryrefslogtreecommitdiff
path: root/tools/llvm-rc/ResourceScriptStmt.h
blob: 2071ac6a9a3ad75c84e393108d00aa85de41cb29 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
//===-- ResourceScriptStmt.h ------------------------------------*- C++-*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// This lists all the resource and statement types occurring in RC scripts.
//
//===---------------------------------------------------------------------===//

#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H
#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTSTMT_H

#include "ResourceScriptToken.h"
#include "ResourceVisitor.h"

#include "llvm/ADT/StringSet.h"

namespace llvm {
namespace rc {

// Integer wrapper that also holds information whether the user declared
// the integer to be long (by appending L to the end of the integer) or not.
// It allows to be implicitly cast from and to uint32_t in order
// to be compatible with the parts of code that don't care about the integers
// being marked long.
class RCInt {
  uint32_t Val;
  bool Long;

public:
  RCInt(const RCToken &Token)
      : Val(Token.intValue()), Long(Token.isLongInt()) {}
  RCInt(uint32_t Value) : Val(Value), Long(false) {}
  RCInt(uint32_t Value, bool IsLong) : Val(Value), Long(IsLong) {}
  operator uint32_t() const { return Val; }
  bool isLong() const { return Long; }

  RCInt &operator+=(const RCInt &Rhs) {
    std::tie(Val, Long) = std::make_pair(Val + Rhs.Val, Long | Rhs.Long);
    return *this;
  }

  RCInt &operator-=(const RCInt &Rhs) {
    std::tie(Val, Long) = std::make_pair(Val - Rhs.Val, Long | Rhs.Long);
    return *this;
  }

  RCInt &operator|=(const RCInt &Rhs) {
    std::tie(Val, Long) = std::make_pair(Val | Rhs.Val, Long | Rhs.Long);
    return *this;
  }

  RCInt &operator&=(const RCInt &Rhs) {
    std::tie(Val, Long) = std::make_pair(Val & Rhs.Val, Long | Rhs.Long);
    return *this;
  }

  RCInt operator-() const { return {-Val, Long}; }
  RCInt operator~() const { return {~Val, Long}; }

  friend raw_ostream &operator<<(raw_ostream &OS, const RCInt &Int) {
    return OS << Int.Val << (Int.Long ? "L" : "");
  }
};

// A class holding a name - either an integer or a reference to the string.
class IntOrString {
private:
  union Data {
    RCInt Int;
    StringRef String;
    Data(RCInt Value) : Int(Value) {}
    Data(const StringRef Value) : String(Value) {}
    Data(const RCToken &Token) {
      if (Token.kind() == RCToken::Kind::Int)
        Int = RCInt(Token);
      else
        String = Token.value();
    }
  } Data;
  bool IsInt;

public:
  IntOrString() : IntOrString(RCInt(0)) {}
  IntOrString(uint32_t Value) : Data(Value), IsInt(1) {}
  IntOrString(RCInt Value) : Data(Value), IsInt(1) {}
  IntOrString(StringRef Value) : Data(Value), IsInt(0) {}
  IntOrString(const RCToken &Token)
      : Data(Token), IsInt(Token.kind() == RCToken::Kind::Int) {}

  bool equalsLower(const char *Str) {
    return !IsInt && Data.String.equals_lower(Str);
  }

  bool isInt() const { return IsInt; }

  RCInt getInt() const {
    assert(IsInt);
    return Data.Int;
  }

  const StringRef &getString() const {
    assert(!IsInt);
    return Data.String;
  }

  operator Twine() const {
    return isInt() ? Twine(getInt()) : Twine(getString());
  }

  friend raw_ostream &operator<<(raw_ostream &, const IntOrString &);
};

enum ResourceKind {
  // These resource kinds have corresponding .res resource type IDs
  // (TYPE in RESOURCEHEADER structure). The numeric value assigned to each
  // kind is equal to this type ID.
  RkNull = 0,
  RkSingleCursor = 1,
  RkBitmap = 2,
  RkSingleIcon = 3,
  RkMenu = 4,
  RkDialog = 5,
  RkStringTableBundle = 6,
  RkAccelerators = 9,
  RkRcData = 10,
  RkCursorGroup = 12,
  RkIconGroup = 14,
  RkVersionInfo = 16,
  RkHTML = 23,

  // These kinds don't have assigned type IDs (they might be the resources
  // of invalid kind, expand to many resource structures in .res files,
  // or have variable type ID). In order to avoid ID clashes with IDs above,
  // we assign the kinds the values 256 and larger.
  RkInvalid = 256,
  RkBase,
  RkCursor,
  RkIcon,
  RkStringTable,
  RkUser,
  RkSingleCursorOrIconRes,
  RkCursorOrIconGroupRes,
};

// Non-zero memory flags.
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648027(v=vs.85).aspx
enum MemoryFlags {
  MfMoveable = 0x10,
  MfPure = 0x20,
  MfPreload = 0x40,
  MfDiscardable = 0x1000
};

// Base resource. All the resources should derive from this base.
class RCResource {
public:
  IntOrString ResName;
  uint16_t MemoryFlags = getDefaultMemoryFlags();
  void setName(const IntOrString &Name) { ResName = Name; }
  virtual raw_ostream &log(raw_ostream &OS) const {
    return OS << "Base statement\n";
  };
  RCResource() {}
  RCResource(uint16_t Flags) : MemoryFlags(Flags) {}
  virtual ~RCResource() {}

  virtual Error visit(Visitor *) const {
    llvm_unreachable("This is unable to call methods from Visitor base");
  }

  // Apply the statements attached to this resource. Generic resources
  // don't have any.
  virtual Error applyStmts(Visitor *) const { return Error::success(); }

  // By default, memory flags are DISCARDABLE | PURE | MOVEABLE.
  static uint16_t getDefaultMemoryFlags() {
    return MfDiscardable | MfPure | MfMoveable;
  }

  virtual ResourceKind getKind() const { return RkBase; }
  static bool classof(const RCResource *Res) { return true; }

  virtual IntOrString getResourceType() const {
    llvm_unreachable("This cannot be called on objects without types.");
  }
  virtual Twine getResourceTypeName() const {
    llvm_unreachable("This cannot be called on objects without types.");
  };
};

// An empty resource. It has no content, type 0, ID 0 and all of its
// characteristics are equal to 0.
class NullResource : public RCResource {
public:
  NullResource() : RCResource(0) {}
  raw_ostream &log(raw_ostream &OS) const override {
    return OS << "Null resource\n";
  }
  Error visit(Visitor *V) const override { return V->visitNullResource(this); }
  IntOrString getResourceType() const override { return 0; }
  Twine getResourceTypeName() const override { return "(NULL)"; }
};

// Optional statement base. All such statements should derive from this base.
class OptionalStmt : public RCResource {};

class OptionalStmtList : public OptionalStmt {
  std::vector<std::unique_ptr<OptionalStmt>> Statements;

public:
  OptionalStmtList() {}
  raw_ostream &log(raw_ostream &OS) const override;

  void addStmt(std::unique_ptr<OptionalStmt> Stmt) {
    Statements.push_back(std::move(Stmt));
  }

  Error visit(Visitor *V) const override {
    for (auto &StmtPtr : Statements)
      if (auto Err = StmtPtr->visit(V))
        return Err;
    return Error::success();
  }
};

class OptStatementsRCResource : public RCResource {
public:
  std::unique_ptr<OptionalStmtList> OptStatements;

  OptStatementsRCResource(OptionalStmtList &&Stmts,
                          uint16_t Flags = RCResource::getDefaultMemoryFlags())
      : RCResource(Flags),
        OptStatements(llvm::make_unique<OptionalStmtList>(std::move(Stmts))) {}

  virtual Error applyStmts(Visitor *V) const { return OptStatements->visit(V); }
};

// LANGUAGE statement. It can occur both as a top-level statement (in such
// a situation, it changes the default language until the end of the file)
// and as an optional resource statement (then it changes the language
// of a single resource).
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381019(v=vs.85).aspx
class LanguageResource : public OptionalStmt {
public:
  uint32_t Lang, SubLang;

  LanguageResource(uint32_t LangId, uint32_t SubLangId)
      : Lang(LangId), SubLang(SubLangId) {}
  raw_ostream &log(raw_ostream &) const override;

  // This is not a regular top-level statement; when it occurs, it just
  // modifies the language context.
  Error visit(Visitor *V) const override { return V->visitLanguageStmt(this); }
  Twine getResourceTypeName() const override { return "LANGUAGE"; }
};

// ACCELERATORS resource. Defines a named table of accelerators for the app.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380610(v=vs.85).aspx
class AcceleratorsResource : public OptStatementsRCResource {
public:
  class Accelerator {
  public:
    IntOrString Event;
    uint32_t Id;
    uint16_t Flags;

    enum Options {
      // This is actually 0x0000 (accelerator is assumed to be ASCII if it's
      // not VIRTKEY). However, rc.exe behavior is different in situations
      // "only ASCII defined" and "neither ASCII nor VIRTKEY defined".
      // Therefore, we include ASCII as another flag. This must be zeroed
      // when serialized.
      ASCII = 0x8000,
      VIRTKEY = 0x0001,
      NOINVERT = 0x0002,
      ALT = 0x0010,
      SHIFT = 0x0004,
      CONTROL = 0x0008
    };

    static constexpr size_t NumFlags = 6;
    static StringRef OptionsStr[NumFlags];
    static uint32_t OptionsFlags[NumFlags];
  };

  AcceleratorsResource(OptionalStmtList &&List, uint16_t Flags)
      : OptStatementsRCResource(std::move(List), Flags) {}

  std::vector<Accelerator> Accelerators;

  void addAccelerator(IntOrString Event, uint32_t Id, uint16_t Flags) {
    Accelerators.push_back(Accelerator{Event, Id, Flags});
  }
  raw_ostream &log(raw_ostream &) const override;

  IntOrString getResourceType() const override { return RkAccelerators; }
  static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; }
  Twine getResourceTypeName() const override { return "ACCELERATORS"; }

  Error visit(Visitor *V) const override {
    return V->visitAcceleratorsResource(this);
  }
  ResourceKind getKind() const override { return RkAccelerators; }
  static bool classof(const RCResource *Res) {
    return Res->getKind() == RkAccelerators;
  }
};

// BITMAP resource. Represents a bitmap (".bmp") file.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380680(v=vs.85).aspx
class BitmapResource : public RCResource {
public:
  StringRef BitmapLoc;

  BitmapResource(StringRef Location, uint16_t Flags)
      : RCResource(Flags), BitmapLoc(Location) {}
  raw_ostream &log(raw_ostream &) const override;

  IntOrString getResourceType() const override { return RkBitmap; }
  static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; }

  Twine getResourceTypeName() const override { return "BITMAP"; }
  Error visit(Visitor *V) const override {
    return V->visitBitmapResource(this);
  }
  ResourceKind getKind() const override { return RkBitmap; }
  static bool classof(const RCResource *Res) {
    return Res->getKind() == RkBitmap;
  }
};

// CURSOR resource. Represents a single cursor (".cur") file.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380920(v=vs.85).aspx
class CursorResource : public RCResource {
public:
  StringRef CursorLoc;

  CursorResource(StringRef Location, uint16_t Flags)
      : RCResource(Flags), CursorLoc(Location) {}
  raw_ostream &log(raw_ostream &) const override;

  Twine getResourceTypeName() const override { return "CURSOR"; }
  static uint16_t getDefaultMemoryFlags() { return MfDiscardable | MfMoveable; }
  Error visit(Visitor *V) const override {
    return V->visitCursorResource(this);
  }
  ResourceKind getKind() const override { return RkCursor; }
  static bool classof(const RCResource *Res) {
    return Res->getKind() == RkCursor;
  }
};

// ICON resource. Represents a single ".ico" file containing a group of icons.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381018(v=vs.85).aspx
class IconResource : public RCResource {
public:
  StringRef IconLoc;

  IconResource(StringRef Location, uint16_t Flags)
      : RCResource(Flags), IconLoc(Location) {}
  raw_ostream &log(raw_ostream &) const override;

  Twine getResourceTypeName() const override { return "ICON"; }
  static uint16_t getDefaultMemoryFlags() { return MfDiscardable | MfMoveable; }
  Error visit(Visitor *V) const override { return V->visitIconResource(this); }
  ResourceKind getKind() const override { return RkIcon; }
  static bool classof(const RCResource *Res) {
    return Res->getKind() == RkIcon;
  }
};

// HTML resource. Represents a local webpage that is to be embedded into the
// resulting resource file. It embeds a file only - no additional resources
// (images etc.) are included with this resource.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa966018(v=vs.85).aspx
class HTMLResource : public RCResource {
public:
  StringRef HTMLLoc;

  HTMLResource(StringRef Location, uint16_t Flags)
      : RCResource(Flags), HTMLLoc(Location) {}
  raw_ostream &log(raw_ostream &) const override;

  Error visit(Visitor *V) const override { return V->visitHTMLResource(this); }

  // Curiously, file resources don't have DISCARDABLE flag set.
  static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; }
  IntOrString getResourceType() const override { return RkHTML; }
  Twine getResourceTypeName() const override { return "HTML"; }
  ResourceKind getKind() const override { return RkHTML; }
  static bool classof(const RCResource *Res) {
    return Res->getKind() == RkHTML;
  }
};

// -- MENU resource and its helper classes --
// This resource describes the contents of an application menu
// (usually located in the upper part of the dialog.)
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381025(v=vs.85).aspx

// Description of a single submenu item.
class MenuDefinition {
public:
  enum Options {
    CHECKED = 0x0008,
    GRAYED = 0x0001,
    HELP = 0x4000,
    INACTIVE = 0x0002,
    MENUBARBREAK = 0x0020,
    MENUBREAK = 0x0040
  };

  enum MenuDefKind { MkBase, MkSeparator, MkMenuItem, MkPopup };

  static constexpr size_t NumFlags = 6;
  static StringRef OptionsStr[NumFlags];
  static uint32_t OptionsFlags[NumFlags];
  static raw_ostream &logFlags(raw_ostream &, uint16_t Flags);
  virtual raw_ostream &log(raw_ostream &OS) const {
    return OS << "Base menu definition\n";
  }
  virtual ~MenuDefinition() {}

  virtual uint16_t getResFlags() const { return 0; }
  virtual MenuDefKind getKind() const { return MkBase; }
};

// Recursive description of a whole submenu.
class MenuDefinitionList : public MenuDefinition {
public:
  std::vector<std::unique_ptr<MenuDefinition>> Definitions;

  void addDefinition(std::unique_ptr<MenuDefinition> Def) {
    Definitions.push_back(std::move(Def));
  }
  raw_ostream &log(raw_ostream &) const override;
};

// Separator in MENU definition (MENUITEM SEPARATOR).
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx
class MenuSeparator : public MenuDefinition {
public:
  raw_ostream &log(raw_ostream &) const override;

  MenuDefKind getKind() const override { return MkSeparator; }
  static bool classof(const MenuDefinition *D) {
    return D->getKind() == MkSeparator;
  }
};

// MENUITEM statement definition.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx
class MenuItem : public MenuDefinition {
public:
  StringRef Name;
  uint32_t Id;
  uint16_t Flags;

  MenuItem(StringRef Caption, uint32_t ItemId, uint16_t ItemFlags)
      : Name(Caption), Id(ItemId), Flags(ItemFlags) {}
  raw_ostream &log(raw_ostream &) const override;

  uint16_t getResFlags() const override { return Flags; }
  MenuDefKind getKind() const override { return MkMenuItem; }
  static bool classof(const MenuDefinition *D) {
    return D->getKind() == MkMenuItem;
  }
};

// POPUP statement definition.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381030(v=vs.85).aspx
class PopupItem : public MenuDefinition {
public:
  StringRef Name;
  uint16_t Flags;
  MenuDefinitionList SubItems;

  PopupItem(StringRef Caption, uint16_t ItemFlags,
            MenuDefinitionList &&SubItemsList)
      : Name(Caption), Flags(ItemFlags), SubItems(std::move(SubItemsList)) {}
  raw_ostream &log(raw_ostream &) const override;

  // This has an additional (0x10) flag. It doesn't match with documented
  // 0x01 flag, though.
  uint16_t getResFlags() const override { return Flags | 0x10; }
  MenuDefKind getKind() const override { return MkPopup; }
  static bool classof(const MenuDefinition *D) {
    return D->getKind() == MkPopup;
  }
};

// Menu resource definition.
class MenuResource : public OptStatementsRCResource {
public:
  MenuDefinitionList Elements;

  MenuResource(OptionalStmtList &&OptStmts, MenuDefinitionList &&Items,
               uint16_t Flags)
      : OptStatementsRCResource(std::move(OptStmts), Flags),
        Elements(std::move(Items)) {}
  raw_ostream &log(raw_ostream &) const override;

  IntOrString getResourceType() const override { return RkMenu; }
  Twine getResourceTypeName() const override { return "MENU"; }
  Error visit(Visitor *V) const override { return V->visitMenuResource(this); }
  ResourceKind getKind() const override { return RkMenu; }
  static bool classof(const RCResource *Res) {
    return Res->getKind() == RkMenu;
  }
};

// STRINGTABLE resource. Contains a list of strings, each having its unique ID.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381050(v=vs.85).aspx
class StringTableResource : public OptStatementsRCResource {
public:
  std::vector<std::pair<uint32_t, StringRef>> Table;

  StringTableResource(OptionalStmtList &&List, uint16_t Flags)
      : OptStatementsRCResource(std::move(List), Flags) {}
  void addString(uint32_t ID, StringRef String) {
    Table.emplace_back(ID, String);
  }
  raw_ostream &log(raw_ostream &) const override;
  Twine getResourceTypeName() const override { return "STRINGTABLE"; }
  Error visit(Visitor *V) const override {
    return V->visitStringTableResource(this);
  }
};

// -- DIALOG(EX) resource and its helper classes --
//
// This resource describes dialog boxes and controls residing inside them.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381003(v=vs.85).aspx
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381002(v=vs.85).aspx

// Single control definition.
class Control {
public:
  StringRef Type;
  IntOrString Title;
  uint32_t ID, X, Y, Width, Height;
  Optional<uint32_t> Style, ExtStyle, HelpID;
  IntOrString Class;

  // Control classes as described in DLGITEMTEMPLATEEX documentation.
  //
  // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms645389.aspx
  enum CtlClasses {
    ClsButton = 0x80,
    ClsEdit = 0x81,
    ClsStatic = 0x82,
    ClsListBox = 0x83,
    ClsScrollBar = 0x84,
    ClsComboBox = 0x85
  };

  // Simple information about a single control type.
  struct CtlInfo {
    uint32_t Style;
    uint16_t CtlClass;
    bool HasTitle;
  };

  Control(StringRef CtlType, IntOrString CtlTitle, uint32_t CtlID,
          uint32_t PosX, uint32_t PosY, uint32_t ItemWidth, uint32_t ItemHeight,
          Optional<uint32_t> ItemStyle, Optional<uint32_t> ExtItemStyle,
          Optional<uint32_t> CtlHelpID, IntOrString CtlClass)
      : Type(CtlType), Title(CtlTitle), ID(CtlID), X(PosX), Y(PosY),
        Width(ItemWidth), Height(ItemHeight), Style(ItemStyle),
        ExtStyle(ExtItemStyle), HelpID(CtlHelpID), Class(CtlClass) {}

  static const StringMap<CtlInfo> SupportedCtls;

  raw_ostream &log(raw_ostream &) const;
};

// Single dialog definition. We don't create distinct classes for DIALOG and
// DIALOGEX because of their being too similar to each other. We only have a
// flag determining the type of the dialog box.
class DialogResource : public OptStatementsRCResource {
public:
  uint32_t X, Y, Width, Height, HelpID;
  std::vector<Control> Controls;
  bool IsExtended;

  DialogResource(uint32_t PosX, uint32_t PosY, uint32_t DlgWidth,
                 uint32_t DlgHeight, uint32_t DlgHelpID,
                 OptionalStmtList &&OptStmts, bool IsDialogEx, uint16_t Flags)
      : OptStatementsRCResource(std::move(OptStmts), Flags), X(PosX), Y(PosY),
        Width(DlgWidth), Height(DlgHeight), HelpID(DlgHelpID),
        IsExtended(IsDialogEx) {}

  void addControl(Control &&Ctl) { Controls.push_back(std::move(Ctl)); }

  raw_ostream &log(raw_ostream &) const override;

  // It was a weird design decision to assign the same resource type number
  // both for DIALOG and DIALOGEX (and the same structure version number).
  // It makes it possible for DIALOG to be mistaken for DIALOGEX.
  IntOrString getResourceType() const override { return RkDialog; }
  Twine getResourceTypeName() const override {
    return "DIALOG" + Twine(IsExtended ? "EX" : "");
  }
  Error visit(Visitor *V) const override {
    return V->visitDialogResource(this);
  }
  ResourceKind getKind() const override { return RkDialog; }
  static bool classof(const RCResource *Res) {
    return Res->getKind() == RkDialog;
  }
};

// User-defined resource. It is either:
//   * a link to the file, e.g. NAME TYPE "filename",
//   * or contains a list of integers and strings, e.g. NAME TYPE {1, "a", 2}.
class UserDefinedResource : public RCResource {
public:
  IntOrString Type;
  StringRef FileLoc;
  std::vector<IntOrString> Contents;
  bool IsFileResource;

  UserDefinedResource(IntOrString ResourceType, StringRef FileLocation,
                      uint16_t Flags)
      : RCResource(Flags), Type(ResourceType), FileLoc(FileLocation),
        IsFileResource(true) {}
  UserDefinedResource(IntOrString ResourceType, std::vector<IntOrString> &&Data,
                      uint16_t Flags)
      : RCResource(Flags), Type(ResourceType), Contents(std::move(Data)),
        IsFileResource(false) {}

  raw_ostream &log(raw_ostream &) const override;
  IntOrString getResourceType() const override { return Type; }
  Twine getResourceTypeName() const override { return Type; }
  static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; }

  Error visit(Visitor *V) const override {
    return V->visitUserDefinedResource(this);
  }
  ResourceKind getKind() const override { return RkUser; }
  static bool classof(const RCResource *Res) {
    return Res->getKind() == RkUser;
  }
};

// -- VERSIONINFO resource and its helper classes --
//
// This resource lists the version information on the executable/library.
// The declaration consists of the following items:
//   * A number of fixed optional version statements (e.g. FILEVERSION, FILEOS)
//   * BEGIN
//   * A number of BLOCK and/or VALUE statements. BLOCK recursively defines
//       another block of version information, whereas VALUE defines a
//       key -> value correspondence. There might be more than one value
//       corresponding to the single key.
//   * END
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381058(v=vs.85).aspx

// A single VERSIONINFO statement;
class VersionInfoStmt {
public:
  enum StmtKind { StBase = 0, StBlock = 1, StValue = 2 };

  virtual raw_ostream &log(raw_ostream &OS) const { return OS << "VI stmt\n"; }
  virtual ~VersionInfoStmt() {}

  virtual StmtKind getKind() const { return StBase; }
  static bool classof(const VersionInfoStmt *S) {
    return S->getKind() == StBase;
  }
};

// BLOCK definition; also the main VERSIONINFO declaration is considered a
// BLOCK, although it has no name.
// The correct top-level blocks are "VarFileInfo" and "StringFileInfo". We don't
// care about them at the parsing phase.
class VersionInfoBlock : public VersionInfoStmt {
public:
  std::vector<std::unique_ptr<VersionInfoStmt>> Stmts;
  StringRef Name;

  VersionInfoBlock(StringRef BlockName) : Name(BlockName) {}
  void addStmt(std::unique_ptr<VersionInfoStmt> Stmt) {
    Stmts.push_back(std::move(Stmt));
  }
  raw_ostream &log(raw_ostream &) const override;

  StmtKind getKind() const override { return StBlock; }
  static bool classof(const VersionInfoStmt *S) {
    return S->getKind() == StBlock;
  }
};

class VersionInfoValue : public VersionInfoStmt {
public:
  StringRef Key;
  std::vector<IntOrString> Values;
  std::vector<bool> HasPrecedingComma;

  VersionInfoValue(StringRef InfoKey, std::vector<IntOrString> &&Vals,
                   std::vector<bool> &&CommasBeforeVals)
      : Key(InfoKey), Values(std::move(Vals)),
        HasPrecedingComma(std::move(CommasBeforeVals)) {}
  raw_ostream &log(raw_ostream &) const override;

  StmtKind getKind() const override { return StValue; }
  static bool classof(const VersionInfoStmt *S) {
    return S->getKind() == StValue;
  }
};

class VersionInfoResource : public RCResource {
public:
  // A class listing fixed VERSIONINFO statements (occuring before main BEGIN).
  // If any of these is not specified, it is assumed by the original tool to
  // be equal to 0.
  class VersionInfoFixed {
  public:
    enum VersionInfoFixedType {
      FtUnknown,
      FtFileVersion,
      FtProductVersion,
      FtFileFlagsMask,
      FtFileFlags,
      FtFileOS,
      FtFileType,
      FtFileSubtype,
      FtNumTypes
    };

  private:
    static const StringMap<VersionInfoFixedType> FixedFieldsInfoMap;
    static const StringRef FixedFieldsNames[FtNumTypes];

  public:
    SmallVector<uint32_t, 4> FixedInfo[FtNumTypes];
    SmallVector<bool, FtNumTypes> IsTypePresent;

    static VersionInfoFixedType getFixedType(StringRef Type);
    static bool isTypeSupported(VersionInfoFixedType Type);
    static bool isVersionType(VersionInfoFixedType Type);

    VersionInfoFixed() : IsTypePresent(FtNumTypes, false) {}

    void setValue(VersionInfoFixedType Type, ArrayRef<uint32_t> Value) {
      FixedInfo[Type] = SmallVector<uint32_t, 4>(Value.begin(), Value.end());
      IsTypePresent[Type] = true;
    }

    raw_ostream &log(raw_ostream &) const;
  };

  VersionInfoBlock MainBlock;
  VersionInfoFixed FixedData;

  VersionInfoResource(VersionInfoBlock &&TopLevelBlock,
                      VersionInfoFixed &&FixedInfo, uint16_t Flags)
      : RCResource(Flags), MainBlock(std::move(TopLevelBlock)),
        FixedData(std::move(FixedInfo)) {}

  raw_ostream &log(raw_ostream &) const override;
  IntOrString getResourceType() const override { return RkVersionInfo; }
  static uint16_t getDefaultMemoryFlags() { return MfMoveable | MfPure; }
  Twine getResourceTypeName() const override { return "VERSIONINFO"; }
  Error visit(Visitor *V) const override {
    return V->visitVersionInfoResource(this);
  }
  ResourceKind getKind() const override { return RkVersionInfo; }
  static bool classof(const RCResource *Res) {
    return Res->getKind() == RkVersionInfo;
  }
};

// CHARACTERISTICS optional statement.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380872(v=vs.85).aspx
class CharacteristicsStmt : public OptionalStmt {
public:
  uint32_t Value;

  CharacteristicsStmt(uint32_t Characteristic) : Value(Characteristic) {}
  raw_ostream &log(raw_ostream &) const override;

  Twine getResourceTypeName() const override { return "CHARACTERISTICS"; }
  Error visit(Visitor *V) const override {
    return V->visitCharacteristicsStmt(this);
  }
};

// VERSION optional statement.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381059(v=vs.85).aspx
class VersionStmt : public OptionalStmt {
public:
  uint32_t Value;

  VersionStmt(uint32_t Version) : Value(Version) {}
  raw_ostream &log(raw_ostream &) const override;

  Twine getResourceTypeName() const override { return "VERSION"; }
  Error visit(Visitor *V) const override { return V->visitVersionStmt(this); }
};

// CAPTION optional statement.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380778(v=vs.85).aspx
class CaptionStmt : public OptionalStmt {
public:
  StringRef Value;

  CaptionStmt(StringRef Caption) : Value(Caption) {}
  raw_ostream &log(raw_ostream &) const override;
  Twine getResourceTypeName() const override { return "CAPTION"; }
  Error visit(Visitor *V) const override { return V->visitCaptionStmt(this); }
};

// FONT optional statement.
// Note that the documentation is inaccurate: it expects five arguments to be
// given, however the example provides only two. In fact, the original tool
// expects two arguments - point size and name of the typeface.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381013(v=vs.85).aspx
class FontStmt : public OptionalStmt {
public:
  uint32_t Size, Weight, Charset;
  StringRef Name;
  bool Italic;

  FontStmt(uint32_t FontSize, StringRef FontName, uint32_t FontWeight,
           bool FontItalic, uint32_t FontCharset)
      : Size(FontSize), Weight(FontWeight), Charset(FontCharset),
        Name(FontName), Italic(FontItalic) {}
  raw_ostream &log(raw_ostream &) const override;
  Twine getResourceTypeName() const override { return "FONT"; }
  Error visit(Visitor *V) const override { return V->visitFontStmt(this); }
};

// STYLE optional statement.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381051(v=vs.85).aspx
class StyleStmt : public OptionalStmt {
public:
  uint32_t Value;

  StyleStmt(uint32_t Style) : Value(Style) {}
  raw_ostream &log(raw_ostream &) const override;
  Twine getResourceTypeName() const override { return "STYLE"; }
  Error visit(Visitor *V) const override { return V->visitStyleStmt(this); }
};

// CLASS optional statement.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380883(v=vs.85).aspx
class ClassStmt : public OptionalStmt {
public:
  IntOrString Value;

  ClassStmt(IntOrString Class) : Value(Class) {}
  raw_ostream &log(raw_ostream &) const override;
  Twine getResourceTypeName() const override { return "CLASS"; }
  Error visit(Visitor *V) const override { return V->visitClassStmt(this); }
};

} // namespace rc
} // namespace llvm

#endif