Serenity Operating System
at master 197 lines 6.0 kB view raw
1/* 2 * Copyright (c) 2023, MacDue <macdue@dueutil.tech> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Format.h> 8#include <AK/Utf8View.h> 9#include <LibCore/ArgsParser.h> 10#include <LibGfx/Font/OpenType/Font.h> 11#include <LibGfx/Font/OpenType/Hinting/Opcodes.h> 12#include <LibMain/Main.h> 13 14using namespace OpenType::Hinting; 15 16#define YELLOW "\e[33m" 17#define CYAN "\e[36m" 18#define PURPLE "\e[95m" 19#define GREEN "\e[92m" 20#define RESET "\e[0m" 21#define GRAY "\e[90m" 22 23struct InstructionPrinter : InstructionHandler { 24 InstructionPrinter(bool enable_highlighting) 25 : m_enable_highlighting(enable_highlighting) 26 { 27 } 28 29 void before_operation(InstructionStream& stream, Opcode opcode) override 30 { 31 if (opcode == Opcode::FDEF && stream.current_position() > 1 && m_indent_level == 1) 32 outln(); 33 switch (opcode) { 34 case Opcode::EIF: 35 case Opcode::ELSE: 36 case Opcode::ENDF: 37 m_indent_level--; 38 break; 39 default: 40 break; 41 } 42 auto digits = int(AK::log10(float(stream.length()))) + 1; 43 if (m_enable_highlighting) 44 out(GRAY); 45 out("{:0{}}:", stream.current_position() - 1, digits); 46 if (m_enable_highlighting) 47 out(RESET); 48 out("{:{}}", ""sv, m_indent_level * 2); 49 } 50 51 void after_operation(InstructionStream&, Opcode opcode) override 52 { 53 switch (opcode) { 54 case Opcode::IF: 55 case Opcode::ELSE: 56 case Opcode::IDEF: 57 case Opcode::FDEF: 58 m_indent_level++; 59 break; 60 default: 61 break; 62 } 63 } 64 65 void print_number(u16 value) 66 { 67 if (m_enable_highlighting) 68 return out(GREEN " {}" RESET, value); 69 return out(" {}", value); 70 } 71 72 void print_bytes(ReadonlyBytes bytes, bool first = true) 73 { 74 for (auto value : bytes) { 75 if (!first) 76 out(","); 77 print_number(value); 78 first = false; 79 } 80 } 81 82 void print_words(ReadonlyBytes bytes, bool first = true) 83 { 84 for (size_t i = 0; i < bytes.size(); i += 2) { 85 if (!first) 86 out(","); 87 print_number(bytes[i] << 8 | bytes[i + 1]); 88 first = false; 89 } 90 } 91 92 void default_handler(Context context) override 93 { 94 auto instruction = context.instruction(); 95 auto name = opcode_mnemonic(instruction.opcode()); 96 if (m_enable_highlighting) 97 out(YELLOW); 98 out(name); 99 if (m_enable_highlighting) 100 out(CYAN); 101 out("["); 102 if (m_enable_highlighting) 103 out(PURPLE); 104 if (instruction.flag_bits() > 0) 105 out("{:0{}b}", to_underlying(instruction.opcode()) & ((1 << instruction.flag_bits()) - 1), instruction.flag_bits()); 106 if (m_enable_highlighting) 107 out(CYAN); 108 out("]"); 109 if (m_enable_highlighting) 110 out(RESET); 111 switch (instruction.opcode()) { 112 case Opcode::NPUSHB... Opcode::NPUSHB_MAX: 113 print_number(instruction.values().size()); 114 print_bytes(instruction.values(), false); 115 break; 116 case Opcode::NPUSHW... Opcode::NPUSHW_MAX: 117 print_number(instruction.values().size() / 2); 118 print_words(instruction.values(), false); 119 break; 120 case Opcode::PUSHB... Opcode::PUSHB_MAX: 121 print_bytes(instruction.values()); 122 break; 123 case Opcode::PUSHW... Opcode::PUSHW_MAX: 124 print_words(instruction.values()); 125 break; 126 default: 127 break; 128 } 129 outln(); 130 } 131 132private: 133 bool m_enable_highlighting; 134 u32 m_indent_level { 1 }; 135}; 136 137static bool s_disassembly_attempted = false; 138 139static void print_disassembly(StringView name, Optional<ReadonlyBytes> program, bool enable_highlighting, u32 code_point = 0) 140{ 141 s_disassembly_attempted = true; 142 if (!program.has_value()) { 143 out(name, code_point); 144 outln(": not found"); 145 return; 146 } 147 out(name, code_point); 148 outln(": ({} bytes)\n", program->size()); 149 InstructionPrinter printer { enable_highlighting }; 150 InstructionStream stream { printer, *program }; 151 while (!stream.at_end()) 152 stream.process_next_instruction(); 153} 154 155ErrorOr<int> serenity_main(Main::Arguments arguments) 156{ 157 Core::ArgsParser args_parser; 158 159 StringView font_path; 160 bool no_color = false; 161 bool dump_font_program = false; 162 bool dump_prep_program = false; 163 StringView text; 164 args_parser.add_positional_argument(font_path, "Path to font", "FILE"); 165 args_parser.add_option(dump_font_program, "Disassemble font program (fpgm table)", "disasm-fpgm", 'f'); 166 args_parser.add_option(dump_prep_program, "Disassemble CVT program (prep table)", "disasm-prep", 'p'); 167 args_parser.add_option(text, "Disassemble glyph programs", "disasm-glyphs", 'g', "text"); 168 args_parser.add_option(no_color, "Disable syntax highlighting", "no-color", 'n'); 169 args_parser.parse(arguments); 170 171 auto font = TRY(OpenType::Font::try_load_from_file(font_path)); 172 173 if (dump_font_program) 174 print_disassembly("Font program"sv, font->font_program(), !no_color); 175 if (dump_prep_program) { 176 if (dump_font_program) 177 outln(); 178 print_disassembly("CVT program"sv, font->control_value_program(), !no_color); 179 } 180 if (!text.is_empty()) { 181 Utf8View utf8_view { text }; 182 bool first = !(dump_font_program || dump_prep_program); 183 for (u32 code_point : utf8_view) { 184 if (!first) 185 outln(); 186 auto glyph_id = font->glyph_id_for_code_point(code_point); 187 print_disassembly("Glyph program for codepoint {}"sv, font->glyph_program(glyph_id), !no_color, code_point); 188 first = false; 189 } 190 } 191 192 if (!s_disassembly_attempted) { 193 args_parser.print_usage(stderr, arguments.strings[0]); 194 return 1; 195 } 196 return 0; 197}