sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

commit 05c7d7031c17b5208516cf0461cda6368605a4db
parent d43d9b7f204433e66f44154a1cedd26ef91fa0a5
Author: n64 <n64>
Date:   Wed, 17 Jun 2020 22:14:32 -0400

Refresh 10.1

Diffstat:
MCHANGES | 7+++++++
Mactors/exclamation_box/model.inc.c | 4++--
Massets.json | 4++--
Mdiff.py | 286++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mextract_assets.py | 2+-
Mtools/Makefile | 18++++++++++++++----
Mtools/armips.cpp | 15186++++++++++++++++++++++++++++++++++++++++----------------------------------------
Atools/audiofile/Makefile | 12++++++++++++
Atools/audiofile/audiofile.cpp | 15915+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/audiofile/audiofile.h | 612+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/audiofile/aupvlist.h | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtools/n64graphics.c | 12++++++------
Atools/util/audiofile_strip.patch | 318+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtools/util/generate_armips_cpp.py | 20++++++++++----------
Atools/util/generate_audiofile_cpp.py | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
15 files changed, 24914 insertions(+), 7686 deletions(-)

diff --git a/CHANGES b/CHANGES @@ -1,3 +1,10 @@ +Refresh #10.1 +1.) Diff update (#1033) +2.) Fix texture dimensions for exclamation boxes (#1034) +3.) Fix armips compilation on Windows by changing order of inclusion files (#1035) +4.) Embed libaudiofile into the repo as a single file (#1036) +5.) Fix some tools issues found while compiling on MSYS2 (#1037) + Refresh #10 1.) GCC 9 noreturn UB fixes (#961) 2.) List supported binutils variants in README.md (#960) diff --git a/actors/exclamation_box/model.inc.c b/actors/exclamation_box/model.inc.c @@ -13,7 +13,7 @@ ALIGNED8 static const u8 exclamation_box_seg8_texture_08012E28[] = { // 0x08013628 ALIGNED8 static const u8 exclamation_box_seg8_texture_08013628[] = { -#include "actors/exclamation_box/vanish_cap_box_sides.rgba16.inc.c" +#include "actors/exclamation_box/vanish_cap_box_side.rgba16.inc.c" }; // 0x08014628 @@ -33,7 +33,7 @@ ALIGNED8 static const u8 exclamation_box_seg8_texture_08015E28[] = { // 0x08016628 ALIGNED8 static const u8 exclamation_box_seg8_texture_08016628[] = { -#include "actors/exclamation_box/wing_cap_box_sides.rgba16.inc.c" +#include "actors/exclamation_box/wing_cap_box_side.rgba16.inc.c" }; // 0x08017628 diff --git a/assets.json b/assets.json @@ -210,9 +210,9 @@ "actors/exclamation_box/metal_cap_box_front.rgba16.png": [32,32,2048,{"jp":[2032944,83496],"us":[2040320,83496],"eu":[1912288,83496],"sh":[1888800,83496]}], "actors/exclamation_box/metal_cap_box_side.rgba16.png": [64,32,4096,{"jp":[2032944,85544],"us":[2040320,85544],"eu":[1912288,85544],"sh":[1888800,85544]}], "actors/exclamation_box/vanish_cap_box_front.rgba16.png": [32,32,2048,{"jp":[2032944,77352],"us":[2040320,77352],"eu":[1912288,77352],"sh":[1888800,77352]}], -"actors/exclamation_box/vanish_cap_box_sides.rgba16.png": [64,32,4096,{"jp":[2032944,79400],"us":[2040320,79400],"eu":[1912288,79400],"sh":[1888800,79400]}], +"actors/exclamation_box/vanish_cap_box_side.rgba16.png": [32,64,4096,{"jp":[2032944,79400],"us":[2040320,79400],"eu":[1912288,79400],"sh":[1888800,79400]}], "actors/exclamation_box/wing_cap_box_front.rgba16.png": [32,32,2048,{"jp":[2032944,89640],"us":[2040320,89640],"eu":[1912288,89640],"sh":[1888800,89640]}], -"actors/exclamation_box/wing_cap_box_sides.rgba16.png": [64,32,4096,{"jp":[2032944,91688],"us":[2040320,91688],"eu":[1912288,91688],"sh":[1888800,91688]}], +"actors/exclamation_box/wing_cap_box_side.rgba16.png": [32,64,4096,{"jp":[2032944,91688],"us":[2040320,91688],"eu":[1912288,91688],"sh":[1888800,91688]}], "actors/exclamation_box_outline/exclamation_box_outline.rgba16.png": [32,32,2048,{"jp":[2032944,151912],"us":[2040320,151912],"eu":[1912288,151912],"sh":[1888800,151912]}], "actors/exclamation_box_outline/exclamation_point.rgba16.png": [16,32,1024,{"jp":[2032944,154240],"us":[2040320,154240],"eu":[1912288,154240],"sh":[1888800,154240]}], "actors/explosion/explosion_0.rgba16.png": [32,32,2048,{"jp":[2094912,2568],"us":[2102288,2568],"eu":[1974256,2568],"sh":[1950768,2568]}], diff --git a/diff.py b/diff.py @@ -18,16 +18,18 @@ def fail(msg): sys.exit(1) +MISSING_PREREQUISITES = ( + "Missing prerequisite python module {}. " + "Run `python3 -m pip install --user colorama ansiwrap attrs watchdog python-Levenshtein` to install prerequisites (python-Levenshtein only needed for --algorithm=levenshtein)." +) + try: import attr from colorama import Fore, Style, Back import ansiwrap import watchdog except ModuleNotFoundError as e: - fail( - f"Missing prerequisite python module {e.name}. " - "Run `python3 -m pip install --user colorama ansiwrap attrs watchdog` to install prerequisites." - ) + fail(MISSING_PREREQUISITES.format(e.name)) # Prefer to use diff_settings.py from the current working directory sys.path.insert(0, ".") @@ -121,6 +123,22 @@ parser.add_argument( default=50, help="Sets the width of the left and right view column.", ) +parser.add_argument( + "--algorithm", + dest="algorithm", + default="difflib", + choices=["levenshtein", "difflib"], + help="Diff algorithm to use.", +) + +parser.add_argument( + "--max-size", + "--max-lines", + dest="max_lines", + type=int, + default=1024, + help="The maximum length of the diff, in lines. Not recommended when -f is used.", +) # Project-specific flags, e.g. different versions/make arguments. if hasattr(diff_settings, "add_custom_arguments"): @@ -138,8 +156,8 @@ mapfile = config.get("mapfile", None) makeflags = config.get("makeflags", []) source_directories = config.get("source_directories", None) -MAX_FUNCTION_SIZE_LINES = 1024 -MAX_FUNCTION_SIZE_BYTES = 1024 * 4 +MAX_FUNCTION_SIZE_LINES = args.max_lines +MAX_FUNCTION_SIZE_BYTES = MAX_FUNCTION_SIZE_LINES * 4 COLOR_ROTATION = [ Fore.MAGENTA, @@ -161,6 +179,12 @@ FS_WATCH_EXTENSIONS = [".c", ".h"] # ==== LOGIC ==== +if args.algorithm == "levenshtein": + try: + import Levenshtein + except ModuleNotFoundError as e: + fail(MISSING_PREREQUISITES.format(e.name)) + binutils_prefix = None for binutils_cand in ["mips-linux-gnu-", "mips64-elf-"]: @@ -292,7 +316,7 @@ def dump_objfile(): run_make(objfile) if not os.path.isfile(objfile): - fail("Not able to find .o file for function.") + fail(f"Not able to find .o file for function: {objfile} is not a file.") refobjfile = "expected/" + objfile if not os.path.isfile(refobjfile): @@ -344,28 +368,27 @@ def ansi_ljust(s, width): re_int = re.compile(r"[0-9]+") re_comments = re.compile(r"<.*?>") -re_regs = re.compile(r"\b(a[0-3]|t[0-9]|s[0-7]|at|v[01]|f[12]?[0-9]|f3[01]|fp)\b") -re_sprel = re.compile(r",([1-9][0-9]*|0x[1-9a-f][0-9a-f]*)\(sp\)") +re_regs = re.compile(r"\$?\b(a[0-3]|t[0-9]|s[0-8]|at|v[01]|f[12]?[0-9]|f3[01]|fp)\b") +re_sprel = re.compile(r",([0-9]+|0x[0-9a-f]+)\(sp\)") re_large_imm = re.compile(r"-?[1-9][0-9]{2,}|-?0x[0-9a-f]{3,}") +re_imm = re.compile(r"(\b|-)([0-9]+|0x[0-9a-fA-F]+)\b(?!\(sp)|%(lo|hi)\([^)]*\)") forbidden = set(string.ascii_letters + "_") -branch_likely_instructions = set( - [ - "beql", - "bnel", - "beqzl", - "bnezl", - "bgezl", - "bgtzl", - "blezl", - "bltzl", - "bc1tl", - "bc1fl", - ] -) -branch_instructions = set( - ["b", "beq", "bne", "beqz", "bnez", "bgez", "bgtz", "blez", "bltz", "bc1t", "bc1f"] - + list(branch_likely_instructions) +branch_likely_instructions = { + "beql", + "bnel", + "beqzl", + "bnezl", + "bgezl", + "bgtzl", + "blezl", + "bltzl", + "bc1tl", + "bc1fl", +} +branch_instructions = branch_likely_instructions.union( + {"b", "beq", "bne", "beqz", "bnez", "bgez", "bgtz", "blez", "bltz", "bc1t", "bc1f"} ) +jump_instructions = branch_instructions.union({"jal", "j"}) def hexify_int(row, pat): @@ -420,6 +443,7 @@ def process_reloc(row, prev): def process(lines): mnemonics = [] diff_rows = [] + rows_with_imms = [] skip_next = False originals = [] line_nums = [] @@ -434,8 +458,9 @@ def process(lines): continue if "R_MIPS_" in row: - if diff_rows[-1] != "<delay-slot>": - diff_rows[-1] = process_reloc(row, diff_rows[-1]) + # N.B. Don't transform the diff rows, they already ignore immediates + # if diff_rows[-1] != '<delay-slot>': + # diff_rows[-1] = process_reloc(row, rows_with_imms[-1]) originals[-1] = process_reloc(row, originals[-1]) continue @@ -446,7 +471,7 @@ def process(lines): line_num = tabs[0].strip() row_parts = row.split("\t", 1) mnemonic = row_parts[0].strip() - if mnemonic not in branch_instructions: + if mnemonic not in jump_instructions: row = re.sub(re_int, lambda s: hexify_int(row, s), row) original = row if skip_next: @@ -457,11 +482,16 @@ def process(lines): skip_next = True row = re.sub(re_regs, "<reg>", row) row = re.sub(re_sprel, ",addr(sp)", row) - if args.ignore_large_imms: - row = re.sub(re_large_imm, "<imm>", row) + row_with_imm = row + if mnemonic in jump_instructions: + row = row.strip() + row, _ = split_off_branch(row) + row += "<imm>" + else: + row = re.sub(re_imm, "<imm>", row) - # Replace tabs with spaces mnemonics.append(mnemonic) + rows_with_imms.append(row_with_imm) diff_rows.append(row) originals.append(original) line_nums.append(line_num) @@ -504,12 +534,85 @@ class SymbolColorer: return f"{color}{t}{Fore.RESET}" -def normalize_large_imms(row): +def maybe_normalize_large_imms(row): if args.ignore_large_imms: row = re.sub(re_large_imm, "<imm>", row) return row +def normalize_imms(row): + return re.sub(re_imm, "<imm>", row) + + +def normalize_stack(row): + return re.sub(re_sprel, ",addr(sp)", row) + + +def split_off_branch(line): + parts = line.split(",") + if len(parts) < 2: + parts = line.split(None, 1) + off = len(line) - len(parts[-1]) + return line[:off], line[off:] + + +def color_imms(out1, out2): + g1 = [] + g2 = [] + re.sub(re_imm, lambda s: g1.append(s.group()), out1) + re.sub(re_imm, lambda s: g2.append(s.group()), out2) + if len(g1) == len(g2): + diffs = [x != y for (x, y) in zip(g1, g2)] + it = iter(diffs) + + def maybe_color(s): + return f"{Fore.LIGHTBLUE_EX}{s}{Style.RESET_ALL}" if next(it) else s + + out1 = re.sub(re_imm, lambda s: maybe_color(s.group()), out1) + it = iter(diffs) + out2 = re.sub(re_imm, lambda s: maybe_color(s.group()), out2) + return out1, out2 + + +def color_branch_imms(br1, br2): + if br1 != br2: + br1 = f"{Fore.LIGHTBLUE_EX}{br1}{Style.RESET_ALL}" + br2 = f"{Fore.LIGHTBLUE_EX}{br2}{Style.RESET_ALL}" + return br1, br2 + + +def diff_sequences_difflib(seq1, seq2): + differ = difflib.SequenceMatcher(a=seq1, b=seq2, autojunk=False) + return differ.get_opcodes() + + +def diff_sequences(seq1, seq2): + if ( + args.algorithm != "levenshtein" + or len(seq1) * len(seq2) > 4 * 10 ** 8 + or len(seq1) + len(seq2) >= 0x110000 + ): + return diff_sequences_difflib(seq1, seq2) + + # The Levenshtein library assumes that we compare strings, not lists. Convert. + # (Per the check above we know we have fewer than 0x110000 unique elements, so chr() works.) + remapping = {} + + def remap(seq): + seq = seq[:] + for i in range(len(seq)): + val = remapping.get(seq[i]) + if val is None: + val = chr(len(remapping)) + remapping[seq[i]] = val + seq[i] = val + return "".join(seq) + + seq1 = remap(seq1) + seq2 = remap(seq2) + return Levenshtein.opcodes(seq1, seq2) + + def do_diff(basedump, mydump): asm_lines1 = basedump.split("\n") asm_lines2 = mydump.split("\n") @@ -545,10 +648,7 @@ def do_diff(basedump, mydump): btset.add(bt + ":") sc.color_symbol(bt + ":") - differ: difflib.SequenceMatcher = difflib.SequenceMatcher( - a=mnemonics1, b=mnemonics2, autojunk=False - ) - for (tag, i1, i2, j1, j2) in differ.get_opcodes(): + for (tag, i1, i2, j1, j2) in diff_sequences(mnemonics1, mnemonics2): lines1 = asm_lines1[i1:i2] lines2 = asm_lines2[j1:j2] @@ -572,65 +672,113 @@ def do_diff(basedump, mydump): original2 = "" line_num2 = "" - line_color = Fore.RESET + has1 = has2 = True + line_color1 = line_color2 = sym_color = Fore.RESET line_prefix = " " if line1 == line2: - if normalize_large_imms(original1) == normalize_large_imms(original2): - out1 = f"{original1}" - out2 = f"{original2}" + if not line1: + has1 = has2 = False + if maybe_normalize_large_imms(original1) == maybe_normalize_large_imms( + original2 + ): + out1 = original1 + out2 = original2 elif line1 == "<delay-slot>": out1 = f"{Style.DIM}{original1}" out2 = f"{Style.DIM}{original2}" else: - line_color = Fore.YELLOW - line_prefix = "r" - out1 = f"{Fore.YELLOW}{original1}{Style.RESET_ALL}" - out2 = f"{Fore.YELLOW}{original2}{Style.RESET_ALL}" - out1 = re.sub(re_regs, lambda s: sc1.color_symbol(s.group()), out1) - out2 = re.sub(re_regs, lambda s: sc2.color_symbol(s.group()), out2) - out1 = re.sub(re_sprel, lambda s: sc3.color_symbol(s.group()), out1) - out2 = re.sub(re_sprel, lambda s: sc4.color_symbol(s.group()), out2) + mnemonic = original1.split()[0] + out1, out2 = original1, original2 + branch1 = branch2 = "" + if mnemonic in jump_instructions: + out1, branch1 = split_off_branch(original1) + out2, branch2 = split_off_branch(original2) + branchless1 = out1 + branchless2 = out2 + out1, out2 = color_imms(out1, out2) + branch1, branch2 = color_branch_imms(branch1, branch2) + out1 += branch1 + out2 += branch2 + if normalize_imms(branchless1) == normalize_imms(branchless2): + # only imms differences + sym_color = Fore.LIGHTBLUE_EX + line_prefix = "i" + else: + out1 = re.sub( + re_sprel, + lambda s: "," + sc3.color_symbol(s.group()[1:]), + out1, + ) + out2 = re.sub( + re_sprel, + lambda s: "," + sc4.color_symbol(s.group()[1:]), + out2, + ) + if normalize_stack(branchless1) == normalize_stack(branchless2): + # only stack differences (luckily stack and imm + # differences can't be combined in MIPS, so we + # don't have to think about that case) + sym_color = Fore.YELLOW + line_prefix = "s" + else: + # regs differences and maybe imms as well + out1 = re.sub( + re_regs, lambda s: sc1.color_symbol(s.group()), out1 + ) + out2 = re.sub( + re_regs, lambda s: sc2.color_symbol(s.group()), out2 + ) + line_color1 = line_color2 = sym_color = Fore.YELLOW + line_prefix = "r" elif tag in ["replace", "equal"]: line_prefix = "|" - line_color = Fore.BLUE - out1 = f"{Fore.BLUE}{original1}{Style.RESET_ALL}" - out2 = f"{Fore.BLUE}{original2}{Style.RESET_ALL}" + line_color1 = Fore.LIGHTBLUE_EX + line_color2 = Fore.LIGHTBLUE_EX + sym_color = Fore.LIGHTBLUE_EX + out1 = original1 + out2 = original2 elif tag == "delete": line_prefix = "<" - line_color = Fore.RED - out1 = f"{Fore.RED}{original1}{Style.RESET_ALL}" + line_color1 = line_color2 = sym_color = Fore.RED + has2 = False + out1 = original1 out2 = "" elif tag == "insert": line_prefix = ">" - line_color = Fore.GREEN + line_color1 = line_color2 = sym_color = Fore.GREEN + has1 = False out1 = "" - out2 = f"{Fore.GREEN}{original2}{Style.RESET_ALL}" + out2 = original2 in_arrow1 = " " in_arrow2 = " " out_arrow1 = "" out_arrow2 = "" - line_num1 = line_num1 if out1 else "" - line_num2 = line_num2 if out2 else "" + line_num1 = line_num1 if has1 else "" + line_num2 = line_num2 if has2 else "" + + if sym_color == line_color2: + line_color2 = "" - if args.show_branches and out1: + if args.show_branches and has1: if line_num1 in bts1: - in_arrow1 = sc5.color_symbol(line_num1, "~>") + in_arrow1 = sc5.color_symbol(line_num1, "~>") + line_color1 if branch_targets1[i1 + k] is not None: out_arrow1 = " " + sc5.color_symbol( branch_targets1[i1 + k] + ":", "~>" ) - if args.show_branches and out2: + if args.show_branches and has2: if line_num2 in bts2: - in_arrow2 = sc6.color_symbol(line_num2, "~>") + in_arrow2 = sc6.color_symbol(line_num2, "~>") + line_color2 if branch_targets2[j1 + k] is not None: out_arrow2 = " " + sc6.color_symbol( branch_targets2[j1 + k] + ":", "~>" ) - out1 = f"{line_color}{line_num1} {in_arrow1} {out1}{Style.RESET_ALL}{out_arrow1}" - out2 = f"{line_color}{line_prefix} {line_num2} {in_arrow2} {out2}{Style.RESET_ALL}{out_arrow2}" - output.append(format_single_line_diff(out1, out2, args.column_width)) + out1 = f"{line_color1}{line_num1} {in_arrow1} {out1}{Style.RESET_ALL}{out_arrow1}" + out2 = f"{line_color2}{line_num2} {in_arrow2} {out2}{Style.RESET_ALL}{out_arrow2}" + mid = f"{sym_color}{line_prefix} " + output.append(format_single_line_diff(out1, mid + out2, args.column_width)) return output[args.skip_lines :] @@ -677,7 +825,7 @@ def debounced_fs_watch(targets, outq, debounce_delay): observer.schedule(event_handler, target, recursive=True) else: file_targets.append(target) - target = os.path.dirname(target) + target = os.path.dirname(target) or "." if target not in observed: observed.add(target) observer.schedule(event_handler, target) @@ -800,7 +948,7 @@ def main(): if args.write_asm is not None: mydump = run_objdump(mycmd) - with open(args.write_asm) as f: + with open(args.write_asm, "w") as f: f.write(mydump) print(f"Wrote assembly to {args.write_asm}.") sys.exit(0) @@ -850,7 +998,9 @@ def main(): ret = run_make(make_target, capture_output=True) if ret.returncode != 0: display.update( - ret.stderr.decode() or ret.stdout.decode(), error=True + ret.stderr.decode("utf-8-sig", "replace") + or ret.stdout.decode("utf-8-sig", "replace"), + error=True, ) continue mydump = run_objdump(mycmd) diff --git a/extract_assets.py b/extract_assets.py @@ -257,7 +257,7 @@ def main(): ) finally: png_file.close() - remove_file(png_file.name) + os.remove(png_file.name) else: with open(asset, "wb") as f: f.write(input) diff --git a/tools/Makefile b/tools/Makefile @@ -9,10 +9,14 @@ ifeq (, $(shell which armips 2> /dev/null)) CXX_PROGRAMS += armips endif +ifeq ($(OS),Windows_NT) +ARMIPS_FLAGS := -municode +endif + default: all armips: armips.cpp - $(CXX) $(CXXFLAGS) -fno-exceptions -fno-rtti -pipe $^ -o $@ -lpthread + $(CXX) $(CXXFLAGS) -fno-exceptions -fno-rtti -pipe $^ -o $@ -lpthread $(ARMIPS_FLAGS) n64graphics_SOURCES := n64graphics.c utils.c n64graphics_CFLAGS := -DN64GRAPHICS_STANDALONE @@ -33,8 +37,8 @@ aifc_decode_SOURCES := aifc_decode.c aiff_extract_codebook_SOURCES := aiff_extract_codebook.c tabledesign_SOURCES := sdk-tools/tabledesign/codebook.c sdk-tools/tabledesign/estimate.c sdk-tools/tabledesign/print.c sdk-tools/tabledesign/tabledesign.c -tabledesign_CFLAGS := -Wno-uninitialized -tabledesign_LDFLAGS := -laudiofile +tabledesign_CFLAGS := -Iaudiofile -Wno-uninitialized +tabledesign_LDFLAGS := -Laudiofile -laudiofile -lstdc++ -lm vadpcm_enc_SOURCES := sdk-tools/adpcm/vadpcm_enc.c sdk-tools/adpcm/vpredictor.c sdk-tools/adpcm/quant.c sdk-tools/adpcm/util.c sdk-tools/adpcm/vencode.c vadpcm_enc_CFLAGS := -Wno-unused-result -Wno-uninitialized -Wno-sign-compare -Wno-absolute-value @@ -43,10 +47,16 @@ extract_data_for_mio_SOURCES := extract_data_for_mio.c skyconv_SOURCES := skyconv.c n64graphics.c utils.c -all: $(PROGRAMS) $(CXX_PROGRAMS) +LIBAUDIOFILE := audiofile/libaudiofile.a + +$(LIBAUDIOFILE): + @$(MAKE) -C audiofile + +all: $(LIBAUDIOFILE) $(PROGRAMS) $(CXX_PROGRAMS) clean: $(RM) $(PROGRAMS) $(CXX_PROGRAMS) + $(MAKE) -C audiofile clean define COMPILE $(1): $($1_SOURCES) diff --git a/tools/armips.cpp b/tools/armips.cpp @@ -10859,7302 +10859,3344 @@ void CommandSequence::writeSymData(SymbolData& symData) const } } -// file: Core/ELF/ElfFile.cpp -#include <vector> -#include <algorithm> +// file: Parser/DirectivesParser.cpp -#ifndef _WIN32 -#include <strings.h> -#define _stricmp strcasecmp -#endif +#include <initializer_list> +#include <algorithm> -static bool stringEqualInsensitive(const std::string& a, const std::string& b) +std::unique_ptr<CAssemblerCommand> parseDirectiveOpen(Parser& parser, int flags) { - if (a.size() != b.size()) - return false; - return _stricmp(a.c_str(),b.c_str()) == 0; -} + std::vector<Expression> list; + if (parser.parseExpressionList(list,2,3) == false) + return nullptr; -bool compareSection(ElfSection* a, ElfSection* b) -{ - return a->getOffset() < b->getOffset(); -} + int64_t memoryAddress; + std::wstring inputName, outputName; -ElfSection::ElfSection(Elf32_Shdr header): header(header) -{ - owner = nullptr; -} + if (list[0].evaluateString(inputName,false) == false) + return nullptr; -void ElfSection::setOwner(ElfSegment* segment) -{ - header.sh_offset -= segment->getOffset(); - owner = segment; -} + if (list.back().evaluateInteger(memoryAddress) == false) + return nullptr; -void ElfSection::writeHeader(ByteArray& data, int pos, Endianness endianness) -{ - data.replaceDoubleWord(pos + 0x00, header.sh_name, endianness); - data.replaceDoubleWord(pos + 0x04, header.sh_type, endianness); - data.replaceDoubleWord(pos + 0x08, header.sh_flags, endianness); - data.replaceDoubleWord(pos + 0x0C, header.sh_addr, endianness); - data.replaceDoubleWord(pos + 0x10, header.sh_offset, endianness); - data.replaceDoubleWord(pos + 0x14, header.sh_size, endianness); - data.replaceDoubleWord(pos + 0x18, header.sh_link, endianness); - data.replaceDoubleWord(pos + 0x1C, header.sh_info, endianness); - data.replaceDoubleWord(pos + 0x20, header.sh_addralign, endianness); - data.replaceDoubleWord(pos + 0x24, header.sh_entsize, endianness); + auto file = ::make_unique<CDirectiveFile>(); + if (list.size() == 3) + { + if (list[1].evaluateString(outputName,false) == false) + return nullptr; + + file->initCopy(inputName,outputName,memoryAddress); + return file; + } else { + file->initOpen(inputName,memoryAddress); + return file; + } } -// only called for segmentless sections -void ElfSection::writeData(ByteArray& output) +std::unique_ptr<CAssemblerCommand> parseDirectiveCreate(Parser& parser, int flags) { - if (header.sh_type == SHT_NULL) return; + std::vector<Expression> list; + if (parser.parseExpressionList(list,2,2) == false) + return nullptr; - // nobits sections still get a provisional file address - if (header.sh_type == SHT_NOBITS) - { - header.sh_offset = (Elf32_Off) output.size(); - } + int64_t memoryAddress; + std::wstring inputName, outputName; - if (header.sh_addralign != (unsigned) -1) - output.alignSize(header.sh_addralign); - header.sh_offset = (Elf32_Off) output.size(); - output.append(data); -} + if (list[0].evaluateString(inputName,false) == false) + return nullptr; -void ElfSection::setOffsetBase(int base) -{ - header.sh_offset += base; + if (list.back().evaluateInteger(memoryAddress) == false) + return nullptr; + + auto file = ::make_unique<CDirectiveFile>(); + file->initCreate(inputName,memoryAddress); + return file; } -ElfSegment::ElfSegment(Elf32_Phdr header, ByteArray& segmentData): header(header) +std::unique_ptr<CAssemblerCommand> parseDirectiveClose(Parser& parser, int flags) { - data = segmentData; - paddrSection = nullptr; + auto file = ::make_unique<CDirectiveFile>(); + file->initClose(); + return file; } -bool ElfSegment::isSectionPartOf(ElfSection* section) +std::unique_ptr<CAssemblerCommand> parseDirectiveIncbin(Parser& parser, int flags) { - int sectionStart = section->getOffset(); - int sectionSize = section->getType() == SHT_NOBITS ? 0 : section->getSize(); - int sectionEnd = sectionStart+sectionSize; - - int segmentStart = header.p_offset; - int segmentEnd = segmentStart+header.p_filesz; + std::vector<Expression> list; + if (parser.parseExpressionList(list,1,3) == false) + return nullptr; - // exclusive > in case the size is 0 - if (sectionStart < (int)header.p_offset || sectionStart > segmentEnd) return false; + std::wstring fileName; + if (list[0].evaluateString(fileName,false) == false) + return nullptr; - // does an empty section belong to this or the next segment? hm... - if (sectionStart == segmentEnd) return sectionSize == 0; + auto incbin = ::make_unique<CDirectiveIncbin>(fileName); + if (list.size() >= 2) + incbin->setStart(list[1]); - // the start is inside the section and the size is not 0, so the end should be in here too - if (sectionEnd > segmentEnd) - { - Logger::printError(Logger::Error,L"Section partially contained in segment"); - return false; - } + if (list.size() == 3) + incbin->setSize(list[2]); - return true; + return incbin; } -void ElfSegment::addSection(ElfSection* section) +std::unique_ptr<CAssemblerCommand> parseDirectivePosition(Parser& parser, int flags) { - if (header.p_paddr != 0) + Expression exp = parser.parseExpression(); + if (exp.isLoaded() == false) + return nullptr; + + CDirectivePosition::Type type; + switch (flags & DIRECTIVE_USERMASK) { - if (section->getOffset() == header.p_paddr) - { - paddrSection = section; - } + case DIRECTIVE_POS_PHYSICAL: + type = CDirectivePosition::Physical; + break; + case DIRECTIVE_POS_VIRTUAL: + type = CDirectivePosition::Virtual; + break; + default: + return nullptr; } - section->setOwner(this); - sections.push_back(section); + return ::make_unique<CDirectivePosition>(exp,type); } -void ElfSegment::writeData(ByteArray& output) +std::unique_ptr<CAssemblerCommand> parseDirectiveAlignFill(Parser& parser, int flags) { - if (sections.size() == 0) + CDirectiveAlignFill::Mode mode; + switch (flags & DIRECTIVE_USERMASK) { - output.alignSize(header.p_align); - if (header.p_offset == header.p_paddr) - header.p_paddr = (Elf32_Addr) output.size(); - - header.p_offset = (Elf32_Off) output.size(); - return; + case DIRECTIVE_ALIGN_VIRTUAL: + mode = CDirectiveAlignFill::AlignVirtual; + break; + case DIRECTIVE_ALIGN_PHYSICAL: + mode = CDirectiveAlignFill::AlignPhysical; + break; + case DIRECTIVE_ALIGN_FILL: + mode = CDirectiveAlignFill::Fill; + break; + default: + return nullptr; } - // align segment to alignment of first section - int align = std::max<int>(sections[0]->getAlignment(),16); - output.alignSize(align); - - header.p_offset = (Elf32_Off) output.size(); - for (int i = 0; i < (int)sections.size(); i++) - { - sections[i]->setOffsetBase(header.p_offset); - } + if (mode != CDirectiveAlignFill::Fill && parser.peekToken().type == TokenType::Separator) + return ::make_unique<CDirectiveAlignFill>(UINT64_C(4),mode); - if (paddrSection) - { - header.p_paddr = paddrSection->getOffset(); - } + std::vector<Expression> list; + if (parser.parseExpressionList(list,1,2) == false) + return nullptr; - output.append(data); + if (list.size() == 2) + return ::make_unique<CDirectiveAlignFill>(list[0],list[1],mode); + else + return ::make_unique<CDirectiveAlignFill>(list[0],mode); } -void ElfSegment::writeHeader(ByteArray& data, int pos, Endianness endianness) +std::unique_ptr<CAssemblerCommand> parseDirectiveSkip(Parser& parser, int flags) { - data.replaceDoubleWord(pos + 0x00, header.p_type, endianness); - data.replaceDoubleWord(pos + 0x04, header.p_offset, endianness); - data.replaceDoubleWord(pos + 0x08, header.p_vaddr, endianness); - data.replaceDoubleWord(pos + 0x0C, header.p_paddr, endianness); - data.replaceDoubleWord(pos + 0x10, header.p_filesz, endianness); - data.replaceDoubleWord(pos + 0x14, header.p_memsz, endianness); - data.replaceDoubleWord(pos + 0x18, header.p_flags, endianness); - data.replaceDoubleWord(pos + 0x1C, header.p_align, endianness); + std::vector<Expression> list; + if (parser.parseExpressionList(list,1,1) == false) + return nullptr; + + return ::make_unique<CDirectiveSkip>(list[0]); } -void ElfSegment::splitSections() +std::unique_ptr<CAssemblerCommand> parseDirectiveHeaderSize(Parser& parser, int flags) { + Expression exp = parser.parseExpression(); + if (exp.isLoaded() == false) + return nullptr; + return ::make_unique<CDirectiveHeaderSize>(exp); } -int ElfSegment::findSection(const std::string& name) +std::unique_ptr<CAssemblerCommand> parseDirectiveObjImport(Parser& parser, int flags) { - for (size_t i = 0; i < sections.size(); i++) - { - if (stringEqualInsensitive(name,sections[i]->getName())) - return i; - } + std::vector<Expression> list; + if (parser.parseExpressionList(list,1,2) == false) + return nullptr; - return -1; -} + std::wstring fileName; + if (list[0].evaluateString(fileName,true) == false) + return nullptr; -void ElfSegment::writeToData(size_t offset, void* src, size_t size) -{ - for (size_t i = 0; i < size; i++) + if (list.size() == 2) { - data[offset+i] = ((byte*)src)[i]; + std::wstring ctorName; + if (list[1].evaluateIdentifier(ctorName) == false) + return nullptr; + + return ::make_unique<DirectiveObjImport>(fileName,ctorName); } -} -void ElfSegment::sortSections() -{ - std::sort(sections.begin(),sections.end(),compareSection); + return ::make_unique<DirectiveObjImport>(fileName); } -void ElfFile::loadSectionNames() +std::unique_ptr<CAssemblerCommand> parseDirectiveConditional(Parser& parser, int flags) { - if (fileHeader.e_shstrndx == SHN_UNDEF) return; + ConditionType type; + std::wstring name; + Expression exp; - // check if the string table is actually a string table - // sometimes it gives the wrong section id - size_t strTablePos = sections[fileHeader.e_shstrndx]->getOffset(); - size_t strTableSize = sections[fileHeader.e_shstrndx]->getSize(); - for (size_t i = 0; i < strTableSize; i++) + const Token& start = parser.peekToken(); + ConditionalResult condResult = ConditionalResult::Unknown; + switch (flags) { - if (fileData[strTablePos+i] != 0 && fileData[strTablePos+i] < 0x20) - return; - if (fileData[strTablePos+i] > 0x7F) - return; + case DIRECTIVE_COND_IF: + type = ConditionType::IF; + exp = parser.parseExpression(); + if (exp.isLoaded() == false) + { + parser.printError(start,L"Invalid condition"); + return ::make_unique<DummyCommand>(); + } + + if (exp.isConstExpression()) + { + ExpressionValue result = exp.evaluate(); + if (result.isInt()) + condResult = result.intValue != 0 ? ConditionalResult::True : ConditionalResult::False; + } + break; + case DIRECTIVE_COND_IFDEF: + type = ConditionType::IFDEF; + if (parser.parseIdentifier(name) == false) + return nullptr; + break; + case DIRECTIVE_COND_IFNDEF: + type = ConditionType::IFNDEF; + if (parser.parseIdentifier(name) == false) + return nullptr; + break; } - for (size_t i = 0; i < sections.size(); i++) + if(parser.nextToken().type != TokenType::Separator) { - ElfSection* section = sections[i]; - if (section->getType() == SHT_NULL) continue; + parser.printError(start,L"Directive not terminated"); + return nullptr; + } - int strTablePos = sections[fileHeader.e_shstrndx]->getOffset(); - int offset = strTablePos+section->getNameOffset(); + parser.pushConditionalResult(condResult); + std::unique_ptr<CAssemblerCommand> ifBlock = parser.parseCommandSequence(L'.', {L".else", L".elseif", L".elseifdef", L".elseifndef", L".endif"}); + parser.popConditionalResult(); - char* name = (char*) fileData.data(offset); - std::string strName = name; - section->setName(strName); + // update the file info so that else commands get the right line number + parser.updateFileInfo(); + + std::unique_ptr<CAssemblerCommand> elseBlock = nullptr; + const Token &next = parser.nextToken(); + const std::wstring stringValue = next.getStringValue(); + + ConditionalResult elseResult; + switch (condResult) + { + case ConditionalResult::True: + elseResult = ConditionalResult::False; + break; + case ConditionalResult::False: + elseResult = ConditionalResult::True; + break; + case ConditionalResult::Unknown: + elseResult = condResult; + break; } -} -void ElfFile::determinePartOrder() -{ - size_t segmentTable = fileHeader.e_phoff; - size_t sectionTable = fileHeader.e_shoff; + parser.pushConditionalResult(elseResult); + if (stringValue == L".else") + { + elseBlock = parser.parseCommandSequence(L'.', {L".endif"}); - // segments - size_t firstSegmentStart = fileData.size(), lastSegmentEnd = 0; - for (size_t i = 0; i < fileHeader.e_phnum; i++) + parser.eatToken(); // eat .endif + } else if (stringValue == L".elseif") { - size_t pos = fileHeader.e_phoff+i*fileHeader.e_phentsize; + elseBlock = parseDirectiveConditional(parser,DIRECTIVE_COND_IF); + } else if (stringValue == L".elseifdef") + { + elseBlock = parseDirectiveConditional(parser,DIRECTIVE_COND_IFDEF); + } else if (stringValue == L".elseifndef") + { + elseBlock = parseDirectiveConditional(parser,DIRECTIVE_COND_IFNDEF); + } else if (stringValue != L".endif") + { + parser.popConditionalResult(); + return nullptr; + } - Elf32_Phdr segmentHeader; - loadProgramHeader(segmentHeader, fileData, pos); - size_t end = segmentHeader.p_offset + segmentHeader.p_filesz; + parser.popConditionalResult(); - if (segmentHeader.p_offset < firstSegmentStart) firstSegmentStart = segmentHeader.p_offset; - if (lastSegmentEnd < end) lastSegmentEnd = end; + // for true or false blocks, there's no need to create a conditional command + if (condResult == ConditionalResult::True) + { + return ifBlock; } - // segmentless sections - size_t firstSectionStart = fileData.size(), lastSectionEnd = 0; - for (size_t i = 0; i < segmentlessSections.size(); i++) + if (condResult == ConditionalResult::False) { - if (segmentlessSections[i]->getType() == SHT_NULL) continue; + if (elseBlock != nullptr) + return elseBlock; + else + return ::make_unique<DummyCommand>(); + } - size_t start = segmentlessSections[i]->getOffset(); - size_t end = start+segmentlessSections[i]->getSize(); + std::unique_ptr<CDirectiveConditional> cond; + if (exp.isLoaded()) + cond = ::make_unique<CDirectiveConditional>(type,exp); + else if (name.size() != 0) + cond = ::make_unique<CDirectiveConditional>(type,name); + else + cond = ::make_unique<CDirectiveConditional>(type); - if (start == 0 && end == 0) - continue; - if (start < firstSectionStart) firstSectionStart = start; - if (lastSectionEnd < end) lastSectionEnd = end; - } + cond->setContent(std::move(ifBlock),std::move(elseBlock)); + return cond; +} - struct PartsSort { - size_t offset; - ElfPart type; - bool operator<(const PartsSort& other) const { return offset < other.offset; }; - }; +std::unique_ptr<CAssemblerCommand> parseDirectiveTable(Parser& parser, int flags) +{ + const Token& start = parser.peekToken(); - PartsSort temp[4] = { - { segmentTable, ELFPART_SEGMENTTABLE }, - { sectionTable, ELFPART_SECTIONTABLE }, - { firstSegmentStart, ELFPART_SEGMENTS }, - { firstSectionStart, ELFPART_SEGMENTLESSSECTIONS }, - }; + std::vector<Expression> list; + if (parser.parseExpressionList(list,1,2) == false) + return nullptr; - std::sort(&temp[0],&temp[4]); + std::wstring fileName; + if (list[0].evaluateString(fileName,true) == false) + { + parser.printError(start,L"Invalid file name"); + return nullptr; + } - for (size_t i = 0; i < 4; i++) + TextFile::Encoding encoding = TextFile::GUESS; + if (list.size() == 2) { - partsOrder[i] = temp[i].type; + std::wstring encodingName; + if (list[1].evaluateString(encodingName,true) == false) + { + parser.printError(start,L"Invalid encoding name"); + return nullptr; + } + + encoding = getEncodingFromString(encodingName); } + + return ::make_unique<TableCommand>(fileName,encoding); } -int ElfFile::findSegmentlessSection(const std::string& name) +std::unique_ptr<CAssemblerCommand> parseDirectiveData(Parser& parser, int flags) { - for (size_t i = 0; i < segmentlessSections.size(); i++) + bool terminate = false; + if (flags & DIRECTIVE_DATA_TERMINATION) { - if (stringEqualInsensitive(name,segmentlessSections[i]->getName())) - return i; + terminate = true; + flags &= ~DIRECTIVE_DATA_TERMINATION; } - return -1; -} + std::vector<Expression> list; + if (parser.parseExpressionList(list,1,-1) == false) + return nullptr; -void ElfFile::loadElfHeader() -{ - memcpy(fileHeader.e_ident, &fileData[0], sizeof(fileHeader.e_ident)); - Endianness endianness = getEndianness(); - fileHeader.e_type = fileData.getWord(0x10, endianness); - fileHeader.e_machine = fileData.getWord(0x12, endianness); - fileHeader.e_version = fileData.getDoubleWord(0x14, endianness); - fileHeader.e_entry = fileData.getDoubleWord(0x18, endianness); - fileHeader.e_phoff = fileData.getDoubleWord(0x1C, endianness); - fileHeader.e_shoff = fileData.getDoubleWord(0x20, endianness); - fileHeader.e_flags = fileData.getDoubleWord(0x24, endianness); - fileHeader.e_ehsize = fileData.getWord(0x28, endianness); - fileHeader.e_phentsize = fileData.getWord(0x2A, endianness); - fileHeader.e_phnum = fileData.getWord(0x2C, endianness); - fileHeader.e_shentsize = fileData.getWord(0x2E, endianness); - fileHeader.e_shnum = fileData.getWord(0x30, endianness); - fileHeader.e_shstrndx = fileData.getWord(0x32, endianness); -} + auto data = ::make_unique<CDirectiveData>(); + switch (flags & DIRECTIVE_USERMASK) + { + case DIRECTIVE_DATA_8: + data->setNormal(list,1); + break; + case DIRECTIVE_DATA_16: + data->setNormal(list,2); + break; + case DIRECTIVE_DATA_32: + data->setNormal(list,4); + break; + case DIRECTIVE_DATA_64: + data->setNormal(list,8); + break; + case DIRECTIVE_DATA_ASCII: + data->setAscii(list,terminate); + break; + case DIRECTIVE_DATA_SJIS: + data->setSjis(list,terminate); + break; + case DIRECTIVE_DATA_CUSTOM: + data->setCustom(list,terminate); + break; + case DIRECTIVE_DATA_FLOAT: + data->setFloat(list); + break; + case DIRECTIVE_DATA_DOUBLE: + data->setDouble(list); + break; + } -void ElfFile::writeHeader(ByteArray& data, int pos, Endianness endianness) -{ - memcpy(&fileData[0], fileHeader.e_ident, sizeof(fileHeader.e_ident)); - data.replaceWord(pos + 0x10, fileHeader.e_type, endianness); - data.replaceWord(pos + 0x12, fileHeader.e_machine, endianness); - data.replaceDoubleWord(pos + 0x14, fileHeader.e_version, endianness); - data.replaceDoubleWord(pos + 0x18, fileHeader.e_entry, endianness); - data.replaceDoubleWord(pos + 0x1C, fileHeader.e_phoff, endianness); - data.replaceDoubleWord(pos + 0x20, fileHeader.e_shoff, endianness); - data.replaceDoubleWord(pos + 0x24, fileHeader.e_flags, endianness); - data.replaceWord(pos + 0x28, fileHeader.e_ehsize, endianness); - data.replaceWord(pos + 0x2A, fileHeader.e_phentsize, endianness); - data.replaceWord(pos + 0x2C, fileHeader.e_phnum, endianness); - data.replaceWord(pos + 0x2E, fileHeader.e_shentsize, endianness); - data.replaceWord(pos + 0x30, fileHeader.e_shnum, endianness); - data.replaceWord(pos + 0x32, fileHeader.e_shstrndx, endianness); + return data; } -void ElfFile::loadProgramHeader(Elf32_Phdr& header, ByteArray& data, int pos) +std::unique_ptr<CAssemblerCommand> parseDirectiveMipsArch(Parser& parser, int flags) { - Endianness endianness = getEndianness(); - header.p_type = data.getDoubleWord(pos + 0x00, endianness); - header.p_offset = data.getDoubleWord(pos + 0x04, endianness); - header.p_vaddr = data.getDoubleWord(pos + 0x08, endianness); - header.p_paddr = data.getDoubleWord(pos + 0x0C, endianness); - header.p_filesz = data.getDoubleWord(pos + 0x10, endianness); - header.p_memsz = data.getDoubleWord(pos + 0x14, endianness); - header.p_flags = data.getDoubleWord(pos + 0x18, endianness); - header.p_align = data.getDoubleWord(pos + 0x1C, endianness); -} + Arch = &Mips; + Mips.SetLoadDelay(false, 0); -void ElfFile::loadSectionHeader(Elf32_Shdr& header, ByteArray& data, int pos) -{ - Endianness endianness = getEndianness(); - header.sh_name = data.getDoubleWord(pos + 0x00, endianness); - header.sh_type = data.getDoubleWord(pos + 0x04, endianness); - header.sh_flags = data.getDoubleWord(pos + 0x08, endianness); - header.sh_addr = data.getDoubleWord(pos + 0x0C, endianness); - header.sh_offset = data.getDoubleWord(pos + 0x10, endianness); - header.sh_size = data.getDoubleWord(pos + 0x14, endianness); - header.sh_link = data.getDoubleWord(pos + 0x18, endianness); - header.sh_info = data.getDoubleWord(pos + 0x1C, endianness); - header.sh_addralign = data.getDoubleWord(pos + 0x20, endianness); - header.sh_entsize = data.getDoubleWord(pos + 0x24, endianness); -} + switch (flags) + { + case DIRECTIVE_MIPS_PSX: + Mips.SetVersion(MARCH_PSX); + return ::make_unique<ArchitectureCommand>(L".psx", L""); + case DIRECTIVE_MIPS_PS2: + Mips.SetVersion(MARCH_PS2); + return ::make_unique<ArchitectureCommand>(L".ps2", L""); + case DIRECTIVE_MIPS_PSP: + Mips.SetVersion(MARCH_PSP); + return ::make_unique<ArchitectureCommand>(L".psp", L""); + case DIRECTIVE_MIPS_N64: + Mips.SetVersion(MARCH_N64); + return ::make_unique<ArchitectureCommand>(L".n64", L""); + case DIRECTIVE_MIPS_RSP: + Mips.SetVersion(MARCH_RSP); + return ::make_unique<ArchitectureCommand>(L".rsp", L""); + } -bool ElfFile::load(const std::wstring& fileName, bool sort) -{ - ByteArray data = ByteArray::fromFile(fileName); - if (data.size() == 0) - return false; - return load(data,sort); + return nullptr; } -bool ElfFile::load(ByteArray& data, bool sort) +std::unique_ptr<CAssemblerCommand> parseDirectiveArmArch(Parser& parser, int flags) { - fileData = data; - - loadElfHeader(); - symTab = nullptr; - strTab = nullptr; +#ifdef ARMIPS_ARM + Arch = &Arm; - // load segments - for (size_t i = 0; i < fileHeader.e_phnum; i++) + switch (flags) { - int pos = fileHeader.e_phoff+i*fileHeader.e_phentsize; - - Elf32_Phdr sectionHeader; - loadProgramHeader(sectionHeader, fileData, pos); - - ByteArray segmentData = fileData.mid(sectionHeader.p_offset,sectionHeader.p_filesz); - ElfSegment* segment = new ElfSegment(sectionHeader,segmentData); - segments.push_back(segment); + case DIRECTIVE_ARM_GBA: + Arm.SetThumbMode(true); + Arm.setVersion(AARCH_GBA); + return ::make_unique<ArchitectureCommand>(L".gba\n.thumb", L".thumb"); + case DIRECTIVE_ARM_NDS: + Arm.SetThumbMode(false); + Arm.setVersion(AARCH_NDS); + return ::make_unique<ArchitectureCommand>(L".nds\n.arm", L".arm"); + case DIRECTIVE_ARM_3DS: + Arm.SetThumbMode(false); + Arm.setVersion(AARCH_3DS); + return ::make_unique<ArchitectureCommand>(L".3ds\n.arm", L".arm"); + case DIRECTIVE_ARM_BIG: + Arm.SetThumbMode(false); + Arm.setVersion(AARCH_BIG); + return ::make_unique<ArchitectureCommand>(L".arm.big\n.arm", L".arm"); + case DIRECTIVE_ARM_LITTLE: + Arm.SetThumbMode(false); + Arm.setVersion(AARCH_LITTLE); + return ::make_unique<ArchitectureCommand>(L".arm.little\n.arm", L".arm"); } +#endif - // load sections and assign them to segments - for (int i = 0; i < fileHeader.e_shnum; i++) - { - int pos = fileHeader.e_shoff+i*fileHeader.e_shentsize; + return nullptr; +} - Elf32_Shdr sectionHeader; - loadSectionHeader(sectionHeader, fileData, pos); +std::unique_ptr<CAssemblerCommand> parseDirectiveArea(Parser& parser, int flags) +{ + std::vector<Expression> parameters; + if (parser.parseExpressionList(parameters,1,2) == false) + return nullptr; - ElfSection* section = new ElfSection(sectionHeader); - sections.push_back(section); + auto area = ::make_unique<CDirectiveArea>(parameters[0]); + if (parameters.size() == 2) + area->setFillExpression(parameters[1]); - // check if the section belongs to a segment - ElfSegment* owner = nullptr; - for (int k = 0; k < (int)segments.size(); k++) - { - if (segments[k]->isSectionPartOf(section)) - { - owner = segments[k]; - break; - } - } + std::unique_ptr<CAssemblerCommand> content = parser.parseCommandSequence(L'.', {L".endarea"}); + parser.eatToken(); - if (owner != nullptr) - { - owner->addSection(section); - } else { - if (section->getType() != SHT_NOBITS && section->getType() != SHT_NULL) - { - ByteArray data = fileData.mid(section->getOffset(),section->getSize()); - section->setData(data); - } + area->setContent(std::move(content)); + return area; +} - switch (section->getType()) - { - case SHT_SYMTAB: - symTab = section; - break; - case SHT_STRTAB: - if (!strTab || i != fileHeader.e_shstrndx) - { - strTab = section; - } - break; - } +std::unique_ptr<CAssemblerCommand> parseDirectiveErrorWarning(Parser& parser, int flags) +{ + const Token &tok = parser.nextToken(); - segmentlessSections.push_back(section); - } - } + if (tok.type != TokenType::Identifier && tok.type != TokenType::String) + return nullptr; - determinePartOrder(); - loadSectionNames(); + std::wstring stringValue = tok.getStringValue(); + std::transform(stringValue.begin(),stringValue.end(),stringValue.begin(),::towlower); - if (sort) + if (stringValue == L"on") { - std::sort(segmentlessSections.begin(),segmentlessSections.end(),compareSection); - - for (int i = 0; i < (int)segments.size(); i++) - { - segments[i]->sortSections(); - } + Logger::setErrorOnWarning(true); + return ::make_unique<DummyCommand>(); + } else if (stringValue == L"off") + { + Logger::setErrorOnWarning(false); + return ::make_unique<DummyCommand>(); } - return true; + return nullptr; } -void ElfFile::save(const std::wstring&fileName) +std::unique_ptr<CAssemblerCommand> parseDirectiveRelativeInclude(Parser& parser, int flags) { - fileData.clear(); + const Token &tok = parser.nextToken(); - // reserve space for header and table data - fileData.reserveBytes(sizeof(Elf32_Ehdr)); + if (tok.type != TokenType::Identifier && tok.type != TokenType::String) + return nullptr; - for (size_t i = 0; i < 4; i++) - { - switch (partsOrder[i]) - { - case ELFPART_SEGMENTTABLE: - fileData.alignSize(4); - fileHeader.e_phoff = (Elf32_Off) fileData.size(); - fileData.reserveBytes(segments.size()*fileHeader.e_phentsize); - break; - case ELFPART_SECTIONTABLE: - fileData.alignSize(4); - fileHeader.e_shoff = (Elf32_Off) fileData.size(); - fileData.reserveBytes(sections.size()*fileHeader.e_shentsize); - break; - case ELFPART_SEGMENTS: - for (size_t i = 0; i < segments.size(); i++) - { - segments[i]->writeData(fileData); - } - break; - case ELFPART_SEGMENTLESSSECTIONS: - for (size_t i = 0; i < segmentlessSections.size(); i++) - { - segmentlessSections[i]->writeData(fileData); - } - break; - } - } + std::wstring stringValue = tok.getStringValue(); + std::transform(stringValue.begin(),stringValue.end(),stringValue.begin(),::towlower); - // copy data to the tables - Endianness endianness = getEndianness(); - writeHeader(fileData, 0, endianness); - for (size_t i = 0; i < segments.size(); i++) + if (stringValue == L"on") { - int pos = fileHeader.e_phoff+i*fileHeader.e_phentsize; - segments[i]->writeHeader(fileData, pos, endianness); - } - - for (size_t i = 0; i < sections.size(); i++) + Global.relativeInclude = true; + return ::make_unique<DummyCommand>(); + } else if (stringValue == L"off") { - int pos = fileHeader.e_shoff+i*fileHeader.e_shentsize; - sections[i]->writeHeader(fileData, pos, endianness); + Global.relativeInclude = false; + return ::make_unique<DummyCommand>(); } - fileData.toFile(fileName); + return nullptr; } -int ElfFile::getSymbolCount() +std::unique_ptr<CAssemblerCommand> parseDirectiveNocash(Parser& parser, int flags) { - if (symTab == nullptr) - return 0; + const Token &tok = parser.nextToken(); - return symTab->getSize()/sizeof(Elf32_Sym); + if (tok.type != TokenType::Identifier && tok.type != TokenType::String) + return nullptr; + + std::wstring stringValue = tok.getStringValue(); + std::transform(stringValue.begin(),stringValue.end(),stringValue.begin(),::towlower); + + if (stringValue == L"on") + { + Global.nocash = true; + return ::make_unique<DummyCommand>(); + } else if (stringValue == L"off") + { + Global.nocash = false; + return ::make_unique<DummyCommand>(); + } + + return nullptr; } -bool ElfFile::getSymbol(Elf32_Sym& symbol, size_t index) +std::unique_ptr<CAssemblerCommand> parseDirectiveSym(Parser& parser, int flags) { - if (symTab == nullptr) - return false; + const Token &tok = parser.nextToken(); - ByteArray &data = symTab->getData(); - int pos = index*sizeof(Elf32_Sym); - Endianness endianness = getEndianness(); - symbol.st_name = data.getDoubleWord(pos + 0x00, endianness); - symbol.st_value = data.getDoubleWord(pos + 0x04, endianness); - symbol.st_size = data.getDoubleWord(pos + 0x08, endianness); - symbol.st_info = data[pos + 0x0C]; - symbol.st_other = data[pos + 0x0D]; - symbol.st_shndx = data.getWord(pos + 0x0E, endianness); + if (tok.type != TokenType::Identifier && tok.type != TokenType::String) + return nullptr; - return true; + std::wstring stringValue = tok.getStringValue(); + std::transform(stringValue.begin(),stringValue.end(),stringValue.begin(),::towlower); + + if (stringValue == L"on") + return ::make_unique<CDirectiveSym>(true); + else if (stringValue == L"off") + return ::make_unique<CDirectiveSym>(false); + else + return nullptr; } -const char* ElfFile::getStrTableString(size_t pos) +std::unique_ptr<CAssemblerCommand> parseDirectiveDefineLabel(Parser& parser, int flags) { - if (strTab == nullptr) + const Token& tok = parser.nextToken(); + if (tok.type != TokenType::Identifier) return nullptr; - return (const char*) &strTab->getData()[pos]; -} + if (parser.nextToken().type != TokenType::Comma) + return nullptr; -// file: Core/ELF/ElfRelocator.cpp + Expression value = parser.parseExpression(); + if (value.isLoaded() == false) + return nullptr; -struct ArFileHeader -{ - char fileName[16]; - char modifactionTime[12]; - char ownerId[6]; - char groupId[6]; - char fileMode[8]; - char fileSize[10]; - char magic[2]; -}; + const std::wstring stringValue = tok.getStringValue(); + if (Global.symbolTable.isValidSymbolName(stringValue) == false) + { + parser.printError(tok,L"Invalid label name \"%s\"",stringValue); + return nullptr; + } -struct ArFileEntry -{ - std::wstring name; - ByteArray data; -}; + return ::make_unique<CAssemblerLabel>(stringValue,tok.getOriginalText(),value); +} -std::vector<ArFileEntry> loadArArchive(const std::wstring& inputName) +std::unique_ptr<CAssemblerCommand> parseDirectiveFunction(Parser& parser, int flags) { - ByteArray input = ByteArray::fromFile(inputName); - std::vector<ArFileEntry> result; + const Token& tok = parser.nextToken(); + if (tok.type != TokenType::Identifier) + return nullptr; - if (input.size() < 8 || memcmp(input.data(),"!<arch>\n",8) != 0) + if (parser.nextToken().type != TokenType::Separator) { - if (input.size() < 4 || memcmp(input.data(),"\x7F""ELF",4) != 0) - return result; - - ArFileEntry entry; - entry.name = getFileNameFromPath(inputName); - entry.data = input; - result.push_back(entry); - return result; + parser.printError(tok,L"Directive not terminated"); + return nullptr; } - size_t pos = 8; - while (pos < input.size()) - { - ArFileHeader* header = (ArFileHeader*) input.data(pos); - pos += sizeof(ArFileHeader); - - // get file size - int size = 0; - for (int i = 0; i < 10; i++) - { - if (header->fileSize[i] == ' ') - break; - - size = size*10; - size += (header->fileSize[i]-'0'); - } + auto func = ::make_unique<CDirectiveFunction>(tok.getStringValue(),tok.getOriginalText()); + std::unique_ptr<CAssemblerCommand> seq = parser.parseCommandSequence(L'.', {L".endfunc",L".endfunction",L".func",L".function"}); - // only ELF files are actually interesting - if (memcmp(input.data(pos),"\x7F""ELF",4) == 0) + const std::wstring stringValue = parser.peekToken().getStringValue(); + if (stringValue == L".endfunc" || + stringValue == L".endfunction") + { + parser.eatToken(); + if(parser.nextToken().type != TokenType::Separator) { - // get file name - char fileName[17]; - fileName[16] = 0; - for (int i = 0; i < 16; i++) - { - if (header->fileName[i] == ' ') - { - // remove trailing slashes of file names - if (i > 0 && fileName[i-1] == '/') - i--; - fileName[i] = 0; - break;; - } - - fileName[i] = header->fileName[i]; - } - - ArFileEntry entry; - entry.name = convertUtf8ToWString(fileName); - entry.data = input.mid(pos,size); - result.push_back(entry); + parser.printError(tok,L"Directive not terminated"); + return nullptr; } - - pos += size; - if (pos % 2) - pos++; } - return result; + func->setContent(std::move(seq)); + return func; } -bool ElfRelocator::init(const std::wstring& inputName) +std::unique_ptr<CAssemblerCommand> parseDirectiveMessage(Parser& parser, int flags) { - relocator = Arch->getElfRelocator(); - if (relocator == nullptr) - { - Logger::printError(Logger::Error,L"Object importing not supported for this architecture"); - return false; - } + Expression exp = parser.parseExpression(); - auto inputFiles = loadArArchive(inputName); - if (inputFiles.size() == 0) + switch (flags) { - Logger::printError(Logger::Error,L"Could not load library"); - return false; + case DIRECTIVE_MSG_WARNING: + return ::make_unique<CDirectiveMessage>(CDirectiveMessage::Type::Warning,exp); + case DIRECTIVE_MSG_ERROR: + return ::make_unique<CDirectiveMessage>(CDirectiveMessage::Type::Error,exp); + case DIRECTIVE_MSG_NOTICE: + return ::make_unique<CDirectiveMessage>(CDirectiveMessage::Type::Notice,exp); } - for (ArFileEntry& entry: inputFiles) - { - ElfRelocatorFile file; - - ElfFile* elf = new ElfFile(); - if (elf->load(entry.data,false) == false) - { - Logger::printError(Logger::Error,L"Could not load object file %s",entry.name); - return false; - } - - if (elf->getType() != ET_REL) - { - Logger::printError(Logger::Error,L"Unexpected ELF type %d in object file %s",elf->getType(),entry.name); - return false; - } + return nullptr; +} - if (elf->getMachine() != relocator->expectedMachine()) - { - Logger::printError(Logger::Error,L"Unexpected ELF machine %d in object file %s",elf->getMachine(),entry.name); - return false; - } +std::unique_ptr<CAssemblerCommand> parseDirectiveInclude(Parser& parser, int flags) +{ + const Token& start = parser.peekToken(); - if (elf->getEndianness() != Arch->getEndianness()) - { - Logger::printError(Logger::Error,L"Incorrect endianness in object file %s",entry.name); - return false; - } + std::vector<Expression> parameters; + if (parser.parseExpressionList(parameters,1,2) == false) + return nullptr; - if (elf->getSegmentCount() != 0) - { - Logger::printError(Logger::Error,L"Unexpected segment count %d in object file %s",elf->getSegmentCount(),entry.name); - return false; - } + std::wstring fileName; + if (parameters[0].evaluateString(fileName,true) == false) + return nullptr; + fileName = getFullPathName(fileName); - // load all relevant sections of this file - for (size_t s = 0; s < elf->getSegmentlessSectionCount(); s++) - { - ElfSection* sec = elf->getSegmentlessSection(s); - if (!(sec->getFlags() & SHF_ALLOC)) - continue; + TextFile::Encoding encoding = TextFile::GUESS; + if (parameters.size() == 2) + { + std::wstring encodingName; + if (parameters[1].evaluateString(encodingName,true) == false + && parameters[1].evaluateIdentifier(encodingName) == false) + return nullptr; - if (sec->getType() == SHT_PROGBITS || sec->getType() == SHT_NOBITS || sec->getType() == SHT_INIT_ARRAY) - { - ElfRelocatorSection sectionEntry; - sectionEntry.section = sec; - sectionEntry.index = s; - sectionEntry.relSection = nullptr; - sectionEntry.label = nullptr; + encoding = getEncodingFromString(encodingName); + } - // search relocation section - for (size_t k = 0; k < elf->getSegmentlessSectionCount(); k++) - { - ElfSection* relSection = elf->getSegmentlessSection(k); - if (relSection->getType() != SHT_REL) - continue; - if (relSection->getInfo() != s) - continue; + // don't include the file if it's inside a false block + if (parser.isInsideTrueBlock() == false) + return ::make_unique<DummyCommand>(); - // got it - sectionEntry.relSection = relSection; - break; - } + if (fileExists(fileName) == false) + { + parser.printError(start,L"Included file \"%s\" does not exist",fileName); + return nullptr; + } - // keep track of constructor sections - if (sec->getName() == ".ctors" || sec->getName() == ".init_array") - { - ElfRelocatorCtor ctor; - ctor.symbolName = Global.symbolTable.getUniqueLabelName(); - ctor.size = sec->getSize(); + TextFile f; + if (f.open(fileName,TextFile::Read,encoding) == false) + { + parser.printError(start,L"Could not open included file \"%s\"",fileName); + return nullptr; + } - sectionEntry.label = Global.symbolTable.getLabel(ctor.symbolName,-1,-1); - sectionEntry.label->setDefined(true); + return parser.parseFile(f); +} - ctors.push_back(ctor); - } +const DirectiveMap directives = { + { L".open", { &parseDirectiveOpen, DIRECTIVE_NOTINMEMORY } }, + { L".openfile", { &parseDirectiveOpen, DIRECTIVE_NOTINMEMORY } }, + { L".create", { &parseDirectiveCreate, DIRECTIVE_NOTINMEMORY } }, + { L".createfile", { &parseDirectiveCreate, DIRECTIVE_NOTINMEMORY } }, + { L".close", { &parseDirectiveClose, DIRECTIVE_NOTINMEMORY } }, + { L".closefile", { &parseDirectiveClose, DIRECTIVE_NOTINMEMORY } }, + { L".incbin", { &parseDirectiveIncbin, 0 } }, + { L".import", { &parseDirectiveIncbin, 0 } }, + { L".org", { &parseDirectivePosition, DIRECTIVE_POS_VIRTUAL } }, + { L"org", { &parseDirectivePosition, DIRECTIVE_POS_VIRTUAL } }, + { L".orga", { &parseDirectivePosition, DIRECTIVE_POS_PHYSICAL } }, + { L"orga", { &parseDirectivePosition, DIRECTIVE_POS_PHYSICAL } }, + { L".headersize", { &parseDirectiveHeaderSize, 0 } }, + { L".align", { &parseDirectiveAlignFill, DIRECTIVE_ALIGN_VIRTUAL } }, + { L".aligna", { &parseDirectiveAlignFill, DIRECTIVE_ALIGN_PHYSICAL } }, + { L".fill", { &parseDirectiveAlignFill, DIRECTIVE_ALIGN_FILL } }, + { L"defs", { &parseDirectiveAlignFill, DIRECTIVE_ALIGN_FILL } }, + { L".skip", { &parseDirectiveSkip, 0 } }, - file.sections.push_back(sectionEntry); - } - } + { L".if", { &parseDirectiveConditional, DIRECTIVE_COND_IF } }, + { L".ifdef", { &parseDirectiveConditional, DIRECTIVE_COND_IFDEF } }, + { L".ifndef", { &parseDirectiveConditional, DIRECTIVE_COND_IFNDEF } }, - // init exportable symbols - for (int i = 0; i < elf->getSymbolCount(); i++) - { - Elf32_Sym symbol; - elf->getSymbol(symbol, i); + { L".loadtable", { &parseDirectiveTable, 0 } }, + { L".table", { &parseDirectiveTable, 0 } }, + { L".byte", { &parseDirectiveData, DIRECTIVE_DATA_8 } }, + { L".halfword", { &parseDirectiveData, DIRECTIVE_DATA_16 } }, + { L".word", { &parseDirectiveData, DIRECTIVE_DATA_32 } }, + { L".doubleword", { &parseDirectiveData, DIRECTIVE_DATA_64 } }, + { L".db", { &parseDirectiveData, DIRECTIVE_DATA_8 } }, + { L".dh", { &parseDirectiveData, DIRECTIVE_DATA_16|DIRECTIVE_NOCASHOFF } }, + { L".dw", { &parseDirectiveData, DIRECTIVE_DATA_32|DIRECTIVE_NOCASHOFF } }, + { L".dd", { &parseDirectiveData, DIRECTIVE_DATA_64|DIRECTIVE_NOCASHOFF } }, + { L".dw", { &parseDirectiveData, DIRECTIVE_DATA_16|DIRECTIVE_NOCASHON } }, + { L".dd", { &parseDirectiveData, DIRECTIVE_DATA_32|DIRECTIVE_NOCASHON } }, + { L".dcb", { &parseDirectiveData, DIRECTIVE_DATA_8 } }, + { L".dcw", { &parseDirectiveData, DIRECTIVE_DATA_16 } }, + { L".dcd", { &parseDirectiveData, DIRECTIVE_DATA_32 } }, + { L".dcq", { &parseDirectiveData, DIRECTIVE_DATA_64 } }, + { L"db", { &parseDirectiveData, DIRECTIVE_DATA_8 } }, + { L"dh", { &parseDirectiveData, DIRECTIVE_DATA_16|DIRECTIVE_NOCASHOFF } }, + { L"dw", { &parseDirectiveData, DIRECTIVE_DATA_32|DIRECTIVE_NOCASHOFF } }, + { L"dd", { &parseDirectiveData, DIRECTIVE_DATA_64|DIRECTIVE_NOCASHOFF } }, + { L"dw", { &parseDirectiveData, DIRECTIVE_DATA_16|DIRECTIVE_NOCASHON } }, + { L"dd", { &parseDirectiveData, DIRECTIVE_DATA_32|DIRECTIVE_NOCASHON } }, + { L"dcb", { &parseDirectiveData, DIRECTIVE_DATA_8 } }, + { L"dcw", { &parseDirectiveData, DIRECTIVE_DATA_16 } }, + { L"dcd", { &parseDirectiveData, DIRECTIVE_DATA_32 } }, + { L"dcq", { &parseDirectiveData, DIRECTIVE_DATA_64 } }, + { L".float", { &parseDirectiveData, DIRECTIVE_DATA_FLOAT } }, + { L".double", { &parseDirectiveData, DIRECTIVE_DATA_DOUBLE } }, + { L".ascii", { &parseDirectiveData, DIRECTIVE_DATA_ASCII } }, + { L".asciiz", { &parseDirectiveData, DIRECTIVE_DATA_ASCII|DIRECTIVE_DATA_TERMINATION } }, + { L".string", { &parseDirectiveData, DIRECTIVE_DATA_CUSTOM|DIRECTIVE_DATA_TERMINATION } }, + { L".str", { &parseDirectiveData, DIRECTIVE_DATA_CUSTOM|DIRECTIVE_DATA_TERMINATION } }, + { L".stringn", { &parseDirectiveData, DIRECTIVE_DATA_CUSTOM } }, + { L".strn", { &parseDirectiveData, DIRECTIVE_DATA_CUSTOM } }, + { L".sjis", { &parseDirectiveData, DIRECTIVE_DATA_SJIS|DIRECTIVE_DATA_TERMINATION } }, + { L".sjisn", { &parseDirectiveData, DIRECTIVE_DATA_SJIS } }, - if (ELF32_ST_BIND(symbol.st_info) == STB_GLOBAL && symbol.st_shndx != 0) - { - ElfRelocatorSymbol symEntry; - symEntry.type = ELF32_ST_TYPE(symbol.st_info); - symEntry.name = convertUtf8ToWString(elf->getStrTableString(symbol.st_name)); - symEntry.relativeAddress = symbol.st_value; - symEntry.section = symbol.st_shndx; - symEntry.size = symbol.st_size; - symEntry.label = nullptr; + { L".psx", { &parseDirectiveMipsArch, DIRECTIVE_MIPS_PSX } }, + { L".ps2", { &parseDirectiveMipsArch, DIRECTIVE_MIPS_PS2 } }, + { L".psp", { &parseDirectiveMipsArch, DIRECTIVE_MIPS_PSP } }, + { L".n64", { &parseDirectiveMipsArch, DIRECTIVE_MIPS_N64 } }, + { L".rsp", { &parseDirectiveMipsArch, DIRECTIVE_MIPS_RSP } }, - file.symbols.push_back(symEntry); - } - } + { L".gba", { &parseDirectiveArmArch, DIRECTIVE_ARM_GBA } }, + { L".nds", { &parseDirectiveArmArch, DIRECTIVE_ARM_NDS } }, + { L".3ds", { &parseDirectiveArmArch, DIRECTIVE_ARM_3DS } }, + { L".arm.big", { &parseDirectiveArmArch, DIRECTIVE_ARM_BIG } }, + { L".arm.little", { &parseDirectiveArmArch, DIRECTIVE_ARM_LITTLE } }, - file.elf = elf; - file.name = entry.name; - files.push_back(file); - } + { L".area", { &parseDirectiveArea, 0 } }, - return true; + { L".importobj", { &parseDirectiveObjImport, 0 } }, + { L".importlib", { &parseDirectiveObjImport, 0 } }, + + { L".erroronwarning", { &parseDirectiveErrorWarning, 0 } }, + { L".relativeinclude", { &parseDirectiveRelativeInclude, 0 } }, + { L".nocash", { &parseDirectiveNocash, 0 } }, + { L".sym", { &parseDirectiveSym, 0 } }, + + { L".definelabel", { &parseDirectiveDefineLabel, 0 } }, + { L".function", { &parseDirectiveFunction, DIRECTIVE_MANUALSEPARATOR } }, + { L".func", { &parseDirectiveFunction, DIRECTIVE_MANUALSEPARATOR } }, + + { L".warning", { &parseDirectiveMessage, DIRECTIVE_MSG_WARNING } }, + { L".error", { &parseDirectiveMessage, DIRECTIVE_MSG_ERROR } }, + { L".notice", { &parseDirectiveMessage, DIRECTIVE_MSG_NOTICE } }, + + { L".include", { &parseDirectiveInclude, 0 } }, +}; + +// file: Parser/ExpressionParser.cpp + +static ExpressionInternal* expression(Tokenizer& tokenizer); + +static bool allowFunctionCall = true; + +void allowFunctionCallExpression(bool allow) +{ + allowFunctionCall = allow; } -bool ElfRelocator::exportSymbols() +static ExpressionInternal* primaryExpression(Tokenizer& tokenizer) { - bool error = false; + const Token &tok = tokenizer.peekToken(); - for (ElfRelocatorFile& file: files) + switch (tok.type) { - for (ElfRelocatorSymbol& sym: file.symbols) + case TokenType::Float: + tokenizer.eatToken(); + return new ExpressionInternal(tok.floatValue); + case TokenType::Identifier: { - if (sym.label != nullptr) - continue; + const std::wstring stringValue = tok.getStringValue(); + tokenizer.eatToken(); + if (stringValue == L".") + return new ExpressionInternal(OperatorType::MemoryPos); + else + return new ExpressionInternal(stringValue,OperatorType::Identifier); + } + case TokenType::String: + tokenizer.eatToken(); + return new ExpressionInternal(tok.getStringValue(),OperatorType::String); + case TokenType::Integer: + tokenizer.eatToken(); + return new ExpressionInternal(tok.intValue); + case TokenType::LParen: + { + tokenizer.eatToken(); + ExpressionInternal* exp = expression(tokenizer); - std::wstring lowered = sym.name; - std::transform(lowered.begin(), lowered.end(), lowered.begin(), ::towlower); + if (tokenizer.nextToken().type != TokenType::RParen) + { + delete exp; + return nullptr; + } - sym.label = Global.symbolTable.getLabel(lowered,-1,-1); - if (sym.label == nullptr) + return exp; + } + case TokenType::Invalid: + default: + break; + } + + return nullptr; +} + +static ExpressionInternal* postfixExpression(Tokenizer& tokenizer) +{ + if (allowFunctionCall && + tokenizer.peekToken(0).type == TokenType::Identifier && + tokenizer.peekToken(1).type == TokenType::LParen) + { + const std::wstring functionName = tokenizer.nextToken().getStringValue(); + tokenizer.eatToken(); + + std::vector<ExpressionInternal*> parameters; + while (tokenizer.peekToken().type != TokenType::RParen) + { + if (parameters.size() != 0 && tokenizer.nextToken().type != TokenType::Comma) { - Logger::printError(Logger::Error,L"Invalid label name \"%s\"",sym.name); - error = true; - continue; + for (ExpressionInternal* exp: parameters) + delete exp; + return nullptr; } - if (sym.label->isDefined()) + ExpressionInternal* exp = expression(tokenizer); + if (exp == nullptr) { - Logger::printError(Logger::Error,L"Label \"%s\" already defined",sym.name); - error = true; - continue; + for (ExpressionInternal* exp: parameters) + delete exp; + return nullptr; } - RelocationData data; - data.symbolAddress = sym.relativeAddress; - relocator->setSymbolAddress(data,sym.relativeAddress,sym.type); + parameters.push_back(exp); + } - sym.relativeAddress = data.symbolAddress; - sym.label->setInfo(data.targetSymbolInfo); - sym.label->setIsData(sym.type == STT_OBJECT); - sym.label->setUpdateInfo(false); + tokenizer.eatToken(); - sym.label->setValue(0); - sym.label->setDefined(true); - sym.label->setOriginalName(sym.name); - } + return new ExpressionInternal(functionName,parameters); } - return !error; + return primaryExpression(tokenizer); } -std::unique_ptr<CAssemblerCommand> ElfRelocator::generateCtor(const std::wstring& ctorName) +static ExpressionInternal* unaryExpression(Tokenizer& tokenizer) { - std::unique_ptr<CAssemblerCommand> content = relocator->generateCtorStub(ctors); + ExpressionInternal* exp = postfixExpression(tokenizer); + if (exp != nullptr) + return exp; - auto func = ::make_unique<CDirectiveFunction>(ctorName,ctorName); - func->setContent(std::move(content)); - return func; -} + const TokenType opType = tokenizer.nextToken().type; + exp = postfixExpression(tokenizer); + if (exp == nullptr) + return nullptr; -void ElfRelocator::loadRelocation(Elf32_Rel& rel, ByteArray& data, int offset, Endianness endianness) -{ - rel.r_offset = data.getDoubleWord(offset + 0x00, endianness); - rel.r_info = data.getDoubleWord(offset + 0x04, endianness); + switch (opType) + { + case TokenType::Plus: + return exp; + case TokenType::Minus: + return new ExpressionInternal(OperatorType::Neg,exp); + case TokenType::Tilde: + return new ExpressionInternal(OperatorType::BitNot,exp); + case TokenType::Exclamation: + return new ExpressionInternal(OperatorType::LogNot,exp); + case TokenType::Degree: + return new ExpressionInternal(OperatorType::ToString,exp); + default: + delete exp; + return nullptr; + } } -bool ElfRelocator::relocateFile(ElfRelocatorFile& file, int64_t& relocationAddress) +static ExpressionInternal* multiplicativeExpression(Tokenizer& tokenizer) { - ElfFile* elf = file.elf; - int64_t start = relocationAddress; + ExpressionInternal* exp = unaryExpression(tokenizer); + if (exp == nullptr) + return nullptr; - // calculate address for each section - std::map<int64_t,int64_t> relocationOffsets; - for (ElfRelocatorSection& entry: file.sections) + while (true) { - ElfSection* section = entry.section; - size_t index = entry.index; - int size = section->getSize(); + OperatorType op = OperatorType::Invalid; + switch (tokenizer.peekToken().type) + { + case TokenType::Mult: + op = OperatorType::Mult; + break; + case TokenType::Div: + op = OperatorType::Div; + break; + case TokenType::Mod: + op = OperatorType::Mod; + break; + default: + break; + } - while (relocationAddress % section->getAlignment()) - relocationAddress++; + if (op == OperatorType::Invalid) + break; - if (entry.label != nullptr) - entry.label->setValue(relocationAddress); + tokenizer.eatToken(); - relocationOffsets[index] = relocationAddress; - relocationAddress += size; + ExpressionInternal* exp2 = unaryExpression(tokenizer); + if (exp2 == nullptr) + { + delete exp; + return nullptr; + } + + exp = new ExpressionInternal(op,exp,exp2); } - size_t dataStart = outputData.size(); - outputData.reserveBytes((size_t)(relocationAddress-start)); + return exp; +} - // load sections - bool error = false; - for (ElfRelocatorSection& entry: file.sections) - { - ElfSection* section = entry.section; - size_t index = entry.index; +static ExpressionInternal* additiveExpression(Tokenizer& tokenizer) +{ + ExpressionInternal* exp = multiplicativeExpression(tokenizer); + if (exp == nullptr) + return nullptr; - if (section->getType() == SHT_NOBITS) + while (true) + { + OperatorType op = OperatorType::Invalid; + switch (tokenizer.peekToken().type) { - // reserveBytes initialized the data to 0 already - continue; + case TokenType::Plus: + op = OperatorType::Add; + break; + case TokenType::Minus: + op = OperatorType::Sub; + break; + default: + break; } - ByteArray sectionData = section->getData(); - - // relocate if necessary - ElfSection* relSection = entry.relSection; - if (relSection != nullptr) - { - std::vector<RelocationAction> relocationActions; - for (unsigned int relOffset = 0; relOffset < relSection->getSize(); relOffset += sizeof(Elf32_Rel)) - { - Elf32_Rel rel; - loadRelocation(rel, relSection->getData(), relOffset, elf->getEndianness()); - int pos = rel.r_offset; - - if (relocator->isDummyRelocationType(rel.getType())) - continue; - - int symNum = rel.getSymbolNum(); - if (symNum <= 0) - { - Logger::queueError(Logger::Warning,L"Invalid symbol num %06X",symNum); - error = true; - continue; - } + if (op == OperatorType::Invalid) + break; - Elf32_Sym sym; - elf->getSymbol(sym, symNum); - int symSection = sym.st_shndx; + tokenizer.eatToken(); - RelocationData relData; - relData.opcode = sectionData.getDoubleWord(pos, elf->getEndianness()); - relData.opcodeOffset = pos+relocationOffsets[index]; - relocator->setSymbolAddress(relData,sym.st_value,sym.st_info & 0xF); + ExpressionInternal* exp2 = multiplicativeExpression(tokenizer); + if (exp2 == nullptr) + { + delete exp; + return nullptr; + } - // externs? - if (sym.st_shndx == 0) - { - if (sym.st_name == 0) - { - Logger::queueError(Logger::Error, L"Symbol without a name"); - error = true; - continue; - } + exp = new ExpressionInternal(op,exp,exp2); + } - std::wstring symName = toWLowercase(elf->getStrTableString(sym.st_name)); + return exp; +} - std::shared_ptr<Label> label = Global.symbolTable.getLabel(symName,-1,-1); - if (label == nullptr) - { - Logger::queueError(Logger::Error,L"Invalid external symbol %s",symName); - error = true; - continue; - } - if (label->isDefined() == false) - { - Logger::queueError(Logger::Error,L"Undefined external symbol %s in file %s",symName,file.name); - error = true; - continue; - } +static ExpressionInternal* shiftExpression(Tokenizer& tokenizer) +{ + ExpressionInternal* exp = additiveExpression(tokenizer); + if (exp == nullptr) + return nullptr; - relData.relocationBase = (unsigned int) label->getValue(); - relData.targetSymbolType = label->isData() ? STT_OBJECT : STT_FUNC; - relData.targetSymbolInfo = label->getInfo(); - } else { - relData.relocationBase = relocationOffsets[symSection]+relData.symbolAddress; - } + while (true) + { + OperatorType op = OperatorType::Invalid; + switch (tokenizer.peekToken().type) + { + case TokenType::LeftShift: + op = OperatorType::LeftShift; + break; + case TokenType::RightShift: + op = OperatorType::RightShift; + break; + default: + break; + } - std::vector<std::wstring> errors; - if (!relocator->relocateOpcode(rel.getType(),relData, relocationActions, errors)) - { - for (const std::wstring& error : errors) - { - Logger::queueError(Logger::Error, error); - } - error = true; - continue; - } - } + if (op == OperatorType::Invalid) + break; - // finish any dangling relocations - std::vector<std::wstring> errors; - if (!relocator->finish(relocationActions, errors)) - { - for (const std::wstring& error : errors) - { - Logger::queueError(Logger::Error, error); - } - error = true; - } + tokenizer.eatToken(); - // now actually write the relocated values - for (const RelocationAction& action : relocationActions) - { - sectionData.replaceDoubleWord(action.offset-relocationOffsets[index], action.newValue, elf->getEndianness()); - } + ExpressionInternal* exp2 = additiveExpression(tokenizer); + if (exp2 == nullptr) + { + delete exp; + return nullptr; } - size_t arrayStart = (size_t) (dataStart+relocationOffsets[index]-start); - memcpy(outputData.data(arrayStart),sectionData.data(),sectionData.size()); + exp = new ExpressionInternal(op,exp,exp2); } - // now update symbols - for (ElfRelocatorSymbol& sym: file.symbols) - { - int64_t oldAddress = sym.relocatedAddress; + return exp; +} - switch (sym.section) +static ExpressionInternal* relationalExpression(Tokenizer& tokenizer) +{ + ExpressionInternal* exp = shiftExpression(tokenizer); + if (exp == nullptr) + return nullptr; + + while (true) + { + OperatorType op = OperatorType::Invalid; + switch (tokenizer.peekToken().type) { - case SHN_ABS: // address does not change - sym.relocatedAddress = sym.relativeAddress; + case TokenType::Less: + op = OperatorType::Less; break; - case SHN_COMMON: // needs to be allocated. relativeAddress gives alignment constraint - { - int64_t start = relocationAddress; - - while (relocationAddress % sym.relativeAddress) - relocationAddress++; - - sym.relocatedAddress = relocationAddress; - relocationAddress += sym.size; - outputData.reserveBytes((size_t)(relocationAddress-start)); - } + case TokenType::LessEqual: + op = OperatorType::LessEqual; break; - default: // normal relocated symbol - sym.relocatedAddress = sym.relativeAddress+relocationOffsets[sym.section]; + case TokenType::Greater: + op = OperatorType::Greater; + break; + case TokenType::GreaterEqual: + op = OperatorType::GreaterEqual; + break; + default: break; } - if (sym.label != nullptr) - sym.label->setValue(sym.relocatedAddress); - - if (oldAddress != sym.relocatedAddress) - dataChanged = true; - } + if (op == OperatorType::Invalid) + break; - return !error; -} + tokenizer.eatToken(); -bool ElfRelocator::relocate(int64_t& memoryAddress) -{ - int oldCrc = getCrc32(outputData.data(),outputData.size()); - outputData.clear(); - dataChanged = false; - - bool error = false; - int64_t start = memoryAddress; + ExpressionInternal* exp2 = shiftExpression(tokenizer); + if (exp2 == nullptr) + { + delete exp; + return nullptr; + } - for (ElfRelocatorFile& file: files) - { - if (relocateFile(file,memoryAddress) == false) - error = true; + exp = new ExpressionInternal(op,exp,exp2); } - int newCrc = getCrc32(outputData.data(),outputData.size()); - if (oldCrc != newCrc) - dataChanged = true; - - memoryAddress -= start; - return !error; + return exp; } -void ElfRelocator::writeSymbols(SymbolData& symData) const +static ExpressionInternal* equalityExpression(Tokenizer& tokenizer) { - for (const ElfRelocatorFile& file: files) + ExpressionInternal* exp = relationalExpression(tokenizer); + if (exp == nullptr) + return nullptr; + + while (true) { - for (const ElfRelocatorSymbol& sym: file.symbols) + OperatorType op = OperatorType::Invalid; + switch (tokenizer.peekToken().type) { - symData.addLabel(sym.relocatedAddress,sym.name); + case TokenType::Equal: + op = OperatorType::Equal; + break; + case TokenType::NotEqual: + op = OperatorType::NotEqual; + break; + default: + break; + } - switch (sym.type) - { - case STT_OBJECT: - symData.addData(sym.relocatedAddress,sym.size,SymbolData::Data8); - break; - case STT_FUNC: - symData.startFunction(sym.relocatedAddress); - symData.endFunction(sym.relocatedAddress+sym.size); - break; - } + if (op == OperatorType::Invalid) + break; + + tokenizer.eatToken(); + + ExpressionInternal* exp2 = relationalExpression(tokenizer); + if (exp2 == nullptr) + { + delete exp; + return nullptr; } - } -} -// file: Core/Assembler.cpp -#include <thread> + exp = new ExpressionInternal(op,exp,exp2); + } -void AddFileName(const std::wstring& FileName) -{ - Global.FileInfo.FileNum = (int) Global.FileInfo.FileList.size(); - Global.FileInfo.FileList.push_back(FileName); - Global.FileInfo.LineNumber = 0; + return exp; } -bool encodeAssembly(std::unique_ptr<CAssemblerCommand> content, SymbolData& symData, TempData& tempData) +static ExpressionInternal* andExpression(Tokenizer& tokenizer) { - bool Revalidate; - -#ifdef ARMIPS_ARM - Arm.Pass2(); -#endif - Mips.Pass2(); + ExpressionInternal* exp = equalityExpression(tokenizer); + if (exp == nullptr) + return nullptr; - int validationPasses = 0; - do // loop until everything is constant + while (tokenizer.peekToken().type == TokenType::BitAnd) { - Global.validationPasses = validationPasses; - Logger::clearQueue(); - Revalidate = false; + tokenizer.eatToken(); - if (validationPasses >= 100) + ExpressionInternal* exp2 = equalityExpression(tokenizer); + if (exp2 == nullptr) { - Logger::queueError(Logger::Error,L"Stuck in infinite validation loop"); - break; + delete exp; + return nullptr; } - g_fileManager->reset(); + exp = new ExpressionInternal(OperatorType::BitAnd,exp,exp2); + } -#ifdef _DEBUG - if (!Logger::isSilent()) - printf("Validate %d...\n",validationPasses); -#endif + return exp; +} - if (Global.memoryMode) - g_fileManager->openFile(Global.memoryFile,true); +static ExpressionInternal* exclusiveOrExpression(Tokenizer& tokenizer) +{ + ExpressionInternal* exp = andExpression(tokenizer); + if (exp == nullptr) + return nullptr; - Revalidate = content->Validate(); + while (tokenizer.peekToken().type == TokenType::Caret) + { + tokenizer.eatToken(); -#ifdef ARMIPS_ARM - Arm.Revalidate(); -#endif - Mips.Revalidate(); + ExpressionInternal* exp2 = andExpression(tokenizer); + if (exp2 == nullptr) + { + delete exp; + return nullptr; + } - if (Global.memoryMode) - g_fileManager->closeFile(); + exp = new ExpressionInternal(OperatorType::Xor,exp,exp2); + } - validationPasses++; - } while (Revalidate == true); + return exp; +} - Logger::printQueue(); - if (Logger::hasError() == true) +static ExpressionInternal* inclusiveOrExpression(Tokenizer& tokenizer) +{ + ExpressionInternal* exp = exclusiveOrExpression(tokenizer); + if (exp == nullptr) + return nullptr; + + while (tokenizer.peekToken().type == TokenType::BitOr) { - return false; - } + tokenizer.eatToken(); -#ifdef _DEBUG - if (!Logger::isSilent()) - printf("Encode...\n"); -#endif + ExpressionInternal* exp2 = exclusiveOrExpression(tokenizer); + if (exp2 == nullptr) + { + delete exp; + return nullptr; + } - // and finally encode - if (Global.memoryMode) - g_fileManager->openFile(Global.memoryFile,false); + exp = new ExpressionInternal(OperatorType::BitOr,exp,exp2); + } - auto writeTempData = [&]() - { - tempData.start(); - if (tempData.isOpen()) - content->writeTempData(tempData); - tempData.end(); - }; + return exp; +} - auto writeSymData = [&]() - { - content->writeSymData(symData); - symData.write(); - }; +static ExpressionInternal* logicalAndExpression(Tokenizer& tokenizer) +{ + ExpressionInternal* exp = inclusiveOrExpression(tokenizer); + if (exp == nullptr) + return nullptr; - // writeTempData, writeSymData and encode all access the same - // memory but never change, so they can run in parallel - if (Global.multiThreading) + while (tokenizer.peekToken().type == TokenType::LogAnd) { - std::thread tempThread(writeTempData); - std::thread symThread(writeSymData); + tokenizer.eatToken(); - content->Encode(); + ExpressionInternal* exp2 = inclusiveOrExpression(tokenizer); + if (exp2 == nullptr) + { + delete exp; + return nullptr; + } - tempThread.join(); - symThread.join(); - } else { - writeTempData(); - writeSymData(); - content->Encode(); + exp = new ExpressionInternal(OperatorType::LogAnd,exp,exp2); } - if (g_fileManager->hasOpenFile()) + return exp; +} + +static ExpressionInternal* logicalOrExpression(Tokenizer& tokenizer) +{ + ExpressionInternal* exp = logicalAndExpression(tokenizer); + if (exp == nullptr) + return nullptr; + + while (tokenizer.peekToken().type == TokenType::LogOr) { - if (!Global.memoryMode) - Logger::printError(Logger::Warning,L"File not closed"); - g_fileManager->closeFile(); + tokenizer.eatToken(); + + ExpressionInternal* exp2 = logicalAndExpression(tokenizer); + if (exp2 == nullptr) + { + delete exp; + return nullptr; + } + + exp = new ExpressionInternal(OperatorType::LogOr,exp,exp2); } - return true; + return exp; } -bool runArmips(ArmipsArguments& settings) +static ExpressionInternal* conditionalExpression(Tokenizer& tokenizer) { - // initialize and reset global data - Global.Section = 0; - Global.nocash = false; - Global.FileInfo.FileCount = 0; - Global.FileInfo.TotalLineCount = 0; - Global.relativeInclude = false; - Global.validationPasses = 0; - Global.multiThreading = true; - Arch = &InvalidArchitecture; + ExpressionInternal* exp = logicalOrExpression(tokenizer); + if (exp == nullptr) + return nullptr; - Tokenizer::clearEquValues(); - Logger::clear(); - Global.Table.clear(); - Global.symbolTable.clear(); + // check a ? b : c + if (tokenizer.peekToken().type != TokenType::Question) + return exp; - Global.FileInfo.FileList.clear(); - Global.FileInfo.FileCount = 0; - Global.FileInfo.TotalLineCount = 0; - Global.FileInfo.LineNumber = 0; - Global.FileInfo.FileNum = 0; + tokenizer.eatToken(); + ExpressionInternal* second = expression(tokenizer); -#ifdef ARMIPS_ARM - Arm.clear(); -#endif + if (second != nullptr && tokenizer.nextToken().type == TokenType::Colon) + { + ExpressionInternal* third = expression(tokenizer); + if (third != nullptr) + return new ExpressionInternal(OperatorType::TertiaryIf,exp,second,third); - // process settings - Parser parser; - SymbolData symData; - TempData tempData; + delete third; + } - Logger::setSilent(settings.silent); - Logger::setErrorOnWarning(settings.errorOnWarning); + delete second; + delete exp; + return nullptr; +} - if (!settings.symFileName.empty()) - symData.setNocashSymFileName(settings.symFileName, settings.symFileVersion); +static ExpressionInternal* expression(Tokenizer& tokenizer) +{ + return conditionalExpression(tokenizer); +} - if (!settings.tempFileName.empty()) - tempData.setFileName(settings.tempFileName); +Expression parseExpression(Tokenizer& tokenizer, bool inUnknownOrFalseBlock) +{ + TokenizerPosition pos = tokenizer.getPosition(); - Token token; - for (size_t i = 0; i < settings.equList.size(); i++) + // parse expression, revert tokenizer to previous position + // if it failed + ExpressionInternal* exp = expression(tokenizer); + if (exp == nullptr) + tokenizer.setPosition(pos); + + Expression result; + result.setExpression(exp, inUnknownOrFalseBlock); + return result; +} + +// file: Parser/Parser.cpp + +inline bool isPartOfList(const std::wstring& value, const std::initializer_list<const wchar_t*>& terminators) +{ + for (const wchar_t* term: terminators) { - parser.addEquation(token, settings.equList[i].name, settings.equList[i].value); + if (value == term) + return true; } - Global.symbolTable.addLabels(settings.labels); - for (const LabelDefinition& label : settings.labels) + return false; +} + +Parser::Parser() +{ + initializingMacro = false; + overrideFileInfo = false; + conditionStack.push_back({true,false}); + clearError(); +} + +void Parser::pushConditionalResult(ConditionalResult cond) +{ + ConditionInfo info = conditionStack.back(); + info.inTrueBlock = info.inTrueBlock && cond != ConditionalResult::False; + info.inUnknownBlock = info.inUnknownBlock || cond == ConditionalResult::Unknown; + conditionStack.push_back(info); +} + +Expression Parser::parseExpression() +{ + return ::parseExpression(*getTokenizer(), !isInsideTrueBlock() || isInsideUnknownBlock()); +} + +bool Parser::parseExpressionList(std::vector<Expression>& list, int min, int max) +{ + bool valid = true; + list.clear(); + list.reserve(max >= 0 ? max : 32); + + const Token& start = peekToken(); + + Expression exp = parseExpression(); + list.push_back(exp); + + if (exp.isLoaded() == false) { - symData.addLabel(label.value, label.originalName); + printError(start,L"Parameter failure"); + getTokenizer()->skipLookahead(); + valid = false; } - if (Logger::hasError()) - return false; - - // run assembler - TextFile input; - switch (settings.mode) + while (peekToken().type == TokenType::Comma) { - case ArmipsMode::FILE: - Global.memoryMode = false; - if (input.open(settings.inputFileName,TextFile::Read) == false) + eatToken(); + + exp = parseExpression(); + list.push_back(exp); + + if (exp.isLoaded() == false) { - Logger::printError(Logger::Error,L"Could not open file"); - return false; + printError(start,L"Parameter failure"); + getTokenizer()->skipLookahead(); + valid = false; } - break; - case ArmipsMode::MEMORY: - Global.memoryMode = true; - Global.memoryFile = settings.memoryFile; - input.openMemory(settings.content); - break; } - std::unique_ptr<CAssemblerCommand> content = parser.parseFile(input); - Logger::printQueue(); - - bool result = !Logger::hasError(); - if (result == true && content != nullptr) - result = encodeAssembly(std::move(content), symData, tempData); - - if (g_fileManager->hasOpenFile()) + if (list.size() < (size_t) min) { - if (!Global.memoryMode) - Logger::printError(Logger::Warning,L"File not closed"); - g_fileManager->closeFile(); + printError(start,L"Not enough parameters (min %d)",min); + return false; } - // return errors - if (settings.errorsResult != nullptr) + if (max != -1 && (size_t) max < list.size()) { - StringList errors = Logger::getErrors(); - for (size_t i = 0; i < errors.size(); i++) - settings.errorsResult->push_back(errors[i]); + printError(start,L"Too many parameters (max %d)",max); + return false; } - return result; + return valid; } -// file: Core/Common.cpp -#include <sys/stat.h> - -FileManager fileManager; -FileManager* g_fileManager = &fileManager; +bool Parser::parseIdentifier(std::wstring& dest) +{ + const Token& tok = nextToken(); + if (tok.type != TokenType::Identifier) + return false; -tGlobal Global; -CArchitecture* Arch; + dest = tok.getStringValue(); + return true; +} -std::wstring getFolderNameFromPath(const std::wstring& src) +std::unique_ptr<CAssemblerCommand> Parser::parseCommandSequence(wchar_t indicator, const std::initializer_list<const wchar_t*> terminators) { -#ifdef _WIN32 - size_t s = src.find_last_of(L"\\/"); -#else - size_t s = src.rfind(L"/"); -#endif - if (s == std::wstring::npos) + auto sequence = ::make_unique<CommandSequence>(); + + bool foundTermination = false; + while (atEnd() == false) { - return L"."; - } + const Token &next = peekToken(); - return src.substr(0,s); -} + if(next.type == TokenType::Separator) + { + eatToken(); + continue; + } -std::wstring getFullPathName(const std::wstring& path) -{ - if (Global.relativeInclude == true) + if (next.stringValueStartsWith(indicator) && isPartOfList(next.getStringValue(), terminators)) + { + foundTermination = true; + break; + } + + bool foundSomething = false; + while (checkEquLabel() || checkMacroDefinition()) + { + // do nothing, just parse all the equs and macros there are + if (hasError()) + sequence->addCommand(handleError()); + + foundSomething = true; + } + + if (foundSomething) + continue; + + std::unique_ptr<CAssemblerCommand> cmd = parseCommand(); + + // omit commands inside blocks that are trivially false + if (isInsideTrueBlock() == false) + { + continue; + } + + sequence->addCommand(std::move(cmd)); + } + + if (!foundTermination && terminators.size()) { - if (isAbsolutePath(path)) + std::wstring expected; + for (const wchar_t* terminator : terminators) { - return path; - } else { - std::wstring source = Global.FileInfo.FileList[Global.FileInfo.FileNum]; - return getFolderNameFromPath(source) + L"/" + path; + if (!expected.empty()) + expected += L", "; + expected += terminator; } - } else { - return path; + + Logger::printError(Logger::Error, L"Unterminated command sequence, expected any of %s.", expected); } -} -bool checkLabelDefined(const std::wstring& labelName, int section) -{ - std::shared_ptr<Label> label = Global.symbolTable.getLabel(labelName,Global.FileInfo.FileNum,section); - return label->isDefined(); + return sequence; } -bool checkValidLabelName(const std::wstring& labelName) +std::unique_ptr<CAssemblerCommand> Parser::parseFile(TextFile& file, bool virtualFile) { - return Global.symbolTable.isValidSymbolName(labelName); + FileTokenizer tokenizer; + if (tokenizer.init(&file) == false) + return nullptr; + + std::unique_ptr<CAssemblerCommand> result = parse(&tokenizer,virtualFile,file.getFileName()); + + if (file.isFromMemory() == false) + Global.FileInfo.TotalLineCount += file.getNumLines(); + + return result; } -bool isPowerOfTwo(int64_t n) +std::unique_ptr<CAssemblerCommand> Parser::parseString(const std::wstring& text) { - if (n == 0) return false; - return !(n & (n - 1)); + TextFile file; + file.openMemory(text); + return parseFile(file,true); } -// file: Core/Expression.cpp - -enum class ExpressionValueCombination +std::unique_ptr<CAssemblerCommand> Parser::parseTemplate(const std::wstring& text, std::initializer_list<AssemblyTemplateArgument> variables) { - II = (int(ExpressionValueType::Integer) << 2) | (int(ExpressionValueType::Integer) << 0), - IF = (int(ExpressionValueType::Integer) << 2) | (int(ExpressionValueType::Float) << 0), - FI = (int(ExpressionValueType::Float) << 2) | (int(ExpressionValueType::Integer) << 0), - FF = (int(ExpressionValueType::Float) << 2) | (int(ExpressionValueType::Float) << 0), - IS = (int(ExpressionValueType::Integer) << 2) | (int(ExpressionValueType::String) << 0), - FS = (int(ExpressionValueType::Float) << 2) | (int(ExpressionValueType::String) << 0), - SI = (int(ExpressionValueType::String) << 2) | (int(ExpressionValueType::Integer) << 0), - SF = (int(ExpressionValueType::String) << 2) | (int(ExpressionValueType::Float) << 0), - SS = (int(ExpressionValueType::String) << 2) | (int(ExpressionValueType::String) << 0), -}; + std::wstring fullText = text; -ExpressionValueCombination getValueCombination(ExpressionValueType a, ExpressionValueType b) -{ - return (ExpressionValueCombination) ((int(a) << 2) | (int(b) << 0)); -} + overrideFileInfo = true; + overrideFileNum = Global.FileInfo.FileNum; + overrideLineNum = Global.FileInfo.LineNumber; -ExpressionValue ExpressionValue::operator+(const ExpressionValue& other) const -{ - ExpressionValue result; - switch (getValueCombination(type,other.type)) + for (auto& arg: variables) { - case ExpressionValueCombination::II: - result.type = ExpressionValueType::Integer; - result.intValue = intValue + other.intValue; - break; - case ExpressionValueCombination::FI: - result.type = ExpressionValueType::Float; - result.floatValue = floatValue + other.intValue; - break; - case ExpressionValueCombination::IF: - result.type = ExpressionValueType::Float; - result.floatValue = intValue + other.floatValue; - break; - case ExpressionValueCombination::FF: - result.type = ExpressionValueType::Float; - result.floatValue = floatValue + other.floatValue; - break; - case ExpressionValueCombination::IS: - result.type = ExpressionValueType::String; - result.strValue = to_wstring(intValue) + other.strValue; - break; - case ExpressionValueCombination::FS: - result.type = ExpressionValueType::String; - result.strValue = to_wstring(floatValue) + other.strValue; - break; - case ExpressionValueCombination::SI: - result.type = ExpressionValueType::String; - result.strValue = strValue + to_wstring(other.intValue); - break; - case ExpressionValueCombination::SF: - result.type = ExpressionValueType::String; - result.strValue = strValue + to_wstring(other.floatValue); - break; - case ExpressionValueCombination::SS: - result.type = ExpressionValueType::String; - result.strValue = strValue + other.strValue; - break; + size_t count = replaceAll(fullText,arg.variableName,arg.value); + (void)count; +#ifdef _DEBUG + if (count != 0 && arg.value.empty()) + Logger::printError(Logger::Warning,L"Empty replacement for %s",arg.variableName); +#endif } + std::unique_ptr<CAssemblerCommand> result = parseString(fullText); + overrideFileInfo = false; + return result; } -ExpressionValue ExpressionValue::operator-(const ExpressionValue& other) const +std::unique_ptr<CAssemblerCommand> Parser::parseDirective(const DirectiveMap &directiveSet) { - ExpressionValue result; - switch (getValueCombination(type,other.type)) + const Token &tok = peekToken(); + if (tok.type != TokenType::Identifier) + return nullptr; + + const std::wstring stringValue = tok.getStringValue(); + + auto matchRange = directiveSet.equal_range(stringValue); + for (auto it = matchRange.first; it != matchRange.second; ++it) { - case ExpressionValueCombination::II: - result.type = ExpressionValueType::Integer; - result.intValue = intValue - other.intValue; - break; - case ExpressionValueCombination::FI: - result.type = ExpressionValueType::Float; - result.floatValue = floatValue - other.intValue; - break; - case ExpressionValueCombination::IF: - result.type = ExpressionValueType::Float; - result.floatValue = intValue - other.floatValue; - break; - case ExpressionValueCombination::FF: - result.type = ExpressionValueType::Float; - result.floatValue = floatValue - other.floatValue; - break; - default: - break; + const DirectiveEntry &directive = it->second; + + if (directive.flags & DIRECTIVE_DISABLED) + continue; + if ((directive.flags & DIRECTIVE_NOCASHOFF) && Global.nocash == true) + continue; + if ((directive.flags & DIRECTIVE_NOCASHON) && Global.nocash == false) + continue; + if ((directive.flags & DIRECTIVE_NOTINMEMORY) && Global.memoryMode == true) + continue; + + if (directive.flags & DIRECTIVE_MIPSRESETDELAY) + Arch->NextSection(); + + eatToken(); + std::unique_ptr<CAssemblerCommand> result = directive.function(*this,directive.flags); + if (result == nullptr) + { + if (hasError() == false) + printError(tok,L"Directive parameter failure"); + return nullptr; + } else if (!(directive.flags & DIRECTIVE_MANUALSEPARATOR) && nextToken().type != TokenType::Separator) + { + printError(tok,L"Directive not terminated"); + return nullptr; + } + + return result; } - return result; + return nullptr; } -ExpressionValue ExpressionValue::operator*(const ExpressionValue& other) const +bool Parser::matchToken(TokenType type, bool optional) { - ExpressionValue result; - switch (getValueCombination(type,other.type)) + if (optional) { - case ExpressionValueCombination::II: - result.type = ExpressionValueType::Integer; - result.intValue = intValue * other.intValue; - break; - case ExpressionValueCombination::FI: - result.type = ExpressionValueType::Float; - result.floatValue = floatValue * other.intValue; - break; - case ExpressionValueCombination::IF: - result.type = ExpressionValueType::Float; - result.floatValue = intValue * other.floatValue; - break; - case ExpressionValueCombination::FF: - result.type = ExpressionValueType::Float; - result.floatValue = floatValue * other.floatValue; - break; - default: - break; + const Token& token = peekToken(); + if (token.type == type) + eatToken(); + return true; } - return result; + return nextToken().type == type; } -ExpressionValue ExpressionValue::operator/(const ExpressionValue& other) const +std::unique_ptr<CAssemblerCommand> Parser::parse(Tokenizer* tokenizer, bool virtualFile, const std::wstring& name) { - ExpressionValue result; - switch (getValueCombination(type,other.type)) + if (entries.size() >= 150) { - case ExpressionValueCombination::II: - result.type = ExpressionValueType::Integer; - if (intValue == INT64_MIN && other.intValue == -1){ - result.intValue = INT64_MIN; - Logger::queueError(Logger::Warning,L"Division overflow in expression"); - return result; - } - if (other.intValue == 0) - { - result.intValue = ~0; - Logger::queueError(Logger::Warning,L"Integer division by zero in expression"); - return result; - } - result.intValue = intValue / other.intValue; - break; - case ExpressionValueCombination::FI: - result.type = ExpressionValueType::Float; - result.floatValue = floatValue / other.intValue; - break; - case ExpressionValueCombination::IF: - result.type = ExpressionValueType::Float; - result.floatValue = intValue / other.floatValue; - break; - case ExpressionValueCombination::FF: - result.type = ExpressionValueType::Float; - result.floatValue = floatValue / other.floatValue; - break; - default: - break; + Logger::queueError(Logger::Error, L"Max include/recursion depth reached"); + return nullptr; } - return result; -} + FileEntry entry; + entry.tokenizer = tokenizer; + entry.virtualFile = virtualFile; -ExpressionValue ExpressionValue::operator%(const ExpressionValue& other) const -{ - ExpressionValue result; - switch (getValueCombination(type,other.type)) + if (virtualFile == false && name.empty() == false) { - case ExpressionValueCombination::II: - result.type = ExpressionValueType::Integer; - if (intValue == INT64_MIN && other.intValue == -1){ - result.intValue = 0; - Logger::queueError(Logger::Warning,L"Division overflow in expression"); - return result; - } - if (other.intValue == 0) - { - result.intValue = intValue; - Logger::queueError(Logger::Warning,L"Integer division by zero in expression"); - return result; - } - result.intValue = intValue % other.intValue; - break; - default: - break; + entry.fileNum = (int) Global.FileInfo.FileList.size(); + Global.FileInfo.FileList.push_back(name); + } else { + entry.fileNum = -1; } - return result; -} - -ExpressionValue ExpressionValue::operator!() const -{ - ExpressionValue result; - result.type = ExpressionValueType::Integer; + entries.push_back(entry); - if (isFloat()) - result.intValue = !floatValue; - else - result.intValue = !intValue; + std::unique_ptr<CAssemblerCommand> sequence = parseCommandSequence(); + entries.pop_back(); - return result; + return sequence; } -ExpressionValue ExpressionValue::operator~() const +void Parser::addEquation(const Token& startToken, const std::wstring& name, const std::wstring& value) { - ExpressionValue result; + // parse value string + TextFile f; + f.openMemory(value); - if (isInt()) + FileTokenizer tok; + tok.init(&f); + + TokenizerPosition start = tok.getPosition(); + while (tok.atEnd() == false && tok.peekToken().type != TokenType::Separator) { - result.type = ExpressionValueType::Integer; - result.intValue = ~intValue; + const Token& token = tok.nextToken(); + if (token.type == TokenType::Identifier && token.getStringValue() == name) + { + printError(startToken,L"Recursive equ definition for \"%s\" not allowed",name); + return; + } + + if (token.type == TokenType::Equ) + { + printError(startToken,L"equ value must not contain another equ instance"); + return; + } } - return result; -} + // extract tokens + TokenizerPosition end = tok.getPosition(); + std::vector<Token> tokens = tok.getTokens(start, end); + size_t index = Tokenizer::addEquValue(tokens); -ExpressionValue ExpressionValue::operator<<(const ExpressionValue& other) const -{ - ExpressionValue result; - switch (getValueCombination(type,other.type)) - { - case ExpressionValueCombination::II: - result.type = ExpressionValueType::Integer; - result.intValue = ((uint64_t) intValue) << other.intValue; - break; - default: - break; - } + for (FileEntry& entry : entries) + entry.tokenizer->resetLookaheadCheckMarks(); - return result; + // register equation + Global.symbolTable.addEquation(name, Global.FileInfo.FileNum, Global.Section, index); } -ExpressionValue ExpressionValue::operator>>(const ExpressionValue& other) const +bool Parser::checkEquLabel() { - ExpressionValue result; - switch (getValueCombination(type,other.type)) + updateFileInfo(); + + const Token& start = peekToken(); + if (start.type == TokenType::Identifier) { - case ExpressionValueCombination::II: - result.type = ExpressionValueType::Integer; - result.intValue = ((uint64_t) intValue) >> other.intValue; - break; - default: - break; - } + int pos = 1; + if (peekToken(pos).type == TokenType::Colon) + pos++; - return result; -} + if (peekToken(pos).type == TokenType::Equ && + peekToken(pos+1).type == TokenType::EquValue) + { + std::wstring name = peekToken(0).getStringValue(); + std::wstring value = peekToken(pos+1).getStringValue(); + eatTokens(pos+2); -bool ExpressionValue::operator<(const ExpressionValue& other) const -{ - switch (getValueCombination(type,other.type)) - { - case ExpressionValueCombination::II: - return intValue < other.intValue; - case ExpressionValueCombination::FI: - return floatValue < other.intValue; - case ExpressionValueCombination::IF: - return intValue < other.floatValue; - case ExpressionValueCombination::FF: - return floatValue < other.floatValue; - case ExpressionValueCombination::SS: - return strValue < other.strValue; - default: - break; - } + // skip the equ if it's inside a false conditional block + if (isInsideTrueBlock() == false) + return true; - return false; -} + // equs can't be inside blocks whose condition can only be + // evaluated during validation + if (isInsideUnknownBlock()) + { + printError(start,L"equ not allowed inside of block with non-trivial condition"); + return true; + } -bool ExpressionValue::operator<=(const ExpressionValue& other) const -{ - switch (getValueCombination(type,other.type)) - { - case ExpressionValueCombination::II: - return intValue <= other.intValue; - case ExpressionValueCombination::FI: - return floatValue <= other.intValue; - case ExpressionValueCombination::IF: - return intValue <= other.floatValue; - case ExpressionValueCombination::FF: - return floatValue <= other.floatValue; - case ExpressionValueCombination::SS: - return strValue <= other.strValue; - default: - break; + // equs are not allowed in macros + if (initializingMacro) + { + printError(start,L"equ not allowed in macro"); + return true; + } + + if (Global.symbolTable.isValidSymbolName(name) == false) + { + printError(start,L"Invalid equation name \"%s\"",name); + return true; + } + + if (Global.symbolTable.symbolExists(name,Global.FileInfo.FileNum,Global.Section)) + { + printError(start,L"Equation name \"%s\" already defined",name); + return true; + } + + addEquation(start,name,value); + return true; + } } return false; } -bool ExpressionValue::operator>(const ExpressionValue& other) const +bool Parser::checkMacroDefinition() { - return other < *this; -} + const Token& first = peekToken(); + if (first.type != TokenType::Identifier) + return false; -bool ExpressionValue::operator>=(const ExpressionValue& other) const -{ - return other <= *this; -} + if (!first.stringValueStartsWith(L'.') || first.getStringValue() != L".macro") + return false; -bool ExpressionValue::operator==(const ExpressionValue& other) const -{ - switch (getValueCombination(type,other.type)) + eatToken(); + + // nested macro definitions are not allowed + if (initializingMacro) { - case ExpressionValueCombination::II: - return intValue == other.intValue; - case ExpressionValueCombination::FI: - return floatValue == other.intValue; - case ExpressionValueCombination::IF: - return intValue == other.floatValue; - case ExpressionValueCombination::FF: - return floatValue == other.floatValue; - case ExpressionValueCombination::IS: - return to_wstring(intValue) == other.strValue; - case ExpressionValueCombination::FS: - return to_wstring(floatValue) == other.strValue; - case ExpressionValueCombination::SI: - return strValue == to_wstring(other.intValue); - case ExpressionValueCombination::SF: - return strValue == to_wstring(other.floatValue); - case ExpressionValueCombination::SS: - return strValue == other.strValue; + printError(first,L"Nested macro definitions not allowed"); + while (!atEnd()) + { + const Token& token = nextToken(); + if (token.type == TokenType::Identifier && token.getStringValue() == L".endmacro") + break; + } + + return true; } - return false; -} + std::vector<Expression> parameters; + if (parseExpressionList(parameters,1,-1) == false) + return false; -bool ExpressionValue::operator!=(const ExpressionValue& other) const -{ - return !(*this == other); -} + ParserMacro macro; + macro.counter = 0; -ExpressionValue ExpressionValue::operator&(const ExpressionValue& other) const -{ - ExpressionValue result; - switch (getValueCombination(type,other.type)) + // load name + if (parameters[0].evaluateIdentifier(macro.name) == false) + return false; + + // load parameters + for (size_t i = 1; i < parameters.size(); i++) { - case ExpressionValueCombination::II: - result.type = ExpressionValueType::Integer; - result.intValue = intValue & other.intValue; - break; - default: - break; - } + std::wstring name; + if (parameters[i].evaluateIdentifier(name) == false) + return false; - return result; -} + macro.parameters.push_back(name); + } -ExpressionValue ExpressionValue::operator|(const ExpressionValue& other) const -{ - ExpressionValue result; - switch (getValueCombination(type,other.type)) + if(nextToken().type != TokenType::Separator) { - case ExpressionValueCombination::II: - result.type = ExpressionValueType::Integer; - result.intValue = intValue | other.intValue; - break; - default: - break; + printError(first,L"Macro directive not terminated"); + return false; } - return result; -} + // load macro content -ExpressionValue ExpressionValue::operator^(const ExpressionValue& other) const -{ - ExpressionValue result; - switch (getValueCombination(type,other.type)) + TokenizerPosition start = getTokenizer()->getPosition(); + bool valid = false; + while (atEnd() == false) { - case ExpressionValueCombination::II: - result.type = ExpressionValueType::Integer; - result.intValue = intValue ^ other.intValue; - break; - default: - break; + const Token& tok = nextToken(); + if (tok.type == TokenType::Identifier && tok.getStringValue() == L".endmacro") + { + valid = true; + break; + } } - return result; -} - -ExpressionValue ExpressionValue::operator&&(const ExpressionValue& other) const -{ - ExpressionValue result; - result.type = ExpressionValueType::Integer; - - switch (getValueCombination(type,other.type)) + // Macros have to be defined at parse time, so they can't be defined in blocks + // with non-trivial conditions + if (isInsideUnknownBlock()) { - case ExpressionValueCombination::II: - result.intValue = intValue && other.intValue; - break; - case ExpressionValueCombination::FI: - result.floatValue = floatValue && other.intValue; - break; - case ExpressionValueCombination::IF: - result.floatValue = intValue && other.floatValue; - break; - case ExpressionValueCombination::FF: - result.floatValue = floatValue && other.floatValue; - break; - default: - break; + printError(first, L"Macro definition not allowed inside of block with non-trivial condition"); + return false; } - return result; -} - -ExpressionValue ExpressionValue::operator||(const ExpressionValue& other) const -{ - ExpressionValue result; - result.type = ExpressionValueType::Integer; + // if we are in a known false block, don't define the macro + if (!isInsideTrueBlock()) + return true; - switch (getValueCombination(type,other.type)) + // duplicate check + if (macros.find(macro.name) != macros.end()) { - case ExpressionValueCombination::II: - result.intValue = intValue || other.intValue; - break; - case ExpressionValueCombination::FI: - result.floatValue = floatValue || other.intValue; - break; - case ExpressionValueCombination::IF: - result.floatValue = intValue || other.floatValue; - break; - case ExpressionValueCombination::FF: - result.floatValue = floatValue || other.floatValue; - break; - default: - break; + printError(first, L"Macro \"%s\" already defined", macro.name); + return false; } - return result; -} + // no .endmacro, not valid + if (valid == false) + { + printError(first, L"Macro \"%s\" not terminated", macro.name); + return true; + } -ExpressionInternal::ExpressionInternal() -{ - children = nullptr; - childrenCount = 0; -} + // get content + TokenizerPosition end = getTokenizer()->getPosition().previous(); + macro.content = getTokenizer()->getTokens(start,end); -ExpressionInternal::~ExpressionInternal() -{ - deallocate(); -} + if(nextToken().type != TokenType::Separator) + { + printError(first,L"Endmacro directive not terminated"); + return false; + } -ExpressionInternal::ExpressionInternal(int64_t value) - : ExpressionInternal() -{ - type = OperatorType::Integer; - intValue = value; + macros[macro.name] = macro; + return true; } -ExpressionInternal::ExpressionInternal(double value) - : ExpressionInternal() +std::unique_ptr<CAssemblerCommand> Parser::parseMacroCall() { - type = OperatorType::Float; - floatValue = value; -} + const Token& start = peekToken(); + if (start.type != TokenType::Identifier) + return nullptr; -ExpressionInternal::ExpressionInternal(const std::wstring& value, OperatorType type) - : ExpressionInternal() -{ - this->type = type; - strValue = value; + auto it = macros.find(start.getStringValue()); + if (it == macros.end()) + return nullptr; - switch (type) - { - case OperatorType::Identifier: - fileNum = Global.FileInfo.FileNum; - section = Global.Section; - break; - case OperatorType::String: - break; - default: - break; - } -} + ParserMacro& macro = it->second; + eatToken(); -ExpressionInternal::ExpressionInternal(OperatorType op, ExpressionInternal* a, - ExpressionInternal* b, ExpressionInternal* c) - : ExpressionInternal() -{ - type = op; - allocate(3); + // create a token stream for the macro content, + // registering replacements for parameter values + TokenStreamTokenizer macroTokenizer; - children[0] = a; - children[1] = b; - children[2] = c; -} + std::set<std::wstring> identifierParameters; + for (size_t i = 0; i < macro.parameters.size(); i++) + { + if (peekToken().type == TokenType::Separator) + { + printError(start,L"Too few macro arguments (%d vs %d)",i,macro.parameters.size()); + return nullptr; + } -ExpressionInternal::ExpressionInternal(const std::wstring& name, const std::vector<ExpressionInternal*>& parameters) - : ExpressionInternal() -{ - type = OperatorType::FunctionCall; - allocate(parameters.size()); + if (i != 0) + { + if (nextToken().type != TokenType::Comma) + { + printError(start,L"Macro arguments not comma-separated"); + return nullptr; + } + } - strValue = name; - for (size_t i = 0; i < parameters.size(); i++) - { - children[i] = parameters[i]; - } -} + TokenizerPosition startPos = getTokenizer()->getPosition(); + Expression exp = parseExpression(); + if (exp.isLoaded() == false) + { + printError(start,L"Invalid macro argument expression"); + return nullptr; + } -void ExpressionInternal::allocate(size_t count) -{ - deallocate(); + TokenizerPosition endPos = getTokenizer()->getPosition(); + std::vector<Token> tokens = getTokenizer()->getTokens(startPos,endPos); - children = new ExpressionInternal*[count]; - childrenCount = count; -} + // remember any single identifier parameters for the label replacement + if (tokens.size() == 1 && tokens[0].type == TokenType::Identifier) + identifierParameters.insert(tokens[0].getStringValue()); -void ExpressionInternal::deallocate() -{ - for (size_t i = 0; i < childrenCount; i++) - { - delete children[i]; + // give them as a replacement to new tokenizer + macroTokenizer.registerReplacement(macro.parameters[i],tokens); } - delete[] children; - children = nullptr; - childrenCount = 0; -} - -void ExpressionInternal::replaceMemoryPos(const std::wstring& identifierName) -{ - for (size_t i = 0; i < childrenCount; i++) + if (peekToken().type == TokenType::Comma) { - if (children[i] != nullptr) + size_t count = macro.parameters.size(); + while (peekToken().type == TokenType::Comma) { - children[i]->replaceMemoryPos(identifierName); + eatToken(); + parseExpression(); + count++; } - } - if (type == OperatorType::MemoryPos) - { - type = OperatorType::Identifier; - strValue = identifierName; - fileNum = Global.FileInfo.FileNum; - section = Global.Section; + printError(start,L"Too many macro arguments (%d vs %d)",count,macro.parameters.size()); + return nullptr; } -} -bool ExpressionInternal::checkParameterCount(size_t minParams, size_t maxParams) -{ - if (minParams > childrenCount) + if(nextToken().type != TokenType::Separator) { - Logger::queueError(Logger::Error,L"Not enough parameters for \"%s\" (min %d)",strValue,minParams); - return false; + printError(start,L"Macro call not terminated"); + return nullptr; } - if (maxParams < childrenCount) + // skip macro instantiation in known false blocks + if (!isInsideUnknownBlock() && !isInsideTrueBlock()) + return ::make_unique<DummyCommand>(); + + // a macro is fully parsed once when it's loaded + // to gather all labels. it's not necessary to + // instantiate other macros at that time + if (initializingMacro) + return ::make_unique<DummyCommand>(); + + // the first time a macro is instantiated, it needs to be analyzed + // for labels + if (macro.counter == 0) { - Logger::queueError(Logger::Error,L"Too many parameters for \"%s\" (min %d)",strValue,maxParams); - return false; - } + initializingMacro = true; - return true; -} + // parse the short lived next command + macroTokenizer.init(macro.content); + Logger::suppressErrors(); + std::unique_ptr<CAssemblerCommand> command = parse(&macroTokenizer,true); + Logger::unsuppressErrors(); -ExpressionValue ExpressionInternal::executeExpressionFunctionCall(const ExpressionFunctionEntry& entry) -{ - // check parameters - if (!checkParameterCount(entry.minParams, entry.maxParams)) - return {}; + macro.labels = macroLabels; + macroLabels.clear(); - // evaluate parameters - std::vector<ExpressionValue> params; - params.reserve(childrenCount); + initializingMacro = false; + } - for (size_t i = 0; i < childrenCount; i++) + // register labels and replacements + for (const std::wstring& label: macro.labels) { - ExpressionValue result = children[i]->evaluate(); - if (!result.isValid()) - { - Logger::queueError(Logger::Error,L"%s: Invalid parameter %d", strValue, i+1); - return result; - } + // check if the label is using the name of a parameter + // in that case, don't register a unique replacement + if (identifierParameters.find(label) != identifierParameters.end()) + continue; - params.push_back(result); + // otherwise make sure the name is unique + std::wstring fullName; + if (Global.symbolTable.isLocalSymbol(label)) + fullName = formatString(L"@@%s_%s_%08X",macro.name,label.substr(2),macro.counter); + else if (Global.symbolTable.isStaticSymbol(label)) + fullName = formatString(L"@%s_%s_%08X",macro.name,label.substr(1),macro.counter); + else + fullName = formatString(L"%s_%s_%08X",macro.name,label,macro.counter); + + macroTokenizer.registerReplacement(label,fullName); } - // execute - return entry.function(strValue, params); + macroTokenizer.init(macro.content); + macro.counter++; + + return parse(&macroTokenizer,true); + } -ExpressionValue ExpressionInternal::executeExpressionLabelFunctionCall(const ExpressionLabelFunctionEntry& entry) +std::unique_ptr<CAssemblerCommand> Parser::parseLabel() { - // check parameters - if (!checkParameterCount(entry.minParams, entry.maxParams)) - return {}; + updateFileInfo(); - // evaluate parameters - std::vector<std::shared_ptr<Label>> params; - params.reserve(childrenCount); + const Token& start = peekToken(0); - for (size_t i = 0; i < childrenCount; i++) + if (peekToken(0).type == TokenType::Identifier && + peekToken(1).type == TokenType::Colon) { - ExpressionInternal *exp = children[i]; - if (!exp || !exp->isIdentifier()) + const std::wstring name = start.getStringValue(); + eatTokens(2); + + if (initializingMacro) + macroLabels.insert(name); + + if (Global.symbolTable.isValidSymbolName(name) == false) { - Logger::queueError(Logger::Error,L"%s: Invalid parameter %d, expecting identifier", strValue, i+1); - return {}; + printError(start,L"Invalid label name \"%s\"",name); + return nullptr; } - const std::wstring& name = exp->getStringValue(); - std::shared_ptr<Label> label = Global.symbolTable.getLabel(name,exp->getFileNum(),exp->getSection()); - params.push_back(label); + return ::make_unique<CAssemblerLabel>(name,start.getOriginalText()); } - // execute - return entry.function(strValue, params); + return nullptr; } -ExpressionValue ExpressionInternal::executeFunctionCall() +std::unique_ptr<CAssemblerCommand> Parser::handleError() { - // try expression functions - auto expFuncIt = expressionFunctions.find(strValue); - if (expFuncIt != expressionFunctions.end()) - return executeExpressionFunctionCall(expFuncIt->second); - - // try expression label functions - auto expLabelFuncIt = expressionLabelFunctions.find(strValue); - if (expLabelFuncIt != expressionLabelFunctions.end()) - return executeExpressionLabelFunctionCall(expLabelFuncIt->second); - - // try architecture specific expression functions - auto& archExpressionFunctions = Arch->getExpressionFunctions(); - expFuncIt = archExpressionFunctions.find(strValue); - if (expFuncIt != archExpressionFunctions.end()) - return executeExpressionFunctionCall(expFuncIt->second); + // skip the rest of the statement + while (!atEnd() && nextToken().type != TokenType::Separator); - // error - Logger::queueError(Logger::Error, L"Unknown function \"%s\"", strValue); - return {}; + clearError(); + return ::make_unique<InvalidCommand>(); } -bool isExpressionFunctionSafe(const std::wstring& name, bool inUnknownOrFalseBlock) -{ - // expression functions may be unsafe, others are safe - ExpFuncSafety safety = ExpFuncSafety::Unsafe; - bool found = false; - auto it = expressionFunctions.find(name); - if (it != expressionFunctions.end()) +void Parser::updateFileInfo() +{ + if (overrideFileInfo) { - safety = it->second.safety; - found = true; + Global.FileInfo.FileNum = overrideFileNum; + Global.FileInfo.LineNumber = overrideLineNum; + return; } - if (!found) + for (size_t i = entries.size(); i > 0; i--) { - auto labelIt = expressionLabelFunctions.find(name); - if (labelIt != expressionLabelFunctions.end()) - { - safety = labelIt->second.safety; - found = true; - } - } + size_t index = i-1; - if (!found) - { - auto& archExpressionFunctions = Arch->getExpressionFunctions(); - it = archExpressionFunctions.find(name); - if (it != archExpressionFunctions.end()) + if (entries[index].virtualFile == false && entries[index].fileNum != -1) { - safety = it->second.safety; - found = true; + Global.FileInfo.FileNum = entries[index].fileNum; + + // if it's not the topmost file, then the command to instantiate the + // following files was already parsed -> take the previous command's line + if (index != entries.size() - 1) + Global.FileInfo.LineNumber = entries[index].previousCommandLine; + else + { + Global.FileInfo.LineNumber = (int)entries[index].tokenizer->peekToken().line; + entries[index].previousCommandLine = Global.FileInfo.LineNumber; + } + return; } } - - if (inUnknownOrFalseBlock && safety == ExpFuncSafety::ConditionalUnsafe) - return false; - - return safety != ExpFuncSafety::Unsafe; } -bool ExpressionInternal::simplify(bool inUnknownOrFalseBlock) +std::unique_ptr<CAssemblerCommand> Parser::parseCommand() { - // check if this expression can actually be simplified - // without causing side effects - switch (type) - { - case OperatorType::Identifier: - case OperatorType::MemoryPos: - case OperatorType::ToString: - return false; - case OperatorType::FunctionCall: - if (isExpressionFunctionSafe(strValue, inUnknownOrFalseBlock) == false) - return false; - break; - default: - break; - } + std::unique_ptr<CAssemblerCommand> command; - // check if the same applies to all children - bool canSimplify = true; - for (size_t i = 0; i < childrenCount; i++) - { - if (children[i] != nullptr && children[i]->simplify(inUnknownOrFalseBlock) == false) - canSimplify = false; - } + updateFileInfo(); - // if so, this expression can be evaluated into a constant - if (canSimplify) - { - ExpressionValue value = evaluate(); + if (atEnd()) + return ::make_unique<DummyCommand>(); - switch (value.type) - { - case ExpressionValueType::Integer: - type = OperatorType::Integer; - intValue = value.intValue; - break; - case ExpressionValueType::Float: - type = OperatorType::Float; - floatValue = value.floatValue; - break; - case ExpressionValueType::String: - type = OperatorType::String; - strValue = value.strValue; - break; - default: - type = OperatorType::Invalid; - break; - } + if ((command = parseLabel()) != nullptr) + return command; + if (hasError()) + return handleError(); - deallocate(); - } + if ((command = parseMacroCall()) != nullptr) + return command; + if (hasError()) + return handleError(); - return canSimplify; + if ((command = Arch->parseDirective(*this)) != nullptr) + return command; + if (hasError()) + return handleError(); + + if ((command = parseDirective(directives)) != nullptr) + return command; + if (hasError()) + return handleError(); + + if ((command = Arch->parseOpcode(*this)) != nullptr) + return command; + if (hasError()) + return handleError(); + + const Token& token = peekToken(); + printError(token,L"Parse error '%s'",token.getOriginalText()); + return handleError(); } -ExpressionValue ExpressionInternal::evaluate() +void TokenSequenceParser::addEntry(int result, TokenSequence tokens, TokenValueSequence values) { - ExpressionValue val; + Entry entry = { tokens, values, result }; + entries.push_back(entry); +} - std::shared_ptr<Label> label; - switch (type) +bool TokenSequenceParser::parse(Parser& parser, int& result) +{ + for (Entry& entry: entries) { - case OperatorType::Integer: - val.type = ExpressionValueType::Integer; - val.intValue = intValue; - return val; - case OperatorType::Float: - val.type = ExpressionValueType::Float; - val.floatValue = floatValue; - return val; - case OperatorType::Identifier: - label = Global.symbolTable.getLabel(strValue,fileNum,section); - if (label == nullptr) - { - Logger::queueError(Logger::Error,L"Invalid label name \"%s\"",strValue); - return val; - } + TokenizerPosition pos = parser.getTokenizer()->getPosition(); + auto values = entry.values.begin(); - if (!label->isDefined()) + bool valid = true; + for (TokenType type: entry.tokens) { - Logger::queueError(Logger::Error,L"Undefined label \"%s\"",label->getName()); - return val; - } + // check of token type matches + const Token& token = parser.nextToken(); + if (token.type != type) + { + valid = false; + break; + } - val.type = ExpressionValueType::Integer; - val.intValue = label->getValue(); - return val; - case OperatorType::String: - val.type = ExpressionValueType::String; - val.strValue = strValue; - return val; - case OperatorType::MemoryPos: - val.type = ExpressionValueType::Integer; - val.intValue = g_fileManager->getVirtualAddress(); - return val; - case OperatorType::ToString: - val.type = ExpressionValueType::String; - val.strValue = children[0]->toString(); - return val; - case OperatorType::Add: - return children[0]->evaluate() + children[1]->evaluate(); - case OperatorType::Sub: - return children[0]->evaluate() - children[1]->evaluate(); - case OperatorType::Mult: - return children[0]->evaluate() * children[1]->evaluate(); - case OperatorType::Div: - return children[0]->evaluate() / children[1]->evaluate(); - case OperatorType::Mod: - return children[0]->evaluate() % children[1]->evaluate(); - case OperatorType::Neg: - val.type = ExpressionValueType::Integer; - val.intValue = 0; - return val - children[0]->evaluate(); - case OperatorType::LogNot: - return !children[0]->evaluate(); - case OperatorType::BitNot: - return ~children[0]->evaluate(); - case OperatorType::LeftShift: - return children[0]->evaluate() << children[1]->evaluate(); - case OperatorType::RightShift: - return children[0]->evaluate() >> children[1]->evaluate(); - case OperatorType::Less: - val.type = ExpressionValueType::Integer; - val.intValue = children[0]->evaluate() < children[1]->evaluate(); - return val; - case OperatorType::Greater: - val.type = ExpressionValueType::Integer; - val.intValue = children[0]->evaluate() > children[1]->evaluate(); - return val; - case OperatorType::LessEqual: - val.type = ExpressionValueType::Integer; - val.intValue = children[0]->evaluate() <= children[1]->evaluate(); - return val; - case OperatorType::GreaterEqual: - val.type = ExpressionValueType::Integer; - val.intValue = children[0]->evaluate() >= children[1]->evaluate(); - return val; - case OperatorType::Equal: - val.type = ExpressionValueType::Integer; - val.intValue = children[0]->evaluate() == children[1]->evaluate(); - return val; - case OperatorType::NotEqual: - val.type = ExpressionValueType::Integer; - val.intValue = children[0]->evaluate() != children[1]->evaluate(); - return val; - case OperatorType::BitAnd: - return children[0]->evaluate() & children[1]->evaluate(); - case OperatorType::BitOr: - return children[0]->evaluate() | children[1]->evaluate(); - case OperatorType::LogAnd: - return children[0]->evaluate() && children[1]->evaluate(); - case OperatorType::LogOr: - return children[0]->evaluate() || children[1]->evaluate(); - case OperatorType::Xor: - return children[0]->evaluate() ^ children[1]->evaluate(); - case OperatorType::TertiaryIf: - val.type = ExpressionValueType::Integer; - val.intValue = 0; - if (children[0]->evaluate() == val) - return children[2]->evaluate(); - else - return children[1]->evaluate(); - case OperatorType::FunctionCall: - return executeFunctionCall(); - default: - return val; - } -} - -static std::wstring escapeString(const std::wstring& text) -{ - std::wstring result = text; - replaceAll(result,LR"(\)",LR"(\\)"); - replaceAll(result,LR"(")",LR"(\")"); - - return formatString(LR"("%s")",text); -} + // if necessary, check if the value of the token also matches + if (type == TokenType::Identifier) + { + if (values == entry.values.end() || values->textValue != token.getStringValue()) + { + valid = false; + break; + } -std::wstring ExpressionInternal::formatFunctionCall() -{ - std::wstring text = strValue + L"("; + values++; + } else if (type == TokenType::Integer) + { + if (values == entry.values.end() || values->intValue != token.intValue) + { + valid = false; + break; + } - for (size_t i = 0; i < childrenCount; i++) - { - if (i != 0) - text += L","; - text += children[i]->toString(); - } + values++; + } + } - return text + L")"; -} + if (valid && values == entry.values.end()) + { + result = entry.result; + return true; + } -std::wstring ExpressionInternal::toString() -{ - switch (type) - { - case OperatorType::Integer: - return formatString(L"%d",intValue); - case OperatorType::Float: - return formatString(L"%g",floatValue); - case OperatorType::Identifier: - return strValue; - case OperatorType::String: - return escapeString(strValue); - case OperatorType::MemoryPos: - return L"."; - case OperatorType::Add: - return formatString(L"(%s + %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::Sub: - return formatString(L"(%s - %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::Mult: - return formatString(L"(%s * %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::Div: - return formatString(L"(%s / %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::Mod: - return formatString(L"(%s %% %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::Neg: - return formatString(L"(-%s)",children[0]->toString()); - case OperatorType::LogNot: - return formatString(L"(!%s)",children[0]->toString()); - case OperatorType::BitNot: - return formatString(L"(~%s)",children[0]->toString()); - case OperatorType::LeftShift: - return formatString(L"(%s << %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::RightShift: - return formatString(L"(%s >> %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::Less: - return formatString(L"(%s < %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::Greater: - return formatString(L"(%s > %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::LessEqual: - return formatString(L"(%s <= %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::GreaterEqual: - return formatString(L"(%s >= %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::Equal: - return formatString(L"(%s == %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::NotEqual: - return formatString(L"(%s != %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::BitAnd: - return formatString(L"(%s & %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::BitOr: - return formatString(L"(%s | %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::LogAnd: - return formatString(L"(%s && %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::LogOr: - return formatString(L"(%s || %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::Xor: - return formatString(L"(%s ^ %s)",children[0]->toString(),children[1]->toString()); - case OperatorType::TertiaryIf: - return formatString(L"(%s ? %s : %s)",children[0]->toString(),children[1]->toString(),children[2]->toString()); - case OperatorType::ToString: - return formatString(L"(%c%s)",L'\U000000B0',children[0]->toString()); - case OperatorType::FunctionCall: - return formatFunctionCall(); - default: - return L""; + parser.getTokenizer()->setPosition(pos); } -} -Expression::Expression() -{ - expression = nullptr; - constExpression = true; + return false; } -void Expression::setExpression(ExpressionInternal* exp, bool inUnknownOrFalseBlock) -{ - expression = std::shared_ptr<ExpressionInternal>(exp); - if (exp != nullptr) - constExpression = expression->simplify(inUnknownOrFalseBlock); - else - constExpression = true; -} +// file: Parser/Tokenizer.cpp +#include <algorithm> -ExpressionValue Expression::evaluate() -{ - if (expression == nullptr) - { - ExpressionValue invalid; - return invalid; - } - return expression->evaluate(); -} +// +// Tokenizer +// -void Expression::replaceMemoryPos(const std::wstring& identifierName) -{ - if (expression != nullptr) - expression->replaceMemoryPos(identifierName); -} +std::vector<std::vector<Token>> Tokenizer::equValues; -Expression createConstExpression(int64_t value) +Tokenizer::Tokenizer() { - Expression exp; - ExpressionInternal* num = new ExpressionInternal(value); - exp.setExpression(num,false); - return exp; + position.it = tokens.begin(); + invalidToken.type = TokenType::Invalid; + invalidToken.setOriginalText(L"Unexpected end of token stream"); } -// file: Core/ExpressionFunctions.cpp -#if ARMIPS_REGEXP -#include <regex> -#endif - -bool getExpFuncParameter(const std::vector<ExpressionValue>& parameters, size_t index, int64_t& dest, - const std::wstring& funcName, bool optional) +bool Tokenizer::processElement(TokenList::iterator& it) { - if (optional && index >= parameters.size()) - return true; - - if (index >= parameters.size() || parameters[index].isInt() == false) - { - Logger::queueError(Logger::Error,L"Invalid parameter %d for %s: expecting integer",index+1,funcName); + if (it == tokens.end()) return false; - } - - dest = parameters[index].intValue; - return true; -} -bool getExpFuncParameter(const std::vector<ExpressionValue>& parameters, size_t index, const std::wstring*& dest, - const std::wstring& funcName, bool optional) -{ - if (optional && index >= parameters.size()) - return true; - - if (index >= parameters.size() || parameters[index].isString() == false) + while ((*it).checked == false) { - Logger::queueError(Logger::Error,L"Invalid parameter %d for %s: expecting string",index+1,funcName); - return false; - } + bool replaced = false; + if ((*it).type == TokenType::Identifier) + { + const std::wstring stringValue = (*it).getStringValue(); + for (const Replacement& replacement: replacements) + { + // if the identifier matches, add all of its tokens + if (replacement.identifier == stringValue) + { + TokenList::iterator insertIt = it; + insertIt++; - dest = &parameters[index].strValue; - return true; -} + // replace old token with the new tokens + // replace the first token manually so that any iterators + // are still guaranteed to be valid + (*it) = replacement.value[0]; + tokens.insert(insertIt,replacement.value.begin()+1, replacement.value.end()); -#define GET_PARAM(params,index,dest) \ - if (getExpFuncParameter(params,index,dest,funcName,false) == false) \ - return ExpressionValue(); -#define GET_OPTIONAL_PARAM(params,index,dest,defaultValue) \ - dest = defaultValue; \ - if (getExpFuncParameter(params,index,dest,funcName,true) == false) \ - return ExpressionValue(); + // If the value at this position didn't change, then just keep going. + // Otherwise we'd be stuck in an endless replace loop + if (stringValue != (*it).getStringValue()) + replaced = true; + break; + } + } + if (replaced) + continue; -ExpressionValue expFuncVersion(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) -{ - int64_t value = ARMIPS_VERSION_MAJOR*1000 + ARMIPS_VERSION_MINOR*10 + ARMIPS_VERSION_REVISION; - return ExpressionValue(value); -} + // check for equs + size_t index; + if (Global.symbolTable.findEquation(stringValue,Global.FileInfo.FileNum,Global.Section,index)) + { + TokenList::iterator nextIt = it; + std::advance(nextIt, 1); -ExpressionValue expFuncEndianness(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) -{ - ExpressionValue result; - result.type = ExpressionValueType::String; + // check if this is another equ with the same name. + // if so, keep equ redefinitions for later error handling + if (nextIt != tokens.end() && nextIt->type == TokenType::Equ) + break; - switch (g_fileManager->getEndianness()) - { - case Endianness::Little: - return ExpressionValue(L"little"); - case Endianness::Big: - return ExpressionValue(L"big"); - } + // make room for the replacement tokens + const std::vector<Token>& replacement = equValues[index]; + tokens.insert(nextIt, replacement.size()-1, {}); - return ExpressionValue(); -} + // insert replacement tokens, while keeping the file info of the original token + Token originalToken = *it; -ExpressionValue expFuncOutputName(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) -{ - std::shared_ptr<AssemblerFile> file = g_fileManager->getOpenFile(); - if (file == nullptr) - { - Logger::queueError(Logger::Error,L"outputName: no file opened"); - return ExpressionValue(); + TokenList::iterator insertIt = it; + for (const Token& token: replacement) + { + (*insertIt) = token; + insertIt->line = originalToken.line; + insertIt->column = originalToken.column; + std::advance(insertIt, 1); + } + + replaced = true; + continue; + } + } + + if (replaced == false) + (*it).checked = true; } - std::wstring value = file->getFileName(); - return ExpressionValue(value); + return true; } -ExpressionValue expFuncFileExists(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +const Token& Tokenizer::nextToken() { - const std::wstring* fileName; - GET_PARAM(parameters,0,fileName); + if (processElement(position.it) == false) + return invalidToken; - std::wstring fullName = getFullPathName(*fileName); - return ExpressionValue(fileExists(fullName) ? INT64_C(1) : INT64_C(0)); + return *position.it++; } -ExpressionValue expFuncFileSize(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +const Token& Tokenizer::peekToken(int ahead) { - const std::wstring* fileName; - GET_PARAM(parameters,0,fileName); + auto it = position.it; + for (int i = 0; i < ahead; i++) + { + if (processElement(it) == false) + return invalidToken; - std::wstring fullName = getFullPathName(*fileName); - return ExpressionValue((int64_t) fileSize(fullName)); + it++; + } + + if (processElement(it) == false) + return invalidToken; + + return *it; } -ExpressionValue expFuncToString(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +void Tokenizer::eatTokens(int num) { - ExpressionValue result; - - switch (parameters[0].type) + for (int i = 0; i < num; i++) { - case ExpressionValueType::String: - result.strValue = parameters[0].strValue; - break; - case ExpressionValueType::Integer: - result.strValue = formatString(L"%d",parameters[0].intValue); - break; - case ExpressionValueType::Float: - result.strValue = formatString(L"%#.17g",parameters[0].floatValue); - break; - default: - return result; + if (processElement(position.it) == false) + break; + position.it++; } - - result.type = ExpressionValueType::String; - return result; } -ExpressionValue expFuncToHex(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +void Tokenizer::skipLookahead() { - int64_t value, digits; - GET_PARAM(parameters,0,value); - GET_OPTIONAL_PARAM(parameters,1,digits,8); - - return ExpressionValue(formatString(L"%0*X",digits,value)); + //position.index = tokens.size(); } -ExpressionValue expFuncInt(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +std::vector<Token> Tokenizer::getTokens(TokenizerPosition start, TokenizerPosition end) const { - ExpressionValue result; + std::vector<Token> result; - switch (parameters[0].type) + for (auto it = start.it; it != end.it; it++) { - case ExpressionValueType::Integer: - result.intValue = parameters[0].intValue; - break; - case ExpressionValueType::Float: - result.intValue = (int64_t) parameters[0].floatValue; - break; - default: - return result; + Token tok = *it; + tok.checked = false; + result.push_back(tok); } - result.type = ExpressionValueType::Integer; return result; } -ExpressionValue expFuncRound(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +void Tokenizer::registerReplacement(const std::wstring& identifier, std::vector<Token>& tokens) { - ExpressionValue result; - - switch (parameters[0].type) - { - case ExpressionValueType::Integer: - result.intValue = parameters[0].intValue; - break; - case ExpressionValueType::Float: - result.intValue = llround(parameters[0].floatValue); - break; - default: - return result; - } - - result.type = ExpressionValueType::Integer; - return result; + Replacement replacement { identifier, tokens }; + replacements.push_back(replacement); } -ExpressionValue expFuncFloat(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +void Tokenizer::registerReplacement(const std::wstring& identifier, const std::wstring& newValue) { - ExpressionValue result; + // Ensure the new identifier is lower case as it would be as a normally parsed string + std::wstring lowerCase = newValue; + std::transform(lowerCase.begin(), lowerCase.end(), lowerCase.begin(), ::towlower); - switch (parameters[0].type) - { - case ExpressionValueType::Integer: - result.floatValue = (double) parameters[0].intValue; - break; - case ExpressionValueType::Float: - result.floatValue = parameters[0].floatValue; - break; - default: - return result; - } + Token tok; + tok.type = TokenType::Identifier; + tok.setStringValue(lowerCase); + tok.setOriginalText(newValue); - result.type = ExpressionValueType::Float; - return result; + Replacement replacement; + replacement.identifier = identifier; + replacement.value.push_back(tok); + + replacements.push_back(replacement); } -ExpressionValue expFuncFrac(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +void Tokenizer::addToken(Token token) { - ExpressionValue result; - double intPart; + tokens.push_back(std::move(token)); +} - switch (parameters[0].type) +size_t Tokenizer::addEquValue(const std::vector<Token>& tokens) +{ + size_t index = equValues.size(); + equValues.push_back(tokens); + return index; +} + +void Tokenizer::resetLookaheadCheckMarks() +{ + auto it = position.it; + while (it != tokens.end() && it->checked) { - case ExpressionValueType::Float: - result.floatValue = modf(parameters[0].floatValue,&intPart); - break; - default: - return result; + it->checked = false; + it++; } - - result.type = ExpressionValueType::Float; - return result; } -ExpressionValue expFuncMin(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) -{ - ExpressionValue result; - double floatMin, floatCur; - int64_t intMin, intCur; +// +// FileTokenizer +// - floatCur = floatMin = std::numeric_limits<double>::max(); - intCur = intMin = std::numeric_limits<int64_t>::max(); - bool isInt = true; +inline bool isWhitespace(const std::wstring& text, size_t pos) +{ + if (pos >= text.size()) + return false; - for (size_t i = 0; i < parameters.size(); i++) - { - switch (parameters[i].type) - { - case ExpressionValueType::Integer: - intCur = parameters[i].intValue; - floatCur = (double)parameters[i].intValue; - break; - case ExpressionValueType::Float: - floatCur = parameters[i].floatValue; - isInt = false; - break; - default: - return result; - } + return text[pos] == ' ' || text[pos] == '\t'; +} - if (intCur < intMin) - intMin = intCur; - if (floatCur < floatMin) - floatMin = floatCur; - } +inline bool isComment(const std::wstring& text, size_t pos) +{ + if (pos < text.size() && text[pos] == ';') + return true; - if (isInt) - { - result.intValue = intMin; - result.type = ExpressionValueType::Integer; - } - else - { - result.floatValue = floatMin; - result.type = ExpressionValueType::Float; - } + if (pos+1 < text.size() && text[pos+0] == '/' && text[pos+1] == '/') + return true; - return result; + return false; } -ExpressionValue expFuncMax(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +inline bool isContinuation(const std::wstring& text, size_t pos) { - ExpressionValue result; - double floatMax, floatCur; - int64_t intMax, intCur; + if (pos >= text.size()) + return false; - floatCur = floatMax = std::numeric_limits<double>::min(); - intCur = intMax = std::numeric_limits<int64_t>::min(); - bool isInt = true; + return text[pos] == '\\'; +} - for (size_t i = 0; i < parameters.size(); i++) - { - switch (parameters[i].type) - { - case ExpressionValueType::Integer: - intCur = parameters[i].intValue; - floatCur = (double)parameters[i].intValue; - break; - case ExpressionValueType::Float: - floatCur = parameters[i].floatValue; - isInt = false; - break; - default: - return result; - } +inline bool isBlockComment(const std::wstring& text, size_t pos){ + if (pos+1 < text.size() && text[pos+0] == '/' && text[pos+1] == '*') + return true; - if (intCur > intMax) - intMax = intCur; - if (floatCur > floatMax) - floatMax = floatCur; - } + return false; +} - if (isInt) - { - result.intValue = intMax; - result.type = ExpressionValueType::Integer; - } - else - { - result.floatValue = floatMax; - result.type = ExpressionValueType::Float; - } +inline bool isBlockCommentEnd(const std::wstring& text, size_t pos){ + if (pos+1 < text.size() && text[pos+0] == '*' && text[pos+1] == '/') + return true; - return result; + return false; } -ExpressionValue expFuncAbs(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +void FileTokenizer::skipWhitespace() { - ExpressionValue result; - - switch (parameters[0].type) + while (true) { - case ExpressionValueType::Float: - result.type = ExpressionValueType::Float; - result.floatValue = fabs(parameters[0].floatValue); - break; - case ExpressionValueType::Integer: - result.type = ExpressionValueType::Integer; - result.intValue = parameters[0].intValue >= 0 ? - parameters[0].intValue : -parameters[0].intValue; - break; - default: - break; + if (isWhitespace(currentLine,linePos)) + { + do { linePos++; } while (isWhitespace(currentLine,linePos)); + } else if (isComment(currentLine,linePos)) + { + linePos = currentLine.size(); + } else if (isBlockComment(currentLine,linePos)) + { + linePos += 2; + while(!isBlockCommentEnd(currentLine,linePos)) + { + linePos++; + if (linePos >= currentLine.size()) + { + if (isInputAtEnd()) + { + createToken(TokenType::Invalid,linePos,L"Unexpected end of file in block comment"); + addToken(token); + return; + } + currentLine = input->readLine(); + linePos = 0; + lineNumber++; + } + } + linePos += 2; + } else + { + break; + } } - - return result; } -ExpressionValue expFuncStrlen(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +void FileTokenizer::createToken(TokenType type, size_t length) { - const std::wstring* source; - GET_PARAM(parameters,0,source); + token.type = type; + token.line = lineNumber; + token.column = linePos+1; + token.setOriginalText(currentLine,linePos,length); - return ExpressionValue((int64_t)source->size()); + linePos += length; } -ExpressionValue expFuncSubstr(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +void FileTokenizer::createToken(TokenType type, size_t length, int64_t value) { - int64_t start, count; - const std::wstring* source; - - GET_PARAM(parameters,0,source); - GET_PARAM(parameters,1,start); - GET_PARAM(parameters,2,count); + token.type = type; + token.line = lineNumber; + token.column = linePos+1; + token.setOriginalText(currentLine,linePos,length); + token.intValue = value; - return ExpressionValue(source->substr((size_t)start,(size_t)count)); + linePos += length; } -#if ARMIPS_REGEXP -ExpressionValue expFuncRegExMatch(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +void FileTokenizer::createToken(TokenType type, size_t length, double value) { - const std::wstring* source; - const std::wstring* regexString; - - GET_PARAM(parameters,0,source); - GET_PARAM(parameters,1,regexString); + token.type = type; + token.line = lineNumber; + token.column = linePos+1; + token.setOriginalText(currentLine,linePos,length); + token.floatValue = value; -#if ARMIPS_EXCEPTIONS - try - { -#endif - std::wregex regex(*regexString); - bool found = std::regex_match(*source,regex); - return ExpressionValue(found ? INT64_C(1) : INT64_C(0)); -#if ARMIPS_EXCEPTIONS - } catch (std::regex_error&) - { - Logger::queueError(Logger::Error,L"Invalid regular expression"); - return ExpressionValue(); - } -#endif + linePos += length; } -ExpressionValue expFuncRegExSearch(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +void FileTokenizer::createToken(TokenType type, size_t length, const std::wstring& value) { - const std::wstring* source; - const std::wstring* regexString; + createToken(type, length, value, 0, value.length()); +} - GET_PARAM(parameters,0,source); - GET_PARAM(parameters,1,regexString); +void FileTokenizer::createToken(TokenType type, size_t length, const std::wstring& value, size_t valuePos, size_t valueLength) +{ + token.type = type; + token.line = lineNumber; + token.column = linePos+1; + token.setOriginalText(currentLine,linePos,length); + token.setStringValue(value,valuePos,valueLength); -#if ARMIPS_EXCEPTIONS - try - { -#endif - std::wregex regex(*regexString); - bool found = std::regex_search(*source,regex); - return ExpressionValue(found ? INT64_C(1) : INT64_C(0)); -#if ARMIPS_EXCEPTIONS - } catch (std::regex_error&) - { - Logger::queueError(Logger::Error,L"Invalid regular expression"); - return ExpressionValue(); - } -#endif + linePos += length; } -ExpressionValue expFuncRegExExtract(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +void FileTokenizer::createTokenCurrentString(TokenType type, size_t length) { - const std::wstring* source; - const std::wstring* regexString; - int64_t matchIndex; + token.type = type; + token.line = lineNumber; + token.column = linePos+1; + token.setStringAndOriginalValue(currentLine,linePos,length); - GET_PARAM(parameters,0,source); - GET_PARAM(parameters,1,regexString); - GET_OPTIONAL_PARAM(parameters,2,matchIndex,0); + linePos += length; +} -#if ARMIPS_EXCEPTIONS - try - { -#endif - std::wregex regex(*regexString); - std::wsmatch result; - bool found = std::regex_search(*source,result,regex); - if (found == false || (size_t)matchIndex >= result.size()) - { - Logger::queueError(Logger::Error,L"Capture group index %d does not exist",matchIndex); - return ExpressionValue(); - } +bool FileTokenizer::parseOperator() +{ + wchar_t first = currentLine[linePos]; + wchar_t second = linePos+1 >= currentLine.size() ? '\0' : currentLine[linePos+1]; - return ExpressionValue(result[(size_t)matchIndex].str()); -#if ARMIPS_EXCEPTIONS - } catch (std::regex_error&) + switch (first) { - Logger::queueError(Logger::Error,L"Invalid regular expression"); - return ExpressionValue(); + case '(': + createToken(TokenType::LParen,1); + return true; + case ')': + createToken(TokenType::RParen,1); + return true; + case '+': + createToken(TokenType::Plus,1); + return true; + case '-': + createToken(TokenType::Minus,1); + return true; + case '*': + createToken(TokenType::Mult,1); + return true; + case '/': + createToken(TokenType::Div,1); + return true; + case '%': + createToken(TokenType::Mod,1); + return true; + case '^': + createToken(TokenType::Caret,1); + return true; + case '~': + createToken(TokenType::Tilde,1); + return true; + case '<': + if (second == '<') + createToken(TokenType::LeftShift,2); + else if (second == '=') + createToken(TokenType::LessEqual,2); + else + createToken(TokenType::Less,1); + return true; + case '>': + if (second == '>') + createToken(TokenType::RightShift,2); + else if (second == '=') + createToken(TokenType::GreaterEqual,2); + else + createToken(TokenType::Greater,1); + return true; + case '=': + if (second == '=') + createToken(TokenType::Equal,2); + else + createToken(TokenType::Assign,1); + return true; + case '!': + if (second == '=') + createToken(TokenType::NotEqual,2); + else + createToken(TokenType::Exclamation,1); + return true; + case '&': + if (second == '&') + createToken(TokenType::LogAnd,2); + else + createToken(TokenType::BitAnd,1); + return true; + case '|': + if (second == '|') + createToken(TokenType::LogOr,2); + else + createToken(TokenType::BitOr,1); + return true; + case '?': + createToken(TokenType::Question,1); + return true; + case ':': + if (second == ':') + createToken(TokenType::Separator,2); + else + createToken(TokenType::Colon,1); + return true; + case ',': + createToken(TokenType::Comma,1); + return true; + case '[': + createToken(TokenType::LBrack,1); + return true; + case ']': + createToken(TokenType::RBrack,1); + return true; + case '#': + createToken(TokenType::Hash,1); + return true; + case '{': + createToken(TokenType::LBrace,1); + return true; + case '}': + createToken(TokenType::RBrace,1); + return true; + case '$': + createToken(TokenType::Dollar,1); + return true; + case L'\U000000B0': // degree sign + createToken(TokenType::Degree,1); + return true; } -#endif + + return false; } -#endif -ExpressionValue expFuncFind(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +bool FileTokenizer::convertInteger(size_t start, size_t end, int64_t& result) { - int64_t start; - const std::wstring* source; - const std::wstring* value; - - GET_PARAM(parameters,0,source); - GET_PARAM(parameters,1,value); - GET_OPTIONAL_PARAM(parameters,2,start,0); - - size_t pos = source->find(*value,(size_t)start); - return ExpressionValue(pos == std::wstring::npos ? INT64_C(-1) : (int64_t) pos); + return stringToInt(currentLine, start, end, result); } -ExpressionValue expFuncRFind(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +bool FileTokenizer::convertFloat(size_t start, size_t end, double& result) { - int64_t start; - const std::wstring* source; - const std::wstring* value; - - GET_PARAM(parameters,0,source); - GET_PARAM(parameters,1,value); - GET_OPTIONAL_PARAM(parameters,2,start,std::wstring::npos); + std::wstring str = currentLine.substr(start, end - start); + wchar_t* end_ptr; - size_t pos = source->rfind(*value,(size_t)start); - return ExpressionValue(pos == std::wstring::npos ? INT64_C(-1) : (int64_t) pos); + result = wcstod(str.c_str(), &end_ptr); + return str.c_str() + str.size() == end_ptr; } - -template<typename T> -ExpressionValue expFuncRead(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +Token FileTokenizer::loadToken() { - const std::wstring* fileName; - int64_t pos; - - GET_PARAM(parameters,0,fileName); - GET_OPTIONAL_PARAM(parameters,1,pos,0); - - std::wstring fullName = getFullPathName(*fileName); - - BinaryFile file; - if (file.open(fullName,BinaryFile::Read) == false) + if (isInputAtEnd()) { - Logger::queueError(Logger::Error,L"Could not open %s",*fileName); - return ExpressionValue(); + createToken(TokenType::Invalid,0); + return std::move(token); } - file.setPos(pos); + size_t pos = linePos; - T buffer; - if (file.read(&buffer, sizeof(T)) != sizeof(T)) + if (equActive) { - Logger::queueError(Logger::Error, L"Failed to read %d byte(s) from offset 0x%08X of %s", sizeof(T), pos, *fileName); - return ExpressionValue(); - } - - return ExpressionValue((int64_t) buffer); -} + while (pos < currentLine.size() && !isComment(currentLine,pos)) + pos++; -ExpressionValue expFuncReadAscii(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) -{ - const std::wstring* fileName; - int64_t start; - int64_t length; + createTokenCurrentString(TokenType::EquValue,pos-linePos); - GET_PARAM(parameters,0,fileName); - GET_OPTIONAL_PARAM(parameters,1,start,0); - GET_OPTIONAL_PARAM(parameters,2,length,0); + equActive = false; + return std::move(token); + } - std::wstring fullName = getFullPathName(*fileName); + if (parseOperator()) + return std::move(token); - int64_t totalSize = fileSize(fullName); - if (length == 0 || start+length > totalSize) - length = totalSize-start; + wchar_t first = currentLine[pos]; - BinaryFile file; - if (file.open(fullName,BinaryFile::Read) == false) + // character constants + if (first == '\'' && pos+2 < currentLine.size() && currentLine[pos+2] == '\'') { - Logger::queueError(Logger::Error,L"Could not open %s",fileName); - return ExpressionValue(); + createToken(TokenType::Integer,3,(int64_t)currentLine[pos+1]); + return std::move(token); } - file.setPos((long)start); - - unsigned char* buffer = new unsigned char[length]; - file.read(buffer,(size_t)length); - - std::wstring result; - for (size_t i = 0; i < (size_t) length; i++) + // strings + if (first == '"') { - if (buffer[i] < 0x20 || buffer[i] > 0x7F) + std::wstring text; + pos++; + + bool valid = false; + while (pos < currentLine.size()) { - Logger::printError(Logger::Warning,L"%s: Non-ASCII character",funcName); - return ExpressionValue(); - } + if (pos+1 < currentLine.size() && currentLine[pos] == '\\') + { + if (currentLine[pos+1] == '"') + { + text += '"'; + pos += 2; + continue; + } - result += (wchar_t) buffer[i]; - } + if (currentLine[pos+1] == '\\') + { + text += '\\'; + pos += 2; + continue; + } + } - delete[] buffer; + if (currentLine[pos] == '"') + { + pos++; + valid = true; + break; + } - return ExpressionValue(result); -} + text += currentLine[pos++]; + } -ExpressionValue expLabelFuncDefined(const std::wstring &funcName, const std::vector<std::shared_ptr<Label>> &parameters) -{ - if (parameters.empty() || !parameters.front()) - { - Logger::queueError(Logger::Error,L"%s: Invalid parameters", funcName); - return ExpressionValue(); - } + if (!valid) + { + createToken(TokenType::Invalid,pos-linePos,L"Unexpected end of line in string constant"); + return std::move(token); + } - return ExpressionValue(parameters.front()->isDefined() ? INT64_C(1) : INT64_C(0)); -} + createToken(TokenType::String,pos-linePos,text); + return std::move(token); + } -ExpressionValue expLabelFuncOrg(const std::wstring& funcName, const std::vector<std::shared_ptr<Label>>& parameters) -{ - // return physical address of label parameter - if (parameters.size()) + // numbers + if (first >= '0' && first <= '9') { - Label* label = parameters.front().get(); - if (!label) - return ExpressionValue(); + // find end of number + size_t start = pos; + size_t end = pos; + bool isValid = true; + bool foundPoint = false; + bool foundExp = false; + bool foundExpSign = false; + bool isHex = start+1 < currentLine.size() && currentLine[start] == '0' && towlower(currentLine[start+1]) == 'x'; - return ExpressionValue(parameters.front()->getValue()); - } + while (end < currentLine.size() && (iswalnum(currentLine[end]) || currentLine[end] == '.')) + { + if (currentLine[end] == '.') + { + if (foundExp || foundPoint) + isValid = false; + foundPoint = true; + } else if (towlower(currentLine[end]) == 'h' && !foundExpSign) { + isHex = true; + } else if (towlower(currentLine[end]) == 'e' && !isHex) + { + if (foundExp) + { + isValid = false; + } else if (end+1 < currentLine.size() && (currentLine[end+1] == '+' || currentLine[end+1] == '-')){ + end++; + if (end+1 >= currentLine.size() || !iswalnum(currentLine[end+1])) + isValid = false; + foundExpSign = true; + } + foundExp = true; + } - if(!g_fileManager->hasOpenFile()) - { - Logger::queueError(Logger::Error,L"%s: no file opened", funcName); - return ExpressionValue(); - } - return ExpressionValue(g_fileManager->getVirtualAddress()); -} + end++; + } -ExpressionValue expLabelFuncOrga(const std::wstring& funcName, const std::vector<std::shared_ptr<Label>>& parameters) -{ - // return physical address of label parameter - if (parameters.size()) - { - Label* label = parameters.front().get(); - if (!label) - return ExpressionValue(); + bool isFloat = foundPoint || (foundExp && !isHex); - if (!label->hasPhysicalValue()) + if (!isFloat) { - Logger::queueError(Logger::Error,L"%s: parameter %s has no physical address", funcName, label->getName() ); - return ExpressionValue(); + int64_t value; + if (convertInteger(start,end,value) == false) + { + createTokenCurrentString(TokenType::NumberString,end-start); + return std::move(token); + } + + createToken(TokenType::Integer,end-start,value); + } else { // isFloat + double value; + if (isValid == false) + { + createToken(TokenType::Invalid,end-start,L"Invalid floating point number"); + return std::move(token); + } + + if (convertFloat(start,end,value) == false) + { + createTokenCurrentString(TokenType::NumberString,end-start); + return std::move(token); + } + + createToken(TokenType::Float,end-start,value); } - return ExpressionValue(parameters.front()->getPhysicalValue()); + return std::move(token); } - // return current physical address otherwise - if(!g_fileManager->hasOpenFile()) + // identifiers + bool isFirst = true; + while (pos < currentLine.size() && Global.symbolTable.isValidSymbolCharacter(currentLine[pos],isFirst)) { - Logger::queueError(Logger::Error,L"%s: no file opened", funcName); - return ExpressionValue(); + pos++; + isFirst = false; } - return ExpressionValue(g_fileManager->getPhysicalAddress()); -} -ExpressionValue expLabelFuncHeaderSize(const std::wstring& funcName, const std::vector<std::shared_ptr<Label>>& parameters) -{ - // return difference between physical and virtual address of label parameter - if (parameters.size()) + if (pos == linePos) { - Label* label = parameters.front().get(); - if (!label) - return ExpressionValue(); - - if (!label->hasPhysicalValue()) - { - Logger::queueError(Logger::Error,L"%s: parameter %s has no physical address", funcName, label->getName() ); - return ExpressionValue(); - } + std::wstring text = formatString(L"Invalid input '%c'",currentLine[pos]); + createToken(TokenType::Invalid,1,text); + return std::move(token); + } - return ExpressionValue(label->getValue() - label->getPhysicalValue()); + std::wstring text = currentLine.substr(linePos,pos-linePos); + bool textLowered = false; + // Lowercase is common, let's try to avoid a copy. + if (std::any_of(text.begin(), text.end(), ::iswupper)) + { + std::transform(text.begin(), text.end(), text.begin(), ::towlower); + textLowered = true; } - if(!g_fileManager->hasOpenFile()) + if (text == L"equ") { - Logger::queueError(Logger::Error,L"headersize: no file opened"); - return ExpressionValue(); + createToken(TokenType::Equ,pos-linePos); + equActive = true; + } else if (textLowered) { + createToken(TokenType::Identifier,pos-linePos,text); + } else { + createTokenCurrentString(TokenType::Identifier,pos-linePos); } - return ExpressionValue(g_fileManager->getHeaderSize()); + + return std::move(token); } -const ExpressionFunctionMap expressionFunctions = { - { L"version", { &expFuncVersion, 0, 0, ExpFuncSafety::Safe } }, - { L"endianness", { &expFuncEndianness, 0, 0, ExpFuncSafety::Unsafe } }, - { L"outputname", { &expFuncOutputName, 0, 0, ExpFuncSafety::Unsafe } }, - { L"fileexists", { &expFuncFileExists, 1, 1, ExpFuncSafety::Safe } }, - { L"filesize", { &expFuncFileSize, 1, 1, ExpFuncSafety::ConditionalUnsafe } }, - { L"tostring", { &expFuncToString, 1, 1, ExpFuncSafety::Safe } }, - { L"tohex", { &expFuncToHex, 1, 2, ExpFuncSafety::Safe } }, +bool FileTokenizer::init(TextFile* input) +{ + clearTokens(); - { L"int", { &expFuncInt, 1, 1, ExpFuncSafety::Safe } }, - { L"float", { &expFuncFloat, 1, 1, ExpFuncSafety::Safe } }, - { L"frac", { &expFuncFrac, 1, 1, ExpFuncSafety::Safe } }, - { L"abs", { &expFuncAbs, 1, 1, ExpFuncSafety::Safe } }, - { L"round", { &expFuncRound, 1, 1, ExpFuncSafety::Safe } }, - { L"min", { &expFuncMin, 1, std::numeric_limits<size_t>::max(), ExpFuncSafety::Safe } }, - { L"max", { &expFuncMax, 1, std::numeric_limits<size_t>::max(), ExpFuncSafety::Safe } }, + lineNumber = 1; + linePos = 0; + equActive = false; + currentLine = input->readLine(); - { L"strlen", { &expFuncStrlen, 1, 1, ExpFuncSafety::Safe } }, - { L"substr", { &expFuncSubstr, 3, 3, ExpFuncSafety::Safe } }, -#if ARMIPS_REGEXP - { L"regex_match", { &expFuncRegExMatch, 2, 2, ExpFuncSafety::Safe } }, - { L"regex_search", { &expFuncRegExSearch, 2, 2, ExpFuncSafety::Safe } }, - { L"regex_extract", { &expFuncRegExExtract, 2, 3, ExpFuncSafety::Safe } }, -#endif - { L"find", { &expFuncFind, 2, 3, ExpFuncSafety::Safe } }, - { L"rfind", { &expFuncRFind, 2, 3, ExpFuncSafety::Safe } }, + this->input = input; + if (input != nullptr && input->isOpen()) + { + while (!isInputAtEnd()) + { + bool addSeparator = true; - { L"readbyte", { &expFuncRead<uint8_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, - { L"readu8", { &expFuncRead<uint8_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, - { L"readu16", { &expFuncRead<uint16_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, - { L"readu32", { &expFuncRead<uint32_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, - { L"readu64", { &expFuncRead<uint64_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, - { L"reads8", { &expFuncRead<int8_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, - { L"reads16", { &expFuncRead<int16_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, - { L"reads32", { &expFuncRead<int32_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, - { L"reads64", { &expFuncRead<int64_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, - { L"readascii", { &expFuncReadAscii, 1, 3, ExpFuncSafety::ConditionalUnsafe } }, -}; + skipWhitespace(); + if (isContinuation(currentLine, linePos)) + { + linePos++; + skipWhitespace(); + if (linePos < currentLine.size()) + { + createToken(TokenType::Invalid,0, + L"Unexpected character after line continuation character"); + addToken(token); + } -extern const ExpressionLabelFunctionMap expressionLabelFunctions = -{ - { L"defined", { &expLabelFuncDefined, 1, 1, ExpFuncSafety::Unsafe } }, - { L"org", { &expLabelFuncOrg, 0, 1, ExpFuncSafety::Unsafe } }, - { L"orga", { &expLabelFuncOrga, 0, 1, ExpFuncSafety::Unsafe } }, - { L"headersize", { &expLabelFuncHeaderSize, 0, 1, ExpFuncSafety::Unsafe } }, -}; + addSeparator = false; + } else if(linePos < currentLine.size()) + { + addToken(std::move(loadToken())); + } -// file: Core/FileManager.cpp + if (linePos >= currentLine.size()) + { + if (addSeparator) + { + createToken(TokenType::Separator,0); + addToken(token); + } -inline uint64_t swapEndianness64(uint64_t value) -{ - return ((value & 0xFF) << 56) | ((value & 0xFF00) << 40) | ((value & 0xFF0000) << 24) | ((value & 0xFF000000) << 8) | - ((value & 0xFF00000000) >> 8) | ((value & 0xFF0000000000) >> 24) | - ((value & 0xFF000000000000) >> 40) | ((value & 0xFF00000000000000) >> 56); -} + if (input->atEnd()) + break; -inline uint32_t swapEndianness32(uint32_t value) -{ - return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24); + currentLine = input->readLine(); + linePos = 0; + lineNumber++; + } + } + + resetPosition(); + return true; + } + + return false; } -inline uint16_t swapEndianness16(uint16_t value) +// file: Util/ByteArray.cpp + +ByteArray::ByteArray() { - return ((value & 0xFF) << 8) | ((value & 0xFF00) >> 8); + data_ = nullptr; + size_ = allocatedSize_ = 0; } +ByteArray::ByteArray(const ByteArray& other) +{ + data_ = nullptr; + size_ = allocatedSize_ = 0; + append(other); +} -GenericAssemblerFile::GenericAssemblerFile(const std::wstring& fileName, int64_t headerSize, bool overwrite) +ByteArray::ByteArray(byte* data, size_t size) { - this->fileName = fileName; - this->headerSize = headerSize; - this->originalHeaderSize = headerSize; - this->seekPhysical(0); - mode = overwrite == true ? Create : Open; + data_ = nullptr; + size_ = allocatedSize_ = 0; + append(data,size); } -GenericAssemblerFile::GenericAssemblerFile(const std::wstring& fileName, const std::wstring& originalFileName, int64_t headerSize) +ByteArray::ByteArray(ByteArray&& other) { - this->fileName = fileName; - this->originalName = originalFileName; - this->headerSize = headerSize; - this->originalHeaderSize = headerSize; - this->seekPhysical(0); - mode = Copy; + data_ = other.data_; + size_ = other.size_; + allocatedSize_ = other.allocatedSize_; + other.data_ = nullptr; + other.allocatedSize_ = other.size_ = 0; } -bool GenericAssemblerFile::open(bool onlyCheck) +ByteArray::~ByteArray() { - headerSize = originalHeaderSize; - virtualAddress = headerSize; + free(data_); +} - if (onlyCheck == false) - { - // actually open the file - bool success; - switch (mode) - { - case Open: - success = handle.open(fileName,BinaryFile::ReadWrite); - if (success == false) - { - Logger::printError(Logger::FatalError,L"Could not open file %s",fileName); - return false; - } - return true; +ByteArray& ByteArray::operator=(ByteArray& other) +{ + free(data_); + data_ = nullptr; + size_ = allocatedSize_ = 0; + append(other); - case Create: - success = handle.open(fileName,BinaryFile::Write); - if (success == false) - { - Logger::printError(Logger::FatalError,L"Could not create file %s",fileName); - return false; - } - return true; + return *this; +} - case Copy: - success = copyFile(originalName,fileName); - if (success == false) - { - Logger::printError(Logger::FatalError,L"Could not copy file %s",originalName); - return false; - } +ByteArray& ByteArray::operator=(ByteArray&& other) +{ + data_ = other.data_; + size_ = other.size_; + allocatedSize_ = other.allocatedSize_; + other.data_ = nullptr; + other.allocatedSize_ = other.size_ = 0; + return *this; +} - success = handle.open(fileName,BinaryFile::ReadWrite); - if (success == false) - { - Logger::printError(Logger::FatalError,L"Could not create file %s",fileName); - return false; - } - return true; +void ByteArray::grow(size_t neededSize) +{ + if (neededSize < allocatedSize_) return; - default: - return false; - } + // align to next 0.5kb... it's a start + allocatedSize_ = ((neededSize+511)/512)*512; + if (data_ == nullptr) + { + data_ = (byte*) malloc(allocatedSize_); + } else { + data_ = (byte*) realloc(data_,allocatedSize_); } +} - // else only check if it can be done, don't actually do it permanently - bool success, exists; - BinaryFile temp; - switch (mode) - { - case Open: - success = temp.open(fileName,BinaryFile::ReadWrite); - if (success == false) - { - Logger::queueError(Logger::FatalError,L"Could not open file %s",fileName); - return false; - } - temp.close(); - return true; +size_t ByteArray::append(const ByteArray& other) +{ + size_t oldSize = size(); + size_t otherSize = other.size(); + grow(size()+otherSize); + memcpy(&data_[size_],other.data(),otherSize); + size_ += otherSize; + return oldSize; +} - case Create: - // if it exists, check if you can open it with read/write access - // otherwise open it with write access and remove it afterwards - exists = fileExists(fileName); - success = temp.open(fileName,exists ? BinaryFile::ReadWrite : BinaryFile::Write); - if (success == false) - { - Logger::queueError(Logger::FatalError,L"Could not create file %s",fileName); - return false; - } - temp.close(); +size_t ByteArray::append(void* data, size_t size) +{ + size_t oldSize = this->size(); + grow(this->size()+size); + memcpy(&data_[size_],data,size); + this->size_ += size; + return oldSize; +} - if (exists == false) - deleteFile(fileName); +void ByteArray::replaceBytes(size_t pos, byte* data, size_t size) +{ + for (size_t i = 0; i < size; i++) + { + replaceByte(pos+i,data[i]); + } +} - return true; +void ByteArray::reserveBytes(size_t count, byte value) +{ + grow(this->size()+count); + memset(&data_[size_],value,count); + size_ += count; +} - case Copy: - // check original file - success = temp.open(originalName,BinaryFile::ReadWrite); - if (success == false) - { - Logger::queueError(Logger::FatalError,L"Could not open file %s",originalName); - return false; - } - temp.close(); +void ByteArray::alignSize(size_t alignment) +{ + if (alignment <= 0) return; - // check new file, same as create - exists = fileExists(fileName); - success = temp.open(fileName,exists ? BinaryFile::ReadWrite : BinaryFile::Write); - if (success == false) - { - Logger::queueError(Logger::FatalError,L"Could not create file %s",fileName); - return false; - } - temp.close(); + while (size_ % alignment) + { + appendByte(0); + } +} - if (exists == false) - deleteFile(fileName); +void ByteArray::resize(size_t newSize) +{ + grow(newSize); + size_ = newSize; +} - return true; +ByteArray ByteArray::mid(size_t start, ssize_t length) +{ + ByteArray ret; - default: - return false; - }; + if (length < 0) + length = size_-start; - return false; + if (start >= size_) + return ret; + + ret.grow(length); + ret.size_ = length; + memcpy(ret.data_,&data_[start],length); + return ret; } -bool GenericAssemblerFile::write(void* data, size_t length) +ByteArray ByteArray::fromFile(const std::wstring& fileName, long start, size_t size) { - if (isOpen() == false) - return false; + ByteArray ret; - size_t len = handle.write(data,length); - virtualAddress += len; - return len == length; -} + FILE* input = openFile(fileName,OpenFileMode::ReadBinary); + if (input == nullptr) + return ret; -bool GenericAssemblerFile::seekVirtual(int64_t virtualAddress) -{ - if (virtualAddress - headerSize < 0) + fseek(input,0,SEEK_END); + long fileSize = ftell(input); + + if (start >= fileSize) { - Logger::queueError(Logger::Error,L"Seeking to virtual address with negative physical address"); - return false; + fclose(input); + return ret; } - if (virtualAddress < 0) - Logger::queueError(Logger::Warning,L"Seeking to negative virtual address"); - this->virtualAddress = virtualAddress; - int64_t physicalAddress = virtualAddress-headerSize; + if (size == 0 || start+(long)size > fileSize) + size = fileSize-start; - if (isOpen()) - handle.setPos((long)physicalAddress); + fseek(input,start,SEEK_SET); - return true; + ret.grow(size); + ret.size_ = fread(ret.data(),1,size,input); + fclose(input); + + return ret; } -bool GenericAssemblerFile::seekPhysical(int64_t physicalAddress) +bool ByteArray::toFile(const std::wstring& fileName) { - if (physicalAddress < 0) - { - Logger::queueError(Logger::Error,L"Seeking to negative physical address"); - return false; - } - if (physicalAddress + headerSize < 0) - Logger::queueError(Logger::Warning,L"Seeking to physical address with negative virtual address"); + FILE* output = openFile(fileName,OpenFileMode::WriteBinary); + if (output == nullptr) return false; + size_t length = fwrite(data_,1,size_,output); + fclose(output); + return length == size_; +} - virtualAddress = physicalAddress+headerSize; +// file: Util/CRC.cpp +#include <stdio.h> - if (isOpen()) - handle.setPos((long)physicalAddress); +const unsigned short Crc16Table[] = /* CRC lookup table */ +{ + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 +}; - return true; -} +const unsigned int Crc32Table[256] = { + 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3, + 0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, + 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7, + 0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5, + 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B, + 0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59, + 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F, + 0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D, + 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433, + 0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01, + 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457, + 0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65, + 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB, + 0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, + 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F, + 0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD, + 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683, + 0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1, + 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7, + 0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5, + 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B, + 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79, + 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F, + 0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D, + 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713, + 0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21, + 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777, + 0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, + 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB, + 0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9, + 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF, + 0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D +}; +unsigned short getCrc16(unsigned char* Source, size_t len) +{ + unsigned short crc = 0xFFFF; + while (len--) + { + crc = (crc >> 8) ^ Crc16Table[(crc ^ *Source++) & 0xFF]; + } + return crc; +} -FileManager::FileManager() +unsigned int getCrc32(unsigned char* Source, size_t len) { - // detect own endianness - volatile union - { - uint32_t i; - uint8_t c[4]; - } u; - u.c[3] = 0xAA; - u.c[2] = 0xBB; - u.c[1] = 0xCC; - u.c[0] = 0xDD; + unsigned int crc = 0xFFFFFFFF; - if (u.i == 0xDDCCBBAA) - ownEndianness = Endianness::Big; - else if (u.i == 0xAABBCCDD) - ownEndianness = Endianness::Little; - else - Logger::printError(Logger::Error,L"Running on unknown endianness"); + while (len--) + { + crc = (crc >> 8) ^ Crc32Table[(crc & 0xFF) ^ *Source++]; + } - reset(); + return crc ^ 0xffffffff; } -FileManager::~FileManager() -{ +unsigned int getChecksum(unsigned char* Source, size_t len) +{ + int checksum = 0; + for (size_t i = 0; i < len; i++) + checksum += *Source++; + return checksum; } -void FileManager::reset() +// file: Util/EncodingTable.cpp + +#define MAXHEXLENGTH 32 + +Trie::Trie() { - activeFile = nullptr; - setEndianness(Endianness::Little); + Node root { 0, false, 0 }; + nodes.push_back(root); } -bool FileManager::checkActiveFile() +void Trie::insert(const wchar_t* text, size_t value) { - if (activeFile == nullptr) + size_t node = 0; // root node + + // traverse existing nodes + while (*text != 0) { - Logger::queueError(Logger::Error,L"No file opened"); - return false; + LookupEntry lookupEntry { node, *text }; + auto it = lookup.find(lookupEntry); + if (it == lookup.end()) + break; + + node = it->second; + text++; } - return true; -} -bool FileManager::openFile(std::shared_ptr<AssemblerFile> file, bool onlyCheck) -{ - if (activeFile != nullptr) + // add new nodes as necessary + while (*text != 0) { - Logger::queueError(Logger::Warning,L"File not closed before opening a new one"); - activeFile->close(); + Node newNode { nodes.size(), false, 0 }; + nodes.push_back(newNode); + + LookupEntry lookupEntry { node, *text }; + lookup[lookupEntry] = newNode.index; + node = newNode.index; + text++; } - activeFile = file; - return activeFile->open(onlyCheck); + // set value + nodes[node].hasValue = true; + nodes[node].value = value; } -void FileManager::addFile(std::shared_ptr<AssemblerFile> file) +void Trie::insert(wchar_t character, size_t value) { - files.push_back(file); + wchar_t str[2]; + str[0] = character; + str[1] = 0; + insert(str,value); } -void FileManager::closeFile() +bool Trie::findLongestPrefix(const wchar_t* text, size_t& result) { - if (activeFile == nullptr) + size_t node = 0; // root node + size_t valueNode = 0; // remember last node that had a value + + while (*text != 0) { - Logger::queueError(Logger::Warning,L"No file opened"); - return; - } + if (nodes[node].hasValue) + valueNode = node; - activeFile->close(); - activeFile = nullptr; -} + LookupEntry lookupEntry { node, *text++ }; + auto it = lookup.find(lookupEntry); -bool FileManager::write(void* data, size_t length) -{ - if (checkActiveFile() == false) - return false; + if (it == lookup.end()) + break; - if (activeFile->isOpen() == false) - { - Logger::queueError(Logger::Error,L"No file opened"); - return false; + node = it->second; } - return activeFile->write(data,length); -} + if (nodes[node].hasValue) + valueNode = node; -bool FileManager::writeU8(uint8_t data) -{ - return write(&data,1); + result = nodes[valueNode].value; + return nodes[valueNode].hasValue; } -bool FileManager::writeU16(uint16_t data) +EncodingTable::EncodingTable() { - if (endianness != ownEndianness) - data = swapEndianness16(data); - return write(&data,2); } -bool FileManager::writeU32(uint32_t data) +EncodingTable::~EncodingTable() { - if (endianness != ownEndianness) - data = swapEndianness32(data); - return write(&data,4); } -bool FileManager::writeU64(uint64_t data) +void EncodingTable::clear() { - if (endianness != ownEndianness) - data = swapEndianness64(data); - - return write(&data,8); + hexData.clear(); + entries.clear(); } -int64_t FileManager::getVirtualAddress() +int parseHexString(std::wstring& hex, unsigned char* dest) { - if (activeFile == nullptr) - return -1; - return activeFile->getVirtualAddress(); -} + for (size_t i = 0; i < hex.size(); i++) + { + wchar_t source = towlower(hex[i]); + int value; -int64_t FileManager::getPhysicalAddress() -{ - if (activeFile == nullptr) - return -1; - return activeFile->getPhysicalAddress(); -} - -int64_t FileManager::getHeaderSize() -{ - if (activeFile == nullptr) - return -1; - return activeFile->getHeaderSize(); -} - -bool FileManager::seekVirtual(int64_t virtualAddress) -{ - if (checkActiveFile() == false) - return false; + if (source >= 'a' && source <= 'f') + { + value = source-'a'+10; + } else if (source >= '0' && source <= '9') + { + value = source-'0'; + } else { + return -1; + } - bool result = activeFile->seekVirtual(virtualAddress); - if (result && Global.memoryMode) - { - int sec = Global.symbolTable.findSection(virtualAddress); - if (sec != -1) - Global.Section = sec; + size_t index = i/2; + if (i % 2) + dest[index] = (dest[index] << 4) | value; + else + dest[index] = value; } - return result; + return (int) hex.size()/2; } -bool FileManager::seekPhysical(int64_t virtualAddress) +bool EncodingTable::load(const std::wstring& fileName, TextFile::Encoding encoding) { - if (checkActiveFile() == false) - return false; - return activeFile->seekPhysical(virtualAddress); -} + unsigned char hexBuffer[MAXHEXLENGTH]; -bool FileManager::advanceMemory(size_t bytes) -{ - if (checkActiveFile() == false) + TextFile input; + if (input.open(fileName,TextFile::Read,encoding) == false) return false; - int64_t pos = activeFile->getVirtualAddress(); - return activeFile->seekVirtual(pos+bytes); -} - -// file: Core/Misc.cpp -#include <iostream> + hexData.clear(); + entries.clear(); + setTerminationEntry((unsigned char*)"\0",1); -#ifdef _WIN32 -#include <windows.h> -#endif + while (!input.atEnd()) + { + std::wstring line = input.readLine(); + if (line.empty() || line[0] == '*') continue; -std::vector<Logger::QueueEntry> Logger::queue; -std::vector<std::wstring> Logger::errors; -bool Logger::error = false; -bool Logger::fatalError = false; -bool Logger::errorOnWarning = false; -bool Logger::silent = false; -int Logger::suppressLevel = 0; + if (line[0] == '/') + { + std::wstring hex = line.substr(1); + if (hex.empty() || hex.length() > 2*MAXHEXLENGTH) + { + // error + continue; + } -std::wstring Logger::formatError(ErrorType type, const wchar_t* text) -{ - std::wstring position; + int length = parseHexString(hex,hexBuffer); + if (length == -1) + { + // error + continue; + } - if (Global.memoryMode == false && Global.FileInfo.FileList.size() > 0) - { - std::wstring& fileName = Global.FileInfo.FileList[Global.FileInfo.FileNum]; - position = formatString(L"%s(%d) ",fileName,Global.FileInfo.LineNumber); - } + setTerminationEntry(hexBuffer,length); + } else { + size_t pos = line.find(L'='); + std::wstring hex = line.substr(0,pos); + std::wstring value = line.substr(pos+1); - switch (type) - { - case Warning: - return formatString(L"%swarning: %s",position,text); - case Error: - return formatString(L"%serror: %s",position,text); - case FatalError: - return formatString(L"%sfatal error: %s",position,text); - case Notice: - return formatString(L"%snotice: %s",position,text); - } + if (hex.empty() || value.empty() || hex.length() > 2*MAXHEXLENGTH) + { + // error + continue; + } - return L""; -} + int length = parseHexString(hex,hexBuffer); + if (length == -1) + { + // error + continue; + } -void Logger::setFlags(ErrorType type) -{ - switch (type) - { - case Warning: - if (errorOnWarning) - error = true; - break; - case Error: - error = true; - break; - case FatalError: - error = true; - fatalError = true; - break; - case Notice: - break; + addEntry(hexBuffer,length,value); + } } -} -void Logger::clear() -{ - queue.clear(); - errors.clear(); - error = false; - fatalError = false; - errorOnWarning = false; - silent = false; + return true; } -void Logger::printLine(const std::wstring& text) +void EncodingTable::addEntry(unsigned char* hex, size_t hexLength, const std::wstring& value) { - if (suppressLevel) + if (value.size() == 0) return; - std::wcout << text << std::endl; + // insert into trie + size_t index = entries.size(); + lookup.insert(value.c_str(),index); -#if defined(_MSC_VER) && defined(_DEBUG) - OutputDebugStringW(text.c_str()); - OutputDebugStringW(L"\n"); -#endif + // add entry + TableEntry entry; + entry.hexPos = hexData.append(hex,hexLength); + entry.hexLen = hexLength; + entry.valueLen = value.size(); + + entries.push_back(entry); } -void Logger::printLine(const std::string& text) +void EncodingTable::addEntry(unsigned char* hex, size_t hexLength, wchar_t value) { - if (suppressLevel) + if (value == '\0') return; - std::cout << text << std::endl; + // insert into trie + size_t index = entries.size(); + lookup.insert(value,index); -#if defined(_MSC_VER) && defined(_DEBUG) - OutputDebugStringA(text.c_str()); - OutputDebugStringA("\n"); -#endif -} + // add entry + TableEntry entry; + entry.hexPos = hexData.append(hex,hexLength); + entry.hexLen = hexLength; + entry.valueLen = 1; -void Logger::print(const std::wstring& text) -{ - if (suppressLevel) - return; + entries.push_back(entry); - std::wcout << text; +} -#if defined(_MSC_VER) && defined(_DEBUG) - OutputDebugStringW(text.c_str()); -#endif +void EncodingTable::setTerminationEntry(unsigned char* hex, size_t hexLength) +{ + terminationEntry.hexPos = hexData.append(hex,hexLength); + terminationEntry.hexLen = hexLength; + terminationEntry.valueLen = 0; } -void Logger::printError(ErrorType type, const std::wstring& text) +ByteArray EncodingTable::encodeString(const std::wstring& str, bool writeTermination) { - if (suppressLevel) - return; + ByteArray result; - std::wstring errorText = formatError(type,text.c_str()); - errors.push_back(errorText); + size_t pos = 0; + while (pos < str.size()) + { + size_t index; + if (lookup.findLongestPrefix(str.c_str()+pos,index) == false) + { + // error + return ByteArray(); + } - if (!silent) - printLine(errorText); + TableEntry& entry = entries[index]; + for (size_t i = 0; i < entry.hexLen; i++) + { + result.appendByte(hexData[entry.hexPos+i]); + } - setFlags(type); + pos += entry.valueLen; + } + + if (writeTermination) + { + TableEntry& entry = terminationEntry; + for (size_t i = 0; i < entry.hexLen; i++) + { + result.appendByte(hexData[entry.hexPos+i]); + } + } + + return result; } -void Logger::printError(ErrorType type, const wchar_t* text) +ByteArray EncodingTable::encodeTermination() { - if (suppressLevel) - return; - - std::wstring errorText = formatError(type,text); - errors.push_back(errorText); + ByteArray result; - if (!silent) - printLine(errorText); + TableEntry& entry = terminationEntry; + for (size_t i = 0; i < entry.hexLen; i++) + { + result.appendByte(hexData[entry.hexPos+i]); + } - setFlags(type); + return result; } -void Logger::queueError(ErrorType type, const std::wstring& text) -{ - if (suppressLevel) - return; - - QueueEntry entry; - entry.type = type; - entry.text = formatError(type,text.c_str()); - queue.push_back(entry); -} - -void Logger::queueError(ErrorType type, const wchar_t* text) -{ - if (suppressLevel) - return; - - QueueEntry entry; - entry.type = type; - entry.text = formatError(type,text); - queue.push_back(entry); -} - -void Logger::printQueue() -{ - for (size_t i = 0; i < queue.size(); i++) - { - errors.push_back(queue[i].text); - - if (!silent) - printLine(queue[i].text); - - setFlags(queue[i].type); - } -} - -void TempData::start() -{ - if (file.getFileName().empty() == false) - { - if (file.open(TextFile::Write) == false) - { - Logger::printError(Logger::Error,L"Could not open temp file %s.",file.getFileName()); - return; - } - - size_t fileCount = Global.FileInfo.FileList.size(); - size_t lineCount = Global.FileInfo.TotalLineCount; - size_t labelCount = Global.symbolTable.getLabelCount(); - size_t equCount = Global.symbolTable.getEquationCount(); - - file.writeFormat(L"; %d %S included\n",fileCount,fileCount == 1 ? "file" : "files"); - file.writeFormat(L"; %d %S\n",lineCount,lineCount == 1 ? "line" : "lines"); - file.writeFormat(L"; %d %S\n",labelCount,labelCount == 1 ? "label" : "labels"); - file.writeFormat(L"; %d %S\n\n",equCount,equCount == 1 ? "equation" : "equations"); - for (size_t i = 0; i < fileCount; i++) - { - file.writeFormat(L"; %S\n",Global.FileInfo.FileList[i]); - } - file.writeLine(""); - } -} - -void TempData::end() -{ - if (file.isOpen()) - file.close(); -} - -void TempData::writeLine(int64_t memoryAddress, const std::wstring& text) -{ - if (file.isOpen()) - { - wchar_t hexbuf[10] = {0}; - swprintf(hexbuf, 10, L"%08X ", (int32_t) memoryAddress); - std::wstring str = hexbuf + text; - while (str.size() < 70) - str += ' '; - - str += formatString(L"; %S line %d", - Global.FileInfo.FileList[Global.FileInfo.FileNum],Global.FileInfo.LineNumber); - - file.writeLine(str); - } -} - -// file: Core/SymbolData.cpp -#include <algorithm> - -SymbolData::SymbolData() -{ - clear(); -} +// file: Util/FileClasses.cpp -void SymbolData::clear() +const wchar_t SjisToUnicodeTable1[] = { - enabled = true; - nocashSymFileName.clear(); - modules.clear(); - files.clear(); - currentModule = 0; - currentFunction = -1; - - SymDataModule defaultModule; - defaultModule.file = nullptr; - modules.push_back(defaultModule); -} + // 0X0080 to 0X00FF + 0x0080, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67, 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F, + 0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77, 0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F, + 0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87, 0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F, + 0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97, 0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, +}; -struct NocashSymEntry +const wchar_t SjisToUnicodeTable2[] = { - int64_t address; - std::wstring text; - - bool operator<(const NocashSymEntry& other) const - { - if (address != other.address) - return address < other.address; - return text < other.text; - } + // 0X8100 to 0X81FF + 0x3000, 0x3001, 0x3002, 0xFF0C, 0xFF0E, 0x30FB, 0xFF1A, 0xFF1B, 0xFF1F, 0xFF01, 0x309B, 0x309C, 0x00B4, 0xFF40, 0x00A8, 0xFF3E, + 0xFFE3, 0xFF3F, 0x30FD, 0x30FE, 0x309D, 0x309E, 0x3003, 0x4EDD, 0x3005, 0x3006, 0x3007, 0x30FC, 0x2015, 0x2010, 0xFF0F, 0xFF3C, + 0xFF5E, 0x2225, 0xFF5C, 0x2026, 0x2025, 0x2018, 0x2019, 0x201C, 0x201D, 0xFF08, 0xFF09, 0x3014, 0x3015, 0xFF3B, 0xFF3D, 0xFF5B, + 0xFF5D, 0x3008, 0x3009, 0x300A, 0x300B, 0x300C, 0x300D, 0x300E, 0x300F, 0x3010, 0x3011, 0xFF0B, 0xFF0D, 0x00B1, 0x00D7, 0xFFFF, + 0x00F7, 0xFF1D, 0x2260, 0xFF1C, 0xFF1E, 0x2266, 0x2267, 0x221E, 0x2234, 0x2642, 0x2640, 0x00B0, 0x2032, 0x2033, 0x2103, 0xFFE5, + 0xFF04, 0xFFE0, 0xFFE1, 0xFF05, 0xFF03, 0xFF06, 0xFF0A, 0xFF20, 0x00A7, 0x2606, 0x2605, 0x25CB, 0x25CF, 0x25CE, 0x25C7, 0x25C6, + 0x25A1, 0x25A0, 0x25B3, 0x25B2, 0x25BD, 0x25BC, 0x203B, 0x3012, 0x2192, 0x2190, 0x2191, 0x2193, 0x3013, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2208, 0x220B, 0x2286, 0x2287, 0x2282, 0x2283, 0x222A, 0x2229, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2227, 0x2228, 0xFFE2, 0x21D2, 0x21D4, 0x2200, 0x2203, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2220, 0x22A5, 0x2312, 0x2202, 0x2207, 0x2261, + 0x2252, 0x226A, 0x226B, 0x221A, 0x223D, 0x221D, 0x2235, 0x222B, 0x222C, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0x212B, 0x2030, 0x266F, 0x266D, 0x266A, 0x2020, 0x2021, 0x00B6, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x25EF, 0xFFFF, 0xFFFF, 0xFFFF, + // 0X8200 to 0X82FF + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFF10, + 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, 0xFF18, 0xFF19, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, + 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, + 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x3041, + 0x3042, 0x3043, 0x3044, 0x3045, 0x3046, 0x3047, 0x3048, 0x3049, 0x304A, 0x304B, 0x304C, 0x304D, 0x304E, 0x304F, 0x3050, 0x3051, + 0x3052, 0x3053, 0x3054, 0x3055, 0x3056, 0x3057, 0x3058, 0x3059, 0x305A, 0x305B, 0x305C, 0x305D, 0x305E, 0x305F, 0x3060, 0x3061, + 0x3062, 0x3063, 0x3064, 0x3065, 0x3066, 0x3067, 0x3068, 0x3069, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F, 0x3070, 0x3071, + 0x3072, 0x3073, 0x3074, 0x3075, 0x3076, 0x3077, 0x3078, 0x3079, 0x307A, 0x307B, 0x307C, 0x307D, 0x307E, 0x307F, 0x3080, 0x3081, + 0x3082, 0x3083, 0x3084, 0x3085, 0x3086, 0x3087, 0x3088, 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308E, 0x308F, 0x3090, 0x3091, + 0x3092, 0x3093, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + // 0X8300 to 0X83FF + 0x30A1, 0x30A2, 0x30A3, 0x30A4, 0x30A5, 0x30A6, 0x30A7, 0x30A8, 0x30A9, 0x30AA, 0x30AB, 0x30AC, 0x30AD, 0x30AE, 0x30AF, 0x30B0, + 0x30B1, 0x30B2, 0x30B3, 0x30B4, 0x30B5, 0x30B6, 0x30B7, 0x30B8, 0x30B9, 0x30BA, 0x30BB, 0x30BC, 0x30BD, 0x30BE, 0x30BF, 0x30C0, + 0x30C1, 0x30C2, 0x30C3, 0x30C4, 0x30C5, 0x30C6, 0x30C7, 0x30C8, 0x30C9, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF, 0x30D0, + 0x30D1, 0x30D2, 0x30D3, 0x30D4, 0x30D5, 0x30D6, 0x30D7, 0x30D8, 0x30D9, 0x30DA, 0x30DB, 0x30DC, 0x30DD, 0x30DE, 0x30DF, 0xFFFF, + 0x30E0, 0x30E1, 0x30E2, 0x30E3, 0x30E4, 0x30E5, 0x30E6, 0x30E7, 0x30E8, 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EE, 0x30EF, + 0x30F0, 0x30F1, 0x30F2, 0x30F3, 0x30F4, 0x30F5, 0x30F6, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0391, + 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, + 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03B1, + 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, + 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + // 0X8400 to 0X84FF + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, + 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, + 0x042F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0451, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0xFFFF, + 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, + 0x044E, 0x044F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2500, + 0x2502, 0x250C, 0x2510, 0x2518, 0x2514, 0x251C, 0x252C, 0x2524, 0x2534, 0x253C, 0x2501, 0x2503, 0x250F, 0x2513, 0x251B, 0x2517, + 0x2523, 0x2533, 0x252B, 0x253B, 0x254B, 0x2520, 0x252F, 0x2528, 0x2537, 0x253F, 0x251D, 0x2530, 0x2525, 0x2538, 0x2542, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, }; -void SymbolData::writeNocashSym() -{ - if (nocashSymFileName.empty()) - return; - - std::vector<NocashSymEntry> entries; - for (size_t k = 0; k < modules.size(); k++) - { - SymDataModule& module = modules[k]; - for (size_t i = 0; i < module.symbols.size(); i++) - { - SymDataSymbol& sym = module.symbols[i]; - - size_t size = 0; - for (size_t f = 0; f < module.functions.size(); f++) - { - if (module.functions[f].address == sym.address) - { - size = module.functions[f].size; - break; - } - } - - NocashSymEntry entry; - entry.address = sym.address; - - if (size != 0 && nocashSymVersion >= 2) - entry.text = formatString(L"%s,%08X",sym.name,size); - else - entry.text = sym.name; - - if (nocashSymVersion == 1) - std::transform(entry.text.begin(), entry.text.end(), entry.text.begin(), ::towlower); - - entries.push_back(entry); - } - - for (const SymDataData& data: module.data) - { - NocashSymEntry entry; - entry.address = data.address; - - switch (data.type) - { - case Data8: - entry.text = formatString(L".byt:%04X",data.size); - break; - case Data16: - entry.text = formatString(L".wrd:%04X",data.size); - break; - case Data32: - entry.text = formatString(L".dbl:%04X",data.size); - break; - case Data64: - entry.text = formatString(L".dbl:%04X",data.size); - break; - case DataAscii: - entry.text = formatString(L".asc:%04X",data.size); - break; - } - - entries.push_back(entry); - } - } - - std::sort(entries.begin(),entries.end()); - - TextFile file; - if (file.open(nocashSymFileName,TextFile::Write,TextFile::ASCII) == false) - { - Logger::printError(Logger::Error,L"Could not open sym file %s.",file.getFileName()); - return; - } - file.writeLine(L"00000000 0"); - - for (size_t i = 0; i < entries.size(); i++) - { - file.writeFormat(L"%08X %s\n",entries[i].address,entries[i].text); - } - - file.write("\x1A"); - file.close(); -} - -void SymbolData::write() -{ - writeNocashSym(); -} - -void SymbolData::addLabel(int64_t memoryAddress, const std::wstring& name) -{ - if (!enabled) - return; - - SymDataSymbol sym; - sym.address = memoryAddress; - sym.name = name; - - for (SymDataSymbol& symbol: modules[currentModule].symbols) - { - if (symbol.address == sym.address && symbol.name == sym.name) - return; - } - - modules[currentModule].symbols.push_back(sym); -} - -void SymbolData::addData(int64_t address, size_t size, DataType type) -{ - if (!enabled) - return; - - SymDataData data; - data.address = address; - data.size = size; - data.type = type; - modules[currentModule].data.insert(data); -} - -size_t SymbolData::addFileName(const std::wstring& fileName) -{ - for (size_t i = 0; i < files.size(); i++) - { - if (files[i] == fileName) - return i; - } - - files.push_back(fileName); - return files.size()-1; -} - -void SymbolData::startModule(AssemblerFile* file) -{ - for (size_t i = 0; i < modules.size(); i++) - { - if (modules[i].file == file) - { - currentModule = i; - return; - } - } - - SymDataModule module; - module.file = file; - modules.push_back(module); - currentModule = modules.size()-1; -} - -void SymbolData::endModule(AssemblerFile* file) -{ - if (modules[currentModule].file != file) - return; - - if (currentModule == 0) - { - Logger::printError(Logger::Error,L"No module opened"); - return; - } - - if (currentFunction != -1) - { - Logger::printError(Logger::Error,L"Module closed before function end"); - currentFunction = -1; - } - - currentModule = 0; -} - -void SymbolData::startFunction(int64_t address) -{ - if (currentFunction != -1) - { - endFunction(address); - } - - currentFunction = modules[currentModule].functions.size(); - - SymDataFunction func; - func.address = address; - func.size = 0; - modules[currentModule].functions.push_back(func); -} - -void SymbolData::endFunction(int64_t address) -{ - if (currentFunction == -1) - { - Logger::printError(Logger::Error,L"Not inside a function"); - return; - } - - SymDataFunction& func = modules[currentModule].functions[currentFunction]; - func.size = (size_t) (address-func.address); - currentFunction = -1; -} - -// file: Core/SymbolTable.cpp - -const wchar_t validSymbolCharacters[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_."; - -bool operator<(SymbolKey const& lhs, SymbolKey const& rhs) -{ - if (lhs.file != rhs.file) - return lhs.file < rhs.file; - if (lhs.section != rhs.section) - return lhs.section < rhs.section; - return lhs.name.compare(rhs.name) < 0; -} - -SymbolTable::SymbolTable() -{ - uniqueCount = 0; -} - -SymbolTable::~SymbolTable() -{ - clear(); -} - -void SymbolTable::clear() -{ - symbols.clear(); - labels.clear(); - equationsCount = 0; - uniqueCount = 0; -} - -void SymbolTable::setFileSectionValues(const std::wstring& symbol, int& file, int& section) -{ - if (symbol[0] == '@') - { - if (symbol[1] != '@') - { - // static label, @. the section doesn't matter - section = -1; - } else { - // local label, @@. the file doesn't matter - file = -1; - } - } else { - // global label. neither file nor section matters - file = section = -1; - } -} - -std::shared_ptr<Label> SymbolTable::getLabel(const std::wstring& symbol, int file, int section) -{ - if (isValidSymbolName(symbol) == false) - return nullptr; - - int actualSection = section; - setFileSectionValues(symbol,file,section); - SymbolKey key = { symbol, file, section }; - - // find label, create new one if it doesn't exist - auto it = symbols.find(key); - if (it == symbols.end()) - { - SymbolInfo value = { LabelSymbol, labels.size() }; - symbols[key] = value; - - std::shared_ptr<Label> result = std::make_shared<Label>(symbol); - if (section == actualSection) - result->setSection(section); // local, set section of parent - else - result->setSection(actualSection+1); // global, set section of children - labels.push_back(result); - return result; - } - - // make sure not to match symbols that aren't labels - if (it->second.type != LabelSymbol) - return nullptr; - - return labels[it->second.index]; -} - -bool SymbolTable::symbolExists(const std::wstring& symbol, int file, int section) -{ - if (isValidSymbolName(symbol) == false) - return false; - - setFileSectionValues(symbol,file,section); - - SymbolKey key = { symbol, file, section }; - auto it = symbols.find(key); - return it != symbols.end(); -} - -bool SymbolTable::isValidSymbolName(const std::wstring& symbol) -{ - size_t size = symbol.size(); - size_t start = 0; - - // don't match empty names - if (size == 0 || symbol.compare(L"@") == 0 || symbol.compare(L"@@") == 0) - return false; - - if (symbol[0] == '@') - { - start++; - if (size > 1 && symbol[1] == '@') - start++; - } - - if (symbol[start] >= '0' && symbol[start] <= '9') - return false; - - for (size_t i = start; i < size; i++) - { - if (wcschr(validSymbolCharacters,symbol[i]) == nullptr) - return false; - } - - return true; -} - -bool SymbolTable::isValidSymbolCharacter(wchar_t character, bool first) -{ - if ((character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z')) return true; - if (!first && (character >= '0' && character <= '9')) return true; - if (character == '_' || character == '.') return true; - if (character == '@') return true; - return false; -} - -bool SymbolTable::addEquation(const std::wstring& name, int file, int section, size_t referenceIndex) -{ - if (isValidSymbolName(name) == false) - return false; - - if (symbolExists(name,file,section)) - return false; - - setFileSectionValues(name,file,section); - - SymbolKey key = { name, file, section }; - SymbolInfo value = { EquationSymbol, referenceIndex }; - symbols[key] = value; - - equationsCount++; - return true; -} - -bool SymbolTable::findEquation(const std::wstring& name, int file, int section, size_t& dest) -{ - setFileSectionValues(name,file,section); - - SymbolKey key = { name, file, section }; - auto it = symbols.find(key); - if (it == symbols.end() || it->second.type != EquationSymbol) - return false; - - dest = it->second.index; - return true; -} - -// TODO: better -std::wstring SymbolTable::getUniqueLabelName(bool local) -{ - std::wstring name = formatString(L"__armips_label_%08x__",uniqueCount++); - if (local) - name = L"@@" + name; - - generatedLabels.insert(name); - return name; -} - -void SymbolTable::addLabels(const std::vector<LabelDefinition>& labels) -{ - for (const LabelDefinition& def: labels) - { - if (!isValidSymbolName(def.name)) - continue; - - std::shared_ptr<Label> label = getLabel(def.name,Global.FileInfo.FileNum,Global.Section); - if (label == nullptr) - continue; - - label->setOriginalName(def.originalName); - - if (isLocalSymbol(def.name) == false) - Global.Section++; - - label->setDefined(true); - label->setValue(def.value); - } -} - -int SymbolTable::findSection(int64_t address) -{ - int64_t smallestBefore = -1; - int64_t smallestDiff = 0x7FFFFFFF; - - for (auto& lab: labels) - { - int diff = address-lab->getValue(); - if (diff >= 0 && diff < smallestDiff) - { - smallestDiff = diff; - smallestBefore = lab->getSection(); - } - } - - return smallestBefore; -} - -// file: Parser/DirectivesParser.cpp - -#include <initializer_list> -#include <algorithm> - -std::unique_ptr<CAssemblerCommand> parseDirectiveOpen(Parser& parser, int flags) -{ - std::vector<Expression> list; - if (parser.parseExpressionList(list,2,3) == false) - return nullptr; - - int64_t memoryAddress; - std::wstring inputName, outputName; - - if (list[0].evaluateString(inputName,false) == false) - return nullptr; - - if (list.back().evaluateInteger(memoryAddress) == false) - return nullptr; - - auto file = ::make_unique<CDirectiveFile>(); - if (list.size() == 3) - { - if (list[1].evaluateString(outputName,false) == false) - return nullptr; - - file->initCopy(inputName,outputName,memoryAddress); - return file; - } else { - file->initOpen(inputName,memoryAddress); - return file; - } -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveCreate(Parser& parser, int flags) -{ - std::vector<Expression> list; - if (parser.parseExpressionList(list,2,2) == false) - return nullptr; - - int64_t memoryAddress; - std::wstring inputName, outputName; - - if (list[0].evaluateString(inputName,false) == false) - return nullptr; - - if (list.back().evaluateInteger(memoryAddress) == false) - return nullptr; - - auto file = ::make_unique<CDirectiveFile>(); - file->initCreate(inputName,memoryAddress); - return file; -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveClose(Parser& parser, int flags) -{ - auto file = ::make_unique<CDirectiveFile>(); - file->initClose(); - return file; -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveIncbin(Parser& parser, int flags) -{ - std::vector<Expression> list; - if (parser.parseExpressionList(list,1,3) == false) - return nullptr; - - std::wstring fileName; - if (list[0].evaluateString(fileName,false) == false) - return nullptr; - - auto incbin = ::make_unique<CDirectiveIncbin>(fileName); - if (list.size() >= 2) - incbin->setStart(list[1]); - - if (list.size() == 3) - incbin->setSize(list[2]); - - return incbin; -} - -std::unique_ptr<CAssemblerCommand> parseDirectivePosition(Parser& parser, int flags) -{ - Expression exp = parser.parseExpression(); - if (exp.isLoaded() == false) - return nullptr; - - CDirectivePosition::Type type; - switch (flags & DIRECTIVE_USERMASK) - { - case DIRECTIVE_POS_PHYSICAL: - type = CDirectivePosition::Physical; - break; - case DIRECTIVE_POS_VIRTUAL: - type = CDirectivePosition::Virtual; - break; - default: - return nullptr; - } - - return ::make_unique<CDirectivePosition>(exp,type); -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveAlignFill(Parser& parser, int flags) -{ - CDirectiveAlignFill::Mode mode; - switch (flags & DIRECTIVE_USERMASK) - { - case DIRECTIVE_ALIGN_VIRTUAL: - mode = CDirectiveAlignFill::AlignVirtual; - break; - case DIRECTIVE_ALIGN_PHYSICAL: - mode = CDirectiveAlignFill::AlignPhysical; - break; - case DIRECTIVE_ALIGN_FILL: - mode = CDirectiveAlignFill::Fill; - break; - default: - return nullptr; - } - - if (mode != CDirectiveAlignFill::Fill && parser.peekToken().type == TokenType::Separator) - return ::make_unique<CDirectiveAlignFill>(UINT64_C(4),mode); - - std::vector<Expression> list; - if (parser.parseExpressionList(list,1,2) == false) - return nullptr; - - if (list.size() == 2) - return ::make_unique<CDirectiveAlignFill>(list[0],list[1],mode); - else - return ::make_unique<CDirectiveAlignFill>(list[0],mode); -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveSkip(Parser& parser, int flags) -{ - std::vector<Expression> list; - if (parser.parseExpressionList(list,1,1) == false) - return nullptr; - - return ::make_unique<CDirectiveSkip>(list[0]); -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveHeaderSize(Parser& parser, int flags) -{ - Expression exp = parser.parseExpression(); - if (exp.isLoaded() == false) - return nullptr; - - return ::make_unique<CDirectiveHeaderSize>(exp); -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveObjImport(Parser& parser, int flags) -{ - std::vector<Expression> list; - if (parser.parseExpressionList(list,1,2) == false) - return nullptr; - - std::wstring fileName; - if (list[0].evaluateString(fileName,true) == false) - return nullptr; - - if (list.size() == 2) - { - std::wstring ctorName; - if (list[1].evaluateIdentifier(ctorName) == false) - return nullptr; - - return ::make_unique<DirectiveObjImport>(fileName,ctorName); - } - - return ::make_unique<DirectiveObjImport>(fileName); -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveConditional(Parser& parser, int flags) -{ - ConditionType type; - std::wstring name; - Expression exp; - - const Token& start = parser.peekToken(); - ConditionalResult condResult = ConditionalResult::Unknown; - switch (flags) - { - case DIRECTIVE_COND_IF: - type = ConditionType::IF; - exp = parser.parseExpression(); - if (exp.isLoaded() == false) - { - parser.printError(start,L"Invalid condition"); - return ::make_unique<DummyCommand>(); - } - - if (exp.isConstExpression()) - { - ExpressionValue result = exp.evaluate(); - if (result.isInt()) - condResult = result.intValue != 0 ? ConditionalResult::True : ConditionalResult::False; - } - break; - case DIRECTIVE_COND_IFDEF: - type = ConditionType::IFDEF; - if (parser.parseIdentifier(name) == false) - return nullptr; - break; - case DIRECTIVE_COND_IFNDEF: - type = ConditionType::IFNDEF; - if (parser.parseIdentifier(name) == false) - return nullptr; - break; - } - - if(parser.nextToken().type != TokenType::Separator) - { - parser.printError(start,L"Directive not terminated"); - return nullptr; - } - - parser.pushConditionalResult(condResult); - std::unique_ptr<CAssemblerCommand> ifBlock = parser.parseCommandSequence(L'.', {L".else", L".elseif", L".elseifdef", L".elseifndef", L".endif"}); - parser.popConditionalResult(); - - // update the file info so that else commands get the right line number - parser.updateFileInfo(); - - std::unique_ptr<CAssemblerCommand> elseBlock = nullptr; - const Token &next = parser.nextToken(); - const std::wstring stringValue = next.getStringValue(); - - ConditionalResult elseResult; - switch (condResult) - { - case ConditionalResult::True: - elseResult = ConditionalResult::False; - break; - case ConditionalResult::False: - elseResult = ConditionalResult::True; - break; - case ConditionalResult::Unknown: - elseResult = condResult; - break; - } - - parser.pushConditionalResult(elseResult); - if (stringValue == L".else") - { - elseBlock = parser.parseCommandSequence(L'.', {L".endif"}); - - parser.eatToken(); // eat .endif - } else if (stringValue == L".elseif") - { - elseBlock = parseDirectiveConditional(parser,DIRECTIVE_COND_IF); - } else if (stringValue == L".elseifdef") - { - elseBlock = parseDirectiveConditional(parser,DIRECTIVE_COND_IFDEF); - } else if (stringValue == L".elseifndef") - { - elseBlock = parseDirectiveConditional(parser,DIRECTIVE_COND_IFNDEF); - } else if (stringValue != L".endif") - { - parser.popConditionalResult(); - return nullptr; - } - - parser.popConditionalResult(); - - // for true or false blocks, there's no need to create a conditional command - if (condResult == ConditionalResult::True) - { - return ifBlock; - } - - if (condResult == ConditionalResult::False) - { - if (elseBlock != nullptr) - return elseBlock; - else - return ::make_unique<DummyCommand>(); - } - - std::unique_ptr<CDirectiveConditional> cond; - if (exp.isLoaded()) - cond = ::make_unique<CDirectiveConditional>(type,exp); - else if (name.size() != 0) - cond = ::make_unique<CDirectiveConditional>(type,name); - else - cond = ::make_unique<CDirectiveConditional>(type); - - cond->setContent(std::move(ifBlock),std::move(elseBlock)); - return cond; -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveTable(Parser& parser, int flags) -{ - const Token& start = parser.peekToken(); - - std::vector<Expression> list; - if (parser.parseExpressionList(list,1,2) == false) - return nullptr; - - std::wstring fileName; - if (list[0].evaluateString(fileName,true) == false) - { - parser.printError(start,L"Invalid file name"); - return nullptr; - } - - TextFile::Encoding encoding = TextFile::GUESS; - if (list.size() == 2) - { - std::wstring encodingName; - if (list[1].evaluateString(encodingName,true) == false) - { - parser.printError(start,L"Invalid encoding name"); - return nullptr; - } - - encoding = getEncodingFromString(encodingName); - } - - return ::make_unique<TableCommand>(fileName,encoding); -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveData(Parser& parser, int flags) -{ - bool terminate = false; - if (flags & DIRECTIVE_DATA_TERMINATION) - { - terminate = true; - flags &= ~DIRECTIVE_DATA_TERMINATION; - } - - std::vector<Expression> list; - if (parser.parseExpressionList(list,1,-1) == false) - return nullptr; - - auto data = ::make_unique<CDirectiveData>(); - switch (flags & DIRECTIVE_USERMASK) - { - case DIRECTIVE_DATA_8: - data->setNormal(list,1); - break; - case DIRECTIVE_DATA_16: - data->setNormal(list,2); - break; - case DIRECTIVE_DATA_32: - data->setNormal(list,4); - break; - case DIRECTIVE_DATA_64: - data->setNormal(list,8); - break; - case DIRECTIVE_DATA_ASCII: - data->setAscii(list,terminate); - break; - case DIRECTIVE_DATA_SJIS: - data->setSjis(list,terminate); - break; - case DIRECTIVE_DATA_CUSTOM: - data->setCustom(list,terminate); - break; - case DIRECTIVE_DATA_FLOAT: - data->setFloat(list); - break; - case DIRECTIVE_DATA_DOUBLE: - data->setDouble(list); - break; - } - - return data; -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveMipsArch(Parser& parser, int flags) -{ - Arch = &Mips; - Mips.SetLoadDelay(false, 0); - - switch (flags) - { - case DIRECTIVE_MIPS_PSX: - Mips.SetVersion(MARCH_PSX); - return ::make_unique<ArchitectureCommand>(L".psx", L""); - case DIRECTIVE_MIPS_PS2: - Mips.SetVersion(MARCH_PS2); - return ::make_unique<ArchitectureCommand>(L".ps2", L""); - case DIRECTIVE_MIPS_PSP: - Mips.SetVersion(MARCH_PSP); - return ::make_unique<ArchitectureCommand>(L".psp", L""); - case DIRECTIVE_MIPS_N64: - Mips.SetVersion(MARCH_N64); - return ::make_unique<ArchitectureCommand>(L".n64", L""); - case DIRECTIVE_MIPS_RSP: - Mips.SetVersion(MARCH_RSP); - return ::make_unique<ArchitectureCommand>(L".rsp", L""); - } - - return nullptr; -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveArmArch(Parser& parser, int flags) -{ -#ifdef ARMIPS_ARM - Arch = &Arm; - - switch (flags) - { - case DIRECTIVE_ARM_GBA: - Arm.SetThumbMode(true); - Arm.setVersion(AARCH_GBA); - return ::make_unique<ArchitectureCommand>(L".gba\n.thumb", L".thumb"); - case DIRECTIVE_ARM_NDS: - Arm.SetThumbMode(false); - Arm.setVersion(AARCH_NDS); - return ::make_unique<ArchitectureCommand>(L".nds\n.arm", L".arm"); - case DIRECTIVE_ARM_3DS: - Arm.SetThumbMode(false); - Arm.setVersion(AARCH_3DS); - return ::make_unique<ArchitectureCommand>(L".3ds\n.arm", L".arm"); - case DIRECTIVE_ARM_BIG: - Arm.SetThumbMode(false); - Arm.setVersion(AARCH_BIG); - return ::make_unique<ArchitectureCommand>(L".arm.big\n.arm", L".arm"); - case DIRECTIVE_ARM_LITTLE: - Arm.SetThumbMode(false); - Arm.setVersion(AARCH_LITTLE); - return ::make_unique<ArchitectureCommand>(L".arm.little\n.arm", L".arm"); - } -#endif - - return nullptr; -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveArea(Parser& parser, int flags) -{ - std::vector<Expression> parameters; - if (parser.parseExpressionList(parameters,1,2) == false) - return nullptr; - - auto area = ::make_unique<CDirectiveArea>(parameters[0]); - if (parameters.size() == 2) - area->setFillExpression(parameters[1]); - - std::unique_ptr<CAssemblerCommand> content = parser.parseCommandSequence(L'.', {L".endarea"}); - parser.eatToken(); - - area->setContent(std::move(content)); - return area; -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveErrorWarning(Parser& parser, int flags) -{ - const Token &tok = parser.nextToken(); - - if (tok.type != TokenType::Identifier && tok.type != TokenType::String) - return nullptr; - - std::wstring stringValue = tok.getStringValue(); - std::transform(stringValue.begin(),stringValue.end(),stringValue.begin(),::towlower); - - if (stringValue == L"on") - { - Logger::setErrorOnWarning(true); - return ::make_unique<DummyCommand>(); - } else if (stringValue == L"off") - { - Logger::setErrorOnWarning(false); - return ::make_unique<DummyCommand>(); - } - - return nullptr; -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveRelativeInclude(Parser& parser, int flags) -{ - const Token &tok = parser.nextToken(); - - if (tok.type != TokenType::Identifier && tok.type != TokenType::String) - return nullptr; - - std::wstring stringValue = tok.getStringValue(); - std::transform(stringValue.begin(),stringValue.end(),stringValue.begin(),::towlower); - - if (stringValue == L"on") - { - Global.relativeInclude = true; - return ::make_unique<DummyCommand>(); - } else if (stringValue == L"off") - { - Global.relativeInclude = false; - return ::make_unique<DummyCommand>(); - } - - return nullptr; -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveNocash(Parser& parser, int flags) -{ - const Token &tok = parser.nextToken(); - - if (tok.type != TokenType::Identifier && tok.type != TokenType::String) - return nullptr; - - std::wstring stringValue = tok.getStringValue(); - std::transform(stringValue.begin(),stringValue.end(),stringValue.begin(),::towlower); - - if (stringValue == L"on") - { - Global.nocash = true; - return ::make_unique<DummyCommand>(); - } else if (stringValue == L"off") - { - Global.nocash = false; - return ::make_unique<DummyCommand>(); - } - - return nullptr; -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveSym(Parser& parser, int flags) -{ - const Token &tok = parser.nextToken(); - - if (tok.type != TokenType::Identifier && tok.type != TokenType::String) - return nullptr; - - std::wstring stringValue = tok.getStringValue(); - std::transform(stringValue.begin(),stringValue.end(),stringValue.begin(),::towlower); - - if (stringValue == L"on") - return ::make_unique<CDirectiveSym>(true); - else if (stringValue == L"off") - return ::make_unique<CDirectiveSym>(false); - else - return nullptr; -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveDefineLabel(Parser& parser, int flags) -{ - const Token& tok = parser.nextToken(); - if (tok.type != TokenType::Identifier) - return nullptr; - - if (parser.nextToken().type != TokenType::Comma) - return nullptr; - - Expression value = parser.parseExpression(); - if (value.isLoaded() == false) - return nullptr; - - const std::wstring stringValue = tok.getStringValue(); - if (Global.symbolTable.isValidSymbolName(stringValue) == false) - { - parser.printError(tok,L"Invalid label name \"%s\"",stringValue); - return nullptr; - } - - return ::make_unique<CAssemblerLabel>(stringValue,tok.getOriginalText(),value); -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveFunction(Parser& parser, int flags) -{ - const Token& tok = parser.nextToken(); - if (tok.type != TokenType::Identifier) - return nullptr; - - if (parser.nextToken().type != TokenType::Separator) - { - parser.printError(tok,L"Directive not terminated"); - return nullptr; - } - - auto func = ::make_unique<CDirectiveFunction>(tok.getStringValue(),tok.getOriginalText()); - std::unique_ptr<CAssemblerCommand> seq = parser.parseCommandSequence(L'.', {L".endfunc",L".endfunction",L".func",L".function"}); - - const std::wstring stringValue = parser.peekToken().getStringValue(); - if (stringValue == L".endfunc" || - stringValue == L".endfunction") - { - parser.eatToken(); - if(parser.nextToken().type != TokenType::Separator) - { - parser.printError(tok,L"Directive not terminated"); - return nullptr; - } - } - - func->setContent(std::move(seq)); - return func; -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveMessage(Parser& parser, int flags) -{ - Expression exp = parser.parseExpression(); - - switch (flags) - { - case DIRECTIVE_MSG_WARNING: - return ::make_unique<CDirectiveMessage>(CDirectiveMessage::Type::Warning,exp); - case DIRECTIVE_MSG_ERROR: - return ::make_unique<CDirectiveMessage>(CDirectiveMessage::Type::Error,exp); - case DIRECTIVE_MSG_NOTICE: - return ::make_unique<CDirectiveMessage>(CDirectiveMessage::Type::Notice,exp); - } - - return nullptr; -} - -std::unique_ptr<CAssemblerCommand> parseDirectiveInclude(Parser& parser, int flags) -{ - const Token& start = parser.peekToken(); - - std::vector<Expression> parameters; - if (parser.parseExpressionList(parameters,1,2) == false) - return nullptr; - - std::wstring fileName; - if (parameters[0].evaluateString(fileName,true) == false) - return nullptr; - - fileName = getFullPathName(fileName); - - TextFile::Encoding encoding = TextFile::GUESS; - if (parameters.size() == 2) - { - std::wstring encodingName; - if (parameters[1].evaluateString(encodingName,true) == false - && parameters[1].evaluateIdentifier(encodingName) == false) - return nullptr; - - encoding = getEncodingFromString(encodingName); - } - - // don't include the file if it's inside a false block - if (parser.isInsideTrueBlock() == false) - return ::make_unique<DummyCommand>(); - - if (fileExists(fileName) == false) - { - parser.printError(start,L"Included file \"%s\" does not exist",fileName); - return nullptr; - } - - TextFile f; - if (f.open(fileName,TextFile::Read,encoding) == false) - { - parser.printError(start,L"Could not open included file \"%s\"",fileName); - return nullptr; - } - - return parser.parseFile(f); -} - -const DirectiveMap directives = { - { L".open", { &parseDirectiveOpen, DIRECTIVE_NOTINMEMORY } }, - { L".openfile", { &parseDirectiveOpen, DIRECTIVE_NOTINMEMORY } }, - { L".create", { &parseDirectiveCreate, DIRECTIVE_NOTINMEMORY } }, - { L".createfile", { &parseDirectiveCreate, DIRECTIVE_NOTINMEMORY } }, - { L".close", { &parseDirectiveClose, DIRECTIVE_NOTINMEMORY } }, - { L".closefile", { &parseDirectiveClose, DIRECTIVE_NOTINMEMORY } }, - { L".incbin", { &parseDirectiveIncbin, 0 } }, - { L".import", { &parseDirectiveIncbin, 0 } }, - { L".org", { &parseDirectivePosition, DIRECTIVE_POS_VIRTUAL } }, - { L"org", { &parseDirectivePosition, DIRECTIVE_POS_VIRTUAL } }, - { L".orga", { &parseDirectivePosition, DIRECTIVE_POS_PHYSICAL } }, - { L"orga", { &parseDirectivePosition, DIRECTIVE_POS_PHYSICAL } }, - { L".headersize", { &parseDirectiveHeaderSize, 0 } }, - { L".align", { &parseDirectiveAlignFill, DIRECTIVE_ALIGN_VIRTUAL } }, - { L".aligna", { &parseDirectiveAlignFill, DIRECTIVE_ALIGN_PHYSICAL } }, - { L".fill", { &parseDirectiveAlignFill, DIRECTIVE_ALIGN_FILL } }, - { L"defs", { &parseDirectiveAlignFill, DIRECTIVE_ALIGN_FILL } }, - { L".skip", { &parseDirectiveSkip, 0 } }, - - { L".if", { &parseDirectiveConditional, DIRECTIVE_COND_IF } }, - { L".ifdef", { &parseDirectiveConditional, DIRECTIVE_COND_IFDEF } }, - { L".ifndef", { &parseDirectiveConditional, DIRECTIVE_COND_IFNDEF } }, - - { L".loadtable", { &parseDirectiveTable, 0 } }, - { L".table", { &parseDirectiveTable, 0 } }, - { L".byte", { &parseDirectiveData, DIRECTIVE_DATA_8 } }, - { L".halfword", { &parseDirectiveData, DIRECTIVE_DATA_16 } }, - { L".word", { &parseDirectiveData, DIRECTIVE_DATA_32 } }, - { L".doubleword", { &parseDirectiveData, DIRECTIVE_DATA_64 } }, - { L".db", { &parseDirectiveData, DIRECTIVE_DATA_8 } }, - { L".dh", { &parseDirectiveData, DIRECTIVE_DATA_16|DIRECTIVE_NOCASHOFF } }, - { L".dw", { &parseDirectiveData, DIRECTIVE_DATA_32|DIRECTIVE_NOCASHOFF } }, - { L".dd", { &parseDirectiveData, DIRECTIVE_DATA_64|DIRECTIVE_NOCASHOFF } }, - { L".dw", { &parseDirectiveData, DIRECTIVE_DATA_16|DIRECTIVE_NOCASHON } }, - { L".dd", { &parseDirectiveData, DIRECTIVE_DATA_32|DIRECTIVE_NOCASHON } }, - { L".dcb", { &parseDirectiveData, DIRECTIVE_DATA_8 } }, - { L".dcw", { &parseDirectiveData, DIRECTIVE_DATA_16 } }, - { L".dcd", { &parseDirectiveData, DIRECTIVE_DATA_32 } }, - { L".dcq", { &parseDirectiveData, DIRECTIVE_DATA_64 } }, - { L"db", { &parseDirectiveData, DIRECTIVE_DATA_8 } }, - { L"dh", { &parseDirectiveData, DIRECTIVE_DATA_16|DIRECTIVE_NOCASHOFF } }, - { L"dw", { &parseDirectiveData, DIRECTIVE_DATA_32|DIRECTIVE_NOCASHOFF } }, - { L"dd", { &parseDirectiveData, DIRECTIVE_DATA_64|DIRECTIVE_NOCASHOFF } }, - { L"dw", { &parseDirectiveData, DIRECTIVE_DATA_16|DIRECTIVE_NOCASHON } }, - { L"dd", { &parseDirectiveData, DIRECTIVE_DATA_32|DIRECTIVE_NOCASHON } }, - { L"dcb", { &parseDirectiveData, DIRECTIVE_DATA_8 } }, - { L"dcw", { &parseDirectiveData, DIRECTIVE_DATA_16 } }, - { L"dcd", { &parseDirectiveData, DIRECTIVE_DATA_32 } }, - { L"dcq", { &parseDirectiveData, DIRECTIVE_DATA_64 } }, - { L".float", { &parseDirectiveData, DIRECTIVE_DATA_FLOAT } }, - { L".double", { &parseDirectiveData, DIRECTIVE_DATA_DOUBLE } }, - { L".ascii", { &parseDirectiveData, DIRECTIVE_DATA_ASCII } }, - { L".asciiz", { &parseDirectiveData, DIRECTIVE_DATA_ASCII|DIRECTIVE_DATA_TERMINATION } }, - { L".string", { &parseDirectiveData, DIRECTIVE_DATA_CUSTOM|DIRECTIVE_DATA_TERMINATION } }, - { L".str", { &parseDirectiveData, DIRECTIVE_DATA_CUSTOM|DIRECTIVE_DATA_TERMINATION } }, - { L".stringn", { &parseDirectiveData, DIRECTIVE_DATA_CUSTOM } }, - { L".strn", { &parseDirectiveData, DIRECTIVE_DATA_CUSTOM } }, - { L".sjis", { &parseDirectiveData, DIRECTIVE_DATA_SJIS|DIRECTIVE_DATA_TERMINATION } }, - { L".sjisn", { &parseDirectiveData, DIRECTIVE_DATA_SJIS } }, - - { L".psx", { &parseDirectiveMipsArch, DIRECTIVE_MIPS_PSX } }, - { L".ps2", { &parseDirectiveMipsArch, DIRECTIVE_MIPS_PS2 } }, - { L".psp", { &parseDirectiveMipsArch, DIRECTIVE_MIPS_PSP } }, - { L".n64", { &parseDirectiveMipsArch, DIRECTIVE_MIPS_N64 } }, - { L".rsp", { &parseDirectiveMipsArch, DIRECTIVE_MIPS_RSP } }, - - { L".gba", { &parseDirectiveArmArch, DIRECTIVE_ARM_GBA } }, - { L".nds", { &parseDirectiveArmArch, DIRECTIVE_ARM_NDS } }, - { L".3ds", { &parseDirectiveArmArch, DIRECTIVE_ARM_3DS } }, - { L".arm.big", { &parseDirectiveArmArch, DIRECTIVE_ARM_BIG } }, - { L".arm.little", { &parseDirectiveArmArch, DIRECTIVE_ARM_LITTLE } }, - - { L".area", { &parseDirectiveArea, 0 } }, - - { L".importobj", { &parseDirectiveObjImport, 0 } }, - { L".importlib", { &parseDirectiveObjImport, 0 } }, - - { L".erroronwarning", { &parseDirectiveErrorWarning, 0 } }, - { L".relativeinclude", { &parseDirectiveRelativeInclude, 0 } }, - { L".nocash", { &parseDirectiveNocash, 0 } }, - { L".sym", { &parseDirectiveSym, 0 } }, - - { L".definelabel", { &parseDirectiveDefineLabel, 0 } }, - { L".function", { &parseDirectiveFunction, DIRECTIVE_MANUALSEPARATOR } }, - { L".func", { &parseDirectiveFunction, DIRECTIVE_MANUALSEPARATOR } }, - - { L".warning", { &parseDirectiveMessage, DIRECTIVE_MSG_WARNING } }, - { L".error", { &parseDirectiveMessage, DIRECTIVE_MSG_ERROR } }, - { L".notice", { &parseDirectiveMessage, DIRECTIVE_MSG_NOTICE } }, - - { L".include", { &parseDirectiveInclude, 0 } }, -}; - -// file: Parser/ExpressionParser.cpp - -static ExpressionInternal* expression(Tokenizer& tokenizer); - -static bool allowFunctionCall = true; - -void allowFunctionCallExpression(bool allow) -{ - allowFunctionCall = allow; -} - -static ExpressionInternal* primaryExpression(Tokenizer& tokenizer) -{ - const Token &tok = tokenizer.peekToken(); - - switch (tok.type) - { - case TokenType::Float: - tokenizer.eatToken(); - return new ExpressionInternal(tok.floatValue); - case TokenType::Identifier: - { - const std::wstring stringValue = tok.getStringValue(); - tokenizer.eatToken(); - if (stringValue == L".") - return new ExpressionInternal(OperatorType::MemoryPos); - else - return new ExpressionInternal(stringValue,OperatorType::Identifier); - } - case TokenType::String: - tokenizer.eatToken(); - return new ExpressionInternal(tok.getStringValue(),OperatorType::String); - case TokenType::Integer: - tokenizer.eatToken(); - return new ExpressionInternal(tok.intValue); - case TokenType::LParen: - { - tokenizer.eatToken(); - ExpressionInternal* exp = expression(tokenizer); - - if (tokenizer.nextToken().type != TokenType::RParen) - { - delete exp; - return nullptr; - } - - return exp; - } - case TokenType::Invalid: - default: - break; - } - - return nullptr; -} - -static ExpressionInternal* postfixExpression(Tokenizer& tokenizer) -{ - if (allowFunctionCall && - tokenizer.peekToken(0).type == TokenType::Identifier && - tokenizer.peekToken(1).type == TokenType::LParen) - { - const std::wstring functionName = tokenizer.nextToken().getStringValue(); - tokenizer.eatToken(); - - std::vector<ExpressionInternal*> parameters; - while (tokenizer.peekToken().type != TokenType::RParen) - { - if (parameters.size() != 0 && tokenizer.nextToken().type != TokenType::Comma) - { - for (ExpressionInternal* exp: parameters) - delete exp; - return nullptr; - } - - ExpressionInternal* exp = expression(tokenizer); - if (exp == nullptr) - { - for (ExpressionInternal* exp: parameters) - delete exp; - return nullptr; - } - - parameters.push_back(exp); - } - - tokenizer.eatToken(); - - return new ExpressionInternal(functionName,parameters); - } - - return primaryExpression(tokenizer); -} - -static ExpressionInternal* unaryExpression(Tokenizer& tokenizer) -{ - ExpressionInternal* exp = postfixExpression(tokenizer); - if (exp != nullptr) - return exp; - - const TokenType opType = tokenizer.nextToken().type; - exp = postfixExpression(tokenizer); - if (exp == nullptr) - return nullptr; - - switch (opType) - { - case TokenType::Plus: - return exp; - case TokenType::Minus: - return new ExpressionInternal(OperatorType::Neg,exp); - case TokenType::Tilde: - return new ExpressionInternal(OperatorType::BitNot,exp); - case TokenType::Exclamation: - return new ExpressionInternal(OperatorType::LogNot,exp); - case TokenType::Degree: - return new ExpressionInternal(OperatorType::ToString,exp); - default: - delete exp; - return nullptr; - } -} - -static ExpressionInternal* multiplicativeExpression(Tokenizer& tokenizer) -{ - ExpressionInternal* exp = unaryExpression(tokenizer); - if (exp == nullptr) - return nullptr; - - while (true) - { - OperatorType op = OperatorType::Invalid; - switch (tokenizer.peekToken().type) - { - case TokenType::Mult: - op = OperatorType::Mult; - break; - case TokenType::Div: - op = OperatorType::Div; - break; - case TokenType::Mod: - op = OperatorType::Mod; - break; - default: - break; - } - - if (op == OperatorType::Invalid) - break; - - tokenizer.eatToken(); - - ExpressionInternal* exp2 = unaryExpression(tokenizer); - if (exp2 == nullptr) - { - delete exp; - return nullptr; - } - - exp = new ExpressionInternal(op,exp,exp2); - } - - return exp; -} - -static ExpressionInternal* additiveExpression(Tokenizer& tokenizer) -{ - ExpressionInternal* exp = multiplicativeExpression(tokenizer); - if (exp == nullptr) - return nullptr; - - while (true) - { - OperatorType op = OperatorType::Invalid; - switch (tokenizer.peekToken().type) - { - case TokenType::Plus: - op = OperatorType::Add; - break; - case TokenType::Minus: - op = OperatorType::Sub; - break; - default: - break; - } - - if (op == OperatorType::Invalid) - break; - - tokenizer.eatToken(); - - ExpressionInternal* exp2 = multiplicativeExpression(tokenizer); - if (exp2 == nullptr) - { - delete exp; - return nullptr; - } - - exp = new ExpressionInternal(op,exp,exp2); - } - - return exp; -} - -static ExpressionInternal* shiftExpression(Tokenizer& tokenizer) -{ - ExpressionInternal* exp = additiveExpression(tokenizer); - if (exp == nullptr) - return nullptr; - - while (true) - { - OperatorType op = OperatorType::Invalid; - switch (tokenizer.peekToken().type) - { - case TokenType::LeftShift: - op = OperatorType::LeftShift; - break; - case TokenType::RightShift: - op = OperatorType::RightShift; - break; - default: - break; - } - - if (op == OperatorType::Invalid) - break; - - tokenizer.eatToken(); - - ExpressionInternal* exp2 = additiveExpression(tokenizer); - if (exp2 == nullptr) - { - delete exp; - return nullptr; - } - - exp = new ExpressionInternal(op,exp,exp2); - } - - return exp; -} - -static ExpressionInternal* relationalExpression(Tokenizer& tokenizer) -{ - ExpressionInternal* exp = shiftExpression(tokenizer); - if (exp == nullptr) - return nullptr; - - while (true) - { - OperatorType op = OperatorType::Invalid; - switch (tokenizer.peekToken().type) - { - case TokenType::Less: - op = OperatorType::Less; - break; - case TokenType::LessEqual: - op = OperatorType::LessEqual; - break; - case TokenType::Greater: - op = OperatorType::Greater; - break; - case TokenType::GreaterEqual: - op = OperatorType::GreaterEqual; - break; - default: - break; - } - - if (op == OperatorType::Invalid) - break; - - tokenizer.eatToken(); - - ExpressionInternal* exp2 = shiftExpression(tokenizer); - if (exp2 == nullptr) - { - delete exp; - return nullptr; - } - - exp = new ExpressionInternal(op,exp,exp2); - } - - return exp; -} - -static ExpressionInternal* equalityExpression(Tokenizer& tokenizer) -{ - ExpressionInternal* exp = relationalExpression(tokenizer); - if (exp == nullptr) - return nullptr; - - while (true) - { - OperatorType op = OperatorType::Invalid; - switch (tokenizer.peekToken().type) - { - case TokenType::Equal: - op = OperatorType::Equal; - break; - case TokenType::NotEqual: - op = OperatorType::NotEqual; - break; - default: - break; - } - - if (op == OperatorType::Invalid) - break; - - tokenizer.eatToken(); - - ExpressionInternal* exp2 = relationalExpression(tokenizer); - if (exp2 == nullptr) - { - delete exp; - return nullptr; - } - - exp = new ExpressionInternal(op,exp,exp2); - } - - return exp; -} - -static ExpressionInternal* andExpression(Tokenizer& tokenizer) -{ - ExpressionInternal* exp = equalityExpression(tokenizer); - if (exp == nullptr) - return nullptr; - - while (tokenizer.peekToken().type == TokenType::BitAnd) - { - tokenizer.eatToken(); - - ExpressionInternal* exp2 = equalityExpression(tokenizer); - if (exp2 == nullptr) - { - delete exp; - return nullptr; - } - - exp = new ExpressionInternal(OperatorType::BitAnd,exp,exp2); - } - - return exp; -} - -static ExpressionInternal* exclusiveOrExpression(Tokenizer& tokenizer) -{ - ExpressionInternal* exp = andExpression(tokenizer); - if (exp == nullptr) - return nullptr; - - while (tokenizer.peekToken().type == TokenType::Caret) - { - tokenizer.eatToken(); - - ExpressionInternal* exp2 = andExpression(tokenizer); - if (exp2 == nullptr) - { - delete exp; - return nullptr; - } - - exp = new ExpressionInternal(OperatorType::Xor,exp,exp2); - } - - return exp; -} - -static ExpressionInternal* inclusiveOrExpression(Tokenizer& tokenizer) -{ - ExpressionInternal* exp = exclusiveOrExpression(tokenizer); - if (exp == nullptr) - return nullptr; - - while (tokenizer.peekToken().type == TokenType::BitOr) - { - tokenizer.eatToken(); - - ExpressionInternal* exp2 = exclusiveOrExpression(tokenizer); - if (exp2 == nullptr) - { - delete exp; - return nullptr; - } - - exp = new ExpressionInternal(OperatorType::BitOr,exp,exp2); - } - - return exp; -} - -static ExpressionInternal* logicalAndExpression(Tokenizer& tokenizer) -{ - ExpressionInternal* exp = inclusiveOrExpression(tokenizer); - if (exp == nullptr) - return nullptr; - - while (tokenizer.peekToken().type == TokenType::LogAnd) - { - tokenizer.eatToken(); - - ExpressionInternal* exp2 = inclusiveOrExpression(tokenizer); - if (exp2 == nullptr) - { - delete exp; - return nullptr; - } - - exp = new ExpressionInternal(OperatorType::LogAnd,exp,exp2); - } - - return exp; -} - -static ExpressionInternal* logicalOrExpression(Tokenizer& tokenizer) -{ - ExpressionInternal* exp = logicalAndExpression(tokenizer); - if (exp == nullptr) - return nullptr; - - while (tokenizer.peekToken().type == TokenType::LogOr) - { - tokenizer.eatToken(); - - ExpressionInternal* exp2 = logicalAndExpression(tokenizer); - if (exp2 == nullptr) - { - delete exp; - return nullptr; - } - - exp = new ExpressionInternal(OperatorType::LogOr,exp,exp2); - } - - return exp; -} - -static ExpressionInternal* conditionalExpression(Tokenizer& tokenizer) -{ - ExpressionInternal* exp = logicalOrExpression(tokenizer); - if (exp == nullptr) - return nullptr; - - // check a ? b : c - if (tokenizer.peekToken().type != TokenType::Question) - return exp; - - tokenizer.eatToken(); - ExpressionInternal* second = expression(tokenizer); - - if (second != nullptr && tokenizer.nextToken().type == TokenType::Colon) - { - ExpressionInternal* third = expression(tokenizer); - if (third != nullptr) - return new ExpressionInternal(OperatorType::TertiaryIf,exp,second,third); - - delete third; - } - - delete second; - delete exp; - return nullptr; -} - -static ExpressionInternal* expression(Tokenizer& tokenizer) -{ - return conditionalExpression(tokenizer); -} - -Expression parseExpression(Tokenizer& tokenizer, bool inUnknownOrFalseBlock) -{ - TokenizerPosition pos = tokenizer.getPosition(); - - // parse expression, revert tokenizer to previous position - // if it failed - ExpressionInternal* exp = expression(tokenizer); - if (exp == nullptr) - tokenizer.setPosition(pos); - - Expression result; - result.setExpression(exp, inUnknownOrFalseBlock); - return result; -} - -// file: Parser/Parser.cpp - -inline bool isPartOfList(const std::wstring& value, const std::initializer_list<const wchar_t*>& terminators) -{ - for (const wchar_t* term: terminators) - { - if (value == term) - return true; - } - - return false; -} - -Parser::Parser() -{ - initializingMacro = false; - overrideFileInfo = false; - conditionStack.push_back({true,false}); - clearError(); -} - -void Parser::pushConditionalResult(ConditionalResult cond) -{ - ConditionInfo info = conditionStack.back(); - info.inTrueBlock = info.inTrueBlock && cond != ConditionalResult::False; - info.inUnknownBlock = info.inUnknownBlock || cond == ConditionalResult::Unknown; - conditionStack.push_back(info); -} - -Expression Parser::parseExpression() -{ - return ::parseExpression(*getTokenizer(), !isInsideTrueBlock() || isInsideUnknownBlock()); -} - -bool Parser::parseExpressionList(std::vector<Expression>& list, int min, int max) -{ - bool valid = true; - list.clear(); - list.reserve(max >= 0 ? max : 32); - - const Token& start = peekToken(); - - Expression exp = parseExpression(); - list.push_back(exp); - - if (exp.isLoaded() == false) - { - printError(start,L"Parameter failure"); - getTokenizer()->skipLookahead(); - valid = false; - } - - while (peekToken().type == TokenType::Comma) - { - eatToken(); - - exp = parseExpression(); - list.push_back(exp); - - if (exp.isLoaded() == false) - { - printError(start,L"Parameter failure"); - getTokenizer()->skipLookahead(); - valid = false; - } - } - - if (list.size() < (size_t) min) - { - printError(start,L"Not enough parameters (min %d)",min); - return false; - } - - if (max != -1 && (size_t) max < list.size()) - { - printError(start,L"Too many parameters (max %d)",max); - return false; - } - - return valid; -} - -bool Parser::parseIdentifier(std::wstring& dest) -{ - const Token& tok = nextToken(); - if (tok.type != TokenType::Identifier) - return false; - - dest = tok.getStringValue(); - return true; -} - -std::unique_ptr<CAssemblerCommand> Parser::parseCommandSequence(wchar_t indicator, const std::initializer_list<const wchar_t*> terminators) -{ - auto sequence = ::make_unique<CommandSequence>(); - - bool foundTermination = false; - while (atEnd() == false) - { - const Token &next = peekToken(); - - if(next.type == TokenType::Separator) - { - eatToken(); - continue; - } - - if (next.stringValueStartsWith(indicator) && isPartOfList(next.getStringValue(), terminators)) - { - foundTermination = true; - break; - } - - bool foundSomething = false; - while (checkEquLabel() || checkMacroDefinition()) - { - // do nothing, just parse all the equs and macros there are - if (hasError()) - sequence->addCommand(handleError()); - - foundSomething = true; - } - - if (foundSomething) - continue; - - std::unique_ptr<CAssemblerCommand> cmd = parseCommand(); - - // omit commands inside blocks that are trivially false - if (isInsideTrueBlock() == false) - { - continue; - } - - sequence->addCommand(std::move(cmd)); - } - - if (!foundTermination && terminators.size()) - { - std::wstring expected; - for (const wchar_t* terminator : terminators) - { - if (!expected.empty()) - expected += L", "; - expected += terminator; - } - - Logger::printError(Logger::Error, L"Unterminated command sequence, expected any of %s.", expected); - } - - return sequence; -} - -std::unique_ptr<CAssemblerCommand> Parser::parseFile(TextFile& file, bool virtualFile) -{ - FileTokenizer tokenizer; - if (tokenizer.init(&file) == false) - return nullptr; - - std::unique_ptr<CAssemblerCommand> result = parse(&tokenizer,virtualFile,file.getFileName()); - - if (file.isFromMemory() == false) - Global.FileInfo.TotalLineCount += file.getNumLines(); - - return result; -} - -std::unique_ptr<CAssemblerCommand> Parser::parseString(const std::wstring& text) -{ - TextFile file; - file.openMemory(text); - return parseFile(file,true); -} - -std::unique_ptr<CAssemblerCommand> Parser::parseTemplate(const std::wstring& text, std::initializer_list<AssemblyTemplateArgument> variables) -{ - std::wstring fullText = text; - - overrideFileInfo = true; - overrideFileNum = Global.FileInfo.FileNum; - overrideLineNum = Global.FileInfo.LineNumber; - - for (auto& arg: variables) - { - size_t count = replaceAll(fullText,arg.variableName,arg.value); - (void)count; -#ifdef _DEBUG - if (count != 0 && arg.value.empty()) - Logger::printError(Logger::Warning,L"Empty replacement for %s",arg.variableName); -#endif - } - - std::unique_ptr<CAssemblerCommand> result = parseString(fullText); - overrideFileInfo = false; - - return result; -} - -std::unique_ptr<CAssemblerCommand> Parser::parseDirective(const DirectiveMap &directiveSet) -{ - const Token &tok = peekToken(); - if (tok.type != TokenType::Identifier) - return nullptr; - - const std::wstring stringValue = tok.getStringValue(); - - auto matchRange = directiveSet.equal_range(stringValue); - for (auto it = matchRange.first; it != matchRange.second; ++it) - { - const DirectiveEntry &directive = it->second; - - if (directive.flags & DIRECTIVE_DISABLED) - continue; - if ((directive.flags & DIRECTIVE_NOCASHOFF) && Global.nocash == true) - continue; - if ((directive.flags & DIRECTIVE_NOCASHON) && Global.nocash == false) - continue; - if ((directive.flags & DIRECTIVE_NOTINMEMORY) && Global.memoryMode == true) - continue; - - if (directive.flags & DIRECTIVE_MIPSRESETDELAY) - Arch->NextSection(); - - eatToken(); - std::unique_ptr<CAssemblerCommand> result = directive.function(*this,directive.flags); - if (result == nullptr) - { - if (hasError() == false) - printError(tok,L"Directive parameter failure"); - return nullptr; - } else if (!(directive.flags & DIRECTIVE_MANUALSEPARATOR) && nextToken().type != TokenType::Separator) - { - printError(tok,L"Directive not terminated"); - return nullptr; - } - - return result; - } - - return nullptr; -} - -bool Parser::matchToken(TokenType type, bool optional) -{ - if (optional) - { - const Token& token = peekToken(); - if (token.type == type) - eatToken(); - return true; - } - - return nextToken().type == type; -} - -std::unique_ptr<CAssemblerCommand> Parser::parse(Tokenizer* tokenizer, bool virtualFile, const std::wstring& name) -{ - if (entries.size() >= 150) - { - Logger::queueError(Logger::Error, L"Max include/recursion depth reached"); - return nullptr; - } - - FileEntry entry; - entry.tokenizer = tokenizer; - entry.virtualFile = virtualFile; - - if (virtualFile == false && name.empty() == false) - { - entry.fileNum = (int) Global.FileInfo.FileList.size(); - Global.FileInfo.FileList.push_back(name); - } else { - entry.fileNum = -1; - } - - entries.push_back(entry); - - std::unique_ptr<CAssemblerCommand> sequence = parseCommandSequence(); - entries.pop_back(); - - return sequence; -} - -void Parser::addEquation(const Token& startToken, const std::wstring& name, const std::wstring& value) -{ - // parse value string - TextFile f; - f.openMemory(value); - - FileTokenizer tok; - tok.init(&f); - - TokenizerPosition start = tok.getPosition(); - while (tok.atEnd() == false && tok.peekToken().type != TokenType::Separator) - { - const Token& token = tok.nextToken(); - if (token.type == TokenType::Identifier && token.getStringValue() == name) - { - printError(startToken,L"Recursive equ definition for \"%s\" not allowed",name); - return; - } - - if (token.type == TokenType::Equ) - { - printError(startToken,L"equ value must not contain another equ instance"); - return; - } - } - - // extract tokens - TokenizerPosition end = tok.getPosition(); - std::vector<Token> tokens = tok.getTokens(start, end); - size_t index = Tokenizer::addEquValue(tokens); - - for (FileEntry& entry : entries) - entry.tokenizer->resetLookaheadCheckMarks(); - - // register equation - Global.symbolTable.addEquation(name, Global.FileInfo.FileNum, Global.Section, index); -} - -bool Parser::checkEquLabel() -{ - updateFileInfo(); - - const Token& start = peekToken(); - if (start.type == TokenType::Identifier) - { - int pos = 1; - if (peekToken(pos).type == TokenType::Colon) - pos++; - - if (peekToken(pos).type == TokenType::Equ && - peekToken(pos+1).type == TokenType::EquValue) - { - std::wstring name = peekToken(0).getStringValue(); - std::wstring value = peekToken(pos+1).getStringValue(); - eatTokens(pos+2); - - // skip the equ if it's inside a false conditional block - if (isInsideTrueBlock() == false) - return true; - - // equs can't be inside blocks whose condition can only be - // evaluated during validation - if (isInsideUnknownBlock()) - { - printError(start,L"equ not allowed inside of block with non-trivial condition"); - return true; - } - - // equs are not allowed in macros - if (initializingMacro) - { - printError(start,L"equ not allowed in macro"); - return true; - } - - if (Global.symbolTable.isValidSymbolName(name) == false) - { - printError(start,L"Invalid equation name \"%s\"",name); - return true; - } - - if (Global.symbolTable.symbolExists(name,Global.FileInfo.FileNum,Global.Section)) - { - printError(start,L"Equation name \"%s\" already defined",name); - return true; - } - - addEquation(start,name,value); - return true; - } - } - - return false; -} - -bool Parser::checkMacroDefinition() -{ - const Token& first = peekToken(); - if (first.type != TokenType::Identifier) - return false; - - if (!first.stringValueStartsWith(L'.') || first.getStringValue() != L".macro") - return false; - - eatToken(); - - // nested macro definitions are not allowed - if (initializingMacro) - { - printError(first,L"Nested macro definitions not allowed"); - while (!atEnd()) - { - const Token& token = nextToken(); - if (token.type == TokenType::Identifier && token.getStringValue() == L".endmacro") - break; - } - - return true; - } - - std::vector<Expression> parameters; - if (parseExpressionList(parameters,1,-1) == false) - return false; - - ParserMacro macro; - macro.counter = 0; - - // load name - if (parameters[0].evaluateIdentifier(macro.name) == false) - return false; - - // load parameters - for (size_t i = 1; i < parameters.size(); i++) - { - std::wstring name; - if (parameters[i].evaluateIdentifier(name) == false) - return false; - - macro.parameters.push_back(name); - } - - if(nextToken().type != TokenType::Separator) - { - printError(first,L"Macro directive not terminated"); - return false; - } - - // load macro content - - TokenizerPosition start = getTokenizer()->getPosition(); - bool valid = false; - while (atEnd() == false) - { - const Token& tok = nextToken(); - if (tok.type == TokenType::Identifier && tok.getStringValue() == L".endmacro") - { - valid = true; - break; - } - } - - // Macros have to be defined at parse time, so they can't be defined in blocks - // with non-trivial conditions - if (isInsideUnknownBlock()) - { - printError(first, L"Macro definition not allowed inside of block with non-trivial condition"); - return false; - } - - // if we are in a known false block, don't define the macro - if (!isInsideTrueBlock()) - return true; - - // duplicate check - if (macros.find(macro.name) != macros.end()) - { - printError(first, L"Macro \"%s\" already defined", macro.name); - return false; - } - - // no .endmacro, not valid - if (valid == false) - { - printError(first, L"Macro \"%s\" not terminated", macro.name); - return true; - } - - // get content - TokenizerPosition end = getTokenizer()->getPosition().previous(); - macro.content = getTokenizer()->getTokens(start,end); - - if(nextToken().type != TokenType::Separator) - { - printError(first,L"Endmacro directive not terminated"); - return false; - } - - macros[macro.name] = macro; - return true; -} - -std::unique_ptr<CAssemblerCommand> Parser::parseMacroCall() -{ - const Token& start = peekToken(); - if (start.type != TokenType::Identifier) - return nullptr; - - auto it = macros.find(start.getStringValue()); - if (it == macros.end()) - return nullptr; - - ParserMacro& macro = it->second; - eatToken(); - - // create a token stream for the macro content, - // registering replacements for parameter values - TokenStreamTokenizer macroTokenizer; - - std::set<std::wstring> identifierParameters; - for (size_t i = 0; i < macro.parameters.size(); i++) - { - if (peekToken().type == TokenType::Separator) - { - printError(start,L"Too few macro arguments (%d vs %d)",i,macro.parameters.size()); - return nullptr; - } - - if (i != 0) - { - if (nextToken().type != TokenType::Comma) - { - printError(start,L"Macro arguments not comma-separated"); - return nullptr; - } - } - - TokenizerPosition startPos = getTokenizer()->getPosition(); - Expression exp = parseExpression(); - if (exp.isLoaded() == false) - { - printError(start,L"Invalid macro argument expression"); - return nullptr; - } - - TokenizerPosition endPos = getTokenizer()->getPosition(); - std::vector<Token> tokens = getTokenizer()->getTokens(startPos,endPos); - - // remember any single identifier parameters for the label replacement - if (tokens.size() == 1 && tokens[0].type == TokenType::Identifier) - identifierParameters.insert(tokens[0].getStringValue()); - - // give them as a replacement to new tokenizer - macroTokenizer.registerReplacement(macro.parameters[i],tokens); - } - - if (peekToken().type == TokenType::Comma) - { - size_t count = macro.parameters.size(); - while (peekToken().type == TokenType::Comma) - { - eatToken(); - parseExpression(); - count++; - } - - printError(start,L"Too many macro arguments (%d vs %d)",count,macro.parameters.size()); - return nullptr; - } - - if(nextToken().type != TokenType::Separator) - { - printError(start,L"Macro call not terminated"); - return nullptr; - } - - // skip macro instantiation in known false blocks - if (!isInsideUnknownBlock() && !isInsideTrueBlock()) - return ::make_unique<DummyCommand>(); - - // a macro is fully parsed once when it's loaded - // to gather all labels. it's not necessary to - // instantiate other macros at that time - if (initializingMacro) - return ::make_unique<DummyCommand>(); - - // the first time a macro is instantiated, it needs to be analyzed - // for labels - if (macro.counter == 0) - { - initializingMacro = true; - - // parse the short lived next command - macroTokenizer.init(macro.content); - Logger::suppressErrors(); - std::unique_ptr<CAssemblerCommand> command = parse(&macroTokenizer,true); - Logger::unsuppressErrors(); - - macro.labels = macroLabels; - macroLabels.clear(); - - initializingMacro = false; - } - - // register labels and replacements - for (const std::wstring& label: macro.labels) - { - // check if the label is using the name of a parameter - // in that case, don't register a unique replacement - if (identifierParameters.find(label) != identifierParameters.end()) - continue; - - // otherwise make sure the name is unique - std::wstring fullName; - if (Global.symbolTable.isLocalSymbol(label)) - fullName = formatString(L"@@%s_%s_%08X",macro.name,label.substr(2),macro.counter); - else if (Global.symbolTable.isStaticSymbol(label)) - fullName = formatString(L"@%s_%s_%08X",macro.name,label.substr(1),macro.counter); - else - fullName = formatString(L"%s_%s_%08X",macro.name,label,macro.counter); - - macroTokenizer.registerReplacement(label,fullName); - } - - macroTokenizer.init(macro.content); - macro.counter++; - - return parse(&macroTokenizer,true); - -} - -std::unique_ptr<CAssemblerCommand> Parser::parseLabel() -{ - updateFileInfo(); - - const Token& start = peekToken(0); - - if (peekToken(0).type == TokenType::Identifier && - peekToken(1).type == TokenType::Colon) - { - const std::wstring name = start.getStringValue(); - eatTokens(2); - - if (initializingMacro) - macroLabels.insert(name); - - if (Global.symbolTable.isValidSymbolName(name) == false) - { - printError(start,L"Invalid label name \"%s\"",name); - return nullptr; - } - - return ::make_unique<CAssemblerLabel>(name,start.getOriginalText()); - } - - return nullptr; -} - -std::unique_ptr<CAssemblerCommand> Parser::handleError() -{ - // skip the rest of the statement - while (!atEnd() && nextToken().type != TokenType::Separator); - - clearError(); - return ::make_unique<InvalidCommand>(); -} - - -void Parser::updateFileInfo() -{ - if (overrideFileInfo) - { - Global.FileInfo.FileNum = overrideFileNum; - Global.FileInfo.LineNumber = overrideLineNum; - return; - } - - for (size_t i = entries.size(); i > 0; i--) - { - size_t index = i-1; - - if (entries[index].virtualFile == false && entries[index].fileNum != -1) - { - Global.FileInfo.FileNum = entries[index].fileNum; - - // if it's not the topmost file, then the command to instantiate the - // following files was already parsed -> take the previous command's line - if (index != entries.size() - 1) - Global.FileInfo.LineNumber = entries[index].previousCommandLine; - else - { - Global.FileInfo.LineNumber = (int)entries[index].tokenizer->peekToken().line; - entries[index].previousCommandLine = Global.FileInfo.LineNumber; - } - return; - } - } -} - -std::unique_ptr<CAssemblerCommand> Parser::parseCommand() -{ - std::unique_ptr<CAssemblerCommand> command; - - updateFileInfo(); - - if (atEnd()) - return ::make_unique<DummyCommand>(); - - if ((command = parseLabel()) != nullptr) - return command; - if (hasError()) - return handleError(); - - if ((command = parseMacroCall()) != nullptr) - return command; - if (hasError()) - return handleError(); - - if ((command = Arch->parseDirective(*this)) != nullptr) - return command; - if (hasError()) - return handleError(); - - if ((command = parseDirective(directives)) != nullptr) - return command; - if (hasError()) - return handleError(); - - if ((command = Arch->parseOpcode(*this)) != nullptr) - return command; - if (hasError()) - return handleError(); - - const Token& token = peekToken(); - printError(token,L"Parse error '%s'",token.getOriginalText()); - return handleError(); -} - -void TokenSequenceParser::addEntry(int result, TokenSequence tokens, TokenValueSequence values) -{ - Entry entry = { tokens, values, result }; - entries.push_back(entry); -} - -bool TokenSequenceParser::parse(Parser& parser, int& result) -{ - for (Entry& entry: entries) - { - TokenizerPosition pos = parser.getTokenizer()->getPosition(); - auto values = entry.values.begin(); - - bool valid = true; - for (TokenType type: entry.tokens) - { - // check of token type matches - const Token& token = parser.nextToken(); - if (token.type != type) - { - valid = false; - break; - } - - // if necessary, check if the value of the token also matches - if (type == TokenType::Identifier) - { - if (values == entry.values.end() || values->textValue != token.getStringValue()) - { - valid = false; - break; - } - - values++; - } else if (type == TokenType::Integer) - { - if (values == entry.values.end() || values->intValue != token.intValue) - { - valid = false; - break; - } - - values++; - } - } - - if (valid && values == entry.values.end()) - { - result = entry.result; - return true; - } - - parser.getTokenizer()->setPosition(pos); - } - - return false; -} - -// file: Parser/Tokenizer.cpp -#include <algorithm> - - -// -// Tokenizer -// - -std::vector<std::vector<Token>> Tokenizer::equValues; - -Tokenizer::Tokenizer() -{ - position.it = tokens.begin(); - invalidToken.type = TokenType::Invalid; - invalidToken.setOriginalText(L"Unexpected end of token stream"); -} - -bool Tokenizer::processElement(TokenList::iterator& it) -{ - if (it == tokens.end()) - return false; - - while ((*it).checked == false) - { - bool replaced = false; - if ((*it).type == TokenType::Identifier) - { - const std::wstring stringValue = (*it).getStringValue(); - for (const Replacement& replacement: replacements) - { - // if the identifier matches, add all of its tokens - if (replacement.identifier == stringValue) - { - TokenList::iterator insertIt = it; - insertIt++; - - // replace old token with the new tokens - // replace the first token manually so that any iterators - // are still guaranteed to be valid - (*it) = replacement.value[0]; - tokens.insert(insertIt,replacement.value.begin()+1, replacement.value.end()); - - // If the value at this position didn't change, then just keep going. - // Otherwise we'd be stuck in an endless replace loop - if (stringValue != (*it).getStringValue()) - replaced = true; - break; - } - } - - if (replaced) - continue; - - // check for equs - size_t index; - if (Global.symbolTable.findEquation(stringValue,Global.FileInfo.FileNum,Global.Section,index)) - { - TokenList::iterator nextIt = it; - std::advance(nextIt, 1); - - // check if this is another equ with the same name. - // if so, keep equ redefinitions for later error handling - if (nextIt != tokens.end() && nextIt->type == TokenType::Equ) - break; - - // make room for the replacement tokens - const std::vector<Token>& replacement = equValues[index]; - tokens.insert(nextIt, replacement.size()-1, {}); - - // insert replacement tokens, while keeping the file info of the original token - Token originalToken = *it; - - TokenList::iterator insertIt = it; - for (const Token& token: replacement) - { - (*insertIt) = token; - insertIt->line = originalToken.line; - insertIt->column = originalToken.column; - std::advance(insertIt, 1); - } - - replaced = true; - continue; - } - } - - if (replaced == false) - (*it).checked = true; - } - - return true; -} - -const Token& Tokenizer::nextToken() -{ - if (processElement(position.it) == false) - return invalidToken; - - return *position.it++; -} - -const Token& Tokenizer::peekToken(int ahead) -{ - auto it = position.it; - for (int i = 0; i < ahead; i++) - { - if (processElement(it) == false) - return invalidToken; - - it++; - } - - if (processElement(it) == false) - return invalidToken; - - return *it; -} - -void Tokenizer::eatTokens(int num) -{ - for (int i = 0; i < num; i++) - { - if (processElement(position.it) == false) - break; - position.it++; - } -} - -void Tokenizer::skipLookahead() -{ - //position.index = tokens.size(); -} - -std::vector<Token> Tokenizer::getTokens(TokenizerPosition start, TokenizerPosition end) const -{ - std::vector<Token> result; - - for (auto it = start.it; it != end.it; it++) - { - Token tok = *it; - tok.checked = false; - result.push_back(tok); - } - - return result; -} - -void Tokenizer::registerReplacement(const std::wstring& identifier, std::vector<Token>& tokens) -{ - Replacement replacement { identifier, tokens }; - replacements.push_back(replacement); -} - -void Tokenizer::registerReplacement(const std::wstring& identifier, const std::wstring& newValue) -{ - // Ensure the new identifier is lower case as it would be as a normally parsed string - std::wstring lowerCase = newValue; - std::transform(lowerCase.begin(), lowerCase.end(), lowerCase.begin(), ::towlower); - - Token tok; - tok.type = TokenType::Identifier; - tok.setStringValue(lowerCase); - tok.setOriginalText(newValue); - - Replacement replacement; - replacement.identifier = identifier; - replacement.value.push_back(tok); - - replacements.push_back(replacement); -} - -void Tokenizer::addToken(Token token) -{ - tokens.push_back(std::move(token)); -} - -size_t Tokenizer::addEquValue(const std::vector<Token>& tokens) -{ - size_t index = equValues.size(); - equValues.push_back(tokens); - return index; -} - -void Tokenizer::resetLookaheadCheckMarks() -{ - auto it = position.it; - while (it != tokens.end() && it->checked) - { - it->checked = false; - it++; - } -} - -// -// FileTokenizer -// - -inline bool isWhitespace(const std::wstring& text, size_t pos) -{ - if (pos >= text.size()) - return false; - - return text[pos] == ' ' || text[pos] == '\t'; -} - -inline bool isComment(const std::wstring& text, size_t pos) -{ - if (pos < text.size() && text[pos] == ';') - return true; - - if (pos+1 < text.size() && text[pos+0] == '/' && text[pos+1] == '/') - return true; - - return false; -} - -inline bool isContinuation(const std::wstring& text, size_t pos) -{ - if (pos >= text.size()) - return false; - - return text[pos] == '\\'; -} - -inline bool isBlockComment(const std::wstring& text, size_t pos){ - if (pos+1 < text.size() && text[pos+0] == '/' && text[pos+1] == '*') - return true; - - return false; -} - -inline bool isBlockCommentEnd(const std::wstring& text, size_t pos){ - if (pos+1 < text.size() && text[pos+0] == '*' && text[pos+1] == '/') - return true; - - return false; -} - -void FileTokenizer::skipWhitespace() -{ - while (true) - { - if (isWhitespace(currentLine,linePos)) - { - do { linePos++; } while (isWhitespace(currentLine,linePos)); - } else if (isComment(currentLine,linePos)) - { - linePos = currentLine.size(); - } else if (isBlockComment(currentLine,linePos)) - { - linePos += 2; - while(!isBlockCommentEnd(currentLine,linePos)) - { - linePos++; - if (linePos >= currentLine.size()) - { - if (isInputAtEnd()) - { - createToken(TokenType::Invalid,linePos,L"Unexpected end of file in block comment"); - addToken(token); - return; - } - currentLine = input->readLine(); - linePos = 0; - lineNumber++; - } - } - linePos += 2; - } else - { - break; - } - } -} - -void FileTokenizer::createToken(TokenType type, size_t length) -{ - token.type = type; - token.line = lineNumber; - token.column = linePos+1; - token.setOriginalText(currentLine,linePos,length); - - linePos += length; -} - -void FileTokenizer::createToken(TokenType type, size_t length, int64_t value) -{ - token.type = type; - token.line = lineNumber; - token.column = linePos+1; - token.setOriginalText(currentLine,linePos,length); - token.intValue = value; - - linePos += length; -} - -void FileTokenizer::createToken(TokenType type, size_t length, double value) -{ - token.type = type; - token.line = lineNumber; - token.column = linePos+1; - token.setOriginalText(currentLine,linePos,length); - token.floatValue = value; - - linePos += length; -} - -void FileTokenizer::createToken(TokenType type, size_t length, const std::wstring& value) -{ - createToken(type, length, value, 0, value.length()); -} - -void FileTokenizer::createToken(TokenType type, size_t length, const std::wstring& value, size_t valuePos, size_t valueLength) -{ - token.type = type; - token.line = lineNumber; - token.column = linePos+1; - token.setOriginalText(currentLine,linePos,length); - token.setStringValue(value,valuePos,valueLength); - - linePos += length; -} - -void FileTokenizer::createTokenCurrentString(TokenType type, size_t length) -{ - token.type = type; - token.line = lineNumber; - token.column = linePos+1; - token.setStringAndOriginalValue(currentLine,linePos,length); - - linePos += length; -} - -bool FileTokenizer::parseOperator() -{ - wchar_t first = currentLine[linePos]; - wchar_t second = linePos+1 >= currentLine.size() ? '\0' : currentLine[linePos+1]; - - switch (first) - { - case '(': - createToken(TokenType::LParen,1); - return true; - case ')': - createToken(TokenType::RParen,1); - return true; - case '+': - createToken(TokenType::Plus,1); - return true; - case '-': - createToken(TokenType::Minus,1); - return true; - case '*': - createToken(TokenType::Mult,1); - return true; - case '/': - createToken(TokenType::Div,1); - return true; - case '%': - createToken(TokenType::Mod,1); - return true; - case '^': - createToken(TokenType::Caret,1); - return true; - case '~': - createToken(TokenType::Tilde,1); - return true; - case '<': - if (second == '<') - createToken(TokenType::LeftShift,2); - else if (second == '=') - createToken(TokenType::LessEqual,2); - else - createToken(TokenType::Less,1); - return true; - case '>': - if (second == '>') - createToken(TokenType::RightShift,2); - else if (second == '=') - createToken(TokenType::GreaterEqual,2); - else - createToken(TokenType::Greater,1); - return true; - case '=': - if (second == '=') - createToken(TokenType::Equal,2); - else - createToken(TokenType::Assign,1); - return true; - case '!': - if (second == '=') - createToken(TokenType::NotEqual,2); - else - createToken(TokenType::Exclamation,1); - return true; - case '&': - if (second == '&') - createToken(TokenType::LogAnd,2); - else - createToken(TokenType::BitAnd,1); - return true; - case '|': - if (second == '|') - createToken(TokenType::LogOr,2); - else - createToken(TokenType::BitOr,1); - return true; - case '?': - createToken(TokenType::Question,1); - return true; - case ':': - if (second == ':') - createToken(TokenType::Separator,2); - else - createToken(TokenType::Colon,1); - return true; - case ',': - createToken(TokenType::Comma,1); - return true; - case '[': - createToken(TokenType::LBrack,1); - return true; - case ']': - createToken(TokenType::RBrack,1); - return true; - case '#': - createToken(TokenType::Hash,1); - return true; - case '{': - createToken(TokenType::LBrace,1); - return true; - case '}': - createToken(TokenType::RBrace,1); - return true; - case '$': - createToken(TokenType::Dollar,1); - return true; - case L'\U000000B0': // degree sign - createToken(TokenType::Degree,1); - return true; - } - - return false; -} - -bool FileTokenizer::convertInteger(size_t start, size_t end, int64_t& result) -{ - return stringToInt(currentLine, start, end, result); -} - -bool FileTokenizer::convertFloat(size_t start, size_t end, double& result) -{ - std::wstring str = currentLine.substr(start, end - start); - wchar_t* end_ptr; - - result = wcstod(str.c_str(), &end_ptr); - return str.c_str() + str.size() == end_ptr; -} - -Token FileTokenizer::loadToken() -{ - if (isInputAtEnd()) - { - createToken(TokenType::Invalid,0); - return std::move(token); - } - - size_t pos = linePos; - - if (equActive) - { - while (pos < currentLine.size() && !isComment(currentLine,pos)) - pos++; - - createTokenCurrentString(TokenType::EquValue,pos-linePos); - - equActive = false; - return std::move(token); - } - - if (parseOperator()) - return std::move(token); - - wchar_t first = currentLine[pos]; - - // character constants - if (first == '\'' && pos+2 < currentLine.size() && currentLine[pos+2] == '\'') - { - createToken(TokenType::Integer,3,(int64_t)currentLine[pos+1]); - return std::move(token); - } - - // strings - if (first == '"') - { - std::wstring text; - pos++; - - bool valid = false; - while (pos < currentLine.size()) - { - if (pos+1 < currentLine.size() && currentLine[pos] == '\\') - { - if (currentLine[pos+1] == '"') - { - text += '"'; - pos += 2; - continue; - } - - if (currentLine[pos+1] == '\\') - { - text += '\\'; - pos += 2; - continue; - } - } - - if (currentLine[pos] == '"') - { - pos++; - valid = true; - break; - } - - text += currentLine[pos++]; - } - - if (!valid) - { - createToken(TokenType::Invalid,pos-linePos,L"Unexpected end of line in string constant"); - return std::move(token); - } - - createToken(TokenType::String,pos-linePos,text); - return std::move(token); - } - - // numbers - if (first >= '0' && first <= '9') - { - // find end of number - size_t start = pos; - size_t end = pos; - bool isValid = true; - bool foundPoint = false; - bool foundExp = false; - bool foundExpSign = false; - bool isHex = start+1 < currentLine.size() && currentLine[start] == '0' && towlower(currentLine[start+1]) == 'x'; - - while (end < currentLine.size() && (iswalnum(currentLine[end]) || currentLine[end] == '.')) - { - if (currentLine[end] == '.') - { - if (foundExp || foundPoint) - isValid = false; - foundPoint = true; - } else if (towlower(currentLine[end]) == 'h' && !foundExpSign) { - isHex = true; - } else if (towlower(currentLine[end]) == 'e' && !isHex) - { - if (foundExp) - { - isValid = false; - } else if (end+1 < currentLine.size() && (currentLine[end+1] == '+' || currentLine[end+1] == '-')){ - end++; - if (end+1 >= currentLine.size() || !iswalnum(currentLine[end+1])) - isValid = false; - foundExpSign = true; - } - foundExp = true; - } - - end++; - } - - bool isFloat = foundPoint || (foundExp && !isHex); - - if (!isFloat) - { - int64_t value; - if (convertInteger(start,end,value) == false) - { - createTokenCurrentString(TokenType::NumberString,end-start); - return std::move(token); - } - - createToken(TokenType::Integer,end-start,value); - } else { // isFloat - double value; - if (isValid == false) - { - createToken(TokenType::Invalid,end-start,L"Invalid floating point number"); - return std::move(token); - } - - if (convertFloat(start,end,value) == false) - { - createTokenCurrentString(TokenType::NumberString,end-start); - return std::move(token); - } - - createToken(TokenType::Float,end-start,value); - } - - return std::move(token); - } - - // identifiers - bool isFirst = true; - while (pos < currentLine.size() && Global.symbolTable.isValidSymbolCharacter(currentLine[pos],isFirst)) - { - pos++; - isFirst = false; - } - - if (pos == linePos) - { - std::wstring text = formatString(L"Invalid input '%c'",currentLine[pos]); - createToken(TokenType::Invalid,1,text); - return std::move(token); - } - - std::wstring text = currentLine.substr(linePos,pos-linePos); - bool textLowered = false; - // Lowercase is common, let's try to avoid a copy. - if (std::any_of(text.begin(), text.end(), ::iswupper)) - { - std::transform(text.begin(), text.end(), text.begin(), ::towlower); - textLowered = true; - } - - if (text == L"equ") - { - createToken(TokenType::Equ,pos-linePos); - equActive = true; - } else if (textLowered) { - createToken(TokenType::Identifier,pos-linePos,text); - } else { - createTokenCurrentString(TokenType::Identifier,pos-linePos); - } - - return std::move(token); -} - -bool FileTokenizer::init(TextFile* input) -{ - clearTokens(); - - lineNumber = 1; - linePos = 0; - equActive = false; - currentLine = input->readLine(); - - this->input = input; - if (input != nullptr && input->isOpen()) - { - while (!isInputAtEnd()) - { - bool addSeparator = true; - - skipWhitespace(); - if (isContinuation(currentLine, linePos)) - { - linePos++; - skipWhitespace(); - if (linePos < currentLine.size()) - { - createToken(TokenType::Invalid,0, - L"Unexpected character after line continuation character"); - addToken(token); - } - - addSeparator = false; - } else if(linePos < currentLine.size()) - { - addToken(std::move(loadToken())); - } - - if (linePos >= currentLine.size()) - { - if (addSeparator) - { - createToken(TokenType::Separator,0); - addToken(token); - } - - if (input->atEnd()) - break; - - currentLine = input->readLine(); - linePos = 0; - lineNumber++; - } - } - - resetPosition(); - return true; - } - - return false; -} - -// file: Util/ByteArray.cpp - -ByteArray::ByteArray() -{ - data_ = nullptr; - size_ = allocatedSize_ = 0; -} - -ByteArray::ByteArray(const ByteArray& other) -{ - data_ = nullptr; - size_ = allocatedSize_ = 0; - append(other); -} - -ByteArray::ByteArray(byte* data, size_t size) -{ - data_ = nullptr; - size_ = allocatedSize_ = 0; - append(data,size); -} - -ByteArray::ByteArray(ByteArray&& other) -{ - data_ = other.data_; - size_ = other.size_; - allocatedSize_ = other.allocatedSize_; - other.data_ = nullptr; - other.allocatedSize_ = other.size_ = 0; -} - -ByteArray::~ByteArray() -{ - free(data_); -} - -ByteArray& ByteArray::operator=(ByteArray& other) -{ - free(data_); - data_ = nullptr; - size_ = allocatedSize_ = 0; - append(other); - - return *this; -} - -ByteArray& ByteArray::operator=(ByteArray&& other) -{ - data_ = other.data_; - size_ = other.size_; - allocatedSize_ = other.allocatedSize_; - other.data_ = nullptr; - other.allocatedSize_ = other.size_ = 0; - return *this; -} - -void ByteArray::grow(size_t neededSize) -{ - if (neededSize < allocatedSize_) return; - - // align to next 0.5kb... it's a start - allocatedSize_ = ((neededSize+511)/512)*512; - if (data_ == nullptr) - { - data_ = (byte*) malloc(allocatedSize_); - } else { - data_ = (byte*) realloc(data_,allocatedSize_); - } -} - -size_t ByteArray::append(const ByteArray& other) -{ - size_t oldSize = size(); - size_t otherSize = other.size(); - grow(size()+otherSize); - memcpy(&data_[size_],other.data(),otherSize); - size_ += otherSize; - return oldSize; -} - -size_t ByteArray::append(void* data, size_t size) -{ - size_t oldSize = this->size(); - grow(this->size()+size); - memcpy(&data_[size_],data,size); - this->size_ += size; - return oldSize; -} - -void ByteArray::replaceBytes(size_t pos, byte* data, size_t size) -{ - for (size_t i = 0; i < size; i++) - { - replaceByte(pos+i,data[i]); - } -} - -void ByteArray::reserveBytes(size_t count, byte value) -{ - grow(this->size()+count); - memset(&data_[size_],value,count); - size_ += count; -} - -void ByteArray::alignSize(size_t alignment) -{ - if (alignment <= 0) return; - - while (size_ % alignment) - { - appendByte(0); - } -} - -void ByteArray::resize(size_t newSize) -{ - grow(newSize); - size_ = newSize; -} - -ByteArray ByteArray::mid(size_t start, ssize_t length) -{ - ByteArray ret; - - if (length < 0) - length = size_-start; - - if (start >= size_) - return ret; - - ret.grow(length); - ret.size_ = length; - memcpy(ret.data_,&data_[start],length); - return ret; -} - -ByteArray ByteArray::fromFile(const std::wstring& fileName, long start, size_t size) -{ - ByteArray ret; - - FILE* input = openFile(fileName,OpenFileMode::ReadBinary); - if (input == nullptr) - return ret; - - fseek(input,0,SEEK_END); - long fileSize = ftell(input); - - if (start >= fileSize) - { - fclose(input); - return ret; - } - - if (size == 0 || start+(long)size > fileSize) - size = fileSize-start; - - fseek(input,start,SEEK_SET); - - ret.grow(size); - ret.size_ = fread(ret.data(),1,size,input); - fclose(input); - - return ret; -} - -bool ByteArray::toFile(const std::wstring& fileName) -{ - FILE* output = openFile(fileName,OpenFileMode::WriteBinary); - if (output == nullptr) return false; - size_t length = fwrite(data_,1,size_,output); - fclose(output); - return length == size_; -} - -// file: Util/CRC.cpp -#include <stdio.h> - -const unsigned short Crc16Table[] = /* CRC lookup table */ -{ - 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, - 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, - 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, - 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, - 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, - 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, - 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, - 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, - 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, - 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, - 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, - 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, - 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, - 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, - 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, - 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, - 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, - 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, - 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, - 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, - 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, - 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, - 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, - 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, - 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, - 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, - 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, - 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, - 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, - 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, - 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, - 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 -}; - -const unsigned int Crc32Table[256] = { - 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3, - 0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, - 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7, - 0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5, - 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B, - 0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59, - 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F, - 0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D, - 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433, - 0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01, - 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457, - 0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65, - 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB, - 0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, - 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F, - 0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD, - 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683, - 0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1, - 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7, - 0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5, - 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B, - 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79, - 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F, - 0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D, - 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713, - 0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21, - 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777, - 0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, - 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB, - 0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9, - 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF, - 0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D -}; - -unsigned short getCrc16(unsigned char* Source, size_t len) -{ - unsigned short crc = 0xFFFF; - - while (len--) - { - crc = (crc >> 8) ^ Crc16Table[(crc ^ *Source++) & 0xFF]; - } - return crc; -} - -unsigned int getCrc32(unsigned char* Source, size_t len) -{ - unsigned int crc = 0xFFFFFFFF; - - while (len--) - { - crc = (crc >> 8) ^ Crc32Table[(crc & 0xFF) ^ *Source++]; - } - - return crc ^ 0xffffffff; -} - - -unsigned int getChecksum(unsigned char* Source, size_t len) -{ - int checksum = 0; - for (size_t i = 0; i < len; i++) - checksum += *Source++; - return checksum; -} - -// file: Util/EncodingTable.cpp - -#define MAXHEXLENGTH 32 - -Trie::Trie() -{ - Node root { 0, false, 0 }; - nodes.push_back(root); -} - -void Trie::insert(const wchar_t* text, size_t value) -{ - size_t node = 0; // root node - - // traverse existing nodes - while (*text != 0) - { - LookupEntry lookupEntry { node, *text }; - auto it = lookup.find(lookupEntry); - if (it == lookup.end()) - break; - - node = it->second; - text++; - } - - // add new nodes as necessary - while (*text != 0) - { - Node newNode { nodes.size(), false, 0 }; - nodes.push_back(newNode); - - LookupEntry lookupEntry { node, *text }; - lookup[lookupEntry] = newNode.index; - node = newNode.index; - text++; - } - - // set value - nodes[node].hasValue = true; - nodes[node].value = value; -} - -void Trie::insert(wchar_t character, size_t value) -{ - wchar_t str[2]; - str[0] = character; - str[1] = 0; - insert(str,value); -} - -bool Trie::findLongestPrefix(const wchar_t* text, size_t& result) -{ - size_t node = 0; // root node - size_t valueNode = 0; // remember last node that had a value - - while (*text != 0) - { - if (nodes[node].hasValue) - valueNode = node; - - LookupEntry lookupEntry { node, *text++ }; - auto it = lookup.find(lookupEntry); - - if (it == lookup.end()) - break; - - node = it->second; - } - - if (nodes[node].hasValue) - valueNode = node; - - result = nodes[valueNode].value; - return nodes[valueNode].hasValue; -} - -EncodingTable::EncodingTable() -{ - -} - -EncodingTable::~EncodingTable() -{ - -} - -void EncodingTable::clear() -{ - hexData.clear(); - entries.clear(); -} - -int parseHexString(std::wstring& hex, unsigned char* dest) -{ - for (size_t i = 0; i < hex.size(); i++) - { - wchar_t source = towlower(hex[i]); - int value; - - if (source >= 'a' && source <= 'f') - { - value = source-'a'+10; - } else if (source >= '0' && source <= '9') - { - value = source-'0'; - } else { - return -1; - } - - size_t index = i/2; - if (i % 2) - dest[index] = (dest[index] << 4) | value; - else - dest[index] = value; - } - - return (int) hex.size()/2; -} - -bool EncodingTable::load(const std::wstring& fileName, TextFile::Encoding encoding) -{ - unsigned char hexBuffer[MAXHEXLENGTH]; - - TextFile input; - if (input.open(fileName,TextFile::Read,encoding) == false) - return false; - - hexData.clear(); - entries.clear(); - setTerminationEntry((unsigned char*)"\0",1); - - while (!input.atEnd()) - { - std::wstring line = input.readLine(); - if (line.empty() || line[0] == '*') continue; - - if (line[0] == '/') - { - std::wstring hex = line.substr(1); - if (hex.empty() || hex.length() > 2*MAXHEXLENGTH) - { - // error - continue; - } - - int length = parseHexString(hex,hexBuffer); - if (length == -1) - { - // error - continue; - } - - setTerminationEntry(hexBuffer,length); - } else { - size_t pos = line.find(L'='); - std::wstring hex = line.substr(0,pos); - std::wstring value = line.substr(pos+1); - - if (hex.empty() || value.empty() || hex.length() > 2*MAXHEXLENGTH) - { - // error - continue; - } - - int length = parseHexString(hex,hexBuffer); - if (length == -1) - { - // error - continue; - } - - addEntry(hexBuffer,length,value); - } - } - - return true; -} - -void EncodingTable::addEntry(unsigned char* hex, size_t hexLength, const std::wstring& value) -{ - if (value.size() == 0) - return; - - // insert into trie - size_t index = entries.size(); - lookup.insert(value.c_str(),index); - - // add entry - TableEntry entry; - entry.hexPos = hexData.append(hex,hexLength); - entry.hexLen = hexLength; - entry.valueLen = value.size(); - - entries.push_back(entry); -} - -void EncodingTable::addEntry(unsigned char* hex, size_t hexLength, wchar_t value) -{ - if (value == '\0') - return; - - // insert into trie - size_t index = entries.size(); - lookup.insert(value,index); - - // add entry - TableEntry entry; - entry.hexPos = hexData.append(hex,hexLength); - entry.hexLen = hexLength; - entry.valueLen = 1; - - entries.push_back(entry); - -} - -void EncodingTable::setTerminationEntry(unsigned char* hex, size_t hexLength) -{ - terminationEntry.hexPos = hexData.append(hex,hexLength); - terminationEntry.hexLen = hexLength; - terminationEntry.valueLen = 0; -} - -ByteArray EncodingTable::encodeString(const std::wstring& str, bool writeTermination) -{ - ByteArray result; - - size_t pos = 0; - while (pos < str.size()) - { - size_t index; - if (lookup.findLongestPrefix(str.c_str()+pos,index) == false) - { - // error - return ByteArray(); - } - - TableEntry& entry = entries[index]; - for (size_t i = 0; i < entry.hexLen; i++) - { - result.appendByte(hexData[entry.hexPos+i]); - } - - pos += entry.valueLen; - } - - if (writeTermination) - { - TableEntry& entry = terminationEntry; - for (size_t i = 0; i < entry.hexLen; i++) - { - result.appendByte(hexData[entry.hexPos+i]); - } - } - - return result; -} - -ByteArray EncodingTable::encodeTermination() -{ - ByteArray result; - - TableEntry& entry = terminationEntry; - for (size_t i = 0; i < entry.hexLen; i++) - { - result.appendByte(hexData[entry.hexPos+i]); - } - - return result; -} - -// file: Util/FileClasses.cpp - -const wchar_t SjisToUnicodeTable1[] = -{ - // 0X0080 to 0X00FF - 0x0080, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67, 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F, - 0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77, 0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F, - 0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87, 0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F, - 0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97, 0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, -}; - -const wchar_t SjisToUnicodeTable2[] = -{ - // 0X8100 to 0X81FF - 0x3000, 0x3001, 0x3002, 0xFF0C, 0xFF0E, 0x30FB, 0xFF1A, 0xFF1B, 0xFF1F, 0xFF01, 0x309B, 0x309C, 0x00B4, 0xFF40, 0x00A8, 0xFF3E, - 0xFFE3, 0xFF3F, 0x30FD, 0x30FE, 0x309D, 0x309E, 0x3003, 0x4EDD, 0x3005, 0x3006, 0x3007, 0x30FC, 0x2015, 0x2010, 0xFF0F, 0xFF3C, - 0xFF5E, 0x2225, 0xFF5C, 0x2026, 0x2025, 0x2018, 0x2019, 0x201C, 0x201D, 0xFF08, 0xFF09, 0x3014, 0x3015, 0xFF3B, 0xFF3D, 0xFF5B, - 0xFF5D, 0x3008, 0x3009, 0x300A, 0x300B, 0x300C, 0x300D, 0x300E, 0x300F, 0x3010, 0x3011, 0xFF0B, 0xFF0D, 0x00B1, 0x00D7, 0xFFFF, - 0x00F7, 0xFF1D, 0x2260, 0xFF1C, 0xFF1E, 0x2266, 0x2267, 0x221E, 0x2234, 0x2642, 0x2640, 0x00B0, 0x2032, 0x2033, 0x2103, 0xFFE5, - 0xFF04, 0xFFE0, 0xFFE1, 0xFF05, 0xFF03, 0xFF06, 0xFF0A, 0xFF20, 0x00A7, 0x2606, 0x2605, 0x25CB, 0x25CF, 0x25CE, 0x25C7, 0x25C6, - 0x25A1, 0x25A0, 0x25B3, 0x25B2, 0x25BD, 0x25BC, 0x203B, 0x3012, 0x2192, 0x2190, 0x2191, 0x2193, 0x3013, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2208, 0x220B, 0x2286, 0x2287, 0x2282, 0x2283, 0x222A, 0x2229, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2227, 0x2228, 0xFFE2, 0x21D2, 0x21D4, 0x2200, 0x2203, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2220, 0x22A5, 0x2312, 0x2202, 0x2207, 0x2261, - 0x2252, 0x226A, 0x226B, 0x221A, 0x223D, 0x221D, 0x2235, 0x222B, 0x222C, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0x212B, 0x2030, 0x266F, 0x266D, 0x266A, 0x2020, 0x2021, 0x00B6, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x25EF, 0xFFFF, 0xFFFF, 0xFFFF, - // 0X8200 to 0X82FF - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFF10, - 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, 0xFF18, 0xFF19, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, - 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, - 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x3041, - 0x3042, 0x3043, 0x3044, 0x3045, 0x3046, 0x3047, 0x3048, 0x3049, 0x304A, 0x304B, 0x304C, 0x304D, 0x304E, 0x304F, 0x3050, 0x3051, - 0x3052, 0x3053, 0x3054, 0x3055, 0x3056, 0x3057, 0x3058, 0x3059, 0x305A, 0x305B, 0x305C, 0x305D, 0x305E, 0x305F, 0x3060, 0x3061, - 0x3062, 0x3063, 0x3064, 0x3065, 0x3066, 0x3067, 0x3068, 0x3069, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F, 0x3070, 0x3071, - 0x3072, 0x3073, 0x3074, 0x3075, 0x3076, 0x3077, 0x3078, 0x3079, 0x307A, 0x307B, 0x307C, 0x307D, 0x307E, 0x307F, 0x3080, 0x3081, - 0x3082, 0x3083, 0x3084, 0x3085, 0x3086, 0x3087, 0x3088, 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308E, 0x308F, 0x3090, 0x3091, - 0x3092, 0x3093, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - // 0X8300 to 0X83FF - 0x30A1, 0x30A2, 0x30A3, 0x30A4, 0x30A5, 0x30A6, 0x30A7, 0x30A8, 0x30A9, 0x30AA, 0x30AB, 0x30AC, 0x30AD, 0x30AE, 0x30AF, 0x30B0, - 0x30B1, 0x30B2, 0x30B3, 0x30B4, 0x30B5, 0x30B6, 0x30B7, 0x30B8, 0x30B9, 0x30BA, 0x30BB, 0x30BC, 0x30BD, 0x30BE, 0x30BF, 0x30C0, - 0x30C1, 0x30C2, 0x30C3, 0x30C4, 0x30C5, 0x30C6, 0x30C7, 0x30C8, 0x30C9, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF, 0x30D0, - 0x30D1, 0x30D2, 0x30D3, 0x30D4, 0x30D5, 0x30D6, 0x30D7, 0x30D8, 0x30D9, 0x30DA, 0x30DB, 0x30DC, 0x30DD, 0x30DE, 0x30DF, 0xFFFF, - 0x30E0, 0x30E1, 0x30E2, 0x30E3, 0x30E4, 0x30E5, 0x30E6, 0x30E7, 0x30E8, 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EE, 0x30EF, - 0x30F0, 0x30F1, 0x30F2, 0x30F3, 0x30F4, 0x30F5, 0x30F6, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0391, - 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, - 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x03B1, - 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, - 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - // 0X8400 to 0X84FF - 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, - 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, - 0x042F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0451, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0xFFFF, - 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, - 0x044E, 0x044F, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x2500, - 0x2502, 0x250C, 0x2510, 0x2518, 0x2514, 0x251C, 0x252C, 0x2524, 0x2534, 0x253C, 0x2501, 0x2503, 0x250F, 0x2513, 0x251B, 0x2517, - 0x2523, 0x2533, 0x252B, 0x253B, 0x254B, 0x2520, 0x252F, 0x2528, 0x2537, 0x253F, 0x251D, 0x2530, 0x2525, 0x2538, 0x2542, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, -}; - -const wchar_t SjisToUnicodeTable3[] = +const wchar_t SjisToUnicodeTable3[] = { // 0X8700 to 0X87FF 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467, 0x2468, 0x2469, 0x246A, 0x246B, 0x246C, 0x246D, 0x246E, 0x246F, @@ -18483,1378 +14525,5336 @@ const wchar_t SjisToUnicodeTable3[] = 0x6E9F, 0x6F41, 0x6F11, 0x704C, 0x6EEC, 0x6EF8, 0x6EFE, 0x6F3F, 0x6EF2, 0x6F31, 0x6EEF, 0x6F32, 0x6ECC, 0xFFFF, 0xFFFF, 0xFFFF, }; -const wchar_t SjisToUnicodeTable4[] = +const wchar_t SjisToUnicodeTable4[] = +{ + // 0XE000 to 0XE0FF + 0x6F3E, 0x6F13, 0x6EF7, 0x6F86, 0x6F7A, 0x6F78, 0x6F81, 0x6F80, 0x6F6F, 0x6F5B, 0x6FF3, 0x6F6D, 0x6F82, 0x6F7C, 0x6F58, 0x6F8E, + 0x6F91, 0x6FC2, 0x6F66, 0x6FB3, 0x6FA3, 0x6FA1, 0x6FA4, 0x6FB9, 0x6FC6, 0x6FAA, 0x6FDF, 0x6FD5, 0x6FEC, 0x6FD4, 0x6FD8, 0x6FF1, + 0x6FEE, 0x6FDB, 0x7009, 0x700B, 0x6FFA, 0x7011, 0x7001, 0x700F, 0x6FFE, 0x701B, 0x701A, 0x6F74, 0x701D, 0x7018, 0x701F, 0x7030, + 0x703E, 0x7032, 0x7051, 0x7063, 0x7099, 0x7092, 0x70AF, 0x70F1, 0x70AC, 0x70B8, 0x70B3, 0x70AE, 0x70DF, 0x70CB, 0x70DD, 0xFFFF, + 0x70D9, 0x7109, 0x70FD, 0x711C, 0x7119, 0x7165, 0x7155, 0x7188, 0x7166, 0x7162, 0x714C, 0x7156, 0x716C, 0x718F, 0x71FB, 0x7184, + 0x7195, 0x71A8, 0x71AC, 0x71D7, 0x71B9, 0x71BE, 0x71D2, 0x71C9, 0x71D4, 0x71CE, 0x71E0, 0x71EC, 0x71E7, 0x71F5, 0x71FC, 0x71F9, + 0x71FF, 0x720D, 0x7210, 0x721B, 0x7228, 0x722D, 0x722C, 0x7230, 0x7232, 0x723B, 0x723C, 0x723F, 0x7240, 0x7246, 0x724B, 0x7258, + 0x7274, 0x727E, 0x7282, 0x7281, 0x7287, 0x7292, 0x7296, 0x72A2, 0x72A7, 0x72B9, 0x72B2, 0x72C3, 0x72C6, 0x72C4, 0x72CE, 0x72D2, + 0x72E2, 0x72E0, 0x72E1, 0x72F9, 0x72F7, 0x500F, 0x7317, 0x730A, 0x731C, 0x7316, 0x731D, 0x7334, 0x732F, 0x7329, 0x7325, 0x733E, + 0x734E, 0x734F, 0x9ED8, 0x7357, 0x736A, 0x7368, 0x7370, 0x7378, 0x7375, 0x737B, 0x737A, 0x73C8, 0x73B3, 0x73CE, 0x73BB, 0x73C0, + 0x73E5, 0x73EE, 0x73DE, 0x74A2, 0x7405, 0x746F, 0x7425, 0x73F8, 0x7432, 0x743A, 0x7455, 0x743F, 0x745F, 0x7459, 0x7441, 0x745C, + 0x7469, 0x7470, 0x7463, 0x746A, 0x7476, 0x747E, 0x748B, 0x749E, 0x74A7, 0x74CA, 0x74CF, 0x74D4, 0x73F1, 0xFFFF, 0xFFFF, 0xFFFF, + // 0XE100 to 0XE1FF + 0x74E0, 0x74E3, 0x74E7, 0x74E9, 0x74EE, 0x74F2, 0x74F0, 0x74F1, 0x74F8, 0x74F7, 0x7504, 0x7503, 0x7505, 0x750C, 0x750E, 0x750D, + 0x7515, 0x7513, 0x751E, 0x7526, 0x752C, 0x753C, 0x7544, 0x754D, 0x754A, 0x7549, 0x755B, 0x7546, 0x755A, 0x7569, 0x7564, 0x7567, + 0x756B, 0x756D, 0x7578, 0x7576, 0x7586, 0x7587, 0x7574, 0x758A, 0x7589, 0x7582, 0x7594, 0x759A, 0x759D, 0x75A5, 0x75A3, 0x75C2, + 0x75B3, 0x75C3, 0x75B5, 0x75BD, 0x75B8, 0x75BC, 0x75B1, 0x75CD, 0x75CA, 0x75D2, 0x75D9, 0x75E3, 0x75DE, 0x75FE, 0x75FF, 0xFFFF, + 0x75FC, 0x7601, 0x75F0, 0x75FA, 0x75F2, 0x75F3, 0x760B, 0x760D, 0x7609, 0x761F, 0x7627, 0x7620, 0x7621, 0x7622, 0x7624, 0x7634, + 0x7630, 0x763B, 0x7647, 0x7648, 0x7646, 0x765C, 0x7658, 0x7661, 0x7662, 0x7668, 0x7669, 0x766A, 0x7667, 0x766C, 0x7670, 0x7672, + 0x7676, 0x7678, 0x767C, 0x7680, 0x7683, 0x7688, 0x768B, 0x768E, 0x7696, 0x7693, 0x7699, 0x769A, 0x76B0, 0x76B4, 0x76B8, 0x76B9, + 0x76BA, 0x76C2, 0x76CD, 0x76D6, 0x76D2, 0x76DE, 0x76E1, 0x76E5, 0x76E7, 0x76EA, 0x862F, 0x76FB, 0x7708, 0x7707, 0x7704, 0x7729, + 0x7724, 0x771E, 0x7725, 0x7726, 0x771B, 0x7737, 0x7738, 0x7747, 0x775A, 0x7768, 0x776B, 0x775B, 0x7765, 0x777F, 0x777E, 0x7779, + 0x778E, 0x778B, 0x7791, 0x77A0, 0x779E, 0x77B0, 0x77B6, 0x77B9, 0x77BF, 0x77BC, 0x77BD, 0x77BB, 0x77C7, 0x77CD, 0x77D7, 0x77DA, + 0x77DC, 0x77E3, 0x77EE, 0x77FC, 0x780C, 0x7812, 0x7926, 0x7820, 0x792A, 0x7845, 0x788E, 0x7874, 0x7886, 0x787C, 0x789A, 0x788C, + 0x78A3, 0x78B5, 0x78AA, 0x78AF, 0x78D1, 0x78C6, 0x78CB, 0x78D4, 0x78BE, 0x78BC, 0x78C5, 0x78CA, 0x78EC, 0xFFFF, 0xFFFF, 0xFFFF, + // 0XE200 to 0XE2FF + 0x78E7, 0x78DA, 0x78FD, 0x78F4, 0x7907, 0x7912, 0x7911, 0x7919, 0x792C, 0x792B, 0x7940, 0x7960, 0x7957, 0x795F, 0x795A, 0x7955, + 0x7953, 0x797A, 0x797F, 0x798A, 0x799D, 0x79A7, 0x9F4B, 0x79AA, 0x79AE, 0x79B3, 0x79B9, 0x79BA, 0x79C9, 0x79D5, 0x79E7, 0x79EC, + 0x79E1, 0x79E3, 0x7A08, 0x7A0D, 0x7A18, 0x7A19, 0x7A20, 0x7A1F, 0x7980, 0x7A31, 0x7A3B, 0x7A3E, 0x7A37, 0x7A43, 0x7A57, 0x7A49, + 0x7A61, 0x7A62, 0x7A69, 0x9F9D, 0x7A70, 0x7A79, 0x7A7D, 0x7A88, 0x7A97, 0x7A95, 0x7A98, 0x7A96, 0x7AA9, 0x7AC8, 0x7AB0, 0xFFFF, + 0x7AB6, 0x7AC5, 0x7AC4, 0x7ABF, 0x9083, 0x7AC7, 0x7ACA, 0x7ACD, 0x7ACF, 0x7AD5, 0x7AD3, 0x7AD9, 0x7ADA, 0x7ADD, 0x7AE1, 0x7AE2, + 0x7AE6, 0x7AED, 0x7AF0, 0x7B02, 0x7B0F, 0x7B0A, 0x7B06, 0x7B33, 0x7B18, 0x7B19, 0x7B1E, 0x7B35, 0x7B28, 0x7B36, 0x7B50, 0x7B7A, + 0x7B04, 0x7B4D, 0x7B0B, 0x7B4C, 0x7B45, 0x7B75, 0x7B65, 0x7B74, 0x7B67, 0x7B70, 0x7B71, 0x7B6C, 0x7B6E, 0x7B9D, 0x7B98, 0x7B9F, + 0x7B8D, 0x7B9C, 0x7B9A, 0x7B8B, 0x7B92, 0x7B8F, 0x7B5D, 0x7B99, 0x7BCB, 0x7BC1, 0x7BCC, 0x7BCF, 0x7BB4, 0x7BC6, 0x7BDD, 0x7BE9, + 0x7C11, 0x7C14, 0x7BE6, 0x7BE5, 0x7C60, 0x7C00, 0x7C07, 0x7C13, 0x7BF3, 0x7BF7, 0x7C17, 0x7C0D, 0x7BF6, 0x7C23, 0x7C27, 0x7C2A, + 0x7C1F, 0x7C37, 0x7C2B, 0x7C3D, 0x7C4C, 0x7C43, 0x7C54, 0x7C4F, 0x7C40, 0x7C50, 0x7C58, 0x7C5F, 0x7C64, 0x7C56, 0x7C65, 0x7C6C, + 0x7C75, 0x7C83, 0x7C90, 0x7CA4, 0x7CAD, 0x7CA2, 0x7CAB, 0x7CA1, 0x7CA8, 0x7CB3, 0x7CB2, 0x7CB1, 0x7CAE, 0x7CB9, 0x7CBD, 0x7CC0, + 0x7CC5, 0x7CC2, 0x7CD8, 0x7CD2, 0x7CDC, 0x7CE2, 0x9B3B, 0x7CEF, 0x7CF2, 0x7CF4, 0x7CF6, 0x7CFA, 0x7D06, 0xFFFF, 0xFFFF, 0xFFFF, + // 0XE300 to 0XE3FF + 0x7D02, 0x7D1C, 0x7D15, 0x7D0A, 0x7D45, 0x7D4B, 0x7D2E, 0x7D32, 0x7D3F, 0x7D35, 0x7D46, 0x7D73, 0x7D56, 0x7D4E, 0x7D72, 0x7D68, + 0x7D6E, 0x7D4F, 0x7D63, 0x7D93, 0x7D89, 0x7D5B, 0x7D8F, 0x7D7D, 0x7D9B, 0x7DBA, 0x7DAE, 0x7DA3, 0x7DB5, 0x7DC7, 0x7DBD, 0x7DAB, + 0x7E3D, 0x7DA2, 0x7DAF, 0x7DDC, 0x7DB8, 0x7D9F, 0x7DB0, 0x7DD8, 0x7DDD, 0x7DE4, 0x7DDE, 0x7DFB, 0x7DF2, 0x7DE1, 0x7E05, 0x7E0A, + 0x7E23, 0x7E21, 0x7E12, 0x7E31, 0x7E1F, 0x7E09, 0x7E0B, 0x7E22, 0x7E46, 0x7E66, 0x7E3B, 0x7E35, 0x7E39, 0x7E43, 0x7E37, 0xFFFF, + 0x7E32, 0x7E3A, 0x7E67, 0x7E5D, 0x7E56, 0x7E5E, 0x7E59, 0x7E5A, 0x7E79, 0x7E6A, 0x7E69, 0x7E7C, 0x7E7B, 0x7E83, 0x7DD5, 0x7E7D, + 0x8FAE, 0x7E7F, 0x7E88, 0x7E89, 0x7E8C, 0x7E92, 0x7E90, 0x7E93, 0x7E94, 0x7E96, 0x7E8E, 0x7E9B, 0x7E9C, 0x7F38, 0x7F3A, 0x7F45, + 0x7F4C, 0x7F4D, 0x7F4E, 0x7F50, 0x7F51, 0x7F55, 0x7F54, 0x7F58, 0x7F5F, 0x7F60, 0x7F68, 0x7F69, 0x7F67, 0x7F78, 0x7F82, 0x7F86, + 0x7F83, 0x7F88, 0x7F87, 0x7F8C, 0x7F94, 0x7F9E, 0x7F9D, 0x7F9A, 0x7FA3, 0x7FAF, 0x7FB2, 0x7FB9, 0x7FAE, 0x7FB6, 0x7FB8, 0x8B71, + 0x7FC5, 0x7FC6, 0x7FCA, 0x7FD5, 0x7FD4, 0x7FE1, 0x7FE6, 0x7FE9, 0x7FF3, 0x7FF9, 0x98DC, 0x8006, 0x8004, 0x800B, 0x8012, 0x8018, + 0x8019, 0x801C, 0x8021, 0x8028, 0x803F, 0x803B, 0x804A, 0x8046, 0x8052, 0x8058, 0x805A, 0x805F, 0x8062, 0x8068, 0x8073, 0x8072, + 0x8070, 0x8076, 0x8079, 0x807D, 0x807F, 0x8084, 0x8086, 0x8085, 0x809B, 0x8093, 0x809A, 0x80AD, 0x5190, 0x80AC, 0x80DB, 0x80E5, + 0x80D9, 0x80DD, 0x80C4, 0x80DA, 0x80D6, 0x8109, 0x80EF, 0x80F1, 0x811B, 0x8129, 0x8123, 0x812F, 0x814B, 0xFFFF, 0xFFFF, 0xFFFF, + // 0XE400 to 0XE4FF + 0x968B, 0x8146, 0x813E, 0x8153, 0x8151, 0x80FC, 0x8171, 0x816E, 0x8165, 0x8166, 0x8174, 0x8183, 0x8188, 0x818A, 0x8180, 0x8182, + 0x81A0, 0x8195, 0x81A4, 0x81A3, 0x815F, 0x8193, 0x81A9, 0x81B0, 0x81B5, 0x81BE, 0x81B8, 0x81BD, 0x81C0, 0x81C2, 0x81BA, 0x81C9, + 0x81CD, 0x81D1, 0x81D9, 0x81D8, 0x81C8, 0x81DA, 0x81DF, 0x81E0, 0x81E7, 0x81FA, 0x81FB, 0x81FE, 0x8201, 0x8202, 0x8205, 0x8207, + 0x820A, 0x820D, 0x8210, 0x8216, 0x8229, 0x822B, 0x8238, 0x8233, 0x8240, 0x8259, 0x8258, 0x825D, 0x825A, 0x825F, 0x8264, 0xFFFF, + 0x8262, 0x8268, 0x826A, 0x826B, 0x822E, 0x8271, 0x8277, 0x8278, 0x827E, 0x828D, 0x8292, 0x82AB, 0x829F, 0x82BB, 0x82AC, 0x82E1, + 0x82E3, 0x82DF, 0x82D2, 0x82F4, 0x82F3, 0x82FA, 0x8393, 0x8303, 0x82FB, 0x82F9, 0x82DE, 0x8306, 0x82DC, 0x8309, 0x82D9, 0x8335, + 0x8334, 0x8316, 0x8332, 0x8331, 0x8340, 0x8339, 0x8350, 0x8345, 0x832F, 0x832B, 0x8317, 0x8318, 0x8385, 0x839A, 0x83AA, 0x839F, + 0x83A2, 0x8396, 0x8323, 0x838E, 0x8387, 0x838A, 0x837C, 0x83B5, 0x8373, 0x8375, 0x83A0, 0x8389, 0x83A8, 0x83F4, 0x8413, 0x83EB, + 0x83CE, 0x83FD, 0x8403, 0x83D8, 0x840B, 0x83C1, 0x83F7, 0x8407, 0x83E0, 0x83F2, 0x840D, 0x8422, 0x8420, 0x83BD, 0x8438, 0x8506, + 0x83FB, 0x846D, 0x842A, 0x843C, 0x855A, 0x8484, 0x8477, 0x846B, 0x84AD, 0x846E, 0x8482, 0x8469, 0x8446, 0x842C, 0x846F, 0x8479, + 0x8435, 0x84CA, 0x8462, 0x84B9, 0x84BF, 0x849F, 0x84D9, 0x84CD, 0x84BB, 0x84DA, 0x84D0, 0x84C1, 0x84C6, 0x84D6, 0x84A1, 0x8521, + 0x84FF, 0x84F4, 0x8517, 0x8518, 0x852C, 0x851F, 0x8515, 0x8514, 0x84FC, 0x8540, 0x8563, 0x8558, 0x8548, 0xFFFF, 0xFFFF, 0xFFFF, + // 0XE500 to 0XE5FF + 0x8541, 0x8602, 0x854B, 0x8555, 0x8580, 0x85A4, 0x8588, 0x8591, 0x858A, 0x85A8, 0x856D, 0x8594, 0x859B, 0x85EA, 0x8587, 0x859C, + 0x8577, 0x857E, 0x8590, 0x85C9, 0x85BA, 0x85CF, 0x85B9, 0x85D0, 0x85D5, 0x85DD, 0x85E5, 0x85DC, 0x85F9, 0x860A, 0x8613, 0x860B, + 0x85FE, 0x85FA, 0x8606, 0x8622, 0x861A, 0x8630, 0x863F, 0x864D, 0x4E55, 0x8654, 0x865F, 0x8667, 0x8671, 0x8693, 0x86A3, 0x86A9, + 0x86AA, 0x868B, 0x868C, 0x86B6, 0x86AF, 0x86C4, 0x86C6, 0x86B0, 0x86C9, 0x8823, 0x86AB, 0x86D4, 0x86DE, 0x86E9, 0x86EC, 0xFFFF, + 0x86DF, 0x86DB, 0x86EF, 0x8712, 0x8706, 0x8708, 0x8700, 0x8703, 0x86FB, 0x8711, 0x8709, 0x870D, 0x86F9, 0x870A, 0x8734, 0x873F, + 0x8737, 0x873B, 0x8725, 0x8729, 0x871A, 0x8760, 0x875F, 0x8778, 0x874C, 0x874E, 0x8774, 0x8757, 0x8768, 0x876E, 0x8759, 0x8753, + 0x8763, 0x876A, 0x8805, 0x87A2, 0x879F, 0x8782, 0x87AF, 0x87CB, 0x87BD, 0x87C0, 0x87D0, 0x96D6, 0x87AB, 0x87C4, 0x87B3, 0x87C7, + 0x87C6, 0x87BB, 0x87EF, 0x87F2, 0x87E0, 0x880F, 0x880D, 0x87FE, 0x87F6, 0x87F7, 0x880E, 0x87D2, 0x8811, 0x8816, 0x8815, 0x8822, + 0x8821, 0x8831, 0x8836, 0x8839, 0x8827, 0x883B, 0x8844, 0x8842, 0x8852, 0x8859, 0x885E, 0x8862, 0x886B, 0x8881, 0x887E, 0x889E, + 0x8875, 0x887D, 0x88B5, 0x8872, 0x8882, 0x8897, 0x8892, 0x88AE, 0x8899, 0x88A2, 0x888D, 0x88A4, 0x88B0, 0x88BF, 0x88B1, 0x88C3, + 0x88C4, 0x88D4, 0x88D8, 0x88D9, 0x88DD, 0x88F9, 0x8902, 0x88FC, 0x88F4, 0x88E8, 0x88F2, 0x8904, 0x890C, 0x890A, 0x8913, 0x8943, + 0x891E, 0x8925, 0x892A, 0x892B, 0x8941, 0x8944, 0x893B, 0x8936, 0x8938, 0x894C, 0x891D, 0x8960, 0x895E, 0xFFFF, 0xFFFF, 0xFFFF, + // 0XE600 to 0XE6FF + 0x8966, 0x8964, 0x896D, 0x896A, 0x896F, 0x8974, 0x8977, 0x897E, 0x8983, 0x8988, 0x898A, 0x8993, 0x8998, 0x89A1, 0x89A9, 0x89A6, + 0x89AC, 0x89AF, 0x89B2, 0x89BA, 0x89BD, 0x89BF, 0x89C0, 0x89DA, 0x89DC, 0x89DD, 0x89E7, 0x89F4, 0x89F8, 0x8A03, 0x8A16, 0x8A10, + 0x8A0C, 0x8A1B, 0x8A1D, 0x8A25, 0x8A36, 0x8A41, 0x8A5B, 0x8A52, 0x8A46, 0x8A48, 0x8A7C, 0x8A6D, 0x8A6C, 0x8A62, 0x8A85, 0x8A82, + 0x8A84, 0x8AA8, 0x8AA1, 0x8A91, 0x8AA5, 0x8AA6, 0x8A9A, 0x8AA3, 0x8AC4, 0x8ACD, 0x8AC2, 0x8ADA, 0x8AEB, 0x8AF3, 0x8AE7, 0xFFFF, + 0x8AE4, 0x8AF1, 0x8B14, 0x8AE0, 0x8AE2, 0x8AF7, 0x8ADE, 0x8ADB, 0x8B0C, 0x8B07, 0x8B1A, 0x8AE1, 0x8B16, 0x8B10, 0x8B17, 0x8B20, + 0x8B33, 0x97AB, 0x8B26, 0x8B2B, 0x8B3E, 0x8B28, 0x8B41, 0x8B4C, 0x8B4F, 0x8B4E, 0x8B49, 0x8B56, 0x8B5B, 0x8B5A, 0x8B6B, 0x8B5F, + 0x8B6C, 0x8B6F, 0x8B74, 0x8B7D, 0x8B80, 0x8B8C, 0x8B8E, 0x8B92, 0x8B93, 0x8B96, 0x8B99, 0x8B9A, 0x8C3A, 0x8C41, 0x8C3F, 0x8C48, + 0x8C4C, 0x8C4E, 0x8C50, 0x8C55, 0x8C62, 0x8C6C, 0x8C78, 0x8C7A, 0x8C82, 0x8C89, 0x8C85, 0x8C8A, 0x8C8D, 0x8C8E, 0x8C94, 0x8C7C, + 0x8C98, 0x621D, 0x8CAD, 0x8CAA, 0x8CBD, 0x8CB2, 0x8CB3, 0x8CAE, 0x8CB6, 0x8CC8, 0x8CC1, 0x8CE4, 0x8CE3, 0x8CDA, 0x8CFD, 0x8CFA, + 0x8CFB, 0x8D04, 0x8D05, 0x8D0A, 0x8D07, 0x8D0F, 0x8D0D, 0x8D10, 0x9F4E, 0x8D13, 0x8CCD, 0x8D14, 0x8D16, 0x8D67, 0x8D6D, 0x8D71, + 0x8D73, 0x8D81, 0x8D99, 0x8DC2, 0x8DBE, 0x8DBA, 0x8DCF, 0x8DDA, 0x8DD6, 0x8DCC, 0x8DDB, 0x8DCB, 0x8DEA, 0x8DEB, 0x8DDF, 0x8DE3, + 0x8DFC, 0x8E08, 0x8E09, 0x8DFF, 0x8E1D, 0x8E1E, 0x8E10, 0x8E1F, 0x8E42, 0x8E35, 0x8E30, 0x8E34, 0x8E4A, 0xFFFF, 0xFFFF, 0xFFFF, + // 0XE700 to 0XE7FF + 0x8E47, 0x8E49, 0x8E4C, 0x8E50, 0x8E48, 0x8E59, 0x8E64, 0x8E60, 0x8E2A, 0x8E63, 0x8E55, 0x8E76, 0x8E72, 0x8E7C, 0x8E81, 0x8E87, + 0x8E85, 0x8E84, 0x8E8B, 0x8E8A, 0x8E93, 0x8E91, 0x8E94, 0x8E99, 0x8EAA, 0x8EA1, 0x8EAC, 0x8EB0, 0x8EC6, 0x8EB1, 0x8EBE, 0x8EC5, + 0x8EC8, 0x8ECB, 0x8EDB, 0x8EE3, 0x8EFC, 0x8EFB, 0x8EEB, 0x8EFE, 0x8F0A, 0x8F05, 0x8F15, 0x8F12, 0x8F19, 0x8F13, 0x8F1C, 0x8F1F, + 0x8F1B, 0x8F0C, 0x8F26, 0x8F33, 0x8F3B, 0x8F39, 0x8F45, 0x8F42, 0x8F3E, 0x8F4C, 0x8F49, 0x8F46, 0x8F4E, 0x8F57, 0x8F5C, 0xFFFF, + 0x8F62, 0x8F63, 0x8F64, 0x8F9C, 0x8F9F, 0x8FA3, 0x8FAD, 0x8FAF, 0x8FB7, 0x8FDA, 0x8FE5, 0x8FE2, 0x8FEA, 0x8FEF, 0x9087, 0x8FF4, + 0x9005, 0x8FF9, 0x8FFA, 0x9011, 0x9015, 0x9021, 0x900D, 0x901E, 0x9016, 0x900B, 0x9027, 0x9036, 0x9035, 0x9039, 0x8FF8, 0x904F, + 0x9050, 0x9051, 0x9052, 0x900E, 0x9049, 0x903E, 0x9056, 0x9058, 0x905E, 0x9068, 0x906F, 0x9076, 0x96A8, 0x9072, 0x9082, 0x907D, + 0x9081, 0x9080, 0x908A, 0x9089, 0x908F, 0x90A8, 0x90AF, 0x90B1, 0x90B5, 0x90E2, 0x90E4, 0x6248, 0x90DB, 0x9102, 0x9112, 0x9119, + 0x9132, 0x9130, 0x914A, 0x9156, 0x9158, 0x9163, 0x9165, 0x9169, 0x9173, 0x9172, 0x918B, 0x9189, 0x9182, 0x91A2, 0x91AB, 0x91AF, + 0x91AA, 0x91B5, 0x91B4, 0x91BA, 0x91C0, 0x91C1, 0x91C9, 0x91CB, 0x91D0, 0x91D6, 0x91DF, 0x91E1, 0x91DB, 0x91FC, 0x91F5, 0x91F6, + 0x921E, 0x91FF, 0x9214, 0x922C, 0x9215, 0x9211, 0x925E, 0x9257, 0x9245, 0x9249, 0x9264, 0x9248, 0x9295, 0x923F, 0x924B, 0x9250, + 0x929C, 0x9296, 0x9293, 0x929B, 0x925A, 0x92CF, 0x92B9, 0x92B7, 0x92E9, 0x930F, 0x92FA, 0x9344, 0x932E, 0xFFFF, 0xFFFF, 0xFFFF, + // 0XE800 to 0XE8FF + 0x9319, 0x9322, 0x931A, 0x9323, 0x933A, 0x9335, 0x933B, 0x935C, 0x9360, 0x937C, 0x936E, 0x9356, 0x93B0, 0x93AC, 0x93AD, 0x9394, + 0x93B9, 0x93D6, 0x93D7, 0x93E8, 0x93E5, 0x93D8, 0x93C3, 0x93DD, 0x93D0, 0x93C8, 0x93E4, 0x941A, 0x9414, 0x9413, 0x9403, 0x9407, + 0x9410, 0x9436, 0x942B, 0x9435, 0x9421, 0x943A, 0x9441, 0x9452, 0x9444, 0x945B, 0x9460, 0x9462, 0x945E, 0x946A, 0x9229, 0x9470, + 0x9475, 0x9477, 0x947D, 0x945A, 0x947C, 0x947E, 0x9481, 0x947F, 0x9582, 0x9587, 0x958A, 0x9594, 0x9596, 0x9598, 0x9599, 0xFFFF, + 0x95A0, 0x95A8, 0x95A7, 0x95AD, 0x95BC, 0x95BB, 0x95B9, 0x95BE, 0x95CA, 0x6FF6, 0x95C3, 0x95CD, 0x95CC, 0x95D5, 0x95D4, 0x95D6, + 0x95DC, 0x95E1, 0x95E5, 0x95E2, 0x9621, 0x9628, 0x962E, 0x962F, 0x9642, 0x964C, 0x964F, 0x964B, 0x9677, 0x965C, 0x965E, 0x965D, + 0x965F, 0x9666, 0x9672, 0x966C, 0x968D, 0x9698, 0x9695, 0x9697, 0x96AA, 0x96A7, 0x96B1, 0x96B2, 0x96B0, 0x96B4, 0x96B6, 0x96B8, + 0x96B9, 0x96CE, 0x96CB, 0x96C9, 0x96CD, 0x894D, 0x96DC, 0x970D, 0x96D5, 0x96F9, 0x9704, 0x9706, 0x9708, 0x9713, 0x970E, 0x9711, + 0x970F, 0x9716, 0x9719, 0x9724, 0x972A, 0x9730, 0x9739, 0x973D, 0x973E, 0x9744, 0x9746, 0x9748, 0x9742, 0x9749, 0x975C, 0x9760, + 0x9764, 0x9766, 0x9768, 0x52D2, 0x976B, 0x9771, 0x9779, 0x9785, 0x977C, 0x9781, 0x977A, 0x9786, 0x978B, 0x978F, 0x9790, 0x979C, + 0x97A8, 0x97A6, 0x97A3, 0x97B3, 0x97B4, 0x97C3, 0x97C6, 0x97C8, 0x97CB, 0x97DC, 0x97ED, 0x9F4F, 0x97F2, 0x7ADF, 0x97F6, 0x97F5, + 0x980F, 0x980C, 0x9838, 0x9824, 0x9821, 0x9837, 0x983D, 0x9846, 0x984F, 0x984B, 0x986B, 0x986F, 0x9870, 0xFFFF, 0xFFFF, 0xFFFF, + // 0XE900 to 0XE9FF + 0x9871, 0x9874, 0x9873, 0x98AA, 0x98AF, 0x98B1, 0x98B6, 0x98C4, 0x98C3, 0x98C6, 0x98E9, 0x98EB, 0x9903, 0x9909, 0x9912, 0x9914, + 0x9918, 0x9921, 0x991D, 0x991E, 0x9924, 0x9920, 0x992C, 0x992E, 0x993D, 0x993E, 0x9942, 0x9949, 0x9945, 0x9950, 0x994B, 0x9951, + 0x9952, 0x994C, 0x9955, 0x9997, 0x9998, 0x99A5, 0x99AD, 0x99AE, 0x99BC, 0x99DF, 0x99DB, 0x99DD, 0x99D8, 0x99D1, 0x99ED, 0x99EE, + 0x99F1, 0x99F2, 0x99FB, 0x99F8, 0x9A01, 0x9A0F, 0x9A05, 0x99E2, 0x9A19, 0x9A2B, 0x9A37, 0x9A45, 0x9A42, 0x9A40, 0x9A43, 0xFFFF, + 0x9A3E, 0x9A55, 0x9A4D, 0x9A5B, 0x9A57, 0x9A5F, 0x9A62, 0x9A65, 0x9A64, 0x9A69, 0x9A6B, 0x9A6A, 0x9AAD, 0x9AB0, 0x9ABC, 0x9AC0, + 0x9ACF, 0x9AD1, 0x9AD3, 0x9AD4, 0x9ADE, 0x9ADF, 0x9AE2, 0x9AE3, 0x9AE6, 0x9AEF, 0x9AEB, 0x9AEE, 0x9AF4, 0x9AF1, 0x9AF7, 0x9AFB, + 0x9B06, 0x9B18, 0x9B1A, 0x9B1F, 0x9B22, 0x9B23, 0x9B25, 0x9B27, 0x9B28, 0x9B29, 0x9B2A, 0x9B2E, 0x9B2F, 0x9B32, 0x9B44, 0x9B43, + 0x9B4F, 0x9B4D, 0x9B4E, 0x9B51, 0x9B58, 0x9B74, 0x9B93, 0x9B83, 0x9B91, 0x9B96, 0x9B97, 0x9B9F, 0x9BA0, 0x9BA8, 0x9BB4, 0x9BC0, + 0x9BCA, 0x9BB9, 0x9BC6, 0x9BCF, 0x9BD1, 0x9BD2, 0x9BE3, 0x9BE2, 0x9BE4, 0x9BD4, 0x9BE1, 0x9C3A, 0x9BF2, 0x9BF1, 0x9BF0, 0x9C15, + 0x9C14, 0x9C09, 0x9C13, 0x9C0C, 0x9C06, 0x9C08, 0x9C12, 0x9C0A, 0x9C04, 0x9C2E, 0x9C1B, 0x9C25, 0x9C24, 0x9C21, 0x9C30, 0x9C47, + 0x9C32, 0x9C46, 0x9C3E, 0x9C5A, 0x9C60, 0x9C67, 0x9C76, 0x9C78, 0x9CE7, 0x9CEC, 0x9CF0, 0x9D09, 0x9D08, 0x9CEB, 0x9D03, 0x9D06, + 0x9D2A, 0x9D26, 0x9DAF, 0x9D23, 0x9D1F, 0x9D44, 0x9D15, 0x9D12, 0x9D41, 0x9D3F, 0x9D3E, 0x9D46, 0x9D48, 0xFFFF, 0xFFFF, 0xFFFF, + // 0XEA00 to 0XEAFF + 0x9D5D, 0x9D5E, 0x9D64, 0x9D51, 0x9D50, 0x9D59, 0x9D72, 0x9D89, 0x9D87, 0x9DAB, 0x9D6F, 0x9D7A, 0x9D9A, 0x9DA4, 0x9DA9, 0x9DB2, + 0x9DC4, 0x9DC1, 0x9DBB, 0x9DB8, 0x9DBA, 0x9DC6, 0x9DCF, 0x9DC2, 0x9DD9, 0x9DD3, 0x9DF8, 0x9DE6, 0x9DED, 0x9DEF, 0x9DFD, 0x9E1A, + 0x9E1B, 0x9E1E, 0x9E75, 0x9E79, 0x9E7D, 0x9E81, 0x9E88, 0x9E8B, 0x9E8C, 0x9E92, 0x9E95, 0x9E91, 0x9E9D, 0x9EA5, 0x9EA9, 0x9EB8, + 0x9EAA, 0x9EAD, 0x9761, 0x9ECC, 0x9ECE, 0x9ECF, 0x9ED0, 0x9ED4, 0x9EDC, 0x9EDE, 0x9EDD, 0x9EE0, 0x9EE5, 0x9EE8, 0x9EEF, 0xFFFF, + 0x9EF4, 0x9EF6, 0x9EF7, 0x9EF9, 0x9EFB, 0x9EFC, 0x9EFD, 0x9F07, 0x9F08, 0x76B7, 0x9F15, 0x9F21, 0x9F2C, 0x9F3E, 0x9F4A, 0x9F52, + 0x9F54, 0x9F63, 0x9F5F, 0x9F60, 0x9F61, 0x9F66, 0x9F67, 0x9F6C, 0x9F6A, 0x9F77, 0x9F72, 0x9F76, 0x9F95, 0x9F9C, 0x9FA0, 0x582F, + 0x69C7, 0x9059, 0x7464, 0x51DC, 0x7199, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, +}; + +const wchar_t SjisToUnicodeTable5[] = +{ + // 0XED00 to 0XEDFF + 0x7E8A, 0x891C, 0x9348, 0x9288, 0x84DC, 0x4FC9, 0x70BB, 0x6631, 0x68C8, 0x92F9, 0x66FB, 0x5F45, 0x4E28, 0x4EE1, 0x4EFC, 0x4F00, + 0x4F03, 0x4F39, 0x4F56, 0x4F92, 0x4F8A, 0x4F9A, 0x4F94, 0x4FCD, 0x5040, 0x5022, 0x4FFF, 0x501E, 0x5046, 0x5070, 0x5042, 0x5094, + 0x50F4, 0x50D8, 0x514A, 0x5164, 0x519D, 0x51BE, 0x51EC, 0x5215, 0x529C, 0x52A6, 0x52C0, 0x52DB, 0x5300, 0x5307, 0x5324, 0x5372, + 0x5393, 0x53B2, 0x53DD, 0xFA0E, 0x549C, 0x548A, 0x54A9, 0x54FF, 0x5586, 0x5759, 0x5765, 0x57AC, 0x57C8, 0x57C7, 0xFA0F, 0xFFFF, + 0xFA10, 0x589E, 0x58B2, 0x590B, 0x5953, 0x595B, 0x595D, 0x5963, 0x59A4, 0x59BA, 0x5B56, 0x5BC0, 0x752F, 0x5BD8, 0x5BEC, 0x5C1E, + 0x5CA6, 0x5CBA, 0x5CF5, 0x5D27, 0x5D53, 0xFA11, 0x5D42, 0x5D6D, 0x5DB8, 0x5DB9, 0x5DD0, 0x5F21, 0x5F34, 0x5F67, 0x5FB7, 0x5FDE, + 0x605D, 0x6085, 0x608A, 0x60DE, 0x60D5, 0x6120, 0x60F2, 0x6111, 0x6137, 0x6130, 0x6198, 0x6213, 0x62A6, 0x63F5, 0x6460, 0x649D, + 0x64CE, 0x654E, 0x6600, 0x6615, 0x663B, 0x6609, 0x662E, 0x661E, 0x6624, 0x6665, 0x6657, 0x6659, 0xFA12, 0x6673, 0x6699, 0x66A0, + 0x66B2, 0x66BF, 0x66FA, 0x670E, 0xF929, 0x6766, 0x67BB, 0x6852, 0x67C0, 0x6801, 0x6844, 0x68CF, 0xFA13, 0x6968, 0xFA14, 0x6998, + 0x69E2, 0x6A30, 0x6A6B, 0x6A46, 0x6A73, 0x6A7E, 0x6AE2, 0x6AE4, 0x6BD6, 0x6C3F, 0x6C5C, 0x6C86, 0x6C6F, 0x6CDA, 0x6D04, 0x6D87, + 0x6D6F, 0x6D96, 0x6DAC, 0x6DCF, 0x6DF8, 0x6DF2, 0x6DFC, 0x6E39, 0x6E5C, 0x6E27, 0x6E3C, 0x6EBF, 0x6F88, 0x6FB5, 0x6FF5, 0x7005, + 0x7007, 0x7028, 0x7085, 0x70AB, 0x710F, 0x7104, 0x715C, 0x7146, 0x7147, 0xFA15, 0x71C1, 0x71FE, 0x72B1, 0xFFFF, 0xFFFF, 0xFFFF, + // 0XEE00 to 0XEEFF + 0x72BE, 0x7324, 0xFA16, 0x7377, 0x73BD, 0x73C9, 0x73D6, 0x73E3, 0x73D2, 0x7407, 0x73F5, 0x7426, 0x742A, 0x7429, 0x742E, 0x7462, + 0x7489, 0x749F, 0x7501, 0x756F, 0x7682, 0x769C, 0x769E, 0x769B, 0x76A6, 0xFA17, 0x7746, 0x52AF, 0x7821, 0x784E, 0x7864, 0x787A, + 0x7930, 0xFA18, 0xFA19, 0xFA1A, 0x7994, 0xFA1B, 0x799B, 0x7AD1, 0x7AE7, 0xFA1C, 0x7AEB, 0x7B9E, 0xFA1D, 0x7D48, 0x7D5C, 0x7DB7, + 0x7DA0, 0x7DD6, 0x7E52, 0x7F47, 0x7FA1, 0xFA1E, 0x8301, 0x8362, 0x837F, 0x83C7, 0x83F6, 0x8448, 0x84B4, 0x8553, 0x8559, 0xFFFF, + 0x856B, 0xFA1F, 0x85B0, 0xFA20, 0xFA21, 0x8807, 0x88F5, 0x8A12, 0x8A37, 0x8A79, 0x8AA7, 0x8ABE, 0x8ADF, 0xFA22, 0x8AF6, 0x8B53, + 0x8B7F, 0x8CF0, 0x8CF4, 0x8D12, 0x8D76, 0xFA23, 0x8ECF, 0xFA24, 0xFA25, 0x9067, 0x90DE, 0xFA26, 0x9115, 0x9127, 0x91DA, 0x91D7, + 0x91DE, 0x91ED, 0x91EE, 0x91E4, 0x91E5, 0x9206, 0x9210, 0x920A, 0x923A, 0x9240, 0x923C, 0x924E, 0x9259, 0x9251, 0x9239, 0x9267, + 0x92A7, 0x9277, 0x9278, 0x92E7, 0x92D7, 0x92D9, 0x92D0, 0xFA27, 0x92D5, 0x92E0, 0x92D3, 0x9325, 0x9321, 0x92FB, 0xFA28, 0x931E, + 0x92FF, 0x931D, 0x9302, 0x9370, 0x9357, 0x93A4, 0x93C6, 0x93DE, 0x93F8, 0x9431, 0x9445, 0x9448, 0x9592, 0xF9DC, 0xFA29, 0x969D, + 0x96AF, 0x9733, 0x973B, 0x9743, 0x974D, 0x974F, 0x9751, 0x9755, 0x9857, 0x9865, 0xFA2A, 0xFA2B, 0x9927, 0xFA2C, 0x999E, 0x9A4E, + 0x9AD9, 0x9ADC, 0x9B75, 0x9B72, 0x9B8F, 0x9BB1, 0x9BBB, 0x9C00, 0x9D70, 0x9D6B, 0xFA2D, 0x9E19, 0x9ED1, 0xFFFF, 0xFFFF, 0x2170, + 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0xFFE2, 0xFFE4, 0xFF07, 0xFF02, 0xFFFF, 0xFFFF, 0xFFFF, +}; + +wchar_t sjisToUnicode(unsigned short SjisCharacter) +{ + if (SjisCharacter < 0x80) + { + return SjisCharacter; + } else if (SjisCharacter < 0x100) + { + return SjisToUnicodeTable1[SjisCharacter-0x80]; + } + + if ((SjisCharacter & 0xFF) < 0x40) return 0xFFFF; + + if (SjisCharacter >= 0x8100 && SjisCharacter < 0x8500) + { + SjisCharacter -= 0x8140; + SjisCharacter -= (SjisCharacter >> 8) * 0x40; + return SjisToUnicodeTable2[SjisCharacter]; + } else if (SjisCharacter >= 0x8700 && SjisCharacter < 0xA000) + { + SjisCharacter -= 0x8740; + SjisCharacter -= (SjisCharacter >> 8) * 0x40; + return SjisToUnicodeTable3[SjisCharacter]; + } else if (SjisCharacter >= 0xE000 && SjisCharacter < 0xEB00) + { + SjisCharacter -= 0xE040; + SjisCharacter -= (SjisCharacter >> 8) * 0x40; + return SjisToUnicodeTable4[SjisCharacter]; + } else if (SjisCharacter >= 0xED00 && SjisCharacter < 0xEF00) + { + SjisCharacter -= 0xED40; + SjisCharacter -= (SjisCharacter >> 8) * 0x40; + return SjisToUnicodeTable5[SjisCharacter]; + } else { + return 0xFFFF; + } +} + +BinaryFile::BinaryFile() +{ + handle = nullptr; +} + +BinaryFile::~BinaryFile() +{ + close(); +} + +bool BinaryFile::open(const std::wstring& fileName, Mode mode) +{ + setFileName(fileName); + return open(mode); +} + +bool BinaryFile::open(Mode mode) +{ + if (isOpen()) + close(); + + this->mode = mode; + + // open all files as binary due to unicode + switch (mode) + { + case Read: + handle = openFile(fileName,OpenFileMode::ReadBinary); + break; + case Write: + handle = openFile(fileName,OpenFileMode::WriteBinary); + break; + case ReadWrite: + handle = openFile(fileName,OpenFileMode::ReadWriteBinary); + break; + default: + return false; + } + + if (handle == nullptr) + return false; + + if (mode != Write) + { + fseek(handle,0,SEEK_END); + size_ = ftell(handle); + fseek(handle,0,SEEK_SET); + } + + return true; +} + +void BinaryFile::close() +{ + if (isOpen()) + { + fclose(handle); + handle = nullptr; + } +} + +size_t BinaryFile::read(void* dest, size_t length) +{ + if (isOpen() == false || mode == Write) + return 0; + + return fread(dest,1,length,handle); +} + +size_t BinaryFile::write(void* source, size_t length) +{ + if (isOpen() == false || mode == Read) + return 0; + + return fwrite(source,1,length,handle); +} + +const size_t TEXTFILE_BUF_MAX_SIZE = 4096; + +TextFile::TextFile() +{ + handle = nullptr; + recursion = false; + errorRetrieved = false; + fromMemory = false; + bufPos = 0; + lineCount = 0; +} + +TextFile::~TextFile() +{ + close(); +} + +void TextFile::openMemory(const std::wstring& content) +{ + fromMemory = true; + this->content = content; + contentPos = 0; + size_ = (long) content.size(); + encoding = UTF16LE; + mode = Read; + lineCount = 0; +} + +bool TextFile::open(const std::wstring& fileName, Mode mode, Encoding defaultEncoding) +{ + setFileName(fileName); + return open(mode,defaultEncoding); +} + +bool TextFile::open(Mode mode, Encoding defaultEncoding) +{ + if (fileName.empty()) + return false; + + if (isOpen()) + close(); + + fromMemory = false; + guessedEncoding = false; + encoding = defaultEncoding; + this->mode = mode; + + // open all files as binary due to unicode + switch (mode) + { + case Read: + handle = openFile(fileName,OpenFileMode::ReadBinary); + break; + case Write: + handle = openFile(fileName,OpenFileMode::WriteBinary); + if (handle == nullptr) + return false; + + buf.resize(TEXTFILE_BUF_MAX_SIZE); + if (encoding != ASCII) + { + encoding = UTF8; + writeCharacter(0xFEFF); + } + break; + default: + return false; + } + + if (handle == nullptr) + return false; + + // detect encoding + unsigned short num; + contentPos = 0; + + if (mode == Read) + { + fseek(handle,0,SEEK_END); + size_ = ftell(handle); + fseek(handle,0,SEEK_SET); + + if (fread(&num,2,1,handle) == 1) + { + switch (num) + { + case 0xFFFE: + encoding = UTF16BE; + contentPos += 2; + break; + case 0xFEFF: + encoding = UTF16LE; + contentPos += 2; + break; + case 0xBBEF: + if (fgetc(handle) == 0xBF) + { + encoding = UTF8; + contentPos += 3; + break; + } // fallthrough + default: + if (defaultEncoding == GUESS) + { + encoding = UTF8; + guessedEncoding = true; + } + fseek(handle,0,SEEK_SET); + break; + } + } else { + if (defaultEncoding == GUESS) + { + encoding = UTF8; + guessedEncoding = true; + } + } + } + + return true; +} + +void TextFile::close() +{ + if (isOpen() && !fromMemory) + { + bufDrainWrite(); + fclose(handle); + handle = nullptr; + } + bufPos = 0; +} + +long TextFile::tell() +{ + return (long) contentPos; +} + +void TextFile::seek(long pos) +{ + if (fromMemory) + contentPos = pos; + else + fseek(handle,pos,SEEK_SET); +} + +void TextFile::bufFillRead() +{ + assert(mode == Read); + + buf.resize(TEXTFILE_BUF_MAX_SIZE); + size_t read = fread(&buf[0], 1, TEXTFILE_BUF_MAX_SIZE, handle); + buf.resize(read); + + bufPos = 0; +} + +wchar_t TextFile::readCharacter() +{ + wchar_t value; + + switch (encoding) + { + case UTF8: + { + value = bufGetChar(); + contentPos++; + + int extraBytes = 0; + if ((value & 0xE0) == 0xC0) + { + extraBytes = 1; + value &= 0x1F; + } else if ((value & 0xF0) == 0xE0) + { + extraBytes = 2; + value &= 0x0F; + } else if (value > 0x7F) + { + errorText = formatString(L"One or more invalid UTF-8 characters in this file"); + } + + for (int i = 0; i < extraBytes; i++) + { + int b = bufGetChar(); + contentPos++; + + if ((b & 0xC0) != 0x80) + { + errorText = formatString(L"One or more invalid UTF-8 characters in this file"); + } + + value = (value << 6) | (b & 0x3F); + } + } + break; + case UTF16LE: + if (fromMemory) + { + value = content[contentPos++]; + } else { + value = bufGet16LE(); + contentPos += 2; + } + break; + case UTF16BE: + value = bufGet16BE(); + contentPos += 2; + break; + case SJIS: + { + unsigned short sjis = bufGetChar(); + contentPos++; + if (sjis >= 0x80) + { + sjis = (sjis << 8) | bufGetChar(); + contentPos++; + } + value = sjisToUnicode(sjis); + if (value == (wchar_t)-1) + { + errorText = formatString(L"One or more invalid Shift-JIS characters in this file"); + } + } + break; + case ASCII: + value = bufGetChar(); + contentPos++; + break; + case GUESS: + errorText = formatString(L"Cannot read from GUESS encoding"); + break; + } + + // convert \r\n to \n + if (value == L'\r' && recursion == false && atEnd() == false) + { + recursion = true; + long pos = tell(); + wchar_t nextValue = readCharacter(); + recursion = false; + + if (nextValue == L'\n') + return nextValue; + seek(pos); + } + + return value; +} + +std::wstring TextFile::readLine() +{ + std::wstring result; + wchar_t value; + + if (isOpen()) + { + while (tell() < size() && (value = readCharacter()) != L'\n') + { + result += value; + } + } + + lineCount++; + return result; +} + +StringList TextFile::readAll() +{ + StringList result; + while (!atEnd()) + { + result.push_back(readLine()); + } + + return result; +} + +void TextFile::bufPut(const void *p, const size_t len) +{ + assert(mode == Write); + + if (len > TEXTFILE_BUF_MAX_SIZE) + { + // Lots of data. Let's write directly. + bufDrainWrite(); + fwrite(p, 1, len, handle); + } + else + { + if (bufPos + len > TEXTFILE_BUF_MAX_SIZE) + bufDrainWrite(); + + memcpy(&buf[bufPos], p, len); + bufPos += len; + } +} + +void TextFile::bufPut(const char c) +{ + assert(mode == Write); + + if (bufPos >= TEXTFILE_BUF_MAX_SIZE) + bufDrainWrite(); + + buf[bufPos++] = c; +} + +void TextFile::bufDrainWrite() +{ + fwrite(&buf[0], 1, bufPos, handle); + bufPos = 0; +} + +void TextFile::writeCharacter(wchar_t character) +{ + if (mode != Write) return; + + // only support utf8 for now + if (character < 0x80) + { +#ifdef _WIN32 + if (character == L'\n') + { + bufPut('\r'); + } +#endif + bufPut(character & 0x7F); + } else if (encoding != ASCII) + { + if (character < 0x800) + { + bufPut(0xC0 | ((character >> 6) & 0x1F)); + bufPut(0x80 | (character & 0x3F)); + } else { + bufPut(0xE0 | ((character >> 12) & 0xF)); + bufPut(0x80 | ((character >> 6) & 0x3F)); + bufPut(0x80 | (character & 0x3F)); + } + } +} + +void TextFile::write(const wchar_t* line) +{ + if (mode != Write) return; + while (*line != 0) + { + writeCharacter(*line); + line++; + } +} + +void TextFile::write(const std::wstring& line) +{ + write(line.c_str()); +} + +void TextFile::write(const char* line) +{ + if (mode != Write) return; + while (*line != 0) + { + writeCharacter(*line); + line++; + } +} + +void TextFile::write(const std::string& line) +{ + write(line.c_str()); +} + +void TextFile::writeLine(const wchar_t* line) +{ + if (mode != Write) return; + write(line); + writeCharacter(L'\n'); +} + +void TextFile::writeLine(const std::wstring& line) +{ + writeLine(line.c_str()); +} + +void TextFile::writeLine(const char* line) +{ + if (mode != Write) return; + write(line); + writeCharacter(L'\n'); +} + +void TextFile::writeLine(const std::string& line) +{ + writeLine(line.c_str()); +} + +void TextFile::writeLines(StringList& list) +{ + for (size_t i = 0; i < list.size(); i++) + { + writeLine(list[i]); + } +} + +struct EncodingValue +{ + const wchar_t* name; + TextFile::Encoding value; +}; + +const EncodingValue encodingValues[] = { + { L"sjis", TextFile::SJIS }, + { L"shift-jis", TextFile::SJIS }, + { L"utf8", TextFile::UTF8 }, + { L"utf-8", TextFile::UTF8 }, + { L"utf16", TextFile::UTF16LE }, + { L"utf-16", TextFile::UTF16LE }, + { L"utf16-be", TextFile::UTF16BE }, + { L"utf-16-be", TextFile::UTF16BE }, + { L"ascii", TextFile::ASCII }, +}; + +TextFile::Encoding getEncodingFromString(const std::wstring& str) +{ + for (size_t i = 0; i < sizeof(encodingValues)/sizeof(EncodingValue); i++) + { + if (str.compare(encodingValues[i].name) == 0) + return encodingValues[i].value; + } + + return TextFile::GUESS; +} + +// file: Util/Util.cpp +#include <sys/stat.h> +#ifdef _WIN32 +#include <windows.h> +#include <shlwapi.h> +#if defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_PARTITION) +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP +#define ARMIPS_WINDOWS_UWP +#endif +#endif +#else +#include <unistd.h> +#endif + +std::wstring convertUtf8ToWString(const char* source) +{ + std::wstring result; + + int index = 0; + while (source[index] != 0) + { + int extraBytes = 0; + int value = source[index++]; + + if ((value & 0xE0) == 0xC0) + { + extraBytes = 1; + value &= 0x1F; + } else if ((value & 0xF0) == 0xE0) + { + extraBytes = 2; + value &= 0x0F; + } else if (value > 0x7F) + { + // error + return std::wstring(); + } + + for (int i = 0; i < extraBytes; i++) + { + int b = source[index++]; + if ((b & 0xC0) != 0x80) + { + // error + return std::wstring(); + } + + value = (value << 6) | (b & 0x3F); + } + + result += value; + } + + return result; +} + +std::string convertWCharToUtf8(wchar_t character) +{ + std::string result; + + if (character < 0x80) + { + result += character & 0x7F; + } else if (character < 0x800) + { + result += 0xC0 | ((character >> 6) & 0x1F); + result += (0x80 | (character & 0x3F)); + } else { + result += 0xE0 | ((character >> 12) & 0xF); + result += 0x80 | ((character >> 6) & 0x3F); + result += 0x80 | (character & 0x3F); + } + + return result; +} + +std::string convertWStringToUtf8(const std::wstring& source) +{ + std::string result; + + for (size_t i = 0; i < source.size(); i++) + { + wchar_t character = source[i]; + if (character < 0x80) + { + result += character & 0x7F; + } else if (character < 0x800) + { + result += 0xC0 | ((character >> 6) & 0x1F); + result += (0x80 | (character & 0x3F)); + } else { + result += 0xE0 | ((character >> 12) & 0xF); + result += 0x80 | ((character >> 6) & 0x3F); + result += 0x80 | (character & 0x3F); + } + } + + return result; +} + +std::wstring intToHexString(unsigned int value, int digits, bool prefix) +{ + std::wstring result; + result.reserve((digits+prefix) ? 2 : 0); + + if (prefix) + { + result += '0'; + result += 'x'; + } + + while (digits > 8) + { + result += '0'; + digits--; + } + + wchar_t buf[9]; + swprintf(buf,9,L"%0*X",digits,value); + result += buf; + + return result; +} + +std::wstring intToString(unsigned int value, int digits) +{ + std::wstring result; + result.reserve(digits); + + while (digits > 8) + { + result += ' '; + digits--; + } + + wchar_t buf[9]; + swprintf(buf,9,L"%*d",digits,value); + result += buf; + + return result; +} + +bool stringToInt(const std::wstring& line, size_t start, size_t end, int64_t& result) +{ + // find base of number + int32_t base = 10; + if (line[start] == '0') + { + if (towlower(line[start+1]) == 'x') + { + base = 16; + start += 2; + } else if (towlower(line[start+1]) == 'o') + { + base = 8; + start += 2; + } else if (towlower(line[start+1]) == 'b' && towlower(line[end-1]) != 'h') + { + base = 2; + start += 2; + } + } + + if (base == 10) + { + if (towlower(line[end-1]) == 'h') + { + base = 16; + end--; + } else if (towlower(line[end-1]) == 'b') + { + base = 2; + end--; + } else if (towlower(line[end-1]) == 'o') + { + base = 8; + end--; + } + } + + // convert number + result = 0; + while (start < end) + { + wchar_t c = towlower(line[start++]); + + int32_t value = c >= 'a' ? c-'a'+10 : c-'0'; + + if (value >= base) + return false; + + result = (result*base) + value; + } + + return true; +} + +int32_t getFloatBits(float value) +{ + union { float f; int32_t i; } u; + u.f = value; + return u.i; +} + +float bitsToFloat(int32_t value) +{ + union { float f; int32_t i; } u; + u.i = value; + return u.f; +} + +int64_t getDoubleBits(double value) +{ + union { double f; int64_t i; } u; + u.f = value; + return u.i; +} + +StringList getStringListFromArray(wchar_t** source, int count) +{ + StringList result; + for (int i = 0; i < count; i++) + { + result.push_back(std::wstring(source[i])); + } + + return result; +} + +StringList splitString(const std::wstring& str, const wchar_t delim, bool skipEmpty) +{ + StringList result; + std::wstringstream stream(str); + std::wstring arg; + while (std::getline(stream,arg,delim)) + { + if (arg.empty() && skipEmpty) continue; + result.push_back(arg); + } + + return result; +} + +int64_t fileSize(const std::wstring& fileName) +{ +#ifdef _WIN32 + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesEx(fileName.c_str(),GetFileExInfoStandard,&attr) + || (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + return 0; + return ((int64_t) attr.nFileSizeHigh << 32) | (int64_t) attr.nFileSizeLow; +#else + std::string utf8 = convertWStringToUtf8(fileName); + struct stat fileStat; + int err = stat(utf8.c_str(),&fileStat); + if (0 != err) + return 0; + return fileStat.st_size; +#endif +} + +bool fileExists(const std::wstring& strFilename) +{ +#ifdef _WIN32 +#ifdef ARMIPS_WINDOWS_UWP + return GetFileAttributes(strFilename.c_str()) != INVALID_FILE_ATTRIBUTES; +#else + int OldMode = SetErrorMode(SEM_FAILCRITICALERRORS); + bool success = GetFileAttributes(strFilename.c_str()) != INVALID_FILE_ATTRIBUTES; + SetErrorMode(OldMode); + return success; +#endif +#else + std::string utf8 = convertWStringToUtf8(strFilename); + struct stat stFileInfo; + int intStat = stat(utf8.c_str(),&stFileInfo); + return intStat == 0; +#endif +} + +bool copyFile(const std::wstring& existingFile, const std::wstring& newFile) +{ +#ifdef _WIN32 + return CopyFileW(existingFile.c_str(),newFile.c_str(),false) != FALSE; +#else + unsigned char buffer[BUFSIZ]; + bool error = false; + + std::string existingUtf8 = convertWStringToUtf8(existingFile); + std::string newUtf8 = convertWStringToUtf8(newFile); + + FILE* input = fopen(existingUtf8.c_str(),"rb"); + FILE* output = fopen(newUtf8.c_str(),"wb"); + + if (input == nullptr || output == nullptr) + return false; + + size_t n; + while ((n = fread(buffer,1,BUFSIZ,input)) > 0) + { + if (fwrite(buffer,1,n,output) != n) + error = true; + } + + fclose(input); + fclose(output); + return !error; +#endif +} + +bool deleteFile(const std::wstring& fileName) +{ +#ifdef _WIN32 + return DeleteFileW(fileName.c_str()) != FALSE; +#else + std::string utf8 = convertWStringToUtf8(fileName); + return unlink(utf8.c_str()) == 0; +#endif +} + +FILE* openFile(const std::wstring& fileName, OpenFileMode mode) +{ +#ifdef _WIN32 + switch (mode) + { + case OpenFileMode::ReadBinary: + return _wfopen(fileName.c_str(),L"rb"); + case OpenFileMode::WriteBinary: + return _wfopen(fileName.c_str(),L"wb"); + case OpenFileMode::ReadWriteBinary: + return _wfopen(fileName.c_str(),L"rb+"); + } +#else + std::string nameUtf8 = convertWStringToUtf8(fileName); + + switch (mode) + { + case OpenFileMode::ReadBinary: + return fopen(nameUtf8.c_str(),"rb"); + case OpenFileMode::WriteBinary: + return fopen(nameUtf8.c_str(),"wb"); + case OpenFileMode::ReadWriteBinary: + return fopen(nameUtf8.c_str(),"rb+"); + } +#endif + + return nullptr; +} + +std::wstring getCurrentDirectory() +{ +#ifdef _WIN32 + wchar_t dir[MAX_PATH]; + _wgetcwd(dir,MAX_PATH-1); + return dir; +#else + char* dir = getcwd(nullptr,0); + std::wstring result = convertUtf8ToWString(dir); + free(dir); + return result; +#endif +} + +bool changeDirectory(const std::wstring& dir) +{ +#ifdef _WIN32 + return _wchdir(dir.c_str()) == 0; +#else + std::string utf8 = convertWStringToUtf8(dir); + return chdir(utf8.c_str()) == 0; +#endif +} + +std::wstring toWLowercase(const std::string& str) +{ + std::wstring result; + for (size_t i = 0; i < str.size(); i++) + { + result += tolower(str[i]); + } + + return result; +} + +std::wstring getFileNameFromPath(const std::wstring& path) +{ + size_t n = path.find_last_of(L"/\\"); + if (n == path.npos) + return path; + return path.substr(n); +} + +size_t replaceAll(std::wstring& str, const wchar_t* oldValue,const std::wstring& newValue) +{ + size_t pos = 0; + size_t len = wcslen(oldValue); + + size_t count = 0; + while ((pos = str.find(oldValue, pos)) != std::string::npos) + { + str.replace(pos,len,newValue); + pos += newValue.length(); + count++; + } + + return count; +} + +bool startsWith(const std::wstring& str, const wchar_t* value, size_t stringPos) +{ + while (*value != 0 && stringPos < str.size()) + { + if (str[stringPos++] != *value++) + return false; + } + + return *value == 0; +} + +bool isAbsolutePath(const std::wstring& path) +{ +#ifdef _WIN32 + return path.size() > 2 && (path[1] == ':' || (path[0] == '\\' && path[1] == '\\')); +#else + return path.size() >= 1 && path[0] == '/'; +#endif +} + +// file: Main/CommandLineInterface.h + +int runFromCommandLine(const StringList& arguments, ArmipsArguments settings = {}); + +// file: Main/CommandLineInterface.cpp + +static void printUsage(std::wstring executableName) +{ + Logger::printLine(L"armips assembler v%d.%d.%d (%s %s) by Kingcom", + ARMIPS_VERSION_MAJOR, ARMIPS_VERSION_MINOR, ARMIPS_VERSION_REVISION, __DATE__, __TIME__); + Logger::printLine(L"Usage: %s [optional parameters] <FILE>", executableName); + Logger::printLine(L""); + Logger::printLine(L"Optional parameters:"); + Logger::printLine(L" -temp <TEMP> Output temporary assembly data to <TEMP> file"); + Logger::printLine(L" -sym <SYM> Output symbol data in the sym format to <SYM> file"); + Logger::printLine(L" -sym2 <SYM2> Output symbol data in the sym2 format to <SYM2> file"); + Logger::printLine(L" -root <ROOT> Use <ROOT> as working directory during execution"); + Logger::printLine(L" -equ <NAME> <VAL> Equivalent to \'<NAME> equ <VAL>\' in code"); + Logger::printLine(L" -strequ <NAME> <VAL> Equivalent to \'<NAME> equ \"<VAL>\"\' in code"); + Logger::printLine(L" -definelabel <NAME> <VAL> Equivalent to \'.definelabel <NAME>, <VAL>\' in code"); + Logger::printLine(L" -erroronwarning Treat all warnings like errors"); + Logger::printLine(L""); + Logger::printLine(L"File arguments:"); + Logger::printLine(L" <FILE> Main assembly code file"); +} + +static bool parseArguments(const StringList& arguments, ArmipsArguments& settings) +{ + size_t argpos = 1; + bool readflags = true; + while (argpos < arguments.size()) + { + if (readflags && arguments[argpos][0] == L'-') + { + if (arguments[argpos] == L"--") + { + readflags = false; + argpos += 1; + } + else if (arguments[argpos] == L"-temp" && argpos + 1 < arguments.size()) + { + settings.tempFileName = arguments[argpos + 1]; + argpos += 2; + } + else if (arguments[argpos] == L"-sym" && argpos + 1 < arguments.size()) + { + settings.symFileName = arguments[argpos + 1]; + settings.symFileVersion = 1; + argpos += 2; + } + else if (arguments[argpos] == L"-sym2" && argpos + 1 < arguments.size()) + { + settings.symFileName = arguments[argpos + 1]; + settings.symFileVersion = 2; + argpos += 2; + } + else if (arguments[argpos] == L"-erroronwarning") + { + settings.errorOnWarning = true; + argpos += 1; + } + else if (arguments[argpos] == L"-equ" && argpos + 2 < arguments.size()) + { + EquationDefinition def; + + def.name = arguments[argpos+1]; + std::transform(def.name.begin(), def.name.end(), def.name.begin(), ::towlower); + + if (!checkValidLabelName(def.name)) + { + Logger::printError(Logger::Error, L"Invalid equation name \"%s\"", def.name); + return false; + } + + auto it = std::find_if(settings.equList.begin(), settings.equList.end(), + [&def](EquationDefinition x) -> bool {return def.name == x.name;}); + if(it != settings.equList.end()) + { + Logger::printError(Logger::Error, L"Equation name \"%s\" already defined", def.name); + return false; + } + + def.value = arguments[argpos + 2]; + settings.equList.push_back(def); + argpos += 3; + } + else if (arguments[argpos] == L"-strequ" && argpos + 2 < arguments.size()) + { + EquationDefinition def; + + def.name = arguments[argpos+1]; + std::transform(def.name.begin(), def.name.end(), def.name.begin(), ::towlower); + + if (!checkValidLabelName(def.name)) + { + Logger::printError(Logger::Error, L"Invalid equation name \"%s\"", def.name); + return false; + } + + auto it = std::find_if(settings.equList.begin(), settings.equList.end(), + [&def](EquationDefinition x) -> bool {return def.name == x.name;}); + if(it != settings.equList.end()) + { + Logger::printError(Logger::Error, L"Equation name \"%s\" already defined", def.name); + return false; + } + + def.value = formatString(L"\"%s\"", arguments[argpos + 2]); + settings.equList.push_back(def); + argpos += 3; + } + else if (arguments[argpos] == L"-time") + { + Logger::printError(Logger::Warning, L"-time flag is deprecated"); + argpos += 1; + } + else if (arguments[argpos] == L"-root" && argpos + 1 < arguments.size()) + { + if(!changeDirectory(arguments[argpos + 1])) + { + Logger::printError(Logger::Error, L"Could not open directory \"%s\"", arguments[argpos + 1]); + return false; + } + argpos += 2; + } + else if (arguments[argpos] == L"-definelabel" && argpos + 2 < arguments.size()) + { + LabelDefinition def; + + def.originalName = arguments[argpos + 1]; + def.name = def.originalName; + std::transform(def.name.begin(), def.name.end(), def.name.begin(), ::towlower); + + if (!checkValidLabelName(def.name)) + { + Logger::printError(Logger::Error, L"Invalid label name \"%s\"", def.name); + return false; + } + + auto it = std::find_if(settings.labels.begin(), settings.labels.end(), + [&def](LabelDefinition x) -> bool {return def.name == x.name;}); + if(it != settings.labels.end()) + { + Logger::printError(Logger::Error, L"Label name \"%s\" already defined", def.name); + return false; + } + + int64_t value; + if (!stringToInt(arguments[argpos + 2], 0, arguments[argpos + 2].size(), value)) + { + Logger::printError(Logger::Error, L"Invalid label value \"%s\"", arguments[argpos + 2]); + return false; + } + def.value = value; + + settings.labels.push_back(def); + argpos += 3; + } + else { + Logger::printError(Logger::Error, L"Invalid command line argument \"%s\"\n", arguments[argpos]); + printUsage(arguments[0]); + return false; + } + } + else { + // only allow one input filename + if (settings.inputFileName == L"") + { + settings.inputFileName = arguments[argpos]; + argpos++; + } + else { + Logger::printError(Logger::Error, L"Multiple input assembly files specified\n"); + printUsage(arguments[0]); + return false; + } + } + } + + // ensure input file was specified + if (settings.inputFileName == L"") + { + if (arguments.size() > 1) + Logger::printError(Logger::Error, L"Missing input assembly file\n"); + + printUsage(arguments[0]); + return false; + } + + // turn input filename into an absolute path + if (settings.useAbsoluteFileNames && isAbsolutePath(settings.inputFileName) == false) + settings.inputFileName = formatString(L"%s/%s", getCurrentDirectory(), settings.inputFileName); + + if (fileExists(settings.inputFileName) == false) + { + Logger::printError(Logger::Error, L"File \"%s\" not found", settings.inputFileName); + return false; + } + return true; +} + +int runFromCommandLine(const StringList& arguments, ArmipsArguments settings) +{ + if (parseArguments(arguments, settings) == false) + { + if (arguments.size() > 1 && !settings.silent) + Logger::printLine(L"Cannot parse arguments; aborting."); + + return 1; + } + + if (runArmips(settings) == false) + { + if (!settings.silent) + Logger::printLine(L"Aborting."); + + return 1; + } + + return 0; +} + +// file: Core/ELF/ElfFile.cpp +#include <vector> +#include <algorithm> + +#ifndef _WIN32 +#include <strings.h> +#define _stricmp strcasecmp +#endif + +static bool stringEqualInsensitive(const std::string& a, const std::string& b) +{ + if (a.size() != b.size()) + return false; + return _stricmp(a.c_str(),b.c_str()) == 0; +} + +bool compareSection(ElfSection* a, ElfSection* b) +{ + return a->getOffset() < b->getOffset(); +} + +ElfSection::ElfSection(Elf32_Shdr header): header(header) +{ + owner = nullptr; +} + +void ElfSection::setOwner(ElfSegment* segment) +{ + header.sh_offset -= segment->getOffset(); + owner = segment; +} + +void ElfSection::writeHeader(ByteArray& data, int pos, Endianness endianness) +{ + data.replaceDoubleWord(pos + 0x00, header.sh_name, endianness); + data.replaceDoubleWord(pos + 0x04, header.sh_type, endianness); + data.replaceDoubleWord(pos + 0x08, header.sh_flags, endianness); + data.replaceDoubleWord(pos + 0x0C, header.sh_addr, endianness); + data.replaceDoubleWord(pos + 0x10, header.sh_offset, endianness); + data.replaceDoubleWord(pos + 0x14, header.sh_size, endianness); + data.replaceDoubleWord(pos + 0x18, header.sh_link, endianness); + data.replaceDoubleWord(pos + 0x1C, header.sh_info, endianness); + data.replaceDoubleWord(pos + 0x20, header.sh_addralign, endianness); + data.replaceDoubleWord(pos + 0x24, header.sh_entsize, endianness); +} + +// only called for segmentless sections +void ElfSection::writeData(ByteArray& output) +{ + if (header.sh_type == SHT_NULL) return; + + // nobits sections still get a provisional file address + if (header.sh_type == SHT_NOBITS) + { + header.sh_offset = (Elf32_Off) output.size(); + } + + if (header.sh_addralign != (unsigned) -1) + output.alignSize(header.sh_addralign); + header.sh_offset = (Elf32_Off) output.size(); + output.append(data); +} + +void ElfSection::setOffsetBase(int base) +{ + header.sh_offset += base; +} + +ElfSegment::ElfSegment(Elf32_Phdr header, ByteArray& segmentData): header(header) +{ + data = segmentData; + paddrSection = nullptr; +} + +bool ElfSegment::isSectionPartOf(ElfSection* section) +{ + int sectionStart = section->getOffset(); + int sectionSize = section->getType() == SHT_NOBITS ? 0 : section->getSize(); + int sectionEnd = sectionStart+sectionSize; + + int segmentStart = header.p_offset; + int segmentEnd = segmentStart+header.p_filesz; + + // exclusive > in case the size is 0 + if (sectionStart < (int)header.p_offset || sectionStart > segmentEnd) return false; + + // does an empty section belong to this or the next segment? hm... + if (sectionStart == segmentEnd) return sectionSize == 0; + + // the start is inside the section and the size is not 0, so the end should be in here too + if (sectionEnd > segmentEnd) + { + Logger::printError(Logger::Error,L"Section partially contained in segment"); + return false; + } + + return true; +} + +void ElfSegment::addSection(ElfSection* section) +{ + if (header.p_paddr != 0) + { + if (section->getOffset() == header.p_paddr) + { + paddrSection = section; + } + } + + section->setOwner(this); + sections.push_back(section); +} + +void ElfSegment::writeData(ByteArray& output) +{ + if (sections.size() == 0) + { + output.alignSize(header.p_align); + if (header.p_offset == header.p_paddr) + header.p_paddr = (Elf32_Addr) output.size(); + + header.p_offset = (Elf32_Off) output.size(); + return; + } + + // align segment to alignment of first section + int align = std::max<int>(sections[0]->getAlignment(),16); + output.alignSize(align); + + header.p_offset = (Elf32_Off) output.size(); + for (int i = 0; i < (int)sections.size(); i++) + { + sections[i]->setOffsetBase(header.p_offset); + } + + if (paddrSection) + { + header.p_paddr = paddrSection->getOffset(); + } + + output.append(data); +} + +void ElfSegment::writeHeader(ByteArray& data, int pos, Endianness endianness) +{ + data.replaceDoubleWord(pos + 0x00, header.p_type, endianness); + data.replaceDoubleWord(pos + 0x04, header.p_offset, endianness); + data.replaceDoubleWord(pos + 0x08, header.p_vaddr, endianness); + data.replaceDoubleWord(pos + 0x0C, header.p_paddr, endianness); + data.replaceDoubleWord(pos + 0x10, header.p_filesz, endianness); + data.replaceDoubleWord(pos + 0x14, header.p_memsz, endianness); + data.replaceDoubleWord(pos + 0x18, header.p_flags, endianness); + data.replaceDoubleWord(pos + 0x1C, header.p_align, endianness); +} + +void ElfSegment::splitSections() +{ + +} + +int ElfSegment::findSection(const std::string& name) +{ + for (size_t i = 0; i < sections.size(); i++) + { + if (stringEqualInsensitive(name,sections[i]->getName())) + return i; + } + + return -1; +} + +void ElfSegment::writeToData(size_t offset, void* src, size_t size) +{ + for (size_t i = 0; i < size; i++) + { + data[offset+i] = ((byte*)src)[i]; + } +} + +void ElfSegment::sortSections() +{ + std::sort(sections.begin(),sections.end(),compareSection); +} + +void ElfFile::loadSectionNames() +{ + if (fileHeader.e_shstrndx == SHN_UNDEF) return; + + // check if the string table is actually a string table + // sometimes it gives the wrong section id + size_t strTablePos = sections[fileHeader.e_shstrndx]->getOffset(); + size_t strTableSize = sections[fileHeader.e_shstrndx]->getSize(); + for (size_t i = 0; i < strTableSize; i++) + { + if (fileData[strTablePos+i] != 0 && fileData[strTablePos+i] < 0x20) + return; + if (fileData[strTablePos+i] > 0x7F) + return; + } + + for (size_t i = 0; i < sections.size(); i++) + { + ElfSection* section = sections[i]; + if (section->getType() == SHT_NULL) continue; + + int strTablePos = sections[fileHeader.e_shstrndx]->getOffset(); + int offset = strTablePos+section->getNameOffset(); + + char* name = (char*) fileData.data(offset); + std::string strName = name; + section->setName(strName); + } +} + +void ElfFile::determinePartOrder() +{ + size_t segmentTable = fileHeader.e_phoff; + size_t sectionTable = fileHeader.e_shoff; + + // segments + size_t firstSegmentStart = fileData.size(), lastSegmentEnd = 0; + for (size_t i = 0; i < fileHeader.e_phnum; i++) + { + size_t pos = fileHeader.e_phoff+i*fileHeader.e_phentsize; + + Elf32_Phdr segmentHeader; + loadProgramHeader(segmentHeader, fileData, pos); + size_t end = segmentHeader.p_offset + segmentHeader.p_filesz; + + if (segmentHeader.p_offset < firstSegmentStart) firstSegmentStart = segmentHeader.p_offset; + if (lastSegmentEnd < end) lastSegmentEnd = end; + } + + // segmentless sections + size_t firstSectionStart = fileData.size(), lastSectionEnd = 0; + for (size_t i = 0; i < segmentlessSections.size(); i++) + { + if (segmentlessSections[i]->getType() == SHT_NULL) continue; + + size_t start = segmentlessSections[i]->getOffset(); + size_t end = start+segmentlessSections[i]->getSize(); + + if (start == 0 && end == 0) + continue; + if (start < firstSectionStart) firstSectionStart = start; + if (lastSectionEnd < end) lastSectionEnd = end; + } + + struct PartsSort { + size_t offset; + ElfPart type; + bool operator<(const PartsSort& other) const { return offset < other.offset; }; + }; + + PartsSort temp[4] = { + { segmentTable, ELFPART_SEGMENTTABLE }, + { sectionTable, ELFPART_SECTIONTABLE }, + { firstSegmentStart, ELFPART_SEGMENTS }, + { firstSectionStart, ELFPART_SEGMENTLESSSECTIONS }, + }; + + std::sort(&temp[0],&temp[4]); + + for (size_t i = 0; i < 4; i++) + { + partsOrder[i] = temp[i].type; + } +} + +int ElfFile::findSegmentlessSection(const std::string& name) +{ + for (size_t i = 0; i < segmentlessSections.size(); i++) + { + if (stringEqualInsensitive(name,segmentlessSections[i]->getName())) + return i; + } + + return -1; +} + +void ElfFile::loadElfHeader() +{ + memcpy(fileHeader.e_ident, &fileData[0], sizeof(fileHeader.e_ident)); + Endianness endianness = getEndianness(); + fileHeader.e_type = fileData.getWord(0x10, endianness); + fileHeader.e_machine = fileData.getWord(0x12, endianness); + fileHeader.e_version = fileData.getDoubleWord(0x14, endianness); + fileHeader.e_entry = fileData.getDoubleWord(0x18, endianness); + fileHeader.e_phoff = fileData.getDoubleWord(0x1C, endianness); + fileHeader.e_shoff = fileData.getDoubleWord(0x20, endianness); + fileHeader.e_flags = fileData.getDoubleWord(0x24, endianness); + fileHeader.e_ehsize = fileData.getWord(0x28, endianness); + fileHeader.e_phentsize = fileData.getWord(0x2A, endianness); + fileHeader.e_phnum = fileData.getWord(0x2C, endianness); + fileHeader.e_shentsize = fileData.getWord(0x2E, endianness); + fileHeader.e_shnum = fileData.getWord(0x30, endianness); + fileHeader.e_shstrndx = fileData.getWord(0x32, endianness); +} + +void ElfFile::writeHeader(ByteArray& data, int pos, Endianness endianness) +{ + memcpy(&fileData[0], fileHeader.e_ident, sizeof(fileHeader.e_ident)); + data.replaceWord(pos + 0x10, fileHeader.e_type, endianness); + data.replaceWord(pos + 0x12, fileHeader.e_machine, endianness); + data.replaceDoubleWord(pos + 0x14, fileHeader.e_version, endianness); + data.replaceDoubleWord(pos + 0x18, fileHeader.e_entry, endianness); + data.replaceDoubleWord(pos + 0x1C, fileHeader.e_phoff, endianness); + data.replaceDoubleWord(pos + 0x20, fileHeader.e_shoff, endianness); + data.replaceDoubleWord(pos + 0x24, fileHeader.e_flags, endianness); + data.replaceWord(pos + 0x28, fileHeader.e_ehsize, endianness); + data.replaceWord(pos + 0x2A, fileHeader.e_phentsize, endianness); + data.replaceWord(pos + 0x2C, fileHeader.e_phnum, endianness); + data.replaceWord(pos + 0x2E, fileHeader.e_shentsize, endianness); + data.replaceWord(pos + 0x30, fileHeader.e_shnum, endianness); + data.replaceWord(pos + 0x32, fileHeader.e_shstrndx, endianness); +} + +void ElfFile::loadProgramHeader(Elf32_Phdr& header, ByteArray& data, int pos) +{ + Endianness endianness = getEndianness(); + header.p_type = data.getDoubleWord(pos + 0x00, endianness); + header.p_offset = data.getDoubleWord(pos + 0x04, endianness); + header.p_vaddr = data.getDoubleWord(pos + 0x08, endianness); + header.p_paddr = data.getDoubleWord(pos + 0x0C, endianness); + header.p_filesz = data.getDoubleWord(pos + 0x10, endianness); + header.p_memsz = data.getDoubleWord(pos + 0x14, endianness); + header.p_flags = data.getDoubleWord(pos + 0x18, endianness); + header.p_align = data.getDoubleWord(pos + 0x1C, endianness); +} + +void ElfFile::loadSectionHeader(Elf32_Shdr& header, ByteArray& data, int pos) +{ + Endianness endianness = getEndianness(); + header.sh_name = data.getDoubleWord(pos + 0x00, endianness); + header.sh_type = data.getDoubleWord(pos + 0x04, endianness); + header.sh_flags = data.getDoubleWord(pos + 0x08, endianness); + header.sh_addr = data.getDoubleWord(pos + 0x0C, endianness); + header.sh_offset = data.getDoubleWord(pos + 0x10, endianness); + header.sh_size = data.getDoubleWord(pos + 0x14, endianness); + header.sh_link = data.getDoubleWord(pos + 0x18, endianness); + header.sh_info = data.getDoubleWord(pos + 0x1C, endianness); + header.sh_addralign = data.getDoubleWord(pos + 0x20, endianness); + header.sh_entsize = data.getDoubleWord(pos + 0x24, endianness); +} + +bool ElfFile::load(const std::wstring& fileName, bool sort) +{ + ByteArray data = ByteArray::fromFile(fileName); + if (data.size() == 0) + return false; + return load(data,sort); +} + +bool ElfFile::load(ByteArray& data, bool sort) +{ + fileData = data; + + loadElfHeader(); + symTab = nullptr; + strTab = nullptr; + + // load segments + for (size_t i = 0; i < fileHeader.e_phnum; i++) + { + int pos = fileHeader.e_phoff+i*fileHeader.e_phentsize; + + Elf32_Phdr sectionHeader; + loadProgramHeader(sectionHeader, fileData, pos); + + ByteArray segmentData = fileData.mid(sectionHeader.p_offset,sectionHeader.p_filesz); + ElfSegment* segment = new ElfSegment(sectionHeader,segmentData); + segments.push_back(segment); + } + + // load sections and assign them to segments + for (int i = 0; i < fileHeader.e_shnum; i++) + { + int pos = fileHeader.e_shoff+i*fileHeader.e_shentsize; + + Elf32_Shdr sectionHeader; + loadSectionHeader(sectionHeader, fileData, pos); + + ElfSection* section = new ElfSection(sectionHeader); + sections.push_back(section); + + // check if the section belongs to a segment + ElfSegment* owner = nullptr; + for (int k = 0; k < (int)segments.size(); k++) + { + if (segments[k]->isSectionPartOf(section)) + { + owner = segments[k]; + break; + } + } + + if (owner != nullptr) + { + owner->addSection(section); + } else { + if (section->getType() != SHT_NOBITS && section->getType() != SHT_NULL) + { + ByteArray data = fileData.mid(section->getOffset(),section->getSize()); + section->setData(data); + } + + switch (section->getType()) + { + case SHT_SYMTAB: + symTab = section; + break; + case SHT_STRTAB: + if (!strTab || i != fileHeader.e_shstrndx) + { + strTab = section; + } + break; + } + + segmentlessSections.push_back(section); + } + } + + determinePartOrder(); + loadSectionNames(); + + if (sort) + { + std::sort(segmentlessSections.begin(),segmentlessSections.end(),compareSection); + + for (int i = 0; i < (int)segments.size(); i++) + { + segments[i]->sortSections(); + } + } + + return true; +} + +void ElfFile::save(const std::wstring&fileName) +{ + fileData.clear(); + + // reserve space for header and table data + fileData.reserveBytes(sizeof(Elf32_Ehdr)); + + for (size_t i = 0; i < 4; i++) + { + switch (partsOrder[i]) + { + case ELFPART_SEGMENTTABLE: + fileData.alignSize(4); + fileHeader.e_phoff = (Elf32_Off) fileData.size(); + fileData.reserveBytes(segments.size()*fileHeader.e_phentsize); + break; + case ELFPART_SECTIONTABLE: + fileData.alignSize(4); + fileHeader.e_shoff = (Elf32_Off) fileData.size(); + fileData.reserveBytes(sections.size()*fileHeader.e_shentsize); + break; + case ELFPART_SEGMENTS: + for (size_t i = 0; i < segments.size(); i++) + { + segments[i]->writeData(fileData); + } + break; + case ELFPART_SEGMENTLESSSECTIONS: + for (size_t i = 0; i < segmentlessSections.size(); i++) + { + segmentlessSections[i]->writeData(fileData); + } + break; + } + } + + // copy data to the tables + Endianness endianness = getEndianness(); + writeHeader(fileData, 0, endianness); + for (size_t i = 0; i < segments.size(); i++) + { + int pos = fileHeader.e_phoff+i*fileHeader.e_phentsize; + segments[i]->writeHeader(fileData, pos, endianness); + } + + for (size_t i = 0; i < sections.size(); i++) + { + int pos = fileHeader.e_shoff+i*fileHeader.e_shentsize; + sections[i]->writeHeader(fileData, pos, endianness); + } + + fileData.toFile(fileName); +} + +int ElfFile::getSymbolCount() +{ + if (symTab == nullptr) + return 0; + + return symTab->getSize()/sizeof(Elf32_Sym); +} + +bool ElfFile::getSymbol(Elf32_Sym& symbol, size_t index) +{ + if (symTab == nullptr) + return false; + + ByteArray &data = symTab->getData(); + int pos = index*sizeof(Elf32_Sym); + Endianness endianness = getEndianness(); + symbol.st_name = data.getDoubleWord(pos + 0x00, endianness); + symbol.st_value = data.getDoubleWord(pos + 0x04, endianness); + symbol.st_size = data.getDoubleWord(pos + 0x08, endianness); + symbol.st_info = data[pos + 0x0C]; + symbol.st_other = data[pos + 0x0D]; + symbol.st_shndx = data.getWord(pos + 0x0E, endianness); + + return true; +} + +const char* ElfFile::getStrTableString(size_t pos) +{ + if (strTab == nullptr) + return nullptr; + + return (const char*) &strTab->getData()[pos]; +} + +// file: Core/ELF/ElfRelocator.cpp + +struct ArFileHeader +{ + char fileName[16]; + char modifactionTime[12]; + char ownerId[6]; + char groupId[6]; + char fileMode[8]; + char fileSize[10]; + char magic[2]; +}; + +struct ArFileEntry +{ + std::wstring name; + ByteArray data; +}; + +std::vector<ArFileEntry> loadArArchive(const std::wstring& inputName) +{ + ByteArray input = ByteArray::fromFile(inputName); + std::vector<ArFileEntry> result; + + if (input.size() < 8 || memcmp(input.data(),"!<arch>\n",8) != 0) + { + if (input.size() < 4 || memcmp(input.data(),"\x7F""ELF",4) != 0) + return result; + + ArFileEntry entry; + entry.name = getFileNameFromPath(inputName); + entry.data = input; + result.push_back(entry); + return result; + } + + size_t pos = 8; + while (pos < input.size()) + { + ArFileHeader* header = (ArFileHeader*) input.data(pos); + pos += sizeof(ArFileHeader); + + // get file size + int size = 0; + for (int i = 0; i < 10; i++) + { + if (header->fileSize[i] == ' ') + break; + + size = size*10; + size += (header->fileSize[i]-'0'); + } + + // only ELF files are actually interesting + if (memcmp(input.data(pos),"\x7F""ELF",4) == 0) + { + // get file name + char fileName[17]; + fileName[16] = 0; + for (int i = 0; i < 16; i++) + { + if (header->fileName[i] == ' ') + { + // remove trailing slashes of file names + if (i > 0 && fileName[i-1] == '/') + i--; + fileName[i] = 0; + break;; + } + + fileName[i] = header->fileName[i]; + } + + ArFileEntry entry; + entry.name = convertUtf8ToWString(fileName); + entry.data = input.mid(pos,size); + result.push_back(entry); + } + + pos += size; + if (pos % 2) + pos++; + } + + return result; +} + +bool ElfRelocator::init(const std::wstring& inputName) +{ + relocator = Arch->getElfRelocator(); + if (relocator == nullptr) + { + Logger::printError(Logger::Error,L"Object importing not supported for this architecture"); + return false; + } + + auto inputFiles = loadArArchive(inputName); + if (inputFiles.size() == 0) + { + Logger::printError(Logger::Error,L"Could not load library"); + return false; + } + + for (ArFileEntry& entry: inputFiles) + { + ElfRelocatorFile file; + + ElfFile* elf = new ElfFile(); + if (elf->load(entry.data,false) == false) + { + Logger::printError(Logger::Error,L"Could not load object file %s",entry.name); + return false; + } + + if (elf->getType() != ET_REL) + { + Logger::printError(Logger::Error,L"Unexpected ELF type %d in object file %s",elf->getType(),entry.name); + return false; + } + + if (elf->getMachine() != relocator->expectedMachine()) + { + Logger::printError(Logger::Error,L"Unexpected ELF machine %d in object file %s",elf->getMachine(),entry.name); + return false; + } + + if (elf->getEndianness() != Arch->getEndianness()) + { + Logger::printError(Logger::Error,L"Incorrect endianness in object file %s",entry.name); + return false; + } + + if (elf->getSegmentCount() != 0) + { + Logger::printError(Logger::Error,L"Unexpected segment count %d in object file %s",elf->getSegmentCount(),entry.name); + return false; + } + + + // load all relevant sections of this file + for (size_t s = 0; s < elf->getSegmentlessSectionCount(); s++) + { + ElfSection* sec = elf->getSegmentlessSection(s); + if (!(sec->getFlags() & SHF_ALLOC)) + continue; + + if (sec->getType() == SHT_PROGBITS || sec->getType() == SHT_NOBITS || sec->getType() == SHT_INIT_ARRAY) + { + ElfRelocatorSection sectionEntry; + sectionEntry.section = sec; + sectionEntry.index = s; + sectionEntry.relSection = nullptr; + sectionEntry.label = nullptr; + + // search relocation section + for (size_t k = 0; k < elf->getSegmentlessSectionCount(); k++) + { + ElfSection* relSection = elf->getSegmentlessSection(k); + if (relSection->getType() != SHT_REL) + continue; + if (relSection->getInfo() != s) + continue; + + // got it + sectionEntry.relSection = relSection; + break; + } + + // keep track of constructor sections + if (sec->getName() == ".ctors" || sec->getName() == ".init_array") + { + ElfRelocatorCtor ctor; + ctor.symbolName = Global.symbolTable.getUniqueLabelName(); + ctor.size = sec->getSize(); + + sectionEntry.label = Global.symbolTable.getLabel(ctor.symbolName,-1,-1); + sectionEntry.label->setDefined(true); + + ctors.push_back(ctor); + } + + file.sections.push_back(sectionEntry); + } + } + + // init exportable symbols + for (int i = 0; i < elf->getSymbolCount(); i++) + { + Elf32_Sym symbol; + elf->getSymbol(symbol, i); + + if (ELF32_ST_BIND(symbol.st_info) == STB_GLOBAL && symbol.st_shndx != 0) + { + ElfRelocatorSymbol symEntry; + symEntry.type = ELF32_ST_TYPE(symbol.st_info); + symEntry.name = convertUtf8ToWString(elf->getStrTableString(symbol.st_name)); + symEntry.relativeAddress = symbol.st_value; + symEntry.section = symbol.st_shndx; + symEntry.size = symbol.st_size; + symEntry.label = nullptr; + + file.symbols.push_back(symEntry); + } + } + + file.elf = elf; + file.name = entry.name; + files.push_back(file); + } + + return true; +} + +bool ElfRelocator::exportSymbols() +{ + bool error = false; + + for (ElfRelocatorFile& file: files) + { + for (ElfRelocatorSymbol& sym: file.symbols) + { + if (sym.label != nullptr) + continue; + + std::wstring lowered = sym.name; + std::transform(lowered.begin(), lowered.end(), lowered.begin(), ::towlower); + + sym.label = Global.symbolTable.getLabel(lowered,-1,-1); + if (sym.label == nullptr) + { + Logger::printError(Logger::Error,L"Invalid label name \"%s\"",sym.name); + error = true; + continue; + } + + if (sym.label->isDefined()) + { + Logger::printError(Logger::Error,L"Label \"%s\" already defined",sym.name); + error = true; + continue; + } + + RelocationData data; + data.symbolAddress = sym.relativeAddress; + relocator->setSymbolAddress(data,sym.relativeAddress,sym.type); + + sym.relativeAddress = data.symbolAddress; + sym.label->setInfo(data.targetSymbolInfo); + sym.label->setIsData(sym.type == STT_OBJECT); + sym.label->setUpdateInfo(false); + + sym.label->setValue(0); + sym.label->setDefined(true); + sym.label->setOriginalName(sym.name); + } + } + + return !error; +} + +std::unique_ptr<CAssemblerCommand> ElfRelocator::generateCtor(const std::wstring& ctorName) +{ + std::unique_ptr<CAssemblerCommand> content = relocator->generateCtorStub(ctors); + + auto func = ::make_unique<CDirectiveFunction>(ctorName,ctorName); + func->setContent(std::move(content)); + return func; +} + +void ElfRelocator::loadRelocation(Elf32_Rel& rel, ByteArray& data, int offset, Endianness endianness) +{ + rel.r_offset = data.getDoubleWord(offset + 0x00, endianness); + rel.r_info = data.getDoubleWord(offset + 0x04, endianness); +} + +bool ElfRelocator::relocateFile(ElfRelocatorFile& file, int64_t& relocationAddress) +{ + ElfFile* elf = file.elf; + int64_t start = relocationAddress; + + // calculate address for each section + std::map<int64_t,int64_t> relocationOffsets; + for (ElfRelocatorSection& entry: file.sections) + { + ElfSection* section = entry.section; + size_t index = entry.index; + int size = section->getSize(); + + while (relocationAddress % section->getAlignment()) + relocationAddress++; + + if (entry.label != nullptr) + entry.label->setValue(relocationAddress); + + relocationOffsets[index] = relocationAddress; + relocationAddress += size; + } + + size_t dataStart = outputData.size(); + outputData.reserveBytes((size_t)(relocationAddress-start)); + + // load sections + bool error = false; + for (ElfRelocatorSection& entry: file.sections) + { + ElfSection* section = entry.section; + size_t index = entry.index; + + if (section->getType() == SHT_NOBITS) + { + // reserveBytes initialized the data to 0 already + continue; + } + + ByteArray sectionData = section->getData(); + + // relocate if necessary + ElfSection* relSection = entry.relSection; + if (relSection != nullptr) + { + std::vector<RelocationAction> relocationActions; + for (unsigned int relOffset = 0; relOffset < relSection->getSize(); relOffset += sizeof(Elf32_Rel)) + { + Elf32_Rel rel; + loadRelocation(rel, relSection->getData(), relOffset, elf->getEndianness()); + int pos = rel.r_offset; + + if (relocator->isDummyRelocationType(rel.getType())) + continue; + + int symNum = rel.getSymbolNum(); + if (symNum <= 0) + { + Logger::queueError(Logger::Warning,L"Invalid symbol num %06X",symNum); + error = true; + continue; + } + + Elf32_Sym sym; + elf->getSymbol(sym, symNum); + int symSection = sym.st_shndx; + + RelocationData relData; + relData.opcode = sectionData.getDoubleWord(pos, elf->getEndianness()); + relData.opcodeOffset = pos+relocationOffsets[index]; + relocator->setSymbolAddress(relData,sym.st_value,sym.st_info & 0xF); + + // externs? + if (sym.st_shndx == 0) + { + if (sym.st_name == 0) + { + Logger::queueError(Logger::Error, L"Symbol without a name"); + error = true; + continue; + } + + std::wstring symName = toWLowercase(elf->getStrTableString(sym.st_name)); + + std::shared_ptr<Label> label = Global.symbolTable.getLabel(symName,-1,-1); + if (label == nullptr) + { + Logger::queueError(Logger::Error,L"Invalid external symbol %s",symName); + error = true; + continue; + } + if (label->isDefined() == false) + { + Logger::queueError(Logger::Error,L"Undefined external symbol %s in file %s",symName,file.name); + error = true; + continue; + } + + relData.relocationBase = (unsigned int) label->getValue(); + relData.targetSymbolType = label->isData() ? STT_OBJECT : STT_FUNC; + relData.targetSymbolInfo = label->getInfo(); + } else { + relData.relocationBase = relocationOffsets[symSection]+relData.symbolAddress; + } + + std::vector<std::wstring> errors; + if (!relocator->relocateOpcode(rel.getType(),relData, relocationActions, errors)) + { + for (const std::wstring& error : errors) + { + Logger::queueError(Logger::Error, error); + } + error = true; + continue; + } + } + + // finish any dangling relocations + std::vector<std::wstring> errors; + if (!relocator->finish(relocationActions, errors)) + { + for (const std::wstring& error : errors) + { + Logger::queueError(Logger::Error, error); + } + error = true; + } + + // now actually write the relocated values + for (const RelocationAction& action : relocationActions) + { + sectionData.replaceDoubleWord(action.offset-relocationOffsets[index], action.newValue, elf->getEndianness()); + } + } + + size_t arrayStart = (size_t) (dataStart+relocationOffsets[index]-start); + memcpy(outputData.data(arrayStart),sectionData.data(),sectionData.size()); + } + + // now update symbols + for (ElfRelocatorSymbol& sym: file.symbols) + { + int64_t oldAddress = sym.relocatedAddress; + + switch (sym.section) + { + case SHN_ABS: // address does not change + sym.relocatedAddress = sym.relativeAddress; + break; + case SHN_COMMON: // needs to be allocated. relativeAddress gives alignment constraint + { + int64_t start = relocationAddress; + + while (relocationAddress % sym.relativeAddress) + relocationAddress++; + + sym.relocatedAddress = relocationAddress; + relocationAddress += sym.size; + outputData.reserveBytes((size_t)(relocationAddress-start)); + } + break; + default: // normal relocated symbol + sym.relocatedAddress = sym.relativeAddress+relocationOffsets[sym.section]; + break; + } + + if (sym.label != nullptr) + sym.label->setValue(sym.relocatedAddress); + + if (oldAddress != sym.relocatedAddress) + dataChanged = true; + } + + return !error; +} + +bool ElfRelocator::relocate(int64_t& memoryAddress) +{ + int oldCrc = getCrc32(outputData.data(),outputData.size()); + outputData.clear(); + dataChanged = false; + + bool error = false; + int64_t start = memoryAddress; + + for (ElfRelocatorFile& file: files) + { + if (relocateFile(file,memoryAddress) == false) + error = true; + } + + int newCrc = getCrc32(outputData.data(),outputData.size()); + if (oldCrc != newCrc) + dataChanged = true; + + memoryAddress -= start; + return !error; +} + +void ElfRelocator::writeSymbols(SymbolData& symData) const +{ + for (const ElfRelocatorFile& file: files) + { + for (const ElfRelocatorSymbol& sym: file.symbols) + { + symData.addLabel(sym.relocatedAddress,sym.name); + + switch (sym.type) + { + case STT_OBJECT: + symData.addData(sym.relocatedAddress,sym.size,SymbolData::Data8); + break; + case STT_FUNC: + symData.startFunction(sym.relocatedAddress); + symData.endFunction(sym.relocatedAddress+sym.size); + break; + } + } + } +} + +// file: Core/Assembler.cpp +#include <thread> + +void AddFileName(const std::wstring& FileName) +{ + Global.FileInfo.FileNum = (int) Global.FileInfo.FileList.size(); + Global.FileInfo.FileList.push_back(FileName); + Global.FileInfo.LineNumber = 0; +} + +bool encodeAssembly(std::unique_ptr<CAssemblerCommand> content, SymbolData& symData, TempData& tempData) +{ + bool Revalidate; + +#ifdef ARMIPS_ARM + Arm.Pass2(); +#endif + Mips.Pass2(); + + int validationPasses = 0; + do // loop until everything is constant + { + Global.validationPasses = validationPasses; + Logger::clearQueue(); + Revalidate = false; + + if (validationPasses >= 100) + { + Logger::queueError(Logger::Error,L"Stuck in infinite validation loop"); + break; + } + + g_fileManager->reset(); + +#ifdef _DEBUG + if (!Logger::isSilent()) + printf("Validate %d...\n",validationPasses); +#endif + + if (Global.memoryMode) + g_fileManager->openFile(Global.memoryFile,true); + + Revalidate = content->Validate(); + +#ifdef ARMIPS_ARM + Arm.Revalidate(); +#endif + Mips.Revalidate(); + + if (Global.memoryMode) + g_fileManager->closeFile(); + + validationPasses++; + } while (Revalidate == true); + + Logger::printQueue(); + if (Logger::hasError() == true) + { + return false; + } + +#ifdef _DEBUG + if (!Logger::isSilent()) + printf("Encode...\n"); +#endif + + // and finally encode + if (Global.memoryMode) + g_fileManager->openFile(Global.memoryFile,false); + + auto writeTempData = [&]() + { + tempData.start(); + if (tempData.isOpen()) + content->writeTempData(tempData); + tempData.end(); + }; + + auto writeSymData = [&]() + { + content->writeSymData(symData); + symData.write(); + }; + + // writeTempData, writeSymData and encode all access the same + // memory but never change, so they can run in parallel + if (Global.multiThreading) + { + std::thread tempThread(writeTempData); + std::thread symThread(writeSymData); + + content->Encode(); + + tempThread.join(); + symThread.join(); + } else { + writeTempData(); + writeSymData(); + content->Encode(); + } + + if (g_fileManager->hasOpenFile()) + { + if (!Global.memoryMode) + Logger::printError(Logger::Warning,L"File not closed"); + g_fileManager->closeFile(); + } + + return true; +} + +bool runArmips(ArmipsArguments& settings) +{ + // initialize and reset global data + Global.Section = 0; + Global.nocash = false; + Global.FileInfo.FileCount = 0; + Global.FileInfo.TotalLineCount = 0; + Global.relativeInclude = false; + Global.validationPasses = 0; + Global.multiThreading = true; + Arch = &InvalidArchitecture; + + Tokenizer::clearEquValues(); + Logger::clear(); + Global.Table.clear(); + Global.symbolTable.clear(); + + Global.FileInfo.FileList.clear(); + Global.FileInfo.FileCount = 0; + Global.FileInfo.TotalLineCount = 0; + Global.FileInfo.LineNumber = 0; + Global.FileInfo.FileNum = 0; + +#ifdef ARMIPS_ARM + Arm.clear(); +#endif + + // process settings + Parser parser; + SymbolData symData; + TempData tempData; + + Logger::setSilent(settings.silent); + Logger::setErrorOnWarning(settings.errorOnWarning); + + if (!settings.symFileName.empty()) + symData.setNocashSymFileName(settings.symFileName, settings.symFileVersion); + + if (!settings.tempFileName.empty()) + tempData.setFileName(settings.tempFileName); + + Token token; + for (size_t i = 0; i < settings.equList.size(); i++) + { + parser.addEquation(token, settings.equList[i].name, settings.equList[i].value); + } + + Global.symbolTable.addLabels(settings.labels); + for (const LabelDefinition& label : settings.labels) + { + symData.addLabel(label.value, label.originalName); + } + + if (Logger::hasError()) + return false; + + // run assembler + TextFile input; + switch (settings.mode) + { + case ArmipsMode::FILE: + Global.memoryMode = false; + if (input.open(settings.inputFileName,TextFile::Read) == false) + { + Logger::printError(Logger::Error,L"Could not open file"); + return false; + } + break; + case ArmipsMode::MEMORY: + Global.memoryMode = true; + Global.memoryFile = settings.memoryFile; + input.openMemory(settings.content); + break; + } + + std::unique_ptr<CAssemblerCommand> content = parser.parseFile(input); + Logger::printQueue(); + + bool result = !Logger::hasError(); + if (result == true && content != nullptr) + result = encodeAssembly(std::move(content), symData, tempData); + + if (g_fileManager->hasOpenFile()) + { + if (!Global.memoryMode) + Logger::printError(Logger::Warning,L"File not closed"); + g_fileManager->closeFile(); + } + + // return errors + if (settings.errorsResult != nullptr) + { + StringList errors = Logger::getErrors(); + for (size_t i = 0; i < errors.size(); i++) + settings.errorsResult->push_back(errors[i]); + } + + return result; +} + +// file: Core/Common.cpp +#include <sys/stat.h> + +FileManager fileManager; +FileManager* g_fileManager = &fileManager; + +tGlobal Global; +CArchitecture* Arch; + +std::wstring getFolderNameFromPath(const std::wstring& src) +{ +#ifdef _WIN32 + size_t s = src.find_last_of(L"\\/"); +#else + size_t s = src.rfind(L"/"); +#endif + if (s == std::wstring::npos) + { + return L"."; + } + + return src.substr(0,s); +} + +std::wstring getFullPathName(const std::wstring& path) +{ + if (Global.relativeInclude == true) + { + if (isAbsolutePath(path)) + { + return path; + } else { + std::wstring source = Global.FileInfo.FileList[Global.FileInfo.FileNum]; + return getFolderNameFromPath(source) + L"/" + path; + } + } else { + return path; + } +} + +bool checkLabelDefined(const std::wstring& labelName, int section) +{ + std::shared_ptr<Label> label = Global.symbolTable.getLabel(labelName,Global.FileInfo.FileNum,section); + return label->isDefined(); +} + +bool checkValidLabelName(const std::wstring& labelName) +{ + return Global.symbolTable.isValidSymbolName(labelName); +} + +bool isPowerOfTwo(int64_t n) +{ + if (n == 0) return false; + return !(n & (n - 1)); +} + +// file: Core/Expression.cpp + +enum class ExpressionValueCombination +{ + II = (int(ExpressionValueType::Integer) << 2) | (int(ExpressionValueType::Integer) << 0), + IF = (int(ExpressionValueType::Integer) << 2) | (int(ExpressionValueType::Float) << 0), + FI = (int(ExpressionValueType::Float) << 2) | (int(ExpressionValueType::Integer) << 0), + FF = (int(ExpressionValueType::Float) << 2) | (int(ExpressionValueType::Float) << 0), + IS = (int(ExpressionValueType::Integer) << 2) | (int(ExpressionValueType::String) << 0), + FS = (int(ExpressionValueType::Float) << 2) | (int(ExpressionValueType::String) << 0), + SI = (int(ExpressionValueType::String) << 2) | (int(ExpressionValueType::Integer) << 0), + SF = (int(ExpressionValueType::String) << 2) | (int(ExpressionValueType::Float) << 0), + SS = (int(ExpressionValueType::String) << 2) | (int(ExpressionValueType::String) << 0), +}; + +ExpressionValueCombination getValueCombination(ExpressionValueType a, ExpressionValueType b) +{ + return (ExpressionValueCombination) ((int(a) << 2) | (int(b) << 0)); +} + +ExpressionValue ExpressionValue::operator+(const ExpressionValue& other) const +{ + ExpressionValue result; + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + result.type = ExpressionValueType::Integer; + result.intValue = intValue + other.intValue; + break; + case ExpressionValueCombination::FI: + result.type = ExpressionValueType::Float; + result.floatValue = floatValue + other.intValue; + break; + case ExpressionValueCombination::IF: + result.type = ExpressionValueType::Float; + result.floatValue = intValue + other.floatValue; + break; + case ExpressionValueCombination::FF: + result.type = ExpressionValueType::Float; + result.floatValue = floatValue + other.floatValue; + break; + case ExpressionValueCombination::IS: + result.type = ExpressionValueType::String; + result.strValue = to_wstring(intValue) + other.strValue; + break; + case ExpressionValueCombination::FS: + result.type = ExpressionValueType::String; + result.strValue = to_wstring(floatValue) + other.strValue; + break; + case ExpressionValueCombination::SI: + result.type = ExpressionValueType::String; + result.strValue = strValue + to_wstring(other.intValue); + break; + case ExpressionValueCombination::SF: + result.type = ExpressionValueType::String; + result.strValue = strValue + to_wstring(other.floatValue); + break; + case ExpressionValueCombination::SS: + result.type = ExpressionValueType::String; + result.strValue = strValue + other.strValue; + break; + } + + return result; +} + +ExpressionValue ExpressionValue::operator-(const ExpressionValue& other) const +{ + ExpressionValue result; + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + result.type = ExpressionValueType::Integer; + result.intValue = intValue - other.intValue; + break; + case ExpressionValueCombination::FI: + result.type = ExpressionValueType::Float; + result.floatValue = floatValue - other.intValue; + break; + case ExpressionValueCombination::IF: + result.type = ExpressionValueType::Float; + result.floatValue = intValue - other.floatValue; + break; + case ExpressionValueCombination::FF: + result.type = ExpressionValueType::Float; + result.floatValue = floatValue - other.floatValue; + break; + default: + break; + } + + return result; +} + +ExpressionValue ExpressionValue::operator*(const ExpressionValue& other) const +{ + ExpressionValue result; + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + result.type = ExpressionValueType::Integer; + result.intValue = intValue * other.intValue; + break; + case ExpressionValueCombination::FI: + result.type = ExpressionValueType::Float; + result.floatValue = floatValue * other.intValue; + break; + case ExpressionValueCombination::IF: + result.type = ExpressionValueType::Float; + result.floatValue = intValue * other.floatValue; + break; + case ExpressionValueCombination::FF: + result.type = ExpressionValueType::Float; + result.floatValue = floatValue * other.floatValue; + break; + default: + break; + } + + return result; +} + +ExpressionValue ExpressionValue::operator/(const ExpressionValue& other) const +{ + ExpressionValue result; + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + result.type = ExpressionValueType::Integer; + if (intValue == INT64_MIN && other.intValue == -1){ + result.intValue = INT64_MIN; + Logger::queueError(Logger::Warning,L"Division overflow in expression"); + return result; + } + if (other.intValue == 0) + { + result.intValue = ~0; + Logger::queueError(Logger::Warning,L"Integer division by zero in expression"); + return result; + } + result.intValue = intValue / other.intValue; + break; + case ExpressionValueCombination::FI: + result.type = ExpressionValueType::Float; + result.floatValue = floatValue / other.intValue; + break; + case ExpressionValueCombination::IF: + result.type = ExpressionValueType::Float; + result.floatValue = intValue / other.floatValue; + break; + case ExpressionValueCombination::FF: + result.type = ExpressionValueType::Float; + result.floatValue = floatValue / other.floatValue; + break; + default: + break; + } + + return result; +} + +ExpressionValue ExpressionValue::operator%(const ExpressionValue& other) const +{ + ExpressionValue result; + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + result.type = ExpressionValueType::Integer; + if (intValue == INT64_MIN && other.intValue == -1){ + result.intValue = 0; + Logger::queueError(Logger::Warning,L"Division overflow in expression"); + return result; + } + if (other.intValue == 0) + { + result.intValue = intValue; + Logger::queueError(Logger::Warning,L"Integer division by zero in expression"); + return result; + } + result.intValue = intValue % other.intValue; + break; + default: + break; + } + + return result; +} + +ExpressionValue ExpressionValue::operator!() const +{ + ExpressionValue result; + result.type = ExpressionValueType::Integer; + + if (isFloat()) + result.intValue = !floatValue; + else + result.intValue = !intValue; + + return result; +} + +ExpressionValue ExpressionValue::operator~() const +{ + ExpressionValue result; + + if (isInt()) + { + result.type = ExpressionValueType::Integer; + result.intValue = ~intValue; + } + + return result; +} + +ExpressionValue ExpressionValue::operator<<(const ExpressionValue& other) const +{ + ExpressionValue result; + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + result.type = ExpressionValueType::Integer; + result.intValue = ((uint64_t) intValue) << other.intValue; + break; + default: + break; + } + + return result; +} + +ExpressionValue ExpressionValue::operator>>(const ExpressionValue& other) const +{ + ExpressionValue result; + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + result.type = ExpressionValueType::Integer; + result.intValue = ((uint64_t) intValue) >> other.intValue; + break; + default: + break; + } + + return result; +} + +bool ExpressionValue::operator<(const ExpressionValue& other) const +{ + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + return intValue < other.intValue; + case ExpressionValueCombination::FI: + return floatValue < other.intValue; + case ExpressionValueCombination::IF: + return intValue < other.floatValue; + case ExpressionValueCombination::FF: + return floatValue < other.floatValue; + case ExpressionValueCombination::SS: + return strValue < other.strValue; + default: + break; + } + + return false; +} + +bool ExpressionValue::operator<=(const ExpressionValue& other) const +{ + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + return intValue <= other.intValue; + case ExpressionValueCombination::FI: + return floatValue <= other.intValue; + case ExpressionValueCombination::IF: + return intValue <= other.floatValue; + case ExpressionValueCombination::FF: + return floatValue <= other.floatValue; + case ExpressionValueCombination::SS: + return strValue <= other.strValue; + default: + break; + } + + return false; +} + +bool ExpressionValue::operator>(const ExpressionValue& other) const +{ + return other < *this; +} + +bool ExpressionValue::operator>=(const ExpressionValue& other) const +{ + return other <= *this; +} + +bool ExpressionValue::operator==(const ExpressionValue& other) const +{ + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + return intValue == other.intValue; + case ExpressionValueCombination::FI: + return floatValue == other.intValue; + case ExpressionValueCombination::IF: + return intValue == other.floatValue; + case ExpressionValueCombination::FF: + return floatValue == other.floatValue; + case ExpressionValueCombination::IS: + return to_wstring(intValue) == other.strValue; + case ExpressionValueCombination::FS: + return to_wstring(floatValue) == other.strValue; + case ExpressionValueCombination::SI: + return strValue == to_wstring(other.intValue); + case ExpressionValueCombination::SF: + return strValue == to_wstring(other.floatValue); + case ExpressionValueCombination::SS: + return strValue == other.strValue; + } + + return false; +} + +bool ExpressionValue::operator!=(const ExpressionValue& other) const +{ + return !(*this == other); +} + +ExpressionValue ExpressionValue::operator&(const ExpressionValue& other) const +{ + ExpressionValue result; + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + result.type = ExpressionValueType::Integer; + result.intValue = intValue & other.intValue; + break; + default: + break; + } + + return result; +} + +ExpressionValue ExpressionValue::operator|(const ExpressionValue& other) const +{ + ExpressionValue result; + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + result.type = ExpressionValueType::Integer; + result.intValue = intValue | other.intValue; + break; + default: + break; + } + + return result; +} + +ExpressionValue ExpressionValue::operator^(const ExpressionValue& other) const +{ + ExpressionValue result; + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + result.type = ExpressionValueType::Integer; + result.intValue = intValue ^ other.intValue; + break; + default: + break; + } + + return result; +} + +ExpressionValue ExpressionValue::operator&&(const ExpressionValue& other) const +{ + ExpressionValue result; + result.type = ExpressionValueType::Integer; + + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + result.intValue = intValue && other.intValue; + break; + case ExpressionValueCombination::FI: + result.floatValue = floatValue && other.intValue; + break; + case ExpressionValueCombination::IF: + result.floatValue = intValue && other.floatValue; + break; + case ExpressionValueCombination::FF: + result.floatValue = floatValue && other.floatValue; + break; + default: + break; + } + + return result; +} + +ExpressionValue ExpressionValue::operator||(const ExpressionValue& other) const +{ + ExpressionValue result; + result.type = ExpressionValueType::Integer; + + switch (getValueCombination(type,other.type)) + { + case ExpressionValueCombination::II: + result.intValue = intValue || other.intValue; + break; + case ExpressionValueCombination::FI: + result.floatValue = floatValue || other.intValue; + break; + case ExpressionValueCombination::IF: + result.floatValue = intValue || other.floatValue; + break; + case ExpressionValueCombination::FF: + result.floatValue = floatValue || other.floatValue; + break; + default: + break; + } + + return result; +} + +ExpressionInternal::ExpressionInternal() +{ + children = nullptr; + childrenCount = 0; +} + +ExpressionInternal::~ExpressionInternal() +{ + deallocate(); +} + +ExpressionInternal::ExpressionInternal(int64_t value) + : ExpressionInternal() +{ + type = OperatorType::Integer; + intValue = value; +} + +ExpressionInternal::ExpressionInternal(double value) + : ExpressionInternal() +{ + type = OperatorType::Float; + floatValue = value; +} + +ExpressionInternal::ExpressionInternal(const std::wstring& value, OperatorType type) + : ExpressionInternal() +{ + this->type = type; + strValue = value; + + switch (type) + { + case OperatorType::Identifier: + fileNum = Global.FileInfo.FileNum; + section = Global.Section; + break; + case OperatorType::String: + break; + default: + break; + } +} + +ExpressionInternal::ExpressionInternal(OperatorType op, ExpressionInternal* a, + ExpressionInternal* b, ExpressionInternal* c) + : ExpressionInternal() +{ + type = op; + allocate(3); + + children[0] = a; + children[1] = b; + children[2] = c; +} + +ExpressionInternal::ExpressionInternal(const std::wstring& name, const std::vector<ExpressionInternal*>& parameters) + : ExpressionInternal() +{ + type = OperatorType::FunctionCall; + allocate(parameters.size()); + + strValue = name; + for (size_t i = 0; i < parameters.size(); i++) + { + children[i] = parameters[i]; + } +} + +void ExpressionInternal::allocate(size_t count) +{ + deallocate(); + + children = new ExpressionInternal*[count]; + childrenCount = count; +} + +void ExpressionInternal::deallocate() +{ + for (size_t i = 0; i < childrenCount; i++) + { + delete children[i]; + } + + delete[] children; + children = nullptr; + childrenCount = 0; +} + +void ExpressionInternal::replaceMemoryPos(const std::wstring& identifierName) +{ + for (size_t i = 0; i < childrenCount; i++) + { + if (children[i] != nullptr) + { + children[i]->replaceMemoryPos(identifierName); + } + } + + if (type == OperatorType::MemoryPos) + { + type = OperatorType::Identifier; + strValue = identifierName; + fileNum = Global.FileInfo.FileNum; + section = Global.Section; + } +} + +bool ExpressionInternal::checkParameterCount(size_t minParams, size_t maxParams) +{ + if (minParams > childrenCount) + { + Logger::queueError(Logger::Error,L"Not enough parameters for \"%s\" (min %d)",strValue,minParams); + return false; + } + + if (maxParams < childrenCount) + { + Logger::queueError(Logger::Error,L"Too many parameters for \"%s\" (min %d)",strValue,maxParams); + return false; + } + + return true; +} + +ExpressionValue ExpressionInternal::executeExpressionFunctionCall(const ExpressionFunctionEntry& entry) +{ + // check parameters + if (!checkParameterCount(entry.minParams, entry.maxParams)) + return {}; + + // evaluate parameters + std::vector<ExpressionValue> params; + params.reserve(childrenCount); + + for (size_t i = 0; i < childrenCount; i++) + { + ExpressionValue result = children[i]->evaluate(); + if (!result.isValid()) + { + Logger::queueError(Logger::Error,L"%s: Invalid parameter %d", strValue, i+1); + return result; + } + + params.push_back(result); + } + + // execute + return entry.function(strValue, params); +} + +ExpressionValue ExpressionInternal::executeExpressionLabelFunctionCall(const ExpressionLabelFunctionEntry& entry) +{ + // check parameters + if (!checkParameterCount(entry.minParams, entry.maxParams)) + return {}; + + // evaluate parameters + std::vector<std::shared_ptr<Label>> params; + params.reserve(childrenCount); + + for (size_t i = 0; i < childrenCount; i++) + { + ExpressionInternal *exp = children[i]; + if (!exp || !exp->isIdentifier()) + { + Logger::queueError(Logger::Error,L"%s: Invalid parameter %d, expecting identifier", strValue, i+1); + return {}; + } + + const std::wstring& name = exp->getStringValue(); + std::shared_ptr<Label> label = Global.symbolTable.getLabel(name,exp->getFileNum(),exp->getSection()); + params.push_back(label); + } + + // execute + return entry.function(strValue, params); +} + +ExpressionValue ExpressionInternal::executeFunctionCall() +{ + // try expression functions + auto expFuncIt = expressionFunctions.find(strValue); + if (expFuncIt != expressionFunctions.end()) + return executeExpressionFunctionCall(expFuncIt->second); + + // try expression label functions + auto expLabelFuncIt = expressionLabelFunctions.find(strValue); + if (expLabelFuncIt != expressionLabelFunctions.end()) + return executeExpressionLabelFunctionCall(expLabelFuncIt->second); + + // try architecture specific expression functions + auto& archExpressionFunctions = Arch->getExpressionFunctions(); + expFuncIt = archExpressionFunctions.find(strValue); + if (expFuncIt != archExpressionFunctions.end()) + return executeExpressionFunctionCall(expFuncIt->second); + + // error + Logger::queueError(Logger::Error, L"Unknown function \"%s\"", strValue); + return {}; +} + +bool isExpressionFunctionSafe(const std::wstring& name, bool inUnknownOrFalseBlock) +{ + // expression functions may be unsafe, others are safe + ExpFuncSafety safety = ExpFuncSafety::Unsafe; + bool found = false; + + auto it = expressionFunctions.find(name); + if (it != expressionFunctions.end()) + { + safety = it->second.safety; + found = true; + } + + if (!found) + { + auto labelIt = expressionLabelFunctions.find(name); + if (labelIt != expressionLabelFunctions.end()) + { + safety = labelIt->second.safety; + found = true; + } + } + + if (!found) + { + auto& archExpressionFunctions = Arch->getExpressionFunctions(); + it = archExpressionFunctions.find(name); + if (it != archExpressionFunctions.end()) + { + safety = it->second.safety; + found = true; + } + } + + if (inUnknownOrFalseBlock && safety == ExpFuncSafety::ConditionalUnsafe) + return false; + + return safety != ExpFuncSafety::Unsafe; +} + +bool ExpressionInternal::simplify(bool inUnknownOrFalseBlock) +{ + // check if this expression can actually be simplified + // without causing side effects + switch (type) + { + case OperatorType::Identifier: + case OperatorType::MemoryPos: + case OperatorType::ToString: + return false; + case OperatorType::FunctionCall: + if (isExpressionFunctionSafe(strValue, inUnknownOrFalseBlock) == false) + return false; + break; + default: + break; + } + + // check if the same applies to all children + bool canSimplify = true; + for (size_t i = 0; i < childrenCount; i++) + { + if (children[i] != nullptr && children[i]->simplify(inUnknownOrFalseBlock) == false) + canSimplify = false; + } + + // if so, this expression can be evaluated into a constant + if (canSimplify) + { + ExpressionValue value = evaluate(); + + switch (value.type) + { + case ExpressionValueType::Integer: + type = OperatorType::Integer; + intValue = value.intValue; + break; + case ExpressionValueType::Float: + type = OperatorType::Float; + floatValue = value.floatValue; + break; + case ExpressionValueType::String: + type = OperatorType::String; + strValue = value.strValue; + break; + default: + type = OperatorType::Invalid; + break; + } + + deallocate(); + } + + return canSimplify; +} + +ExpressionValue ExpressionInternal::evaluate() +{ + ExpressionValue val; + + std::shared_ptr<Label> label; + switch (type) + { + case OperatorType::Integer: + val.type = ExpressionValueType::Integer; + val.intValue = intValue; + return val; + case OperatorType::Float: + val.type = ExpressionValueType::Float; + val.floatValue = floatValue; + return val; + case OperatorType::Identifier: + label = Global.symbolTable.getLabel(strValue,fileNum,section); + if (label == nullptr) + { + Logger::queueError(Logger::Error,L"Invalid label name \"%s\"",strValue); + return val; + } + + if (!label->isDefined()) + { + Logger::queueError(Logger::Error,L"Undefined label \"%s\"",label->getName()); + return val; + } + + val.type = ExpressionValueType::Integer; + val.intValue = label->getValue(); + return val; + case OperatorType::String: + val.type = ExpressionValueType::String; + val.strValue = strValue; + return val; + case OperatorType::MemoryPos: + val.type = ExpressionValueType::Integer; + val.intValue = g_fileManager->getVirtualAddress(); + return val; + case OperatorType::ToString: + val.type = ExpressionValueType::String; + val.strValue = children[0]->toString(); + return val; + case OperatorType::Add: + return children[0]->evaluate() + children[1]->evaluate(); + case OperatorType::Sub: + return children[0]->evaluate() - children[1]->evaluate(); + case OperatorType::Mult: + return children[0]->evaluate() * children[1]->evaluate(); + case OperatorType::Div: + return children[0]->evaluate() / children[1]->evaluate(); + case OperatorType::Mod: + return children[0]->evaluate() % children[1]->evaluate(); + case OperatorType::Neg: + val.type = ExpressionValueType::Integer; + val.intValue = 0; + return val - children[0]->evaluate(); + case OperatorType::LogNot: + return !children[0]->evaluate(); + case OperatorType::BitNot: + return ~children[0]->evaluate(); + case OperatorType::LeftShift: + return children[0]->evaluate() << children[1]->evaluate(); + case OperatorType::RightShift: + return children[0]->evaluate() >> children[1]->evaluate(); + case OperatorType::Less: + val.type = ExpressionValueType::Integer; + val.intValue = children[0]->evaluate() < children[1]->evaluate(); + return val; + case OperatorType::Greater: + val.type = ExpressionValueType::Integer; + val.intValue = children[0]->evaluate() > children[1]->evaluate(); + return val; + case OperatorType::LessEqual: + val.type = ExpressionValueType::Integer; + val.intValue = children[0]->evaluate() <= children[1]->evaluate(); + return val; + case OperatorType::GreaterEqual: + val.type = ExpressionValueType::Integer; + val.intValue = children[0]->evaluate() >= children[1]->evaluate(); + return val; + case OperatorType::Equal: + val.type = ExpressionValueType::Integer; + val.intValue = children[0]->evaluate() == children[1]->evaluate(); + return val; + case OperatorType::NotEqual: + val.type = ExpressionValueType::Integer; + val.intValue = children[0]->evaluate() != children[1]->evaluate(); + return val; + case OperatorType::BitAnd: + return children[0]->evaluate() & children[1]->evaluate(); + case OperatorType::BitOr: + return children[0]->evaluate() | children[1]->evaluate(); + case OperatorType::LogAnd: + return children[0]->evaluate() && children[1]->evaluate(); + case OperatorType::LogOr: + return children[0]->evaluate() || children[1]->evaluate(); + case OperatorType::Xor: + return children[0]->evaluate() ^ children[1]->evaluate(); + case OperatorType::TertiaryIf: + val.type = ExpressionValueType::Integer; + val.intValue = 0; + if (children[0]->evaluate() == val) + return children[2]->evaluate(); + else + return children[1]->evaluate(); + case OperatorType::FunctionCall: + return executeFunctionCall(); + default: + return val; + } +} + +static std::wstring escapeString(const std::wstring& text) +{ + std::wstring result = text; + replaceAll(result,LR"(\)",LR"(\\)"); + replaceAll(result,LR"(")",LR"(\")"); + + return formatString(LR"("%s")",text); +} + +std::wstring ExpressionInternal::formatFunctionCall() +{ + std::wstring text = strValue + L"("; + + for (size_t i = 0; i < childrenCount; i++) + { + if (i != 0) + text += L","; + text += children[i]->toString(); + } + + return text + L")"; +} + +std::wstring ExpressionInternal::toString() +{ + switch (type) + { + case OperatorType::Integer: + return formatString(L"%d",intValue); + case OperatorType::Float: + return formatString(L"%g",floatValue); + case OperatorType::Identifier: + return strValue; + case OperatorType::String: + return escapeString(strValue); + case OperatorType::MemoryPos: + return L"."; + case OperatorType::Add: + return formatString(L"(%s + %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::Sub: + return formatString(L"(%s - %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::Mult: + return formatString(L"(%s * %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::Div: + return formatString(L"(%s / %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::Mod: + return formatString(L"(%s %% %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::Neg: + return formatString(L"(-%s)",children[0]->toString()); + case OperatorType::LogNot: + return formatString(L"(!%s)",children[0]->toString()); + case OperatorType::BitNot: + return formatString(L"(~%s)",children[0]->toString()); + case OperatorType::LeftShift: + return formatString(L"(%s << %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::RightShift: + return formatString(L"(%s >> %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::Less: + return formatString(L"(%s < %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::Greater: + return formatString(L"(%s > %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::LessEqual: + return formatString(L"(%s <= %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::GreaterEqual: + return formatString(L"(%s >= %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::Equal: + return formatString(L"(%s == %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::NotEqual: + return formatString(L"(%s != %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::BitAnd: + return formatString(L"(%s & %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::BitOr: + return formatString(L"(%s | %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::LogAnd: + return formatString(L"(%s && %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::LogOr: + return formatString(L"(%s || %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::Xor: + return formatString(L"(%s ^ %s)",children[0]->toString(),children[1]->toString()); + case OperatorType::TertiaryIf: + return formatString(L"(%s ? %s : %s)",children[0]->toString(),children[1]->toString(),children[2]->toString()); + case OperatorType::ToString: + return formatString(L"(%c%s)",L'\U000000B0',children[0]->toString()); + case OperatorType::FunctionCall: + return formatFunctionCall(); + default: + return L""; + } +} + +Expression::Expression() +{ + expression = nullptr; + constExpression = true; +} + +void Expression::setExpression(ExpressionInternal* exp, bool inUnknownOrFalseBlock) +{ + expression = std::shared_ptr<ExpressionInternal>(exp); + if (exp != nullptr) + constExpression = expression->simplify(inUnknownOrFalseBlock); + else + constExpression = true; +} + +ExpressionValue Expression::evaluate() +{ + if (expression == nullptr) + { + ExpressionValue invalid; + return invalid; + } + + return expression->evaluate(); +} + +void Expression::replaceMemoryPos(const std::wstring& identifierName) +{ + if (expression != nullptr) + expression->replaceMemoryPos(identifierName); +} + +Expression createConstExpression(int64_t value) +{ + Expression exp; + ExpressionInternal* num = new ExpressionInternal(value); + exp.setExpression(num,false); + return exp; +} + +// file: Core/ExpressionFunctions.cpp +#if ARMIPS_REGEXP +#include <regex> +#endif + +bool getExpFuncParameter(const std::vector<ExpressionValue>& parameters, size_t index, int64_t& dest, + const std::wstring& funcName, bool optional) +{ + if (optional && index >= parameters.size()) + return true; + + if (index >= parameters.size() || parameters[index].isInt() == false) + { + Logger::queueError(Logger::Error,L"Invalid parameter %d for %s: expecting integer",index+1,funcName); + return false; + } + + dest = parameters[index].intValue; + return true; +} + +bool getExpFuncParameter(const std::vector<ExpressionValue>& parameters, size_t index, const std::wstring*& dest, + const std::wstring& funcName, bool optional) +{ + if (optional && index >= parameters.size()) + return true; + + if (index >= parameters.size() || parameters[index].isString() == false) + { + Logger::queueError(Logger::Error,L"Invalid parameter %d for %s: expecting string",index+1,funcName); + return false; + } + + dest = &parameters[index].strValue; + return true; +} + +#define GET_PARAM(params,index,dest) \ + if (getExpFuncParameter(params,index,dest,funcName,false) == false) \ + return ExpressionValue(); +#define GET_OPTIONAL_PARAM(params,index,dest,defaultValue) \ + dest = defaultValue; \ + if (getExpFuncParameter(params,index,dest,funcName,true) == false) \ + return ExpressionValue(); + + +ExpressionValue expFuncVersion(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + int64_t value = ARMIPS_VERSION_MAJOR*1000 + ARMIPS_VERSION_MINOR*10 + ARMIPS_VERSION_REVISION; + return ExpressionValue(value); +} + +ExpressionValue expFuncEndianness(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + ExpressionValue result; + result.type = ExpressionValueType::String; + + switch (g_fileManager->getEndianness()) + { + case Endianness::Little: + return ExpressionValue(L"little"); + case Endianness::Big: + return ExpressionValue(L"big"); + } + + return ExpressionValue(); +} + +ExpressionValue expFuncOutputName(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + std::shared_ptr<AssemblerFile> file = g_fileManager->getOpenFile(); + if (file == nullptr) + { + Logger::queueError(Logger::Error,L"outputName: no file opened"); + return ExpressionValue(); + } + + std::wstring value = file->getFileName(); + return ExpressionValue(value); +} + +ExpressionValue expFuncFileExists(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + const std::wstring* fileName; + GET_PARAM(parameters,0,fileName); + + std::wstring fullName = getFullPathName(*fileName); + return ExpressionValue(fileExists(fullName) ? INT64_C(1) : INT64_C(0)); +} + +ExpressionValue expFuncFileSize(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + const std::wstring* fileName; + GET_PARAM(parameters,0,fileName); + + std::wstring fullName = getFullPathName(*fileName); + return ExpressionValue((int64_t) fileSize(fullName)); +} + +ExpressionValue expFuncToString(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + ExpressionValue result; + + switch (parameters[0].type) + { + case ExpressionValueType::String: + result.strValue = parameters[0].strValue; + break; + case ExpressionValueType::Integer: + result.strValue = formatString(L"%d",parameters[0].intValue); + break; + case ExpressionValueType::Float: + result.strValue = formatString(L"%#.17g",parameters[0].floatValue); + break; + default: + return result; + } + + result.type = ExpressionValueType::String; + return result; +} + +ExpressionValue expFuncToHex(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + int64_t value, digits; + GET_PARAM(parameters,0,value); + GET_OPTIONAL_PARAM(parameters,1,digits,8); + + return ExpressionValue(formatString(L"%0*X",digits,value)); +} + +ExpressionValue expFuncInt(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + ExpressionValue result; + + switch (parameters[0].type) + { + case ExpressionValueType::Integer: + result.intValue = parameters[0].intValue; + break; + case ExpressionValueType::Float: + result.intValue = (int64_t) parameters[0].floatValue; + break; + default: + return result; + } + + result.type = ExpressionValueType::Integer; + return result; +} + +ExpressionValue expFuncRound(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + ExpressionValue result; + + switch (parameters[0].type) + { + case ExpressionValueType::Integer: + result.intValue = parameters[0].intValue; + break; + case ExpressionValueType::Float: + result.intValue = llround(parameters[0].floatValue); + break; + default: + return result; + } + + result.type = ExpressionValueType::Integer; + return result; +} + +ExpressionValue expFuncFloat(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + ExpressionValue result; + + switch (parameters[0].type) + { + case ExpressionValueType::Integer: + result.floatValue = (double) parameters[0].intValue; + break; + case ExpressionValueType::Float: + result.floatValue = parameters[0].floatValue; + break; + default: + return result; + } + + result.type = ExpressionValueType::Float; + return result; +} + +ExpressionValue expFuncFrac(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + ExpressionValue result; + double intPart; + + switch (parameters[0].type) + { + case ExpressionValueType::Float: + result.floatValue = modf(parameters[0].floatValue,&intPart); + break; + default: + return result; + } + + result.type = ExpressionValueType::Float; + return result; +} + +ExpressionValue expFuncMin(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + ExpressionValue result; + double floatMin, floatCur; + int64_t intMin, intCur; + + floatCur = floatMin = std::numeric_limits<double>::max(); + intCur = intMin = std::numeric_limits<int64_t>::max(); + bool isInt = true; + + for (size_t i = 0; i < parameters.size(); i++) + { + switch (parameters[i].type) + { + case ExpressionValueType::Integer: + intCur = parameters[i].intValue; + floatCur = (double)parameters[i].intValue; + break; + case ExpressionValueType::Float: + floatCur = parameters[i].floatValue; + isInt = false; + break; + default: + return result; + } + + if (intCur < intMin) + intMin = intCur; + if (floatCur < floatMin) + floatMin = floatCur; + } + + if (isInt) + { + result.intValue = intMin; + result.type = ExpressionValueType::Integer; + } + else + { + result.floatValue = floatMin; + result.type = ExpressionValueType::Float; + } + + return result; +} + +ExpressionValue expFuncMax(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + ExpressionValue result; + double floatMax, floatCur; + int64_t intMax, intCur; + + floatCur = floatMax = std::numeric_limits<double>::min(); + intCur = intMax = std::numeric_limits<int64_t>::min(); + bool isInt = true; + + for (size_t i = 0; i < parameters.size(); i++) + { + switch (parameters[i].type) + { + case ExpressionValueType::Integer: + intCur = parameters[i].intValue; + floatCur = (double)parameters[i].intValue; + break; + case ExpressionValueType::Float: + floatCur = parameters[i].floatValue; + isInt = false; + break; + default: + return result; + } + + if (intCur > intMax) + intMax = intCur; + if (floatCur > floatMax) + floatMax = floatCur; + } + + if (isInt) + { + result.intValue = intMax; + result.type = ExpressionValueType::Integer; + } + else + { + result.floatValue = floatMax; + result.type = ExpressionValueType::Float; + } + + return result; +} + +ExpressionValue expFuncAbs(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + ExpressionValue result; + + switch (parameters[0].type) + { + case ExpressionValueType::Float: + result.type = ExpressionValueType::Float; + result.floatValue = fabs(parameters[0].floatValue); + break; + case ExpressionValueType::Integer: + result.type = ExpressionValueType::Integer; + result.intValue = parameters[0].intValue >= 0 ? + parameters[0].intValue : -parameters[0].intValue; + break; + default: + break; + } + + return result; +} + +ExpressionValue expFuncStrlen(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + const std::wstring* source; + GET_PARAM(parameters,0,source); + + return ExpressionValue((int64_t)source->size()); +} + +ExpressionValue expFuncSubstr(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + int64_t start, count; + const std::wstring* source; + + GET_PARAM(parameters,0,source); + GET_PARAM(parameters,1,start); + GET_PARAM(parameters,2,count); + + return ExpressionValue(source->substr((size_t)start,(size_t)count)); +} + +#if ARMIPS_REGEXP +ExpressionValue expFuncRegExMatch(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + const std::wstring* source; + const std::wstring* regexString; + + GET_PARAM(parameters,0,source); + GET_PARAM(parameters,1,regexString); + +#if ARMIPS_EXCEPTIONS + try + { +#endif + std::wregex regex(*regexString); + bool found = std::regex_match(*source,regex); + return ExpressionValue(found ? INT64_C(1) : INT64_C(0)); +#if ARMIPS_EXCEPTIONS + } catch (std::regex_error&) + { + Logger::queueError(Logger::Error,L"Invalid regular expression"); + return ExpressionValue(); + } +#endif +} + +ExpressionValue expFuncRegExSearch(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + const std::wstring* source; + const std::wstring* regexString; + + GET_PARAM(parameters,0,source); + GET_PARAM(parameters,1,regexString); + +#if ARMIPS_EXCEPTIONS + try + { +#endif + std::wregex regex(*regexString); + bool found = std::regex_search(*source,regex); + return ExpressionValue(found ? INT64_C(1) : INT64_C(0)); +#if ARMIPS_EXCEPTIONS + } catch (std::regex_error&) + { + Logger::queueError(Logger::Error,L"Invalid regular expression"); + return ExpressionValue(); + } +#endif +} + +ExpressionValue expFuncRegExExtract(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + const std::wstring* source; + const std::wstring* regexString; + int64_t matchIndex; + + GET_PARAM(parameters,0,source); + GET_PARAM(parameters,1,regexString); + GET_OPTIONAL_PARAM(parameters,2,matchIndex,0); + +#if ARMIPS_EXCEPTIONS + try + { +#endif + std::wregex regex(*regexString); + std::wsmatch result; + bool found = std::regex_search(*source,result,regex); + if (found == false || (size_t)matchIndex >= result.size()) + { + Logger::queueError(Logger::Error,L"Capture group index %d does not exist",matchIndex); + return ExpressionValue(); + } + + return ExpressionValue(result[(size_t)matchIndex].str()); +#if ARMIPS_EXCEPTIONS + } catch (std::regex_error&) + { + Logger::queueError(Logger::Error,L"Invalid regular expression"); + return ExpressionValue(); + } +#endif +} +#endif + +ExpressionValue expFuncFind(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + int64_t start; + const std::wstring* source; + const std::wstring* value; + + GET_PARAM(parameters,0,source); + GET_PARAM(parameters,1,value); + GET_OPTIONAL_PARAM(parameters,2,start,0); + + size_t pos = source->find(*value,(size_t)start); + return ExpressionValue(pos == std::wstring::npos ? INT64_C(-1) : (int64_t) pos); +} + +ExpressionValue expFuncRFind(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) +{ + int64_t start; + const std::wstring* source; + const std::wstring* value; + + GET_PARAM(parameters,0,source); + GET_PARAM(parameters,1,value); + GET_OPTIONAL_PARAM(parameters,2,start,std::wstring::npos); + + size_t pos = source->rfind(*value,(size_t)start); + return ExpressionValue(pos == std::wstring::npos ? INT64_C(-1) : (int64_t) pos); +} + + +template<typename T> +ExpressionValue expFuncRead(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) { - // 0XE000 to 0XE0FF - 0x6F3E, 0x6F13, 0x6EF7, 0x6F86, 0x6F7A, 0x6F78, 0x6F81, 0x6F80, 0x6F6F, 0x6F5B, 0x6FF3, 0x6F6D, 0x6F82, 0x6F7C, 0x6F58, 0x6F8E, - 0x6F91, 0x6FC2, 0x6F66, 0x6FB3, 0x6FA3, 0x6FA1, 0x6FA4, 0x6FB9, 0x6FC6, 0x6FAA, 0x6FDF, 0x6FD5, 0x6FEC, 0x6FD4, 0x6FD8, 0x6FF1, - 0x6FEE, 0x6FDB, 0x7009, 0x700B, 0x6FFA, 0x7011, 0x7001, 0x700F, 0x6FFE, 0x701B, 0x701A, 0x6F74, 0x701D, 0x7018, 0x701F, 0x7030, - 0x703E, 0x7032, 0x7051, 0x7063, 0x7099, 0x7092, 0x70AF, 0x70F1, 0x70AC, 0x70B8, 0x70B3, 0x70AE, 0x70DF, 0x70CB, 0x70DD, 0xFFFF, - 0x70D9, 0x7109, 0x70FD, 0x711C, 0x7119, 0x7165, 0x7155, 0x7188, 0x7166, 0x7162, 0x714C, 0x7156, 0x716C, 0x718F, 0x71FB, 0x7184, - 0x7195, 0x71A8, 0x71AC, 0x71D7, 0x71B9, 0x71BE, 0x71D2, 0x71C9, 0x71D4, 0x71CE, 0x71E0, 0x71EC, 0x71E7, 0x71F5, 0x71FC, 0x71F9, - 0x71FF, 0x720D, 0x7210, 0x721B, 0x7228, 0x722D, 0x722C, 0x7230, 0x7232, 0x723B, 0x723C, 0x723F, 0x7240, 0x7246, 0x724B, 0x7258, - 0x7274, 0x727E, 0x7282, 0x7281, 0x7287, 0x7292, 0x7296, 0x72A2, 0x72A7, 0x72B9, 0x72B2, 0x72C3, 0x72C6, 0x72C4, 0x72CE, 0x72D2, - 0x72E2, 0x72E0, 0x72E1, 0x72F9, 0x72F7, 0x500F, 0x7317, 0x730A, 0x731C, 0x7316, 0x731D, 0x7334, 0x732F, 0x7329, 0x7325, 0x733E, - 0x734E, 0x734F, 0x9ED8, 0x7357, 0x736A, 0x7368, 0x7370, 0x7378, 0x7375, 0x737B, 0x737A, 0x73C8, 0x73B3, 0x73CE, 0x73BB, 0x73C0, - 0x73E5, 0x73EE, 0x73DE, 0x74A2, 0x7405, 0x746F, 0x7425, 0x73F8, 0x7432, 0x743A, 0x7455, 0x743F, 0x745F, 0x7459, 0x7441, 0x745C, - 0x7469, 0x7470, 0x7463, 0x746A, 0x7476, 0x747E, 0x748B, 0x749E, 0x74A7, 0x74CA, 0x74CF, 0x74D4, 0x73F1, 0xFFFF, 0xFFFF, 0xFFFF, - // 0XE100 to 0XE1FF - 0x74E0, 0x74E3, 0x74E7, 0x74E9, 0x74EE, 0x74F2, 0x74F0, 0x74F1, 0x74F8, 0x74F7, 0x7504, 0x7503, 0x7505, 0x750C, 0x750E, 0x750D, - 0x7515, 0x7513, 0x751E, 0x7526, 0x752C, 0x753C, 0x7544, 0x754D, 0x754A, 0x7549, 0x755B, 0x7546, 0x755A, 0x7569, 0x7564, 0x7567, - 0x756B, 0x756D, 0x7578, 0x7576, 0x7586, 0x7587, 0x7574, 0x758A, 0x7589, 0x7582, 0x7594, 0x759A, 0x759D, 0x75A5, 0x75A3, 0x75C2, - 0x75B3, 0x75C3, 0x75B5, 0x75BD, 0x75B8, 0x75BC, 0x75B1, 0x75CD, 0x75CA, 0x75D2, 0x75D9, 0x75E3, 0x75DE, 0x75FE, 0x75FF, 0xFFFF, - 0x75FC, 0x7601, 0x75F0, 0x75FA, 0x75F2, 0x75F3, 0x760B, 0x760D, 0x7609, 0x761F, 0x7627, 0x7620, 0x7621, 0x7622, 0x7624, 0x7634, - 0x7630, 0x763B, 0x7647, 0x7648, 0x7646, 0x765C, 0x7658, 0x7661, 0x7662, 0x7668, 0x7669, 0x766A, 0x7667, 0x766C, 0x7670, 0x7672, - 0x7676, 0x7678, 0x767C, 0x7680, 0x7683, 0x7688, 0x768B, 0x768E, 0x7696, 0x7693, 0x7699, 0x769A, 0x76B0, 0x76B4, 0x76B8, 0x76B9, - 0x76BA, 0x76C2, 0x76CD, 0x76D6, 0x76D2, 0x76DE, 0x76E1, 0x76E5, 0x76E7, 0x76EA, 0x862F, 0x76FB, 0x7708, 0x7707, 0x7704, 0x7729, - 0x7724, 0x771E, 0x7725, 0x7726, 0x771B, 0x7737, 0x7738, 0x7747, 0x775A, 0x7768, 0x776B, 0x775B, 0x7765, 0x777F, 0x777E, 0x7779, - 0x778E, 0x778B, 0x7791, 0x77A0, 0x779E, 0x77B0, 0x77B6, 0x77B9, 0x77BF, 0x77BC, 0x77BD, 0x77BB, 0x77C7, 0x77CD, 0x77D7, 0x77DA, - 0x77DC, 0x77E3, 0x77EE, 0x77FC, 0x780C, 0x7812, 0x7926, 0x7820, 0x792A, 0x7845, 0x788E, 0x7874, 0x7886, 0x787C, 0x789A, 0x788C, - 0x78A3, 0x78B5, 0x78AA, 0x78AF, 0x78D1, 0x78C6, 0x78CB, 0x78D4, 0x78BE, 0x78BC, 0x78C5, 0x78CA, 0x78EC, 0xFFFF, 0xFFFF, 0xFFFF, - // 0XE200 to 0XE2FF - 0x78E7, 0x78DA, 0x78FD, 0x78F4, 0x7907, 0x7912, 0x7911, 0x7919, 0x792C, 0x792B, 0x7940, 0x7960, 0x7957, 0x795F, 0x795A, 0x7955, - 0x7953, 0x797A, 0x797F, 0x798A, 0x799D, 0x79A7, 0x9F4B, 0x79AA, 0x79AE, 0x79B3, 0x79B9, 0x79BA, 0x79C9, 0x79D5, 0x79E7, 0x79EC, - 0x79E1, 0x79E3, 0x7A08, 0x7A0D, 0x7A18, 0x7A19, 0x7A20, 0x7A1F, 0x7980, 0x7A31, 0x7A3B, 0x7A3E, 0x7A37, 0x7A43, 0x7A57, 0x7A49, - 0x7A61, 0x7A62, 0x7A69, 0x9F9D, 0x7A70, 0x7A79, 0x7A7D, 0x7A88, 0x7A97, 0x7A95, 0x7A98, 0x7A96, 0x7AA9, 0x7AC8, 0x7AB0, 0xFFFF, - 0x7AB6, 0x7AC5, 0x7AC4, 0x7ABF, 0x9083, 0x7AC7, 0x7ACA, 0x7ACD, 0x7ACF, 0x7AD5, 0x7AD3, 0x7AD9, 0x7ADA, 0x7ADD, 0x7AE1, 0x7AE2, - 0x7AE6, 0x7AED, 0x7AF0, 0x7B02, 0x7B0F, 0x7B0A, 0x7B06, 0x7B33, 0x7B18, 0x7B19, 0x7B1E, 0x7B35, 0x7B28, 0x7B36, 0x7B50, 0x7B7A, - 0x7B04, 0x7B4D, 0x7B0B, 0x7B4C, 0x7B45, 0x7B75, 0x7B65, 0x7B74, 0x7B67, 0x7B70, 0x7B71, 0x7B6C, 0x7B6E, 0x7B9D, 0x7B98, 0x7B9F, - 0x7B8D, 0x7B9C, 0x7B9A, 0x7B8B, 0x7B92, 0x7B8F, 0x7B5D, 0x7B99, 0x7BCB, 0x7BC1, 0x7BCC, 0x7BCF, 0x7BB4, 0x7BC6, 0x7BDD, 0x7BE9, - 0x7C11, 0x7C14, 0x7BE6, 0x7BE5, 0x7C60, 0x7C00, 0x7C07, 0x7C13, 0x7BF3, 0x7BF7, 0x7C17, 0x7C0D, 0x7BF6, 0x7C23, 0x7C27, 0x7C2A, - 0x7C1F, 0x7C37, 0x7C2B, 0x7C3D, 0x7C4C, 0x7C43, 0x7C54, 0x7C4F, 0x7C40, 0x7C50, 0x7C58, 0x7C5F, 0x7C64, 0x7C56, 0x7C65, 0x7C6C, - 0x7C75, 0x7C83, 0x7C90, 0x7CA4, 0x7CAD, 0x7CA2, 0x7CAB, 0x7CA1, 0x7CA8, 0x7CB3, 0x7CB2, 0x7CB1, 0x7CAE, 0x7CB9, 0x7CBD, 0x7CC0, - 0x7CC5, 0x7CC2, 0x7CD8, 0x7CD2, 0x7CDC, 0x7CE2, 0x9B3B, 0x7CEF, 0x7CF2, 0x7CF4, 0x7CF6, 0x7CFA, 0x7D06, 0xFFFF, 0xFFFF, 0xFFFF, - // 0XE300 to 0XE3FF - 0x7D02, 0x7D1C, 0x7D15, 0x7D0A, 0x7D45, 0x7D4B, 0x7D2E, 0x7D32, 0x7D3F, 0x7D35, 0x7D46, 0x7D73, 0x7D56, 0x7D4E, 0x7D72, 0x7D68, - 0x7D6E, 0x7D4F, 0x7D63, 0x7D93, 0x7D89, 0x7D5B, 0x7D8F, 0x7D7D, 0x7D9B, 0x7DBA, 0x7DAE, 0x7DA3, 0x7DB5, 0x7DC7, 0x7DBD, 0x7DAB, - 0x7E3D, 0x7DA2, 0x7DAF, 0x7DDC, 0x7DB8, 0x7D9F, 0x7DB0, 0x7DD8, 0x7DDD, 0x7DE4, 0x7DDE, 0x7DFB, 0x7DF2, 0x7DE1, 0x7E05, 0x7E0A, - 0x7E23, 0x7E21, 0x7E12, 0x7E31, 0x7E1F, 0x7E09, 0x7E0B, 0x7E22, 0x7E46, 0x7E66, 0x7E3B, 0x7E35, 0x7E39, 0x7E43, 0x7E37, 0xFFFF, - 0x7E32, 0x7E3A, 0x7E67, 0x7E5D, 0x7E56, 0x7E5E, 0x7E59, 0x7E5A, 0x7E79, 0x7E6A, 0x7E69, 0x7E7C, 0x7E7B, 0x7E83, 0x7DD5, 0x7E7D, - 0x8FAE, 0x7E7F, 0x7E88, 0x7E89, 0x7E8C, 0x7E92, 0x7E90, 0x7E93, 0x7E94, 0x7E96, 0x7E8E, 0x7E9B, 0x7E9C, 0x7F38, 0x7F3A, 0x7F45, - 0x7F4C, 0x7F4D, 0x7F4E, 0x7F50, 0x7F51, 0x7F55, 0x7F54, 0x7F58, 0x7F5F, 0x7F60, 0x7F68, 0x7F69, 0x7F67, 0x7F78, 0x7F82, 0x7F86, - 0x7F83, 0x7F88, 0x7F87, 0x7F8C, 0x7F94, 0x7F9E, 0x7F9D, 0x7F9A, 0x7FA3, 0x7FAF, 0x7FB2, 0x7FB9, 0x7FAE, 0x7FB6, 0x7FB8, 0x8B71, - 0x7FC5, 0x7FC6, 0x7FCA, 0x7FD5, 0x7FD4, 0x7FE1, 0x7FE6, 0x7FE9, 0x7FF3, 0x7FF9, 0x98DC, 0x8006, 0x8004, 0x800B, 0x8012, 0x8018, - 0x8019, 0x801C, 0x8021, 0x8028, 0x803F, 0x803B, 0x804A, 0x8046, 0x8052, 0x8058, 0x805A, 0x805F, 0x8062, 0x8068, 0x8073, 0x8072, - 0x8070, 0x8076, 0x8079, 0x807D, 0x807F, 0x8084, 0x8086, 0x8085, 0x809B, 0x8093, 0x809A, 0x80AD, 0x5190, 0x80AC, 0x80DB, 0x80E5, - 0x80D9, 0x80DD, 0x80C4, 0x80DA, 0x80D6, 0x8109, 0x80EF, 0x80F1, 0x811B, 0x8129, 0x8123, 0x812F, 0x814B, 0xFFFF, 0xFFFF, 0xFFFF, - // 0XE400 to 0XE4FF - 0x968B, 0x8146, 0x813E, 0x8153, 0x8151, 0x80FC, 0x8171, 0x816E, 0x8165, 0x8166, 0x8174, 0x8183, 0x8188, 0x818A, 0x8180, 0x8182, - 0x81A0, 0x8195, 0x81A4, 0x81A3, 0x815F, 0x8193, 0x81A9, 0x81B0, 0x81B5, 0x81BE, 0x81B8, 0x81BD, 0x81C0, 0x81C2, 0x81BA, 0x81C9, - 0x81CD, 0x81D1, 0x81D9, 0x81D8, 0x81C8, 0x81DA, 0x81DF, 0x81E0, 0x81E7, 0x81FA, 0x81FB, 0x81FE, 0x8201, 0x8202, 0x8205, 0x8207, - 0x820A, 0x820D, 0x8210, 0x8216, 0x8229, 0x822B, 0x8238, 0x8233, 0x8240, 0x8259, 0x8258, 0x825D, 0x825A, 0x825F, 0x8264, 0xFFFF, - 0x8262, 0x8268, 0x826A, 0x826B, 0x822E, 0x8271, 0x8277, 0x8278, 0x827E, 0x828D, 0x8292, 0x82AB, 0x829F, 0x82BB, 0x82AC, 0x82E1, - 0x82E3, 0x82DF, 0x82D2, 0x82F4, 0x82F3, 0x82FA, 0x8393, 0x8303, 0x82FB, 0x82F9, 0x82DE, 0x8306, 0x82DC, 0x8309, 0x82D9, 0x8335, - 0x8334, 0x8316, 0x8332, 0x8331, 0x8340, 0x8339, 0x8350, 0x8345, 0x832F, 0x832B, 0x8317, 0x8318, 0x8385, 0x839A, 0x83AA, 0x839F, - 0x83A2, 0x8396, 0x8323, 0x838E, 0x8387, 0x838A, 0x837C, 0x83B5, 0x8373, 0x8375, 0x83A0, 0x8389, 0x83A8, 0x83F4, 0x8413, 0x83EB, - 0x83CE, 0x83FD, 0x8403, 0x83D8, 0x840B, 0x83C1, 0x83F7, 0x8407, 0x83E0, 0x83F2, 0x840D, 0x8422, 0x8420, 0x83BD, 0x8438, 0x8506, - 0x83FB, 0x846D, 0x842A, 0x843C, 0x855A, 0x8484, 0x8477, 0x846B, 0x84AD, 0x846E, 0x8482, 0x8469, 0x8446, 0x842C, 0x846F, 0x8479, - 0x8435, 0x84CA, 0x8462, 0x84B9, 0x84BF, 0x849F, 0x84D9, 0x84CD, 0x84BB, 0x84DA, 0x84D0, 0x84C1, 0x84C6, 0x84D6, 0x84A1, 0x8521, - 0x84FF, 0x84F4, 0x8517, 0x8518, 0x852C, 0x851F, 0x8515, 0x8514, 0x84FC, 0x8540, 0x8563, 0x8558, 0x8548, 0xFFFF, 0xFFFF, 0xFFFF, - // 0XE500 to 0XE5FF - 0x8541, 0x8602, 0x854B, 0x8555, 0x8580, 0x85A4, 0x8588, 0x8591, 0x858A, 0x85A8, 0x856D, 0x8594, 0x859B, 0x85EA, 0x8587, 0x859C, - 0x8577, 0x857E, 0x8590, 0x85C9, 0x85BA, 0x85CF, 0x85B9, 0x85D0, 0x85D5, 0x85DD, 0x85E5, 0x85DC, 0x85F9, 0x860A, 0x8613, 0x860B, - 0x85FE, 0x85FA, 0x8606, 0x8622, 0x861A, 0x8630, 0x863F, 0x864D, 0x4E55, 0x8654, 0x865F, 0x8667, 0x8671, 0x8693, 0x86A3, 0x86A9, - 0x86AA, 0x868B, 0x868C, 0x86B6, 0x86AF, 0x86C4, 0x86C6, 0x86B0, 0x86C9, 0x8823, 0x86AB, 0x86D4, 0x86DE, 0x86E9, 0x86EC, 0xFFFF, - 0x86DF, 0x86DB, 0x86EF, 0x8712, 0x8706, 0x8708, 0x8700, 0x8703, 0x86FB, 0x8711, 0x8709, 0x870D, 0x86F9, 0x870A, 0x8734, 0x873F, - 0x8737, 0x873B, 0x8725, 0x8729, 0x871A, 0x8760, 0x875F, 0x8778, 0x874C, 0x874E, 0x8774, 0x8757, 0x8768, 0x876E, 0x8759, 0x8753, - 0x8763, 0x876A, 0x8805, 0x87A2, 0x879F, 0x8782, 0x87AF, 0x87CB, 0x87BD, 0x87C0, 0x87D0, 0x96D6, 0x87AB, 0x87C4, 0x87B3, 0x87C7, - 0x87C6, 0x87BB, 0x87EF, 0x87F2, 0x87E0, 0x880F, 0x880D, 0x87FE, 0x87F6, 0x87F7, 0x880E, 0x87D2, 0x8811, 0x8816, 0x8815, 0x8822, - 0x8821, 0x8831, 0x8836, 0x8839, 0x8827, 0x883B, 0x8844, 0x8842, 0x8852, 0x8859, 0x885E, 0x8862, 0x886B, 0x8881, 0x887E, 0x889E, - 0x8875, 0x887D, 0x88B5, 0x8872, 0x8882, 0x8897, 0x8892, 0x88AE, 0x8899, 0x88A2, 0x888D, 0x88A4, 0x88B0, 0x88BF, 0x88B1, 0x88C3, - 0x88C4, 0x88D4, 0x88D8, 0x88D9, 0x88DD, 0x88F9, 0x8902, 0x88FC, 0x88F4, 0x88E8, 0x88F2, 0x8904, 0x890C, 0x890A, 0x8913, 0x8943, - 0x891E, 0x8925, 0x892A, 0x892B, 0x8941, 0x8944, 0x893B, 0x8936, 0x8938, 0x894C, 0x891D, 0x8960, 0x895E, 0xFFFF, 0xFFFF, 0xFFFF, - // 0XE600 to 0XE6FF - 0x8966, 0x8964, 0x896D, 0x896A, 0x896F, 0x8974, 0x8977, 0x897E, 0x8983, 0x8988, 0x898A, 0x8993, 0x8998, 0x89A1, 0x89A9, 0x89A6, - 0x89AC, 0x89AF, 0x89B2, 0x89BA, 0x89BD, 0x89BF, 0x89C0, 0x89DA, 0x89DC, 0x89DD, 0x89E7, 0x89F4, 0x89F8, 0x8A03, 0x8A16, 0x8A10, - 0x8A0C, 0x8A1B, 0x8A1D, 0x8A25, 0x8A36, 0x8A41, 0x8A5B, 0x8A52, 0x8A46, 0x8A48, 0x8A7C, 0x8A6D, 0x8A6C, 0x8A62, 0x8A85, 0x8A82, - 0x8A84, 0x8AA8, 0x8AA1, 0x8A91, 0x8AA5, 0x8AA6, 0x8A9A, 0x8AA3, 0x8AC4, 0x8ACD, 0x8AC2, 0x8ADA, 0x8AEB, 0x8AF3, 0x8AE7, 0xFFFF, - 0x8AE4, 0x8AF1, 0x8B14, 0x8AE0, 0x8AE2, 0x8AF7, 0x8ADE, 0x8ADB, 0x8B0C, 0x8B07, 0x8B1A, 0x8AE1, 0x8B16, 0x8B10, 0x8B17, 0x8B20, - 0x8B33, 0x97AB, 0x8B26, 0x8B2B, 0x8B3E, 0x8B28, 0x8B41, 0x8B4C, 0x8B4F, 0x8B4E, 0x8B49, 0x8B56, 0x8B5B, 0x8B5A, 0x8B6B, 0x8B5F, - 0x8B6C, 0x8B6F, 0x8B74, 0x8B7D, 0x8B80, 0x8B8C, 0x8B8E, 0x8B92, 0x8B93, 0x8B96, 0x8B99, 0x8B9A, 0x8C3A, 0x8C41, 0x8C3F, 0x8C48, - 0x8C4C, 0x8C4E, 0x8C50, 0x8C55, 0x8C62, 0x8C6C, 0x8C78, 0x8C7A, 0x8C82, 0x8C89, 0x8C85, 0x8C8A, 0x8C8D, 0x8C8E, 0x8C94, 0x8C7C, - 0x8C98, 0x621D, 0x8CAD, 0x8CAA, 0x8CBD, 0x8CB2, 0x8CB3, 0x8CAE, 0x8CB6, 0x8CC8, 0x8CC1, 0x8CE4, 0x8CE3, 0x8CDA, 0x8CFD, 0x8CFA, - 0x8CFB, 0x8D04, 0x8D05, 0x8D0A, 0x8D07, 0x8D0F, 0x8D0D, 0x8D10, 0x9F4E, 0x8D13, 0x8CCD, 0x8D14, 0x8D16, 0x8D67, 0x8D6D, 0x8D71, - 0x8D73, 0x8D81, 0x8D99, 0x8DC2, 0x8DBE, 0x8DBA, 0x8DCF, 0x8DDA, 0x8DD6, 0x8DCC, 0x8DDB, 0x8DCB, 0x8DEA, 0x8DEB, 0x8DDF, 0x8DE3, - 0x8DFC, 0x8E08, 0x8E09, 0x8DFF, 0x8E1D, 0x8E1E, 0x8E10, 0x8E1F, 0x8E42, 0x8E35, 0x8E30, 0x8E34, 0x8E4A, 0xFFFF, 0xFFFF, 0xFFFF, - // 0XE700 to 0XE7FF - 0x8E47, 0x8E49, 0x8E4C, 0x8E50, 0x8E48, 0x8E59, 0x8E64, 0x8E60, 0x8E2A, 0x8E63, 0x8E55, 0x8E76, 0x8E72, 0x8E7C, 0x8E81, 0x8E87, - 0x8E85, 0x8E84, 0x8E8B, 0x8E8A, 0x8E93, 0x8E91, 0x8E94, 0x8E99, 0x8EAA, 0x8EA1, 0x8EAC, 0x8EB0, 0x8EC6, 0x8EB1, 0x8EBE, 0x8EC5, - 0x8EC8, 0x8ECB, 0x8EDB, 0x8EE3, 0x8EFC, 0x8EFB, 0x8EEB, 0x8EFE, 0x8F0A, 0x8F05, 0x8F15, 0x8F12, 0x8F19, 0x8F13, 0x8F1C, 0x8F1F, - 0x8F1B, 0x8F0C, 0x8F26, 0x8F33, 0x8F3B, 0x8F39, 0x8F45, 0x8F42, 0x8F3E, 0x8F4C, 0x8F49, 0x8F46, 0x8F4E, 0x8F57, 0x8F5C, 0xFFFF, - 0x8F62, 0x8F63, 0x8F64, 0x8F9C, 0x8F9F, 0x8FA3, 0x8FAD, 0x8FAF, 0x8FB7, 0x8FDA, 0x8FE5, 0x8FE2, 0x8FEA, 0x8FEF, 0x9087, 0x8FF4, - 0x9005, 0x8FF9, 0x8FFA, 0x9011, 0x9015, 0x9021, 0x900D, 0x901E, 0x9016, 0x900B, 0x9027, 0x9036, 0x9035, 0x9039, 0x8FF8, 0x904F, - 0x9050, 0x9051, 0x9052, 0x900E, 0x9049, 0x903E, 0x9056, 0x9058, 0x905E, 0x9068, 0x906F, 0x9076, 0x96A8, 0x9072, 0x9082, 0x907D, - 0x9081, 0x9080, 0x908A, 0x9089, 0x908F, 0x90A8, 0x90AF, 0x90B1, 0x90B5, 0x90E2, 0x90E4, 0x6248, 0x90DB, 0x9102, 0x9112, 0x9119, - 0x9132, 0x9130, 0x914A, 0x9156, 0x9158, 0x9163, 0x9165, 0x9169, 0x9173, 0x9172, 0x918B, 0x9189, 0x9182, 0x91A2, 0x91AB, 0x91AF, - 0x91AA, 0x91B5, 0x91B4, 0x91BA, 0x91C0, 0x91C1, 0x91C9, 0x91CB, 0x91D0, 0x91D6, 0x91DF, 0x91E1, 0x91DB, 0x91FC, 0x91F5, 0x91F6, - 0x921E, 0x91FF, 0x9214, 0x922C, 0x9215, 0x9211, 0x925E, 0x9257, 0x9245, 0x9249, 0x9264, 0x9248, 0x9295, 0x923F, 0x924B, 0x9250, - 0x929C, 0x9296, 0x9293, 0x929B, 0x925A, 0x92CF, 0x92B9, 0x92B7, 0x92E9, 0x930F, 0x92FA, 0x9344, 0x932E, 0xFFFF, 0xFFFF, 0xFFFF, - // 0XE800 to 0XE8FF - 0x9319, 0x9322, 0x931A, 0x9323, 0x933A, 0x9335, 0x933B, 0x935C, 0x9360, 0x937C, 0x936E, 0x9356, 0x93B0, 0x93AC, 0x93AD, 0x9394, - 0x93B9, 0x93D6, 0x93D7, 0x93E8, 0x93E5, 0x93D8, 0x93C3, 0x93DD, 0x93D0, 0x93C8, 0x93E4, 0x941A, 0x9414, 0x9413, 0x9403, 0x9407, - 0x9410, 0x9436, 0x942B, 0x9435, 0x9421, 0x943A, 0x9441, 0x9452, 0x9444, 0x945B, 0x9460, 0x9462, 0x945E, 0x946A, 0x9229, 0x9470, - 0x9475, 0x9477, 0x947D, 0x945A, 0x947C, 0x947E, 0x9481, 0x947F, 0x9582, 0x9587, 0x958A, 0x9594, 0x9596, 0x9598, 0x9599, 0xFFFF, - 0x95A0, 0x95A8, 0x95A7, 0x95AD, 0x95BC, 0x95BB, 0x95B9, 0x95BE, 0x95CA, 0x6FF6, 0x95C3, 0x95CD, 0x95CC, 0x95D5, 0x95D4, 0x95D6, - 0x95DC, 0x95E1, 0x95E5, 0x95E2, 0x9621, 0x9628, 0x962E, 0x962F, 0x9642, 0x964C, 0x964F, 0x964B, 0x9677, 0x965C, 0x965E, 0x965D, - 0x965F, 0x9666, 0x9672, 0x966C, 0x968D, 0x9698, 0x9695, 0x9697, 0x96AA, 0x96A7, 0x96B1, 0x96B2, 0x96B0, 0x96B4, 0x96B6, 0x96B8, - 0x96B9, 0x96CE, 0x96CB, 0x96C9, 0x96CD, 0x894D, 0x96DC, 0x970D, 0x96D5, 0x96F9, 0x9704, 0x9706, 0x9708, 0x9713, 0x970E, 0x9711, - 0x970F, 0x9716, 0x9719, 0x9724, 0x972A, 0x9730, 0x9739, 0x973D, 0x973E, 0x9744, 0x9746, 0x9748, 0x9742, 0x9749, 0x975C, 0x9760, - 0x9764, 0x9766, 0x9768, 0x52D2, 0x976B, 0x9771, 0x9779, 0x9785, 0x977C, 0x9781, 0x977A, 0x9786, 0x978B, 0x978F, 0x9790, 0x979C, - 0x97A8, 0x97A6, 0x97A3, 0x97B3, 0x97B4, 0x97C3, 0x97C6, 0x97C8, 0x97CB, 0x97DC, 0x97ED, 0x9F4F, 0x97F2, 0x7ADF, 0x97F6, 0x97F5, - 0x980F, 0x980C, 0x9838, 0x9824, 0x9821, 0x9837, 0x983D, 0x9846, 0x984F, 0x984B, 0x986B, 0x986F, 0x9870, 0xFFFF, 0xFFFF, 0xFFFF, - // 0XE900 to 0XE9FF - 0x9871, 0x9874, 0x9873, 0x98AA, 0x98AF, 0x98B1, 0x98B6, 0x98C4, 0x98C3, 0x98C6, 0x98E9, 0x98EB, 0x9903, 0x9909, 0x9912, 0x9914, - 0x9918, 0x9921, 0x991D, 0x991E, 0x9924, 0x9920, 0x992C, 0x992E, 0x993D, 0x993E, 0x9942, 0x9949, 0x9945, 0x9950, 0x994B, 0x9951, - 0x9952, 0x994C, 0x9955, 0x9997, 0x9998, 0x99A5, 0x99AD, 0x99AE, 0x99BC, 0x99DF, 0x99DB, 0x99DD, 0x99D8, 0x99D1, 0x99ED, 0x99EE, - 0x99F1, 0x99F2, 0x99FB, 0x99F8, 0x9A01, 0x9A0F, 0x9A05, 0x99E2, 0x9A19, 0x9A2B, 0x9A37, 0x9A45, 0x9A42, 0x9A40, 0x9A43, 0xFFFF, - 0x9A3E, 0x9A55, 0x9A4D, 0x9A5B, 0x9A57, 0x9A5F, 0x9A62, 0x9A65, 0x9A64, 0x9A69, 0x9A6B, 0x9A6A, 0x9AAD, 0x9AB0, 0x9ABC, 0x9AC0, - 0x9ACF, 0x9AD1, 0x9AD3, 0x9AD4, 0x9ADE, 0x9ADF, 0x9AE2, 0x9AE3, 0x9AE6, 0x9AEF, 0x9AEB, 0x9AEE, 0x9AF4, 0x9AF1, 0x9AF7, 0x9AFB, - 0x9B06, 0x9B18, 0x9B1A, 0x9B1F, 0x9B22, 0x9B23, 0x9B25, 0x9B27, 0x9B28, 0x9B29, 0x9B2A, 0x9B2E, 0x9B2F, 0x9B32, 0x9B44, 0x9B43, - 0x9B4F, 0x9B4D, 0x9B4E, 0x9B51, 0x9B58, 0x9B74, 0x9B93, 0x9B83, 0x9B91, 0x9B96, 0x9B97, 0x9B9F, 0x9BA0, 0x9BA8, 0x9BB4, 0x9BC0, - 0x9BCA, 0x9BB9, 0x9BC6, 0x9BCF, 0x9BD1, 0x9BD2, 0x9BE3, 0x9BE2, 0x9BE4, 0x9BD4, 0x9BE1, 0x9C3A, 0x9BF2, 0x9BF1, 0x9BF0, 0x9C15, - 0x9C14, 0x9C09, 0x9C13, 0x9C0C, 0x9C06, 0x9C08, 0x9C12, 0x9C0A, 0x9C04, 0x9C2E, 0x9C1B, 0x9C25, 0x9C24, 0x9C21, 0x9C30, 0x9C47, - 0x9C32, 0x9C46, 0x9C3E, 0x9C5A, 0x9C60, 0x9C67, 0x9C76, 0x9C78, 0x9CE7, 0x9CEC, 0x9CF0, 0x9D09, 0x9D08, 0x9CEB, 0x9D03, 0x9D06, - 0x9D2A, 0x9D26, 0x9DAF, 0x9D23, 0x9D1F, 0x9D44, 0x9D15, 0x9D12, 0x9D41, 0x9D3F, 0x9D3E, 0x9D46, 0x9D48, 0xFFFF, 0xFFFF, 0xFFFF, - // 0XEA00 to 0XEAFF - 0x9D5D, 0x9D5E, 0x9D64, 0x9D51, 0x9D50, 0x9D59, 0x9D72, 0x9D89, 0x9D87, 0x9DAB, 0x9D6F, 0x9D7A, 0x9D9A, 0x9DA4, 0x9DA9, 0x9DB2, - 0x9DC4, 0x9DC1, 0x9DBB, 0x9DB8, 0x9DBA, 0x9DC6, 0x9DCF, 0x9DC2, 0x9DD9, 0x9DD3, 0x9DF8, 0x9DE6, 0x9DED, 0x9DEF, 0x9DFD, 0x9E1A, - 0x9E1B, 0x9E1E, 0x9E75, 0x9E79, 0x9E7D, 0x9E81, 0x9E88, 0x9E8B, 0x9E8C, 0x9E92, 0x9E95, 0x9E91, 0x9E9D, 0x9EA5, 0x9EA9, 0x9EB8, - 0x9EAA, 0x9EAD, 0x9761, 0x9ECC, 0x9ECE, 0x9ECF, 0x9ED0, 0x9ED4, 0x9EDC, 0x9EDE, 0x9EDD, 0x9EE0, 0x9EE5, 0x9EE8, 0x9EEF, 0xFFFF, - 0x9EF4, 0x9EF6, 0x9EF7, 0x9EF9, 0x9EFB, 0x9EFC, 0x9EFD, 0x9F07, 0x9F08, 0x76B7, 0x9F15, 0x9F21, 0x9F2C, 0x9F3E, 0x9F4A, 0x9F52, - 0x9F54, 0x9F63, 0x9F5F, 0x9F60, 0x9F61, 0x9F66, 0x9F67, 0x9F6C, 0x9F6A, 0x9F77, 0x9F72, 0x9F76, 0x9F95, 0x9F9C, 0x9FA0, 0x582F, - 0x69C7, 0x9059, 0x7464, 0x51DC, 0x7199, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, -}; + const std::wstring* fileName; + int64_t pos; -const wchar_t SjisToUnicodeTable5[] = + GET_PARAM(parameters,0,fileName); + GET_OPTIONAL_PARAM(parameters,1,pos,0); + + std::wstring fullName = getFullPathName(*fileName); + + BinaryFile file; + if (file.open(fullName,BinaryFile::Read) == false) + { + Logger::queueError(Logger::Error,L"Could not open %s",*fileName); + return ExpressionValue(); + } + + file.setPos(pos); + + T buffer; + if (file.read(&buffer, sizeof(T)) != sizeof(T)) + { + Logger::queueError(Logger::Error, L"Failed to read %d byte(s) from offset 0x%08X of %s", sizeof(T), pos, *fileName); + return ExpressionValue(); + } + + return ExpressionValue((int64_t) buffer); +} + +ExpressionValue expFuncReadAscii(const std::wstring& funcName, const std::vector<ExpressionValue>& parameters) { - // 0XED00 to 0XEDFF - 0x7E8A, 0x891C, 0x9348, 0x9288, 0x84DC, 0x4FC9, 0x70BB, 0x6631, 0x68C8, 0x92F9, 0x66FB, 0x5F45, 0x4E28, 0x4EE1, 0x4EFC, 0x4F00, - 0x4F03, 0x4F39, 0x4F56, 0x4F92, 0x4F8A, 0x4F9A, 0x4F94, 0x4FCD, 0x5040, 0x5022, 0x4FFF, 0x501E, 0x5046, 0x5070, 0x5042, 0x5094, - 0x50F4, 0x50D8, 0x514A, 0x5164, 0x519D, 0x51BE, 0x51EC, 0x5215, 0x529C, 0x52A6, 0x52C0, 0x52DB, 0x5300, 0x5307, 0x5324, 0x5372, - 0x5393, 0x53B2, 0x53DD, 0xFA0E, 0x549C, 0x548A, 0x54A9, 0x54FF, 0x5586, 0x5759, 0x5765, 0x57AC, 0x57C8, 0x57C7, 0xFA0F, 0xFFFF, - 0xFA10, 0x589E, 0x58B2, 0x590B, 0x5953, 0x595B, 0x595D, 0x5963, 0x59A4, 0x59BA, 0x5B56, 0x5BC0, 0x752F, 0x5BD8, 0x5BEC, 0x5C1E, - 0x5CA6, 0x5CBA, 0x5CF5, 0x5D27, 0x5D53, 0xFA11, 0x5D42, 0x5D6D, 0x5DB8, 0x5DB9, 0x5DD0, 0x5F21, 0x5F34, 0x5F67, 0x5FB7, 0x5FDE, - 0x605D, 0x6085, 0x608A, 0x60DE, 0x60D5, 0x6120, 0x60F2, 0x6111, 0x6137, 0x6130, 0x6198, 0x6213, 0x62A6, 0x63F5, 0x6460, 0x649D, - 0x64CE, 0x654E, 0x6600, 0x6615, 0x663B, 0x6609, 0x662E, 0x661E, 0x6624, 0x6665, 0x6657, 0x6659, 0xFA12, 0x6673, 0x6699, 0x66A0, - 0x66B2, 0x66BF, 0x66FA, 0x670E, 0xF929, 0x6766, 0x67BB, 0x6852, 0x67C0, 0x6801, 0x6844, 0x68CF, 0xFA13, 0x6968, 0xFA14, 0x6998, - 0x69E2, 0x6A30, 0x6A6B, 0x6A46, 0x6A73, 0x6A7E, 0x6AE2, 0x6AE4, 0x6BD6, 0x6C3F, 0x6C5C, 0x6C86, 0x6C6F, 0x6CDA, 0x6D04, 0x6D87, - 0x6D6F, 0x6D96, 0x6DAC, 0x6DCF, 0x6DF8, 0x6DF2, 0x6DFC, 0x6E39, 0x6E5C, 0x6E27, 0x6E3C, 0x6EBF, 0x6F88, 0x6FB5, 0x6FF5, 0x7005, - 0x7007, 0x7028, 0x7085, 0x70AB, 0x710F, 0x7104, 0x715C, 0x7146, 0x7147, 0xFA15, 0x71C1, 0x71FE, 0x72B1, 0xFFFF, 0xFFFF, 0xFFFF, - // 0XEE00 to 0XEEFF - 0x72BE, 0x7324, 0xFA16, 0x7377, 0x73BD, 0x73C9, 0x73D6, 0x73E3, 0x73D2, 0x7407, 0x73F5, 0x7426, 0x742A, 0x7429, 0x742E, 0x7462, - 0x7489, 0x749F, 0x7501, 0x756F, 0x7682, 0x769C, 0x769E, 0x769B, 0x76A6, 0xFA17, 0x7746, 0x52AF, 0x7821, 0x784E, 0x7864, 0x787A, - 0x7930, 0xFA18, 0xFA19, 0xFA1A, 0x7994, 0xFA1B, 0x799B, 0x7AD1, 0x7AE7, 0xFA1C, 0x7AEB, 0x7B9E, 0xFA1D, 0x7D48, 0x7D5C, 0x7DB7, - 0x7DA0, 0x7DD6, 0x7E52, 0x7F47, 0x7FA1, 0xFA1E, 0x8301, 0x8362, 0x837F, 0x83C7, 0x83F6, 0x8448, 0x84B4, 0x8553, 0x8559, 0xFFFF, - 0x856B, 0xFA1F, 0x85B0, 0xFA20, 0xFA21, 0x8807, 0x88F5, 0x8A12, 0x8A37, 0x8A79, 0x8AA7, 0x8ABE, 0x8ADF, 0xFA22, 0x8AF6, 0x8B53, - 0x8B7F, 0x8CF0, 0x8CF4, 0x8D12, 0x8D76, 0xFA23, 0x8ECF, 0xFA24, 0xFA25, 0x9067, 0x90DE, 0xFA26, 0x9115, 0x9127, 0x91DA, 0x91D7, - 0x91DE, 0x91ED, 0x91EE, 0x91E4, 0x91E5, 0x9206, 0x9210, 0x920A, 0x923A, 0x9240, 0x923C, 0x924E, 0x9259, 0x9251, 0x9239, 0x9267, - 0x92A7, 0x9277, 0x9278, 0x92E7, 0x92D7, 0x92D9, 0x92D0, 0xFA27, 0x92D5, 0x92E0, 0x92D3, 0x9325, 0x9321, 0x92FB, 0xFA28, 0x931E, - 0x92FF, 0x931D, 0x9302, 0x9370, 0x9357, 0x93A4, 0x93C6, 0x93DE, 0x93F8, 0x9431, 0x9445, 0x9448, 0x9592, 0xF9DC, 0xFA29, 0x969D, - 0x96AF, 0x9733, 0x973B, 0x9743, 0x974D, 0x974F, 0x9751, 0x9755, 0x9857, 0x9865, 0xFA2A, 0xFA2B, 0x9927, 0xFA2C, 0x999E, 0x9A4E, - 0x9AD9, 0x9ADC, 0x9B75, 0x9B72, 0x9B8F, 0x9BB1, 0x9BBB, 0x9C00, 0x9D70, 0x9D6B, 0xFA2D, 0x9E19, 0x9ED1, 0xFFFF, 0xFFFF, 0x2170, - 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0xFFE2, 0xFFE4, 0xFF07, 0xFF02, 0xFFFF, 0xFFFF, 0xFFFF, -}; + const std::wstring* fileName; + int64_t start; + int64_t length; -wchar_t sjisToUnicode(unsigned short SjisCharacter) + GET_PARAM(parameters,0,fileName); + GET_OPTIONAL_PARAM(parameters,1,start,0); + GET_OPTIONAL_PARAM(parameters,2,length,0); + + std::wstring fullName = getFullPathName(*fileName); + + int64_t totalSize = fileSize(fullName); + if (length == 0 || start+length > totalSize) + length = totalSize-start; + + BinaryFile file; + if (file.open(fullName,BinaryFile::Read) == false) + { + Logger::queueError(Logger::Error,L"Could not open %s",fileName); + return ExpressionValue(); + } + + file.setPos((long)start); + + unsigned char* buffer = new unsigned char[length]; + file.read(buffer,(size_t)length); + + std::wstring result; + for (size_t i = 0; i < (size_t) length; i++) + { + if (buffer[i] < 0x20 || buffer[i] > 0x7F) + { + Logger::printError(Logger::Warning,L"%s: Non-ASCII character",funcName); + return ExpressionValue(); + } + + result += (wchar_t) buffer[i]; + } + + delete[] buffer; + + return ExpressionValue(result); +} + +ExpressionValue expLabelFuncDefined(const std::wstring &funcName, const std::vector<std::shared_ptr<Label>> &parameters) { - if (SjisCharacter < 0x80) + if (parameters.empty() || !parameters.front()) { - return SjisCharacter; - } else if (SjisCharacter < 0x100) + Logger::queueError(Logger::Error,L"%s: Invalid parameters", funcName); + return ExpressionValue(); + } + + return ExpressionValue(parameters.front()->isDefined() ? INT64_C(1) : INT64_C(0)); +} + +ExpressionValue expLabelFuncOrg(const std::wstring& funcName, const std::vector<std::shared_ptr<Label>>& parameters) +{ + // return physical address of label parameter + if (parameters.size()) { - return SjisToUnicodeTable1[SjisCharacter-0x80]; + Label* label = parameters.front().get(); + if (!label) + return ExpressionValue(); + + return ExpressionValue(parameters.front()->getValue()); } - if ((SjisCharacter & 0xFF) < 0x40) return 0xFFFF; + if(!g_fileManager->hasOpenFile()) + { + Logger::queueError(Logger::Error,L"%s: no file opened", funcName); + return ExpressionValue(); + } + return ExpressionValue(g_fileManager->getVirtualAddress()); +} - if (SjisCharacter >= 0x8100 && SjisCharacter < 0x8500) +ExpressionValue expLabelFuncOrga(const std::wstring& funcName, const std::vector<std::shared_ptr<Label>>& parameters) +{ + // return physical address of label parameter + if (parameters.size()) { - SjisCharacter -= 0x8140; - SjisCharacter -= (SjisCharacter >> 8) * 0x40; - return SjisToUnicodeTable2[SjisCharacter]; - } else if (SjisCharacter >= 0x8700 && SjisCharacter < 0xA000) + Label* label = parameters.front().get(); + if (!label) + return ExpressionValue(); + + if (!label->hasPhysicalValue()) + { + Logger::queueError(Logger::Error,L"%s: parameter %s has no physical address", funcName, label->getName() ); + return ExpressionValue(); + } + + return ExpressionValue(parameters.front()->getPhysicalValue()); + } + + // return current physical address otherwise + if(!g_fileManager->hasOpenFile()) { - SjisCharacter -= 0x8740; - SjisCharacter -= (SjisCharacter >> 8) * 0x40; - return SjisToUnicodeTable3[SjisCharacter]; - } else if (SjisCharacter >= 0xE000 && SjisCharacter < 0xEB00) + Logger::queueError(Logger::Error,L"%s: no file opened", funcName); + return ExpressionValue(); + } + return ExpressionValue(g_fileManager->getPhysicalAddress()); +} + +ExpressionValue expLabelFuncHeaderSize(const std::wstring& funcName, const std::vector<std::shared_ptr<Label>>& parameters) +{ + // return difference between physical and virtual address of label parameter + if (parameters.size()) { - SjisCharacter -= 0xE040; - SjisCharacter -= (SjisCharacter >> 8) * 0x40; - return SjisToUnicodeTable4[SjisCharacter]; - } else if (SjisCharacter >= 0xED00 && SjisCharacter < 0xEF00) + Label* label = parameters.front().get(); + if (!label) + return ExpressionValue(); + + if (!label->hasPhysicalValue()) + { + Logger::queueError(Logger::Error,L"%s: parameter %s has no physical address", funcName, label->getName() ); + return ExpressionValue(); + } + + return ExpressionValue(label->getValue() - label->getPhysicalValue()); + } + + if(!g_fileManager->hasOpenFile()) { - SjisCharacter -= 0xED40; - SjisCharacter -= (SjisCharacter >> 8) * 0x40; - return SjisToUnicodeTable5[SjisCharacter]; - } else { - return 0xFFFF; + Logger::queueError(Logger::Error,L"headersize: no file opened"); + return ExpressionValue(); } + return ExpressionValue(g_fileManager->getHeaderSize()); +} + +const ExpressionFunctionMap expressionFunctions = { + { L"version", { &expFuncVersion, 0, 0, ExpFuncSafety::Safe } }, + { L"endianness", { &expFuncEndianness, 0, 0, ExpFuncSafety::Unsafe } }, + { L"outputname", { &expFuncOutputName, 0, 0, ExpFuncSafety::Unsafe } }, + { L"fileexists", { &expFuncFileExists, 1, 1, ExpFuncSafety::Safe } }, + { L"filesize", { &expFuncFileSize, 1, 1, ExpFuncSafety::ConditionalUnsafe } }, + { L"tostring", { &expFuncToString, 1, 1, ExpFuncSafety::Safe } }, + { L"tohex", { &expFuncToHex, 1, 2, ExpFuncSafety::Safe } }, + + { L"int", { &expFuncInt, 1, 1, ExpFuncSafety::Safe } }, + { L"float", { &expFuncFloat, 1, 1, ExpFuncSafety::Safe } }, + { L"frac", { &expFuncFrac, 1, 1, ExpFuncSafety::Safe } }, + { L"abs", { &expFuncAbs, 1, 1, ExpFuncSafety::Safe } }, + { L"round", { &expFuncRound, 1, 1, ExpFuncSafety::Safe } }, + { L"min", { &expFuncMin, 1, std::numeric_limits<size_t>::max(), ExpFuncSafety::Safe } }, + { L"max", { &expFuncMax, 1, std::numeric_limits<size_t>::max(), ExpFuncSafety::Safe } }, + + { L"strlen", { &expFuncStrlen, 1, 1, ExpFuncSafety::Safe } }, + { L"substr", { &expFuncSubstr, 3, 3, ExpFuncSafety::Safe } }, +#if ARMIPS_REGEXP + { L"regex_match", { &expFuncRegExMatch, 2, 2, ExpFuncSafety::Safe } }, + { L"regex_search", { &expFuncRegExSearch, 2, 2, ExpFuncSafety::Safe } }, + { L"regex_extract", { &expFuncRegExExtract, 2, 3, ExpFuncSafety::Safe } }, +#endif + { L"find", { &expFuncFind, 2, 3, ExpFuncSafety::Safe } }, + { L"rfind", { &expFuncRFind, 2, 3, ExpFuncSafety::Safe } }, + + { L"readbyte", { &expFuncRead<uint8_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, + { L"readu8", { &expFuncRead<uint8_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, + { L"readu16", { &expFuncRead<uint16_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, + { L"readu32", { &expFuncRead<uint32_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, + { L"readu64", { &expFuncRead<uint64_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, + { L"reads8", { &expFuncRead<int8_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, + { L"reads16", { &expFuncRead<int16_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, + { L"reads32", { &expFuncRead<int32_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, + { L"reads64", { &expFuncRead<int64_t>, 1, 2, ExpFuncSafety::ConditionalUnsafe } }, + { L"readascii", { &expFuncReadAscii, 1, 3, ExpFuncSafety::ConditionalUnsafe } }, +}; + +extern const ExpressionLabelFunctionMap expressionLabelFunctions = +{ + { L"defined", { &expLabelFuncDefined, 1, 1, ExpFuncSafety::Unsafe } }, + { L"org", { &expLabelFuncOrg, 0, 1, ExpFuncSafety::Unsafe } }, + { L"orga", { &expLabelFuncOrga, 0, 1, ExpFuncSafety::Unsafe } }, + { L"headersize", { &expLabelFuncHeaderSize, 0, 1, ExpFuncSafety::Unsafe } }, +}; + +// file: Core/FileManager.cpp + +inline uint64_t swapEndianness64(uint64_t value) +{ + return ((value & 0xFF) << 56) | ((value & 0xFF00) << 40) | ((value & 0xFF0000) << 24) | ((value & 0xFF000000) << 8) | + ((value & 0xFF00000000) >> 8) | ((value & 0xFF0000000000) >> 24) | + ((value & 0xFF000000000000) >> 40) | ((value & 0xFF00000000000000) >> 56); +} + +inline uint32_t swapEndianness32(uint32_t value) +{ + return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24); +} + +inline uint16_t swapEndianness16(uint16_t value) +{ + return ((value & 0xFF) << 8) | ((value & 0xFF00) >> 8); +} + + +GenericAssemblerFile::GenericAssemblerFile(const std::wstring& fileName, int64_t headerSize, bool overwrite) +{ + this->fileName = fileName; + this->headerSize = headerSize; + this->originalHeaderSize = headerSize; + this->seekPhysical(0); + mode = overwrite == true ? Create : Open; +} + +GenericAssemblerFile::GenericAssemblerFile(const std::wstring& fileName, const std::wstring& originalFileName, int64_t headerSize) +{ + this->fileName = fileName; + this->originalName = originalFileName; + this->headerSize = headerSize; + this->originalHeaderSize = headerSize; + this->seekPhysical(0); + mode = Copy; } -BinaryFile::BinaryFile() -{ - handle = nullptr; -} +bool GenericAssemblerFile::open(bool onlyCheck) +{ + headerSize = originalHeaderSize; + virtualAddress = headerSize; + + if (onlyCheck == false) + { + // actually open the file + bool success; + switch (mode) + { + case Open: + success = handle.open(fileName,BinaryFile::ReadWrite); + if (success == false) + { + Logger::printError(Logger::FatalError,L"Could not open file %s",fileName); + return false; + } + return true; + + case Create: + success = handle.open(fileName,BinaryFile::Write); + if (success == false) + { + Logger::printError(Logger::FatalError,L"Could not create file %s",fileName); + return false; + } + return true; + + case Copy: + success = copyFile(originalName,fileName); + if (success == false) + { + Logger::printError(Logger::FatalError,L"Could not copy file %s",originalName); + return false; + } + + success = handle.open(fileName,BinaryFile::ReadWrite); + if (success == false) + { + Logger::printError(Logger::FatalError,L"Could not create file %s",fileName); + return false; + } + return true; + + default: + return false; + } + } + + // else only check if it can be done, don't actually do it permanently + bool success, exists; + BinaryFile temp; + switch (mode) + { + case Open: + success = temp.open(fileName,BinaryFile::ReadWrite); + if (success == false) + { + Logger::queueError(Logger::FatalError,L"Could not open file %s",fileName); + return false; + } + temp.close(); + return true; + + case Create: + // if it exists, check if you can open it with read/write access + // otherwise open it with write access and remove it afterwards + exists = fileExists(fileName); + success = temp.open(fileName,exists ? BinaryFile::ReadWrite : BinaryFile::Write); + if (success == false) + { + Logger::queueError(Logger::FatalError,L"Could not create file %s",fileName); + return false; + } + temp.close(); + + if (exists == false) + deleteFile(fileName); + + return true; + + case Copy: + // check original file + success = temp.open(originalName,BinaryFile::ReadWrite); + if (success == false) + { + Logger::queueError(Logger::FatalError,L"Could not open file %s",originalName); + return false; + } + temp.close(); + + // check new file, same as create + exists = fileExists(fileName); + success = temp.open(fileName,exists ? BinaryFile::ReadWrite : BinaryFile::Write); + if (success == false) + { + Logger::queueError(Logger::FatalError,L"Could not create file %s",fileName); + return false; + } + temp.close(); -BinaryFile::~BinaryFile() -{ - close(); -} + if (exists == false) + deleteFile(fileName); -bool BinaryFile::open(const std::wstring& fileName, Mode mode) -{ - setFileName(fileName); - return open(mode); + return true; + + default: + return false; + }; + + return false; } -bool BinaryFile::open(Mode mode) +bool GenericAssemblerFile::write(void* data, size_t length) { - if (isOpen()) - close(); + if (isOpen() == false) + return false; - this->mode = mode; + size_t len = handle.write(data,length); + virtualAddress += len; + return len == length; +} - // open all files as binary due to unicode - switch (mode) +bool GenericAssemblerFile::seekVirtual(int64_t virtualAddress) +{ + if (virtualAddress - headerSize < 0) { - case Read: - handle = openFile(fileName,OpenFileMode::ReadBinary); - break; - case Write: - handle = openFile(fileName,OpenFileMode::WriteBinary); - break; - case ReadWrite: - handle = openFile(fileName,OpenFileMode::ReadWriteBinary); - break; - default: + Logger::queueError(Logger::Error,L"Seeking to virtual address with negative physical address"); return false; } + if (virtualAddress < 0) + Logger::queueError(Logger::Warning,L"Seeking to negative virtual address"); - if (handle == nullptr) - return false; + this->virtualAddress = virtualAddress; + int64_t physicalAddress = virtualAddress-headerSize; - if (mode != Write) - { - fseek(handle,0,SEEK_END); - size_ = ftell(handle); - fseek(handle,0,SEEK_SET); - } + if (isOpen()) + handle.setPos((long)physicalAddress); return true; } -void BinaryFile::close() +bool GenericAssemblerFile::seekPhysical(int64_t physicalAddress) { - if (isOpen()) + if (physicalAddress < 0) { - fclose(handle); - handle = nullptr; + Logger::queueError(Logger::Error,L"Seeking to negative physical address"); + return false; } -} - -size_t BinaryFile::read(void* dest, size_t length) -{ - if (isOpen() == false || mode == Write) - return 0; + if (physicalAddress + headerSize < 0) + Logger::queueError(Logger::Warning,L"Seeking to physical address with negative virtual address"); - return fread(dest,1,length,handle); -} + virtualAddress = physicalAddress+headerSize; -size_t BinaryFile::write(void* source, size_t length) -{ - if (isOpen() == false || mode == Read) - return 0; + if (isOpen()) + handle.setPos((long)physicalAddress); - return fwrite(source,1,length,handle); + return true; } -const size_t TEXTFILE_BUF_MAX_SIZE = 4096; -TextFile::TextFile() -{ - handle = nullptr; - recursion = false; - errorRetrieved = false; - fromMemory = false; - bufPos = 0; - lineCount = 0; -} -TextFile::~TextFile() +FileManager::FileManager() { - close(); + // detect own endianness + volatile union + { + uint32_t i; + uint8_t c[4]; + } u; + u.c[3] = 0xAA; + u.c[2] = 0xBB; + u.c[1] = 0xCC; + u.c[0] = 0xDD; + + if (u.i == 0xDDCCBBAA) + ownEndianness = Endianness::Big; + else if (u.i == 0xAABBCCDD) + ownEndianness = Endianness::Little; + else + Logger::printError(Logger::Error,L"Running on unknown endianness"); + + reset(); } -void TextFile::openMemory(const std::wstring& content) +FileManager::~FileManager() { - fromMemory = true; - this->content = content; - contentPos = 0; - size_ = (long) content.size(); - encoding = UTF16LE; - mode = Read; - lineCount = 0; + } -bool TextFile::open(const std::wstring& fileName, Mode mode, Encoding defaultEncoding) +void FileManager::reset() { - setFileName(fileName); - return open(mode,defaultEncoding); + activeFile = nullptr; + setEndianness(Endianness::Little); } -bool TextFile::open(Mode mode, Encoding defaultEncoding) +bool FileManager::checkActiveFile() { - if (fileName.empty()) - return false; - - if (isOpen()) - close(); - - fromMemory = false; - guessedEncoding = false; - encoding = defaultEncoding; - this->mode = mode; - - // open all files as binary due to unicode - switch (mode) + if (activeFile == nullptr) { - case Read: - handle = openFile(fileName,OpenFileMode::ReadBinary); - break; - case Write: - handle = openFile(fileName,OpenFileMode::WriteBinary); - if (handle == nullptr) - return false; - - buf.resize(TEXTFILE_BUF_MAX_SIZE); - if (encoding != ASCII) - { - encoding = UTF8; - writeCharacter(0xFEFF); - } - break; - default: - return false; - } - - if (handle == nullptr) + Logger::queueError(Logger::Error,L"No file opened"); return false; - - // detect encoding - unsigned short num; - contentPos = 0; - - if (mode == Read) - { - fseek(handle,0,SEEK_END); - size_ = ftell(handle); - fseek(handle,0,SEEK_SET); - - if (fread(&num,2,1,handle) == 1) - { - switch (num) - { - case 0xFFFE: - encoding = UTF16BE; - contentPos += 2; - break; - case 0xFEFF: - encoding = UTF16LE; - contentPos += 2; - break; - case 0xBBEF: - if (fgetc(handle) == 0xBF) - { - encoding = UTF8; - contentPos += 3; - break; - } // fallthrough - default: - if (defaultEncoding == GUESS) - { - encoding = UTF8; - guessedEncoding = true; - } - fseek(handle,0,SEEK_SET); - break; - } - } else { - if (defaultEncoding == GUESS) - { - encoding = UTF8; - guessedEncoding = true; - } - } } - return true; } -void TextFile::close() +bool FileManager::openFile(std::shared_ptr<AssemblerFile> file, bool onlyCheck) { - if (isOpen() && !fromMemory) + if (activeFile != nullptr) { - bufDrainWrite(); - fclose(handle); - handle = nullptr; + Logger::queueError(Logger::Warning,L"File not closed before opening a new one"); + activeFile->close(); } - bufPos = 0; -} - -long TextFile::tell() -{ - return (long) contentPos; -} - -void TextFile::seek(long pos) -{ - if (fromMemory) - contentPos = pos; - else - fseek(handle,pos,SEEK_SET); + + activeFile = file; + return activeFile->open(onlyCheck); } -void TextFile::bufFillRead() +void FileManager::addFile(std::shared_ptr<AssemblerFile> file) { - assert(mode == Read); + files.push_back(file); +} - buf.resize(TEXTFILE_BUF_MAX_SIZE); - size_t read = fread(&buf[0], 1, TEXTFILE_BUF_MAX_SIZE, handle); - buf.resize(read); +void FileManager::closeFile() +{ + if (activeFile == nullptr) + { + Logger::queueError(Logger::Warning,L"No file opened"); + return; + } - bufPos = 0; + activeFile->close(); + activeFile = nullptr; } -wchar_t TextFile::readCharacter() +bool FileManager::write(void* data, size_t length) { - wchar_t value; + if (checkActiveFile() == false) + return false; - switch (encoding) + if (activeFile->isOpen() == false) { - case UTF8: - { - value = bufGetChar(); - contentPos++; - - int extraBytes = 0; - if ((value & 0xE0) == 0xC0) - { - extraBytes = 1; - value &= 0x1F; - } else if ((value & 0xF0) == 0xE0) - { - extraBytes = 2; - value &= 0x0F; - } else if (value > 0x7F) - { - errorText = formatString(L"One or more invalid UTF-8 characters in this file"); - } + Logger::queueError(Logger::Error,L"No file opened"); + return false; + } - for (int i = 0; i < extraBytes; i++) - { - int b = bufGetChar(); - contentPos++; + return activeFile->write(data,length); +} - if ((b & 0xC0) != 0x80) - { - errorText = formatString(L"One or more invalid UTF-8 characters in this file"); - } +bool FileManager::writeU8(uint8_t data) +{ + return write(&data,1); +} - value = (value << 6) | (b & 0x3F); - } - } - break; - case UTF16LE: - if (fromMemory) - { - value = content[contentPos++]; - } else { - value = bufGet16LE(); - contentPos += 2; - } - break; - case UTF16BE: - value = bufGet16BE(); - contentPos += 2; - break; - case SJIS: - { - unsigned short sjis = bufGetChar(); - contentPos++; - if (sjis >= 0x80) - { - sjis = (sjis << 8) | bufGetChar(); - contentPos++; - } - value = sjisToUnicode(sjis); - if (value == (wchar_t)-1) - { - errorText = formatString(L"One or more invalid Shift-JIS characters in this file"); - } - } - break; - case ASCII: - value = bufGetChar(); - contentPos++; - break; - case GUESS: - errorText = formatString(L"Cannot read from GUESS encoding"); - break; - } +bool FileManager::writeU16(uint16_t data) +{ + if (endianness != ownEndianness) + data = swapEndianness16(data); - // convert \r\n to \n - if (value == L'\r' && recursion == false && atEnd() == false) - { - recursion = true; - long pos = tell(); - wchar_t nextValue = readCharacter(); - recursion = false; + return write(&data,2); +} - if (nextValue == L'\n') - return nextValue; - seek(pos); - } +bool FileManager::writeU32(uint32_t data) +{ + if (endianness != ownEndianness) + data = swapEndianness32(data); - return value; + return write(&data,4); } -std::wstring TextFile::readLine() +bool FileManager::writeU64(uint64_t data) { - std::wstring result; - wchar_t value; + if (endianness != ownEndianness) + data = swapEndianness64(data); - if (isOpen()) - { - while (tell() < size() && (value = readCharacter()) != L'\n') - { - result += value; - } - } + return write(&data,8); +} - lineCount++; - return result; +int64_t FileManager::getVirtualAddress() +{ + if (activeFile == nullptr) + return -1; + return activeFile->getVirtualAddress(); } -StringList TextFile::readAll() +int64_t FileManager::getPhysicalAddress() { - StringList result; - while (!atEnd()) - { - result.push_back(readLine()); - } + if (activeFile == nullptr) + return -1; + return activeFile->getPhysicalAddress(); +} - return result; +int64_t FileManager::getHeaderSize() +{ + if (activeFile == nullptr) + return -1; + return activeFile->getHeaderSize(); } -void TextFile::bufPut(const void *p, const size_t len) +bool FileManager::seekVirtual(int64_t virtualAddress) { - assert(mode == Write); + if (checkActiveFile() == false) + return false; - if (len > TEXTFILE_BUF_MAX_SIZE) + bool result = activeFile->seekVirtual(virtualAddress); + if (result && Global.memoryMode) { - // Lots of data. Let's write directly. - bufDrainWrite(); - fwrite(p, 1, len, handle); + int sec = Global.symbolTable.findSection(virtualAddress); + if (sec != -1) + Global.Section = sec; } - else - { - if (bufPos + len > TEXTFILE_BUF_MAX_SIZE) - bufDrainWrite(); - memcpy(&buf[bufPos], p, len); - bufPos += len; - } + return result; } -void TextFile::bufPut(const char c) +bool FileManager::seekPhysical(int64_t virtualAddress) { - assert(mode == Write); - - if (bufPos >= TEXTFILE_BUF_MAX_SIZE) - bufDrainWrite(); - - buf[bufPos++] = c; + if (checkActiveFile() == false) + return false; + return activeFile->seekPhysical(virtualAddress); } -void TextFile::bufDrainWrite() +bool FileManager::advanceMemory(size_t bytes) { - fwrite(&buf[0], 1, bufPos, handle); - bufPos = 0; + if (checkActiveFile() == false) + return false; + + int64_t pos = activeFile->getVirtualAddress(); + return activeFile->seekVirtual(pos+bytes); } -void TextFile::writeCharacter(wchar_t character) -{ - if (mode != Write) return; +// file: Core/Misc.cpp +#include <iostream> - // only support utf8 for now - if (character < 0x80) - { #ifdef _WIN32 - if (character == L'\n') - { - bufPut('\r'); - } +#include <windows.h> #endif - bufPut(character & 0x7F); - } else if (encoding != ASCII) + +std::vector<Logger::QueueEntry> Logger::queue; +std::vector<std::wstring> Logger::errors; +bool Logger::error = false; +bool Logger::fatalError = false; +bool Logger::errorOnWarning = false; +bool Logger::silent = false; +int Logger::suppressLevel = 0; + +std::wstring Logger::formatError(ErrorType type, const wchar_t* text) +{ + std::wstring position; + + if (Global.memoryMode == false && Global.FileInfo.FileList.size() > 0) { - if (character < 0x800) - { - bufPut(0xC0 | ((character >> 6) & 0x1F)); - bufPut(0x80 | (character & 0x3F)); - } else { - bufPut(0xE0 | ((character >> 12) & 0xF)); - bufPut(0x80 | ((character >> 6) & 0x3F)); - bufPut(0x80 | (character & 0x3F)); - } + std::wstring& fileName = Global.FileInfo.FileList[Global.FileInfo.FileNum]; + position = formatString(L"%s(%d) ",fileName,Global.FileInfo.LineNumber); } -} -void TextFile::write(const wchar_t* line) -{ - if (mode != Write) return; - while (*line != 0) + switch (type) { - writeCharacter(*line); - line++; + case Warning: + return formatString(L"%swarning: %s",position,text); + case Error: + return formatString(L"%serror: %s",position,text); + case FatalError: + return formatString(L"%sfatal error: %s",position,text); + case Notice: + return formatString(L"%snotice: %s",position,text); } -} -void TextFile::write(const std::wstring& line) -{ - write(line.c_str()); + return L""; } -void TextFile::write(const char* line) +void Logger::setFlags(ErrorType type) { - if (mode != Write) return; - while (*line != 0) + switch (type) { - writeCharacter(*line); - line++; + case Warning: + if (errorOnWarning) + error = true; + break; + case Error: + error = true; + break; + case FatalError: + error = true; + fatalError = true; + break; + case Notice: + break; } } -void TextFile::write(const std::string& line) -{ - write(line.c_str()); -} - -void TextFile::writeLine(const wchar_t* line) +void Logger::clear() { - if (mode != Write) return; - write(line); - writeCharacter(L'\n'); + queue.clear(); + errors.clear(); + error = false; + fatalError = false; + errorOnWarning = false; + silent = false; } -void TextFile::writeLine(const std::wstring& line) +void Logger::printLine(const std::wstring& text) { - writeLine(line.c_str()); -} + if (suppressLevel) + return; -void TextFile::writeLine(const char* line) -{ - if (mode != Write) return; - write(line); - writeCharacter(L'\n'); -} + std::wcout << text << std::endl; -void TextFile::writeLine(const std::string& line) -{ - writeLine(line.c_str()); +#if defined(_MSC_VER) && defined(_DEBUG) + OutputDebugStringW(text.c_str()); + OutputDebugStringW(L"\n"); +#endif } -void TextFile::writeLines(StringList& list) +void Logger::printLine(const std::string& text) { - for (size_t i = 0; i < list.size(); i++) - { - writeLine(list[i]); - } -} + if (suppressLevel) + return; -struct EncodingValue -{ - const wchar_t* name; - TextFile::Encoding value; -}; + std::cout << text << std::endl; -const EncodingValue encodingValues[] = { - { L"sjis", TextFile::SJIS }, - { L"shift-jis", TextFile::SJIS }, - { L"utf8", TextFile::UTF8 }, - { L"utf-8", TextFile::UTF8 }, - { L"utf16", TextFile::UTF16LE }, - { L"utf-16", TextFile::UTF16LE }, - { L"utf16-be", TextFile::UTF16BE }, - { L"utf-16-be", TextFile::UTF16BE }, - { L"ascii", TextFile::ASCII }, -}; +#if defined(_MSC_VER) && defined(_DEBUG) + OutputDebugStringA(text.c_str()); + OutputDebugStringA("\n"); +#endif +} -TextFile::Encoding getEncodingFromString(const std::wstring& str) +void Logger::print(const std::wstring& text) { - for (size_t i = 0; i < sizeof(encodingValues)/sizeof(EncodingValue); i++) - { - if (str.compare(encodingValues[i].name) == 0) - return encodingValues[i].value; - } + if (suppressLevel) + return; - return TextFile::GUESS; -} + std::wcout << text; -// file: Util/Util.cpp -#include <sys/stat.h> -#ifdef _WIN32 -#include <windows.h> -#include <shlwapi.h> -#if defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_PARTITION) -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP -#define ARMIPS_WINDOWS_UWP -#endif -#endif -#else -#include <unistd.h> +#if defined(_MSC_VER) && defined(_DEBUG) + OutputDebugStringW(text.c_str()); #endif +} -std::wstring convertUtf8ToWString(const char* source) +void Logger::printError(ErrorType type, const std::wstring& text) { - std::wstring result; - - int index = 0; - while (source[index] != 0) - { - int extraBytes = 0; - int value = source[index++]; - - if ((value & 0xE0) == 0xC0) - { - extraBytes = 1; - value &= 0x1F; - } else if ((value & 0xF0) == 0xE0) - { - extraBytes = 2; - value &= 0x0F; - } else if (value > 0x7F) - { - // error - return std::wstring(); - } - - for (int i = 0; i < extraBytes; i++) - { - int b = source[index++]; - if ((b & 0xC0) != 0x80) - { - // error - return std::wstring(); - } + if (suppressLevel) + return; - value = (value << 6) | (b & 0x3F); - } + std::wstring errorText = formatError(type,text.c_str()); + errors.push_back(errorText); - result += value; - } + if (!silent) + printLine(errorText); - return result; + setFlags(type); } -std::string convertWCharToUtf8(wchar_t character) +void Logger::printError(ErrorType type, const wchar_t* text) { - std::string result; + if (suppressLevel) + return; - if (character < 0x80) - { - result += character & 0x7F; - } else if (character < 0x800) - { - result += 0xC0 | ((character >> 6) & 0x1F); - result += (0x80 | (character & 0x3F)); - } else { - result += 0xE0 | ((character >> 12) & 0xF); - result += 0x80 | ((character >> 6) & 0x3F); - result += 0x80 | (character & 0x3F); - } + std::wstring errorText = formatError(type,text); + errors.push_back(errorText); - return result; + if (!silent) + printLine(errorText); + + setFlags(type); } -std::string convertWStringToUtf8(const std::wstring& source) +void Logger::queueError(ErrorType type, const std::wstring& text) { - std::string result; - - for (size_t i = 0; i < source.size(); i++) - { - wchar_t character = source[i]; - if (character < 0x80) - { - result += character & 0x7F; - } else if (character < 0x800) - { - result += 0xC0 | ((character >> 6) & 0x1F); - result += (0x80 | (character & 0x3F)); - } else { - result += 0xE0 | ((character >> 12) & 0xF); - result += 0x80 | ((character >> 6) & 0x3F); - result += 0x80 | (character & 0x3F); - } - } + if (suppressLevel) + return; - return result; + QueueEntry entry; + entry.type = type; + entry.text = formatError(type,text.c_str()); + queue.push_back(entry); } -std::wstring intToHexString(unsigned int value, int digits, bool prefix) +void Logger::queueError(ErrorType type, const wchar_t* text) { - std::wstring result; - result.reserve((digits+prefix) ? 2 : 0); - - if (prefix) - { - result += '0'; - result += 'x'; - } - - while (digits > 8) - { - result += '0'; - digits--; - } - - wchar_t buf[9]; - swprintf(buf,9,L"%0*X",digits,value); - result += buf; + if (suppressLevel) + return; - return result; + QueueEntry entry; + entry.type = type; + entry.text = formatError(type,text); + queue.push_back(entry); } -std::wstring intToString(unsigned int value, int digits) +void Logger::printQueue() { - std::wstring result; - result.reserve(digits); - - while (digits > 8) + for (size_t i = 0; i < queue.size(); i++) { - result += ' '; - digits--; - } + errors.push_back(queue[i].text); - wchar_t buf[9]; - swprintf(buf,9,L"%*d",digits,value); - result += buf; + if (!silent) + printLine(queue[i].text); - return result; + setFlags(queue[i].type); + } } -bool stringToInt(const std::wstring& line, size_t start, size_t end, int64_t& result) +void TempData::start() { - // find base of number - int32_t base = 10; - if (line[start] == '0') + if (file.getFileName().empty() == false) { - if (towlower(line[start+1]) == 'x') - { - base = 16; - start += 2; - } else if (towlower(line[start+1]) == 'o') - { - base = 8; - start += 2; - } else if (towlower(line[start+1]) == 'b' && towlower(line[end-1]) != 'h') + if (file.open(TextFile::Write) == false) { - base = 2; - start += 2; + Logger::printError(Logger::Error,L"Could not open temp file %s.",file.getFileName()); + return; } - } - if (base == 10) - { - if (towlower(line[end-1]) == 'h') - { - base = 16; - end--; - } else if (towlower(line[end-1]) == 'b') - { - base = 2; - end--; - } else if (towlower(line[end-1]) == 'o') + size_t fileCount = Global.FileInfo.FileList.size(); + size_t lineCount = Global.FileInfo.TotalLineCount; + size_t labelCount = Global.symbolTable.getLabelCount(); + size_t equCount = Global.symbolTable.getEquationCount(); + + file.writeFormat(L"; %d %S included\n",fileCount,fileCount == 1 ? "file" : "files"); + file.writeFormat(L"; %d %S\n",lineCount,lineCount == 1 ? "line" : "lines"); + file.writeFormat(L"; %d %S\n",labelCount,labelCount == 1 ? "label" : "labels"); + file.writeFormat(L"; %d %S\n\n",equCount,equCount == 1 ? "equation" : "equations"); + for (size_t i = 0; i < fileCount; i++) { - base = 8; - end--; + file.writeFormat(L"; %S\n",Global.FileInfo.FileList[i]); } + file.writeLine(""); } +} - // convert number - result = 0; - while (start < end) - { - wchar_t c = towlower(line[start++]); +void TempData::end() +{ + if (file.isOpen()) + file.close(); +} - int32_t value = c >= 'a' ? c-'a'+10 : c-'0'; +void TempData::writeLine(int64_t memoryAddress, const std::wstring& text) +{ + if (file.isOpen()) + { + wchar_t hexbuf[10] = {0}; + swprintf(hexbuf, 10, L"%08X ", (int32_t) memoryAddress); + std::wstring str = hexbuf + text; + while (str.size() < 70) + str += ' '; - if (value >= base) - return false; + str += formatString(L"; %S line %d", + Global.FileInfo.FileList[Global.FileInfo.FileNum],Global.FileInfo.LineNumber); - result = (result*base) + value; + file.writeLine(str); } - - return true; } -int32_t getFloatBits(float value) -{ - union { float f; int32_t i; } u; - u.f = value; - return u.i; -} +// file: Core/SymbolData.cpp +#include <algorithm> -float bitsToFloat(int32_t value) +SymbolData::SymbolData() { - union { float f; int32_t i; } u; - u.i = value; - return u.f; + clear(); } -int64_t getDoubleBits(double value) +void SymbolData::clear() { - union { double f; int64_t i; } u; - u.f = value; - return u.i; + enabled = true; + nocashSymFileName.clear(); + modules.clear(); + files.clear(); + currentModule = 0; + currentFunction = -1; + + SymDataModule defaultModule; + defaultModule.file = nullptr; + modules.push_back(defaultModule); } -StringList getStringListFromArray(wchar_t** source, int count) +struct NocashSymEntry { - StringList result; - for (int i = 0; i < count; i++) + int64_t address; + std::wstring text; + + bool operator<(const NocashSymEntry& other) const { - result.push_back(std::wstring(source[i])); + if (address != other.address) + return address < other.address; + return text < other.text; } +}; - return result; -} - -StringList splitString(const std::wstring& str, const wchar_t delim, bool skipEmpty) +void SymbolData::writeNocashSym() { - StringList result; - std::wstringstream stream(str); - std::wstring arg; - while (std::getline(stream,arg,delim)) + if (nocashSymFileName.empty()) + return; + + std::vector<NocashSymEntry> entries; + for (size_t k = 0; k < modules.size(); k++) { - if (arg.empty() && skipEmpty) continue; - result.push_back(arg); - } + SymDataModule& module = modules[k]; + for (size_t i = 0; i < module.symbols.size(); i++) + { + SymDataSymbol& sym = module.symbols[i]; - return result; -} + size_t size = 0; + for (size_t f = 0; f < module.functions.size(); f++) + { + if (module.functions[f].address == sym.address) + { + size = module.functions[f].size; + break; + } + } -int64_t fileSize(const std::wstring& fileName) -{ -#ifdef _WIN32 - WIN32_FILE_ATTRIBUTE_DATA attr; - if (!GetFileAttributesEx(fileName.c_str(),GetFileExInfoStandard,&attr) - || (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - return 0; - return ((int64_t) attr.nFileSizeHigh << 32) | (int64_t) attr.nFileSizeLow; -#else - std::string utf8 = convertWStringToUtf8(fileName); - struct stat fileStat; - int err = stat(utf8.c_str(),&fileStat); - if (0 != err) - return 0; - return fileStat.st_size; -#endif -} + NocashSymEntry entry; + entry.address = sym.address; -bool fileExists(const std::wstring& strFilename) -{ -#ifdef _WIN32 -#ifdef ARMIPS_WINDOWS_UWP - return GetFileAttributes(strFilename.c_str()) != INVALID_FILE_ATTRIBUTES; -#else - int OldMode = SetErrorMode(SEM_FAILCRITICALERRORS); - bool success = GetFileAttributes(strFilename.c_str()) != INVALID_FILE_ATTRIBUTES; - SetErrorMode(OldMode); - return success; -#endif -#else - std::string utf8 = convertWStringToUtf8(strFilename); - struct stat stFileInfo; - int intStat = stat(utf8.c_str(),&stFileInfo); - return intStat == 0; -#endif -} + if (size != 0 && nocashSymVersion >= 2) + entry.text = formatString(L"%s,%08X",sym.name,size); + else + entry.text = sym.name; -bool copyFile(const std::wstring& existingFile, const std::wstring& newFile) -{ -#ifdef _WIN32 - return CopyFileW(existingFile.c_str(),newFile.c_str(),false) != FALSE; -#else - unsigned char buffer[BUFSIZ]; - bool error = false; + if (nocashSymVersion == 1) + std::transform(entry.text.begin(), entry.text.end(), entry.text.begin(), ::towlower); - std::string existingUtf8 = convertWStringToUtf8(existingFile); - std::string newUtf8 = convertWStringToUtf8(newFile); + entries.push_back(entry); + } - FILE* input = fopen(existingUtf8.c_str(),"rb"); - FILE* output = fopen(newUtf8.c_str(),"wb"); + for (const SymDataData& data: module.data) + { + NocashSymEntry entry; + entry.address = data.address; - if (input == nullptr || output == nullptr) - return false; + switch (data.type) + { + case Data8: + entry.text = formatString(L".byt:%04X",data.size); + break; + case Data16: + entry.text = formatString(L".wrd:%04X",data.size); + break; + case Data32: + entry.text = formatString(L".dbl:%04X",data.size); + break; + case Data64: + entry.text = formatString(L".dbl:%04X",data.size); + break; + case DataAscii: + entry.text = formatString(L".asc:%04X",data.size); + break; + } - size_t n; - while ((n = fread(buffer,1,BUFSIZ,input)) > 0) + entries.push_back(entry); + } + } + + std::sort(entries.begin(),entries.end()); + + TextFile file; + if (file.open(nocashSymFileName,TextFile::Write,TextFile::ASCII) == false) + { + Logger::printError(Logger::Error,L"Could not open sym file %s.",file.getFileName()); + return; + } + file.writeLine(L"00000000 0"); + + for (size_t i = 0; i < entries.size(); i++) { - if (fwrite(buffer,1,n,output) != n) - error = true; + file.writeFormat(L"%08X %s\n",entries[i].address,entries[i].text); } - fclose(input); - fclose(output); - return !error; -#endif + file.write("\x1A"); + file.close(); } -bool deleteFile(const std::wstring& fileName) +void SymbolData::write() { -#ifdef _WIN32 - return DeleteFileW(fileName.c_str()) != FALSE; -#else - std::string utf8 = convertWStringToUtf8(fileName); - return unlink(utf8.c_str()) == 0; -#endif + writeNocashSym(); } -FILE* openFile(const std::wstring& fileName, OpenFileMode mode) +void SymbolData::addLabel(int64_t memoryAddress, const std::wstring& name) { -#ifdef _WIN32 - switch (mode) - { - case OpenFileMode::ReadBinary: - return _wfopen(fileName.c_str(),L"rb"); - case OpenFileMode::WriteBinary: - return _wfopen(fileName.c_str(),L"wb"); - case OpenFileMode::ReadWriteBinary: - return _wfopen(fileName.c_str(),L"rb+"); - } -#else - std::string nameUtf8 = convertWStringToUtf8(fileName); + if (!enabled) + return; - switch (mode) + SymDataSymbol sym; + sym.address = memoryAddress; + sym.name = name; + + for (SymDataSymbol& symbol: modules[currentModule].symbols) { - case OpenFileMode::ReadBinary: - return fopen(nameUtf8.c_str(),"rb"); - case OpenFileMode::WriteBinary: - return fopen(nameUtf8.c_str(),"wb"); - case OpenFileMode::ReadWriteBinary: - return fopen(nameUtf8.c_str(),"rb+"); + if (symbol.address == sym.address && symbol.name == sym.name) + return; } -#endif - return nullptr; + modules[currentModule].symbols.push_back(sym); } -std::wstring getCurrentDirectory() +void SymbolData::addData(int64_t address, size_t size, DataType type) { -#ifdef _WIN32 - wchar_t dir[MAX_PATH]; - _wgetcwd(dir,MAX_PATH-1); - return dir; -#else - char* dir = getcwd(nullptr,0); - std::wstring result = convertUtf8ToWString(dir); - free(dir); - return result; -#endif -} + if (!enabled) + return; -bool changeDirectory(const std::wstring& dir) -{ -#ifdef _WIN32 - return _wchdir(dir.c_str()) == 0; -#else - std::string utf8 = convertWStringToUtf8(dir); - return chdir(utf8.c_str()) == 0; -#endif + SymDataData data; + data.address = address; + data.size = size; + data.type = type; + modules[currentModule].data.insert(data); } -std::wstring toWLowercase(const std::string& str) +size_t SymbolData::addFileName(const std::wstring& fileName) { - std::wstring result; - for (size_t i = 0; i < str.size(); i++) + for (size_t i = 0; i < files.size(); i++) { - result += tolower(str[i]); + if (files[i] == fileName) + return i; } - return result; + files.push_back(fileName); + return files.size()-1; } -std::wstring getFileNameFromPath(const std::wstring& path) +void SymbolData::startModule(AssemblerFile* file) { - size_t n = path.find_last_of(L"/\\"); - if (n == path.npos) - return path; - return path.substr(n); + for (size_t i = 0; i < modules.size(); i++) + { + if (modules[i].file == file) + { + currentModule = i; + return; + } + } + + SymDataModule module; + module.file = file; + modules.push_back(module); + currentModule = modules.size()-1; } -size_t replaceAll(std::wstring& str, const wchar_t* oldValue,const std::wstring& newValue) +void SymbolData::endModule(AssemblerFile* file) { - size_t pos = 0; - size_t len = wcslen(oldValue); + if (modules[currentModule].file != file) + return; - size_t count = 0; - while ((pos = str.find(oldValue, pos)) != std::string::npos) + if (currentModule == 0) { - str.replace(pos,len,newValue); - pos += newValue.length(); - count++; + Logger::printError(Logger::Error,L"No module opened"); + return; } - return count; + if (currentFunction != -1) + { + Logger::printError(Logger::Error,L"Module closed before function end"); + currentFunction = -1; + } + + currentModule = 0; } -bool startsWith(const std::wstring& str, const wchar_t* value, size_t stringPos) +void SymbolData::startFunction(int64_t address) { - while (*value != 0 && stringPos < str.size()) + if (currentFunction != -1) { - if (str[stringPos++] != *value++) - return false; + endFunction(address); } - return *value == 0; + currentFunction = modules[currentModule].functions.size(); + + SymDataFunction func; + func.address = address; + func.size = 0; + modules[currentModule].functions.push_back(func); } -bool isAbsolutePath(const std::wstring& path) +void SymbolData::endFunction(int64_t address) { -#ifdef _WIN32 - return path.size() > 2 && (path[1] == ':' || (path[0] == '\\' && path[1] == '\\')); -#else - return path.size() >= 1 && path[0] == '/'; -#endif + if (currentFunction == -1) + { + Logger::printError(Logger::Error,L"Not inside a function"); + return; + } + + SymDataFunction& func = modules[currentModule].functions[currentFunction]; + func.size = (size_t) (address-func.address); + currentFunction = -1; } -// file: Main/CommandLineInterface.h +// file: Core/SymbolTable.cpp -int runFromCommandLine(const StringList& arguments, ArmipsArguments settings = {}); +const wchar_t validSymbolCharacters[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_."; -// file: Main/CommandLineInterface.cpp +bool operator<(SymbolKey const& lhs, SymbolKey const& rhs) +{ + if (lhs.file != rhs.file) + return lhs.file < rhs.file; + if (lhs.section != rhs.section) + return lhs.section < rhs.section; + return lhs.name.compare(rhs.name) < 0; +} -static void printUsage(std::wstring executableName) +SymbolTable::SymbolTable() { - Logger::printLine(L"armips assembler v%d.%d.%d (%s %s) by Kingcom", - ARMIPS_VERSION_MAJOR, ARMIPS_VERSION_MINOR, ARMIPS_VERSION_REVISION, __DATE__, __TIME__); - Logger::printLine(L"Usage: %s [optional parameters] <FILE>", executableName); - Logger::printLine(L""); - Logger::printLine(L"Optional parameters:"); - Logger::printLine(L" -temp <TEMP> Output temporary assembly data to <TEMP> file"); - Logger::printLine(L" -sym <SYM> Output symbol data in the sym format to <SYM> file"); - Logger::printLine(L" -sym2 <SYM2> Output symbol data in the sym2 format to <SYM2> file"); - Logger::printLine(L" -root <ROOT> Use <ROOT> as working directory during execution"); - Logger::printLine(L" -equ <NAME> <VAL> Equivalent to \'<NAME> equ <VAL>\' in code"); - Logger::printLine(L" -strequ <NAME> <VAL> Equivalent to \'<NAME> equ \"<VAL>\"\' in code"); - Logger::printLine(L" -definelabel <NAME> <VAL> Equivalent to \'.definelabel <NAME>, <VAL>\' in code"); - Logger::printLine(L" -erroronwarning Treat all warnings like errors"); - Logger::printLine(L""); - Logger::printLine(L"File arguments:"); - Logger::printLine(L" <FILE> Main assembly code file"); + uniqueCount = 0; } -static bool parseArguments(const StringList& arguments, ArmipsArguments& settings) +SymbolTable::~SymbolTable() { - size_t argpos = 1; - bool readflags = true; - while (argpos < arguments.size()) + clear(); +} + +void SymbolTable::clear() +{ + symbols.clear(); + labels.clear(); + equationsCount = 0; + uniqueCount = 0; +} + +void SymbolTable::setFileSectionValues(const std::wstring& symbol, int& file, int& section) +{ + if (symbol[0] == '@') { - if (readflags && arguments[argpos][0] == L'-') + if (symbol[1] != '@') { - if (arguments[argpos] == L"--") - { - readflags = false; - argpos += 1; - } - else if (arguments[argpos] == L"-temp" && argpos + 1 < arguments.size()) - { - settings.tempFileName = arguments[argpos + 1]; - argpos += 2; - } - else if (arguments[argpos] == L"-sym" && argpos + 1 < arguments.size()) - { - settings.symFileName = arguments[argpos + 1]; - settings.symFileVersion = 1; - argpos += 2; - } - else if (arguments[argpos] == L"-sym2" && argpos + 1 < arguments.size()) - { - settings.symFileName = arguments[argpos + 1]; - settings.symFileVersion = 2; - argpos += 2; - } - else if (arguments[argpos] == L"-erroronwarning") - { - settings.errorOnWarning = true; - argpos += 1; - } - else if (arguments[argpos] == L"-equ" && argpos + 2 < arguments.size()) - { - EquationDefinition def; - - def.name = arguments[argpos+1]; - std::transform(def.name.begin(), def.name.end(), def.name.begin(), ::towlower); + // static label, @. the section doesn't matter + section = -1; + } else { + // local label, @@. the file doesn't matter + file = -1; + } + } else { + // global label. neither file nor section matters + file = section = -1; + } +} - if (!checkValidLabelName(def.name)) - { - Logger::printError(Logger::Error, L"Invalid equation name \"%s\"", def.name); - return false; - } +std::shared_ptr<Label> SymbolTable::getLabel(const std::wstring& symbol, int file, int section) +{ + if (isValidSymbolName(symbol) == false) + return nullptr; - auto it = std::find_if(settings.equList.begin(), settings.equList.end(), - [&def](EquationDefinition x) -> bool {return def.name == x.name;}); - if(it != settings.equList.end()) - { - Logger::printError(Logger::Error, L"Equation name \"%s\" already defined", def.name); - return false; - } + int actualSection = section; + setFileSectionValues(symbol,file,section); + SymbolKey key = { symbol, file, section }; - def.value = arguments[argpos + 2]; - settings.equList.push_back(def); - argpos += 3; - } - else if (arguments[argpos] == L"-strequ" && argpos + 2 < arguments.size()) - { - EquationDefinition def; + // find label, create new one if it doesn't exist + auto it = symbols.find(key); + if (it == symbols.end()) + { + SymbolInfo value = { LabelSymbol, labels.size() }; + symbols[key] = value; - def.name = arguments[argpos+1]; - std::transform(def.name.begin(), def.name.end(), def.name.begin(), ::towlower); + std::shared_ptr<Label> result = std::make_shared<Label>(symbol); + if (section == actualSection) + result->setSection(section); // local, set section of parent + else + result->setSection(actualSection+1); // global, set section of children + labels.push_back(result); + return result; + } - if (!checkValidLabelName(def.name)) - { - Logger::printError(Logger::Error, L"Invalid equation name \"%s\"", def.name); - return false; - } + // make sure not to match symbols that aren't labels + if (it->second.type != LabelSymbol) + return nullptr; - auto it = std::find_if(settings.equList.begin(), settings.equList.end(), - [&def](EquationDefinition x) -> bool {return def.name == x.name;}); - if(it != settings.equList.end()) - { - Logger::printError(Logger::Error, L"Equation name \"%s\" already defined", def.name); - return false; - } + return labels[it->second.index]; +} - def.value = formatString(L"\"%s\"", arguments[argpos + 2]); - settings.equList.push_back(def); - argpos += 3; - } - else if (arguments[argpos] == L"-time") - { - Logger::printError(Logger::Warning, L"-time flag is deprecated"); - argpos += 1; - } - else if (arguments[argpos] == L"-root" && argpos + 1 < arguments.size()) - { - if(!changeDirectory(arguments[argpos + 1])) - { - Logger::printError(Logger::Error, L"Could not open directory \"%s\"", arguments[argpos + 1]); - return false; - } - argpos += 2; - } - else if (arguments[argpos] == L"-definelabel" && argpos + 2 < arguments.size()) - { - LabelDefinition def; +bool SymbolTable::symbolExists(const std::wstring& symbol, int file, int section) +{ + if (isValidSymbolName(symbol) == false) + return false; - def.originalName = arguments[argpos + 1]; - def.name = def.originalName; - std::transform(def.name.begin(), def.name.end(), def.name.begin(), ::towlower); + setFileSectionValues(symbol,file,section); - if (!checkValidLabelName(def.name)) - { - Logger::printError(Logger::Error, L"Invalid label name \"%s\"", def.name); - return false; - } + SymbolKey key = { symbol, file, section }; + auto it = symbols.find(key); + return it != symbols.end(); +} - auto it = std::find_if(settings.labels.begin(), settings.labels.end(), - [&def](LabelDefinition x) -> bool {return def.name == x.name;}); - if(it != settings.labels.end()) - { - Logger::printError(Logger::Error, L"Label name \"%s\" already defined", def.name); - return false; - } +bool SymbolTable::isValidSymbolName(const std::wstring& symbol) +{ + size_t size = symbol.size(); + size_t start = 0; - int64_t value; - if (!stringToInt(arguments[argpos + 2], 0, arguments[argpos + 2].size(), value)) - { - Logger::printError(Logger::Error, L"Invalid label value \"%s\"", arguments[argpos + 2]); - return false; - } - def.value = value; + // don't match empty names + if (size == 0 || symbol.compare(L"@") == 0 || symbol.compare(L"@@") == 0) + return false; - settings.labels.push_back(def); - argpos += 3; - } - else { - Logger::printError(Logger::Error, L"Invalid command line argument \"%s\"\n", arguments[argpos]); - printUsage(arguments[0]); - return false; - } - } - else { - // only allow one input filename - if (settings.inputFileName == L"") - { - settings.inputFileName = arguments[argpos]; - argpos++; - } - else { - Logger::printError(Logger::Error, L"Multiple input assembly files specified\n"); - printUsage(arguments[0]); - return false; - } - } + if (symbol[0] == '@') + { + start++; + if (size > 1 && symbol[1] == '@') + start++; } - // ensure input file was specified - if (settings.inputFileName == L"") + if (symbol[start] >= '0' && symbol[start] <= '9') + return false; + + for (size_t i = start; i < size; i++) { - if (arguments.size() > 1) - Logger::printError(Logger::Error, L"Missing input assembly file\n"); + if (wcschr(validSymbolCharacters,symbol[i]) == nullptr) + return false; + } - printUsage(arguments[0]); + return true; +} + +bool SymbolTable::isValidSymbolCharacter(wchar_t character, bool first) +{ + if ((character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z')) return true; + if (!first && (character >= '0' && character <= '9')) return true; + if (character == '_' || character == '.') return true; + if (character == '@') return true; + return false; +} + +bool SymbolTable::addEquation(const std::wstring& name, int file, int section, size_t referenceIndex) +{ + if (isValidSymbolName(name) == false) return false; - } - // turn input filename into an absolute path - if (settings.useAbsoluteFileNames && isAbsolutePath(settings.inputFileName) == false) - settings.inputFileName = formatString(L"%s/%s", getCurrentDirectory(), settings.inputFileName); + if (symbolExists(name,file,section)) + return false; - if (fileExists(settings.inputFileName) == false) - { - Logger::printError(Logger::Error, L"File \"%s\" not found", settings.inputFileName); + setFileSectionValues(name,file,section); + + SymbolKey key = { name, file, section }; + SymbolInfo value = { EquationSymbol, referenceIndex }; + symbols[key] = value; + + equationsCount++; + return true; +} + +bool SymbolTable::findEquation(const std::wstring& name, int file, int section, size_t& dest) +{ + setFileSectionValues(name,file,section); + + SymbolKey key = { name, file, section }; + auto it = symbols.find(key); + if (it == symbols.end() || it->second.type != EquationSymbol) return false; - } + + dest = it->second.index; return true; } -int runFromCommandLine(const StringList& arguments, ArmipsArguments settings) +// TODO: better +std::wstring SymbolTable::getUniqueLabelName(bool local) { - if (parseArguments(arguments, settings) == false) + std::wstring name = formatString(L"__armips_label_%08x__",uniqueCount++); + if (local) + name = L"@@" + name; + + generatedLabels.insert(name); + return name; +} + +void SymbolTable::addLabels(const std::vector<LabelDefinition>& labels) +{ + for (const LabelDefinition& def: labels) { - if (arguments.size() > 1 && !settings.silent) - Logger::printLine(L"Cannot parse arguments; aborting."); + if (!isValidSymbolName(def.name)) + continue; - return 1; + std::shared_ptr<Label> label = getLabel(def.name,Global.FileInfo.FileNum,Global.Section); + if (label == nullptr) + continue; + + label->setOriginalName(def.originalName); + + if (isLocalSymbol(def.name) == false) + Global.Section++; + + label->setDefined(true); + label->setValue(def.value); } +} - if (runArmips(settings) == false) - { - if (!settings.silent) - Logger::printLine(L"Aborting."); +int SymbolTable::findSection(int64_t address) +{ + int64_t smallestBefore = -1; + int64_t smallestDiff = 0x7FFFFFFF; - return 1; + for (auto& lab: labels) + { + int diff = address-lab->getValue(); + if (diff >= 0 && diff < smallestDiff) + { + smallestDiff = diff; + smallestBefore = lab->getSection(); + } } - return 0; + return smallestBefore; } // file: Main/main.cpp diff --git a/tools/audiofile/Makefile b/tools/audiofile/Makefile @@ -0,0 +1,12 @@ +CXX := g++ + +libaudiofile.a: audiofile.o + ar rcs libaudiofile.a audiofile.o + +audiofile.o: audiofile.cpp audiofile.h aupvlist.h + $(CXX) -std=c++11 -O2 -I. -c audiofile.cpp + +clean: + rm -f audiofile.o libaudiofile.a + +.PHONY: clean diff --git a/tools/audiofile/audiofile.cpp b/tools/audiofile/audiofile.cpp @@ -0,0 +1,15915 @@ +// libaudiofile b62c902 +// https://github.com/mpruett/audiofile +// To simplify compilation, all files have been concatenated into one. +// Support for all formats except WAVE, AIFF(C) and RAW has been stripped out. + +/* + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + +*/ + +#define HAVE_UNISTD_H 1 +#if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +#endif +#include <stdlib.h> + +// file: Features.h +/* + Audio File Library + Copyright (C) 2013 Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef Features_h +#define Features_h + +#define ENABLE(FEATURE) (defined ENABLE_##FEATURE && ENABLE_##FEATURE) + +#endif + +// file: Compiler.h +/* + Audio File Library + Copyright (C) 2013 Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef COMPILER_H +#define COMPILER_H + +#if defined(__GNUC__) && !defined(__clang__) +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#define GCC_VERSION_AT_LEAST(major, minor, patch) \ + (GCC_VERSION >= (major * 10000 + minor * 100 + patch)) +#if GCC_VERSION_AT_LEAST(4, 7, 0) && defined(__cplusplus) && __cplusplus >= 201103L +#define OVERRIDE override +#endif +#endif + +#if defined(__clang__) +#if __has_extension(cxx_override_control) +#define OVERRRIDE override +#endif +#endif + +#ifndef OVERRIDE +#define OVERRIDE +#endif + +#endif + +// file: error.h +/* + Audio File Library + Copyright (C) 1998, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef ERROR_H +#define ERROR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__GNUC__) && !defined(__clang__) && !defined(__attribute__) +#define __attribute__(x) +#endif + +void _af_error (int errorCode, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); + +#ifdef __cplusplus +} +#endif + +#endif + +// file: extended.h +/* + Audio File Library + Copyright (C) 1998, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + extended.h + + This file defines interfaces to Apple's extended floating-point + conversion routines. +*/ + +#ifndef EXTENDED_H +#define EXTENDED_H + +#ifdef __cplusplus +extern "C" { +#endif + +void _af_convert_to_ieee_extended (double num, unsigned char *bytes); +double _af_convert_from_ieee_extended (const unsigned char *bytes); + +#ifdef __cplusplus +} +#endif + +#endif + +// file: compression.h +/* + Audio File Library + Copyright (C) 1999, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + compression.h +*/ + +#ifndef COMPRESSION_H +#define COMPRESSION_H + +struct CompressionUnit; + +const CompressionUnit *_af_compression_unit_from_id (int compressionid); + +#endif + +// file: aupvinternal.h +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + aupvinternal.h + + This file contains the private data structures for the parameter + value list data types. +*/ + +#ifndef AUPVINTERNAL_H +#define AUPVINTERNAL_H + +struct _AUpvitem +{ + int valid; + int type; + int parameter; + + union + { + long l; + double d; + void *v; + } + value; +}; + +struct _AUpvlist +{ + int valid; + size_t count; + struct _AUpvitem *items; +}; + +enum +{ + _AU_VALID_PVLIST = 30932, + _AU_VALID_PVITEM = 30933 +}; + +enum +{ + AU_BAD_PVLIST = -5, + AU_BAD_PVITEM = -6, + AU_BAD_PVTYPE = -7, + AU_BAD_ALLOC = -8 +}; + +enum +{ + _AU_FAIL = -1, + _AU_SUCCESS = 0 +}; + +#define _AU_NULL_PVITEM ((struct _AUpvitem *) NULL) + +#endif + +// file: aupvlist.h +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + aupvlist.h + + This file contains the interface to the parameter value list data + structures and routines. +*/ + +#ifndef AUPVLIST_H +#define AUPVLIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__clang__) +#define AFAPI __attribute__((visibility("default"))) +#else +#define AFAPI +#endif + +enum +{ + AU_PVTYPE_LONG = 1, + AU_PVTYPE_DOUBLE = 2, + AU_PVTYPE_PTR = 3 +}; + +typedef struct _AUpvlist *AUpvlist; + +#define AU_NULL_PVLIST ((struct _AUpvlist *) 0) + +AFAPI AUpvlist AUpvnew (int maxItems); +AFAPI int AUpvgetmaxitems (AUpvlist); +AFAPI int AUpvfree (AUpvlist); +AFAPI int AUpvsetparam (AUpvlist, int item, int param); +AFAPI int AUpvsetvaltype (AUpvlist, int item, int type); +AFAPI int AUpvsetval (AUpvlist, int item, void *val); +AFAPI int AUpvgetparam (AUpvlist, int item, int *param); +AFAPI int AUpvgetvaltype (AUpvlist, int item, int *type); +AFAPI int AUpvgetval (AUpvlist, int item, void *val); + +#undef AFAPI + +#ifdef __cplusplus +} +#endif + +#endif /* AUPVLIST_H */ + +// file: audiofile.h +/* + Audio File Library + Copyright (C) 1998-2000, 2010-2013 Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + audiofile.h + + This file contains the public interfaces to the Audio File Library. +*/ + +#ifndef AUDIOFILE_H +#define AUDIOFILE_H + +#include <aupvlist.h> +#include <stdint.h> +#include <sys/types.h> + +#define LIBAUDIOFILE_MAJOR_VERSION 0 +#define LIBAUDIOFILE_MINOR_VERSION 3 +#define LIBAUDIOFILE_MICRO_VERSION 6 + +#ifdef __cplusplus +extern "C" { +#endif + +#if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__clang__) +#define AFAPI __attribute__((visibility("default"))) +#else +#define AFAPI +#endif + +typedef struct _AFvirtualfile AFvirtualfile; + +typedef struct _AFfilesetup *AFfilesetup; +typedef struct _AFfilehandle *AFfilehandle; +typedef void (*AFerrfunc)(long, const char *); + +// Define AFframecount and AFfileoffset as 64-bit signed integers. +#if defined(__FreeBSD__) || \ + defined(__DragonFly__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) || \ + defined(__APPLE__) || \ + defined(__sgi) || \ + (defined(__linux__) && defined(__LP64__)) +// BSD and IRIX systems define off_t as a 64-bit signed integer. +// Linux defines off_t as a 64-bit signed integer in LP64 mode. +typedef off_t AFframecount; +typedef off_t AFfileoffset; +#else +// For all other systems, use int64_t. +typedef int64_t AFframecount; +typedef int64_t AFfileoffset; +#endif + +#define AF_NULL_FILESETUP ((struct _AFfilesetup *) 0) +#define AF_NULL_FILEHANDLE ((struct _AFfilehandle *) 0) + +#define AF_ERR_BASE 3000 + +enum +{ + AF_DEFAULT_TRACK = 1001 +}; + +enum +{ + AF_DEFAULT_INST = 2001 +}; + +enum +{ + AF_NUM_UNLIMITED = 99999 +}; + +enum +{ + AF_BYTEORDER_BIGENDIAN = 501, + AF_BYTEORDER_LITTLEENDIAN = 502 +}; + +enum +{ + AF_FILE_UNKNOWN = -1, + AF_FILE_RAWDATA = 0, + AF_FILE_AIFFC = 1, + AF_FILE_AIFF = 2, + AF_FILE_NEXTSND = 3, + AF_FILE_WAVE = 4, + AF_FILE_BICSF = 5, + AF_FILE_IRCAM = AF_FILE_BICSF, + AF_FILE_MPEG1BITSTREAM = 6, /* not implemented */ + AF_FILE_SOUNDDESIGNER1 = 7, /* not implemented */ + AF_FILE_SOUNDDESIGNER2 = 8, /* not implemented */ + AF_FILE_AVR = 9, + AF_FILE_IFF_8SVX = 10, + AF_FILE_SAMPLEVISION = 11, + AF_FILE_VOC = 12, + AF_FILE_NIST_SPHERE = 13, + AF_FILE_SOUNDFONT2 = 14, /* not implemented */ + AF_FILE_CAF = 15, + AF_FILE_FLAC = 16 +}; + +enum +{ + AF_LOOP_MODE_NOLOOP = 0, + AF_LOOP_MODE_FORW = 1, + AF_LOOP_MODE_FORWBAKW = 2 +}; + +enum +{ + AF_SAMPFMT_TWOSCOMP = 401, /* linear two's complement */ + AF_SAMPFMT_UNSIGNED = 402, /* unsigned integer */ + AF_SAMPFMT_FLOAT = 403, /* 32-bit IEEE floating-point */ + AF_SAMPFMT_DOUBLE = 404 /* 64-bit IEEE double-precision floating-point */ +}; + +enum +{ + AF_INST_LOOP_OFF = 0, /* no looping */ + AF_INST_LOOP_CONTINUOUS = 1, /* loop continuously through decay */ + AF_INST_LOOP_SUSTAIN = 3 /* loop during sustain, then continue */ +}; + +enum +{ + AF_INST_MIDI_BASENOTE = 301, + AF_INST_NUMCENTS_DETUNE = 302, + AF_INST_MIDI_LONOTE = 303, + AF_INST_MIDI_HINOTE = 304, + AF_INST_MIDI_LOVELOCITY = 305, + AF_INST_MIDI_HIVELOCITY = 306, + AF_INST_NUMDBS_GAIN = 307, + AF_INST_SUSLOOPID = 308, /* loop id for AIFF sustain loop */ + AF_INST_RELLOOPID = 309, /* loop id for AIFF release loop */ + AF_INST_SAMP_STARTFRAME = 310, /* start sample for this inst */ + AF_INST_SAMP_ENDFRAME = 311, /* end sample for this inst */ + AF_INST_SAMP_MODE = 312, /* looping mode for this inst */ + AF_INST_TRACKID = 313, + AF_INST_NAME = 314, /* name of this inst */ + AF_INST_SAMP_RATE = 315, /* sample rate of this inst's sample */ + AF_INST_PRESETID = 316, /* ID of preset containing this inst */ + AF_INST_PRESET_NAME = 317 /* name of preset containing this inst */ +}; + +enum +{ + AF_MISC_UNRECOGNIZED = 0, /* unrecognized data chunk */ + AF_MISC_COPY = 201, /* copyright string */ + AF_MISC_AUTH = 202, /* author string */ + AF_MISC_NAME = 203, /* name string */ + AF_MISC_ANNO = 204, /* annotation string */ + AF_MISC_APPL = 205, /* application-specific data */ + AF_MISC_MIDI = 206, /* MIDI exclusive data */ + AF_MISC_PCMMAP = 207, /* PCM mapping information (future use) */ + AF_MISC_NeXT = 208, /* misc binary data appended to NeXT header */ + AF_MISC_IRCAM_PEAKAMP = 209, /* peak amplitude information */ + AF_MISC_IRCAM_COMMENT = 210, /* BICSF text comment */ + AF_MISC_COMMENT = 210, /* general text comment */ + + AF_MISC_ICMT = AF_MISC_COMMENT, /* comments chunk (WAVE format) */ + AF_MISC_ICRD = 211, /* creation date (WAVE format) */ + AF_MISC_ISFT = 212 /* software name (WAVE format) */ +}; + +enum +{ + /* supported compression schemes */ + AF_COMPRESSION_UNKNOWN = -1, + AF_COMPRESSION_NONE = 0, + AF_COMPRESSION_G722 = 501, + AF_COMPRESSION_G711_ULAW = 502, + AF_COMPRESSION_G711_ALAW = 503, + + /* Apple proprietary AIFF-C compression schemes (not supported) */ + AF_COMPRESSION_APPLE_ACE2 = 504, + AF_COMPRESSION_APPLE_ACE8 = 505, + AF_COMPRESSION_APPLE_MAC3 = 506, + AF_COMPRESSION_APPLE_MAC6 = 507, + + AF_COMPRESSION_G726 = 517, + AF_COMPRESSION_G728 = 518, + AF_COMPRESSION_DVI_AUDIO = 519, + AF_COMPRESSION_IMA = AF_COMPRESSION_DVI_AUDIO, + AF_COMPRESSION_GSM = 520, + AF_COMPRESSION_FS1016 = 521, + AF_COMPRESSION_DV = 522, + AF_COMPRESSION_MS_ADPCM = 523, + + AF_COMPRESSION_FLAC = 530, + AF_COMPRESSION_ALAC = 540 +}; + +/* tokens for afQuery() -- see the man page for instructions */ +/* level 1 selectors */ +enum +{ + AF_QUERYTYPE_INSTPARAM = 500, + AF_QUERYTYPE_FILEFMT = 501, + AF_QUERYTYPE_COMPRESSION = 502, + AF_QUERYTYPE_COMPRESSIONPARAM = 503, + AF_QUERYTYPE_MISC = 504, + AF_QUERYTYPE_INST = 505, + AF_QUERYTYPE_MARK = 506, + AF_QUERYTYPE_LOOP = 507 +}; + +/* level 2 selectors */ +enum +{ + AF_QUERY_NAME = 600, /* get name (1-3 words) */ + AF_QUERY_DESC = 601, /* get description */ + AF_QUERY_LABEL = 602, /* get 4- or 5-char label */ + AF_QUERY_TYPE = 603, /* get type token */ + AF_QUERY_DEFAULT = 604, /* dflt. value for param */ + AF_QUERY_ID_COUNT = 605, /* get number of ids avail. */ + AF_QUERY_IDS = 606, /* get array of id tokens */ + AF_QUERY_IMPLEMENTED = 613, /* boolean */ + AF_QUERY_TYPE_COUNT = 607, /* get number of types av. */ + AF_QUERY_TYPES = 608, /* get array of types */ + AF_QUERY_NATIVE_SAMPFMT = 609, /* for compression */ + AF_QUERY_NATIVE_SAMPWIDTH = 610, + AF_QUERY_SQUISHFAC = 611, /* 1.0 means variable */ + AF_QUERY_MAX_NUMBER = 612, /* max allowed in file */ + AF_QUERY_SUPPORTED = 613 /* insts, loops, etc., supported? */ +}; + +/* level 2 selectors which have sub-selectors */ +enum +{ + AF_QUERY_TRACKS = 620, + AF_QUERY_CHANNELS = 621, + AF_QUERY_SAMPLE_SIZES = 622, + AF_QUERY_SAMPLE_FORMATS = 623, + AF_QUERY_COMPRESSION_TYPES = 624 +}; + +/* level 3 sub-selectors */ +enum +{ + AF_QUERY_VALUE_COUNT = 650, /* number of values of the above */ + AF_QUERY_VALUES = 651 /* array of those values */ +}; + + +/* + Old Audio File Library error codes. These are still returned by the + AFerrorhandler calls, but are not used by the new digital media library + error reporting routines. See the bottom of this file for the new error + tokens. +*/ + +enum +{ + AF_BAD_NOT_IMPLEMENTED = 0, /* not implemented yet */ + AF_BAD_FILEHANDLE = 1, /* tried to use invalid filehandle */ + AF_BAD_OPEN = 3, /* unix open failed */ + AF_BAD_CLOSE = 4, /* unix close failed */ + AF_BAD_READ = 5, /* unix read failed */ + AF_BAD_WRITE = 6, /* unix write failed */ + AF_BAD_LSEEK = 7, /* unix lseek failed */ + AF_BAD_NO_FILEHANDLE = 8, /* failed to allocate a filehandle struct */ + AF_BAD_ACCMODE = 10, /* unrecognized audio file access mode */ + AF_BAD_NOWRITEACC = 11, /* file not open for writing */ + AF_BAD_NOREADACC = 12, /* file not open for reading */ + AF_BAD_FILEFMT = 13, /* unrecognized audio file format */ + AF_BAD_RATE = 14, /* invalid sample rate */ + AF_BAD_CHANNELS = 15, /* invalid number of channels*/ + AF_BAD_SAMPCNT = 16, /* invalid sample count */ + AF_BAD_WIDTH = 17, /* invalid sample width */ + AF_BAD_SEEKMODE = 18, /* invalid seek mode */ + AF_BAD_NO_LOOPDATA = 19, /* failed to allocate loop struct */ + AF_BAD_MALLOC = 20, /* malloc failed somewhere */ + AF_BAD_LOOPID = 21, + AF_BAD_SAMPFMT = 22, /* bad sample format */ + AF_BAD_FILESETUP = 23, /* bad file setup structure*/ + AF_BAD_TRACKID = 24, /* no track corresponding to id */ + AF_BAD_NUMTRACKS = 25, /* wrong number of tracks for file format */ + AF_BAD_NO_FILESETUP = 26, /* failed to allocate a filesetup struct*/ + AF_BAD_LOOPMODE = 27, /* unrecognized loop mode value */ + AF_BAD_INSTID = 28, /* invalid instrument id */ + AF_BAD_NUMLOOPS = 29, /* bad number of loops */ + AF_BAD_NUMMARKS = 30, /* bad number of markers */ + AF_BAD_MARKID = 31, /* bad marker id */ + AF_BAD_MARKPOS = 32, /* invalid marker position value */ + AF_BAD_NUMINSTS = 33, /* invalid number of instruments */ + AF_BAD_NOAESDATA = 34, + AF_BAD_MISCID = 35, + AF_BAD_NUMMISC = 36, + AF_BAD_MISCSIZE = 37, + AF_BAD_MISCTYPE = 38, + AF_BAD_MISCSEEK = 39, + AF_BAD_STRLEN = 40, /* invalid string length */ + AF_BAD_RATECONV = 45, + AF_BAD_SYNCFILE = 46, + AF_BAD_CODEC_CONFIG = 47, /* improperly configured codec */ + AF_BAD_CODEC_STATE = 48, /* invalid codec state: can't recover */ + AF_BAD_CODEC_LICENSE = 49, /* no license available for codec */ + AF_BAD_CODEC_TYPE = 50, /* unsupported codec type */ + AF_BAD_COMPRESSION = AF_BAD_CODEC_CONFIG, /* for back compat */ + AF_BAD_COMPTYPE = AF_BAD_CODEC_TYPE, /* for back compat */ + + AF_BAD_INSTPTYPE = 51, /* invalid instrument parameter type */ + AF_BAD_INSTPID = 52, /* invalid instrument parameter id */ + AF_BAD_BYTEORDER = 53, + AF_BAD_FILEFMT_PARAM = 54, /* unrecognized file format parameter */ + AF_BAD_COMP_PARAM = 55, /* unrecognized compression parameter */ + AF_BAD_DATAOFFSET = 56, /* bad data offset */ + AF_BAD_FRAMECNT = 57, /* bad frame count */ + AF_BAD_QUERYTYPE = 58, /* bad query type */ + AF_BAD_QUERY = 59, /* bad argument to afQuery() */ + AF_WARNING_CODEC_RATE = 60, /* using 8k instead of codec rate 8012 */ + AF_WARNING_RATECVT = 61, /* warning about rate conversion used */ + + AF_BAD_HEADER = 62, /* failed to parse header */ + AF_BAD_FRAME = 63, /* bad frame number */ + AF_BAD_LOOPCOUNT = 64, /* bad loop count */ + AF_BAD_DMEDIA_CALL = 65, /* error in dmedia subsystem call */ + + /* AIFF/AIFF-C specific errors when parsing file header */ + AF_BAD_AIFF_HEADER = 108, /* failed to parse chunk header */ + AF_BAD_AIFF_FORM = 109, /* failed to parse FORM chunk */ + AF_BAD_AIFF_SSND = 110, /* failed to parse SSND chunk */ + AF_BAD_AIFF_CHUNKID = 111, /* unrecognized AIFF/AIFF-C chunk id */ + AF_BAD_AIFF_COMM = 112, /* failed to parse COMM chunk */ + AF_BAD_AIFF_INST = 113, /* failed to parse INST chunk */ + AF_BAD_AIFF_MARK = 114, /* failed to parse MARK chunk */ + AF_BAD_AIFF_SKIP = 115, /* failed to skip unsupported chunk */ + AF_BAD_AIFF_LOOPMODE = 116 /* unrecognized loop mode (forw, etc)*/ +}; + +/* new error codes which may be retrieved via dmGetError() */ +/* The old error tokens continue to be retrievable via the AFerrorhandler */ +/* AF_ERR_BASE is #defined in dmedia/dmedia.h */ + +enum +{ + AF_ERR_NOT_IMPLEMENTED = 0+AF_ERR_BASE, /* not implemented yet */ + AF_ERR_BAD_FILEHANDLE = 1+AF_ERR_BASE, /* invalid filehandle */ + AF_ERR_BAD_READ = 5+AF_ERR_BASE, /* unix read failed */ + AF_ERR_BAD_WRITE = 6+AF_ERR_BASE, /* unix write failed */ + AF_ERR_BAD_LSEEK = 7+AF_ERR_BASE, /* unix lseek failed */ + AF_ERR_BAD_ACCMODE = 10+AF_ERR_BASE, /* unrecognized audio file access mode */ + AF_ERR_NO_WRITEACC = 11+AF_ERR_BASE, /* file not open for writing */ + AF_ERR_NO_READACC = 12+AF_ERR_BASE, /* file not open for reading */ + AF_ERR_BAD_FILEFMT = 13+AF_ERR_BASE, /* unrecognized audio file format */ + AF_ERR_BAD_RATE = 14+AF_ERR_BASE, /* invalid sample rate */ + AF_ERR_BAD_CHANNELS = 15+AF_ERR_BASE, /* invalid # channels*/ + AF_ERR_BAD_SAMPCNT = 16+AF_ERR_BASE, /* invalid sample count */ + AF_ERR_BAD_WIDTH = 17+AF_ERR_BASE, /* invalid sample width */ + AF_ERR_BAD_SEEKMODE = 18+AF_ERR_BASE, /* invalid seek mode */ + AF_ERR_BAD_LOOPID = 21+AF_ERR_BASE, /* invalid loop id */ + AF_ERR_BAD_SAMPFMT = 22+AF_ERR_BASE, /* bad sample format */ + AF_ERR_BAD_FILESETUP = 23+AF_ERR_BASE, /* bad file setup structure*/ + AF_ERR_BAD_TRACKID = 24+AF_ERR_BASE, /* no track corresponding to id */ + AF_ERR_BAD_NUMTRACKS = 25+AF_ERR_BASE, /* wrong number of tracks for file format */ + AF_ERR_BAD_LOOPMODE = 27+AF_ERR_BASE, /* unrecognized loop mode symbol */ + AF_ERR_BAD_INSTID = 28+AF_ERR_BASE, /* invalid instrument id */ + AF_ERR_BAD_NUMLOOPS = 29+AF_ERR_BASE, /* bad number of loops */ + AF_ERR_BAD_NUMMARKS = 30+AF_ERR_BASE, /* bad number of markers */ + AF_ERR_BAD_MARKID = 31+AF_ERR_BASE, /* bad marker id */ + AF_ERR_BAD_MARKPOS = 32+AF_ERR_BASE, /* invalid marker position value */ + AF_ERR_BAD_NUMINSTS = 33+AF_ERR_BASE, /* invalid number of instruments */ + AF_ERR_BAD_NOAESDATA = 34+AF_ERR_BASE, + AF_ERR_BAD_MISCID = 35+AF_ERR_BASE, + AF_ERR_BAD_NUMMISC = 36+AF_ERR_BASE, + AF_ERR_BAD_MISCSIZE = 37+AF_ERR_BASE, + AF_ERR_BAD_MISCTYPE = 38+AF_ERR_BASE, + AF_ERR_BAD_MISCSEEK = 39+AF_ERR_BASE, + AF_ERR_BAD_STRLEN = 40+AF_ERR_BASE, /* invalid string length */ + AF_ERR_BAD_RATECONV = 45+AF_ERR_BASE, + AF_ERR_BAD_SYNCFILE = 46+AF_ERR_BASE, + AF_ERR_BAD_CODEC_CONFIG = 47+AF_ERR_BASE, /* improperly configured codec */ + AF_ERR_BAD_CODEC_TYPE = 50+AF_ERR_BASE, /* unsupported codec type */ + AF_ERR_BAD_INSTPTYPE = 51+AF_ERR_BASE, /* invalid instrument parameter type */ + AF_ERR_BAD_INSTPID = 52+AF_ERR_BASE, /* invalid instrument parameter id */ + + AF_ERR_BAD_BYTEORDER = 53+AF_ERR_BASE, + AF_ERR_BAD_FILEFMT_PARAM = 54+AF_ERR_BASE, /* unrecognized file format parameter */ + AF_ERR_BAD_COMP_PARAM = 55+AF_ERR_BASE, /* unrecognized compression parameter */ + AF_ERR_BAD_DATAOFFSET = 56+AF_ERR_BASE, /* bad data offset */ + AF_ERR_BAD_FRAMECNT = 57+AF_ERR_BASE, /* bad frame count */ + + AF_ERR_BAD_QUERYTYPE = 58+AF_ERR_BASE, /* bad query type */ + AF_ERR_BAD_QUERY = 59+AF_ERR_BASE, /* bad argument to afQuery() */ + AF_ERR_BAD_HEADER = 62+AF_ERR_BASE, /* failed to parse header */ + AF_ERR_BAD_FRAME = 63+AF_ERR_BASE, /* bad frame number */ + AF_ERR_BAD_LOOPCOUNT = 64+AF_ERR_BASE, /* bad loop count */ + + /* AIFF/AIFF-C specific errors when parsing file header */ + + AF_ERR_BAD_AIFF_HEADER = 66+AF_ERR_BASE, /* failed to parse chunk header */ + AF_ERR_BAD_AIFF_FORM = 67+AF_ERR_BASE, /* failed to parse FORM chunk */ + AF_ERR_BAD_AIFF_SSND = 68+AF_ERR_BASE, /* failed to parse SSND chunk */ + AF_ERR_BAD_AIFF_CHUNKID = 69+AF_ERR_BASE, /* unrecognized AIFF/AIFF-C chunk id */ + AF_ERR_BAD_AIFF_COMM = 70+AF_ERR_BASE, /* failed to parse COMM chunk */ + AF_ERR_BAD_AIFF_INST = 71+AF_ERR_BASE, /* failed to parse INST chunk */ + AF_ERR_BAD_AIFF_MARK = 72+AF_ERR_BASE, /* failed to parse MARK chunk */ + AF_ERR_BAD_AIFF_SKIP = 73+AF_ERR_BASE, /* failed to skip unsupported chunk */ + AF_ERR_BAD_AIFF_LOOPMODE = 74+AF_ERR_BASE /* unrecognized loop mode (forw, etc) */ +}; + + +/* global routines */ +AFAPI AFerrfunc afSetErrorHandler (AFerrfunc efunc); + +/* query routines */ +AFAPI AUpvlist afQuery (int querytype, int arg1, int arg2, int arg3, int arg4); +AFAPI long afQueryLong (int querytype, int arg1, int arg2, int arg3, int arg4); +AFAPI double afQueryDouble (int querytype, int arg1, int arg2, int arg3, int arg4); +AFAPI void *afQueryPointer (int querytype, int arg1, int arg2, int arg3, int arg4); + +/* basic operations on file handles and file setups */ +AFAPI AFfilesetup afNewFileSetup (void); +AFAPI void afFreeFileSetup (AFfilesetup); +AFAPI int afIdentifyFD (int); +AFAPI int afIdentifyNamedFD (int, const char *filename, int *implemented); + +AFAPI AFfilehandle afOpenFile (const char *filename, const char *mode, + AFfilesetup setup); +AFAPI AFfilehandle afOpenVirtualFile (AFvirtualfile *vfile, const char *mode, + AFfilesetup setup); +AFAPI AFfilehandle afOpenFD (int fd, const char *mode, AFfilesetup setup); +AFAPI AFfilehandle afOpenNamedFD (int fd, const char *mode, AFfilesetup setup, + const char *filename); + +AFAPI void afSaveFilePosition (AFfilehandle file); +AFAPI void afRestoreFilePosition (AFfilehandle file); +AFAPI int afSyncFile (AFfilehandle file); +AFAPI int afCloseFile (AFfilehandle file); + +AFAPI void afInitFileFormat (AFfilesetup, int format); +AFAPI int afGetFileFormat (AFfilehandle, int *version); + +/* track */ +AFAPI void afInitTrackIDs (AFfilesetup, const int *trackids, int trackCount); +AFAPI int afGetTrackIDs (AFfilehandle, int *trackids); + +/* track data: reading, writng, seeking, sizing frames */ +AFAPI int afReadFrames (AFfilehandle, int track, void *buffer, int frameCount); +AFAPI int afWriteFrames (AFfilehandle, int track, const void *buffer, int frameCount); +AFAPI AFframecount afSeekFrame (AFfilehandle, int track, AFframecount frameoffset); +AFAPI AFframecount afTellFrame (AFfilehandle, int track); +AFAPI AFfileoffset afGetTrackBytes (AFfilehandle, int track); +AFAPI float afGetFrameSize (AFfilehandle, int track, int expand3to4); +AFAPI float afGetVirtualFrameSize (AFfilehandle, int track, int expand3to4); + +/* track data: AES data */ +/* afInitAESChannelData is obsolete -- use afInitAESChannelDataTo() */ +AFAPI void afInitAESChannelData (AFfilesetup, int track); /* obsolete */ +AFAPI void afInitAESChannelDataTo (AFfilesetup, int track, int willBeData); +AFAPI int afGetAESChannelData (AFfilehandle, int track, unsigned char buf[24]); +AFAPI void afSetAESChannelData (AFfilehandle, int track, unsigned char buf[24]); + +/* track data: byte order */ +AFAPI void afInitByteOrder (AFfilesetup, int track, int byteOrder); +AFAPI int afGetByteOrder (AFfilehandle, int track); +AFAPI int afSetVirtualByteOrder (AFfilehandle, int track, int byteOrder); +AFAPI int afGetVirtualByteOrder (AFfilehandle, int track); + +/* track data: number of channels */ +AFAPI void afInitChannels (AFfilesetup, int track, int nchannels); +AFAPI int afGetChannels (AFfilehandle, int track); +AFAPI int afSetVirtualChannels (AFfilehandle, int track, int channelCount); +AFAPI int afGetVirtualChannels (AFfilehandle, int track); +AFAPI void afSetChannelMatrix (AFfilehandle, int track, double *matrix); + +/* track data: sample format and sample width */ +AFAPI void afInitSampleFormat (AFfilesetup, int track, int sampleFormat, + int sampleWidth); +AFAPI void afGetSampleFormat (AFfilehandle file, int track, int *sampleFormat, + int *sampleWidth); +AFAPI int afSetVirtualSampleFormat (AFfilehandle, int track, + int sampleFormat, int sampleWidth); +AFAPI void afGetVirtualSampleFormat (AFfilehandle, int track, + int *sampleFormat, int *sampleWidth); + +/* track data: sampling rate */ +AFAPI void afInitRate (AFfilesetup, int track, double rate); +AFAPI double afGetRate (AFfilehandle, int track); + +#if 0 +int afSetVirtualRate (AFfilehandle, int track, double rate); +double afGetVirtualRate (AFfilehandle, int track); +#endif + +/* track data: compression */ +AFAPI void afInitCompression (AFfilesetup, int track, int compression); +#if 0 +void afInitCompressionParams (AFfilesetup, int track, int compression + AUpvlist params, int parameterCount); +#endif + +AFAPI int afGetCompression (AFfilehandle, int track); +#if 0 +void afGetCompressionParams (AFfilehandle, int track, int *compression, + AUpvlist params, int parameterCount); + +int afSetVirtualCompression (AFfilesetup, int track, int compression); +void afSetVirtualCompressionParams (AFfilehandle, int track, int compression, + AUpvlist params, int parameterCount); + +int afGetVirtualCompression (AFfilesetup, int track, int compression); +void afGetVirtualCompressionParams (AFfilehandle, int track, int *compression, + AUpvlist params, int parameterCount); +#endif + +/* track data: pcm mapping */ +AFAPI void afInitPCMMapping (AFfilesetup filesetup, int track, + double slope, double intercept, double minClip, double maxClip); +AFAPI void afGetPCMMapping (AFfilehandle file, int track, + double *slope, double *intercept, double *minClip, double *maxClip); +/* NOTE: afSetTrackPCMMapping() is special--it does not set the virtual */ +/* format; it changes what the AF thinks the track format is! Be careful. */ +AFAPI int afSetTrackPCMMapping (AFfilehandle file, int track, + double slope, double intercept, double minClip, double maxClip); +/* NOTE: afSetVirtualPCMMapping() is different from afSetTrackPCMMapping(): */ +/* see comment for afSetTrackPCMMapping(). */ +AFAPI int afSetVirtualPCMMapping (AFfilehandle file, int track, + double slope, double intercept, double minClip, double maxClip); +AFAPI void afGetVirtualPCMMapping (AFfilehandle file, int track, + double *slope, double *intercept, double *minClip, double *maxClip); + +/* track data: data offset within the file */ +/* initialize for raw reading only */ +AFAPI void afInitDataOffset(AFfilesetup, int track, AFfileoffset offset); +AFAPI AFfileoffset afGetDataOffset (AFfilehandle, int track); + +/* track data: count of frames in file */ +AFAPI void afInitFrameCount (AFfilesetup, int track, AFframecount frameCount); +AFAPI AFframecount afGetFrameCount (AFfilehandle file, int track); + +/* loop operations */ +AFAPI void afInitLoopIDs (AFfilesetup, int instid, const int *ids, int nids); +AFAPI int afGetLoopIDs (AFfilehandle, int instid, int loopids[]); +AFAPI void afSetLoopMode (AFfilehandle, int instid, int loop, int mode); +AFAPI int afGetLoopMode (AFfilehandle, int instid, int loopid); +AFAPI int afSetLoopCount (AFfilehandle, int instid, int loop, int count); +AFAPI int afGetLoopCount (AFfilehandle, int instid, int loopid); +AFAPI void afSetLoopStart (AFfilehandle, int instid, int loopid, int markerid); +AFAPI int afGetLoopStart (AFfilehandle, int instid, int loopid); +AFAPI void afSetLoopEnd (AFfilehandle, int instid, int loopid, int markerid); +AFAPI int afGetLoopEnd (AFfilehandle, int instid, int loopid); + +AFAPI int afSetLoopStartFrame (AFfilehandle, int instid, int loop, + AFframecount startFrame); +AFAPI AFframecount afGetLoopStartFrame (AFfilehandle, int instid, int loop); +AFAPI int afSetLoopEndFrame (AFfilehandle, int instid, int loop, + AFframecount startFrame); +AFAPI AFframecount afGetLoopEndFrame (AFfilehandle, int instid, int loop); + +AFAPI void afSetLoopTrack (AFfilehandle, int instid, int loopid, int trackid); +AFAPI int afGetLoopTrack (AFfilehandle, int instid, int loopid); + +/* marker operations */ +AFAPI void afInitMarkIDs (AFfilesetup, int trackid, const int *ids, int nids); +AFAPI int afGetMarkIDs (AFfilehandle file, int trackid, int markids[]); +AFAPI void afSetMarkPosition (AFfilehandle file, int trackid, int markid, + AFframecount markpos); +AFAPI AFframecount afGetMarkPosition (AFfilehandle file, int trackid, int markid); +AFAPI void afInitMarkName (AFfilesetup, int trackid, int marker, const char *name); +AFAPI void afInitMarkComment (AFfilesetup, int trackid, int marker, + const char *comment); +AFAPI char *afGetMarkName (AFfilehandle file, int trackid, int markid); +AFAPI char *afGetMarkComment (AFfilehandle file, int trackid, int markid); + +/* instrument operations */ +AFAPI void afInitInstIDs (AFfilesetup, const int *ids, int nids); +AFAPI int afGetInstIDs (AFfilehandle file, int *instids); +AFAPI void afGetInstParams (AFfilehandle file, int instid, AUpvlist pvlist, + int nparams); +AFAPI void afSetInstParams (AFfilehandle file, int instid, AUpvlist pvlist, + int nparams); +AFAPI long afGetInstParamLong (AFfilehandle file, int instid, int param); +AFAPI void afSetInstParamLong (AFfilehandle file, int instid, int param, long value); + +/* miscellaneous data operations */ +AFAPI void afInitMiscIDs (AFfilesetup, const int *ids, int nids); +AFAPI int afGetMiscIDs (AFfilehandle, int *ids); +AFAPI void afInitMiscType (AFfilesetup, int miscellaneousid, int type); +AFAPI int afGetMiscType (AFfilehandle, int miscellaneousid); +AFAPI void afInitMiscSize (AFfilesetup, int miscellaneousid, int size); +AFAPI int afGetMiscSize (AFfilehandle, int miscellaneousid); +AFAPI int afWriteMisc (AFfilehandle, int miscellaneousid, const void *buf, int bytes); +AFAPI int afReadMisc (AFfilehandle, int miscellaneousid, void *buf, int bytes); +AFAPI int afSeekMisc (AFfilehandle, int miscellaneousid, int offset); + +#undef AFAPI + +#ifdef __cplusplus +} +#endif + +#endif /* AUDIOFILE_H */ + +// file: afinternal.h +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + afinternal.h + + This file defines the internal structures for the Audio File Library. +*/ + +#ifndef AFINTERNAL_H +#define AFINTERNAL_H + +#include <sys/types.h> + +enum status +{ + AF_SUCCEED = 0, + AF_FAIL = -1 +}; + +union AFPVu +{ + long l; + double d; + void *v; +}; + +struct InstParamInfo +{ + int id; + int type; + const char *name; + AFPVu defaultValue; +}; + +struct Loop +{ + int id; + int mode; /* AF_LOOP_MODE_... */ + int count; /* how many times the loop is played */ + int beginMarker, endMarker; + int trackid; +}; + +struct LoopSetup +{ + int id; +}; + +struct Miscellaneous +{ + int id; + int type; + int size; + + void *buffer; + + int position; // offset within the miscellaneous chunk +}; + +struct MiscellaneousSetup +{ + int id; + int type; + int size; +}; + +struct TrackSetup; + +class File; +struct Track; + +enum +{ + _AF_VALID_FILEHANDLE = 38212, + _AF_VALID_FILESETUP = 38213 +}; + +enum +{ + _AF_READ_ACCESS = 1, + _AF_WRITE_ACCESS = 2 +}; + +// The following are tokens for compression parameters in PV lists. +enum +{ + _AF_MS_ADPCM_NUM_COEFFICIENTS = 800, /* type: long */ + _AF_MS_ADPCM_COEFFICIENTS = 801, /* type: array of int16_t[2] */ + _AF_IMA_ADPCM_TYPE = 810, + _AF_IMA_ADPCM_TYPE_WAVE = 1, + _AF_IMA_ADPCM_TYPE_QT = 2, + _AF_CODEC_DATA = 900, // type: pointer + _AF_CODEC_DATA_SIZE = 901 // type: long +}; + +/* NeXT/Sun sampling rate */ +#define _AF_SRATE_CODEC (8012.8210513) + +#endif + +// file: byteorder.h +/* + Audio File Library + Copyright (C) 1998-1999, 2010-2011, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef BYTEORDER_H +#define BYTEORDER_H + + +#include <stdint.h> + +#if WORDS_BIGENDIAN + #define _AF_BYTEORDER_NATIVE (AF_BYTEORDER_BIGENDIAN) +#else + #define _AF_BYTEORDER_NATIVE (AF_BYTEORDER_LITTLEENDIAN) +#endif + +inline uint16_t _af_byteswap_int16 (uint16_t x) +{ + return (x >> 8) | (x << 8); +} + +inline uint32_t _af_byteswap_int32 (uint32_t x) +{ + return ((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24); +} + +inline uint64_t _af_byteswap_int64 (uint64_t x) +{ + return ((x & 0x00000000000000ffULL) << 56) | + ((x & 0x000000000000ff00ULL) << 40) | + ((x & 0x0000000000ff0000ULL) << 24) | + ((x & 0x00000000ff000000ULL) << 8) | + ((x & 0x000000ff00000000ULL) >> 8) | + ((x & 0x0000ff0000000000ULL) >> 24) | + ((x & 0x00ff000000000000ULL) >> 40) | + ((x & 0xff00000000000000ULL) >> 56); +} + +inline float _af_byteswap_float32 (float x) +{ + union + { + uint32_t i; + float f; + } u; + u.f = x; + u.i = _af_byteswap_int32(u.i); + return u.f; +} + +inline double _af_byteswap_float64 (double x) +{ + union + { + uint64_t i; + double f; + } u; + u.f = x; + u.i = _af_byteswap_int64(u.i); + return u.f; +} + +inline uint64_t byteswap(uint64_t value) { return _af_byteswap_int64(value); } +inline int64_t byteswap(int64_t value) { return _af_byteswap_int64(value); } +inline uint32_t byteswap(uint32_t value) { return _af_byteswap_int32(value); } +inline int32_t byteswap(int32_t value) { return _af_byteswap_int32(value); } +inline uint16_t byteswap(uint16_t value) { return _af_byteswap_int16(value); } +inline int16_t byteswap(int16_t value) { return _af_byteswap_int16(value); } + +inline double byteswap(double value) { return _af_byteswap_float64(value); } +inline float byteswap(float value) { return _af_byteswap_float32(value); } + +template <typename T> +T bigToHost(T value) +{ + return _AF_BYTEORDER_NATIVE == AF_BYTEORDER_BIGENDIAN ? value : byteswap(value); +} + +template <typename T> +T littleToHost(T value) +{ + return _AF_BYTEORDER_NATIVE == AF_BYTEORDER_LITTLEENDIAN ? value : byteswap(value); +} + +template <typename T> +T hostToBig(T value) +{ + return _AF_BYTEORDER_NATIVE == AF_BYTEORDER_BIGENDIAN ? value : byteswap(value); +} + +template <typename T> +T hostToLittle(T value) +{ + return _AF_BYTEORDER_NATIVE == AF_BYTEORDER_LITTLEENDIAN ? value : byteswap(value); +} + +#endif + +// file: AudioFormat.h +/* + Audio File Library + Copyright (C) 2010, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef AUDIOFORMAT_H +#define AUDIOFORMAT_H + + +#include <sys/types.h> +#include <string> + +struct PCMInfo +{ + double slope, intercept, minClip, maxClip; +}; + +struct AudioFormat +{ + double sampleRate; /* sampling rate in Hz */ + int sampleFormat; /* AF_SAMPFMT_... */ + int sampleWidth; /* sample width in bits */ + int byteOrder; /* AF_BYTEORDER_... */ + + PCMInfo pcm; /* parameters of PCM data */ + + int channelCount; /* number of channels */ + + int compressionType; /* AF_COMPRESSION_... */ + AUpvlist compressionParams; /* NULL if no compression */ + + bool packed : 1; + + size_t framesPerPacket; + size_t bytesPerPacket; + + size_t bytesPerSample(bool stretch3to4) const; + size_t bytesPerFrame(bool stretch3to4) const; + size_t bytesPerSample() const; + size_t bytesPerFrame() const; + bool isInteger() const; + bool isSigned() const; + bool isUnsigned() const; + bool isFloat() const; + bool isCompressed() const; + bool isUncompressed() const; + bool isPacked() const { return packed; } + bool isByteOrderSignificant() const { return sampleWidth > 8; } + + void computeBytesPerPacketPCM(); + + std::string description() const; +}; + +#endif + +// file: debug.h +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + debug.h + + This header file declares debugging functions for the Audio + File Library. +*/ + +#ifndef DEBUG_H +#define DEBUG_H + +#include <stdint.h> + +void _af_print_filehandle (AFfilehandle filehandle); +void _af_print_tracks (AFfilehandle filehandle); +void _af_print_channel_matrix (double *matrix, int fchans, int vchans); +void _af_print_pvlist (AUpvlist list); + +void _af_print_audioformat (AudioFormat *format); +void _af_print_frame (AFframecount frameno, double *frame, int nchannels, + char *formatstring, int numberwidth, + double slope, double intercept, double minclip, double maxclip); + +#endif + +// file: util.h +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + util.h + + This file contains some general utility functions for the Audio + File Library. +*/ + +#ifndef UTIL_H +#define UTIL_H + +#include <stdint.h> +#include <stdlib.h> + + +struct AudioFormat; + +bool _af_filesetup_ok (AFfilesetup setup); +bool _af_filehandle_ok (AFfilehandle file); + +void *_af_malloc (size_t size); +void *_af_realloc (void *ptr, size_t size); +void *_af_calloc (size_t nmemb, size_t size); +char *_af_strdup (const char *s); + +AUpvlist _af_pv_long (long val); +AUpvlist _af_pv_double (double val); +AUpvlist _af_pv_pointer (void *val); + +bool _af_pv_getlong (AUpvlist pvlist, int param, long *l); +bool _af_pv_getdouble (AUpvlist pvlist, int param, double *d); +bool _af_pv_getptr (AUpvlist pvlist, int param, void **v); + +bool _af_unique_ids (const int *ids, int nids, const char *idname, int iderr); + +float _af_format_frame_size (const AudioFormat *format, bool stretch3to4); +int _af_format_frame_size_uncompressed (const AudioFormat *format, bool stretch3to4); +float _af_format_sample_size (const AudioFormat *format, bool stretch3to4); +int _af_format_sample_size_uncompressed (const AudioFormat *format, bool stretch3to4); + +status _af_set_sample_format (AudioFormat *f, int sampleFormat, int sampleWidth); + +#endif + +// file: units.h +/* + Audio File Library + Copyright (C) 2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + units.h + + This file defines the internal Unit and CompressionUnit + structures for the Audio File Library. +*/ + +#ifndef UNIT_H +#define UNIT_H + + +struct AudioFormat; +class FileModule; + +struct Unit +{ + int fileFormat; /* AF_FILEFMT_... */ + const char *name; /* a 2-3 word name of the file format */ + const char *description; /* a more descriptive name for the format */ + const char *label; /* a 4-character label for the format */ + bool implemented; /* if implemented */ + + AFfilesetup (*completesetup) (AFfilesetup setup); + bool (*recognize) (File *fh); + + int defaultSampleFormat; + int defaultSampleWidth; + + int compressionTypeCount; + const int *compressionTypes; + + int markerCount; + + int instrumentCount; + int loopPerInstrumentCount; + + int instrumentParameterCount; + const InstParamInfo *instrumentParameters; +}; + +struct CompressionUnit +{ + int compressionID; /* AF_COMPRESSION_... */ + bool implemented; + const char *label; /* 4-character (approximately) label */ + const char *shortname; /* short name in English */ + const char *name; /* long name in English */ + double squishFactor; /* compression ratio */ + int nativeSampleFormat; /* AF_SAMPFMT_... */ + int nativeSampleWidth; /* sample width in bits */ + bool needsRebuffer; /* if there are chunk boundary requirements */ + bool multiple_of; /* can accept any multiple of chunksize */ + bool (*fmtok) (AudioFormat *format); + + FileModule *(*initcompress) (Track *track, File *fh, + bool seekok, bool headerless, AFframecount *chunkframes); + FileModule *(*initdecompress) (Track *track, File *fh, + bool seekok, bool headerless, AFframecount *chunkframes); +}; + +#define _AF_NUM_UNITS 17 +#define _AF_NUM_COMPRESSION 7 + +extern const Unit _af_units[_AF_NUM_UNITS]; +extern const CompressionUnit _af_compression[_AF_NUM_COMPRESSION]; + +#endif /* UNIT_H */ + +// file: UUID.h +/* + Copyright (C) 2011, Michael Pruett. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UUID_H +#define UUID_H + +#include <stdint.h> +#include <string> + +struct UUID +{ + uint8_t data[16]; + + bool operator==(const UUID &) const; + bool operator!=(const UUID &) const; + std::string name() const; +}; + +#endif + +// file: Shared.h +/* + Copyright (C) 2010, Michael Pruett. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SHARED_H +#define SHARED_H + +template <typename T> +class Shared +{ +public: + Shared() : m_refCount(0) + { + } + void retain() { m_refCount++; } + void release() { if (--m_refCount == 0) delete static_cast<T *>(this); } + +protected: + ~Shared() + { + } + +private: + int m_refCount; + + Shared(const Shared &); + Shared &operator =(const Shared &); +}; + +template <typename T> +class SharedPtr +{ +public: + SharedPtr() : m_ptr(0) + { + } + SharedPtr(T *ptr) : m_ptr(ptr) + { + if (m_ptr) m_ptr->retain(); + } + SharedPtr(const SharedPtr &p) : m_ptr(p.m_ptr) + { + if (m_ptr) m_ptr->retain(); + } + ~SharedPtr() + { + if (T *p = m_ptr) p->release(); + } + + SharedPtr &operator =(T *ptr) + { + if (m_ptr != ptr) + { + if (ptr) ptr->retain(); + if (m_ptr) m_ptr->release(); + m_ptr = ptr; + } + return *this; + } + SharedPtr &operator =(const SharedPtr &p) + { + if (m_ptr != p.m_ptr) + { + if (p.m_ptr) p.m_ptr->retain(); + if (m_ptr) m_ptr->release(); + m_ptr = p.m_ptr; + } + return *this; + } + + T *get() const { return m_ptr; } + T &operator *() const { return *m_ptr; } + T *operator ->() const { return m_ptr; } + + typedef T *SharedPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_ptr ? &SharedPtr::m_ptr : 0; } + + bool operator !() const { return !m_ptr; } + +private: + T *m_ptr; +}; + +#endif + +// file: Buffer.h +/* + Audio File Library + Copyright (C) 2013 Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef Buffer_h +#define Buffer_h + + +#include <sys/types.h> + +class Buffer : public Shared<Buffer> +{ +public: + Buffer(); + Buffer(size_t size); + Buffer(const void *data, size_t size); + ~Buffer(); + + void *data() { return m_data; } + const void *data() const { return m_data; } + + size_t size() const { return m_size; } + +private: + void *m_data; + size_t m_size; +}; + +#endif + +// file: File.h +/* + Copyright (C) 2010, Michael Pruett. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef FILE_H +#define FILE_H + +#include <sys/types.h> + +typedef struct _AFvirtualfile AFvirtualfile; + +class File : public Shared<File> +{ +public: + enum AccessMode + { + ReadAccess, + WriteAccess + }; + + enum SeekOrigin + { + SeekFromBeginning, + SeekFromCurrent, + SeekFromEnd + }; + + static File *open(const char *path, AccessMode mode); + static File *create(int fd, AccessMode mode); + static File *create(AFvirtualfile *vf, AccessMode mode); + + virtual ~File(); + virtual int close() = 0; + virtual ssize_t read(void *data, size_t nbytes) = 0; + virtual ssize_t write(const void *data, size_t nbytes) = 0; + virtual off_t length() = 0; + virtual off_t seek(off_t offset, SeekOrigin origin) = 0; + virtual off_t tell() = 0; + + bool canSeek(); + + AccessMode accessMode() const { return m_accessMode; } + +private: + AccessMode m_accessMode; + +protected: + File(AccessMode mode) : m_accessMode(mode) { } +}; + +#endif // FILE_H + +// file: FileHandle.h +/* + Audio File Library + Copyright (C) 2010-2011, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef FILEHANDLE_H +#define FILEHANDLE_H + +#include <stdint.h> + +class File; +class Tag; +struct Instrument; +struct Miscellaneous; +struct Track; + +struct _AFfilehandle +{ + static _AFfilehandle *create(int fileFormat); + + int m_valid; // _AF_VALID_FILEHANDLE + int m_access; // _AF_READ_ACCESS or _AF_WRITE_ACCESS + + bool m_seekok; + + File *m_fh; + + char *m_fileName; + + int m_fileFormat; + + int m_trackCount; + Track *m_tracks; + + int m_instrumentCount; + Instrument *m_instruments; + + int m_miscellaneousCount; + Miscellaneous *m_miscellaneous; + +private: + int m_formatByteOrder; + + status copyTracksFromSetup(AFfilesetup setup); + status copyInstrumentsFromSetup(AFfilesetup setup); + status copyMiscellaneousFromSetup(AFfilesetup setup); + +public: + virtual ~_AFfilehandle(); + + virtual int getVersion() { return 0; } + virtual status readInit(AFfilesetup) = 0; + virtual status writeInit(AFfilesetup) = 0; + virtual status update() = 0; + virtual bool isInstrumentParameterValid(AUpvlist, int) { return false; } + + bool checkCanRead(); + bool checkCanWrite(); + + Track *allocateTrack(); + Track *getTrack(int trackID = AF_DEFAULT_TRACK); + Instrument *getInstrument(int instrumentID); + Miscellaneous *getMiscellaneous(int miscellaneousID); + +protected: + _AFfilehandle(); + + status initFromSetup(AFfilesetup setup); + + void setFormatByteOrder(int byteOrder) { m_formatByteOrder = byteOrder; } + + bool readU8(uint8_t *); + bool readS8(int8_t *); + bool readU16(uint16_t *); + bool readS16(int16_t *); + bool readU32(uint32_t *); + bool readS32(int32_t *); + bool readU64(uint64_t *); + bool readS64(int64_t *); + bool readFloat(float *); + bool readDouble(double *); + + bool writeU8(const uint8_t *); + bool writeS8(const int8_t *); + bool writeU16(const uint16_t *); + bool writeS16(const int16_t *); + bool writeU32(const uint32_t *); + bool writeS32(const int32_t *); + bool writeU64(const uint64_t *); + bool writeS64(const int64_t *); + bool writeFloat(const float *); + bool writeDouble(const double *); + + bool readTag(Tag *t); + bool writeTag(const Tag *t); +}; + +#endif + +// file: Instrument.h +/* + Audio File Library + Copyright (C) 2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + Instrument.h + + This file declares routines for dealing with instruments. +*/ + +#ifndef INSTRUMENT_H +#define INSTRUMENT_H + + +struct LoopSetup; +struct Loop; + +struct InstrumentSetup +{ + int id; + + int loopCount; + LoopSetup *loops; + + bool loopSet; + + bool allocateLoops(int count); + void freeLoops(); +}; + +struct Instrument +{ + int id; + + int loopCount; + Loop *loops; + + AFPVu *values; + + Loop *getLoop(int loopID); +}; + +void _af_instparam_get (AFfilehandle file, int instid, AUpvlist pvlist, + int npv, bool forceLong); + +void _af_instparam_set (AFfilehandle file, int instid, AUpvlist pvlist, + int npv); + +int _af_instparam_index_from_id (int fileFormat, int id); + +#endif /* INSTRUMENT_H */ + +// file: Track.h +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + track.h +*/ + +#ifndef TRACK_H +#define TRACK_H + + +class ModuleState; +class PacketTable; +struct Marker; +struct MarkerSetup; + +struct TrackSetup +{ + int id; + + AudioFormat f; + + bool rateSet, sampleFormatSet, sampleWidthSet, byteOrderSet, + channelCountSet, compressionSet, aesDataSet, markersSet, + dataOffsetSet, frameCountSet; + + int markerCount; + MarkerSetup *markers; + + AFfileoffset dataOffset; + AFframecount frameCount; +}; + +struct Track +{ + Track(); + ~Track(); + + int id; /* usually AF_DEFAULT_TRACKID */ + + AudioFormat f, v; /* file and virtual audio formats */ + + SharedPtr<PacketTable> m_packetTable; + + double *channelMatrix; + + int markerCount; + Marker *markers; + + bool hasAESData; /* Is AES nonaudio data present? */ + unsigned char aesData[24]; /* AES nonaudio data */ + + AFframecount totalfframes; /* frameCount */ + AFframecount nextfframe; /* currentFrame */ + AFframecount frames2ignore; + AFfileoffset fpos_first_frame; /* dataStart */ + AFfileoffset fpos_next_frame; + AFfileoffset fpos_after_data; + AFframecount totalvframes; + AFframecount nextvframe; + AFfileoffset data_size; /* trackBytes */ + + SharedPtr<ModuleState> ms; + + double taper, dynamic_range; + bool ratecvt_filter_params_set; + + bool filemodhappy; + + void print(); + + Marker *getMarker(int markerID); + status copyMarkers(TrackSetup *setup); + + void computeTotalFileFrames(); +}; + +#endif + +// file: Marker.h +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef MARKER_H +#define MARKER_H + +struct MarkerSetup +{ + int id; + char *name, *comment; +}; + +struct Marker +{ + short id; + unsigned long position; + char *name; + char *comment; +}; + +struct Track; + +Marker *_af_marker_new (int count); +Marker *_af_marker_find_by_id (Track *track, int id); + +#endif /* MARKER_H */ + +// file: Setup.h +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef SETUP_H +#define SETUP_H + + +struct InstrumentSetup; +struct MiscellaneousSetup; +struct TrackSetup; + +struct _AFfilesetup +{ + int valid; + + int fileFormat; + + bool trackSet, instrumentSet, miscellaneousSet; + + int trackCount; + TrackSetup *tracks; + + int instrumentCount; + InstrumentSetup *instruments; + + int miscellaneousCount; + MiscellaneousSetup *miscellaneous; + + TrackSetup *getTrack(int trackID = AF_DEFAULT_TRACK); + InstrumentSetup *getInstrument(int instrumentID); + MiscellaneousSetup *getMiscellaneous(int miscellaneousID); +}; + +void _af_setup_free_markers (AFfilesetup setup, int trackno); +void _af_setup_free_tracks (AFfilesetup setup); +void _af_setup_free_instruments (AFfilesetup setup); + +AFfilesetup _af_filesetup_copy (const _AFfilesetup *setup, + const _AFfilesetup *defaultSetup, bool copyMarks); + +InstrumentSetup *_af_instsetup_new (int count); + +#endif + +// file: Tag.h +/* + Audio File Library + Copyright (C) 2011, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef TAG_H +#define TAG_H + +#include <assert.h> +#include <stdint.h> +#include <string.h> +#include <string> + +class Tag +{ +public: + Tag() : m_value(0) { } + Tag(uint32_t value) : m_value(value) { } + Tag(const char *s) + { + assert(strlen(s) == 4); + memcpy(&m_value, s, 4); + } + + uint32_t value() const { return m_value; } + + bool operator==(const Tag &t) const { return m_value == t.m_value; } + bool operator!=(const Tag &t) const { return m_value != t.m_value; } + + std::string name() const + { + char s[5]; + memcpy(s, &m_value, 4); + s[4] = '\0'; + return std::string(s); + } + +private: + uint32_t m_value; +}; + +#endif + +// file: PacketTable.h +/* + Audio File Library + Copyright (C) 2013 Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef PacketTable_h +#define PacketTable_h + + +#include <audiofile.h> + +#include <stdint.h> +#include <sys/types.h> +#include <vector> + +class PacketTable : public Shared<PacketTable> +{ +public: + PacketTable(); + PacketTable(int64_t numValidFrames, + int32_t primingFrames, + int32_t remainderFrames); + ~PacketTable(); + + size_t numPackets() const { return m_bytesPerPacket.size(); } + int64_t numValidFrames() const { return m_numValidFrames; } + void setNumValidFrames(int64_t numValidFrames); + int32_t primingFrames() const { return m_primingFrames; } + void setPrimingFrames(int32_t primingFrames); + int32_t remainderFrames() const { return m_remainderFrames; } + void setRemainderFrames(int32_t remainderFrames); + + void append(size_t bytesPerPacket); + size_t bytesPerPacket(size_t packet) const { return m_bytesPerPacket[packet]; } + AFfileoffset startOfPacket(size_t packet) const; + +private: + int64_t m_numValidFrames; + int32_t m_primingFrames; + int32_t m_remainderFrames; + + std::vector<size_t> m_bytesPerPacket; +}; + +#endif + +// file: pcm.h +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + pcm.h + + This file defines various constants for PCM mapping. +*/ + +#ifndef PCM_H +#define PCM_H + +/* + SLOPE_INTN = 2^(N-1) +*/ +#define SLOPE_INT8 (128.0) +#define SLOPE_INT16 (32768.0) +#define SLOPE_INT24 (8388608.0) +#define SLOPE_INT32 (2147483648.0) + +/* + INTERCEPT_U_INTN = 2^(N-1) +*/ +#define INTERCEPT_U_INT8 (128.0) +#define INTERCEPT_U_INT16 (32768.0) +#define INTERCEPT_U_INT24 (8388608.0) +#define INTERCEPT_U_INT32 (2147483648.0) + +/* + MIN_INTN = -(2^(N-1)) +*/ +#define MIN_INT8 (-128.0) +#define MIN_INT16 (-32768.0) +#define MIN_INT24 (-8388608.0) +#define MIN_INT32 (-2147483648.0) + +/* + MAX_INTN = 2^(N-1) - 1 +*/ +#define MAX_INT8 127.0 +#define MAX_INT16 32767.0 +#define MAX_INT24 8388607.0 +#define MAX_INT32 2147483647.0 + +/* + MAX_U_INTN = 2^N - 1 +*/ +#define MAX_U_INT8 255.0 +#define MAX_U_INT16 65535.0 +#define MAX_U_INT24 16777215.0 +#define MAX_U_INT32 4294967295.0 + +extern const PCMInfo _af_default_signed_integer_pcm_mappings[]; +extern const PCMInfo _af_default_unsigned_integer_pcm_mappings[]; +extern const PCMInfo _af_default_float_pcm_mapping; +extern const PCMInfo _af_default_double_pcm_mapping; + +#endif + +// file: g711.h +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * g711.h + * + * u-law, A-law and linear PCM conversions. + */ + +#ifndef G711_H +#define G711_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +/* pcm_val is 2's complement (16-bit range) */ +unsigned char _af_linear2alaw (int pcm_val); + +/* + * alaw2linear() - Convert an A-law value to 16-bit linear PCM + * + */ + +int _af_alaw2linear (unsigned char a_val); + +/* + * linear2ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +/* pcm_val is 2's complement (16-bit range) */ +unsigned char _af_linear2ulaw (int pcm_val); + +/* + * ulaw2linear() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ + +int _af_ulaw2linear (unsigned char u_val); + +#ifdef __cplusplus +} +#endif + +#endif /* G711_H */ + +// file: af_vfs.h +/* + Audio File Library + Copyright (C) 1999, Elliot Lee <sopwith@redhat.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + af_vfs.h + + Virtual file operations for the Audio File Library. +*/ + +#ifndef AUDIOFILE_VFS_H +#define AUDIOFILE_VFS_H + +#include <audiofile.h> +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__clang__) +#define AFAPI __attribute__((visibility("default"))) +#else +#define AFAPI +#endif + +struct _AFvirtualfile +{ + ssize_t (*read) (AFvirtualfile *vfile, void *data, size_t nbytes); + AFfileoffset (*length) (AFvirtualfile *vfile); + ssize_t (*write) (AFvirtualfile *vfile, const void *data, size_t nbytes); + void (*destroy) (AFvirtualfile *vfile); + AFfileoffset (*seek) (AFvirtualfile *vfile, AFfileoffset offset, int is_relative); + AFfileoffset (*tell) (AFvirtualfile *vfile); + + void *closure; +}; + +AFAPI AFvirtualfile *af_virtual_file_new (void); +AFAPI void af_virtual_file_destroy (AFvirtualfile *vfile); + +#undef AFAPI + +#ifdef __cplusplus +} +#endif + +#endif + +// file: Raw.h +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + Raw.h +*/ + +#ifndef RAW_H +#define RAW_H + + +#define _AF_RAW_NUM_COMPTYPES 2 +extern const int _af_raw_compression_types[_AF_RAW_NUM_COMPTYPES]; + +class RawFile : public _AFfilehandle +{ +public: + static bool recognize(File *fh); + static AFfilesetup completeSetup(AFfilesetup); + + status readInit(AFfilesetup setup) OVERRIDE; + status writeInit(AFfilesetup setup) OVERRIDE; + status update() OVERRIDE; +}; + +#endif + +// file: WAVE.h +/* + Audio File Library + Copyright (C) 1998-2000, 2003, 2010-2012, Michael Pruett <michael@68k.org> + Copyright (C) 2002-2003, Davy Durham + Copyright (C) 2000-2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + WAVE.h + + This file contains structures and constants related to the RIFF + WAVE sound file format. +*/ + +#ifndef WAVE_H +#define WAVE_H + +#include <stdint.h> + +#define _AF_WAVE_NUM_INSTPARAMS 7 +extern const InstParamInfo _af_wave_inst_params[_AF_WAVE_NUM_INSTPARAMS]; +#define _AF_WAVE_NUM_COMPTYPES 4 +extern const int _af_wave_compression_types[_AF_WAVE_NUM_COMPTYPES]; + +struct UUID; + +class WAVEFile : public _AFfilehandle +{ +public: + static bool recognize(File *fh); + static AFfilesetup completeSetup(AFfilesetup); + + WAVEFile(); + + status readInit(AFfilesetup) OVERRIDE; + status writeInit(AFfilesetup) OVERRIDE; + + status update() OVERRIDE; + + bool isInstrumentParameterValid(AUpvlist, int) OVERRIDE; + +private: + AFfileoffset m_factOffset; // start of fact (frame count) chunk + AFfileoffset m_miscellaneousOffset; + AFfileoffset m_markOffset; + AFfileoffset m_dataSizeOffset; + + /* + The index into the coefficient array is of type + uint8_t, so we can safely limit msadpcmCoefficients to + be 256 coefficient pairs. + */ + int m_msadpcmNumCoefficients; + int16_t m_msadpcmCoefficients[256][2]; + + status parseFrameCount(const Tag &type, uint32_t size); + status parseFormat(const Tag &type, uint32_t size); + status parseData(const Tag &type, uint32_t size); + status parsePlayList(const Tag &type, uint32_t size); + status parseCues(const Tag &type, uint32_t size); + status parseADTLSubChunk(const Tag &type, uint32_t size); + status parseINFOSubChunk(const Tag &type, uint32_t size); + status parseList(const Tag &type, uint32_t size); + status parseInstrument(const Tag &type, uint32_t size); + + status writeFormat(); + status writeFrameCount(); + status writeMiscellaneous(); + status writeCues(); + status writeData(); + + bool readUUID(UUID *g); + bool writeUUID(const UUID *g); + + bool writeZString(const char *); + size_t zStringLength(const char *); + + void initCompressionParams(); + void initIMACompressionParams(); + void initMSADPCMCompressionParams(); +}; + +#endif + +// file: SampleVision.h +/* + Audio File Library + Copyright (C) 2012, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef SAMPLE_VISION_H +#define SAMPLE_VISION_H + + +class SampleVisionFile : public _AFfilehandle +{ +public: + SampleVisionFile(); + virtual ~SampleVisionFile(); + + static bool recognize(File *fh); + + static AFfilesetup completeSetup(AFfilesetup); + + status readInit(AFfilesetup) OVERRIDE; + status writeInit(AFfilesetup) OVERRIDE; + + status update() OVERRIDE; + +private: + AFfileoffset m_frameCountOffset; + + status parseLoops(); + status parseMarkers(); + status writeTrailer(); + status writeLoops(); + status writeMarkers(); + + void addMiscellaneous(int type, const char *data); +}; + +#endif + +// file: modules/Module.h +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + Copyright (C) 2010, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef MODULE_H +#define MODULE_H + + +#include <vector> + +enum FormatCode +{ + kUndefined = -1, + kInt8, + kInt16, + kInt24, + kInt32, + kFloat, + kDouble, +}; + +class Chunk : public Shared<Chunk> +{ +public: + void *buffer; + size_t frameCount; + AudioFormat f; + bool ownsMemory; + + Chunk() : buffer(NULL), frameCount(0), ownsMemory(false) { } + ~Chunk() + { + deallocate(); + } + void allocate(size_t capacity) + { + deallocate(); + ownsMemory = true; + buffer = ::operator new(capacity); + } + void deallocate() + { + if (ownsMemory) + ::operator delete(buffer); + ownsMemory = false; + buffer = NULL; + } +}; + +class Module : public Shared<Module> +{ +public: + Module(); + virtual ~Module(); + + void setSink(Module *); + void setSource(Module *); + Chunk *inChunk() const { return m_inChunk.get(); } + void setInChunk(Chunk *chunk) { m_inChunk = chunk; } + Chunk *outChunk() const { return m_outChunk.get(); } + void setOutChunk(Chunk *chunk) { m_outChunk = chunk; } + + virtual const char *name() const; + /* + Set format of m_outChunk based on how this module transforms m_inChunk. + */ + virtual void describe(); + /* + Set frame count of m_inChunk to the maximum number of frames needed to + produce frame count of m_outChunk. + */ + virtual void maxPull(); + /* + Set frame count of m_outChunk to the maximum number of frames needed to + produce frame count of m_inChunk. + */ + virtual void maxPush(); + virtual void runPull(); + virtual void reset1() { } + virtual void reset2() { } + virtual void runPush(); + virtual void sync1() { } + virtual void sync2() { } + +protected: + SharedPtr<Chunk> m_inChunk, m_outChunk; + union + { + Module *m_sink; + Module *m_source; + }; + + void pull(size_t frames); + void push(size_t frames); +}; + +/* + _AF_ATOMIC_NVFRAMES is NOT the maximum number of frames a module + can be requested to produce. + + This IS the maximum number of virtual (user) frames that will + be produced or processed per run of the modules. + + Modules can be requested more frames than this because of rate + conversion and rebuffering. +*/ + +#define _AF_ATOMIC_NVFRAMES 1024 + +#endif // MODULE_H + +// file: modules/ModuleState.h +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + Copyright (C) 2010, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef MODULESTATE_H +#define MODULESTATE_H + +#include <vector> + +class FileModule; +class Module; + +class ModuleState : public Shared<ModuleState> +{ +public: + ModuleState(); + virtual ~ModuleState(); + + bool isDirty() const { return m_isDirty; } + void setDirty() { m_isDirty = true; } + status init(AFfilehandle file, Track *track); + status setup(AFfilehandle file, Track *track); + status reset(AFfilehandle file, Track *track); + status sync(AFfilehandle file, Track *track); + + int numModules() const { return m_modules.size(); } + const std::vector<SharedPtr<Module> > &modules() const; + const std::vector<SharedPtr<Chunk> > &chunks() const; + + bool mustUseAtomicNVFrames() const { return true; } + + void print(); + + bool fileModuleHandlesSeeking() const; + +private: + std::vector<SharedPtr<Module> > m_modules; + std::vector<SharedPtr<Chunk> > m_chunks; + bool m_isDirty; + + SharedPtr<FileModule> m_fileModule; + SharedPtr<Module> m_fileRebufferModule; + + status initFileModule(AFfilehandle file, Track *track); + + status arrange(AFfilehandle file, Track *track); + + void addModule(Module *module); + + void addConvertIntToInt(FormatCode input, FormatCode output); + void addConvertIntToFloat(FormatCode input, FormatCode output); + void addConvertFloatToInt(FormatCode input, FormatCode output, + const PCMInfo &inputMapping, const PCMInfo &outputMapping); + void addConvertFloatToFloat(FormatCode input, FormatCode output); +}; + +#endif + +// file: modules/SimpleModule.h +/* + Audio File Library + Copyright (C) 2010, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef SIMPLE_MODULE_H +#define SIMPLE_MODULE_H + + + +#include <algorithm> +#include <cassert> +#include <climits> +#include <functional> + +class SimpleModule : public Module +{ +public: + virtual void runPull() OVERRIDE; + virtual void runPush() OVERRIDE; + virtual void run(Chunk &inChunk, Chunk &outChunk) = 0; +}; + +struct SwapModule : public SimpleModule +{ +public: + virtual const char *name() const OVERRIDE { return "swap"; } + virtual void describe() OVERRIDE + { + m_outChunk->f.byteOrder = m_inChunk->f.byteOrder == AF_BYTEORDER_BIGENDIAN ? + AF_BYTEORDER_LITTLEENDIAN : AF_BYTEORDER_BIGENDIAN; + } + virtual void run(Chunk &inChunk, Chunk &outChunk) OVERRIDE + { + switch (m_inChunk->f.bytesPerSample(false)) + { + case 2: + run<2, int16_t>(inChunk, outChunk); break; + case 3: + run<3, char>(inChunk, outChunk); break; + case 4: + run<4, int32_t>(inChunk, outChunk); break; + case 8: + run<8, int64_t>(inChunk, outChunk); break; + default: + assert(false); break; + } + } + +private: + template <int N, typename T> + void run(Chunk &inChunk, Chunk &outChunk) + { + int sampleCount = inChunk.f.channelCount * inChunk.frameCount; + runSwap<N, T>(reinterpret_cast<const T *>(inChunk.buffer), + reinterpret_cast<T *>(outChunk.buffer), + sampleCount); + } + template <int N, typename T> + void runSwap(const T *input, T *output, int sampleCount) + { + for (int i=0; i<sampleCount; i++) + output[i] = byteswap(input[i]); + } +}; + +template <> +inline void SwapModule::runSwap<3, char>(const char *input, char *output, int count) +{ + for (int i=0; i<count; i++) + { + output[3*i] = input[3*i+2]; + output[3*i+1] = input[3*i+1]; + output[3*i+2] = input[3*i]; + } +} + +template <typename UnaryFunction> +void transform(const void *srcData, void *dstData, size_t count) +{ + typedef typename UnaryFunction::argument_type InputType; + typedef typename UnaryFunction::result_type OutputType; + const InputType *src = reinterpret_cast<const InputType *>(srcData); + OutputType *dst = reinterpret_cast<OutputType *>(dstData); + std::transform(src, src + count, dst, UnaryFunction()); +} + +template <FormatCode> +struct IntTypes; + +template <> +struct IntTypes<kInt8> { typedef int8_t SignedType; typedef uint8_t UnsignedType; }; +template <> +struct IntTypes<kInt16> { typedef int16_t SignedType; typedef uint16_t UnsignedType; }; +template <> +struct IntTypes<kInt24> { typedef int32_t SignedType; typedef uint32_t UnsignedType; }; +template <> +struct IntTypes<kInt32> { typedef int32_t SignedType; typedef uint32_t UnsignedType; }; + +template <FormatCode Format> +struct signConverter +{ + typedef typename IntTypes<Format>::SignedType SignedType; + typedef typename IntTypes<Format>::UnsignedType UnsignedType; + + static const int kScaleBits = (Format + 1) * CHAR_BIT - 1; + static const int kMaxSignedValue = (((1 << (kScaleBits - 1)) - 1) << 1) + 1; + static const int kMinSignedValue = -kMaxSignedValue - 1; + + struct signedToUnsigned : public std::unary_function<SignedType, UnsignedType> + { + UnsignedType operator()(SignedType x) { return x - kMinSignedValue; } + }; + + struct unsignedToSigned : public std::unary_function<SignedType, UnsignedType> + { + SignedType operator()(UnsignedType x) { return x + kMinSignedValue; } + }; +}; + +class ConvertSign : public SimpleModule +{ +public: + ConvertSign(FormatCode format, bool fromSigned) : + m_format(format), + m_fromSigned(fromSigned) + { + } + virtual const char *name() const OVERRIDE { return "sign"; } + virtual void describe() OVERRIDE + { + const int scaleBits = m_inChunk->f.bytesPerSample(false) * CHAR_BIT; + m_outChunk->f.sampleFormat = + m_fromSigned ? AF_SAMPFMT_UNSIGNED : AF_SAMPFMT_TWOSCOMP; + double shift = -(1 << (scaleBits - 1)); + if (m_fromSigned) + shift = -shift; + m_outChunk->f.pcm.intercept += shift; + m_outChunk->f.pcm.minClip += shift; + m_outChunk->f.pcm.maxClip += shift; + } + virtual void run(Chunk &input, Chunk &output) OVERRIDE + { + size_t count = input.frameCount * m_inChunk->f.channelCount; + if (m_fromSigned) + convertSignedToUnsigned(input.buffer, output.buffer, count); + else + convertUnsignedToSigned(input.buffer, output.buffer, count); + } + +private: + FormatCode m_format; + bool m_fromSigned; + + template <FormatCode Format> + static void convertSignedToUnsigned(const void *src, void *dst, size_t count) + { + transform<typename signConverter<Format>::signedToUnsigned>(src, dst, count); + } + void convertSignedToUnsigned(const void *src, void *dst, size_t count) + { + switch (m_format) + { + case kInt8: + convertSignedToUnsigned<kInt8>(src, dst, count); + break; + case kInt16: + convertSignedToUnsigned<kInt16>(src, dst, count); + break; + case kInt24: + convertSignedToUnsigned<kInt24>(src, dst, count); + break; + case kInt32: + convertSignedToUnsigned<kInt32>(src, dst, count); + break; + default: + assert(false); + } + } + + template <FormatCode Format> + static void convertUnsignedToSigned(const void *src, void *dst, size_t count) + { + transform<typename signConverter<Format>::unsignedToSigned>(src, dst, count); + } + void convertUnsignedToSigned(const void *src, void *dst, size_t count) + { + switch (m_format) + { + case kInt8: + convertUnsignedToSigned<kInt8>(src, dst, count); + break; + case kInt16: + convertUnsignedToSigned<kInt16>(src, dst, count); + break; + case kInt24: + convertUnsignedToSigned<kInt24>(src, dst, count); + break; + case kInt32: + convertUnsignedToSigned<kInt32>(src, dst, count); + break; + default: + assert(false); + } + } +}; + +struct Expand3To4Module : public SimpleModule +{ +public: + Expand3To4Module(bool isSigned) : m_isSigned(isSigned) + { + } + virtual const char *name() const OVERRIDE { return "expand3to4"; } + virtual void describe() OVERRIDE + { + m_outChunk->f.packed = false; + } + virtual void run(Chunk &inChunk, Chunk &outChunk) OVERRIDE + { + int count = inChunk.f.channelCount * inChunk.frameCount; + if (m_isSigned) + run<int32_t>(reinterpret_cast<const uint8_t *>(inChunk.buffer), + reinterpret_cast<int32_t *>(outChunk.buffer), + count); + else + run<uint32_t>(reinterpret_cast<const uint8_t *>(inChunk.buffer), + reinterpret_cast<uint32_t *>(outChunk.buffer), + count); + } + +private: + bool m_isSigned; + + template <typename T> + void run(const uint8_t *input, T *output, int sampleCount) + { + for (int i=0; i<sampleCount; i++) + { + T t = +#ifdef WORDS_BIGENDIAN + (input[3*i] << 24) | + (input[3*i+1] << 16) | + input[3*i+2] << 8; +#else + (input[3*i+2] << 24) | + (input[3*i+1] << 16) | + input[3*i] << 8; +#endif + output[i] = t >> 8; + } + } +}; + +struct Compress4To3Module : public SimpleModule +{ +public: + Compress4To3Module(bool isSigned) : m_isSigned(isSigned) + { + } + virtual const char *name() const OVERRIDE { return "compress4to3"; } + virtual void describe() OVERRIDE + { + m_outChunk->f.packed = true; + } + virtual void run(Chunk &inChunk, Chunk &outChunk) OVERRIDE + { + int count = inChunk.f.channelCount * inChunk.frameCount; + if (m_isSigned) + run<int32_t>(inChunk.buffer, outChunk.buffer, count); + else + run<uint32_t>(inChunk.buffer, outChunk.buffer, count); + } + +private: + bool m_isSigned; + + template <typename T> + void run(const void *input, void *output, int count) + { + const T *in = reinterpret_cast<const T *>(input); + uint8_t *out = reinterpret_cast<uint8_t *>(output); + for (int i=0; i<count; i++) + { + uint8_t c0, c1, c2; + extract3(in[i], c0, c1, c2); + out[3*i] = c0; + out[3*i+1] = c1; + out[3*i+2] = c2; + } + } + template <typename T> + void extract3(T in, uint8_t &c0, uint8_t &c1, uint8_t &c2) + { +#ifdef WORDS_BIGENDIAN + c0 = (in >> 16) & 0xff; + c1 = (in >> 8) & 0xff; + c2 = in & 0xff; +#else + c2 = (in >> 16) & 0xff; + c1 = (in >> 8) & 0xff; + c0 = in & 0xff; +#endif + } +}; + +template <typename Arg, typename Result> +struct intToFloat : public std::unary_function<Arg, Result> +{ + Result operator()(Arg x) const { return x; } +}; + +struct ConvertIntToFloat : public SimpleModule +{ + ConvertIntToFloat(FormatCode inFormat, FormatCode outFormat) : + m_inFormat(inFormat), m_outFormat(outFormat) + { + } + virtual const char *name() const OVERRIDE { return "intToFloat"; } + virtual void describe() OVERRIDE + { + m_outChunk->f.sampleFormat = m_outFormat == kDouble ? + AF_SAMPFMT_DOUBLE : AF_SAMPFMT_FLOAT; + m_outChunk->f.sampleWidth = m_outFormat == kDouble ? 64 : 32; + } + virtual void run(Chunk &inChunk, Chunk &outChunk) OVERRIDE + { + const void *src = inChunk.buffer; + void *dst = outChunk.buffer; + int count = inChunk.frameCount * inChunk.f.channelCount; + if (m_outFormat == kFloat) + { + switch (m_inFormat) + { + case kInt8: + run<int8_t, float>(src, dst, count); break; + case kInt16: + run<int16_t, float>(src, dst, count); break; + case kInt24: + case kInt32: + run<int32_t, float>(src, dst, count); break; + default: + assert(false); + } + } + else if (m_outFormat == kDouble) + { + switch (m_inFormat) + { + case kInt8: + run<int8_t, double>(src, dst, count); break; + case kInt16: + run<int16_t, double>(src, dst, count); break; + case kInt24: + case kInt32: + run<int32_t, double>(src, dst, count); break; + default: + assert(false); + } + } + } + +private: + FormatCode m_inFormat, m_outFormat; + + template <typename Arg, typename Result> + static void run(const void *src, void *dst, int count) + { + transform<intToFloat<Arg, Result> >(src, dst, count); + } +}; + +template <typename Arg, typename Result, unsigned shift> +struct lshift : public std::unary_function<Arg, Result> +{ + Result operator()(const Arg &x) const { return x << shift; } +}; + +template <typename Arg, typename Result, unsigned shift> +struct rshift : public std::unary_function<Arg, Result> +{ + Result operator()(const Arg &x) const { return x >> shift; } +}; + +struct ConvertInt : public SimpleModule +{ + ConvertInt(FormatCode inFormat, FormatCode outFormat) : + m_inFormat(inFormat), + m_outFormat(outFormat) + { + assert(isInteger(m_inFormat)); + assert(isInteger(m_outFormat)); + } + virtual const char *name() const OVERRIDE { return "convertInt"; } + virtual void describe() OVERRIDE + { + getDefaultPCMMapping(m_outChunk->f.sampleWidth, + m_outChunk->f.pcm.slope, + m_outChunk->f.pcm.intercept, + m_outChunk->f.pcm.minClip, + m_outChunk->f.pcm.maxClip); + } + virtual void run(Chunk &inChunk, Chunk &outChunk) OVERRIDE + { + const void *src = inChunk.buffer; + void *dst = outChunk.buffer; + size_t count = inChunk.frameCount * inChunk.f.channelCount; + +#define MASK(N, M) (((N)<<3) | (M)) +#define HANDLE(N, M) \ + case MASK(N, M): convertInt<N, M>(src, dst, count); break; + switch (MASK(m_inFormat, m_outFormat)) + { + HANDLE(kInt8, kInt16) + HANDLE(kInt8, kInt24) + HANDLE(kInt8, kInt32) + HANDLE(kInt16, kInt8) + HANDLE(kInt16, kInt24) + HANDLE(kInt16, kInt32) + HANDLE(kInt24, kInt8) + HANDLE(kInt24, kInt16) + HANDLE(kInt24, kInt32) + HANDLE(kInt32, kInt8) + HANDLE(kInt32, kInt16) + HANDLE(kInt32, kInt24) + } +#undef MASK +#undef HANDLE + } + +private: + FormatCode m_inFormat, m_outFormat; + + void getDefaultPCMMapping(int &bits, double &slope, double &intercept, + double &minClip, double &maxClip) + { + bits = (m_outFormat + 1) * CHAR_BIT; + slope = (1LL << (bits - 1)); + intercept = 0; + minClip = -(1 << (bits - 1)); + maxClip = (1LL << (bits - 1)) - 1; + } + + static bool isInteger(FormatCode code) + { + return code >= kInt8 && code <= kInt32; + } + + template <FormatCode Input, FormatCode Output, bool = (Input > Output)> + struct shift; + + template <FormatCode Input, FormatCode Output> + struct shift<Input, Output, true> : + public rshift<typename IntTypes<Input>::SignedType, + typename IntTypes<Output>::SignedType, + (Input - Output) * CHAR_BIT> + { + }; + + template <FormatCode Input, FormatCode Output> + struct shift<Input, Output, false> : + public lshift<typename IntTypes<Input>::SignedType, + typename IntTypes<Output>::SignedType, + (Output - Input) * CHAR_BIT> + { + }; + + template <FormatCode Input, FormatCode Output> + static void convertInt(const void *src, void *dst, int count) + { + transform<shift<Input, Output> >(src, dst, count); + } +}; + +template <typename Arg, typename Result> +struct floatToFloat : public std::unary_function<Arg, Result> +{ + Result operator()(Arg x) const { return x; } +}; + +struct ConvertFloat : public SimpleModule +{ + ConvertFloat(FormatCode inFormat, FormatCode outFormat) : + m_inFormat(inFormat), m_outFormat(outFormat) + { + assert((m_inFormat == kFloat && m_outFormat == kDouble) || + (m_inFormat == kDouble && m_outFormat == kFloat)); + } + virtual const char *name() const OVERRIDE { return "convertFloat"; } + virtual void describe() OVERRIDE + { + switch (m_outFormat) + { + case kFloat: + m_outChunk->f.sampleFormat = AF_SAMPFMT_FLOAT; + m_outChunk->f.sampleWidth = 32; + break; + case kDouble: + m_outChunk->f.sampleFormat = AF_SAMPFMT_DOUBLE; + m_outChunk->f.sampleWidth = 64; + break; + default: + assert(false); + } + } + virtual void run(Chunk &inChunk, Chunk &outChunk) OVERRIDE + { + const void *src = inChunk.buffer; + void *dst = outChunk.buffer; + size_t count = inChunk.frameCount * inChunk.f.channelCount; + + switch (m_outFormat) + { + case kFloat: + transform<floatToFloat<double, float> >(src, dst, count); + break; + case kDouble: + transform<floatToFloat<float, double> >(src, dst, count); + break; + default: + assert(false); + } + } + +private: + FormatCode m_inFormat, m_outFormat; +}; + +struct Clip : public SimpleModule +{ + Clip(FormatCode format, const PCMInfo &outputMapping) : + m_format(format), + m_outputMapping(outputMapping) + { + } + virtual const char *name() const OVERRIDE { return "clip"; } + virtual void describe() OVERRIDE + { + m_outChunk->f.pcm = m_outputMapping; + } + virtual void run(Chunk &inChunk, Chunk &outChunk) OVERRIDE + { + const void *src = inChunk.buffer; + void *dst = outChunk.buffer; + int count = inChunk.frameCount * inChunk.f.channelCount; + + switch (m_format) + { + case kInt8: + run<int8_t>(src, dst, count); break; + case kInt16: + run<int16_t>(src, dst, count); break; + case kInt24: + case kInt32: + run<int32_t>(src, dst, count); break; + case kFloat: + run<float>(src, dst, count); break; + case kDouble: + run<double>(src, dst, count); break; + default: + assert(false); + } + } + +private: + FormatCode m_format; + PCMInfo m_outputMapping; + + template <typename T> + void run(const void *srcData, void *dstData, int count) + { + const T minValue = m_outputMapping.minClip; + const T maxValue = m_outputMapping.maxClip; + + const T *src = reinterpret_cast<const T *>(srcData); + T *dst = reinterpret_cast<T *>(dstData); + + for (int i=0; i<count; i++) + { + T t = src[i]; + t = std::min(t, maxValue); + t = std::max(t, minValue); + dst[i] = t; + } + } +}; + +struct ConvertFloatToIntClip : public SimpleModule +{ + ConvertFloatToIntClip(FormatCode inputFormat, FormatCode outputFormat, + const PCMInfo &inputMapping, const PCMInfo &outputMapping) : + m_inputFormat(inputFormat), + m_outputFormat(outputFormat), + m_inputMapping(inputMapping), + m_outputMapping(outputMapping) + { + assert(m_inputFormat == kFloat || m_inputFormat == kDouble); + assert(m_outputFormat == kInt8 || + m_outputFormat == kInt16 || + m_outputFormat == kInt24 || + m_outputFormat == kInt32); + } + virtual const char *name() const OVERRIDE { return "convertPCMMapping"; } + virtual void describe() OVERRIDE + { + m_outChunk->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + m_outChunk->f.sampleWidth = (m_outputFormat + 1) * CHAR_BIT; + m_outChunk->f.pcm = m_outputMapping; + } + virtual void run(Chunk &inChunk, Chunk &outChunk) OVERRIDE + { + const void *src = inChunk.buffer; + void *dst = outChunk.buffer; + int count = inChunk.frameCount * inChunk.f.channelCount; + + if (m_inputFormat == kFloat) + { + switch (m_outputFormat) + { + case kInt8: + run<float, int8_t>(src, dst, count); break; + case kInt16: + run<float, int16_t>(src, dst, count); break; + case kInt24: + case kInt32: + run<float, int32_t>(src, dst, count); break; + default: + assert(false); + } + } + else if (m_inputFormat == kDouble) + { + switch (m_outputFormat) + { + case kInt8: + run<double, int8_t>(src, dst, count); break; + case kInt16: + run<double, int16_t>(src, dst, count); break; + case kInt24: + case kInt32: + run<double, int32_t>(src, dst, count); break; + default: + assert(false); + } + } + } + +private: + FormatCode m_inputFormat, m_outputFormat; + PCMInfo m_inputMapping, m_outputMapping; + + template <typename Input, typename Output> + void run(const void *srcData, void *dstData, int count) + { + const Input *src = reinterpret_cast<const Input *>(srcData); + Output *dst = reinterpret_cast<Output *>(dstData); + + double m = m_outputMapping.slope / m_inputMapping.slope; + double b = m_outputMapping.intercept - m * m_inputMapping.intercept; + double minValue = m_outputMapping.minClip; + double maxValue = m_outputMapping.maxClip; + + for (int i=0; i<count; i++) + { + double t = m * src[i] + b; + t = std::min(t, maxValue); + t = std::max(t, minValue); + dst[i] = static_cast<Output>(t); + } + } +}; + +struct ApplyChannelMatrix : public SimpleModule +{ +public: + ApplyChannelMatrix(FormatCode format, bool isReading, + int inChannels, int outChannels, + double minClip, double maxClip, const double *matrix); + virtual ~ApplyChannelMatrix(); + virtual const char *name() const OVERRIDE; + virtual void describe() OVERRIDE; + virtual void run(Chunk &inChunk, Chunk &outChunk) OVERRIDE; + +private: + FormatCode m_format; + int m_inChannels, m_outChannels; + double m_minClip, m_maxClip; + double *m_matrix; + + void initDefaultMatrix(); + template <typename T> + void run(const void *input, void *output, int frameCount); +}; + +struct Transform : public SimpleModule +{ +public: + Transform(FormatCode format, + const PCMInfo &inputMapping, + const PCMInfo &outputMapping) : + m_format(format), + m_inputMapping(inputMapping), + m_outputMapping(outputMapping) + { + assert(m_format == kFloat || m_format == kDouble); + } + virtual const char *name() const OVERRIDE { return "transform"; } + virtual void describe() OVERRIDE + { + m_outChunk->f.pcm = m_outputMapping; + } + virtual void run(Chunk &inChunk, Chunk &outChunk) OVERRIDE + { + int count = inChunk.frameCount * inChunk.f.channelCount; + if (m_format == kFloat) + run<float>(inChunk.buffer, outChunk.buffer, count); + else if (m_format == kDouble) + run<double>(inChunk.buffer, outChunk.buffer, count); + else + assert(false); + } + +private: + FormatCode m_format; + PCMInfo m_inputMapping, m_outputMapping; + + template <typename T> + void run(const void *srcData, void *dstData, int count) + { + const T *src = reinterpret_cast<const T *>(srcData); + T *dst = reinterpret_cast<T *>(dstData); + + double m = m_outputMapping.slope / m_inputMapping.slope; + double b = m_outputMapping.intercept - m * m_inputMapping.intercept; + + for (int i=0; i<count; i++) + dst[i] = m * src[i] + b; + } +}; + +#endif // SIMPLE_MODULE_H + +// file: modules/FileModule.h +/* + Audio File Library + Copyright (C) 2010-2012, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef FILE_MODULE_H +#define FILE_MODULE_H + + +class FileModule : public Module +{ +public: + virtual bool handlesSeeking() const { return false; } + + virtual int bufferSize() const; + +protected: + enum Mode { Compress, Decompress }; + FileModule(Mode, Track *, File *fh, bool canSeek); + + Mode mode() const { return m_mode; } + bool canSeek() const { return m_canSeek; } + + ssize_t read(void *data, size_t nbytes); + ssize_t write(const void *data, size_t nbytes); + off_t seek(off_t offset); + off_t tell(); + off_t length(); + +private: + Mode m_mode; + +protected: + Track *m_track; + + void reportReadError(AFframecount framesRead, AFframecount framesRequested); + void reportWriteError(AFframecount framesWritten, AFframecount framesRequested); + +private: + File *m_fh; + bool m_canSeek; +}; + +#endif // FILE_MODULE_H + +// file: modules/RebufferModule.h +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + Copyright (C) 2010, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef REBUFFER_MODULE_H +#define REBUFFER_MODULE_H + + +class RebufferModule : public Module +{ +public: + enum Direction + { + FixedToVariable, + VariableToFixed + }; + + RebufferModule(Direction, int bytesPerFrame, int numFrames, bool multipleOf); + virtual ~RebufferModule(); + + virtual const char *name() const OVERRIDE { return "rebuffer"; } + + virtual void maxPull() OVERRIDE; + virtual void maxPush() OVERRIDE; + + virtual void runPull() OVERRIDE; + virtual void reset1() OVERRIDE; + virtual void reset2() OVERRIDE; + virtual void runPush() OVERRIDE; + virtual void sync1() OVERRIDE; + virtual void sync2() OVERRIDE; + +private: + Direction m_direction; + int m_bytesPerFrame; + int m_numFrames; + bool m_multipleOf; // buffer to multiple of m_numFrames + bool m_eof; // end of input stream reached + bool m_sentShortChunk; // end of input stream indicated + char *m_buffer; + int m_offset; + char *m_savedBuffer; + int m_savedOffset; + + void initFixedToVariable(); + void initVariableToFixed(); +}; + +#endif // REBUFFER_MODULE_H + +// file: modules/BlockCodec.h +/* + Audio File Library + Copyright (C) 2013 Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +// BlockCodec is a base class for codecs with fixed-size packets. + +#ifndef BlockCodec_h +#define BlockCodec_h + + +class BlockCodec : public FileModule +{ +public: + virtual void runPull() OVERRIDE; + virtual void reset1() OVERRIDE; + virtual void reset2() OVERRIDE; + virtual void runPush() OVERRIDE; + virtual void sync1() OVERRIDE; + virtual void sync2() OVERRIDE; + +protected: + int m_bytesPerPacket, m_framesPerPacket; + AFframecount m_framesToIgnore; + AFfileoffset m_savedPositionNextFrame; + AFframecount m_savedNextFrame; + + BlockCodec(Mode, Track *, File *, bool canSeek); + + virtual int decodeBlock(const uint8_t *encoded, int16_t *decoded) = 0; + virtual int encodeBlock(const int16_t *decoded, uint8_t *encoded) = 0; +}; + +#endif + +// file: modules/BlockCodec.cpp +/* + Audio File Library + Copyright (C) 2013 Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + + + +#include <assert.h> + +BlockCodec::BlockCodec(Mode mode, Track *track, File *fh, bool canSeek) : + FileModule(mode, track, fh, canSeek), + m_bytesPerPacket(-1), + m_framesPerPacket(-1), + m_framesToIgnore(-1), + m_savedPositionNextFrame(-1), + m_savedNextFrame(-1) +{ + m_framesPerPacket = track->f.framesPerPacket; + m_bytesPerPacket = track->f.bytesPerPacket; +} + +void BlockCodec::runPull() +{ + AFframecount framesToRead = m_outChunk->frameCount; + AFframecount framesRead = 0; + + assert(framesToRead % m_framesPerPacket == 0); + int blockCount = framesToRead / m_framesPerPacket; + + // Read the compressed data. + ssize_t bytesRead = read(m_inChunk->buffer, m_bytesPerPacket * blockCount); + int blocksRead = bytesRead >= 0 ? bytesRead / m_bytesPerPacket : 0; + + // Decompress into m_outChunk. + for (int i=0; i<blocksRead; i++) + { + decodeBlock(static_cast<const uint8_t *>(m_inChunk->buffer) + i * m_bytesPerPacket, + static_cast<int16_t *>(m_outChunk->buffer) + i * m_framesPerPacket * m_track->f.channelCount); + + framesRead += m_framesPerPacket; + } + + m_track->nextfframe += framesRead; + + assert(tell() == m_track->fpos_next_frame); + + if (framesRead < framesToRead) + reportReadError(framesRead, framesToRead); + + m_outChunk->frameCount = framesRead; +} + +void BlockCodec::reset1() +{ + AFframecount nextTrackFrame = m_track->nextfframe; + m_track->nextfframe = (nextTrackFrame / m_framesPerPacket) * + m_framesPerPacket; + + m_framesToIgnore = nextTrackFrame - m_track->nextfframe; +} + +void BlockCodec::reset2() +{ + m_track->fpos_next_frame = m_track->fpos_first_frame + + m_bytesPerPacket * (m_track->nextfframe / m_framesPerPacket); + m_track->frames2ignore += m_framesToIgnore; + + assert(m_track->nextfframe % m_framesPerPacket == 0); +} + +void BlockCodec::runPush() +{ + AFframecount framesToWrite = m_inChunk->frameCount; + int channelCount = m_inChunk->f.channelCount; + + int blockCount = (framesToWrite + m_framesPerPacket - 1) / m_framesPerPacket; + for (int i=0; i<blockCount; i++) + { + encodeBlock(static_cast<const int16_t *>(m_inChunk->buffer) + i * m_framesPerPacket * channelCount, + static_cast<uint8_t *>(m_outChunk->buffer) + i * m_bytesPerPacket); + } + + ssize_t bytesWritten = write(m_outChunk->buffer, m_bytesPerPacket * blockCount); + ssize_t blocksWritten = bytesWritten >= 0 ? bytesWritten / m_bytesPerPacket : 0; + AFframecount framesWritten = std::min((AFframecount) blocksWritten * m_framesPerPacket, framesToWrite); + + m_track->nextfframe += framesWritten; + m_track->totalfframes = m_track->nextfframe; + + assert(tell() == m_track->fpos_next_frame); + + if (framesWritten < framesToWrite) + reportWriteError(framesWritten, framesToWrite); +} + +void BlockCodec::sync1() +{ + m_savedPositionNextFrame = m_track->fpos_next_frame; + m_savedNextFrame = m_track->nextfframe; +} + +void BlockCodec::sync2() +{ + assert(tell() == m_track->fpos_next_frame); + m_track->fpos_after_data = tell(); + m_track->fpos_next_frame = m_savedPositionNextFrame; + m_track->nextfframe = m_savedNextFrame; +} + +// file: modules/FileModule.cpp +/* + Audio File Library + Copyright (C) 2010-2012, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + + + +#include <errno.h> +#include <string.h> + +FileModule::FileModule(Mode mode, Track *track, File *fh, bool canSeek) : + m_mode(mode), + m_track(track), + m_fh(fh), + m_canSeek(canSeek) +{ + track->fpos_next_frame = track->fpos_first_frame; + track->frames2ignore = 0; +} + +ssize_t FileModule::read(void *data, size_t nbytes) +{ + ssize_t bytesRead = m_fh->read(data, nbytes); + if (bytesRead > 0) + { + m_track->fpos_next_frame += bytesRead; + } + return bytesRead; +} + +ssize_t FileModule::write(const void *data, size_t nbytes) +{ + ssize_t bytesWritten = m_fh->write(data, nbytes); + if (bytesWritten > 0) + { + m_track->fpos_next_frame += bytesWritten; + m_track->data_size += bytesWritten; + } + return bytesWritten; +} + +off_t FileModule::seek(off_t offset) +{ + return m_fh->seek(offset, File::SeekFromBeginning); +} + +off_t FileModule::tell() +{ + return m_fh->tell(); +} + +off_t FileModule::length() +{ + return m_fh->length(); +} + +void FileModule::reportReadError(AFframecount framesRead, + AFframecount framesToRead) +{ + // Report error if we haven't already. + if (!m_track->filemodhappy) + return; + + _af_error(AF_BAD_READ, + "file missing data -- read %jd frames, should be %jd", + static_cast<intmax_t>(m_track->nextfframe), + static_cast<intmax_t>(m_track->totalfframes)); + m_track->filemodhappy = false; +} + +void FileModule::reportWriteError(AFframecount framesWritten, + AFframecount framesToWrite) +{ + // Report error if we haven't already. + if (!m_track->filemodhappy) + return; + + if (framesWritten < 0) + { + // Signal I/O error. + _af_error(AF_BAD_WRITE, + "unable to write data (%s) -- wrote %jd out of %jd frames", + strerror(errno), + static_cast<intmax_t>(m_track->nextfframe), + static_cast<intmax_t>(m_track->nextfframe + framesToWrite)); + } + else + { + // Signal disk full error. + _af_error(AF_BAD_WRITE, + "unable to write data (disk full) -- " + "wrote %jd out of %jd frames", + static_cast<intmax_t>(m_track->nextfframe + framesWritten), + static_cast<intmax_t>(m_track->nextfframe + framesToWrite)); + } + + m_track->filemodhappy = false; +} + +int FileModule::bufferSize() const +{ + if (mode() == Compress) + return outChunk()->frameCount * inChunk()->f.bytesPerFrame(true); + else + return inChunk()->frameCount * outChunk()->f.bytesPerFrame(true); +} + +// file: modules/G711.h +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + Copyright (C) 2010, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + g711.h +*/ + +#ifndef MODULES_G711_H +#define MODULES_G711_H + + +class File; +class FileModule; +struct AudioFormat; +struct Track; + +bool _af_g711_format_ok (AudioFormat *f); + +FileModule *_AFg711initcompress (Track *, File *, bool canSeek, + bool headerless, AFframecount *chunkframes); + +FileModule *_AFg711initdecompress (Track *, File *, bool canSeek, + bool headerless, AFframecount *chunkframes); + +#endif /* MODULES_G711_H */ + +// file: modules/G711.cpp +/* + Audio File Library + Copyright (C) 2000-2001, Silicon Graphics, Inc. + Copyright (C) 2010-2013, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + + +#include <assert.h> + + + +static void ulaw2linear_buf (const uint8_t *ulaw, int16_t *linear, int nsamples) +{ + for (int i=0; i < nsamples; i++) + linear[i] = _af_ulaw2linear(ulaw[i]); +} + +static void linear2ulaw_buf (const int16_t *linear, uint8_t *ulaw, int nsamples) +{ + for (int i=0; i < nsamples; i++) + ulaw[i] = _af_linear2ulaw(linear[i]); +} + +static void alaw2linear_buf (const uint8_t *alaw, int16_t *linear, int nsamples) +{ + for (int i=0; i < nsamples; i++) + linear[i] = _af_alaw2linear(alaw[i]); +} + +static void linear2alaw_buf (const int16_t *linear, uint8_t *alaw, int nsamples) +{ + for (int i=0; i < nsamples; i++) + alaw[i] = _af_linear2alaw(linear[i]); +} + +bool _af_g711_format_ok (AudioFormat *f) +{ + if (f->sampleFormat != AF_SAMPFMT_TWOSCOMP || f->sampleWidth != 16) + { + _af_error(AF_BAD_COMPRESSION, + "G.711 compression requires 16-bit signed integer format"); + return false; + } + + if (f->byteOrder != _AF_BYTEORDER_NATIVE) + { + _af_error(AF_BAD_COMPRESSION, + "G.711 compression requires native byte order"); + return false; + } + + return true; +} + +class G711 : public FileModule +{ +public: + static G711 *createCompress(Track *trk, File *fh, bool canSeek, + bool headerless, AFframecount *chunkframes); + static G711 *createDecompress(Track *trk, File *fh, bool canSeek, + bool headerless, AFframecount *chunkframes); + + virtual const char *name() const OVERRIDE + { + return mode() == Compress ? "g711compress" : "g711decompress"; + } + virtual void describe() OVERRIDE; + virtual void runPull() OVERRIDE; + virtual void reset2() OVERRIDE; + virtual void runPush() OVERRIDE; + virtual void sync1() OVERRIDE; + virtual void sync2() OVERRIDE; + +private: + G711(Mode mode, Track *track, File *fh, bool canSeek); + + AFfileoffset m_savedPositionNextFrame; + AFframecount m_savedNextFrame; +}; + +G711::G711(Mode mode, Track *track, File *fh, bool canSeek) : + FileModule(mode, track, fh, canSeek), + m_savedPositionNextFrame(-1), + m_savedNextFrame(-1) +{ + if (mode == Decompress) + track->f.compressionParams = AU_NULL_PVLIST; +} + +G711 *G711::createCompress(Track *track, File *fh, + bool canSeek, bool headerless, AFframecount *chunkframes) +{ + return new G711(Compress, track, fh, canSeek); +} + +void G711::runPush() +{ + AFframecount framesToWrite = m_inChunk->frameCount; + AFframecount samplesToWrite = m_inChunk->frameCount * m_inChunk->f.channelCount; + int framesize = m_inChunk->f.channelCount; + + assert(m_track->f.compressionType == AF_COMPRESSION_G711_ULAW || + m_track->f.compressionType == AF_COMPRESSION_G711_ALAW); + + /* Compress frames into i->outc. */ + + if (m_track->f.compressionType == AF_COMPRESSION_G711_ULAW) + linear2ulaw_buf(static_cast<const int16_t *>(m_inChunk->buffer), + static_cast<uint8_t *>(m_outChunk->buffer), samplesToWrite); + else + linear2alaw_buf(static_cast<const int16_t *>(m_inChunk->buffer), + static_cast<uint8_t *>(m_outChunk->buffer), samplesToWrite); + + /* Write the compressed data. */ + + ssize_t bytesWritten = write(m_outChunk->buffer, framesize * framesToWrite); + AFframecount framesWritten = bytesWritten >= 0 ? bytesWritten / framesize : 0; + + if (framesWritten != framesToWrite) + reportWriteError(framesWritten, framesToWrite); + + m_track->nextfframe += framesWritten; + m_track->totalfframes = m_track->nextfframe; + + assert(!canSeek() || (tell() == m_track->fpos_next_frame)); +} + +void G711::sync1() +{ + m_savedPositionNextFrame = m_track->fpos_next_frame; + m_savedNextFrame = m_track->nextfframe; +} + +void G711::sync2() +{ + /* sanity check. */ + assert(!canSeek() || (tell() == m_track->fpos_next_frame)); + + /* We can afford to do an lseek just in case because sync2 is rare. */ + m_track->fpos_after_data = tell(); + + m_track->fpos_next_frame = m_savedPositionNextFrame; + m_track->nextfframe = m_savedNextFrame; +} + +void G711::describe() +{ + if (mode() == Compress) + { + m_outChunk->f.compressionType = m_track->f.compressionType; + } + else + { + m_outChunk->f.byteOrder = _AF_BYTEORDER_NATIVE; + m_outChunk->f.compressionType = AF_COMPRESSION_NONE; + } +} + +G711 *G711::createDecompress(Track *track, File *fh, + bool canSeek, bool headerless, AFframecount *chunkframes) +{ + return new G711(Decompress, track, fh, canSeek); +} + +void G711::runPull() +{ + AFframecount framesToRead = m_outChunk->frameCount; + AFframecount samplesToRead = m_outChunk->frameCount * m_outChunk->f.channelCount; + int framesize = m_outChunk->f.channelCount; + + /* Read the compressed frames. */ + + ssize_t bytesRead = read(m_inChunk->buffer, framesize * framesToRead); + AFframecount framesRead = bytesRead >= 0 ? bytesRead / framesize : 0; + + /* Decompress into i->outc. */ + + if (m_track->f.compressionType == AF_COMPRESSION_G711_ULAW) + ulaw2linear_buf(static_cast<const uint8_t *>(m_inChunk->buffer), + static_cast<int16_t *>(m_outChunk->buffer), samplesToRead); + else + alaw2linear_buf(static_cast<const uint8_t *>(m_inChunk->buffer), + static_cast<int16_t *>(m_outChunk->buffer), samplesToRead); + + m_track->nextfframe += framesRead; + assert(!canSeek() || (tell() == m_track->fpos_next_frame)); + + /* + If we got EOF from read, then we return the actual amount read. + + Complain only if there should have been more frames in the file. + */ + + if (m_track->totalfframes != -1 && framesRead != framesToRead) + reportReadError(framesRead, framesToRead); + + m_outChunk->frameCount = framesRead; +} + +void G711::reset2() +{ + int framesize = m_inChunk->f.channelCount; + + m_track->fpos_next_frame = m_track->fpos_first_frame + + framesize * m_track->nextfframe; + + m_track->frames2ignore = 0; +} + +FileModule *_AFg711initcompress(Track *track, File *fh, bool canSeek, + bool headerless, AFframecount *chunkFrames) +{ + return G711::createCompress(track, fh, canSeek, headerless, chunkFrames); +} + +FileModule *_AFg711initdecompress(Track *track, File *fh, bool canSeek, + bool headerless, AFframecount *chunkFrames) +{ + return G711::createDecompress(track, fh, canSeek, headerless, chunkFrames); +} + +// file: modules/Module.cpp +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + Copyright (C) 2010, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + + + +Module::Module() : + m_sink(NULL) +{ +} + +Module::~Module() +{ +} + +void Module::setSink(Module *module) { m_sink = module; } +void Module::setSource(Module *module) { m_source = module; } + +const char *Module::name() const { return ""; } + +void Module::describe() +{ +} + +void Module::maxPull() +{ + m_inChunk->frameCount = m_outChunk->frameCount; +} + +void Module::maxPush() +{ + m_outChunk->frameCount = m_inChunk->frameCount; +} + +void Module::runPull() +{ +} + +void Module::runPush() +{ +} + +void Module::pull(size_t frames) +{ + m_inChunk->frameCount = frames; + m_source->runPull(); +} + +void Module::push(size_t frames) +{ + m_outChunk->frameCount = frames; + m_sink->runPush(); +} + +// file: modules/ModuleState.cpp +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + Copyright (C) 2010-2013, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + + + +#include <algorithm> +#include <cassert> +#include <cmath> +#include <functional> +#include <stdio.h> + +ModuleState::ModuleState() : + m_isDirty(true) +{ +} + +ModuleState::~ModuleState() +{ +} + +status ModuleState::initFileModule(AFfilehandle file, Track *track) +{ + const CompressionUnit *unit = _af_compression_unit_from_id(track->f.compressionType); + if (!unit) + return AF_FAIL; + + // Validate compression format and parameters. + if (!unit->fmtok(&track->f)) + return AF_FAIL; + + if (file->m_seekok && + file->m_fh->seek(track->fpos_first_frame, File::SeekFromBeginning) != + track->fpos_first_frame) + { + _af_error(AF_BAD_LSEEK, "unable to position file handle at beginning of sound data"); + return AF_FAIL; + } + + AFframecount chunkFrames; + if (file->m_access == _AF_READ_ACCESS) + m_fileModule = unit->initdecompress(track, file->m_fh, file->m_seekok, + file->m_fileFormat == AF_FILE_RAWDATA, &chunkFrames); + else + m_fileModule = unit->initcompress(track, file->m_fh, file->m_seekok, + file->m_fileFormat == AF_FILE_RAWDATA, &chunkFrames); + + if (unit->needsRebuffer) + { + assert(unit->nativeSampleFormat == AF_SAMPFMT_TWOSCOMP); + + RebufferModule::Direction direction = + file->m_access == _AF_WRITE_ACCESS ? + RebufferModule::VariableToFixed : RebufferModule::FixedToVariable; + + m_fileRebufferModule = new RebufferModule(direction, + track->f.bytesPerFrame(false), chunkFrames, + unit->multiple_of); + } + + track->filemodhappy = true; + + return AF_SUCCEED; +} + +status ModuleState::init(AFfilehandle file, Track *track) +{ + if (initFileModule(file, track) == AF_FAIL) + return AF_FAIL; + + return AF_SUCCEED; +} + +bool ModuleState::fileModuleHandlesSeeking() const +{ + return m_fileModule->handlesSeeking(); +} + +status ModuleState::setup(AFfilehandle file, Track *track) +{ + AFframecount fframepos = std::llrint((long double)track->nextvframe * track->f.sampleRate / track->v.sampleRate); + bool isReading = file->m_access == _AF_READ_ACCESS; + + if (!track->v.isUncompressed()) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, + "library does not support compression in virtual format yet"); + return AF_FAIL; + } + + if (arrange(file, track) == AF_FAIL) + return AF_FAIL; + + track->filemodhappy = true; + int maxbufsize = 0; + if (isReading) + { + m_chunks.back()->frameCount = _AF_ATOMIC_NVFRAMES; + for (int i=m_modules.size() - 1; i >= 0; i--) + { + SharedPtr<Chunk> inChunk = m_chunks[i]; + SharedPtr<Chunk> outChunk = m_chunks[i+1]; + int bufsize = outChunk->frameCount * outChunk->f.bytesPerFrame(true); + if (bufsize > maxbufsize) + maxbufsize = bufsize; + if (i != 0) + m_modules[i]->setSource(m_modules[i-1].get()); + m_modules[i]->maxPull(); + } + + if (!track->filemodhappy) + return AF_FAIL; + int bufsize = m_fileModule->bufferSize(); + if (bufsize > maxbufsize) + maxbufsize = bufsize; + } + else + { + m_chunks.front()->frameCount = _AF_ATOMIC_NVFRAMES; + for (size_t i=0; i<m_modules.size(); i++) + { + SharedPtr<Chunk> inChunk = m_chunks[i]; + SharedPtr<Chunk> outChunk = m_chunks[i+1]; + int bufsize = inChunk->frameCount * inChunk->f.bytesPerFrame(true); + if (bufsize > maxbufsize) + maxbufsize = bufsize; + if (i != m_modules.size() - 1) + m_modules[i]->setSink(m_modules[i+1].get()); + m_modules[i]->maxPush(); + } + + if (!track->filemodhappy) + return AF_FAIL; + + int bufsize = m_fileModule->bufferSize(); + if (bufsize > maxbufsize) + maxbufsize = bufsize; + } + + for (size_t i=0; i<m_chunks.size(); i++) + { + if ((isReading && i==m_chunks.size() - 1) || (!isReading && i==0)) + continue; + m_chunks[i]->allocate(maxbufsize); + } + + if (isReading) + { + if (track->totalfframes == -1) + track->totalvframes = -1; + else + track->totalvframes = std::llrint((long double)track->totalfframes * + (track->v.sampleRate / track->f.sampleRate)); + + track->nextfframe = fframepos; + track->nextvframe = std::llrint((long double)fframepos * track->v.sampleRate / track->f.sampleRate); + + m_isDirty = false; + + if (reset(file, track) == AF_FAIL) + return AF_FAIL; + } + else + { + track->nextvframe = track->totalvframes = + (AFframecount) (fframepos * track->v.sampleRate / track->f.sampleRate); + m_isDirty = false; + } + + return AF_SUCCEED; +} + +const std::vector<SharedPtr<Module> > &ModuleState::modules() const +{ + return m_modules; +} + +const std::vector<SharedPtr<Chunk> > &ModuleState::chunks() const +{ + return m_chunks; +} + +status ModuleState::reset(AFfilehandle file, Track *track) +{ + track->filemodhappy = true; + for (std::vector<SharedPtr<Module> >::reverse_iterator i=m_modules.rbegin(); + i != m_modules.rend(); ++i) + (*i)->reset1(); + track->frames2ignore = 0; + if (!track->filemodhappy) + return AF_FAIL; + for (std::vector<SharedPtr<Module> >::iterator i=m_modules.begin(); + i != m_modules.end(); ++i) + (*i)->reset2(); + if (!track->filemodhappy) + return AF_FAIL; + return AF_SUCCEED; +} + +status ModuleState::sync(AFfilehandle file, Track *track) +{ + track->filemodhappy = true; + for (std::vector<SharedPtr<Module> >::reverse_iterator i=m_modules.rbegin(); + i != m_modules.rend(); ++i) + (*i)->sync1(); + if (!track->filemodhappy) + return AF_FAIL; + for (std::vector<SharedPtr<Module> >::iterator i=m_modules.begin(); + i != m_modules.end(); ++i) + (*i)->sync2(); + return AF_SUCCEED; +} + +static const PCMInfo * const intmappings[6] = +{ + &_af_default_signed_integer_pcm_mappings[1], + &_af_default_signed_integer_pcm_mappings[2], + &_af_default_signed_integer_pcm_mappings[3], + &_af_default_signed_integer_pcm_mappings[4], + NULL, + NULL +}; + +static FormatCode getFormatCode(const AudioFormat &format) +{ + if (format.sampleFormat == AF_SAMPFMT_FLOAT) + return kFloat; + if (format.sampleFormat == AF_SAMPFMT_DOUBLE) + return kDouble; + if (format.isInteger()) + { + switch (format.bytesPerSample(false)) + { + case 1: return kInt8; + case 2: return kInt16; + case 3: return kInt24; + case 4: return kInt32; + } + } + + /* NOTREACHED */ + assert(false); + return kUndefined; +} + +static bool isInteger(FormatCode code) { return code >= kInt8 && code <= kInt32; } +static bool isFloat(FormatCode code) { return code >= kFloat && code <= kDouble; } + +static bool isTrivialIntMapping(const AudioFormat &format, FormatCode code) +{ + return intmappings[code] != NULL && + format.pcm.slope == intmappings[code]->slope && + format.pcm.intercept == intmappings[code]->intercept; +} + +static bool isTrivialIntClip(const AudioFormat &format, FormatCode code) +{ + return intmappings[code] != NULL && + format.pcm.minClip == intmappings[code]->minClip && + format.pcm.maxClip == intmappings[code]->maxClip; +} + +status ModuleState::arrange(AFfilehandle file, Track *track) +{ + bool isReading = file->m_access == _AF_READ_ACCESS; + AudioFormat in, out; + if (isReading) + { + in = track->f; + out = track->v; + } + else + { + in = track->v; + out = track->f; + } + + FormatCode infc = getFormatCode(in); + FormatCode outfc = getFormatCode(out); + if (infc == kUndefined || outfc == kUndefined) + return AF_FAIL; + + m_chunks.clear(); + m_chunks.push_back(new Chunk()); + m_chunks.back()->f = in; + + m_modules.clear(); + + if (isReading) + { + addModule(m_fileModule.get()); + addModule(m_fileRebufferModule.get()); + } + + // Convert to native byte order. + if (in.byteOrder != _AF_BYTEORDER_NATIVE) + { + size_t bytesPerSample = in.bytesPerSample(!isReading); + if (bytesPerSample > 1 && in.compressionType == AF_COMPRESSION_NONE) + addModule(new SwapModule()); + else + in.byteOrder = _AF_BYTEORDER_NATIVE; + } + + // Handle 24-bit integer input format. + if (in.isInteger() && in.bytesPerSample(false) == 3) + { + if (isReading || in.compressionType != AF_COMPRESSION_NONE) + addModule(new Expand3To4Module(in.isSigned())); + } + + // Make data signed. + if (in.isUnsigned()) + addModule(new ConvertSign(infc, false)); + + in.pcm = m_chunks.back()->f.pcm; + + // Reverse the unsigned shift for output. + if (out.isUnsigned()) + { + const double shift = intmappings[outfc]->minClip; + out.pcm.intercept += shift; + out.pcm.minClip += shift; + out.pcm.maxClip += shift; + } + + // Clip input samples if necessary. + if (in.pcm.minClip < in.pcm.maxClip && !isTrivialIntClip(in, infc)) + addModule(new Clip(infc, in.pcm)); + + bool alreadyClippedOutput = false; + bool alreadyTransformedOutput = false; + // Perform range transformation if input and output PCM mappings differ. + bool transforming = (in.pcm.slope != out.pcm.slope || + in.pcm.intercept != out.pcm.intercept) && + !(isTrivialIntMapping(in, infc) && + isTrivialIntMapping(out, outfc)); + + // Range transformation requires input to be floating-point. + if (isInteger(infc) && transforming) + { + if (infc == kInt32 || outfc == kDouble || outfc == kInt32) + { + addConvertIntToFloat(infc, kDouble); + infc = kDouble; + } + else + { + addConvertIntToFloat(infc, kFloat); + infc = kFloat; + } + } + + if (transforming && infc == kDouble && isFloat(outfc)) + addModule(new Transform(infc, in.pcm, out.pcm)); + + // Add format conversion if needed. + if (isInteger(infc) && isInteger(outfc)) + addConvertIntToInt(infc, outfc); + else if (isInteger(infc) && isFloat(outfc)) + addConvertIntToFloat(infc, outfc); + else if (isFloat(infc) && isInteger(outfc)) + { + addConvertFloatToInt(infc, outfc, in.pcm, out.pcm); + alreadyClippedOutput = true; + alreadyTransformedOutput = true; + } + else if (isFloat(infc) && isFloat(outfc)) + addConvertFloatToFloat(infc, outfc); + + if (transforming && !alreadyTransformedOutput && infc != kDouble) + addModule(new Transform(outfc, in.pcm, out.pcm)); + + if (in.channelCount != out.channelCount) + addModule(new ApplyChannelMatrix(outfc, isReading, + in.channelCount, out.channelCount, + in.pcm.minClip, in.pcm.maxClip, + track->channelMatrix)); + + // Perform clipping if necessary. + if (!alreadyClippedOutput) + { + if (out.pcm.minClip < out.pcm.maxClip && !isTrivialIntClip(out, outfc)) + addModule(new Clip(outfc, out.pcm)); + } + + // Make data unsigned if necessary. + if (out.isUnsigned()) + addModule(new ConvertSign(outfc, true)); + + // Handle 24-bit integer output format. + if (out.isInteger() && out.bytesPerSample(false) == 3) + { + if (!isReading || out.compressionType != AF_COMPRESSION_NONE) + addModule(new Compress4To3Module(out.isSigned())); + } + + if (out.byteOrder != _AF_BYTEORDER_NATIVE) + { + size_t bytesPerSample = out.bytesPerSample(isReading); + if (bytesPerSample > 1 && out.compressionType == AF_COMPRESSION_NONE) + addModule(new SwapModule()); + else + out.byteOrder = _AF_BYTEORDER_NATIVE; + } + + if (!isReading) + { + addModule(m_fileRebufferModule.get()); + addModule(m_fileModule.get()); + } + + return AF_SUCCEED; +} + +void ModuleState::addModule(Module *module) +{ + if (!module) + return; + + m_modules.push_back(module); + module->setInChunk(m_chunks.back().get()); + Chunk *chunk = new Chunk(); + chunk->f = m_chunks.back()->f; + m_chunks.push_back(chunk); + module->setOutChunk(chunk); + module->describe(); +} + +void ModuleState::addConvertIntToInt(FormatCode input, FormatCode output) +{ + if (input == output) + return; + + assert(isInteger(input)); + assert(isInteger(output)); + addModule(new ConvertInt(input, output)); +} + +void ModuleState::addConvertIntToFloat(FormatCode input, FormatCode output) +{ + addModule(new ConvertIntToFloat(input, output)); +} + +void ModuleState::addConvertFloatToInt(FormatCode input, FormatCode output, + const PCMInfo &inputMapping, const PCMInfo &outputMapping) +{ + addModule(new ConvertFloatToIntClip(input, output, inputMapping, outputMapping)); +} + +void ModuleState::addConvertFloatToFloat(FormatCode input, FormatCode output) +{ + if (input == output) + return; + + assert((input == kFloat && output == kDouble) || + (input == kDouble && output == kFloat)); + addModule(new ConvertFloat(input, output)); +} + +void ModuleState::print() +{ + fprintf(stderr, "modules:\n"); + for (size_t i=0; i<m_modules.size(); i++) + fprintf(stderr, " %s (%p) in %p out %p\n", + m_modules[i]->name(), m_modules[i].get(), + m_modules[i]->inChunk(), + m_modules[i]->outChunk()); + fprintf(stderr, "chunks:\n"); + for (size_t i=0; i<m_chunks.size(); i++) + fprintf(stderr, " %p %s\n", + m_chunks[i].get(), + m_chunks[i]->f.description().c_str()); +} + +// file: modules/MSADPCM.h +/* + Audio File Library + Copyright (C) 2001, Silicon Graphics, Inc. + Copyright (C) 2010, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + msadpcm.h + + This module declares the interface for the Microsoft ADPCM + compression module. +*/ + +#ifndef MSADPCM_H +#define MSADPCM_H + + +class File; +class FileModule; +struct AudioFormat; +struct Track; + +bool _af_ms_adpcm_format_ok (AudioFormat *f); + +FileModule *_af_ms_adpcm_init_decompress(Track *, File *, + bool canSeek, bool headerless, AFframecount *chunkframes); + +FileModule *_af_ms_adpcm_init_compress(Track *, File *, + bool canSeek, bool headerless, AFframecount *chunkframes); + +#endif + +// file: modules/MSADPCM.cpp +/* + Audio File Library + Copyright (C) 2010-2013, Michael Pruett <michael@68k.org> + Copyright (C) 2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + This module implements Microsoft ADPCM compression. +*/ + + +#include <assert.h> +#include <cstdlib> +#include <limits> +#include <string.h> + + +struct ms_adpcm_state +{ + uint8_t predictorIndex; + int delta; + int16_t sample1, sample2; + + ms_adpcm_state() + { + predictorIndex = 0; + delta = 16; + sample1 = 0; + sample2 = 0; + } +}; + +class MSADPCM : public BlockCodec +{ +public: + static MSADPCM *createDecompress(Track *, File *, bool canSeek, + bool headerless, AFframecount *chunkFrames); + static MSADPCM *createCompress(Track *, File *, bool canSeek, + bool headerless, AFframecount *chunkFrames); + + virtual ~MSADPCM(); + + bool initializeCoefficients(); + + virtual const char *name() const OVERRIDE + { + return mode() == Compress ? "ms_adpcm_compress" : "ms_adpcm_decompress"; + } + virtual void describe() OVERRIDE; + +private: + // m_coefficients is an array of m_numCoefficients ADPCM coefficient pairs. + int m_numCoefficients; + int16_t m_coefficients[256][2]; + + ms_adpcm_state *m_state; + + MSADPCM(Mode mode, Track *track, File *fh, bool canSeek); + + int decodeBlock(const uint8_t *encoded, int16_t *decoded) OVERRIDE; + int encodeBlock(const int16_t *decoded, uint8_t *encoded) OVERRIDE; + void choosePredictorForBlock(const int16_t *decoded); +}; + +static inline int clamp(int x, int low, int high) +{ + if (x < low) return low; + if (x > high) return high; + return x; +} + +static const int16_t adaptationTable[] = +{ + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 +}; + +// Compute a linear PCM value from the given differential coded value. +static int16_t decodeSample(ms_adpcm_state &state, + uint8_t code, const int16_t *coefficient) +{ + int linearSample = (state.sample1 * coefficient[0] + + state.sample2 * coefficient[1]) >> 8; + + linearSample += ((code & 0x08) ? (code - 0x10) : code) * state.delta; + + linearSample = clamp(linearSample, MIN_INT16, MAX_INT16); + + int delta = (state.delta * adaptationTable[code]) >> 8; + if (delta < 16) + delta = 16; + + state.delta = delta; + state.sample2 = state.sample1; + state.sample1 = linearSample; + + return static_cast<int16_t>(linearSample); +} + +// Compute a differential coded value from the given linear PCM sample. +static uint8_t encodeSample(ms_adpcm_state &state, int16_t sample, + const int16_t *coefficient) +{ + int predictor = (state.sample1 * coefficient[0] + + state.sample2 * coefficient[1]) >> 8; + int code = sample - predictor; + int bias = state.delta / 2; + if (code < 0) + bias = -bias; + code = (code + bias) / state.delta; + code = clamp(code, -8, 7) & 0xf; + + predictor += ((code & 0x8) ? (code - 0x10) : code) * state.delta; + + state.sample2 = state.sample1; + state.sample1 = clamp(predictor, MIN_INT16, MAX_INT16); + state.delta = (adaptationTable[code] * state.delta) >> 8; + if (state.delta < 16) + state.delta = 16; + return code; +} + +// Decode one block of MS ADPCM data. +int MSADPCM::decodeBlock(const uint8_t *encoded, int16_t *decoded) +{ + ms_adpcm_state decoderState[2]; + ms_adpcm_state *state[2]; + + int channelCount = m_track->f.channelCount; + + // Calculate the number of bytes needed for decoded data. + int outputLength = m_framesPerPacket * sizeof (int16_t) * channelCount; + + state[0] = &decoderState[0]; + if (channelCount == 2) + state[1] = &decoderState[1]; + else + state[1] = &decoderState[0]; + + // Initialize block predictor. + for (int i=0; i<channelCount; i++) + { + state[i]->predictorIndex = *encoded++; + assert(state[i]->predictorIndex < m_numCoefficients); + } + + // Initialize delta. + for (int i=0; i<channelCount; i++) + { + state[i]->delta = (encoded[1]<<8) | encoded[0]; + encoded += sizeof (uint16_t); + } + + // Initialize first two samples. + for (int i=0; i<channelCount; i++) + { + state[i]->sample1 = (encoded[1]<<8) | encoded[0]; + encoded += sizeof (uint16_t); + } + + for (int i=0; i<channelCount; i++) + { + state[i]->sample2 = (encoded[1]<<8) | encoded[0]; + encoded += sizeof (uint16_t); + } + + const int16_t *coefficient[2] = + { + m_coefficients[state[0]->predictorIndex], + m_coefficients[state[1]->predictorIndex] + }; + + for (int i=0; i<channelCount; i++) + *decoded++ = state[i]->sample2; + + for (int i=0; i<channelCount; i++) + *decoded++ = state[i]->sample1; + + /* + The first two samples have already been 'decoded' in + the block header. + */ + int samplesRemaining = (m_framesPerPacket - 2) * m_track->f.channelCount; + + while (samplesRemaining > 0) + { + uint8_t code; + int16_t newSample; + + code = *encoded >> 4; + newSample = decodeSample(*state[0], code, coefficient[0]); + *decoded++ = newSample; + + code = *encoded & 0x0f; + newSample = decodeSample(*state[1], code, coefficient[1]); + *decoded++ = newSample; + + encoded++; + samplesRemaining -= 2; + } + + return outputLength; +} + +int MSADPCM::encodeBlock(const int16_t *decoded, uint8_t *encoded) +{ + choosePredictorForBlock(decoded); + + int channelCount = m_track->f.channelCount; + + // Encode predictor. + for (int c=0; c<channelCount; c++) + *encoded++ = m_state[c].predictorIndex; + + // Encode delta. + for (int c=0; c<channelCount; c++) + { + *encoded++ = m_state[c].delta & 0xff; + *encoded++ = m_state[c].delta >> 8; + } + + // Enccode first two samples. + for (int c=0; c<channelCount; c++) + m_state[c].sample2 = *decoded++; + + for (int c=0; c<channelCount; c++) + m_state[c].sample1 = *decoded++; + + for (int c=0; c<channelCount; c++) + { + *encoded++ = m_state[c].sample1 & 0xff; + *encoded++ = m_state[c].sample1 >> 8; + } + + for (int c=0; c<channelCount; c++) + { + *encoded++ = m_state[c].sample2 & 0xff; + *encoded++ = m_state[c].sample2 >> 8; + } + + ms_adpcm_state *state[2] = { &m_state[0], &m_state[channelCount - 1] }; + const int16_t *coefficient[2] = + { + m_coefficients[state[0]->predictorIndex], + m_coefficients[state[1]->predictorIndex] + }; + + int samplesRemaining = (m_framesPerPacket - 2) * m_track->f.channelCount; + while (samplesRemaining > 0) + { + uint8_t code1 = encodeSample(*state[0], *decoded++, coefficient[0]); + uint8_t code2 = encodeSample(*state[1], *decoded++, coefficient[1]); + + *encoded++ = (code1 << 4) | code2; + samplesRemaining -= 2; + } + + return m_bytesPerPacket; +} + +void MSADPCM::choosePredictorForBlock(const int16_t *decoded) +{ + const int kPredictorSampleLength = 3; + + int channelCount = m_track->f.channelCount; + + for (int c=0; c<channelCount; c++) + { + int bestPredictorIndex = 0; + int bestPredictorError = std::numeric_limits<int>::max(); + for (int k=0; k<m_numCoefficients; k++) + { + int a0 = m_coefficients[k][0]; + int a1 = m_coefficients[k][1]; + + int currentPredictorError = 0; + for (int i=2; i<2+kPredictorSampleLength; i++) + { + int error = std::abs(decoded[i*channelCount + c] - + ((a0 * decoded[(i-1)*channelCount + c] + + a1 * decoded[(i-2)*channelCount + c]) >> 8)); + currentPredictorError += error; + } + + currentPredictorError /= 4 * kPredictorSampleLength; + + if (currentPredictorError < bestPredictorError) + { + bestPredictorError = currentPredictorError; + bestPredictorIndex = k; + } + + if (!currentPredictorError) + break; + } + + if (bestPredictorError < 16) + bestPredictorError = 16; + + m_state[c].predictorIndex = bestPredictorIndex; + m_state[c].delta = bestPredictorError; + } +} + +void MSADPCM::describe() +{ + m_outChunk->f.byteOrder = _AF_BYTEORDER_NATIVE; + m_outChunk->f.compressionType = AF_COMPRESSION_NONE; + m_outChunk->f.compressionParams = AU_NULL_PVLIST; +} + +MSADPCM::MSADPCM(Mode mode, Track *track, File *fh, bool canSeek) : + BlockCodec(mode, track, fh, canSeek), + m_numCoefficients(0), + m_state(NULL) +{ + m_state = new ms_adpcm_state[m_track->f.channelCount]; +} + +MSADPCM::~MSADPCM() +{ + delete [] m_state; +} + +bool MSADPCM::initializeCoefficients() +{ + AUpvlist pv = m_track->f.compressionParams; + + long l; + if (_af_pv_getlong(pv, _AF_MS_ADPCM_NUM_COEFFICIENTS, &l)) + { + m_numCoefficients = l; + } + else + { + _af_error(AF_BAD_CODEC_CONFIG, "number of coefficients not set"); + return false; + } + + void *v; + if (_af_pv_getptr(pv, _AF_MS_ADPCM_COEFFICIENTS, &v)) + { + memcpy(m_coefficients, v, m_numCoefficients * 2 * sizeof (int16_t)); + } + else + { + _af_error(AF_BAD_CODEC_CONFIG, "coefficient array not set"); + return false; + } + + return true; +} + +MSADPCM *MSADPCM::createDecompress(Track *track, File *fh, + bool canSeek, bool headerless, AFframecount *chunkFrames) +{ + assert(fh->tell() == track->fpos_first_frame); + + MSADPCM *msadpcm = new MSADPCM(Decompress, track, fh, canSeek); + + if (!msadpcm->initializeCoefficients()) + { + delete msadpcm; + return NULL; + } + + *chunkFrames = msadpcm->m_framesPerPacket; + + return msadpcm; +} + +MSADPCM *MSADPCM::createCompress(Track *track, File *fh, + bool canSeek, bool headerless, AFframecount *chunkFrames) +{ + assert(fh->tell() == track->fpos_first_frame); + + MSADPCM *msadpcm = new MSADPCM(Compress, track, fh, canSeek); + + if (!msadpcm->initializeCoefficients()) + { + delete msadpcm; + return NULL; + } + + *chunkFrames = msadpcm->m_framesPerPacket; + + return msadpcm; +} + +bool _af_ms_adpcm_format_ok (AudioFormat *f) +{ + if (f->channelCount != 1 && f->channelCount != 2) + { + _af_error(AF_BAD_COMPRESSION, + "MS ADPCM compression requires 1 or 2 channels"); + return false; + } + + if (f->sampleFormat != AF_SAMPFMT_TWOSCOMP || f->sampleWidth != 16) + { + _af_error(AF_BAD_COMPRESSION, + "MS ADPCM compression requires 16-bit signed integer format"); + return false; + } + + if (f->byteOrder != _AF_BYTEORDER_NATIVE) + { + _af_error(AF_BAD_COMPRESSION, + "MS ADPCM compression requires native byte order"); + return false; + } + + return true; +} + +FileModule *_af_ms_adpcm_init_decompress (Track *track, File *fh, + bool canSeek, bool headerless, AFframecount *chunkFrames) +{ + return MSADPCM::createDecompress(track, fh, canSeek, headerless, chunkFrames); +} + +FileModule *_af_ms_adpcm_init_compress (Track *track, File *fh, + bool canSeek, bool headerless, AFframecount *chunkFrames) +{ + return MSADPCM::createCompress(track, fh, canSeek, headerless, chunkFrames); +} + +// file: modules/PCM.h +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + Copyright (C) 2010, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + PCM.h +*/ + +#ifndef MODULES_PCM_H +#define MODULES_PCM_H + + +class File; +class FileModule; +struct AudioFormat; +struct Track; + +bool _af_pcm_format_ok (AudioFormat *f); + +FileModule *_AFpcminitcompress(Track *, File *, bool seekok, + bool headerless, AFframecount *chunkframes); + +FileModule *_AFpcminitdecompress(Track *, File *, bool seekok, + bool headerless, AFframecount *chunkframes); + +#endif /* MODULES_PCM_H */ + +// file: modules/PCM.cpp +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + Copyright (C) 2010, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + PCM.cpp - read and file write module for uncompressed data +*/ + + +#include <assert.h> +#include <math.h> + + +bool _af_pcm_format_ok (AudioFormat *f) +{ + assert(!isnan(f->pcm.slope)); + assert(!isnan(f->pcm.intercept)); + assert(!isnan(f->pcm.minClip)); + assert(!isnan(f->pcm.maxClip)); + + return true; +} + +class PCM : public FileModule +{ +public: + static PCM *createCompress(Track *track, File *fh, bool canSeek, + bool headerless, AFframecount *chunkFrames); + static PCM *createDecompress(Track *track, File *fh, bool canSeek, + bool headerless, AFframecount *chunkFrames); + + virtual const char *name() const OVERRIDE { return "pcm"; } + virtual void runPull() OVERRIDE; + virtual void reset2() OVERRIDE; + virtual void runPush() OVERRIDE; + virtual void sync1() OVERRIDE; + virtual void sync2() OVERRIDE; + +private: + int m_bytesPerFrame; + + /* saved_fpos_next_frame and saved_nextfframe apply only to writing. */ + int m_saved_fpos_next_frame; + int m_saved_nextfframe; + + PCM(Mode, Track *, File *, bool canSeek); +}; + +PCM::PCM(Mode mode, Track *track, File *fh, bool canSeek) : + FileModule(mode, track, fh, canSeek), + m_bytesPerFrame(track->f.bytesPerFrame(false)), + m_saved_fpos_next_frame(-1), + m_saved_nextfframe(-1) +{ + if (mode == Decompress) + track->f.compressionParams = AU_NULL_PVLIST; +} + +PCM *PCM::createCompress(Track *track, File *fh, bool canSeek, + bool headerless, AFframecount *chunkframes) +{ + return new PCM(Compress, track, fh, canSeek); +} + +void PCM::runPush() +{ + AFframecount frames2write = m_inChunk->frameCount; + AFframecount n; + + /* + WARNING: due to the optimization explained at the end + of arrangemodules(), the pcm file module cannot depend + on the presence of the intermediate working buffer + which _AFsetupmodules usually allocates for file + modules in their input or output chunk (for reading or + writing, respectively). + + Fortunately, the pcm module has no need for such a buffer. + */ + + ssize_t bytesWritten = write(m_inChunk->buffer, m_bytesPerFrame * frames2write); + n = bytesWritten >= 0 ? bytesWritten / m_bytesPerFrame : 0; + + if (n != frames2write) + reportWriteError(n, frames2write); + + m_track->nextfframe += n; + m_track->totalfframes = m_track->nextfframe; + assert(!canSeek() || (tell() == m_track->fpos_next_frame)); +} + +void PCM::sync1() +{ + m_saved_fpos_next_frame = m_track->fpos_next_frame; + m_saved_nextfframe = m_track->nextfframe; +} + +void PCM::sync2() +{ + assert(!canSeek() || (tell() == m_track->fpos_next_frame)); + + /* We can afford to seek because sync2 is rare. */ + m_track->fpos_after_data = tell(); + + m_track->fpos_next_frame = m_saved_fpos_next_frame; + m_track->nextfframe = m_saved_nextfframe; +} + +PCM *PCM::createDecompress(Track *track, File *fh, bool canSeek, + bool headerless, AFframecount *chunkframes) +{ + return new PCM(Decompress, track, fh, canSeek); +} + +void PCM::runPull() +{ + AFframecount framesToRead = m_outChunk->frameCount; + + /* + WARNING: Due to the optimization explained at the end of + arrangemodules(), the pcm file module cannot depend on + the presence of the intermediate working buffer which + _AFsetupmodules usually allocates for file modules in + their input or output chunk (for reading or writing, + respectively). + + Fortunately, the pcm module has no need for such a buffer. + */ + + /* + Limit the number of frames to be read to the number of + frames left in the track. + */ + if (m_track->totalfframes != -1 && + m_track->nextfframe + framesToRead > m_track->totalfframes) + { + framesToRead = m_track->totalfframes - m_track->nextfframe; + } + + ssize_t bytesRead = read(m_outChunk->buffer, m_bytesPerFrame * framesToRead); + AFframecount framesRead = bytesRead >= 0 ? bytesRead / m_bytesPerFrame : 0; + + m_track->nextfframe += framesRead; + assert(!canSeek() || (tell() == m_track->fpos_next_frame)); + + /* + If we got EOF from read, then we return the actual amount read. + + Complain only if there should have been more frames in the file. + */ + + if (framesRead != framesToRead && m_track->totalfframes != -1) + reportReadError(framesRead, framesToRead); + + m_outChunk->frameCount = framesRead; +} + +void PCM::reset2() +{ + m_track->fpos_next_frame = m_track->fpos_first_frame + + m_bytesPerFrame * m_track->nextfframe; + + m_track->frames2ignore = 0; +} + +FileModule *_AFpcminitcompress (Track *track, File *fh, bool canSeek, + bool headerless, AFframecount *chunkFrames) +{ + return PCM::createCompress(track, fh, canSeek, headerless, chunkFrames); +} + +FileModule *_AFpcminitdecompress (Track *track, File *fh, bool canSeek, + bool headerless, AFframecount *chunkFrames) +{ + return PCM::createDecompress(track, fh, canSeek, headerless, chunkFrames); +} + +// file: modules/SimpleModule.cpp +/* + Audio File Library + Copyright (C) 2010, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + + +#include <algorithm> + +void SimpleModule::runPull() +{ + pull(m_outChunk->frameCount); + run(*m_inChunk, *m_outChunk); +} + +void SimpleModule::runPush() +{ + m_outChunk->frameCount = m_inChunk->frameCount; + run(*m_inChunk, *m_outChunk); + push(m_outChunk->frameCount); +} + +ApplyChannelMatrix::ApplyChannelMatrix(FormatCode format, bool isReading, + int inChannels, int outChannels, + double minClip, double maxClip, const double *matrix) : + m_format(format), + m_inChannels(inChannels), + m_outChannels(outChannels), + m_minClip(minClip), + m_maxClip(maxClip), + m_matrix(NULL) +{ + m_matrix = new double[m_inChannels * m_outChannels]; + if (matrix) + { + if (isReading) + { + // Copy channel matrix for reading. + std::copy(matrix, matrix + m_inChannels * m_outChannels, m_matrix); + } + else + { + // Transpose channel matrix for writing. + for (int i=0; i < inChannels; i++) + for (int j=0; j < outChannels; j++) + m_matrix[j*inChannels + i] = matrix[i*outChannels + j]; + } + } + else + { + initDefaultMatrix(); + } +} + +ApplyChannelMatrix::~ApplyChannelMatrix() +{ + delete [] m_matrix; +} + +const char *ApplyChannelMatrix::name() const { return "channelMatrix"; } + +void ApplyChannelMatrix::describe() +{ + m_outChunk->f.channelCount = m_outChannels; + m_outChunk->f.pcm.minClip = m_minClip; + m_outChunk->f.pcm.maxClip = m_maxClip; +} + +void ApplyChannelMatrix::run(Chunk &inChunk, Chunk &outChunk) +{ + switch (m_format) + { + case kInt8: + run<int8_t>(inChunk.buffer, outChunk.buffer, inChunk.frameCount); + break; + case kInt16: + run<int16_t>(inChunk.buffer, outChunk.buffer, inChunk.frameCount); + break; + case kInt24: + case kInt32: + run<int32_t>(inChunk.buffer, outChunk.buffer, inChunk.frameCount); + break; + case kFloat: + run<float>(inChunk.buffer, outChunk.buffer, inChunk.frameCount); + break; + case kDouble: + run<double>(inChunk.buffer, outChunk.buffer, inChunk.frameCount); + break; + default: + assert(false); + } +} + +template <typename T> +void ApplyChannelMatrix::run(const void *inputData, void *outputData, int frameCount) +{ + const T *input = reinterpret_cast<const T *>(inputData); + T *output = reinterpret_cast<T *>(outputData); + for (int frame=0; frame<frameCount; frame++) + { + const T *inputSave = input; + const double *m = m_matrix; + for (int outChannel=0; outChannel < m_outChannels; outChannel++) + { + input = inputSave; + double t = 0; + for (int inChannel=0; inChannel < m_inChannels; inChannel++) + t += *input++ * *m++; + *output++ = t; + } + } +} + +void ApplyChannelMatrix::initDefaultMatrix() +{ + const double *matrix = NULL; + if (m_inChannels==1 && m_outChannels==2) + { + static const double m[]={1,1}; + matrix = m; + } + else if (m_inChannels==1 && m_outChannels==4) + { + static const double m[]={1,1,0,0}; + matrix = m; + } + else if (m_inChannels==2 && m_outChannels==1) + { + static const double m[]={.5,.5}; + matrix = m; + } + else if (m_inChannels==2 && m_outChannels==4) + { + static const double m[]={1,0,0,1,0,0,0,0}; + matrix = m; + } + else if (m_inChannels==4 && m_outChannels==1) + { + static const double m[]={.5,.5,.5,.5}; + matrix = m; + } + else if (m_inChannels==4 && m_outChannels==2) + { + static const double m[]={1,0,1,0,0,1,0,1}; + matrix = m; + } + + if (matrix) + { + std::copy(matrix, matrix + m_inChannels * m_outChannels, m_matrix); + } + else + { + for (int i=0; i < m_inChannels; i++) + for (int j=0; j < m_outChannels; j++) + m_matrix[j*m_inChannels + i] = (i==j) ? 1 : 0; + } +} + +// file: modules/RebufferModule.cpp +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + Copyright (C) 2010, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + + +#include <algorithm> +#include <assert.h> +#include <string.h> + +RebufferModule::RebufferModule(Direction direction, int bytesPerFrame, + int numFrames, bool multipleOf) : + m_direction(direction), + m_bytesPerFrame(bytesPerFrame), + m_numFrames(numFrames), + m_multipleOf(multipleOf), + m_eof(false), + m_sentShortChunk(false), + m_buffer(NULL), + m_offset(-1), + m_savedBuffer(NULL), + m_savedOffset(-1) +{ + if (m_direction == FixedToVariable) + initFixedToVariable(); + else + initVariableToFixed(); +} + +RebufferModule::~RebufferModule() +{ + delete [] m_buffer; + delete [] m_savedBuffer; +} + +void RebufferModule::initFixedToVariable() +{ + m_offset = m_numFrames; + m_buffer = new char[m_numFrames * m_bytesPerFrame]; +} + +void RebufferModule::initVariableToFixed() +{ + m_offset = 0; + m_buffer = new char[m_numFrames * m_bytesPerFrame]; + m_savedBuffer = new char[m_numFrames * m_bytesPerFrame]; +} + +void RebufferModule::maxPull() +{ + assert(m_direction == FixedToVariable); + if (m_multipleOf) + m_inChunk->frameCount = m_outChunk->frameCount + m_numFrames; + else + m_inChunk->frameCount = m_numFrames; +} + +void RebufferModule::maxPush() +{ + assert(m_direction == VariableToFixed); + if (m_multipleOf) + m_outChunk->frameCount = m_inChunk->frameCount + m_numFrames; + else + m_outChunk->frameCount = m_numFrames; +} + +void RebufferModule::runPull() +{ + int framesToPull = m_outChunk->frameCount; + const char *inBuffer = static_cast<const char *>(m_inChunk->buffer); + char *outBuffer = static_cast<char *>(m_outChunk->buffer); + + assert(m_offset > 0 && m_offset <= m_numFrames); + + /* + A module should not pull more frames from its input + after receiving a short chunk. + */ + assert(!m_sentShortChunk); + + if (m_offset < m_numFrames) + { + int buffered = m_numFrames - m_offset; + int n = std::min(framesToPull, buffered); + memcpy(outBuffer, m_buffer + m_offset * m_bytesPerFrame, + n * m_bytesPerFrame); + outBuffer += buffered * m_bytesPerFrame; + framesToPull -= buffered; + m_offset += n; + } + + // Try to pull more frames from the source. + while (!m_eof && framesToPull > 0) + { + int framesRequested; + if (m_multipleOf) + // Round framesToPull up to nearest multiple of m_numFrames. + framesRequested = ((framesToPull - 1) / m_numFrames + 1) * m_numFrames; + else + framesRequested = m_numFrames; + + assert(framesRequested > 0); + + pull(framesRequested); + + int framesReceived = m_inChunk->frameCount; + + if (framesReceived != framesRequested) + m_eof = true; + + memcpy(outBuffer, inBuffer, + std::min(framesToPull, framesReceived) * m_bytesPerFrame); + + outBuffer += framesReceived * m_bytesPerFrame; + framesToPull -= framesReceived; + + if (m_multipleOf) + assert(m_eof || framesToPull <= 0); + + if (framesToPull < 0) + { + assert(m_offset == m_numFrames); + + m_offset = m_numFrames + framesToPull; + + assert(m_offset > 0 && m_offset <= m_numFrames); + + memcpy(m_buffer + m_offset * m_bytesPerFrame, + inBuffer + (framesReceived + framesToPull) * m_bytesPerFrame, + (m_numFrames - m_offset) * m_bytesPerFrame); + } + else + { + assert(m_offset == m_numFrames); + } + } + + if (m_eof && framesToPull > 0) + { + // Output short chunk. + m_outChunk->frameCount -= framesToPull; + m_sentShortChunk = true; + assert(m_offset == m_numFrames); + } + else + { + assert(framesToPull <= 0); + assert(m_offset == m_numFrames + framesToPull); + } + assert(m_offset > 0 && m_offset <= m_numFrames); +} + +void RebufferModule::reset1() +{ + m_offset = m_numFrames; + m_eof = false; + m_sentShortChunk = false; + assert(m_offset > 0 && m_offset <= m_numFrames); +} + +void RebufferModule::reset2() +{ + assert(m_offset > 0 && m_offset <= m_numFrames); +} + +void RebufferModule::runPush() +{ + int framesToPush = m_inChunk->frameCount; + const char *inBuffer = static_cast<const char *>(m_inChunk->buffer); + char *outBuffer = static_cast<char *>(m_outChunk->buffer); + + assert(m_offset >= 0 && m_offset < m_numFrames); + + // Check that we will be able to push even one block. + if (m_offset + framesToPush >= m_numFrames) + { + if (m_offset > 0) + memcpy(m_outChunk->buffer, m_buffer, m_offset * m_bytesPerFrame); + + if (m_multipleOf) + { + // Round down to nearest multiple of m_numFrames. + int n = ((m_offset + framesToPush) / m_numFrames) * m_numFrames; + + assert(n > m_offset); + memcpy(outBuffer + m_offset * m_bytesPerFrame, + inBuffer, + (n - m_offset) * m_bytesPerFrame); + + push(n); + + inBuffer += (n - m_offset) * m_bytesPerFrame; + framesToPush -= n - m_offset; + assert(framesToPush >= 0); + m_offset = 0; + } + else + { + while (m_offset + framesToPush >= m_numFrames) + { + int n = m_numFrames - m_offset; + memcpy(outBuffer + m_offset * m_bytesPerFrame, + inBuffer, + n * m_bytesPerFrame); + + push(m_numFrames); + + inBuffer += n * m_bytesPerFrame; + framesToPush -= n; + assert(framesToPush >= 0); + m_offset = 0; + } + } + + assert(m_offset == 0); + } + + assert(m_offset + framesToPush < m_numFrames); + + // Save remaining samples in buffer. + if (framesToPush > 0) + { + memcpy(m_buffer + m_offset * m_bytesPerFrame, + inBuffer, + framesToPush * m_bytesPerFrame); + m_offset += framesToPush; + } + + assert(m_offset >= 0 && m_offset < m_numFrames); +} + +void RebufferModule::sync1() +{ + assert(m_offset >= 0 && m_offset < m_numFrames); + + // Save all the frames and the offset so we can restore our state later. + memcpy(m_savedBuffer, m_buffer, m_numFrames * m_bytesPerFrame); + m_savedOffset = m_offset; +} + +void RebufferModule::sync2() +{ + assert(m_offset >= 0 && m_offset < m_numFrames); + + memcpy(m_outChunk->buffer, m_buffer, m_offset * m_bytesPerFrame); + + push(m_offset); + + memcpy(m_buffer, m_savedBuffer, m_numFrames * m_bytesPerFrame); + m_offset = m_savedOffset; + + assert(m_offset >= 0 && m_offset < m_numFrames); +} + +// file: AIFF.h +/* + Audio File Library + Copyright (C) 1998-2000, 2003-2004, 2010-2012, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + AIFF.h + + This file contains structures and constants related to the AIFF + and AIFF-C formats. +*/ + +#ifndef AIFF_H +#define AIFF_H + + +#define _AF_AIFF_NUM_INSTPARAMS 9 +extern const InstParamInfo _af_aiff_inst_params[_AF_AIFF_NUM_INSTPARAMS]; +#define _AF_AIFFC_NUM_COMPTYPES 3 +extern const int _af_aiffc_compression_types[_AF_AIFFC_NUM_COMPTYPES]; + +class AIFFFile : public _AFfilehandle +{ +public: + AIFFFile(); + + static bool recognizeAIFF(File *fh); + static bool recognizeAIFFC(File *fh); + + static AFfilesetup completeSetup(AFfilesetup); + + int getVersion() OVERRIDE; + + status readInit(AFfilesetup) OVERRIDE; + status writeInit(AFfilesetup) OVERRIDE; + + status update() OVERRIDE; + + bool isInstrumentParameterValid(AUpvlist, int) OVERRIDE; + +private: + AFfileoffset m_miscellaneousPosition; + AFfileoffset m_FVER_offset; + AFfileoffset m_COMM_offset; + AFfileoffset m_MARK_offset; + AFfileoffset m_INST_offset; + AFfileoffset m_AESD_offset; + AFfileoffset m_SSND_offset; + + status parseFVER(const Tag &type, size_t size); + status parseAESD(const Tag &type, size_t size); + status parseMiscellaneous(const Tag &type, size_t size); + status parseINST(const Tag &type, size_t size); + status parseMARK(const Tag &type, size_t size); + status parseCOMM(const Tag &type, size_t size); + status parseSSND(const Tag &type, size_t size); + + status writeCOMM(); + status writeSSND(); + status writeMARK(); + status writeINST(); + status writeFVER(); + status writeAESD(); + status writeMiscellaneous(); + + void initCompressionParams(); + void initIMACompressionParams(); + + bool isAIFFC() const { return m_fileFormat == AF_FILE_AIFFC; } + + bool readPString(char s[256]); + bool writePString(const char *); +}; + +#endif + +// file: AIFF.cpp +/* + Audio File Library + Copyright (C) 1998-2000, 2003-2004, 2010-2013, Michael Pruett <michael@68k.org> + Copyright (C) 2000-2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + AIFF.cpp + + This file contains routines for reading and writing AIFF and + AIFF-C sound files. +*/ + + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + + +const InstParamInfo _af_aiff_inst_params[_AF_AIFF_NUM_INSTPARAMS] = +{ + { AF_INST_MIDI_BASENOTE, AU_PVTYPE_LONG, "MIDI base note", {60} }, + { AF_INST_NUMCENTS_DETUNE, AU_PVTYPE_LONG, "Detune in cents", {0} }, + { AF_INST_MIDI_LOVELOCITY, AU_PVTYPE_LONG, "Low velocity", {1} }, + { AF_INST_MIDI_HIVELOCITY, AU_PVTYPE_LONG, "High velocity", {127} }, + { AF_INST_MIDI_LONOTE, AU_PVTYPE_LONG, "Low note", {0} }, + { AF_INST_MIDI_HINOTE, AU_PVTYPE_LONG, "High note", {127} }, + { AF_INST_NUMDBS_GAIN, AU_PVTYPE_LONG, "Gain in dB", {0} }, + { AF_INST_SUSLOOPID, AU_PVTYPE_LONG, "Sustain loop id", {0} }, + { AF_INST_RELLOOPID, AU_PVTYPE_LONG, "Release loop id", {0} } +}; + +const int _af_aiffc_compression_types[_AF_AIFFC_NUM_COMPTYPES] = +{ + AF_COMPRESSION_G711_ULAW, + AF_COMPRESSION_G711_ALAW, + AF_COMPRESSION_IMA +}; + +static const _AFfilesetup aiffDefaultFileSetup = +{ + _AF_VALID_FILESETUP, /* valid */ + AF_FILE_AIFF, /* fileFormat */ + true, /* trackSet */ + true, /* instrumentSet */ + true, /* miscellaneousSet */ + 1, /* trackCount */ + NULL, /* tracks */ + 1, /* instrumentCount */ + NULL, /* instruments */ + 0, /* miscellaneousCount */ + NULL /* miscellaneous */ +}; + +#define AIFC_VERSION_1 0xa2805140 + +struct _INST +{ + uint8_t baseNote; + int8_t detune; + uint8_t lowNote, highNote; + uint8_t lowVelocity, highVelocity; + int16_t gain; + + uint16_t sustainLoopPlayMode; + uint16_t sustainLoopBegin; + uint16_t sustainLoopEnd; + + uint16_t releaseLoopPlayMode; + uint16_t releaseLoopBegin; + uint16_t releaseLoopEnd; +}; + +AIFFFile::AIFFFile() +{ + setFormatByteOrder(AF_BYTEORDER_BIGENDIAN); + + m_miscellaneousPosition = 0; + m_FVER_offset = 0; + m_COMM_offset = 0; + m_MARK_offset = 0; + m_INST_offset = 0; + m_AESD_offset = 0; + m_SSND_offset = 0; +} + +/* + FVER chunks are only present in AIFF-C files. +*/ +status AIFFFile::parseFVER(const Tag &type, size_t size) +{ + assert(type == "FVER"); + + uint32_t timestamp; + readU32(&timestamp); + /* timestamp holds the number of seconds since January 1, 1904. */ + + return AF_SUCCEED; +} + +/* + Parse AES recording data. +*/ +status AIFFFile::parseAESD(const Tag &type, size_t size) +{ + unsigned char aesChannelStatusData[24]; + + assert(type == "AESD"); + assert(size == 24); + + Track *track = getTrack(); + + track->hasAESData = true; + + /* + Try to read 24 bytes of AES nonaudio data from the file. + Fail if the file disappoints. + */ + if (m_fh->read(aesChannelStatusData, 24) != 24) + return AF_FAIL; + + memcpy(track->aesData, aesChannelStatusData, 24); + + return AF_SUCCEED; +} + +/* + Parse miscellaneous data chunks such as name, author, copyright, + and annotation chunks. +*/ +status AIFFFile::parseMiscellaneous(const Tag &type, size_t size) +{ + int misctype = AF_MISC_UNRECOGNIZED; + + assert(type == "NAME" || + type == "AUTH" || + type == "(c) " || + type == "ANNO" || + type == "APPL" || + type == "MIDI"); + + /* Skip zero-length miscellaneous chunks. */ + if (size == 0) + return AF_FAIL; + + m_miscellaneousCount++; + m_miscellaneous = (Miscellaneous *) _af_realloc(m_miscellaneous, + m_miscellaneousCount * sizeof (Miscellaneous)); + + if (type == "NAME") + misctype = AF_MISC_NAME; + else if (type == "AUTH") + misctype = AF_MISC_AUTH; + else if (type == "(c) ") + misctype = AF_MISC_COPY; + else if (type == "ANNO") + misctype = AF_MISC_ANNO; + else if (type == "APPL") + misctype = AF_MISC_APPL; + else if (type == "MIDI") + misctype = AF_MISC_MIDI; + + m_miscellaneous[m_miscellaneousCount - 1].id = m_miscellaneousCount; + m_miscellaneous[m_miscellaneousCount - 1].type = misctype; + m_miscellaneous[m_miscellaneousCount - 1].size = size; + m_miscellaneous[m_miscellaneousCount - 1].position = 0; + m_miscellaneous[m_miscellaneousCount - 1].buffer = _af_malloc(size); + m_fh->read(m_miscellaneous[m_miscellaneousCount - 1].buffer, size); + + return AF_SUCCEED; +} + +/* + Parse instrument chunks, which contain information about using + sound data as a sampled instrument. +*/ +status AIFFFile::parseINST(const Tag &type, size_t size) +{ + uint8_t baseNote; + int8_t detune; + uint8_t lowNote, highNote, lowVelocity, highVelocity; + int16_t gain; + + uint16_t sustainLoopPlayMode, sustainLoopBegin, sustainLoopEnd; + uint16_t releaseLoopPlayMode, releaseLoopBegin, releaseLoopEnd; + + Instrument *instrument = (Instrument *) _af_calloc(1, sizeof (Instrument)); + instrument->id = AF_DEFAULT_INST; + instrument->values = (AFPVu *) _af_calloc(_AF_AIFF_NUM_INSTPARAMS, sizeof (AFPVu)); + instrument->loopCount = 2; + instrument->loops = (Loop *) _af_calloc(2, sizeof (Loop)); + + m_instrumentCount = 1; + m_instruments = instrument; + + readU8(&baseNote); + readS8(&detune); + readU8(&lowNote); + readU8(&highNote); + readU8(&lowVelocity); + readU8(&highVelocity); + readS16(&gain); + + instrument->values[0].l = baseNote; + instrument->values[1].l = detune; + instrument->values[2].l = lowVelocity; + instrument->values[3].l = highVelocity; + instrument->values[4].l = lowNote; + instrument->values[5].l = highNote; + instrument->values[6].l = gain; + + instrument->values[7].l = 1; /* sustain loop id */ + instrument->values[8].l = 2; /* release loop id */ + + readU16(&sustainLoopPlayMode); + readU16(&sustainLoopBegin); + readU16(&sustainLoopEnd); + + readU16(&releaseLoopPlayMode); + readU16(&releaseLoopBegin); + readU16(&releaseLoopEnd); + + instrument->loops[0].id = 1; + instrument->loops[0].mode = sustainLoopPlayMode; + instrument->loops[0].beginMarker = sustainLoopBegin; + instrument->loops[0].endMarker = sustainLoopEnd; + instrument->loops[0].trackid = AF_DEFAULT_TRACK; + + instrument->loops[1].id = 2; + instrument->loops[1].mode = releaseLoopPlayMode; + instrument->loops[1].beginMarker = releaseLoopBegin; + instrument->loops[1].endMarker = releaseLoopEnd; + instrument->loops[1].trackid = AF_DEFAULT_TRACK; + + return AF_SUCCEED; +} + +/* + Parse marker chunks, which contain the positions and names of loop markers. +*/ +status AIFFFile::parseMARK(const Tag &type, size_t size) +{ + assert(type == "MARK"); + + Track *track = getTrack(); + + uint16_t numMarkers; + readU16(&numMarkers); + + track->markerCount = numMarkers; + if (numMarkers) + track->markers = _af_marker_new(numMarkers); + + for (unsigned i=0; i<numMarkers; i++) + { + uint16_t markerID = 0; + uint32_t markerPosition = 0; + uint8_t sizeByte = 0; + char *markerName = NULL; + + readU16(&markerID); + readU32(&markerPosition); + m_fh->read(&sizeByte, 1); + markerName = (char *) _af_malloc(sizeByte + 1); + m_fh->read(markerName, sizeByte); + + markerName[sizeByte] = '\0'; + + /* + If sizeByte is even, then 1+sizeByte (the length + of the string) is odd. Skip an extra byte to + make it even. + */ + + if ((sizeByte % 2) == 0) + m_fh->seek(1, File::SeekFromCurrent); + + track->markers[i].id = markerID; + track->markers[i].position = markerPosition; + track->markers[i].name = markerName; + track->markers[i].comment = _af_strdup(""); + } + + return AF_SUCCEED; +} + +/* + Parse common data chunks, which contain information regarding the + sampling rate, the number of sample frames, and the number of + sound channels. +*/ +status AIFFFile::parseCOMM(const Tag &type, size_t size) +{ + assert(type == "COMM"); + + Track *track = getTrack(); + + uint16_t numChannels; + uint32_t numSampleFrames; + uint16_t sampleSize; + unsigned char sampleRate[10]; + + readU16(&numChannels); + track->f.channelCount = numChannels; + + if (!numChannels) + { + _af_error(AF_BAD_CHANNELS, "invalid file with 0 channels"); + return AF_FAIL; + } + + readU32(&numSampleFrames); + track->totalfframes = numSampleFrames; + + readU16(&sampleSize); + track->f.sampleWidth = sampleSize; + + m_fh->read(sampleRate, 10); + track->f.sampleRate = _af_convert_from_ieee_extended(sampleRate); + + track->f.compressionType = AF_COMPRESSION_NONE; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; + + track->f.framesPerPacket = 1; + + if (isAIFFC()) + { + Tag compressionID; + // Pascal strings are at most 255 bytes long. + char compressionName[256]; + + readTag(&compressionID); + + // Read the Pascal-style string containing the name. + readPString(compressionName); + + if (compressionID == "NONE" || compressionID == "twos") + { + track->f.compressionType = AF_COMPRESSION_NONE; + } + else if (compressionID == "in24") + { + track->f.compressionType = AF_COMPRESSION_NONE; + track->f.sampleWidth = 24; + } + else if (compressionID == "in32") + { + track->f.compressionType = AF_COMPRESSION_NONE; + track->f.sampleWidth = 32; + } + else if (compressionID == "ACE2" || + compressionID == "ACE8" || + compressionID == "MAC3" || + compressionID == "MAC6") + { + _af_error(AF_BAD_NOT_IMPLEMENTED, "AIFF-C format does not support Apple's proprietary %s compression format", compressionName); + return AF_FAIL; + } + else if (compressionID == "ulaw" || compressionID == "ULAW") + { + track->f.compressionType = AF_COMPRESSION_G711_ULAW; + track->f.byteOrder = _AF_BYTEORDER_NATIVE; + track->f.sampleWidth = 16; + track->f.bytesPerPacket = track->f.channelCount; + } + else if (compressionID == "alaw" || compressionID == "ALAW") + { + track->f.compressionType = AF_COMPRESSION_G711_ALAW; + track->f.byteOrder = _AF_BYTEORDER_NATIVE; + track->f.sampleWidth = 16; + track->f.bytesPerPacket = track->f.channelCount; + } + else if (compressionID == "fl32" || compressionID == "FL32") + { + track->f.sampleFormat = AF_SAMPFMT_FLOAT; + track->f.sampleWidth = 32; + track->f.compressionType = AF_COMPRESSION_NONE; + } + else if (compressionID == "fl64" || compressionID == "FL64") + { + track->f.sampleFormat = AF_SAMPFMT_DOUBLE; + track->f.sampleWidth = 64; + track->f.compressionType = AF_COMPRESSION_NONE; + } + else if (compressionID == "sowt") + { + track->f.compressionType = AF_COMPRESSION_NONE; + track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN; + } + else if (compressionID == "ima4") + { + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.compressionType = AF_COMPRESSION_IMA; + track->f.byteOrder = _AF_BYTEORDER_NATIVE; + + initIMACompressionParams(); + + track->totalfframes *= 64; + } + else + { + _af_error(AF_BAD_NOT_IMPLEMENTED, "AIFF-C compression type '%s' not currently supported", + compressionID.name().c_str()); + return AF_FAIL; + } + } + + if (track->f.isUncompressed()) + track->f.computeBytesPerPacketPCM(); + + if (_af_set_sample_format(&track->f, track->f.sampleFormat, track->f.sampleWidth) == AF_FAIL) + return AF_FAIL; + + return AF_SUCCEED; +} + +/* + Parse the stored sound chunk, which usually contains little more + than the sound data. +*/ +status AIFFFile::parseSSND(const Tag &type, size_t size) +{ + assert(type == "SSND"); + + Track *track = getTrack(); + + uint32_t offset, blockSize; + readU32(&offset); + readU32(&blockSize); + + track->data_size = size - 8 - offset; + + track->fpos_first_frame = m_fh->tell() + offset; + + return AF_SUCCEED; +} + +status AIFFFile::readInit(AFfilesetup setup) +{ + uint32_t type, size, formtype; + + bool hasCOMM = false; + bool hasFVER = false; + bool hasSSND = false; + + m_fh->seek(0, File::SeekFromBeginning); + + m_fh->read(&type, 4); + readU32(&size); + m_fh->read(&formtype, 4); + + if (memcmp(&type, "FORM", 4) != 0 || + (memcmp(&formtype, "AIFF", 4) && memcmp(&formtype, "AIFC", 4))) + return AF_FAIL; + + if (!allocateTrack()) + return AF_FAIL; + + /* Include the offset of the form type. */ + size_t index = 4; + while (index < size) + { + Tag chunkid; + uint32_t chunksize = 0; + status result = AF_SUCCEED; + + readTag(&chunkid); + readU32(&chunksize); + + if (chunkid == "COMM") + { + hasCOMM = true; + result = parseCOMM(chunkid, chunksize); + } + else if (chunkid == "FVER") + { + hasFVER = true; + parseFVER(chunkid, chunksize); + } + else if (chunkid == "INST") + { + parseINST(chunkid, chunksize); + } + else if (chunkid == "MARK") + { + parseMARK(chunkid, chunksize); + } + else if (chunkid == "AESD") + { + parseAESD(chunkid, chunksize); + } + else if (chunkid == "NAME" || + chunkid == "AUTH" || + chunkid == "(c) " || + chunkid == "ANNO" || + chunkid == "APPL" || + chunkid == "MIDI") + { + parseMiscellaneous(chunkid, chunksize); + } + /* + The sound data chunk is required if there are more than + zero sample frames. + */ + else if (chunkid == "SSND") + { + if (hasSSND) + { + _af_error(AF_BAD_AIFF_SSND, "AIFF file has more than one SSND chunk"); + return AF_FAIL; + } + hasSSND = true; + result = parseSSND(chunkid, chunksize); + } + + if (result == AF_FAIL) + return AF_FAIL; + + index += chunksize + 8; + + /* all chunks must be aligned on an even number of bytes */ + if ((index % 2) != 0) + index++; + + m_fh->seek(index + 8, File::SeekFromBeginning); + } + + if (!hasCOMM) + { + _af_error(AF_BAD_AIFF_COMM, "bad AIFF COMM chunk"); + } + + if (isAIFFC() && !hasFVER) + { + _af_error(AF_BAD_HEADER, "FVER chunk is required in AIFF-C"); + } + + /* The file has been successfully parsed. */ + return AF_SUCCEED; +} + +bool AIFFFile::recognizeAIFF(File *fh) +{ + uint8_t buffer[8]; + + fh->seek(0, File::SeekFromBeginning); + + if (fh->read(buffer, 8) != 8 || memcmp(buffer, "FORM", 4) != 0) + return false; + if (fh->read(buffer, 4) != 4 || memcmp(buffer, "AIFF", 4) != 0) + return false; + + return true; +} + +bool AIFFFile::recognizeAIFFC(File *fh) +{ + uint8_t buffer[8]; + + fh->seek(0, File::SeekFromBeginning); + + if (fh->read(buffer, 8) != 8 || memcmp(buffer, "FORM", 4) != 0) + return false; + if (fh->read(buffer, 4) != 4 || memcmp(buffer, "AIFC", 4) != 0) + return false; + + return true; +} + +AFfilesetup AIFFFile::completeSetup(AFfilesetup setup) +{ + bool isAIFF = setup->fileFormat == AF_FILE_AIFF; + + if (setup->trackSet && setup->trackCount != 1) + { + _af_error(AF_BAD_NUMTRACKS, "AIFF/AIFF-C file must have 1 track"); + return AF_NULL_FILESETUP; + } + + TrackSetup *track = setup->getTrack(); + if (!track) + return AF_NULL_FILESETUP; + + if (track->sampleFormatSet) + { + if (track->f.sampleFormat == AF_SAMPFMT_UNSIGNED) + { + _af_error(AF_BAD_FILEFMT, "AIFF/AIFF-C format does not support unsigned data"); + return AF_NULL_FILESETUP; + } + else if (isAIFF && track->f.sampleFormat != AF_SAMPFMT_TWOSCOMP) + { + _af_error(AF_BAD_FILEFMT, "AIFF format supports only two's complement integer data"); + return AF_NULL_FILESETUP; + } + } + else + _af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, + track->f.sampleWidth); + + /* Check sample width if writing two's complement. Otherwise ignore. */ + if (track->f.sampleFormat == AF_SAMPFMT_TWOSCOMP && + (track->f.sampleWidth < 1 || track->f.sampleWidth > 32)) + { + _af_error(AF_BAD_WIDTH, + "invalid sample width %d for AIFF/AIFF-C file " + "(must be 1-32)", track->f.sampleWidth); + return AF_NULL_FILESETUP; + } + + if (isAIFF && track->f.compressionType != AF_COMPRESSION_NONE) + { + _af_error(AF_BAD_FILESETUP, + "AIFF does not support compression; use AIFF-C"); + return AF_NULL_FILESETUP; + } + + if (track->f.compressionType != AF_COMPRESSION_NONE && + track->f.compressionType != AF_COMPRESSION_G711_ULAW && + track->f.compressionType != AF_COMPRESSION_G711_ALAW && + track->f.compressionType != AF_COMPRESSION_IMA) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, "compression format not supported in AIFF-C"); + return AF_NULL_FILESETUP; + } + + if (track->f.isUncompressed() && + track->byteOrderSet && + track->f.byteOrder != AF_BYTEORDER_BIGENDIAN && + track->f.isByteOrderSignificant()) + { + _af_error(AF_BAD_BYTEORDER, + "AIFF/AIFF-C format supports only big-endian data"); + return AF_NULL_FILESETUP; + } + + if (track->f.isUncompressed()) + track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; + + if (setup->instrumentSet) + { + if (setup->instrumentCount != 0 && setup->instrumentCount != 1) + { + _af_error(AF_BAD_NUMINSTS, "AIFF/AIFF-C file must have 0 or 1 instrument chunk"); + return AF_NULL_FILESETUP; + } + if (setup->instruments != 0 && + setup->instruments[0].loopCount != 2) + { + _af_error(AF_BAD_NUMLOOPS, "AIFF/AIFF-C file with instrument must also have 2 loops"); + return AF_NULL_FILESETUP; + } + } + + if (setup->miscellaneousSet) + { + for (int i=0; i<setup->miscellaneousCount; i++) + { + switch (setup->miscellaneous[i].type) + { + case AF_MISC_COPY: + case AF_MISC_AUTH: + case AF_MISC_NAME: + case AF_MISC_ANNO: + case AF_MISC_APPL: + case AF_MISC_MIDI: + break; + + default: + _af_error(AF_BAD_MISCTYPE, "invalid miscellaneous type %d for AIFF/AIFF-C file", setup->miscellaneous[i].type); + return AF_NULL_FILESETUP; + } + } + } + + return _af_filesetup_copy(setup, &aiffDefaultFileSetup, true); +} + +bool AIFFFile::isInstrumentParameterValid(AUpvlist list, int i) +{ + int param, type; + + AUpvgetparam(list, i, &param); + AUpvgetvaltype(list, i, &type); + if (type != AU_PVTYPE_LONG) + return false; + + long lval; + AUpvgetval(list, i, &lval); + + switch (param) + { + case AF_INST_MIDI_BASENOTE: + return ((lval >= 0) && (lval <= 127)); + + case AF_INST_NUMCENTS_DETUNE: + return ((lval >= -50) && (lval <= 50)); + + case AF_INST_MIDI_LOVELOCITY: + return ((lval >= 1) && (lval <= 127)); + + case AF_INST_MIDI_HIVELOCITY: + return ((lval >= 1) && (lval <= 127)); + + case AF_INST_MIDI_LONOTE: + return ((lval >= 0) && (lval <= 127)); + + case AF_INST_MIDI_HINOTE: + return ((lval >= 0) && (lval <= 127)); + + case AF_INST_NUMDBS_GAIN: + case AF_INST_SUSLOOPID: + case AF_INST_RELLOOPID: + return true; + + default: + return false; + break; + } + + return true; +} + +int AIFFFile::getVersion() +{ + if (isAIFFC()) + return AIFC_VERSION_1; + return 0; +} + +status AIFFFile::writeInit(AFfilesetup setup) +{ + if (initFromSetup(setup) == AF_FAIL) + return AF_FAIL; + + initCompressionParams(); + + uint32_t fileSize = 0; + m_fh->write("FORM", 4); + writeU32(&fileSize); + + if (isAIFFC()) + m_fh->write("AIFC", 4); + else + m_fh->write("AIFF", 4); + + if (isAIFFC()) + writeFVER(); + + writeCOMM(); + writeMARK(); + writeINST(); + writeAESD(); + writeMiscellaneous(); + writeSSND(); + + return AF_SUCCEED; +} + +status AIFFFile::update() +{ + /* Get the length of the file. */ + uint32_t length = m_fh->length(); + length -= 8; + + /* Set the length of the FORM chunk. */ + m_fh->seek(4, File::SeekFromBeginning); + writeU32(&length); + + if (isAIFFC()) + writeFVER(); + + writeCOMM(); + writeMARK(); + writeINST(); + writeAESD(); + writeMiscellaneous(); + writeSSND(); + + return AF_SUCCEED; +} + +status AIFFFile::writeCOMM() +{ + /* + If COMM_offset hasn't been set yet, set it to the + current offset. + */ + if (m_COMM_offset == 0) + m_COMM_offset = m_fh->tell(); + else + m_fh->seek(m_COMM_offset, File::SeekFromBeginning); + + Track *track = getTrack(); + + Tag compressionTag; + /* Pascal strings can occupy only 255 bytes (+ a size byte). */ + char compressionName[256]; + + if (isAIFFC()) + { + if (track->f.compressionType == AF_COMPRESSION_NONE) + { + if (track->f.sampleFormat == AF_SAMPFMT_TWOSCOMP) + { + compressionTag = "NONE"; + strcpy(compressionName, "not compressed"); + } + else if (track->f.sampleFormat == AF_SAMPFMT_FLOAT) + { + compressionTag = "fl32"; + strcpy(compressionName, "32-bit Floating Point"); + } + else if (track->f.sampleFormat == AF_SAMPFMT_DOUBLE) + { + compressionTag = "fl64"; + strcpy(compressionName, "64-bit Floating Point"); + } + /* + We disallow unsigned sample data for + AIFF files in _af_aiff_complete_setup, + so the next condition should never be + satisfied. + */ + else if (track->f.sampleFormat == AF_SAMPFMT_UNSIGNED) + { + _af_error(AF_BAD_SAMPFMT, + "AIFF/AIFF-C format does not support unsigned data"); + assert(0); + return AF_FAIL; + } + } + else if (track->f.compressionType == AF_COMPRESSION_G711_ULAW) + { + compressionTag = "ulaw"; + strcpy(compressionName, "CCITT G.711 u-law"); + } + else if (track->f.compressionType == AF_COMPRESSION_G711_ALAW) + { + compressionTag = "alaw"; + strcpy(compressionName, "CCITT G.711 A-law"); + } + else if (track->f.compressionType == AF_COMPRESSION_IMA) + { + compressionTag = "ima4"; + strcpy(compressionName, "IMA 4:1 compression"); + } + } + + m_fh->write("COMM", 4); + + /* + For AIFF-C files, the length of the COMM chunk is 22 + plus the length of the compression name plus the size + byte. If the length of the data is an odd number of + bytes, add a zero pad byte at the end, but don't + include the pad byte in the chunk's size. + */ + uint32_t chunkSize; + if (isAIFFC()) + chunkSize = 22 + strlen(compressionName) + 1; + else + chunkSize = 18; + writeU32(&chunkSize); + + /* number of channels, 2 bytes */ + uint16_t channelCount = track->f.channelCount; + writeU16(&channelCount); + + /* number of sample frames, 4 bytes */ + uint32_t frameCount = track->totalfframes; + if (track->f.compressionType == AF_COMPRESSION_IMA) + frameCount = track->totalfframes / track->f.framesPerPacket; + writeU32(&frameCount); + + /* sample size, 2 bytes */ + uint16_t sampleSize = track->f.sampleWidth; + writeU16(&sampleSize); + + /* sample rate, 10 bytes */ + uint8_t sampleRate[10]; + _af_convert_to_ieee_extended(track->f.sampleRate, sampleRate); + m_fh->write(sampleRate, 10); + + if (isAIFFC()) + { + writeTag(&compressionTag); + writePString(compressionName); + } + + return AF_SUCCEED; +} + +/* + The AESD chunk contains information pertinent to audio recording + devices. +*/ +status AIFFFile::writeAESD() +{ + Track *track = getTrack(); + + if (!track->hasAESData) + return AF_SUCCEED; + + if (m_AESD_offset == 0) + m_AESD_offset = m_fh->tell(); + else + m_fh->seek(m_AESD_offset, File::SeekFromBeginning); + + if (m_fh->write("AESD", 4) < 4) + return AF_FAIL; + + uint32_t size = 24; + if (!writeU32(&size)) + return AF_FAIL; + + if (m_fh->write(track->aesData, 24) < 24) + return AF_FAIL; + + return AF_SUCCEED; +} + +status AIFFFile::writeSSND() +{ + Track *track = getTrack(); + + if (m_SSND_offset == 0) + m_SSND_offset = m_fh->tell(); + else + m_fh->seek(m_SSND_offset, File::SeekFromBeginning); + + m_fh->write("SSND", 4); + + uint32_t chunkSize = track->data_size + 8; + writeU32(&chunkSize); + + uint32_t zero = 0; + /* data offset */ + writeU32(&zero); + /* block size */ + writeU32(&zero); + + if (track->fpos_first_frame == 0) + track->fpos_first_frame = m_fh->tell(); + + return AF_SUCCEED; +} + +status AIFFFile::writeINST() +{ + uint32_t length = 20; + + struct _INST instrumentdata; + + instrumentdata.sustainLoopPlayMode = + afGetLoopMode(this, AF_DEFAULT_INST, 1); + instrumentdata.sustainLoopBegin = + afGetLoopStart(this, AF_DEFAULT_INST, 1); + instrumentdata.sustainLoopEnd = + afGetLoopEnd(this, AF_DEFAULT_INST, 1); + + instrumentdata.releaseLoopPlayMode = + afGetLoopMode(this, AF_DEFAULT_INST, 2); + instrumentdata.releaseLoopBegin = + afGetLoopStart(this, AF_DEFAULT_INST, 2); + instrumentdata.releaseLoopEnd = + afGetLoopEnd(this, AF_DEFAULT_INST, 2); + + m_fh->write("INST", 4); + writeU32(&length); + + instrumentdata.baseNote = + afGetInstParamLong(this, AF_DEFAULT_INST, AF_INST_MIDI_BASENOTE); + writeU8(&instrumentdata.baseNote); + instrumentdata.detune = + afGetInstParamLong(this, AF_DEFAULT_INST, AF_INST_NUMCENTS_DETUNE); + writeS8(&instrumentdata.detune); + instrumentdata.lowNote = + afGetInstParamLong(this, AF_DEFAULT_INST, AF_INST_MIDI_LONOTE); + writeU8(&instrumentdata.lowNote); + instrumentdata.highNote = + afGetInstParamLong(this, AF_DEFAULT_INST, AF_INST_MIDI_HINOTE); + writeU8(&instrumentdata.highNote); + instrumentdata.lowVelocity = + afGetInstParamLong(this, AF_DEFAULT_INST, AF_INST_MIDI_LOVELOCITY); + writeU8(&instrumentdata.lowVelocity); + instrumentdata.highVelocity = + afGetInstParamLong(this, AF_DEFAULT_INST, AF_INST_MIDI_HIVELOCITY); + writeU8(&instrumentdata.highVelocity); + + instrumentdata.gain = + afGetInstParamLong(this, AF_DEFAULT_INST, AF_INST_NUMDBS_GAIN); + writeS16(&instrumentdata.gain); + + writeU16(&instrumentdata.sustainLoopPlayMode); + writeU16(&instrumentdata.sustainLoopBegin); + writeU16(&instrumentdata.sustainLoopEnd); + + writeU16(&instrumentdata.releaseLoopPlayMode); + writeU16(&instrumentdata.releaseLoopBegin); + writeU16(&instrumentdata.releaseLoopEnd); + + return AF_SUCCEED; +} + +status AIFFFile::writeMARK() +{ + Track *track = getTrack(); + if (!track->markerCount) + return AF_SUCCEED; + + if (m_MARK_offset == 0) + m_MARK_offset = m_fh->tell(); + else + m_fh->seek(m_MARK_offset, File::SeekFromBeginning); + + Tag markTag("MARK"); + uint32_t length = 0; + + writeTag(&markTag); + writeU32(&length); + + AFfileoffset chunkStartPosition = m_fh->tell(); + + uint16_t numMarkers = track->markerCount; + writeU16(&numMarkers); + + for (unsigned i=0; i<numMarkers; i++) + { + uint16_t id = track->markers[i].id; + writeU16(&id); + + uint32_t position = track->markers[i].position; + writeU32(&position); + + const char *name = track->markers[i].name; + assert(name); + + // Write the name as a Pascal-style string. + writePString(name); + } + + AFfileoffset chunkEndPosition = m_fh->tell(); + length = chunkEndPosition - chunkStartPosition; + + m_fh->seek(chunkStartPosition - 4, File::SeekFromBeginning); + + writeU32(&length); + m_fh->seek(chunkEndPosition, File::SeekFromBeginning); + + return AF_SUCCEED; +} + +/* + The FVER chunk, if present, is always the first chunk in the file. +*/ +status AIFFFile::writeFVER() +{ + uint32_t chunkSize, timeStamp; + + assert(isAIFFC()); + + if (m_FVER_offset == 0) + m_FVER_offset = m_fh->tell(); + else + m_fh->seek(m_FVER_offset, File::SeekFromBeginning); + + m_fh->write("FVER", 4); + + chunkSize = 4; + writeU32(&chunkSize); + + timeStamp = AIFC_VERSION_1; + writeU32(&timeStamp); + + return AF_SUCCEED; +} + +/* + WriteMiscellaneous writes all the miscellaneous data chunks in a + file handle structure to an AIFF or AIFF-C file. +*/ +status AIFFFile::writeMiscellaneous() +{ + if (m_miscellaneousPosition == 0) + m_miscellaneousPosition = m_fh->tell(); + else + m_fh->seek(m_miscellaneousPosition, File::SeekFromBeginning); + + for (int i=0; i<m_miscellaneousCount; i++) + { + Miscellaneous *misc = &m_miscellaneous[i]; + Tag chunkType; + uint32_t chunkSize; + uint8_t padByte = 0; + + switch (misc->type) + { + case AF_MISC_NAME: + chunkType = "NAME"; break; + case AF_MISC_AUTH: + chunkType = "AUTH"; break; + case AF_MISC_COPY: + chunkType = "(c) "; break; + case AF_MISC_ANNO: + chunkType = "ANNO"; break; + case AF_MISC_MIDI: + chunkType = "MIDI"; break; + case AF_MISC_APPL: + chunkType = "APPL"; break; + } + + writeTag(&chunkType); + + chunkSize = misc->size; + writeU32(&chunkSize); + /* + Write the miscellaneous buffer and then a pad byte + if necessary. If the buffer is null, skip the space + for now. + */ + if (misc->buffer != NULL) + m_fh->write(misc->buffer, misc->size); + else + m_fh->seek(misc->size, File::SeekFromCurrent); + + if (misc->size % 2 != 0) + writeU8(&padByte); + } + + return AF_SUCCEED; +} + +void AIFFFile::initCompressionParams() +{ + Track *track = getTrack(); + if (track->f.compressionType == AF_COMPRESSION_IMA) + initIMACompressionParams(); +} + +void AIFFFile::initIMACompressionParams() +{ + Track *track = getTrack(); + + track->f.bytesPerPacket = 34 * track->f.channelCount; + track->f.framesPerPacket = 64; + + AUpvlist pv = AUpvnew(1); + AUpvsetparam(pv, 0, _AF_IMA_ADPCM_TYPE); + AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG); + long l = _AF_IMA_ADPCM_TYPE_QT; + AUpvsetval(pv, 0, &l); + + track->f.compressionParams = pv; +} + +// Read a Pascal-style string. +bool AIFFFile::readPString(char s[256]) +{ + uint8_t length; + if (m_fh->read(&length, 1) != 1) + return false; + if (m_fh->read(s, length) != static_cast<ssize_t>(length)) + return false; + s[length] = '\0'; + return true; +} + +// Write a Pascal-style string. +bool AIFFFile::writePString(const char *s) +{ + size_t length = strlen(s); + if (length > 255) + return false; + uint8_t sizeByte = static_cast<uint8_t>(length); + if (m_fh->write(&sizeByte, 1) != 1) + return false; + if (m_fh->write(s, length) != (ssize_t) length) + return false; + /* + Add a pad byte if the length of the Pascal-style string + (including the size byte) is odd. + */ + if ((length % 2) == 0) + { + uint8_t zero = 0; + if (m_fh->write(&zero, 1) != 1) + return false; + } + return true; +} + +// file: AudioFormat.cpp +/* + Audio File Library + Copyright (C) 2010, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + + +#include <assert.h> +#include <stdio.h> + +size_t AudioFormat::bytesPerSample(bool stretch3to4) const +{ + switch (sampleFormat) + { + case AF_SAMPFMT_FLOAT: + return sizeof (float); + case AF_SAMPFMT_DOUBLE: + return sizeof (double); + default: + { + int size = (sampleWidth + 7) / 8; + if (compressionType == AF_COMPRESSION_NONE && + size == 3 && stretch3to4) + size = 4; + return size; + } + } +} + +size_t AudioFormat::bytesPerFrame(bool stretch3to4) const +{ + return bytesPerSample(stretch3to4) * channelCount; +} + +size_t AudioFormat::bytesPerSample() const +{ + return bytesPerSample(!isPacked()); +} + +size_t AudioFormat::bytesPerFrame() const +{ + return bytesPerFrame(!isPacked()); +} + +bool AudioFormat::isInteger() const +{ + return sampleFormat == AF_SAMPFMT_TWOSCOMP || + sampleFormat == AF_SAMPFMT_UNSIGNED; +} + +bool AudioFormat::isSigned() const +{ + return sampleFormat == AF_SAMPFMT_TWOSCOMP; +} + +bool AudioFormat::isUnsigned() const +{ + return sampleFormat == AF_SAMPFMT_UNSIGNED; +} + +bool AudioFormat::isFloat() const +{ + return sampleFormat == AF_SAMPFMT_FLOAT || + sampleFormat == AF_SAMPFMT_DOUBLE; +} + +bool AudioFormat::isCompressed() const +{ + return compressionType != AF_COMPRESSION_NONE; +} + +bool AudioFormat::isUncompressed() const +{ + return compressionType == AF_COMPRESSION_NONE; +} + +void AudioFormat::computeBytesPerPacketPCM() +{ + assert(isUncompressed()); + int bytesPerSample = (sampleWidth + 7) / 8; + bytesPerPacket = bytesPerSample * channelCount; +} + +std::string AudioFormat::description() const +{ + std::string d; + char s[1024]; + /* sampleRate, channelCount */ + sprintf(s, "{ %7.2f Hz %d ch ", sampleRate, channelCount); + d += s; + + /* sampleFormat, sampleWidth */ + switch (sampleFormat) + { + case AF_SAMPFMT_TWOSCOMP: + sprintf(s, "%db 2 ", sampleWidth); + break; + case AF_SAMPFMT_UNSIGNED: + sprintf(s, "%db u ", sampleWidth); + break; + case AF_SAMPFMT_FLOAT: + sprintf(s, "flt "); + break; + case AF_SAMPFMT_DOUBLE: + sprintf(s, "dbl "); + break; + default: + assert(false); + break; + } + + d += s; + + /* pcm */ + sprintf(s, "(%.30g+-%.30g [%.30g,%.30g]) ", + pcm.intercept, pcm.slope, + pcm.minClip, pcm.maxClip); + d += s; + + /* byteOrder */ + switch (byteOrder) + { + case AF_BYTEORDER_BIGENDIAN: + d += "big "; + break; + case AF_BYTEORDER_LITTLEENDIAN: + d += "little "; + break; + default: + assert(false); + break; + } + + if (isCompressed()) + { + const CompressionUnit *unit = _af_compression_unit_from_id(compressionType); + assert(unit); + d += "compression: "; + d += unit->label; + } + + return d; +} + +// file: Buffer.cpp +/* + Audio File Library + Copyright (C) 2013 Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + + +#include <string.h> + +Buffer::Buffer() : m_data(0), m_size(0) +{ +} + +Buffer::Buffer(size_t size) : m_data(0), m_size(0) +{ + if (size) + m_data = ::operator new(size); + if (m_data) + { + m_size = size; + } +} + +Buffer::Buffer(const void *data, size_t size) : m_data(0), m_size(0) +{ + if (size) + m_data = ::operator new(size); + if (m_data) + { + ::memcpy(m_data, data, m_size); + m_size = size; + } +} + +Buffer::~Buffer() +{ + ::operator delete(m_data); +} + +// file: File.cpp +/* + Copyright (C) 2010, Michael Pruett. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include <assert.h> +#include <fcntl.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> + +class FilePOSIX : public File +{ +public: + FilePOSIX(int fd, AccessMode mode) : File(mode), m_fd(fd) { } + virtual ~FilePOSIX() { close(); } + + virtual int close() OVERRIDE; + virtual ssize_t read(void *data, size_t nbytes) OVERRIDE; + virtual ssize_t write(const void *data, size_t nbytes) OVERRIDE; + virtual off_t length() OVERRIDE; + virtual off_t seek(off_t offset, SeekOrigin origin) OVERRIDE; + virtual off_t tell() OVERRIDE; + +private: + int m_fd; +}; + +class FileVF : public File +{ +public: + FileVF(AFvirtualfile *vf, AccessMode mode) : File(mode), m_vf(vf) { } + virtual ~FileVF() { close(); } + + virtual int close() OVERRIDE; + virtual ssize_t read(void *data, size_t nbytes) OVERRIDE; + virtual ssize_t write(const void *data, size_t nbytes) OVERRIDE; + virtual off_t length() OVERRIDE; + virtual off_t seek(off_t offset, SeekOrigin origin) OVERRIDE; + virtual off_t tell() OVERRIDE; + +private: + AFvirtualfile *m_vf; +}; + +File *File::open(const char *path, File::AccessMode mode) +{ + int flags = 0; + if (mode == ReadAccess) + flags = O_RDONLY; + else if (mode == WriteAccess) + flags = O_CREAT | O_WRONLY | O_TRUNC; +#if defined(WIN32) || defined(__CYGWIN__) + flags |= O_BINARY; +#endif + int fd = ::open(path, flags, 0666); + if (fd == -1) + return NULL; + File *file = new FilePOSIX(fd, mode); + return file; +} + +File *File::create(int fd, File::AccessMode mode) +{ + return new FilePOSIX(fd, mode); +} + +File *File::create(AFvirtualfile *vf, File::AccessMode mode) +{ + return new FileVF(vf, mode); +} + +File::~File() +{ +} + +bool File::canSeek() +{ + return seek(0, File::SeekFromCurrent) != -1; +} + +int FilePOSIX::close() +{ + if (m_fd == -1) + return 0; + + int result = ::close(m_fd); + m_fd = -1; + return result; +} + +ssize_t FilePOSIX::read(void *data, size_t nbytes) +{ + return ::read(m_fd, data, nbytes); +} + +ssize_t FilePOSIX::write(const void *data, size_t nbytes) +{ + return ::write(m_fd, data, nbytes); +} + +off_t FilePOSIX::length() +{ + off_t current = tell(); + if (current == -1) + return -1; + off_t length = seek(0, SeekFromEnd); + if (length == -1) + return -1; + seek(current, SeekFromBeginning); + return length; +} + +off_t FilePOSIX::seek(off_t offset, File::SeekOrigin origin) +{ + int whence; + switch (origin) + { + case SeekFromBeginning: whence = SEEK_SET; break; + case SeekFromCurrent: whence = SEEK_CUR; break; + case SeekFromEnd: whence = SEEK_END; break; + default: assert(false); return -1; + } + return ::lseek(m_fd, offset, whence); +} + +off_t FilePOSIX::tell() +{ + return seek(0, File::SeekFromCurrent); +} + +int FileVF::close() +{ + if (m_vf) + af_virtual_file_destroy(m_vf); + m_vf = 0; + return 0; +} + +ssize_t FileVF::read(void *data, size_t nbytes) +{ + return m_vf->read(m_vf, data, nbytes); +} + +ssize_t FileVF::write(const void *data, size_t nbytes) +{ + return m_vf->write(m_vf, data, nbytes); +} + +off_t FileVF::length() +{ + return m_vf->length(m_vf); +} + +off_t FileVF::seek(off_t offset, SeekOrigin origin) +{ + if (origin == SeekFromEnd) + offset += length(); + return m_vf->seek(m_vf, offset, origin == SeekFromCurrent); +} + +off_t FileVF::tell() +{ + return m_vf->tell(m_vf); +} + +// file: FileHandle.cpp +/* + Audio File Library + Copyright (C) 2010-2012, Michael Pruett <michael@68k.org> + Copyright (C) 2000-2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + + +#include <stdlib.h> +#include <assert.h> + + + +static void freeInstParams (AFPVu *values, int fileFormat) +{ + if (!values) + return; + + int parameterCount = _af_units[fileFormat].instrumentParameterCount; + + for (int i=0; i<parameterCount; i++) + { + if (_af_units[fileFormat].instrumentParameters[i].type == AU_PVTYPE_PTR) + free(values[i].v); + } + + free(values); +} + +_AFfilehandle *_AFfilehandle::create(int fileFormat) +{ + switch (fileFormat) + { + case AF_FILE_RAWDATA: + return new RawFile(); + case AF_FILE_AIFF: + case AF_FILE_AIFFC: + return new AIFFFile(); + case AF_FILE_WAVE: + return new WAVEFile(); + default: + return NULL; + } +} + +_AFfilehandle::_AFfilehandle() +{ + m_valid = _AF_VALID_FILEHANDLE; + m_access = 0; + m_seekok = false; + m_fh = NULL; + m_fileName = NULL; + m_fileFormat = AF_FILE_UNKNOWN; + m_trackCount = 0; + m_tracks = NULL; + m_instrumentCount = 0; + m_instruments = NULL; + m_miscellaneousCount = 0; + m_miscellaneous = NULL; + m_formatByteOrder = 0; +} + +_AFfilehandle::~_AFfilehandle() +{ + m_valid = 0; + + free(m_fileName); + + delete [] m_tracks; + m_tracks = NULL; + m_trackCount = 0; + + if (m_instruments) + { + for (int i=0; i<m_instrumentCount; i++) + { + free(m_instruments[i].loops); + m_instruments[i].loops = NULL; + m_instruments[i].loopCount = 0; + + freeInstParams(m_instruments[i].values, m_fileFormat); + m_instruments[i].values = NULL; + } + + free(m_instruments); + m_instruments = NULL; + } + m_instrumentCount = 0; + + if (m_miscellaneous) + { + for (int i=0; i<m_miscellaneousCount; i++) + free(m_miscellaneous[i].buffer); + free(m_miscellaneous); + m_miscellaneous = NULL; + } + m_miscellaneousCount = 0; +} + +Track *_AFfilehandle::allocateTrack() +{ + assert(!m_trackCount); + assert(!m_tracks); + + m_trackCount = 1; + m_tracks = new Track[1]; + return m_tracks; +} + +Track *_AFfilehandle::getTrack(int trackID) +{ + for (int i=0; i<m_trackCount; i++) + if (m_tracks[i].id == trackID) + return &m_tracks[i]; + + _af_error(AF_BAD_TRACKID, "bad track id %d", trackID); + + return NULL; +} + +bool _AFfilehandle::checkCanRead() +{ + if (m_access != _AF_READ_ACCESS) + { + _af_error(AF_BAD_NOREADACC, "file not opened for read access"); + return false; + } + + return true; +} + +bool _AFfilehandle::checkCanWrite() +{ + if (m_access != _AF_WRITE_ACCESS) + { + _af_error(AF_BAD_NOWRITEACC, "file not opened for write access"); + return false; + } + + return true; +} + +Instrument *_AFfilehandle::getInstrument(int instrumentID) +{ + for (int i = 0; i < m_instrumentCount; i++) + if (m_instruments[i].id == instrumentID) + return &m_instruments[i]; + + _af_error(AF_BAD_INSTID, "invalid instrument id %d", instrumentID); + return NULL; +} + +Miscellaneous *_AFfilehandle::getMiscellaneous(int miscellaneousID) +{ + for (int i=0; i<m_miscellaneousCount; i++) + { + if (m_miscellaneous[i].id == miscellaneousID) + return &m_miscellaneous[i]; + } + + _af_error(AF_BAD_MISCID, "bad miscellaneous id %d", miscellaneousID); + + return NULL; +} + +status _AFfilehandle::initFromSetup(AFfilesetup setup) +{ + if (copyTracksFromSetup(setup) == AF_FAIL) + return AF_FAIL; + if (copyInstrumentsFromSetup(setup) == AF_FAIL) + return AF_FAIL; + if (copyMiscellaneousFromSetup(setup) == AF_FAIL) + return AF_FAIL; + return AF_SUCCEED; +} + +status _AFfilehandle::copyTracksFromSetup(AFfilesetup setup) +{ + if ((m_trackCount = setup->trackCount) == 0) + { + m_tracks = NULL; + return AF_SUCCEED; + } + + m_tracks = new Track[m_trackCount]; + if (!m_tracks) + return AF_FAIL; + + for (int i=0; i<m_trackCount; i++) + { + Track *track = &m_tracks[i]; + TrackSetup *trackSetup = &setup->tracks[i]; + + track->id = trackSetup->id; + track->f = trackSetup->f; + + if (track->copyMarkers(trackSetup) == AF_FAIL) + return AF_FAIL; + + track->hasAESData = trackSetup->aesDataSet; + } + + return AF_SUCCEED; +} + +status _AFfilehandle::copyInstrumentsFromSetup(AFfilesetup setup) +{ + if ((m_instrumentCount = setup->instrumentCount) == 0) + { + m_instruments = NULL; + return AF_SUCCEED; + } + + m_instruments = static_cast<Instrument *>(_af_calloc(m_instrumentCount, + sizeof (Instrument))); + if (!m_instruments) + return AF_FAIL; + + for (int i=0; i<m_instrumentCount; i++) + { + m_instruments[i].id = setup->instruments[i].id; + + // Copy loops. + if ((m_instruments[i].loopCount = setup->instruments[i].loopCount) == 0) + { + m_instruments[i].loops = NULL; + } + else + { + m_instruments[i].loops = + static_cast<Loop *>(_af_calloc(m_instruments[i].loopCount, + sizeof (Loop))); + if (!m_instruments[i].loops) + return AF_FAIL; + for (int j=0; j<m_instruments[i].loopCount; j++) + { + Loop *loop = &m_instruments[i].loops[j]; + loop->id = setup->instruments[i].loops[j].id; + loop->mode = AF_LOOP_MODE_NOLOOP; + loop->count = 0; + loop->trackid = AF_DEFAULT_TRACK; + loop->beginMarker = 2*j + 1; + loop->endMarker = 2*j + 2; + } + } + + int instParamCount; + // Copy instrument parameters. + if ((instParamCount = _af_units[setup->fileFormat].instrumentParameterCount) == 0) + { + m_instruments[i].values = NULL; + } + else + { + m_instruments[i].values = + static_cast<AFPVu *>(_af_calloc(instParamCount, sizeof (AFPVu))); + if (!m_instruments[i].values) + return AF_FAIL; + for (int j=0; j<instParamCount; j++) + { + m_instruments[i].values[j] = _af_units[setup->fileFormat].instrumentParameters[j].defaultValue; + } + } + } + + return AF_SUCCEED; +} + +status _AFfilehandle::copyMiscellaneousFromSetup(AFfilesetup setup) +{ + if ((m_miscellaneousCount = setup->miscellaneousCount) == 0) + { + m_miscellaneous = NULL; + return AF_SUCCEED; + } + + m_miscellaneous = static_cast<Miscellaneous *>(_af_calloc(m_miscellaneousCount, + sizeof (Miscellaneous))); + if (!m_miscellaneous) + return AF_FAIL; + + for (int i=0; i<m_miscellaneousCount; i++) + { + m_miscellaneous[i].id = setup->miscellaneous[i].id; + m_miscellaneous[i].type = setup->miscellaneous[i].type; + m_miscellaneous[i].size = setup->miscellaneous[i].size; + m_miscellaneous[i].position = 0; + m_miscellaneous[i].buffer = NULL; + } + + return AF_SUCCEED; +} + +template <typename T> +static bool readValue(File *f, T *value) +{ + return f->read(value, sizeof (*value)) == sizeof (*value); +} + +template <typename T> +static bool writeValue(File *f, const T *value) +{ + return f->write(value, sizeof (*value)) == sizeof (*value); +} + +template <typename T> +static T swapValue(T value, int order) +{ + if (order == AF_BYTEORDER_BIGENDIAN) + return bigToHost(value); + else if (order == AF_BYTEORDER_LITTLEENDIAN) + return littleToHost(value); + return value; +} + +template <typename T> +static bool readSwap(File *f, T *value, int order) +{ + if (!readValue(f, value)) return false; + *value = swapValue(*value, order); + return true; +} + +template <typename T> +static bool writeSwap(File *f, const T *value, int order) +{ + T t = swapValue(*value, order); + return writeValue(f, &t); +} + +bool _AFfilehandle::readU8(uint8_t *v) { return readValue(m_fh, v); } +bool _AFfilehandle::readS8(int8_t *v) { return readValue(m_fh, v); } + +bool _AFfilehandle::readU16(uint16_t *v) +{ + return readSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::readS16(int16_t *v) +{ + return readSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::readU32(uint32_t *v) +{ + return readSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::readS32(int32_t *v) +{ + return readSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::readU64(uint64_t *v) +{ + return readSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::readS64(int64_t *v) +{ + return readSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::readFloat(float *v) +{ + return readSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::readDouble(double *v) +{ + return readSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::writeU8(const uint8_t *v) { return writeValue(m_fh, v); } +bool _AFfilehandle::writeS8(const int8_t *v) { return writeValue(m_fh, v); } + +bool _AFfilehandle::writeU16(const uint16_t *v) +{ + return writeSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::writeS16(const int16_t *v) +{ + return writeSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::writeU32(const uint32_t *v) +{ + return writeSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::writeS32(const int32_t *v) +{ + return writeSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::writeU64(const uint64_t *v) +{ + return writeSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::writeS64(const int64_t *v) +{ + return writeSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::writeFloat(const float *v) +{ + return writeSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::writeDouble(const double *v) +{ + return writeSwap(m_fh, v, m_formatByteOrder); +} + +bool _AFfilehandle::readTag(Tag *t) +{ + uint32_t v; + if (m_fh->read(&v, sizeof (v)) == sizeof (v)) + { + *t = Tag(v); + return true; + } + return false; +} + +bool _AFfilehandle::writeTag(const Tag *t) +{ + uint32_t v = t->value(); + return m_fh->write(&v, sizeof (v)) == sizeof (v); +} + +// file: Instrument.cpp +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + Instrument.cpp + + Info about instrument parameters: + + Each unit has an array of _InstParamInfo structures, one for + each instrument parameter. Each of these structures describes + the inst parameters. + + id: a 4-byte id as in AIFF file + type: data type AU_PVLIST_* + name: text name + defaultValue: default value, to which it is set when a file with + instruments is first opened for writing. + + Each inst has only an array of values (_AFPVu's). Each value in the + instrument's array is the value of the corresponding index into the + unit's instparaminfo array. + + So for a unit u and an instrument i, u.instparam[N] describes + the parameter whose value is given in i.value[N]. +*/ + + + +#include <stdlib.h> + +bool InstrumentSetup::allocateLoops(int count) +{ + freeLoops(); + loops = (LoopSetup *) _af_calloc(count, sizeof (LoopSetup)); + if (loops) + { + loopCount = count; + return true; + } + return false; +} + +void InstrumentSetup::freeLoops() +{ + if (loops) + free(loops); + loops = NULL; + loopCount = 0; +} + +/* + Initialize instrument id list for audio file. +*/ +void afInitInstIDs (AFfilesetup setup, const int *instids, int ninsts) +{ + if (!_af_filesetup_ok(setup)) + return; + + if (!_af_unique_ids(instids, ninsts, "instrument", AF_BAD_INSTID)) + return; + + _af_setup_free_instruments(setup); + + setup->instrumentCount = ninsts; + setup->instrumentSet = true; + + setup->instruments = _af_instsetup_new(setup->instrumentCount); + + for (int i=0; i < setup->instrumentCount; i++) + setup->instruments[i].id = instids[i]; +} + +int afGetInstIDs (AFfilehandle file, int *instids) +{ + if (!_af_filehandle_ok(file)) + return -1; + + if (instids) + for (int i=0; i < file->m_instrumentCount; i++) + instids[i] = file->m_instruments[i].id; + + return file->m_instrumentCount; +} + +/* + This routine checks and sets instrument parameters. + npv is number of valid AUpvlist pairs. +*/ +void _af_instparam_set (AFfilehandle file, int instid, AUpvlist pvlist, int npv) +{ + if (!_af_filehandle_ok(file)) + return; + + if (!file->checkCanWrite()) + return; + + Instrument *instrument = file->getInstrument(instid); + if (!instrument) + return; + + if (AUpvgetmaxitems(pvlist) < npv) + npv = AUpvgetmaxitems(pvlist); + + for (int i=0; i < npv; i++) + { + int param; + + AUpvgetparam(pvlist, i, &param); + + int j; + if ((j = _af_instparam_index_from_id(file->m_fileFormat, param)) == -1) + /* no parameter with that id; ignore */ + continue; + + if (!file->isInstrumentParameterValid(pvlist, i)) + /* bad parameter value; ignore */ + continue; + + int type = _af_units[file->m_fileFormat].instrumentParameters[j].type; + + switch (type) + { + case AU_PVTYPE_LONG: + AUpvgetval(pvlist, i, &instrument->values[j].l); + break; + case AU_PVTYPE_DOUBLE: + AUpvgetval(pvlist, i, &instrument->values[j].d); + break; + case AU_PVTYPE_PTR: + AUpvgetval(pvlist, i, &instrument->values[j].v); + break; + default: + return; + } + } +} + +void afSetInstParams (AFfilehandle file, int instid, AUpvlist pvlist, int npv) +{ + _af_instparam_set(file, instid, pvlist, npv); +} + +void afSetInstParamLong (AFfilehandle file, int instid, int param, long value) +{ + AUpvlist pvlist = AUpvnew(1); + + AUpvsetparam(pvlist, 0, param); + AUpvsetvaltype(pvlist, 0, AU_PVTYPE_LONG); + AUpvsetval(pvlist, 0, &value); + + _af_instparam_set(file, instid, pvlist, 1); + + AUpvfree(pvlist); +} + +/* + This routine gets instrument parameters. + npv is number of valid AUpvlist pairs +*/ +void _af_instparam_get (AFfilehandle file, int instid, AUpvlist pvlist, int npv, + bool forceLong) +{ + if (!_af_filehandle_ok(file)) + return; + + Instrument *instrument = file->getInstrument(instid); + if (!instrument) + return; + + if (AUpvgetmaxitems(pvlist) < npv) + npv = AUpvgetmaxitems(pvlist); + + for (int i=0; i < npv; i++) + { + int param; + AUpvgetparam(pvlist, i, &param); + + int j; + if ((j = _af_instparam_index_from_id(file->m_fileFormat, param)) == -1) + /* no parameter with that id; ignore */ + continue; + + int type = _af_units[file->m_fileFormat].instrumentParameters[j].type; + + /* + forceLong is true when this routine called by + afGetInstParamLong(). + */ + if (forceLong && type != AU_PVTYPE_LONG) + { + _af_error(AF_BAD_INSTPTYPE, "type of instrument parameter %d is not AU_PVTYPE_LONG", param); + continue; + } + + AUpvsetvaltype(pvlist, i, type); + + switch (type) + { + case AU_PVTYPE_LONG: + AUpvsetval(pvlist, i, &instrument->values[j].l); + break; + case AU_PVTYPE_DOUBLE: + AUpvsetval(pvlist, i, &instrument->values[j].d); + break; + case AU_PVTYPE_PTR: + AUpvsetval(pvlist, i, &instrument->values[j].v); + break; + + default: + _af_error(AF_BAD_INSTPTYPE, "invalid instrument parameter type %d", type); + return; + } + } +} + +/* + afGetInstParams -- get a parameter-value array containing + instrument parameters for the specified instrument chunk +*/ +void afGetInstParams (AFfilehandle file, int inst, AUpvlist pvlist, int npv) +{ + _af_instparam_get(file, inst, pvlist, npv, false); +} + +long afGetInstParamLong (AFfilehandle file, int inst, int param) +{ + long val; + AUpvlist pvlist = AUpvnew(1); + + AUpvsetparam(pvlist, 0, param); + AUpvsetvaltype(pvlist, 0, AU_PVTYPE_LONG); + + _af_instparam_get(file, inst, pvlist, 1, true); + + AUpvgetval(pvlist, 0, &val); + AUpvfree(pvlist); + + return(val); +} + +/* + Search _af_units[fileFormat].instrumentParameters for the instrument + parameter with the specified id. + + Report an error and return -1 if no such instrument parameter + exists. +*/ + +int _af_instparam_index_from_id (int filefmt, int id) +{ + int i; + + for (i = 0; i < _af_units[filefmt].instrumentParameterCount; i++) + if (_af_units[filefmt].instrumentParameters[i].id == id) + break; + + if (i == _af_units[filefmt].instrumentParameterCount) + { + _af_error(AF_BAD_INSTPID, "invalid instrument parameter id %d", + id); + return -1; + } + + return i; +} + +Loop *Instrument::getLoop(int loopID) +{ + for (int i=0; i<loopCount; i++) + if (loops[i].id == loopID) + return &loops[i]; + + _af_error(AF_BAD_LOOPID, "no loop with id %d for instrument %d\n", + loopID, id); + return NULL; +} + +// file: Loop.cpp +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + Loop.cpp + + All routines that operate on loops. +*/ + + + +void afInitLoopIDs (AFfilesetup setup, int instid, const int *loopids, int nloops) +{ + if (!_af_filesetup_ok(setup)) + return; + + if (!_af_unique_ids(loopids, nloops, "loop", AF_BAD_LOOPID)) + return; + + InstrumentSetup *instrument = setup->getInstrument(instid); + if (!instrument) + return; + + instrument->freeLoops(); + if (!instrument->allocateLoops(nloops)) + return; + + for (int i=0; i < nloops; i++) + instrument->loops[i].id = loopids[i]; +} + +int afGetLoopIDs (AFfilehandle file, int instid, int *loopids) +{ + if (!_af_filehandle_ok(file)) + return AF_FAIL; + + Instrument *instrument = file->getInstrument(instid); + if (!instrument) + return AF_FAIL; + + if (loopids) + for (int i=0; i < instrument->loopCount; i++) + loopids[i] = instrument->loops[i].id; + + return instrument->loopCount; +} + +/* + getLoop returns pointer to requested loop if it exists, and if + mustWrite is true, only if handle is writable. +*/ + +static Loop *getLoop (AFfilehandle handle, int instid, int loopid, + bool mustWrite) +{ + if (!_af_filehandle_ok(handle)) + return NULL; + + if (mustWrite && !handle->checkCanWrite()) + return NULL; + + Instrument *instrument = handle->getInstrument(instid); + if (!instrument) + return NULL; + + return instrument->getLoop(loopid); +} + +/* + Set loop mode (as in AF_LOOP_MODE_...). +*/ +void afSetLoopMode (AFfilehandle file, int instid, int loopid, int mode) +{ + Loop *loop = getLoop(file, instid, loopid, true); + + if (!loop) + return; + + if (mode != AF_LOOP_MODE_NOLOOP && + mode != AF_LOOP_MODE_FORW && + mode != AF_LOOP_MODE_FORWBAKW) + { + _af_error(AF_BAD_LOOPMODE, "unrecognized loop mode %d", mode); + return; + } + + loop->mode = mode; +} + +/* + Get loop mode (as in AF_LOOP_MODE_...). +*/ +int afGetLoopMode (AFfilehandle file, int instid, int loopid) +{ + Loop *loop = getLoop(file, instid, loopid, false); + + if (!loop) + return -1; + + return loop->mode; +} + +/* + Set loop count. +*/ +int afSetLoopCount (AFfilehandle file, int instid, int loopid, int count) +{ + Loop *loop = getLoop(file, instid, loopid, true); + + if (!loop) + return AF_FAIL; + + if (count < 1) + { + _af_error(AF_BAD_LOOPCOUNT, "invalid loop count: %d", count); + return AF_FAIL; + } + + loop->count = count; + return AF_SUCCEED; +} + +/* + Get loop count. +*/ +int afGetLoopCount(AFfilehandle file, int instid, int loopid) +{ + Loop *loop = getLoop(file, instid, loopid, false); + + if (!loop) + return -1; + + return loop->count; +} + +/* + Set loop start marker id in the file structure +*/ +void afSetLoopStart(AFfilehandle file, int instid, int loopid, int markid) +{ + Loop *loop = getLoop(file, instid, loopid, true); + + if (!loop) + return; + + loop->beginMarker = markid; +} + +/* + Get loop start marker id. +*/ +int afGetLoopStart (AFfilehandle file, int instid, int loopid) +{ + Loop *loop = getLoop(file, instid, loopid, false); + + if (!loop) + return -1; + + return loop->beginMarker; +} + +/* + Set loop start frame in the file structure. +*/ +int afSetLoopStartFrame (AFfilehandle file, int instid, int loopid, AFframecount startFrame) +{ + Loop *loop = getLoop(file, instid, loopid, true); + + if (!loop) + return -1; + + if (startFrame < 0) + { + _af_error(AF_BAD_FRAME, "loop start frame must not be negative"); + return AF_FAIL; + } + + int trackid = loop->trackid; + int beginMarker = loop->beginMarker; + + afSetMarkPosition(file, trackid, beginMarker, startFrame); + return AF_SUCCEED; +} + +/* + Get loop start frame. +*/ +AFframecount afGetLoopStartFrame (AFfilehandle file, int instid, int loopid) +{ + Loop *loop = getLoop(file, instid, loopid, false); + if (!loop) + return -1; + + int trackid = loop->trackid; + int beginMarker = loop->beginMarker; + + return afGetMarkPosition(file, trackid, beginMarker); +} + +/* + Set loop track id. +*/ +void afSetLoopTrack (AFfilehandle file, int instid, int loopid, int track) +{ + Loop *loop = getLoop(file, instid, loopid, true); + + if (!loop) return; + + loop->trackid = track; +} + +/* + Get loop track. +*/ +int afGetLoopTrack (AFfilehandle file, int instid, int loopid) +{ + Loop *loop = getLoop(file, instid, loopid, false); + + if (!loop) + return -1; + + return loop->trackid; +} + +/* + Set loop end frame marker id. +*/ +void afSetLoopEnd (AFfilehandle file, int instid, int loopid, int markid) +{ + Loop *loop = getLoop(file, instid, loopid, true); + + if (!loop) + return; + + loop->endMarker = markid; +} + +/* + Get loop end frame marker id. +*/ +int afGetLoopEnd (AFfilehandle file, int instid, int loopid) +{ + Loop *loop = getLoop(file, instid, loopid, false); + + if (!loop) + return -1; + + return loop->endMarker; +} + +/* + Set loop end frame. +*/ +int afSetLoopEndFrame (AFfilehandle file, int instid, int loopid, AFframecount endFrame) +{ + Loop *loop = getLoop(file, instid, loopid, true); + if (!loop) + return -1; + + if (endFrame < 0) + { + _af_error(AF_BAD_FRAME, "loop end frame must not be negative"); + return AF_FAIL; + } + + int trackid = loop->trackid; + int endMarker = loop->endMarker; + + afSetMarkPosition(file, trackid, endMarker, endFrame); + return AF_SUCCEED; +} + +/* + Get loop end frame. +*/ + +AFframecount afGetLoopEndFrame (AFfilehandle file, int instid, int loopid) +{ + Loop *loop = getLoop(file, instid, loopid, false); + + if (!loop) + return -1; + + int trackid = loop->trackid; + int endMarker = loop->endMarker; + + return afGetMarkPosition(file, trackid, endMarker); +} + +// file: Marker.cpp +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + Marker.cpp + + This file contains routines for dealing with loop markers. +*/ + + +#include <string.h> +#include <stdlib.h> +#include <assert.h> + + +void afInitMarkIDs(AFfilesetup setup, int trackid, const int *markids, int nmarks) +{ + if (!_af_filesetup_ok(setup)) + return; + + TrackSetup *track = setup->getTrack(trackid); + if (!track) + return; + + if (track->markers != NULL) + { + for (int i=0; i<track->markerCount; i++) + { + if (track->markers[i].name != NULL) + free(track->markers[i].name); + if (track->markers[i].comment != NULL) + free(track->markers[i].comment); + } + free(track->markers); + } + + track->markers = (MarkerSetup *) _af_calloc(nmarks, sizeof (struct MarkerSetup)); + track->markerCount = nmarks; + + for (int i=0; i<nmarks; i++) + { + track->markers[i].id = markids[i]; + track->markers[i].name = _af_strdup(""); + track->markers[i].comment = _af_strdup(""); + } + + track->markersSet = true; +} + +void afInitMarkName(AFfilesetup setup, int trackid, int markid, + const char *namestr) +{ + int markno; + int length; + + if (!_af_filesetup_ok(setup)) + return; + + TrackSetup *track = setup->getTrack(trackid); + if (!track) + return; + + for (markno=0; markno<track->markerCount; markno++) + { + if (track->markers[markno].id == markid) + break; + } + + if (markno == track->markerCount) + { + _af_error(AF_BAD_MARKID, "no marker id %d for file setup", markid); + return; + } + + length = strlen(namestr); + if (length > 255) + { + _af_error(AF_BAD_STRLEN, + "warning: marker name truncated to 255 characters"); + length = 255; + } + + if (track->markers[markno].name) + free(track->markers[markno].name); + if ((track->markers[markno].name = (char *) _af_malloc(length+1)) == NULL) + return; + strncpy(track->markers[markno].name, namestr, length); + /* + The null terminator is not set by strncpy if + strlen(namestr) > length. Set it here. + */ + track->markers[markno].name[length] = '\0'; +} + +void afInitMarkComment(AFfilesetup setup, int trackid, int markid, + const char *commstr) +{ + int markno; + int length; + + if (!_af_filesetup_ok(setup)) + return; + + TrackSetup *track = setup->getTrack(trackid); + if (!track) + return; + + for (markno=0; markno<track->markerCount; markno++) + { + if (track->markers[markno].id == markid) + break; + } + + if (markno == track->markerCount) + { + _af_error(AF_BAD_MARKID, "no marker id %d for file setup", markid); + return; + } + + length = strlen(commstr); + + if (track->markers[markno].comment) + free(track->markers[markno].comment); + if ((track->markers[markno].comment = (char *) _af_malloc(length+1)) == NULL) + return; + strcpy(track->markers[markno].comment, commstr); +} + +char *afGetMarkName (AFfilehandle file, int trackid, int markid) +{ + if (!_af_filehandle_ok(file)) + return NULL; + + Track *track = file->getTrack(trackid); + if (!track) + return NULL; + + Marker *marker = track->getMarker(markid); + if (!marker) + return NULL; + + return marker->name; +} + +char *afGetMarkComment (AFfilehandle file, int trackid, int markid) +{ + if (!_af_filehandle_ok(file)) + return NULL; + + Track *track = file->getTrack(trackid); + if (!track) + return NULL; + + Marker *marker = track->getMarker(markid); + if (!marker) + return NULL; + + return marker->comment; +} + +void afSetMarkPosition (AFfilehandle file, int trackid, int markid, + AFframecount position) +{ + if (!_af_filehandle_ok(file)) + return; + + if (!file->checkCanWrite()) + return; + + Track *track = file->getTrack(trackid); + if (!track) + return; + + Marker *marker = track->getMarker(markid); + if (!marker) + return; + + if (position < 0) + { + _af_error(AF_BAD_MARKPOS, "invalid marker position %jd", + static_cast<intmax_t>(position)); + position = 0; + } + + marker->position = position; +} + +int afGetMarkIDs (AFfilehandle file, int trackid, int markids[]) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + if (markids != NULL) + { + for (int i=0; i<track->markerCount; i++) + { + markids[i] = track->markers[i].id; + } + } + + return track->markerCount; +} + +AFframecount afGetMarkPosition (AFfilehandle file, int trackid, int markid) +{ + if (!_af_filehandle_ok(file)) + return 0L; + + Track *track = file->getTrack(trackid); + if (!track) + return 0L; + + Marker *marker = track->getMarker(markid); + if (!marker) + return 0L; + + return marker->position; +} + +Marker *_af_marker_new (int count) +{ + Marker *markers = (Marker *) _af_calloc(count, sizeof (Marker)); + if (markers == NULL) + return NULL; + + return markers; +} + +// file: Miscellaneous.cpp +/* + Audio File Library + Copyright (C) 1998, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + Miscellaneous.cpp + + This file contains routines for dealing with the Audio File + Library's internal miscellaneous data types. +*/ + + +#include <algorithm> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + + +void afInitMiscIDs (AFfilesetup setup, const int *ids, int nids) +{ + if (!_af_filesetup_ok(setup)) + return; + + if (setup->miscellaneous != NULL) + { + free(setup->miscellaneous); + } + + setup->miscellaneousCount = nids; + + if (nids == 0) + setup->miscellaneous = NULL; + else + { + setup->miscellaneous = (MiscellaneousSetup *) _af_calloc(nids, + sizeof (MiscellaneousSetup)); + + if (setup->miscellaneous == NULL) + return; + + for (int i=0; i<nids; i++) + { + setup->miscellaneous[i].id = ids[i]; + setup->miscellaneous[i].type = 0; + setup->miscellaneous[i].size = 0; + } + } + + setup->miscellaneousSet = true; +} + +int afGetMiscIDs (AFfilehandle file, int *ids) +{ + if (!_af_filehandle_ok(file)) + return -1; + + if (ids != NULL) + { + for (int i=0; i<file->m_miscellaneousCount; i++) + { + ids[i] = file->m_miscellaneous[i].id; + } + } + + return file->m_miscellaneousCount; +} + +void afInitMiscType (AFfilesetup setup, int miscellaneousid, int type) +{ + if (!_af_filesetup_ok(setup)) + return; + + MiscellaneousSetup *miscellaneous = setup->getMiscellaneous(miscellaneousid); + if (miscellaneous) + miscellaneous->type = type; +} + +int afGetMiscType (AFfilehandle file, int miscellaneousid) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Miscellaneous *miscellaneous = file->getMiscellaneous(miscellaneousid); + if (miscellaneous) + return miscellaneous->type; + return -1; +} + +void afInitMiscSize (AFfilesetup setup, int miscellaneousid, int size) +{ + if (!_af_filesetup_ok(setup)) + return; + + MiscellaneousSetup *miscellaneous = setup->getMiscellaneous(miscellaneousid); + if (miscellaneous) + miscellaneous->size = size; +} + +int afGetMiscSize (AFfilehandle file, int miscellaneousid) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Miscellaneous *miscellaneous = file->getMiscellaneous(miscellaneousid); + if (miscellaneous) + return miscellaneous->size; + return -1; +} + +int afWriteMisc (AFfilehandle file, int miscellaneousid, const void *buf, int bytes) +{ + if (!_af_filehandle_ok(file)) + return -1; + + if (!file->checkCanWrite()) + return -1; + + Miscellaneous *miscellaneous = file->getMiscellaneous(miscellaneousid); + if (!miscellaneous) + return -1; + + if (bytes <= 0) + { + _af_error(AF_BAD_MISCSIZE, "invalid size (%d) for miscellaneous chunk", bytes); + return -1; + } + + if (miscellaneous->buffer == NULL && miscellaneous->size != 0) + { + miscellaneous->buffer = _af_malloc(miscellaneous->size); + if (miscellaneous->buffer == NULL) + return -1; + memset(miscellaneous->buffer, 0, miscellaneous->size); + } + + int localsize = std::min(bytes, + miscellaneous->size - miscellaneous->position); + memcpy((char *) miscellaneous->buffer + miscellaneous->position, + buf, localsize); + miscellaneous->position += localsize; + return localsize; +} + +int afReadMisc (AFfilehandle file, int miscellaneousid, void *buf, int bytes) +{ + if (!_af_filehandle_ok(file)) + return -1; + + if (!file->checkCanRead()) + return -1; + + Miscellaneous *miscellaneous = file->getMiscellaneous(miscellaneousid); + if (!miscellaneous) + return -1; + + if (bytes <= 0) + { + _af_error(AF_BAD_MISCSIZE, "invalid size (%d) for miscellaneous chunk", bytes); + return -1; + } + + int localsize = std::min(bytes, + miscellaneous->size - miscellaneous->position); + memcpy(buf, (char *) miscellaneous->buffer + miscellaneous->position, + localsize); + miscellaneous->position += localsize; + return localsize; +} + +int afSeekMisc (AFfilehandle file, int miscellaneousid, int offset) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Miscellaneous *miscellaneous = file->getMiscellaneous(miscellaneousid); + if (!miscellaneous) + return -1; + + if (offset >= miscellaneous->size) + { + _af_error(AF_BAD_MISCSEEK, + "offset %d too big for miscellaneous chunk %d " + "(%d data bytes)", + offset, miscellaneousid, miscellaneous->size); + return -1; + } + + miscellaneous->position = offset; + + return offset; +} + +// file: PacketTable.cpp +/* + Audio File Library + Copyright (C) 2013 Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + + +PacketTable::PacketTable(int64_t numValidFrames, int32_t primingFrames, + int32_t remainderFrames) : + m_numValidFrames(numValidFrames), + m_primingFrames(primingFrames), + m_remainderFrames(remainderFrames) +{ +} + +PacketTable::PacketTable() +{ + m_numValidFrames = 0; + m_primingFrames = 0; + m_remainderFrames = 0; +} + +PacketTable::~PacketTable() +{ +} + +void PacketTable::setNumValidFrames(int64_t numValidFrames) +{ + m_numValidFrames = numValidFrames; +} + +void PacketTable::setPrimingFrames(int32_t primingFrames) +{ + m_primingFrames = primingFrames; +} + +void PacketTable::setRemainderFrames(int32_t remainderFrames) +{ + m_remainderFrames = remainderFrames; +} + +void PacketTable::append(size_t bytesPerPacket) +{ + m_bytesPerPacket.push_back(bytesPerPacket); +} + +AFfileoffset PacketTable::startOfPacket(size_t packet) const +{ + AFfileoffset offset = 0; + for (size_t i=0; i<packet; i++) + offset += m_bytesPerPacket[i]; + return offset; +} + +// file: Raw.cpp +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + Raw.cpp + + This file contains code for reading and writing raw audio + data files. +*/ + + + +static const _AFfilesetup rawDefaultFileSetup = +{ + _AF_VALID_FILESETUP, // valid + AF_FILE_RAWDATA, // fileFormat + true, // trackSet + true, // instrumentSet + true, // miscellaneousSet + 1, // trackCount + NULL, // tracks + 0, // instrumentCount + NULL, // instruments + 0, // miscellaneousCount + NULL // miscellaneous +}; + +const int _af_raw_compression_types[_AF_RAW_NUM_COMPTYPES] = +{ + AF_COMPRESSION_G711_ULAW, + AF_COMPRESSION_G711_ALAW +}; + +bool RawFile::recognize(File *fh) +{ + return false; +} + +status RawFile::readInit(AFfilesetup fileSetup) +{ + if (!fileSetup) + { + _af_error(AF_BAD_FILESETUP, "a valid AFfilesetup is required for reading raw data"); + return AF_FAIL; + } + + if (initFromSetup(fileSetup) == AF_FAIL) + return AF_FAIL; + + TrackSetup *trackSetup = fileSetup->getTrack(); + if (!trackSetup) + return AF_FAIL; + + Track *track = getTrack(); + + /* Set the track's data offset. */ + if (trackSetup->dataOffsetSet) + track->fpos_first_frame = trackSetup->dataOffset; + else + track->fpos_first_frame = 0; + + /* Set the track's frame count. */ + if (trackSetup->frameCountSet) + { + track->totalfframes = trackSetup->frameCount; + } + else + { + AFfileoffset filesize = m_fh->length(); + if (filesize == -1) + track->totalfframes = -1; + else + { + /* Ensure that the data offset is valid. */ + if (track->fpos_first_frame > filesize) + { + _af_error(AF_BAD_FILESETUP, "data offset is larger than file size"); + return AF_FAIL; + } + + filesize -= track->fpos_first_frame; + track->totalfframes = filesize / (int) _af_format_frame_size(&track->f, false); + } + track->data_size = filesize; + } + + return AF_SUCCEED; +} + +status RawFile::writeInit(AFfilesetup setup) +{ + if (initFromSetup(setup) == AF_FAIL) + return AF_FAIL; + + TrackSetup *trackSetup = setup->getTrack(); + if (!trackSetup) + return AF_FAIL; + + Track *track = getTrack(); + + if (trackSetup->dataOffsetSet) + track->fpos_first_frame = trackSetup->dataOffset; + else + track->fpos_first_frame = 0; + + return AF_SUCCEED; +} + +status RawFile::update() +{ + return AF_SUCCEED; +} + +AFfilesetup RawFile::completeSetup(AFfilesetup setup) +{ + AFfilesetup newSetup; + + if (setup->trackSet && setup->trackCount != 1) + { + _af_error(AF_BAD_FILESETUP, "raw file must have exactly one track"); + return AF_NULL_FILESETUP; + } + + TrackSetup *track = setup->getTrack(); + if (!track) + { + _af_error(AF_BAD_FILESETUP, "could not access track in file setup"); + return AF_NULL_FILESETUP; + } + + if (track->aesDataSet) + { + _af_error(AF_BAD_FILESETUP, "raw file cannot have AES data"); + return AF_NULL_FILESETUP; + } + + if (track->markersSet && track->markerCount != 0) + { + _af_error(AF_BAD_NUMMARKS, "raw file cannot have markers"); + return AF_NULL_FILESETUP; + } + + if (setup->instrumentSet && setup->instrumentCount != 0) + { + _af_error(AF_BAD_NUMINSTS, "raw file cannot have instruments"); + return AF_NULL_FILESETUP; + } + + if (setup->miscellaneousSet && setup->miscellaneousCount != 0) + { + _af_error(AF_BAD_NUMMISC, "raw file cannot have miscellaneous data"); + return AF_NULL_FILESETUP; + } + + newSetup = (_AFfilesetup *) _af_malloc(sizeof (_AFfilesetup)); + *newSetup = rawDefaultFileSetup; + + newSetup->tracks = (TrackSetup *) _af_malloc(sizeof (TrackSetup)); + newSetup->tracks[0] = setup->tracks[0]; + newSetup->tracks[0].f.compressionParams = NULL; + + newSetup->tracks[0].markerCount = 0; + newSetup->tracks[0].markers = NULL; + + return newSetup; +} + +// file: Setup.cpp +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + Setup.cpp +*/ + + +#include <stdlib.h> +#include <string.h> + + +static const _AFfilesetup _af_default_file_setup = +{ + _AF_VALID_FILESETUP, /* valid */ +#if WORDS_BIGENDIAN + AF_FILE_AIFFC, /* file format */ +#else + AF_FILE_WAVE, /* file format */ +#endif + false, /* trackSet */ + false, /* instrumentSet */ + false, /* miscellaneousSet */ + 1, /* trackCount */ + NULL, /* tracks */ + 1, /* instrumentCount */ + NULL, /* instruments */ + 0, /* miscellaneousCount */ + NULL /* miscellaneous */ +}; + +static const InstrumentSetup _af_default_instrumentsetup = +{ + 0, /* id */ + 2, /* loopCount */ + NULL, /* loops */ + false /* loopSet */ +}; + +static const TrackSetup _af_default_tracksetup = +{ + 0, + { + 44100.0, + AF_SAMPFMT_TWOSCOMP, + 16, + _AF_BYTEORDER_NATIVE, + { SLOPE_INT16, 0, MIN_INT16, MAX_INT16 }, + 2, + AF_COMPRESSION_NONE, + NULL + }, + false, /* rateSet */ + false, /* sampleFormatSet */ + false, /* sampleWidthSet */ + false, /* byteOrderSet */ + false, /* channelCountSet */ + false, /* compressionSet */ + false, /* aesDataSet */ + false, /* markersSet */ + false, /* dataOffsetSet */ + false, /* frameCountSet */ + + 4, /* markerCount */ + NULL, /* markers */ + 0, /* dataOffset */ + 0 /* frameCount */ +}; + +TrackSetup *_af_tracksetup_new (int trackCount) +{ + TrackSetup *tracks; + + if (trackCount == 0) + return NULL; + + tracks = (TrackSetup *) _af_calloc(trackCount, sizeof (TrackSetup)); + if (tracks == NULL) + return NULL; + + for (int i=0; i<trackCount; i++) + { + tracks[i] = _af_default_tracksetup; + + tracks[i].id = AF_DEFAULT_TRACK + i; + + /* XXXmpruett deal with compression */ + + _af_set_sample_format(&tracks[i].f, tracks[i].f.sampleFormat, + tracks[i].f.sampleWidth); + + if (tracks[i].markerCount == 0) + tracks[i].markers = NULL; + else + { + tracks[i].markers = (MarkerSetup *) _af_calloc(tracks[i].markerCount, + sizeof (MarkerSetup)); + + if (tracks[i].markers == NULL) + return NULL; + + for (int j=0; j<tracks[i].markerCount; j++) + { + tracks[i].markers[j].id = j+1; + + tracks[i].markers[j].name = _af_strdup(""); + if (tracks[i].markers[j].name == NULL) + return NULL; + + tracks[i].markers[j].comment = _af_strdup(""); + if (tracks[i].markers[j].comment == NULL) + return NULL; + } + } + } + + return tracks; +} + +InstrumentSetup *_af_instsetup_new (int instrumentCount) +{ + InstrumentSetup *instruments; + + if (instrumentCount == 0) + return NULL; + instruments = (InstrumentSetup *) _af_calloc(instrumentCount, sizeof (InstrumentSetup)); + if (instruments == NULL) + return NULL; + + for (int i=0; i<instrumentCount; i++) + { + instruments[i] = _af_default_instrumentsetup; + instruments[i].id = AF_DEFAULT_INST + i; + if (instruments[i].loopCount == 0) + instruments[i].loops = NULL; + else + { + instruments[i].loops = (LoopSetup *) _af_calloc(instruments[i].loopCount, sizeof (LoopSetup)); + if (instruments[i].loops == NULL) + return NULL; + + for (int j=0; j<instruments[i].loopCount; j++) + instruments[i].loops[j].id = j+1; + } + } + + return instruments; +} + +AFfilesetup afNewFileSetup (void) +{ + AFfilesetup setup; + + setup = (_AFfilesetup *) _af_malloc(sizeof (_AFfilesetup)); + if (setup == NULL) return AF_NULL_FILESETUP; + + *setup = _af_default_file_setup; + + setup->tracks = _af_tracksetup_new(setup->trackCount); + + setup->instruments = _af_instsetup_new(setup->instrumentCount); + + if (setup->miscellaneousCount == 0) + setup->miscellaneous = NULL; + else + { + setup->miscellaneous = (MiscellaneousSetup *) _af_calloc(setup->miscellaneousCount, + sizeof (MiscellaneousSetup)); + for (int i=0; i<setup->miscellaneousCount; i++) + { + setup->miscellaneous[i].id = i+1; + setup->miscellaneous[i].type = 0; + setup->miscellaneous[i].size = 0; + } + } + + return setup; +} + +/* + Free the specified track's markers and their subfields. +*/ +void _af_setup_free_markers (AFfilesetup setup, int trackno) +{ + if (setup->tracks[trackno].markerCount != 0) + { + for (int i=0; i<setup->tracks[trackno].markerCount; i++) + { + free(setup->tracks[trackno].markers[i].name); + free(setup->tracks[trackno].markers[i].comment); + } + + free(setup->tracks[trackno].markers); + } + + setup->tracks[trackno].markers = NULL; + setup->tracks[trackno].markerCount = 0; +} + +/* + Free the specified setup's tracks and their subfields. +*/ +void _af_setup_free_tracks (AFfilesetup setup) +{ + if (setup->tracks) + { + for (int i=0; i<setup->trackCount; i++) + { + _af_setup_free_markers(setup, i); + } + + free(setup->tracks); + } + + setup->tracks = NULL; + setup->trackCount = 0; +} + +/* + Free the specified setup's instruments and their subfields. +*/ +void _af_setup_free_instruments (AFfilesetup setup) +{ + if (setup->instruments) + { + for (int i=0; i < setup->instrumentCount; i++) + setup->instruments[i].freeLoops(); + + free(setup->instruments); + } + + setup->instruments = NULL; + setup->instrumentCount = 0; +} + +void afFreeFileSetup (AFfilesetup setup) +{ + if (!_af_filesetup_ok(setup)) + return; + + _af_setup_free_tracks(setup); + + _af_setup_free_instruments(setup); + + if (setup->miscellaneousCount) + { + free(setup->miscellaneous); + setup->miscellaneous = NULL; + setup->miscellaneousCount = 0; + } + + memset(setup, 0, sizeof (_AFfilesetup)); + free(setup); +} + +void afInitFileFormat (AFfilesetup setup, int filefmt) +{ + if (!_af_filesetup_ok(setup)) + return; + + if (filefmt < 0 || filefmt >= _AF_NUM_UNITS) + { + _af_error(AF_BAD_FILEFMT, "unrecognized file format %d", + filefmt); + return; + } + + if (!_af_units[filefmt].implemented) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, + "%s format not currently supported", + _af_units[filefmt].name); + return; + } + + setup->fileFormat = filefmt; +} + +void afInitChannels (AFfilesetup setup, int trackid, int channels) +{ + if (!_af_filesetup_ok(setup)) + return; + + TrackSetup *track = setup->getTrack(trackid); + if (!track) + return; + + if (channels < 1) + { + _af_error(AF_BAD_CHANNELS, "invalid number of channels %d", + channels); + return; + } + + track->f.channelCount = channels; + track->channelCountSet = true; +} + +void afInitSampleFormat (AFfilesetup setup, int trackid, int sampfmt, int sampwidth) +{ + if (!_af_filesetup_ok(setup)) + return; + + TrackSetup *track = setup->getTrack(trackid); + if (!track) + return; + + _af_set_sample_format(&track->f, sampfmt, sampwidth); + + track->sampleFormatSet = true; + track->sampleWidthSet = true; +} + +void afInitByteOrder (AFfilesetup setup, int trackid, int byteorder) +{ + if (!_af_filesetup_ok(setup)) + return; + + TrackSetup *track = setup->getTrack(trackid); + if (!track) + return; + + if (byteorder != AF_BYTEORDER_BIGENDIAN && + byteorder != AF_BYTEORDER_LITTLEENDIAN) + { + _af_error(AF_BAD_BYTEORDER, "invalid byte order %d", byteorder); + return; + } + + track->f.byteOrder = byteorder; + track->byteOrderSet = true; +} + +void afInitRate (AFfilesetup setup, int trackid, double rate) +{ + if (!_af_filesetup_ok(setup)) + return; + + TrackSetup *track = setup->getTrack(trackid); + if (!track) + return; + + if (rate <= 0.0) + { + _af_error(AF_BAD_RATE, "invalid sample rate %.30g", rate); + return; + } + + track->f.sampleRate = rate; + track->rateSet = true; +} + +/* + track data: data offset within the file (initialized for raw reading only) +*/ +void afInitDataOffset (AFfilesetup setup, int trackid, AFfileoffset offset) +{ + if (!_af_filesetup_ok(setup)) + return; + + TrackSetup *track = setup->getTrack(trackid); + if (!track) + return; + + if (offset < 0) + { + _af_error(AF_BAD_DATAOFFSET, "invalid data offset %jd", + static_cast<intmax_t>(offset)); + return; + } + + track->dataOffset = offset; + track->dataOffsetSet = true; +} + +/* + track data: data offset within the file (initialized for raw reading only) +*/ +void afInitFrameCount (AFfilesetup setup, int trackid, AFfileoffset count) +{ + if (!_af_filesetup_ok(setup)) + return; + + TrackSetup *track = setup->getTrack(trackid); + if (!track) + return; + + if (count < 0) + { + _af_error(AF_BAD_FRAMECNT, "invalid frame count %jd", + static_cast<intmax_t>(count)); + return; + } + + track->frameCount = count; + track->frameCountSet = true; +} + +#define alloccopy(type, n, var, copyfrom) \ +{ \ + if (n == 0) \ + var = NULL; \ + else \ + { \ + if ((var = (type *) _af_calloc(n, sizeof (type))) == NULL) \ + goto fail; \ + memcpy((var), (copyfrom), (n) * sizeof (type)); \ + } \ +} + +AFfilesetup _af_filesetup_copy (const _AFfilesetup *setup, + const _AFfilesetup *defaultSetup, bool copyMarks) +{ + AFfilesetup newsetup; + int instrumentCount, miscellaneousCount, trackCount; + + newsetup = (_AFfilesetup *) _af_malloc(sizeof (_AFfilesetup)); + if (newsetup == AF_NULL_FILESETUP) + return AF_NULL_FILESETUP; + + *newsetup = *defaultSetup; + + newsetup->tracks = NULL; + newsetup->instruments = NULL; + newsetup->miscellaneous = NULL; + + /* Copy tracks. */ + trackCount = setup->trackSet ? setup->trackCount : + newsetup->trackSet ? newsetup->trackCount : 0; + alloccopy(TrackSetup, trackCount, newsetup->tracks, setup->tracks); + newsetup->trackCount = trackCount; + + /* Copy instruments. */ + instrumentCount = setup->instrumentSet ? setup->instrumentCount : + newsetup->instrumentSet ? newsetup->instrumentCount : 0; + alloccopy(InstrumentSetup, instrumentCount, newsetup->instruments, setup->instruments); + newsetup->instrumentCount = instrumentCount; + + /* Copy miscellaneous information. */ + miscellaneousCount = setup->miscellaneousSet ? setup->miscellaneousCount : + newsetup->miscellaneousSet ? newsetup->miscellaneousCount : 0; + alloccopy(MiscellaneousSetup, miscellaneousCount, newsetup->miscellaneous, setup->miscellaneous); + newsetup->miscellaneousCount = miscellaneousCount; + + for (int i=0; i<setup->trackCount; i++) + { + TrackSetup *track = &newsetup->tracks[i]; + + /* XXXmpruett set compression information */ + + if (!setup->tracks[i].markersSet && !copyMarks) + { + track->markers = NULL; + track->markerCount = 0; + continue; + } + + alloccopy(MarkerSetup, setup->tracks[i].markerCount, + track->markers, setup->tracks[i].markers); + track->markerCount = setup->tracks[i].markerCount; + + for (int j=0; j<setup->tracks[i].markerCount; j++) + { + track->markers[j].name = + _af_strdup(setup->tracks[i].markers[j].name); + if (track->markers[j].name == NULL) + goto fail; + + track->markers[j].comment = + _af_strdup(setup->tracks[i].markers[j].comment); + if (track->markers[j].comment == NULL) + goto fail; + } + } + + for (int i=0; i<newsetup->instrumentCount; i++) + { + InstrumentSetup *instrument = &newsetup->instruments[i]; + alloccopy(LoopSetup, setup->instruments[i].loopCount, + instrument->loops, setup->instruments[i].loops); + } + + return newsetup; + + fail: + if (newsetup->miscellaneous) + free(newsetup->miscellaneous); + if (newsetup->instruments) + free(newsetup->instruments); + if (newsetup->tracks) + free(newsetup->tracks); + if (newsetup) + free(newsetup); + + return AF_NULL_FILESETUP; +} + +TrackSetup *_AFfilesetup::getTrack(int trackID) +{ + for (int i=0; i<trackCount; i++) + { + if (tracks[i].id == trackID) + return &tracks[i]; + } + + _af_error(AF_BAD_TRACKID, "bad track id %d", trackID); + return NULL; +} + +InstrumentSetup *_AFfilesetup::getInstrument(int instrumentID) +{ + for (int i=0; i < instrumentCount; i++) + if (instruments[i].id == instrumentID) + return &instruments[i]; + + _af_error(AF_BAD_INSTID, "invalid instrument id %d", instrumentID); + return NULL; +} + +MiscellaneousSetup *_AFfilesetup::getMiscellaneous(int miscellaneousID) +{ + for (int i=0; i<miscellaneousCount; i++) + { + if (miscellaneous[i].id == miscellaneousID) + return &miscellaneous[i]; + } + + _af_error(AF_BAD_MISCID, "bad miscellaneous id %d", miscellaneousID); + + return NULL; +} + +// file: Track.cpp +/* + Audio File Library + Copyright (C) 1998, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + track.c + + This file contains functions for dealing with tracks within an + audio file. +*/ + + +#include <assert.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> + + +void afInitTrackIDs (AFfilesetup file, const int *trackids, int trackCount) +{ + assert(file); + assert(trackids); + assert(trackCount == 1); + assert(trackids[0] == AF_DEFAULT_TRACK); +} + +int afGetTrackIDs (AFfilehandle file, int *trackids) +{ + assert(file); + + if (trackids != NULL) + trackids[0] = AF_DEFAULT_TRACK; + + return 1; +} + +Track::Track() +{ + id = AF_DEFAULT_TRACK; + + f.compressionParams = NULL; + v.compressionParams = NULL; + + channelMatrix = NULL; + + markerCount = 0; + markers = NULL; + + hasAESData = false; + memset(aesData, 0, 24); + + totalfframes = 0; + nextfframe = 0; + frames2ignore = 0; + fpos_first_frame = 0; + fpos_next_frame = 0; + fpos_after_data = 0; + totalvframes = 0; + nextvframe = 0; + data_size = 0; +} + +Track::~Track() +{ + if (f.compressionParams) + { + AUpvfree(f.compressionParams); + f.compressionParams = NULL; + } + + if (v.compressionParams) + { + AUpvfree(v.compressionParams); + v.compressionParams = NULL; + } + + free(channelMatrix); + channelMatrix = NULL; + + if (markers) + { + for (int j=0; j<markerCount; j++) + { + free(markers[j].name); + markers[j].name = NULL; + free(markers[j].comment); + markers[j].comment = NULL; + } + + free(markers); + markers = NULL; + } +} + +void Track::print() +{ + fprintf(stderr, "totalfframes %jd\n", (intmax_t) totalfframes); + fprintf(stderr, "nextfframe %jd\n", (intmax_t) nextfframe); + fprintf(stderr, "frames2ignore %jd\n", (intmax_t) frames2ignore); + fprintf(stderr, "fpos_first_frame %jd\n", (intmax_t) fpos_first_frame); + fprintf(stderr, "fpos_next_frame %jd\n", (intmax_t) fpos_next_frame); + fprintf(stderr, "fpos_after_data %jd\n", (intmax_t) fpos_after_data); + fprintf(stderr, "totalvframes %jd\n", (intmax_t) totalvframes); + fprintf(stderr, "nextvframe %jd\n", (intmax_t) nextvframe); + fprintf(stderr, "data_size %jd\n", (intmax_t) data_size); +} + +Marker *Track::getMarker(int markerID) +{ + for (int i=0; i<markerCount; i++) + if (markers[i].id == markerID) + return &markers[i]; + + _af_error(AF_BAD_MARKID, "no marker with id %d found in track %d", + markerID, id); + + return NULL; +} + +status Track::copyMarkers(TrackSetup *setup) +{ + if ((markerCount = setup->markerCount) == 0) + { + markers = NULL; + return AF_SUCCEED; + } + + markers = _af_marker_new(markerCount); + if (!markers) + return AF_FAIL; + + for (int i=0; i<markerCount; i++) + { + markers[i].id = setup->markers[i].id; + markers[i].name = _af_strdup(setup->markers[i].name); + if (!markers[i].name) + return AF_FAIL; + + markers[i].comment = _af_strdup(setup->markers[i].comment); + if (!markers[i].comment) + return AF_FAIL; + markers[i].position = 0; + } + + return AF_SUCCEED; +} + +void Track::computeTotalFileFrames() +{ + if (f.bytesPerPacket && f.framesPerPacket) + totalfframes = (data_size / f.bytesPerPacket) * f.framesPerPacket; +} + +// file: UUID.cpp +/* + Copyright (C) 2011, Michael Pruett. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include <stdio.h> +#include <string.h> + +bool UUID::operator==(const UUID &u) const +{ + return !memcmp(data, u.data, 16); +} + +bool UUID::operator!=(const UUID &u) const +{ + return memcmp(data, u.data, 16) != 0; +} + +std::string UUID::name() const +{ + char s[37]; + uint32_t u1 = + (data[0] << 24) | + (data[1] << 16) | + (data[2] << 8) | + data[3]; + uint16_t u2 = + (data[4] << 8) | + data[5]; + uint16_t u3 = + (data[6] << 8) | + data[7]; + uint16_t u4 = + (data[8] << 8) | + data[9]; + snprintf(s, 37, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + u1, u2, u3, u4, + data[10], data[11], data[12], data[13], data[14], data[15]); + return std::string(s); +} + +// file: WAVE.cpp +/* + Audio File Library + Copyright (C) 1998-2000, 2003-2004, 2010-2013, Michael Pruett <michael@68k.org> + Copyright (C) 2000-2002, Silicon Graphics, Inc. + Copyright (C) 2002-2003, Davy Durham + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + WAVE.cpp + + This file contains code for reading and writing RIFF WAVE format + sound files. +*/ + + +#include <assert.h> +#include <math.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + + +/* These constants are from RFC 2361. */ +enum +{ + WAVE_FORMAT_UNKNOWN = 0x0000, /* Microsoft Unknown Wave Format */ + WAVE_FORMAT_PCM = 0x0001, /* Microsoft PCM Format */ + WAVE_FORMAT_ADPCM = 0x0002, /* Microsoft ADPCM Format */ + WAVE_FORMAT_IEEE_FLOAT = 0x0003, /* IEEE Float */ + WAVE_FORMAT_VSELP = 0x0004, /* Compaq Computer's VSELP */ + WAVE_FORMAT_IBM_CVSD = 0x0005, /* IBM CVSD */ + WAVE_FORMAT_ALAW = 0x0006, /* Microsoft ALAW */ + WAVE_FORMAT_MULAW = 0x0007, /* Microsoft MULAW */ + WAVE_FORMAT_OKI_ADPCM = 0x0010, /* OKI ADPCM */ + WAVE_FORMAT_DVI_ADPCM = 0x0011, /* Intel's DVI ADPCM */ + WAVE_FORMAT_MEDIASPACE_ADPCM = 0x0012, /* Videologic's MediaSpace ADPCM */ + WAVE_FORMAT_SIERRA_ADPCM = 0x0013, /* Sierra ADPCM */ + WAVE_FORMAT_G723_ADPCM = 0x0014, /* G.723 ADPCM */ + WAVE_FORMAT_DIGISTD = 0x0015, /* DSP Solutions' DIGISTD */ + WAVE_FORMAT_DIGIFIX = 0x0016, /* DSP Solutions' DIGIFIX */ + WAVE_FORMAT_DIALOGIC_OKI_ADPCM = 0x0017, /* Dialogic OKI ADPCM */ + WAVE_FORMAT_MEDIAVISION_ADPCM = 0x0018, /* MediaVision ADPCM */ + WAVE_FORMAT_CU_CODEC = 0x0019, /* HP CU */ + WAVE_FORMAT_YAMAHA_ADPCM = 0x0020, /* Yamaha ADPCM */ + WAVE_FORMAT_SONARC = 0x0021, /* Speech Compression's Sonarc */ + WAVE_FORMAT_DSP_TRUESPEECH = 0x0022, /* DSP Group's True Speech */ + WAVE_FORMAT_ECHOSC1 = 0x0023, /* Echo Speech's EchoSC1 */ + WAVE_FORMAT_AUDIOFILE_AF36 = 0x0024, /* Audiofile AF36 */ + WAVE_FORMAT_APTX = 0x0025, /* APTX */ + WAVE_FORMAT_DOLBY_AC2 = 0x0030, /* Dolby AC2 */ + WAVE_FORMAT_GSM610 = 0x0031, /* GSM610 */ + WAVE_FORMAT_MSNAUDIO = 0x0032, /* MSNAudio */ + WAVE_FORMAT_ANTEX_ADPCME = 0x0033, /* Antex ADPCME */ + + WAVE_FORMAT_MPEG = 0x0050, /* MPEG */ + WAVE_FORMAT_MPEGLAYER3 = 0x0055, /* MPEG layer 3 */ + WAVE_FORMAT_LUCENT_G723 = 0x0059, /* Lucent G.723 */ + WAVE_FORMAT_G726_ADPCM = 0x0064, /* G.726 ADPCM */ + WAVE_FORMAT_G722_ADPCM = 0x0065, /* G.722 ADPCM */ + + IBM_FORMAT_MULAW = 0x0101, + IBM_FORMAT_ALAW = 0x0102, + IBM_FORMAT_ADPCM = 0x0103, + + WAVE_FORMAT_CREATIVE_ADPCM = 0x0200, + + WAVE_FORMAT_EXTENSIBLE = 0xfffe +}; + +const int _af_wave_compression_types[_AF_WAVE_NUM_COMPTYPES] = +{ + AF_COMPRESSION_G711_ULAW, + AF_COMPRESSION_G711_ALAW, + AF_COMPRESSION_IMA, + AF_COMPRESSION_MS_ADPCM +}; + +const InstParamInfo _af_wave_inst_params[_AF_WAVE_NUM_INSTPARAMS] = +{ + { AF_INST_MIDI_BASENOTE, AU_PVTYPE_LONG, "MIDI base note", {60} }, + { AF_INST_NUMCENTS_DETUNE, AU_PVTYPE_LONG, "Detune in cents", {0} }, + { AF_INST_MIDI_LOVELOCITY, AU_PVTYPE_LONG, "Low velocity", {1} }, + { AF_INST_MIDI_HIVELOCITY, AU_PVTYPE_LONG, "High velocity", {127} }, + { AF_INST_MIDI_LONOTE, AU_PVTYPE_LONG, "Low note", {0} }, + { AF_INST_MIDI_HINOTE, AU_PVTYPE_LONG, "High note", {127} }, + { AF_INST_NUMDBS_GAIN, AU_PVTYPE_LONG, "Gain in dB", {0} } +}; + +static const _AFfilesetup waveDefaultFileSetup = +{ + _AF_VALID_FILESETUP, /* valid */ + AF_FILE_WAVE, /* fileFormat */ + true, /* trackSet */ + true, /* instrumentSet */ + true, /* miscellaneousSet */ + 1, /* trackCount */ + NULL, /* tracks */ + 0, /* instrumentCount */ + NULL, /* instruments */ + 0, /* miscellaneousCount */ + NULL /* miscellaneous */ +}; + +static const UUID _af_wave_guid_pcm = +{{ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 +}}; +static const UUID _af_wave_guid_ieee_float = +{{ + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 +}}; +static const UUID _af_wave_guid_ulaw = +{{ + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 +}}; +static const UUID _af_wave_guid_alaw = +{{ + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 +}}; + +WAVEFile::WAVEFile() +{ + setFormatByteOrder(AF_BYTEORDER_LITTLEENDIAN); + + m_factOffset = 0; + m_miscellaneousOffset = 0; + m_markOffset = 0; + m_dataSizeOffset = 0; + + m_msadpcmNumCoefficients = 0; +} + +status WAVEFile::parseFrameCount(const Tag &id, uint32_t size) +{ + Track *track = getTrack(); + + uint32_t totalFrames; + readU32(&totalFrames); + + track->totalfframes = totalFrames; + + return AF_SUCCEED; +} + +status WAVEFile::parseFormat(const Tag &id, uint32_t size) +{ + Track *track = getTrack(); + + uint16_t formatTag; + readU16(&formatTag); + uint16_t channelCount; + readU16(&channelCount); + uint32_t sampleRate; + readU32(&sampleRate); + uint32_t averageBytesPerSecond; + readU32(&averageBytesPerSecond); + uint16_t blockAlign; + readU16(&blockAlign); + + if (!channelCount) + { + _af_error(AF_BAD_CHANNELS, "invalid file with 0 channels"); + return AF_FAIL; + } + + track->f.channelCount = channelCount; + track->f.sampleRate = sampleRate; + track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN; + + /* Default to uncompressed audio data. */ + track->f.compressionType = AF_COMPRESSION_NONE; + track->f.framesPerPacket = 1; + + switch (formatTag) + { + case WAVE_FORMAT_PCM: + { + uint16_t bitsPerSample; + readU16(&bitsPerSample); + + track->f.sampleWidth = bitsPerSample; + + if (bitsPerSample == 0 || bitsPerSample > 32) + { + _af_error(AF_BAD_WIDTH, + "bad sample width of %d bits", + bitsPerSample); + return AF_FAIL; + } + + if (bitsPerSample <= 8) + track->f.sampleFormat = AF_SAMPFMT_UNSIGNED; + else + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + } + break; + + case WAVE_FORMAT_MULAW: + case IBM_FORMAT_MULAW: + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.byteOrder = _AF_BYTEORDER_NATIVE; + track->f.compressionType = AF_COMPRESSION_G711_ULAW; + track->f.bytesPerPacket = track->f.channelCount; + break; + + case WAVE_FORMAT_ALAW: + case IBM_FORMAT_ALAW: + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.byteOrder = _AF_BYTEORDER_NATIVE; + track->f.compressionType = AF_COMPRESSION_G711_ALAW; + track->f.bytesPerPacket = track->f.channelCount; + break; + + case WAVE_FORMAT_IEEE_FLOAT: + { + uint16_t bitsPerSample; + readU16(&bitsPerSample); + + if (bitsPerSample == 64) + { + track->f.sampleWidth = 64; + track->f.sampleFormat = AF_SAMPFMT_DOUBLE; + } + else + { + track->f.sampleWidth = 32; + track->f.sampleFormat = AF_SAMPFMT_FLOAT; + } + } + break; + + case WAVE_FORMAT_ADPCM: + { + uint16_t bitsPerSample, extraByteCount, + samplesPerBlock, numCoefficients; + + if (track->f.channelCount != 1 && + track->f.channelCount != 2) + { + _af_error(AF_BAD_CHANNELS, + "WAVE file with MS ADPCM compression " + "must have 1 or 2 channels"); + } + + readU16(&bitsPerSample); + readU16(&extraByteCount); + readU16(&samplesPerBlock); + readU16(&numCoefficients); + + /* numCoefficients should be at least 7. */ + assert(numCoefficients >= 7 && numCoefficients <= 255); + + m_msadpcmNumCoefficients = numCoefficients; + + for (int i=0; i<m_msadpcmNumCoefficients; i++) + { + readS16(&m_msadpcmCoefficients[i][0]); + readS16(&m_msadpcmCoefficients[i][1]); + } + + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.compressionType = AF_COMPRESSION_MS_ADPCM; + track->f.byteOrder = _AF_BYTEORDER_NATIVE; + + track->f.framesPerPacket = samplesPerBlock; + track->f.bytesPerPacket = blockAlign; + + // Create the parameter list. + AUpvlist pv = AUpvnew(2); + AUpvsetparam(pv, 0, _AF_MS_ADPCM_NUM_COEFFICIENTS); + AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG); + long l = m_msadpcmNumCoefficients; + AUpvsetval(pv, 0, &l); + + AUpvsetparam(pv, 1, _AF_MS_ADPCM_COEFFICIENTS); + AUpvsetvaltype(pv, 1, AU_PVTYPE_PTR); + void *v = m_msadpcmCoefficients; + AUpvsetval(pv, 1, &v); + + track->f.compressionParams = pv; + } + break; + + case WAVE_FORMAT_DVI_ADPCM: + { + uint16_t bitsPerSample, extraByteCount, samplesPerBlock; + + readU16(&bitsPerSample); + readU16(&extraByteCount); + readU16(&samplesPerBlock); + + if (bitsPerSample != 4) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, + "IMA ADPCM compression supports only 4 bits per sample"); + } + + int bytesPerBlock = (samplesPerBlock + 14) / 8 * 4 * channelCount; + if (bytesPerBlock > blockAlign || (samplesPerBlock % 8) != 1) + { + _af_error(AF_BAD_CODEC_CONFIG, + "Invalid samples per block for IMA ADPCM compression"); + } + + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.compressionType = AF_COMPRESSION_IMA; + track->f.byteOrder = _AF_BYTEORDER_NATIVE; + + initIMACompressionParams(); + + track->f.framesPerPacket = samplesPerBlock; + track->f.bytesPerPacket = blockAlign; + } + break; + + case WAVE_FORMAT_EXTENSIBLE: + { + uint16_t bitsPerSample; + readU16(&bitsPerSample); + uint16_t extraByteCount; + readU16(&extraByteCount); + uint16_t reserved; + readU16(&reserved); + uint32_t channelMask; + readU32(&channelMask); + UUID subformat; + readUUID(&subformat); + if (subformat == _af_wave_guid_pcm) + { + track->f.sampleWidth = bitsPerSample; + + if (bitsPerSample == 0 || bitsPerSample > 32) + { + _af_error(AF_BAD_WIDTH, + "bad sample width of %d bits", + bitsPerSample); + return AF_FAIL; + } + + // Use valid bits per sample if bytes per sample is the same. + if (reserved <= bitsPerSample && + (reserved + 7) / 8 == (bitsPerSample + 7) / 8) + track->f.sampleWidth = reserved; + + if (bitsPerSample <= 8) + track->f.sampleFormat = AF_SAMPFMT_UNSIGNED; + else + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + } + else if (subformat == _af_wave_guid_ieee_float) + { + if (bitsPerSample == 64) + { + track->f.sampleWidth = 64; + track->f.sampleFormat = AF_SAMPFMT_DOUBLE; + } + else + { + track->f.sampleWidth = 32; + track->f.sampleFormat = AF_SAMPFMT_FLOAT; + } + } + else if (subformat == _af_wave_guid_alaw || + subformat == _af_wave_guid_ulaw) + { + track->f.compressionType = subformat == _af_wave_guid_alaw ? + AF_COMPRESSION_G711_ALAW : AF_COMPRESSION_G711_ULAW; + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.byteOrder = _AF_BYTEORDER_NATIVE; + track->f.bytesPerPacket = channelCount; + } + else + { + _af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE extensible data format %s is not currently supported", subformat.name().c_str()); + return AF_FAIL; + } + } + break; + + case WAVE_FORMAT_YAMAHA_ADPCM: + case WAVE_FORMAT_OKI_ADPCM: + case WAVE_FORMAT_CREATIVE_ADPCM: + case IBM_FORMAT_ADPCM: + _af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE ADPCM data format 0x%x is not currently supported", formatTag); + return AF_FAIL; + break; + + case WAVE_FORMAT_MPEG: + _af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE MPEG data format is not supported"); + return AF_FAIL; + break; + + case WAVE_FORMAT_MPEGLAYER3: + _af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE MPEG layer 3 data format is not supported"); + return AF_FAIL; + break; + + default: + _af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE file data format 0x%x not currently supported != 0xfffe ? %d, != EXTENSIBLE? %d", formatTag, formatTag != 0xfffe, formatTag != WAVE_FORMAT_EXTENSIBLE); + return AF_FAIL; + break; + } + + if (track->f.isUncompressed()) + track->f.computeBytesPerPacketPCM(); + + _af_set_sample_format(&track->f, track->f.sampleFormat, track->f.sampleWidth); + + return AF_SUCCEED; +} + +status WAVEFile::parseData(const Tag &id, uint32_t size) +{ + Track *track = getTrack(); + + track->fpos_first_frame = m_fh->tell(); + track->data_size = size; + + return AF_SUCCEED; +} + +status WAVEFile::parsePlayList(const Tag &id, uint32_t size) +{ + uint32_t segmentCount; + readU32(&segmentCount); + + if (segmentCount == 0) + { + m_instrumentCount = 0; + m_instruments = NULL; + return AF_SUCCEED; + } + + for (unsigned segment=0; segment<segmentCount; segment++) + { + uint32_t startMarkID, loopLength, loopCount; + + readU32(&startMarkID); + readU32(&loopLength); + readU32(&loopCount); + } + + return AF_SUCCEED; +} + +status WAVEFile::parseCues(const Tag &id, uint32_t size) +{ + Track *track = getTrack(); + + uint32_t markerCount; + readU32(&markerCount); + track->markerCount = markerCount; + + if (markerCount == 0) + { + track->markers = NULL; + return AF_SUCCEED; + } + + if ((track->markers = _af_marker_new(markerCount)) == NULL) + return AF_FAIL; + + for (unsigned i=0; i<markerCount; i++) + { + uint32_t id, position, chunkid; + uint32_t chunkByteOffset, blockByteOffset; + uint32_t sampleFrameOffset; + Marker *marker = &track->markers[i]; + + readU32(&id); + readU32(&position); + readU32(&chunkid); + readU32(&chunkByteOffset); + readU32(&blockByteOffset); + + /* + sampleFrameOffset represents the position of + the mark in units of frames. + */ + readU32(&sampleFrameOffset); + + marker->id = id; + marker->position = sampleFrameOffset; + marker->name = _af_strdup(""); + marker->comment = _af_strdup(""); + } + + return AF_SUCCEED; +} + +/* Parse an adtl sub-chunk within a LIST chunk. */ +status WAVEFile::parseADTLSubChunk(const Tag &id, uint32_t size) +{ + Track *track = getTrack(); + + AFfileoffset endPos = m_fh->tell() + size; + + while (m_fh->tell() < endPos) + { + Tag chunkID; + uint32_t chunkSize; + + readTag(&chunkID); + readU32(&chunkSize); + + if (chunkID == "labl" || chunkID == "note") + { + uint32_t id; + long length=chunkSize-4; + char *p = (char *) _af_malloc(length); + + readU32(&id); + m_fh->read(p, length); + + Marker *marker = track->getMarker(id); + + if (marker) + { + if (chunkID == "labl") + { + free(marker->name); + marker->name = p; + } + else if (chunkID == "note") + { + free(marker->comment); + marker->comment = p; + } + else + free(p); + } + else + free(p); + + /* + If chunkSize is odd, skip an extra byte + at the end of the chunk. + */ + if ((chunkSize % 2) != 0) + m_fh->seek(1, File::SeekFromCurrent); + } + else + { + /* If chunkSize is odd, skip an extra byte. */ + m_fh->seek(chunkSize + (chunkSize % 2), File::SeekFromCurrent); + } + } + return AF_SUCCEED; +} + +/* Parse an INFO sub-chunk within a LIST chunk. */ +status WAVEFile::parseINFOSubChunk(const Tag &id, uint32_t size) +{ + AFfileoffset endPos = m_fh->tell() + size; + + while (m_fh->tell() < endPos) + { + int misctype = AF_MISC_UNRECOGNIZED; + Tag miscid; + uint32_t miscsize; + + readTag(&miscid); + readU32(&miscsize); + + if (miscid == "IART") + misctype = AF_MISC_AUTH; + else if (miscid == "INAM") + misctype = AF_MISC_NAME; + else if (miscid == "ICOP") + misctype = AF_MISC_COPY; + else if (miscid == "ICMT") + misctype = AF_MISC_ICMT; + else if (miscid == "ICRD") + misctype = AF_MISC_ICRD; + else if (miscid == "ISFT") + misctype = AF_MISC_ISFT; + + if (misctype != AF_MISC_UNRECOGNIZED) + { + char *string = (char *) _af_malloc(miscsize); + + m_fh->read(string, miscsize); + + m_miscellaneousCount++; + m_miscellaneous = (Miscellaneous *) _af_realloc(m_miscellaneous, sizeof (Miscellaneous) * m_miscellaneousCount); + + m_miscellaneous[m_miscellaneousCount-1].id = m_miscellaneousCount; + m_miscellaneous[m_miscellaneousCount-1].type = misctype; + m_miscellaneous[m_miscellaneousCount-1].size = miscsize; + m_miscellaneous[m_miscellaneousCount-1].position = 0; + m_miscellaneous[m_miscellaneousCount-1].buffer = string; + } + else + { + m_fh->seek(miscsize, File::SeekFromCurrent); + } + + /* Make the current position an even number of bytes. */ + if (miscsize % 2 != 0) + m_fh->seek(1, File::SeekFromCurrent); + } + return AF_SUCCEED; +} + +status WAVEFile::parseList(const Tag &id, uint32_t size) +{ + Tag typeID; + readTag(&typeID); + size-=4; + + if (typeID == "adtl") + { + /* Handle adtl sub-chunks. */ + return parseADTLSubChunk(typeID, size); + } + else if (typeID == "INFO") + { + /* Handle INFO sub-chunks. */ + return parseINFOSubChunk(typeID, size); + } + else + { + /* Skip unhandled sub-chunks. */ + m_fh->seek(size, File::SeekFromCurrent); + return AF_SUCCEED; + } + return AF_SUCCEED; +} + +status WAVEFile::parseInstrument(const Tag &id, uint32_t size) +{ + uint8_t baseNote; + int8_t detune, gain; + uint8_t lowNote, highNote, lowVelocity, highVelocity; + uint8_t padByte; + + readU8(&baseNote); + readS8(&detune); + readS8(&gain); + readU8(&lowNote); + readU8(&highNote); + readU8(&lowVelocity); + readU8(&highVelocity); + readU8(&padByte); + + return AF_SUCCEED; +} + +bool WAVEFile::recognize(File *fh) +{ + uint8_t buffer[8]; + + fh->seek(0, File::SeekFromBeginning); + + if (fh->read(buffer, 8) != 8 || memcmp(buffer, "RIFF", 4) != 0) + return false; + if (fh->read(buffer, 4) != 4 || memcmp(buffer, "WAVE", 4) != 0) + return false; + + return true; +} + +status WAVEFile::readInit(AFfilesetup setup) +{ + Tag type, formtype; + uint32_t size; + uint32_t index = 0; + + bool hasFormat = false; + bool hasData = false; + bool hasFrameCount = false; + + Track *track = allocateTrack(); + + m_fh->seek(0, File::SeekFromBeginning); + + readTag(&type); + readU32(&size); + readTag(&formtype); + + assert(type == "RIFF"); + assert(formtype == "WAVE"); + + /* Include the offset of the form type. */ + index += 4; + + while (index < size) + { + Tag chunkid; + uint32_t chunksize = 0; + status result; + + readTag(&chunkid); + readU32(&chunksize); + + if (chunkid == "fmt ") + { + result = parseFormat(chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + + hasFormat = true; + } + else if (chunkid == "data") + { + /* The format chunk must precede the data chunk. */ + if (!hasFormat) + { + _af_error(AF_BAD_HEADER, "missing format chunk in WAVE file"); + return AF_FAIL; + } + + result = parseData(chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + + hasData = true; + } + else if (chunkid == "inst") + { + result = parseInstrument(chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + } + else if (chunkid == "fact") + { + hasFrameCount = true; + result = parseFrameCount(chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + } + else if (chunkid == "cue ") + { + result = parseCues(chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + } + else if (chunkid == "LIST" || chunkid == "list") + { + result = parseList(chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + } + else if (chunkid == "INST") + { + result = parseInstrument(chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + } + else if (chunkid == "plst") + { + result = parsePlayList(chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + } + + index += chunksize + 8; + + /* All chunks must be aligned on an even number of bytes */ + if ((index % 2) != 0) + index++; + + m_fh->seek(index + 8, File::SeekFromBeginning); + } + + /* The format chunk and the data chunk are required. */ + if (!hasFormat || !hasData) + { + return AF_FAIL; + } + + /* + At this point we know that the file has a format chunk and a + data chunk, so we can assume that track->f and track->data_size + have been initialized. + */ + if (!hasFrameCount) + { + if (track->f.bytesPerPacket && track->f.framesPerPacket) + { + track->computeTotalFileFrames(); + } + else + { + _af_error(AF_BAD_HEADER, "Frame count required but not found"); + return AF_FAIL; + } + } + + return AF_SUCCEED; +} + +AFfilesetup WAVEFile::completeSetup(AFfilesetup setup) +{ + if (setup->trackSet && setup->trackCount != 1) + { + _af_error(AF_BAD_NUMTRACKS, "WAVE file must have 1 track"); + return AF_NULL_FILESETUP; + } + + TrackSetup *track = setup->getTrack(); + if (!track) + return AF_NULL_FILESETUP; + + if (track->f.isCompressed()) + { + if (!track->sampleFormatSet) + _af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, 16); + else + _af_set_sample_format(&track->f, track->f.sampleFormat, track->f.sampleWidth); + } + else if (track->sampleFormatSet) + { + switch (track->f.sampleFormat) + { + case AF_SAMPFMT_FLOAT: + if (track->sampleWidthSet && + track->f.sampleWidth != 32) + { + _af_error(AF_BAD_WIDTH, + "Warning: invalid sample width for floating-point WAVE file: %d (must be 32 bits)\n", + track->f.sampleWidth); + _af_set_sample_format(&track->f, AF_SAMPFMT_FLOAT, 32); + } + break; + + case AF_SAMPFMT_DOUBLE: + if (track->sampleWidthSet && + track->f.sampleWidth != 64) + { + _af_error(AF_BAD_WIDTH, + "Warning: invalid sample width for double-precision floating-point WAVE file: %d (must be 64 bits)\n", + track->f.sampleWidth); + _af_set_sample_format(&track->f, AF_SAMPFMT_DOUBLE, 64); + } + break; + + case AF_SAMPFMT_UNSIGNED: + if (track->sampleWidthSet) + { + if (track->f.sampleWidth < 1 || track->f.sampleWidth > 32) + { + _af_error(AF_BAD_WIDTH, "invalid sample width for WAVE file: %d (must be 1-32 bits)\n", track->f.sampleWidth); + return AF_NULL_FILESETUP; + } + if (track->f.sampleWidth > 8) + { + _af_error(AF_BAD_SAMPFMT, "WAVE integer data of more than 8 bits must be two's complement signed"); + _af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, track->f.sampleWidth); + } + } + else + /* + If the sample width is not set but the user requests + unsigned data, set the width to 8 bits. + */ + _af_set_sample_format(&track->f, track->f.sampleFormat, 8); + break; + + case AF_SAMPFMT_TWOSCOMP: + if (track->sampleWidthSet) + { + if (track->f.sampleWidth < 1 || track->f.sampleWidth > 32) + { + _af_error(AF_BAD_WIDTH, "invalid sample width %d for WAVE file (must be 1-32)", track->f.sampleWidth); + return AF_NULL_FILESETUP; + } + else if (track->f.sampleWidth <= 8) + { + _af_error(AF_BAD_SAMPFMT, "Warning: WAVE format integer data of 1-8 bits must be unsigned; setting sample format to unsigned"); + _af_set_sample_format(&track->f, AF_SAMPFMT_UNSIGNED, track->f.sampleWidth); + } + } + else + /* + If no sample width was specified, we default to 16 bits + for signed integer data. + */ + _af_set_sample_format(&track->f, track->f.sampleFormat, 16); + break; + } + } + /* + Otherwise set the sample format depending on the sample + width or set completely to default. + */ + else + { + if (!track->sampleWidthSet) + { + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + } + else + { + if (track->f.sampleWidth < 1 || track->f.sampleWidth > 32) + { + _af_error(AF_BAD_WIDTH, "invalid sample width %d for WAVE file (must be 1-32)", track->f.sampleWidth); + return AF_NULL_FILESETUP; + } + else if (track->f.sampleWidth > 8) + /* Here track->f.sampleWidth is in {1..32}. */ + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + else + /* Here track->f.sampleWidth is in {1..8}. */ + track->f.sampleFormat = AF_SAMPFMT_UNSIGNED; + } + } + + if (track->f.compressionType != AF_COMPRESSION_NONE && + track->f.compressionType != AF_COMPRESSION_G711_ULAW && + track->f.compressionType != AF_COMPRESSION_G711_ALAW && + track->f.compressionType != AF_COMPRESSION_IMA && + track->f.compressionType != AF_COMPRESSION_MS_ADPCM) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, "compression format not supported in WAVE format"); + return AF_NULL_FILESETUP; + } + + if (track->f.isUncompressed() && + track->byteOrderSet && + track->f.byteOrder != AF_BYTEORDER_LITTLEENDIAN && + track->f.isByteOrderSignificant()) + { + _af_error(AF_BAD_BYTEORDER, "WAVE format only supports little-endian data"); + return AF_NULL_FILESETUP; + } + + if (track->f.isUncompressed()) + track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN; + + if (track->aesDataSet) + { + _af_error(AF_BAD_FILESETUP, "WAVE files cannot have AES data"); + return AF_NULL_FILESETUP; + } + + if (setup->instrumentSet) + { + if (setup->instrumentCount > 1) + { + _af_error(AF_BAD_NUMINSTS, "WAVE files can have 0 or 1 instrument"); + return AF_NULL_FILESETUP; + } + else if (setup->instrumentCount == 1) + { + if (setup->instruments[0].loopSet && + setup->instruments[0].loopCount > 0 && + (!track->markersSet || track->markerCount == 0)) + { + _af_error(AF_BAD_NUMMARKS, "WAVE files with loops must contain at least 1 marker"); + return AF_NULL_FILESETUP; + } + } + } + + /* Make sure the miscellaneous data is of an acceptable type. */ + if (setup->miscellaneousSet) + { + for (int i=0; i<setup->miscellaneousCount; i++) + { + switch (setup->miscellaneous[i].type) + { + case AF_MISC_COPY: + case AF_MISC_AUTH: + case AF_MISC_NAME: + case AF_MISC_ICRD: + case AF_MISC_ISFT: + case AF_MISC_ICMT: + break; + default: + _af_error(AF_BAD_MISCTYPE, "illegal miscellaneous type [%d] for WAVE file", setup->miscellaneous[i].type); + return AF_NULL_FILESETUP; + } + } + } + + /* + Allocate an AFfilesetup and make all the unset fields correct. + */ + AFfilesetup newsetup = _af_filesetup_copy(setup, &waveDefaultFileSetup, false); + + /* Make sure we do not copy loops if they are not specified in setup. */ + if (setup->instrumentSet && setup->instrumentCount > 0 && + setup->instruments[0].loopSet) + { + free(newsetup->instruments[0].loops); + newsetup->instruments[0].loopCount = 0; + } + + return newsetup; +} + +bool WAVEFile::isInstrumentParameterValid(AUpvlist list, int i) +{ + int param, type; + + AUpvgetparam(list, i, &param); + AUpvgetvaltype(list, i, &type); + if (type != AU_PVTYPE_LONG) + return false; + + long lval; + AUpvgetval(list, i, &lval); + + switch (param) + { + case AF_INST_MIDI_BASENOTE: + return ((lval >= 0) && (lval <= 127)); + + case AF_INST_NUMCENTS_DETUNE: + return ((lval >= -50) && (lval <= 50)); + + case AF_INST_MIDI_LOVELOCITY: + return ((lval >= 1) && (lval <= 127)); + + case AF_INST_MIDI_HIVELOCITY: + return ((lval >= 1) && (lval <= 127)); + + case AF_INST_MIDI_LONOTE: + return ((lval >= 0) && (lval <= 127)); + + case AF_INST_MIDI_HINOTE: + return ((lval >= 0) && (lval <= 127)); + + case AF_INST_NUMDBS_GAIN: + return true; + + default: + return false; + } + + return true; +} + +status WAVEFile::writeFormat() +{ + uint16_t formatTag, channelCount; + uint32_t sampleRate, averageBytesPerSecond; + uint16_t blockAlign; + uint32_t chunkSize; + uint16_t bitsPerSample; + + Track *track = getTrack(); + + m_fh->write("fmt ", 4); + + switch (track->f.compressionType) + { + case AF_COMPRESSION_NONE: + chunkSize = 16; + if (track->f.sampleFormat == AF_SAMPFMT_FLOAT || + track->f.sampleFormat == AF_SAMPFMT_DOUBLE) + { + formatTag = WAVE_FORMAT_IEEE_FLOAT; + } + else if (track->f.sampleFormat == AF_SAMPFMT_TWOSCOMP || + track->f.sampleFormat == AF_SAMPFMT_UNSIGNED) + { + formatTag = WAVE_FORMAT_PCM; + } + else + { + _af_error(AF_BAD_COMPTYPE, "bad sample format"); + return AF_FAIL; + } + + blockAlign = _af_format_frame_size(&track->f, false); + bitsPerSample = 8 * _af_format_sample_size(&track->f, false); + break; + + /* + G.711 compression uses eight bits per sample. + */ + case AF_COMPRESSION_G711_ULAW: + chunkSize = 18; + formatTag = IBM_FORMAT_MULAW; + blockAlign = track->f.channelCount; + bitsPerSample = 8; + break; + + case AF_COMPRESSION_G711_ALAW: + chunkSize = 18; + formatTag = IBM_FORMAT_ALAW; + blockAlign = track->f.channelCount; + bitsPerSample = 8; + break; + + case AF_COMPRESSION_IMA: + chunkSize = 20; + formatTag = WAVE_FORMAT_DVI_ADPCM; + blockAlign = track->f.bytesPerPacket; + bitsPerSample = 4; + break; + + case AF_COMPRESSION_MS_ADPCM: + chunkSize = 50; + formatTag = WAVE_FORMAT_ADPCM; + blockAlign = track->f.bytesPerPacket; + bitsPerSample = 4; + break; + + default: + _af_error(AF_BAD_COMPTYPE, "bad compression type"); + return AF_FAIL; + } + + writeU32(&chunkSize); + writeU16(&formatTag); + + channelCount = track->f.channelCount; + writeU16(&channelCount); + + sampleRate = track->f.sampleRate; + writeU32(&sampleRate); + + averageBytesPerSecond = + track->f.sampleRate * _af_format_frame_size(&track->f, false); + if (track->f.compressionType == AF_COMPRESSION_IMA || + track->f.compressionType == AF_COMPRESSION_MS_ADPCM) + averageBytesPerSecond = track->f.sampleRate * track->f.bytesPerPacket / + track->f.framesPerPacket; + writeU32(&averageBytesPerSecond); + + writeU16(&blockAlign); + + writeU16(&bitsPerSample); + + if (track->f.compressionType == AF_COMPRESSION_G711_ULAW || + track->f.compressionType == AF_COMPRESSION_G711_ALAW) + { + uint16_t zero = 0; + writeU16(&zero); + } + else if (track->f.compressionType == AF_COMPRESSION_IMA) + { + uint16_t extraByteCount = 2; + writeU16(&extraByteCount); + uint16_t samplesPerBlock = track->f.framesPerPacket; + writeU16(&samplesPerBlock); + } + else if (track->f.compressionType == AF_COMPRESSION_MS_ADPCM) + { + uint16_t extraByteCount = 2 + 2 + m_msadpcmNumCoefficients * 4; + writeU16(&extraByteCount); + uint16_t samplesPerBlock = track->f.framesPerPacket; + writeU16(&samplesPerBlock); + + uint16_t numCoefficients = m_msadpcmNumCoefficients; + writeU16(&numCoefficients); + + for (int i=0; i<m_msadpcmNumCoefficients; i++) + { + writeS16(&m_msadpcmCoefficients[i][0]); + writeS16(&m_msadpcmCoefficients[i][1]); + } + } + + return AF_SUCCEED; +} + +status WAVEFile::writeFrameCount() +{ + uint32_t factSize = 4; + uint32_t totalFrameCount; + + Track *track = getTrack(); + + /* Omit the fact chunk only for uncompressed integer audio formats. */ + if (track->f.compressionType == AF_COMPRESSION_NONE && + (track->f.sampleFormat == AF_SAMPFMT_TWOSCOMP || + track->f.sampleFormat == AF_SAMPFMT_UNSIGNED)) + return AF_SUCCEED; + + /* + If the offset for the fact chunk hasn't been set yet, + set it to the file's current position. + */ + if (m_factOffset == 0) + m_factOffset = m_fh->tell(); + else + m_fh->seek(m_factOffset, File::SeekFromBeginning); + + m_fh->write("fact", 4); + writeU32(&factSize); + + totalFrameCount = track->totalfframes; + writeU32(&totalFrameCount); + + return AF_SUCCEED; +} + +status WAVEFile::writeData() +{ + Track *track = getTrack(); + + m_fh->write("data", 4); + m_dataSizeOffset = m_fh->tell(); + + uint32_t chunkSize = track->data_size; + + writeU32(&chunkSize); + track->fpos_first_frame = m_fh->tell(); + + return AF_SUCCEED; +} + +status WAVEFile::update() +{ + Track *track = getTrack(); + + if (track->fpos_first_frame != 0) + { + uint32_t dataLength, fileLength; + + // Update the frame count chunk if present. + writeFrameCount(); + + // Update the length of the data chunk. + m_fh->seek(m_dataSizeOffset, File::SeekFromBeginning); + dataLength = (uint32_t) track->data_size; + writeU32(&dataLength); + + // Update the length of the RIFF chunk. + fileLength = (uint32_t) m_fh->length(); + fileLength -= 8; + + m_fh->seek(4, File::SeekFromBeginning); + writeU32(&fileLength); + } + + /* + Write the actual data that was set after initializing + the miscellaneous IDs. The size of the data will be + unchanged. + */ + writeMiscellaneous(); + + // Write the new positions; the size of the data will be unchanged. + writeCues(); + + return AF_SUCCEED; +} + +/* Convert an Audio File Library miscellaneous type to a WAVE type. */ +static bool misc_type_to_wave (int misctype, Tag *miscid) +{ + if (misctype == AF_MISC_AUTH) + *miscid = "IART"; + else if (misctype == AF_MISC_NAME) + *miscid = "INAM"; + else if (misctype == AF_MISC_COPY) + *miscid = "ICOP"; + else if (misctype == AF_MISC_ICMT) + *miscid = "ICMT"; + else if (misctype == AF_MISC_ICRD) + *miscid = "ICRD"; + else if (misctype == AF_MISC_ISFT) + *miscid = "ISFT"; + else + return false; + + return true; +} + +status WAVEFile::writeMiscellaneous() +{ + if (m_miscellaneousCount != 0) + { + uint32_t miscellaneousBytes; + uint32_t chunkSize; + + /* Start at 12 to account for 'LIST', size, and 'INFO'. */ + miscellaneousBytes = 12; + + /* Then calculate the size of the whole INFO chunk. */ + for (int i=0; i<m_miscellaneousCount; i++) + { + Tag miscid; + + // Skip miscellaneous data of an unsupported type. + if (!misc_type_to_wave(m_miscellaneous[i].type, &miscid)) + continue; + + // Account for miscellaneous type and size. + miscellaneousBytes += 8; + miscellaneousBytes += m_miscellaneous[i].size; + + // Add a pad byte if necessary. + if (m_miscellaneous[i].size % 2 != 0) + miscellaneousBytes++; + + assert(miscellaneousBytes % 2 == 0); + } + + if (m_miscellaneousOffset == 0) + m_miscellaneousOffset = m_fh->tell(); + else + m_fh->seek(m_miscellaneousOffset, File::SeekFromBeginning); + + /* + Write the data. On the first call to this + function (from _af_wave_write_init), the + data won't be available, fh->seek is used to + reserve space until the data has been provided. + On subseuent calls to this function (from + _af_wave_update), the data will really be written. + */ + + /* Write 'LIST'. */ + m_fh->write("LIST", 4); + + /* Write the size of the following chunk. */ + chunkSize = miscellaneousBytes-8; + writeU32(&chunkSize); + + /* Write 'INFO'. */ + m_fh->write("INFO", 4); + + /* Write each miscellaneous chunk. */ + for (int i=0; i<m_miscellaneousCount; i++) + { + uint32_t miscsize = m_miscellaneous[i].size; + Tag miscid; + + // Skip miscellaneous data of an unsupported type. + if (!misc_type_to_wave(m_miscellaneous[i].type, &miscid)) + continue; + + writeTag(&miscid); + writeU32(&miscsize); + if (m_miscellaneous[i].buffer != NULL) + { + uint8_t zero = 0; + + m_fh->write(m_miscellaneous[i].buffer, m_miscellaneous[i].size); + + // Pad if necessary. + if ((m_miscellaneous[i].size%2) != 0) + writeU8(&zero); + } + else + { + int size; + size = m_miscellaneous[i].size; + + // Pad if necessary. + if ((size % 2) != 0) + size++; + m_fh->seek(size, File::SeekFromCurrent); + } + } + } + + return AF_SUCCEED; +} + +status WAVEFile::writeCues() +{ + Track *track = getTrack(); + + if (!track->markerCount) + return AF_SUCCEED; + + if (m_markOffset == 0) + m_markOffset = m_fh->tell(); + else + m_fh->seek(m_markOffset, File::SeekFromBeginning); + + Tag cue("cue "); + writeTag(&cue); + + /* + The cue chunk consists of 4 bytes for the number of cue points + followed by 24 bytes for each cue point record. + */ + uint32_t cueChunkSize = 4 + track->markerCount * 24; + writeU32(&cueChunkSize); + uint32_t numCues = track->markerCount; + writeU32(&numCues); + + // Write each marker to the file. + for (int i=0; i<track->markerCount; i++) + { + uint32_t identifier = track->markers[i].id; + writeU32(&identifier); + + uint32_t position = i; + writeU32(&position); + + Tag data("data"); + writeTag(&data); + + /* + For an uncompressed WAVE file which contains only one data chunk, + chunkStart and blockStart are zero. + */ + uint32_t chunkStart = 0; + writeU32(&chunkStart); + + uint32_t blockStart = 0; + writeU32(&blockStart); + + AFframecount markPosition = track->markers[i].position; + uint32_t sampleOffset = markPosition; + writeU32(&sampleOffset); + } + + // Now write the cue names and comments within a master list chunk. + uint32_t listChunkSize = 4; + for (int i=0; i<track->markerCount; i++) + { + const char *name = track->markers[i].name; + const char *comment = track->markers[i].comment; + + /* + Each 'labl' or 'note' chunk consists of 4 bytes for the chunk ID, + 4 bytes for the chunk data size, 4 bytes for the cue point ID, + and then the length of the label as a null-terminated string. + + In all, this is 12 bytes plus the length of the string, its null + termination byte, and a trailing pad byte if the length of the + chunk is otherwise odd. + */ + listChunkSize += 12 + zStringLength(name); + listChunkSize += 12 + zStringLength(comment); + } + + Tag list("LIST"); + writeTag(&list); + writeU32(&listChunkSize); + Tag adtl("adtl"); + writeTag(&adtl); + + for (int i=0; i<track->markerCount; i++) + { + uint32_t cuePointID = track->markers[i].id; + + const char *name = track->markers[i].name; + uint32_t labelSize = 4 + zStringLength(name); + Tag lablTag("labl"); + writeTag(&lablTag); + writeU32(&labelSize); + writeU32(&cuePointID); + writeZString(name); + + const char *comment = track->markers[i].comment; + uint32_t noteSize = 4 + zStringLength(comment); + Tag noteTag("note"); + writeTag(&noteTag); + writeU32(&noteSize); + writeU32(&cuePointID); + writeZString(comment); + } + + return AF_SUCCEED; +} + +bool WAVEFile::writeZString(const char *s) +{ + ssize_t lengthPlusNull = strlen(s) + 1; + if (m_fh->write(s, lengthPlusNull) != lengthPlusNull) + return false; + if (lengthPlusNull & 1) + { + uint8_t zero = 0; + if (!writeU8(&zero)) + return false; + } + return true; +} + +size_t WAVEFile::zStringLength(const char *s) +{ + size_t lengthPlusNull = strlen(s) + 1; + return lengthPlusNull + (lengthPlusNull & 1); +} + +status WAVEFile::writeInit(AFfilesetup setup) +{ + if (initFromSetup(setup) == AF_FAIL) + return AF_FAIL; + + initCompressionParams(); + + uint32_t zero = 0; + + m_fh->seek(0, File::SeekFromBeginning); + m_fh->write("RIFF", 4); + m_fh->write(&zero, 4); + m_fh->write("WAVE", 4); + + writeMiscellaneous(); + writeCues(); + writeFormat(); + writeFrameCount(); + writeData(); + + return AF_SUCCEED; +} + +bool WAVEFile::readUUID(UUID *u) +{ + return m_fh->read(u->data, 16) == 16; +} + +bool WAVEFile::writeUUID(const UUID *u) +{ + return m_fh->write(u->data, 16) == 16; +} + +void WAVEFile::initCompressionParams() +{ + Track *track = getTrack(); + if (track->f.compressionType == AF_COMPRESSION_IMA) + initIMACompressionParams(); + else if (track->f.compressionType == AF_COMPRESSION_MS_ADPCM) + initMSADPCMCompressionParams(); +} + +void WAVEFile::initIMACompressionParams() +{ + Track *track = getTrack(); + + track->f.framesPerPacket = 505; + track->f.bytesPerPacket = 256 * track->f.channelCount; + + AUpvlist pv = AUpvnew(1); + AUpvsetparam(pv, 0, _AF_IMA_ADPCM_TYPE); + AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG); + long l = _AF_IMA_ADPCM_TYPE_WAVE; + AUpvsetval(pv, 0, &l); + + track->f.compressionParams = pv; +} + +void WAVEFile::initMSADPCMCompressionParams() +{ + const int16_t coefficients[7][2] = + { + { 256, 0 }, + { 512, -256 }, + { 0, 0 }, + { 192, 64 }, + { 240, 0 }, + { 460, -208 }, + { 392, -232 } + }; + memcpy(m_msadpcmCoefficients, coefficients, sizeof (int16_t) * 7 * 2); + m_msadpcmNumCoefficients = 7; + + Track *track = getTrack(); + + track->f.framesPerPacket = 500; + track->f.bytesPerPacket = 256 * track->f.channelCount; + + AUpvlist pv = AUpvnew(2); + AUpvsetparam(pv, 0, _AF_MS_ADPCM_NUM_COEFFICIENTS); + AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG); + long l = m_msadpcmNumCoefficients; + AUpvsetval(pv, 0, &l); + + AUpvsetparam(pv, 1, _AF_MS_ADPCM_COEFFICIENTS); + AUpvsetvaltype(pv, 1, AU_PVTYPE_PTR); + void *v = m_msadpcmCoefficients; + AUpvsetval(pv, 1, &v); + + track->f.compressionParams = pv; +} + +// file: aes.cpp +/* + Audio File Library + Copyright (C) 1998-1999, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + aes.c + + This file contains routines for dealing with AES recording data. +*/ + + +#include <string.h> +#include <assert.h> + + +void afInitAESChannelData (AFfilesetup setup, int trackid) +{ + if (!_af_filesetup_ok(setup)) + return; + + TrackSetup *track = setup->getTrack(trackid); + if (!track) + return; + + track->aesDataSet = true; +} + +void afInitAESChannelDataTo (AFfilesetup setup, int trackid, int willBeData) +{ + if (!_af_filesetup_ok(setup)) + return; + + TrackSetup *track = setup->getTrack(trackid); + if (!track) + return; + + track->aesDataSet = willBeData; +} + +int afGetAESChannelData (AFfilehandle file, int trackid, unsigned char buf[24]) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + if (!track->hasAESData) + { + if (buf) + memset(buf, 0, 24); + return 0; + } + + if (buf) + memcpy(buf, track->aesData, 24); + + return 1; +} + +void afSetAESChannelData (AFfilehandle file, int trackid, unsigned char buf[24]) +{ + if (!_af_filehandle_ok(file)) + return; + + Track *track = file->getTrack(trackid); + if (!track) + return; + + if (!file->checkCanWrite()) + return; + + if (track->hasAESData) + { + memcpy(track->aesData, buf, 24); + } + else + { + _af_error(AF_BAD_NOAESDATA, + "unable to store AES channel status data for track %d", + trackid); + } +} + +// file: af_vfs.cpp +/* + Audio File Library + Copyright (C) 1999, Elliot Lee <sopwith@redhat.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + af_vfs.cpp + + Virtual file operations for the Audio File Library. +*/ + + + +#include <stdlib.h> + +AFvirtualfile *af_virtual_file_new() +{ + return (AFvirtualfile *) calloc(sizeof (AFvirtualfile), 1); +} + +void af_virtual_file_destroy(AFvirtualfile *vfile) +{ + vfile->destroy(vfile); + + free(vfile); +} + +// file: aupv.c +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + aupv.c + + This file contains an implementation of SGI's Audio Library parameter + value list functions. +*/ + + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + + +AUpvlist AUpvnew (int maxitems) +{ + AUpvlist aupvlist; + int i; + + if (maxitems <= 0) + return AU_NULL_PVLIST; + + aupvlist = (AUpvlist) malloc(sizeof (struct _AUpvlist)); + assert(aupvlist); + if (aupvlist == NULL) + return AU_NULL_PVLIST; + + aupvlist->items = (struct _AUpvitem *)calloc(maxitems, sizeof (struct _AUpvitem)); + + assert(aupvlist->items); + if (aupvlist->items == NULL) + { + free(aupvlist); + return AU_NULL_PVLIST; + } + + /* Initialize the items in the list. */ + for (i=0; i<maxitems; i++) + { + aupvlist->items[i].valid = _AU_VALID_PVITEM; + aupvlist->items[i].type = AU_PVTYPE_LONG; + aupvlist->items[i].parameter = 0; + memset(&aupvlist->items[i].value, 0, sizeof (aupvlist->items[i].value)); + } + + aupvlist->valid = _AU_VALID_PVLIST; + aupvlist->count = maxitems; + + return aupvlist; +} + +int AUpvgetmaxitems (AUpvlist list) +{ + assert(list); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + + return list->count; +} + +int AUpvfree (AUpvlist list) +{ + assert(list); + assert(list->items); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + + if ((list->items != _AU_NULL_PVITEM) && + (list->items[0].valid == _AU_VALID_PVITEM)) + { + free(list->items); + } + + free(list); + + return _AU_SUCCESS; +} + +int AUpvsetparam (AUpvlist list, int item, int param) +{ + assert(list); + assert(list->items); + assert(item >= 0); + assert(item < list->count); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + if ((item < 0) || (item > list->count - 1)) + return AU_BAD_PVITEM; + if (list->items[item].valid != _AU_VALID_PVITEM) + return AU_BAD_PVLIST; + + list->items[item].parameter = param; + return _AU_SUCCESS; +} + +int AUpvsetvaltype (AUpvlist list, int item, int type) +{ + assert(list); + assert(list->items); + assert(item >= 0); + assert(item < list->count); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + if ((item < 0) || (item > list->count - 1)) + return AU_BAD_PVITEM; + if (list->items[item].valid != _AU_VALID_PVITEM) + return AU_BAD_PVLIST; + + list->items[item].type = type; + return _AU_SUCCESS; +} + +int AUpvsetval (AUpvlist list, int item, void *val) +{ + assert(list); + assert(list->items); + assert(item >= 0); + assert(item < list->count); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + if ((item < 0) || (item > list->count - 1)) + return AU_BAD_PVITEM; + if (list->items[item].valid != _AU_VALID_PVITEM) + return AU_BAD_PVLIST; + + switch (list->items[item].type) + { + case AU_PVTYPE_LONG: + list->items[item].value.l = *((long *) val); + break; + case AU_PVTYPE_DOUBLE: + list->items[item].value.d = *((double *) val); + break; + case AU_PVTYPE_PTR: + list->items[item].value.v = *((void **) val); + break; + default: + assert(0); + return AU_BAD_PVLIST; + } + + return _AU_SUCCESS; +} + +int AUpvgetparam (AUpvlist list, int item, int *param) +{ + assert(list); + assert(list->items); + assert(item >= 0); + assert(item < list->count); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + if ((item < 0) || (item > list->count - 1)) + return AU_BAD_PVITEM; + if (list->items[item].valid != _AU_VALID_PVITEM) + return AU_BAD_PVLIST; + + *param = list->items[item].parameter; + return _AU_SUCCESS; +} + +int AUpvgetvaltype (AUpvlist list, int item, int *type) +{ + assert(list); + assert(list->items); + assert(item >= 0); + assert(item < list->count); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + if ((item < 0) || (item > list->count - 1)) + return AU_BAD_PVITEM; + if (list->items[item].valid != _AU_VALID_PVITEM) + return AU_BAD_PVLIST; + + *type = list->items[item].type; + return _AU_SUCCESS; +} + +int AUpvgetval (AUpvlist list, int item, void *val) +{ + assert(list); + assert(list->items); + assert(item >= 0); + assert(item < list->count); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + if ((item < 0) || (item > list->count - 1)) + return AU_BAD_PVITEM; + if (list->items[item].valid != _AU_VALID_PVITEM) + return AU_BAD_PVLIST; + + switch (list->items[item].type) + { + case AU_PVTYPE_LONG: + *((long *) val) = list->items[item].value.l; + break; + case AU_PVTYPE_DOUBLE: + *((double *) val) = list->items[item].value.d; + break; + case AU_PVTYPE_PTR: + *((void **) val) = list->items[item].value.v; + break; + } + + return _AU_SUCCESS; +} + +// file: compression.cpp +/* + Audio File Library + Copyright (C) 1999-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + compression.cpp + + This file contains routines for configuring compressed audio. +*/ + + +#include <assert.h> + + +const CompressionUnit *_af_compression_unit_from_id (int compressionid) +{ + for (int i=0; i<_AF_NUM_COMPRESSION; i++) + if (_af_compression[i].compressionID == compressionid) + return &_af_compression[i]; + + _af_error(AF_BAD_COMPTYPE, "compression type %d not available", compressionid); + return NULL; +} + +int afGetCompression (AFfilehandle file, int trackid) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + return track->f.compressionType; +} + +void afInitCompression (AFfilesetup setup, int trackid, int compression) +{ + if (!_af_filesetup_ok(setup)) + return; + + TrackSetup *track = setup->getTrack(trackid); + if (!track) + return; + + if (!_af_compression_unit_from_id(compression)) + return; + + track->compressionSet = true; + track->f.compressionType = compression; +} + +#if 0 +int afGetCompressionParams (AFfilehandle file, int trackid, + int *compression, AUpvlist pvlist, int numitems) +{ + assert(file); + assert(trackid == AF_DEFAULT_TRACK); +} + +void afInitCompressionParams (AFfilesetup setup, int trackid, + int compression, AUpvlist pvlist, int numitems) +{ + assert(setup); + assert(trackid == AF_DEFAULT_TRACK); +} +#endif + +// file: data.cpp +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + data.cpp +*/ + + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + + +int afWriteFrames (AFfilehandle file, int trackid, const void *samples, + int nvframes2write) +{ + SharedPtr<Module> firstmod; + SharedPtr<Chunk> userc; + int bytes_per_vframe; + AFframecount vframe; + + if (!_af_filehandle_ok(file)) + return -1; + + if (!file->checkCanWrite()) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + if (track->ms->isDirty() && track->ms->setup(file, track) == AF_FAIL) + return -1; + + if (!track->ms->fileModuleHandlesSeeking() && + file->m_seekok && + file->m_fh->seek(track->fpos_next_frame, File::SeekFromBeginning) != + track->fpos_next_frame) + { + _af_error(AF_BAD_LSEEK, "unable to position write pointer at next frame"); + return -1; + } + + bytes_per_vframe = _af_format_frame_size(&track->v, true); + + firstmod = track->ms->modules().front(); + userc = track->ms->chunks().front(); + + track->filemodhappy = true; + + vframe = 0; +#ifdef UNLIMITED_CHUNK_NVFRAMES + /* + OPTIMIZATION: see the comment at the very end of + arrangemodules() in modules.c for an explanation of this: + */ + if (!trk->ms->mustUseAtomicNVFrames()) + { + userc->buffer = (char *) samples; + userc->frameCount = nvframes2write; + + firstmod->runPush(); + + /* Count this chunk if there was no i/o error. */ + if (trk->filemodhappy) + vframe += userc->frameCount; + } + else +#else + /* Optimization must be off. */ + assert(track->ms->mustUseAtomicNVFrames()); +#endif + { + while (vframe < nvframes2write) + { + userc->buffer = (char *) samples + bytes_per_vframe * vframe; + if (vframe <= nvframes2write - _AF_ATOMIC_NVFRAMES) + userc->frameCount = _AF_ATOMIC_NVFRAMES; + else + userc->frameCount = nvframes2write - vframe; + + firstmod->runPush(); + + if (!track->filemodhappy) + break; + + vframe += userc->frameCount; + } + } + + track->nextvframe += vframe; + track->totalvframes += vframe; + + return vframe; +} + +int afReadFrames (AFfilehandle file, int trackid, void *samples, + int nvframeswanted) +{ + SharedPtr<Module> firstmod; + SharedPtr<Chunk> userc; + AFframecount nvframesleft, nvframes2read; + int bytes_per_vframe; + AFframecount vframe; + + if (!_af_filehandle_ok(file)) + return -1; + + if (!file->checkCanRead()) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + if (track->ms->isDirty() && track->ms->setup(file, track) == AF_FAIL) + return -1; + + if (!track->ms->fileModuleHandlesSeeking() && + file->m_seekok && + file->m_fh->seek(track->fpos_next_frame, File::SeekFromBeginning) != + track->fpos_next_frame) + { + _af_error(AF_BAD_LSEEK, "unable to position read pointer at next frame"); + return -1; + } + + if (track->totalvframes == -1) + nvframes2read = nvframeswanted; + else + { + nvframesleft = track->totalvframes - track->nextvframe; + nvframes2read = (nvframeswanted > nvframesleft) ? + nvframesleft : nvframeswanted; + } + bytes_per_vframe = _af_format_frame_size(&track->v, true); + + firstmod = track->ms->modules().back(); + userc = track->ms->chunks().back(); + + track->filemodhappy = true; + + vframe = 0; + + if (!track->ms->mustUseAtomicNVFrames()) + { + assert(track->frames2ignore == 0); + userc->buffer = samples; + userc->frameCount = nvframes2read; + + firstmod->runPull(); + if (track->filemodhappy) + vframe += userc->frameCount; + } + else + { + bool eof = false; + + if (track->frames2ignore != 0) + { + userc->frameCount = track->frames2ignore; + userc->allocate(track->frames2ignore * bytes_per_vframe); + if (!userc->buffer) + return 0; + + firstmod->runPull(); + + /* Have we hit EOF? */ + if (static_cast<ssize_t>(userc->frameCount) < track->frames2ignore) + eof = true; + + track->frames2ignore = 0; + + userc->deallocate(); + } + + /* + Now start reading useful frames, until EOF or + premature EOF. + */ + + while (track->filemodhappy && !eof && vframe < nvframes2read) + { + AFframecount nvframes2pull; + userc->buffer = (char *) samples + bytes_per_vframe * vframe; + + if (vframe <= nvframes2read - _AF_ATOMIC_NVFRAMES) + nvframes2pull = _AF_ATOMIC_NVFRAMES; + else + nvframes2pull = nvframes2read - vframe; + + userc->frameCount = nvframes2pull; + + firstmod->runPull(); + + if (track->filemodhappy) + { + vframe += userc->frameCount; + if (static_cast<ssize_t>(userc->frameCount) < nvframes2pull) + eof = true; + } + } + } + + track->nextvframe += vframe; + + return vframe; +} + +// file: debug.cpp +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + debug.cpp + + This file contains debugging routines for the Audio File + Library. +*/ + + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include <assert.h> + + + +void _af_print_pvlist (AUpvlist list) +{ + assert(list); + + printf("list.valid: %d\n", list->valid); + printf("list.count: %zu\n", list->count); + + for (unsigned i=0; i<list->count; i++) + { + printf("item %u valid %d, should be %d\n", + i, list->items[i].valid, _AU_VALID_PVITEM); + + switch (list->items[i].type) + { + case AU_PVTYPE_LONG: + printf("item #%u, parameter %d, long: %ld\n", + i, list->items[i].parameter, + list->items[i].value.l); + break; + case AU_PVTYPE_DOUBLE: + printf("item #%u, parameter %d, double: %f\n", + i, list->items[i].parameter, + list->items[i].value.d); + break; + case AU_PVTYPE_PTR: + printf("item #%u, parameter %d, pointer: %p\n", + i, list->items[i].parameter, + list->items[i].value.v); + break; + + default: + printf("item #%u, invalid type %d\n", i, + list->items[i].type); + assert(false); + break; + } + } +} + +void _af_print_audioformat (AudioFormat *fmt) +{ + /* sampleRate, channelCount */ + printf("{ %7.2f Hz %d ch ", fmt->sampleRate, fmt->channelCount); + + /* sampleFormat, sampleWidth */ + switch (fmt->sampleFormat) + { + case AF_SAMPFMT_TWOSCOMP: + printf("%db 2 ", fmt->sampleWidth); + break; + case AF_SAMPFMT_UNSIGNED: + printf("%db u ", fmt->sampleWidth); + break; + case AF_SAMPFMT_FLOAT: + printf("flt "); + break; + case AF_SAMPFMT_DOUBLE: + printf("dbl "); + break; + default: + printf("%dsampfmt? ", fmt->sampleFormat); + } + + /* pcm */ + printf("(%.30g+-%.30g [%.30g,%.30g]) ", + fmt->pcm.intercept, fmt->pcm.slope, + fmt->pcm.minClip, fmt->pcm.maxClip); + + /* byteOrder */ + switch (fmt->byteOrder) + { + case AF_BYTEORDER_BIGENDIAN: + printf("big "); + break; + case AF_BYTEORDER_LITTLEENDIAN: + printf("little "); + break; + default: + printf("%dbyteorder? ", fmt->byteOrder); + break; + } + + /* compression */ + { + const CompressionUnit *unit = _af_compression_unit_from_id(fmt->compressionType); + if (!unit) + printf("%dcompression?", fmt->compressionType); + else if (fmt->compressionType == AF_COMPRESSION_NONE) + printf("pcm"); + else + printf("%s", unit->label); + } + + printf(" }"); +} + +void _af_print_tracks (AFfilehandle filehandle) +{ + for (int i=0; i<filehandle->m_trackCount; i++) + { + Track *track = &filehandle->m_tracks[i]; + printf("track %d\n", i); + printf(" id %d\n", track->id); + printf(" sample format\n"); + _af_print_audioformat(&track->f); + printf(" virtual format\n"); + _af_print_audioformat(&track->v); + printf(" total file frames: %jd\n", + (intmax_t) track->totalfframes); + printf(" total virtual frames: %jd\n", + (intmax_t) track->totalvframes); + printf(" next file frame: %jd\n", + (intmax_t) track->nextfframe); + printf(" next virtual frame: %jd\n", + (intmax_t) track->nextvframe); + printf(" frames to ignore: %jd\n", + (intmax_t) track->frames2ignore); + + printf(" data_size: %jd\n", + (intmax_t) track->data_size); + printf(" fpos_first_frame: %jd\n", + (intmax_t) track->fpos_first_frame); + printf(" fpos_next_frame: %jd\n", + (intmax_t) track->fpos_next_frame); + printf(" fpos_after_data: %jd\n", + (intmax_t) track->fpos_after_data); + + printf(" channel matrix:"); + _af_print_channel_matrix(track->channelMatrix, + track->f.channelCount, track->v.channelCount); + printf("\n"); + + printf(" marker count: %d\n", track->markerCount); + } +} + +void _af_print_filehandle (AFfilehandle filehandle) +{ + printf("file handle: 0x%p\n", filehandle); + + if (filehandle->m_valid == _AF_VALID_FILEHANDLE) + printf("valid\n"); + else + printf("invalid!\n"); + + printf(" access: "); + if (filehandle->m_access == _AF_READ_ACCESS) + putchar('r'); + else + putchar('w'); + + printf(" fileFormat: %d\n", filehandle->m_fileFormat); + + printf(" instrument count: %d\n", filehandle->m_instrumentCount); + printf(" instruments: 0x%p\n", filehandle->m_instruments); + + printf(" miscellaneous count: %d\n", filehandle->m_miscellaneousCount); + printf(" miscellaneous: 0x%p\n", filehandle->m_miscellaneous); + + printf(" trackCount: %d\n", filehandle->m_trackCount); + printf(" tracks: 0x%p\n", filehandle->m_tracks); + _af_print_tracks(filehandle); +} + +void _af_print_channel_matrix (double *matrix, int fchans, int vchans) +{ + int v, f; + + if (!matrix) + { + printf("NULL"); + return; + } + + printf("{"); + for (v=0; v < vchans; v++) + { + if (v) printf(" "); + printf("{"); + for (f=0; f < fchans; f++) + { + if (f) printf(" "); + printf("%5.2f", *(matrix + v*fchans + f)); + } + printf("}"); + } + printf("}"); +} + +void _af_print_frame (AFframecount frameno, double *frame, int nchannels, + char *formatstring, int numberwidth, + double slope, double intercept, double minclip, double maxclip) +{ + char linebuf[81]; + int wavewidth = 78 - numberwidth*nchannels - 6; + int c; + + memset(linebuf, ' ', 80); + linebuf[0] = '|'; + linebuf[wavewidth-1] = '|'; + linebuf[wavewidth] = 0; + + printf("%05jd ", (intmax_t) frameno); + + for (c=0; c < nchannels; c++) + { + double pcm = frame[c]; + printf(formatstring, pcm); + } + for (c=0; c < nchannels; c++) + { + double pcm = frame[c], volts; + if (maxclip > minclip) + { + if (pcm < minclip) pcm = minclip; + if (pcm > maxclip) pcm = maxclip; + } + volts = (pcm - intercept) / slope; + linebuf[(int)((volts/2 + 0.5)*(wavewidth-3)) + 1] = '0' + c; + } + printf("%s\n", linebuf); +} + +// file: error.c +/* + Audio File Library + Copyright (C) 1998, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + error.c + + This file contains the routines used in the Audio File Library's + error handling. +*/ + + +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> + +static void defaultErrorFunction (long error, const char *str); + +static AFerrfunc errorFunction = defaultErrorFunction; + +AFerrfunc afSetErrorHandler (AFerrfunc efunc) +{ + AFerrfunc old; + + old = errorFunction; + errorFunction = efunc; + + return old; +} + +static void defaultErrorFunction (long error, const char *str) +{ + fprintf(stderr, "Audio File Library: "); + fprintf(stderr, "%s", str); + fprintf(stderr, " [error %ld]\n", error); +} + +void _af_error (int errorCode, const char *fmt, ...) +{ + char buf[1024]; + va_list ap; + + va_start(ap, fmt); + + vsnprintf(buf, 1024, fmt, ap); + + va_end(ap); + + if (errorFunction != NULL) + errorFunction(errorCode, buf); +} + +// file: extended.c +/* Copyright (C) 1989-1991 Apple Computer, Inc. + * + * All rights reserved. + * + * Warranty Information + * Even though Apple has reviewed this software, Apple makes no warranty + * or representation, either express or implied, with respect to this + * software, its quality, accuracy, merchantability, or fitness for a + * particular purpose. As a result, this software is provided "as is," + * and you, its user, are assuming the entire risk as to its quality + * and accuracy. + * + * This code may be used and freely distributed as long as it includes + * this copyright notice and the above warranty information. + * + * Machine-independent I/O routines for IEEE floating-point numbers. + * + * NaN's and infinities are converted to HUGE_VAL or HUGE, which + * happens to be infinity on IEEE machines. Unfortunately, it is + * impossible to preserve NaN's in a machine-independent way. + * Infinities are, however, preserved on IEEE machines. + * + * These routines have been tested on the following machines: + * Apple Macintosh, MPW 3.1 C compiler + * Apple Macintosh, THINK C compiler + * Silicon Graphics IRIS, MIPS compiler + * Cray X/MP and Y/MP + * Digital Equipment VAX + * Sequent Balance (Multiprocesor 386) + * NeXT + * + * + * Implemented by Malcolm Slaney and Ken Turkowski. + * + * Malcolm Slaney contributions during 1988-1990 include big- and little- + * endian file I/O, conversion to and from Motorola's extended 80-bit + * floating-point format, and conversions to and from IEEE single- + * precision floating-point format. + * + * In 1991, Ken Turkowski implemented the conversions to and from + * IEEE double-precision format, added more precision to the extended + * conversions, and accommodated conversions involving +/- infinity, + * NaN's, and denormalized numbers. + */ + +/**************************************************************** + * Extended precision IEEE floating-point conversion routines. + * Extended is an 80-bit number as defined by Motorola, + * with a sign bit, 15 bits of exponent (offset 16383?), + * and a 64-bit mantissa, with no hidden bit. + ****************************************************************/ + + +#include <math.h> + +#ifndef HUGE_VAL +#define HUGE_VAL HUGE +#endif + +#define FloatToUnsigned(f) ((unsigned long) (((long) (f - 2147483648.0)) + 2147483647L) + 1) + +void _af_convert_to_ieee_extended (double num, unsigned char *bytes) +{ + int sign; + int expon; + double fMant, fsMant; + unsigned long hiMant, loMant; + + if (num < 0) { + sign = 0x8000; + num *= -1; + } else { + sign = 0; + } + + if (num == 0) { + expon = 0; hiMant = 0; loMant = 0; + } + else { + fMant = frexp(num, &expon); + if ((expon > 16384) || !(fMant < 1)) { /* Infinity or NaN */ + expon = sign|0x7FFF; hiMant = 0; loMant = 0; /* infinity */ + } + else { /* Finite */ + expon += 16382; + if (expon < 0) { /* denormalized */ + fMant = ldexp(fMant, expon); + expon = 0; + } + expon |= sign; + fMant = ldexp(fMant, 32); + fsMant = floor(fMant); + hiMant = FloatToUnsigned(fsMant); + fMant = ldexp(fMant - fsMant, 32); + fsMant = floor(fMant); + loMant = FloatToUnsigned(fsMant); + } + } + + bytes[0] = expon >> 8; + bytes[1] = expon; + bytes[2] = hiMant >> 24; + bytes[3] = hiMant >> 16; + bytes[4] = hiMant >> 8; + bytes[5] = hiMant; + bytes[6] = loMant >> 24; + bytes[7] = loMant >> 16; + bytes[8] = loMant >> 8; + bytes[9] = loMant; +} + +#define UnsignedToFloat(u) (((double) ((long) (u - 2147483647L - 1))) + 2147483648.0) + +double _af_convert_from_ieee_extended (const unsigned char *bytes) +{ + double f; + int expon; + unsigned long hiMant, loMant; + + expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); + hiMant = ((unsigned long)(bytes[2] & 0xFF) << 24) + | ((unsigned long) (bytes[3] & 0xFF) << 16) + | ((unsigned long) (bytes[4] & 0xFF) << 8) + | ((unsigned long) (bytes[5] & 0xFF)); + loMant = ((unsigned long) (bytes[6] & 0xFF) << 24) + | ((unsigned long) (bytes[7] & 0xFF) << 16) + | ((unsigned long) (bytes[8] & 0xFF) << 8) + | ((unsigned long) (bytes[9] & 0xFF)); + + if (expon == 0 && hiMant == 0 && loMant == 0) { + f = 0; + } + else { + if (expon == 0x7FFF) { /* Infinity or NaN */ + f = HUGE_VAL; + } + else { + expon -= 16383; + f = ldexp(UnsignedToFloat(hiMant), expon-=31); + f += ldexp(UnsignedToFloat(loMant), expon-=32); + } + } + + if (bytes[0] & 0x80) + return -f; + else + return f; +} + +// file: format.cpp +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + audiofile.c + + This file implements many of the main interface routines of the + Audio File Library. +*/ + + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + + +AFfileoffset afGetDataOffset (AFfilehandle file, int trackid) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + return track->fpos_first_frame; +} + +AFfileoffset afGetTrackBytes (AFfilehandle file, int trackid) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + return track->data_size; +} + +/* + afGetFrameSize returns the size (in bytes) of a sample frame from + the specified track of an audio file. + + stretch3to4 == true: size which user sees + stretch3to4 == false: size used in file +*/ +float afGetFrameSize (AFfilehandle file, int trackid, int stretch3to4) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + return _af_format_frame_size(&track->f, stretch3to4); +} + +float afGetVirtualFrameSize (AFfilehandle file, int trackid, int stretch3to4) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + return _af_format_frame_size(&track->v, stretch3to4); +} + +AFframecount afSeekFrame (AFfilehandle file, int trackid, AFframecount frame) +{ + if (!_af_filehandle_ok(file)) + return -1; + + if (!file->checkCanRead()) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + if (track->ms->isDirty() && track->ms->setup(file, track) == AF_FAIL) + return -1; + + if (frame < 0) + return track->nextvframe; + + /* Optimize the case of seeking to the current position. */ + if (frame == track->nextvframe) + return track->nextvframe; + + /* Limit request to the number of frames in the file. */ + if (track->totalvframes != -1) + if (frame > track->totalvframes) + frame = track->totalvframes - 1; + + /* + Now that the modules are not dirty and frame + represents a valid virtual frame, we call + _AFsetupmodules again after setting track->nextvframe. + + _AFsetupmodules will look at track->nextvframe and + compute track->nextfframe in clever and mysterious + ways. + */ + track->nextvframe = frame; + + if (track->ms->setup(file, track) == AF_FAIL) + return -1; + + return track->nextvframe; +} + +AFfileoffset afTellFrame (AFfilehandle file, int trackid) +{ + return afSeekFrame(file, trackid, -1); +} + +int afSetVirtualByteOrder (AFfilehandle file, int trackid, int byteorder) +{ + if (!_af_filehandle_ok(file)) + return AF_FAIL; + + Track *track = file->getTrack(trackid); + if (!track) + return AF_FAIL; + + if (byteorder != AF_BYTEORDER_BIGENDIAN && + byteorder != AF_BYTEORDER_LITTLEENDIAN) + { + _af_error(AF_BAD_BYTEORDER, "invalid byte order %d", byteorder); + return AF_FAIL; + } + + track->v.byteOrder = byteorder; + track->ms->setDirty(); + + return AF_SUCCEED; +} + +int afGetByteOrder (AFfilehandle file, int trackid) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + return track->f.byteOrder; +} + +int afGetVirtualByteOrder (AFfilehandle file, int trackid) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + return track->v.byteOrder; +} + +AFframecount afGetFrameCount (AFfilehandle file, int trackid) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + if (track->ms->isDirty() && track->ms->setup(file, track) == AF_FAIL) + return -1; + + return track->totalvframes; +} + +double afGetRate (AFfilehandle file, int trackid) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + return track->f.sampleRate; +} + +int afGetChannels (AFfilehandle file, int trackid) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + return track->f.channelCount; +} + +void afGetSampleFormat (AFfilehandle file, int trackid, int *sampleFormat, int *sampleWidth) +{ + if (!_af_filehandle_ok(file)) + return; + + Track *track = file->getTrack(trackid); + if (!track) + return; + + if (sampleFormat) + *sampleFormat = track->f.sampleFormat; + + if (sampleWidth) + *sampleWidth = track->f.sampleWidth; +} + +void afGetVirtualSampleFormat (AFfilehandle file, int trackid, int *sampleFormat, int *sampleWidth) +{ + if (!_af_filehandle_ok(file)) + return; + + Track *track = file->getTrack(trackid); + if (!track) + return; + + if (sampleFormat) + *sampleFormat = track->v.sampleFormat; + + if (sampleWidth) + *sampleWidth = track->v.sampleWidth; +} + +int afSetVirtualSampleFormat (AFfilehandle file, int trackid, + int sampleFormat, int sampleWidth) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + if (_af_set_sample_format(&track->v, sampleFormat, sampleWidth) == AF_FAIL) + return -1; + + track->ms->setDirty(); + + return 0; +} + +/* XXXmpruett fix the version */ +int afGetFileFormat (AFfilehandle file, int *version) +{ + if (!_af_filehandle_ok(file)) + return -1; + + if (version != NULL) + *version = file->getVersion(); + + return file->m_fileFormat; +} + +int afSetVirtualChannels (AFfilehandle file, int trackid, int channelCount) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + track->v.channelCount = channelCount; + track->ms->setDirty(); + + if (track->channelMatrix) + free(track->channelMatrix); + track->channelMatrix = NULL; + + return 0; +} + +double afGetVirtualRate (AFfilehandle file, int trackid) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + return track->v.sampleRate; +} + +int afSetVirtualRate (AFfilehandle file, int trackid, double rate) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + if (rate < 0) + { + _af_error(AF_BAD_RATE, "invalid sampling rate %.30g", rate); + return -1; + } + + track->v.sampleRate = rate; + track->ms->setDirty(); + + return 0; +} + +void afSetChannelMatrix (AFfilehandle file, int trackid, double* matrix) +{ + if (!_af_filehandle_ok(file)) + return; + + Track *track = file->getTrack(trackid); + if (!track) + return; + + if (track->channelMatrix != NULL) + free(track->channelMatrix); + track->channelMatrix = NULL; + + if (matrix != NULL) + { + int i, size; + + size = track->v.channelCount * track->f.channelCount; + + track->channelMatrix = (double *) malloc(size * sizeof (double)); + + for (i = 0; i < size; i++) + track->channelMatrix[i] = matrix[i]; + } +} + +int afGetVirtualChannels (AFfilehandle file, int trackid) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + return track->v.channelCount; +} + +// file: g711.c +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#define SUPERCEDED + +/* + * g711.c + * + * u-law, A-law and linear PCM conversions. + */ +#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define NSEGS (8) /* Number of A-law segments. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ +#define SEG_MASK (0x70) /* Segment field mask. */ + +/* see libst.h */ +#ifdef SUPERCEDED + +static const short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF, + 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF}; + +static int search(int val, const short *table, int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (val <= *table++) + return (i); + } + return (size); +} + +/* + * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +unsigned char +_af_linear2alaw(int pcm_val) +{ + int mask; + int seg; + unsigned char aval; + + if (pcm_val >= 0) { + mask = 0xD5; /* sign (7th) bit = 1 */ + } else { + mask = 0x55; /* sign bit = 0 */ + pcm_val = -pcm_val - 8; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_end, 8); + + /* Combine the sign, segment, and quantization bits. */ + + if (seg >= 8) /* out of range, return maximum value. */ + return (0x7F ^ mask); + else { + aval = seg << SEG_SHIFT; + if (seg < 2) + aval |= (pcm_val >> 4) & QUANT_MASK; + else + aval |= (pcm_val >> (seg + 3)) & QUANT_MASK; + return (aval ^ mask); + } +} + +/* + * alaw2linear() - Convert an A-law value to 16-bit linear PCM + * + */ +int +_af_alaw2linear(unsigned char a_val) +{ + int t; + int seg; + + a_val ^= 0x55; + + t = (a_val & QUANT_MASK) << 4; + seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; + switch (seg) { + case 0: + t += 8; + break; + case 1: + t += 0x108; + break; + default: + t += 0x108; + t <<= seg - 1; + } + return ((a_val & SIGN_BIT) ? t : -t); +} + +#define BIAS (0x84) /* Bias for linear code. */ + +/* + * linear2ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +/* 2's complement (16-bit range) */ +unsigned char _af_linear2ulaw (int pcm_val) +{ + int mask; + int seg; + unsigned char uval; + + /* Get the sign and the magnitude of the value. */ + if (pcm_val < 0) { + pcm_val = BIAS - pcm_val; + mask = 0x7F; + } else { + pcm_val += BIAS; + mask = 0xFF; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_end, 8); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + if (seg >= 8) /* out of range, return maximum value. */ + return (0x7F ^ mask); + else { + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); + return (uval ^ mask); + } + +} + +/* + * ulaw2linear() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +int _af_ulaw2linear (unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & QUANT_MASK) << 3) + BIAS; + t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; + + return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); +} + +#endif + +// file: openclose.cpp +/* + Audio File Library + Copyright (C) 2000-2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + + +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <audiofile.h> + + +static status _afOpenFile (int access, File *f, const char *filename, + AFfilehandle *file, AFfilesetup filesetup); + +int _af_identify (File *f, int *implemented) +{ + if (!f->canSeek()) + { + _af_error(AF_BAD_LSEEK, "Cannot seek in file"); + return AF_FILE_UNKNOWN; + } + + AFfileoffset curpos = f->tell(); + + for (int i=0; i<_AF_NUM_UNITS; i++) + { + if (_af_units[i].recognize && + _af_units[i].recognize(f)) + { + if (implemented != NULL) + *implemented = _af_units[i].implemented; + f->seek(curpos, File::SeekFromBeginning); + return _af_units[i].fileFormat; + } + } + + f->seek(curpos, File::SeekFromBeginning); + + if (implemented != NULL) + *implemented = false; + + return AF_FILE_UNKNOWN; +} + +int afIdentifyFD (int fd) +{ + /* + Duplicate the file descriptor since otherwise the + original file descriptor would get closed when we close + the virtual file below. + */ + fd = dup(fd); + File *f = File::create(fd, File::ReadAccess); + + int result = _af_identify(f, NULL); + + delete f; + + return result; +} + +int afIdentifyNamedFD (int fd, const char *filename, int *implemented) +{ + /* + Duplicate the file descriptor since otherwise the + original file descriptor would get closed when we close + the virtual file below. + */ + fd = dup(fd); + + File *f = File::create(fd, File::ReadAccess); + if (!f) + { + _af_error(AF_BAD_OPEN, "could not open file '%s'", filename); + return AF_FILE_UNKNOWN; + } + + int result = _af_identify(f, implemented); + + delete f; + + return result; +} + +AFfilehandle afOpenFD (int fd, const char *mode, AFfilesetup setup) +{ + if (!mode) + { + _af_error(AF_BAD_ACCMODE, "null access mode"); + return AF_NULL_FILEHANDLE; + } + + int access; + if (mode[0] == 'r') + { + access = _AF_READ_ACCESS; + } + else if (mode[0] == 'w') + { + access = _AF_WRITE_ACCESS; + } + else + { + _af_error(AF_BAD_ACCMODE, "unrecognized access mode '%s'", mode); + return AF_NULL_FILEHANDLE; + } + + File *f = File::create(fd, access == _AF_READ_ACCESS ? + File::ReadAccess : File::WriteAccess); + + AFfilehandle filehandle = NULL; + if (_afOpenFile(access, f, NULL, &filehandle, setup) != AF_SUCCEED) + { + delete f; + } + + return filehandle; +} + +AFfilehandle afOpenNamedFD (int fd, const char *mode, AFfilesetup setup, + const char *filename) +{ + if (!mode) + { + _af_error(AF_BAD_ACCMODE, "null access mode"); + return AF_NULL_FILEHANDLE; + } + + int access; + if (mode[0] == 'r') + access = _AF_READ_ACCESS; + else if (mode[0] == 'w') + access = _AF_WRITE_ACCESS; + else + { + _af_error(AF_BAD_ACCMODE, "unrecognized access mode '%s'", mode); + return AF_NULL_FILEHANDLE; + } + + File *f = File::create(fd, access == _AF_READ_ACCESS ? + File::ReadAccess : File::WriteAccess); + + AFfilehandle filehandle; + if (_afOpenFile(access, f, filename, &filehandle, setup) != AF_SUCCEED) + { + delete f; + } + + return filehandle; +} + +AFfilehandle afOpenFile (const char *filename, const char *mode, AFfilesetup setup) +{ + if (!mode) + { + _af_error(AF_BAD_ACCMODE, "null access mode"); + return AF_NULL_FILEHANDLE; + } + + int access; + if (mode[0] == 'r') + { + access = _AF_READ_ACCESS; + } + else if (mode[0] == 'w') + { + access = _AF_WRITE_ACCESS; + } + else + { + _af_error(AF_BAD_ACCMODE, "unrecognized access mode '%s'", mode); + return AF_NULL_FILEHANDLE; + } + + File *f = File::open(filename, + access == _AF_READ_ACCESS ? File::ReadAccess : File::WriteAccess); + if (!f) + { + _af_error(AF_BAD_OPEN, "could not open file '%s'", filename); + return AF_NULL_FILEHANDLE; + } + + AFfilehandle filehandle; + if (_afOpenFile(access, f, filename, &filehandle, setup) != AF_SUCCEED) + { + delete f; + } + + return filehandle; +} + +AFfilehandle afOpenVirtualFile (AFvirtualfile *vf, const char *mode, + AFfilesetup setup) +{ + if (!vf) + { + _af_error(AF_BAD_OPEN, "null virtual file"); + return AF_NULL_FILEHANDLE; + } + + if (!mode) + { + _af_error(AF_BAD_ACCMODE, "null access mode"); + return AF_NULL_FILEHANDLE; + } + + int access; + if (mode[0] == 'r') + { + access = _AF_READ_ACCESS; + } + else if (mode[0] == 'w') + { + access = _AF_WRITE_ACCESS; + } + else + { + _af_error(AF_BAD_ACCMODE, "unrecognized access mode '%s'", mode); + return AF_NULL_FILEHANDLE; + } + + File *f = File::create(vf, + access == _AF_READ_ACCESS ? File::ReadAccess : File::WriteAccess); + if (!f) + { + _af_error(AF_BAD_OPEN, "could not open virtual file"); + return AF_NULL_FILEHANDLE; + } + + AFfilehandle filehandle; + if (_afOpenFile(access, f, NULL, &filehandle, setup) != AF_SUCCEED) + { + delete f; + } + + return filehandle; +} + +static status _afOpenFile (int access, File *f, const char *filename, + AFfilehandle *file, AFfilesetup filesetup) +{ + int fileFormat = AF_FILE_UNKNOWN; + int implemented = true; + + int userSampleFormat = 0; + double userSampleRate = 0.0; + PCMInfo userPCM = {0}; + bool userFormatSet = false; + + AFfilehandle filehandle = AF_NULL_FILEHANDLE; + AFfilesetup completesetup = AF_NULL_FILESETUP; + + *file = AF_NULL_FILEHANDLE; + + if (access == _AF_WRITE_ACCESS || filesetup != AF_NULL_FILESETUP) + { + if (!_af_filesetup_ok(filesetup)) + return AF_FAIL; + + fileFormat = filesetup->fileFormat; + if (access == _AF_READ_ACCESS && fileFormat != AF_FILE_RAWDATA) + { + _af_error(AF_BAD_FILESETUP, + "warning: opening file for read access: " + "ignoring file setup with non-raw file format"); + filesetup = AF_NULL_FILESETUP; + fileFormat = _af_identify(f, &implemented); + } + } + else if (filesetup == AF_NULL_FILESETUP) + fileFormat = _af_identify(f, &implemented); + + if (fileFormat == AF_FILE_UNKNOWN) + { + if (filename != NULL) + _af_error(AF_BAD_NOT_IMPLEMENTED, + "'%s': unrecognized audio file format", + filename); + else + _af_error(AF_BAD_NOT_IMPLEMENTED, + "unrecognized audio file format"); + return AF_FAIL; + } + + const char *formatName = _af_units[fileFormat].name; + + if (!implemented) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, + "%s format not currently supported", formatName); + } + + completesetup = NULL; + if (filesetup != AF_NULL_FILESETUP) + { + userSampleFormat = filesetup->tracks[0].f.sampleFormat; + userPCM = filesetup->tracks[0].f.pcm; + userSampleRate = filesetup->tracks[0].f.sampleRate; + userFormatSet = true; + if ((completesetup = _af_units[fileFormat].completesetup(filesetup)) == NULL) + return AF_FAIL; + } + + filehandle = _AFfilehandle::create(fileFormat); + if (!filehandle) + { + if (completesetup) + afFreeFileSetup(completesetup); + return AF_FAIL; + } + + filehandle->m_fh = f; + filehandle->m_access = access; + filehandle->m_seekok = f->canSeek(); + if (filename != NULL) + filehandle->m_fileName = strdup(filename); + else + filehandle->m_fileName = NULL; + filehandle->m_fileFormat = fileFormat; + + status result = access == _AF_READ_ACCESS ? + filehandle->readInit(completesetup) : + filehandle->writeInit(completesetup); + + if (result != AF_SUCCEED) + { + delete filehandle; + filehandle = AF_NULL_FILEHANDLE; + if (completesetup) + afFreeFileSetup(completesetup); + return AF_FAIL; + } + + if (completesetup) + afFreeFileSetup(completesetup); + + /* + Initialize virtual format. + */ + for (int t=0; t<filehandle->m_trackCount; t++) + { + Track *track = &filehandle->m_tracks[t]; + + track->v = track->f; + + if (userFormatSet) + { + track->v.sampleFormat = userSampleFormat; + track->v.pcm = userPCM; + track->v.sampleRate = userSampleRate; + } + + track->v.compressionType = AF_COMPRESSION_NONE; + track->v.compressionParams = NULL; + +#if WORDS_BIGENDIAN + track->v.byteOrder = AF_BYTEORDER_BIGENDIAN; +#else + track->v.byteOrder = AF_BYTEORDER_LITTLEENDIAN; +#endif + + track->ms = new ModuleState(); + if (track->ms->init(filehandle, track) == AF_FAIL) + { + delete filehandle; + return AF_FAIL; + } + } + + *file = filehandle; + + return AF_SUCCEED; +} + +int afSyncFile (AFfilehandle handle) +{ + if (!_af_filehandle_ok(handle)) + return -1; + + if (handle->m_access == _AF_WRITE_ACCESS) + { + /* Finish writes on all tracks. */ + for (int trackno = 0; trackno < handle->m_trackCount; trackno++) + { + Track *track = &handle->m_tracks[trackno]; + + if (track->ms->isDirty() && track->ms->setup(handle, track) == AF_FAIL) + return -1; + + if (track->ms->sync(handle, track) != AF_SUCCEED) + return -1; + } + + /* Update file headers. */ + if (handle->update() != AF_SUCCEED) + return AF_FAIL; + } + else if (handle->m_access == _AF_READ_ACCESS) + { + /* Do nothing. */ + } + else + { + _af_error(AF_BAD_ACCMODE, "unrecognized access mode %d", + handle->m_access); + return AF_FAIL; + } + + return AF_SUCCEED; +} + +int afCloseFile (AFfilehandle file) +{ + int err; + + if (!_af_filehandle_ok(file)) + return -1; + + afSyncFile(file); + + err = file->m_fh->close(); + if (err < 0) + _af_error(AF_BAD_CLOSE, "close returned %d", err); + + delete file->m_fh; + delete file; + + return 0; +} + +// file: pcm.cpp +/* + Audio File Library + Copyright (C) 1999-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000-2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + pcm.cpp + + This file declares default PCM mappings and defines routines + for accessing and modifying PCM mappings in a track. +*/ + + + +extern const PCMInfo _af_default_signed_integer_pcm_mappings[] = +{ + {0, 0, 0, 0}, + {SLOPE_INT8, 0, MIN_INT8, MAX_INT8}, + {SLOPE_INT16, 0, MIN_INT16, MAX_INT16}, + {SLOPE_INT24, 0, MIN_INT24, MAX_INT24}, + {SLOPE_INT32, 0, MIN_INT32, MAX_INT32} +}; + +extern const PCMInfo _af_default_unsigned_integer_pcm_mappings[] = +{ + {0, 0, 0, 0}, + {SLOPE_INT8, INTERCEPT_U_INT8, 0, MAX_U_INT8}, + {SLOPE_INT16, INTERCEPT_U_INT16, 0, MAX_U_INT16}, + {SLOPE_INT24, INTERCEPT_U_INT24, 0, MAX_U_INT24}, + {SLOPE_INT32, INTERCEPT_U_INT32, 0, MAX_U_INT32} +}; + +extern const PCMInfo _af_default_float_pcm_mapping = +{1, 0, 0, 0}; + +extern const PCMInfo _af_default_double_pcm_mapping = +{1, 0, 0, 0}; + +/* + Initialize the PCM mapping for a given track. +*/ +void afInitPCMMapping (AFfilesetup setup, int trackid, + double slope, double intercept, double minClip, double maxClip) +{ + if (!_af_filesetup_ok(setup)) + return; + + TrackSetup *track = setup->getTrack(trackid); + if (!track) + return; + + track->f.pcm.slope = slope; + track->f.pcm.intercept = intercept; + track->f.pcm.minClip = minClip; + track->f.pcm.maxClip = maxClip; +} + +int afSetVirtualPCMMapping (AFfilehandle file, int trackid, + double slope, double intercept, double minClip, double maxClip) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + track->v.pcm.slope = slope; + track->v.pcm.intercept = intercept; + track->v.pcm.minClip = minClip; + track->v.pcm.maxClip = maxClip; + + track->ms->setDirty(); + + return 0; +} + +int afSetTrackPCMMapping (AFfilehandle file, int trackid, + double slope, double intercept, double minClip, double maxClip) +{ + if (!_af_filehandle_ok(file)) + return -1; + + Track *track = file->getTrack(trackid); + if (!track) + return -1; + + /* + NOTE: this is highly unusual: we don't ordinarily + change track.f after the file is opened. + + PCM mapping is the exception because this information + is not encoded in sound files' headers using today's + formats, so the user will probably want to set this + information on a regular basis. The defaults may or + may not be what the user wants. + */ + + track->f.pcm.slope = slope; + track->f.pcm.intercept = intercept; + track->f.pcm.minClip = minClip; + track->f.pcm.maxClip = maxClip; + + track->ms->setDirty(); + + return 0; +} + +void afGetPCMMapping (AFfilehandle file, int trackid, + double *slope, double *intercept, double *minClip, double *maxClip) +{ + if (!_af_filehandle_ok(file)) + return; + + Track *track = file->getTrack(trackid); + if (!track) + return; + + if (slope) + *slope = track->f.pcm.slope; + if (intercept) + *intercept = track->f.pcm.intercept; + if (minClip) + *minClip = track->f.pcm.minClip; + if (maxClip) + *maxClip = track->f.pcm.maxClip; +} + +void afGetVirtualPCMMapping (AFfilehandle file, int trackid, + double *slope, double *intercept, double *minClip, double *maxClip) +{ + if (!_af_filehandle_ok(file)) + return; + + Track *track = file->getTrack(trackid); + if (!track) + return; + + if (slope) + *slope = track->v.pcm.slope; + if (intercept) + *intercept = track->v.pcm.intercept; + if (minClip) + *minClip = track->v.pcm.minClip; + if (maxClip) + *maxClip = track->v.pcm.maxClip; +} + +// file: query.cpp +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + query.cpp + + This file contains the implementation of the Audio File Library's + query mechanism. Querying through the afQuery calls can allow the + programmer to determine the capabilities and characteristics of + the Audio File Library implementation and its supported formats. +*/ + + +#include <assert.h> +#include <stdlib.h> + + +AUpvlist _afQueryFileFormat (int arg1, int arg2, int arg3, int arg4); +AUpvlist _afQueryInstrument (int arg1, int arg2, int arg3, int arg4); +AUpvlist _afQueryInstrumentParameter (int arg1, int arg2, int arg3, int arg4); +AUpvlist _afQueryLoop (int arg1, int arg2, int arg3, int arg4); +AUpvlist _afQueryMarker (int arg1, int arg2, int arg3, int arg4); +AUpvlist _afQueryMiscellaneous (int arg1, int arg2, int arg3, int arg4); +AUpvlist _afQueryCompression (int arg1, int arg2, int arg3, int arg4); +AUpvlist _afQueryCompressionParameter (int arg1, int arg2, int arg3, int arg4); + +AUpvlist afQuery (int querytype, int arg1, int arg2, int arg3, int arg4) +{ + switch (querytype) + { + case AF_QUERYTYPE_INST: + return _afQueryInstrument(arg1, arg2, arg3, arg4); + case AF_QUERYTYPE_INSTPARAM: + return _afQueryInstrumentParameter(arg1, arg2, arg3, arg4); + case AF_QUERYTYPE_LOOP: + return _afQueryLoop(arg1, arg2, arg3, arg4); + case AF_QUERYTYPE_FILEFMT: + return _afQueryFileFormat(arg1, arg2, arg3, arg4); + case AF_QUERYTYPE_COMPRESSION: + return _afQueryCompression(arg1, arg2, arg3, arg4); + case AF_QUERYTYPE_COMPRESSIONPARAM: + /* FIXME: This selector is not implemented. */ + return AU_NULL_PVLIST; + case AF_QUERYTYPE_MISC: + /* FIXME: This selector is not implemented. */ + return AU_NULL_PVLIST; + case AF_QUERYTYPE_MARK: + return _afQueryMarker(arg1, arg2, arg3, arg4); + } + + _af_error(AF_BAD_QUERYTYPE, "bad query type"); + return AU_NULL_PVLIST; +} + +/* ARGSUSED3 */ +AUpvlist _afQueryFileFormat (int arg1, int arg2, int arg3, int arg4) +{ + switch (arg1) + { + /* The following select only on arg1. */ + case AF_QUERY_ID_COUNT: + { + int count = 0, idx; + for (idx = 0; idx < _AF_NUM_UNITS; idx++) + if (_af_units[idx].implemented) + count++; + return _af_pv_long(count); + } + /* NOTREACHED */ + break; + + case AF_QUERY_IDS: + { + int count = 0, idx; + int *buffer; + + buffer = (int *) _af_calloc(_AF_NUM_UNITS, sizeof (int)); + if (buffer == NULL) + return AU_NULL_PVLIST; + + for (idx = 0; idx < _AF_NUM_UNITS; idx++) + if (_af_units[idx].implemented) + buffer[count++] = idx; + + if (count == 0) + { + free(buffer); + return AU_NULL_PVLIST; + } + + return _af_pv_pointer(buffer); + } + /* NOTREACHED */ + break; + + /* The following select on arg2. */ + case AF_QUERY_LABEL: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_pointer(const_cast<char *>(_af_units[arg2].label)); + + case AF_QUERY_NAME: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_pointer(const_cast<char *>(_af_units[arg2].name)); + + case AF_QUERY_DESC: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_pointer(const_cast<char *>(_af_units[arg2].description)); + + case AF_QUERY_IMPLEMENTED: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return _af_pv_long(0); + return _af_pv_long(_af_units[arg2].implemented); + + /* The following select on arg3. */ + case AF_QUERY_SAMPLE_FORMATS: + if (arg3 < 0 || arg3 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + switch (arg2) + { + case AF_QUERY_DEFAULT: + return _af_pv_long(_af_units[arg3].defaultSampleFormat); + default: + break; + } + /* NOTREACHED */ + break; + + case AF_QUERY_SAMPLE_SIZES: + if (arg3 < 0 || arg3 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + + switch (arg2) + { + case AF_QUERY_DEFAULT: + return _af_pv_long(_af_units[arg3].defaultSampleWidth); + default: + break; + } + /* NOTREACHED */ + break; + + case AF_QUERY_COMPRESSION_TYPES: + { + int idx, count; + int *buffer; + + if (arg3 < 0 || arg3 >= _AF_NUM_UNITS) + { + _af_error(AF_BAD_QUERY, + "unrecognized file format %d", arg3); + return AU_NULL_PVLIST; + } + + switch (arg2) + { + case AF_QUERY_VALUE_COUNT: + count = _af_units[arg3].compressionTypeCount; + return _af_pv_long(count); + + case AF_QUERY_VALUES: + count = _af_units[arg3].compressionTypeCount; + if (count == 0) + return AU_NULL_PVLIST; + + buffer = (int *) _af_calloc(count, sizeof (int)); + if (buffer == NULL) + return AU_NULL_PVLIST; + + for (idx = 0; idx < count; idx++) + { + buffer[idx] = _af_units[arg3].compressionTypes[idx]; + } + + return _af_pv_pointer(buffer); + } + } + break; + } + + _af_error(AF_BAD_QUERY, "bad query selector"); + return AU_NULL_PVLIST; +} + +long afQueryLong (int querytype, int arg1, int arg2, int arg3, int arg4) +{ + AUpvlist list; + int type; + long value; + + list = afQuery(querytype, arg1, arg2, arg3, arg4); + if (list == AU_NULL_PVLIST) + return -1; + AUpvgetvaltype(list, 0, &type); + if (type != AU_PVTYPE_LONG) + return -1; + AUpvgetval(list, 0, &value); + AUpvfree(list); + return value; +} + +double afQueryDouble (int querytype, int arg1, int arg2, int arg3, int arg4) +{ + AUpvlist list; + int type; + double value; + + list = afQuery(querytype, arg1, arg2, arg3, arg4); + if (list == AU_NULL_PVLIST) + return -1; + AUpvgetvaltype(list, 0, &type); + if (type != AU_PVTYPE_DOUBLE) + return -1; + AUpvgetval(list, 0, &value); + AUpvfree(list); + return value; +} + +void *afQueryPointer (int querytype, int arg1, int arg2, int arg3, int arg4) +{ + AUpvlist list; + int type; + void *value; + + list = afQuery(querytype, arg1, arg2, arg3, arg4); + if (list == AU_NULL_PVLIST) + return NULL; + AUpvgetvaltype(list, 0, &type); + if (type != AU_PVTYPE_PTR) + return NULL; + AUpvgetval(list, 0, &value); + AUpvfree(list); + return value; +} + +/* ARGSUSED3 */ +AUpvlist _afQueryInstrumentParameter (int arg1, int arg2, int arg3, int arg4) +{ + switch (arg1) + { + /* For the following query types, arg2 is the file format. */ + case AF_QUERY_SUPPORTED: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_long(_af_units[arg2].instrumentParameterCount != 0); + + case AF_QUERY_ID_COUNT: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_long(_af_units[arg2].instrumentParameterCount); + + case AF_QUERY_IDS: + { + int count; + int *buffer; + + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + count = _af_units[arg2].instrumentParameterCount; + if (count == 0) + return AU_NULL_PVLIST; + buffer = (int *) _af_calloc(count, sizeof (int)); + if (buffer == NULL) + return AU_NULL_PVLIST; + for (int i=0; i<count; i++) + buffer[i] = _af_units[arg2].instrumentParameters[i].id; + return _af_pv_pointer(buffer); + } + /* NOTREACHED */ + break; + + /* + For the next few query types, arg2 is the file + format and arg3 is the instrument parameter id. + */ + case AF_QUERY_TYPE: + { + int idx; + + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + + idx = _af_instparam_index_from_id(arg2, arg3); + if (idx<0) + return AU_NULL_PVLIST; + return _af_pv_long(_af_units[arg2].instrumentParameters[idx].type); + } + + case AF_QUERY_NAME: + { + int idx; + + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + idx = _af_instparam_index_from_id(arg2, arg3); + if (idx < 0) + return AU_NULL_PVLIST; + return _af_pv_pointer(const_cast<char *>(_af_units[arg2].instrumentParameters[idx].name)); + } + + case AF_QUERY_DEFAULT: + { + int idx; + + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + idx = _af_instparam_index_from_id(arg2, arg3); + if (idx >= 0) + { + AUpvlist ret = AUpvnew(1); + AUpvsetparam(ret, 0, _af_units[arg2].instrumentParameters[idx].id); + AUpvsetvaltype(ret, 0, _af_units[arg2].instrumentParameters[idx].type); + AUpvsetval(ret, 0, const_cast<AFPVu *>(&_af_units[arg2].instrumentParameters[idx].defaultValue)); + return ret; + } + return AU_NULL_PVLIST; + } + } + + _af_error(AF_BAD_QUERY, "bad query selector"); + return AU_NULL_PVLIST; +} + +/* ARGSUSED2 */ +AUpvlist _afQueryLoop (int arg1, int arg2, int arg3, int arg4) +{ + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + + switch (arg1) + { + case AF_QUERY_SUPPORTED: + return _af_pv_long(_af_units[arg2].loopPerInstrumentCount != 0); + case AF_QUERY_MAX_NUMBER: + return _af_pv_long(_af_units[arg2].loopPerInstrumentCount); + } + + _af_error(AF_BAD_QUERY, "bad query selector"); + return AU_NULL_PVLIST; +} + +/* ARGSUSED2 */ +AUpvlist _afQueryInstrument (int arg1, int arg2, int arg3, int arg4) +{ + switch (arg1) + { + case AF_QUERY_SUPPORTED: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_long(_af_units[arg2].instrumentCount != 0); + + case AF_QUERY_MAX_NUMBER: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_long(_af_units[arg2].instrumentCount); + } + + _af_error(AF_BAD_QUERY, "bad query selector"); + return AU_NULL_PVLIST; +} + +/* ARGSUSED0 */ +AUpvlist _afQueryMiscellaneous (int arg1, int arg2, int arg3, int arg4) +{ + _af_error(AF_BAD_NOT_IMPLEMENTED, "not implemented yet"); + return AU_NULL_PVLIST; +} + +/* ARGSUSED2 */ +AUpvlist _afQueryMarker (int arg1, int arg2, int arg3, int arg4) +{ + switch (arg1) + { + case AF_QUERY_SUPPORTED: + return _af_pv_long(_af_units[arg2].markerCount != 0); + case AF_QUERY_MAX_NUMBER: + return _af_pv_long(_af_units[arg2].markerCount); + } + + _af_error(AF_BAD_QUERY, "bad query selector"); + return AU_NULL_PVLIST; +} + +/* ARGSUSED0 */ +AUpvlist _afQueryCompression (int arg1, int arg2, int arg3, int arg4) +{ + const CompressionUnit *unit = NULL; + + switch (arg1) + { + case AF_QUERY_ID_COUNT: + { + int count = 0; + for (int i = 0; i < _AF_NUM_COMPRESSION; i++) + if (_af_compression[i].implemented) + count++; + return _af_pv_long(count); + } + + case AF_QUERY_IDS: + { + int *buf = (int *) _af_calloc(_AF_NUM_COMPRESSION, sizeof (int)); + if (!buf) + return AU_NULL_PVLIST; + + int count = 0; + for (int i = 0; i < _AF_NUM_COMPRESSION; i++) + { + if (_af_compression[i].implemented) + buf[count++] = _af_compression[i].compressionID; + } + return _af_pv_pointer(buf); + } + + case AF_QUERY_IMPLEMENTED: + unit = _af_compression_unit_from_id(arg2); + if (!unit) + return _af_pv_long(0); + return _af_pv_long(unit->implemented); + + case AF_QUERY_NATIVE_SAMPFMT: + unit = _af_compression_unit_from_id(arg2); + if (!unit) + return AU_NULL_PVLIST; + return _af_pv_long(unit->nativeSampleFormat); + + case AF_QUERY_NATIVE_SAMPWIDTH: + unit = _af_compression_unit_from_id(arg2); + if (!unit) + return AU_NULL_PVLIST; + return _af_pv_long(unit->nativeSampleWidth); + + case AF_QUERY_LABEL: + unit = _af_compression_unit_from_id(arg2); + if (!unit) + return AU_NULL_PVLIST; + return _af_pv_pointer(const_cast<char *>(unit->label)); + + case AF_QUERY_NAME: + unit = _af_compression_unit_from_id(arg2); + if (!unit) + return AU_NULL_PVLIST; + return _af_pv_pointer(const_cast<char *>(unit->shortname)); + + case AF_QUERY_DESC: + unit = _af_compression_unit_from_id(arg2); + if (!unit) + return AU_NULL_PVLIST; + return _af_pv_pointer(const_cast<char *>(unit->name)); + } + + _af_error(AF_BAD_QUERY, "unrecognized query selector %d\n", arg1); + return AU_NULL_PVLIST; +} + +// file: units.cpp +/* + Audio File Library + Copyright (C) 2000-2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + units.cpp + + This file contains the file format units. +*/ + + + + + + +const Unit _af_units[_AF_NUM_UNITS] = +{ + { + AF_FILE_RAWDATA, + "Raw Data", "Raw Sound Data", "raw", + true, + &RawFile::completeSetup, + &RawFile::recognize, + AF_SAMPFMT_TWOSCOMP, 16, + _AF_RAW_NUM_COMPTYPES, + _af_raw_compression_types, + 0, /* maximum marker count */ + 0, /* maximum instrument count */ + 0, /* maxium number of loops per instrument */ + 0, NULL, + }, + { + AF_FILE_AIFFC, + "AIFF-C", "AIFF-C File Format", "aifc", + true, + AIFFFile::completeSetup, + AIFFFile::recognizeAIFFC, + AF_SAMPFMT_TWOSCOMP, 16, + _AF_AIFFC_NUM_COMPTYPES, + _af_aiffc_compression_types, + 65535, /* maximum marker count */ + 1, /* maximum instrument count */ + 2, /* maximum number of loops per instrument */ + _AF_AIFF_NUM_INSTPARAMS, + _af_aiff_inst_params + }, + { + AF_FILE_AIFF, + "AIFF", "Audio Interchange File Format", "aiff", + true, + AIFFFile::completeSetup, + AIFFFile::recognizeAIFF, + AF_SAMPFMT_TWOSCOMP, 16, + 0, /* supported compression types */ + NULL, + 65535, /* maximum marker count */ + 1, /* maximum instrument count */ + 2, /* maximum number of loops per instrument */ + _AF_AIFF_NUM_INSTPARAMS, + _af_aiff_inst_params + }, + { + AF_FILE_WAVE, + "MS RIFF WAVE", "Microsoft RIFF WAVE Format", "wave", + true, + WAVEFile::completeSetup, + WAVEFile::recognize, + AF_SAMPFMT_TWOSCOMP, 16, + _AF_WAVE_NUM_COMPTYPES, + _af_wave_compression_types, + AF_NUM_UNLIMITED, /* maximum marker count */ + 1, /* maximum instrument count */ + AF_NUM_UNLIMITED, /* maximum number of loops per instrument */ + _AF_WAVE_NUM_INSTPARAMS, + _af_wave_inst_params + }, +}; + +const CompressionUnit _af_compression[_AF_NUM_COMPRESSION] = +{ + { + AF_COMPRESSION_NONE, + true, + "none", /* label */ + "none", /* short name */ + "not compressed", + 1.0, + AF_SAMPFMT_TWOSCOMP, 16, + false, /* needsRebuffer */ + false, /* multiple_of */ + _af_pcm_format_ok, + _AFpcminitcompress, _AFpcminitdecompress + }, + { + AF_COMPRESSION_G711_ULAW, + true, + "ulaw", /* label */ + "CCITT G.711 u-law", /* shortname */ + "CCITT G.711 u-law", + 2.0, + AF_SAMPFMT_TWOSCOMP, 16, + false, /* needsRebuffer */ + false, /* multiple_of */ + _af_g711_format_ok, + _AFg711initcompress, _AFg711initdecompress + }, + { + AF_COMPRESSION_G711_ALAW, + true, + "alaw", /* label */ + "CCITT G.711 A-law", /* short name */ + "CCITT G.711 A-law", + 2.0, + AF_SAMPFMT_TWOSCOMP, 16, + false, /* needsRebuffer */ + false, /* multiple_of */ + _af_g711_format_ok, + _AFg711initcompress, _AFg711initdecompress + }, + { + AF_COMPRESSION_MS_ADPCM, + true, + "msadpcm", /* label */ + "MS ADPCM", /* short name */ + "Microsoft ADPCM", + 4.0, + AF_SAMPFMT_TWOSCOMP, 16, + true, /* needsRebuffer */ + false, /* multiple_of */ + _af_ms_adpcm_format_ok, + _af_ms_adpcm_init_compress, _af_ms_adpcm_init_decompress + }, +}; + +// file: util.cpp +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + util.c + + This file contains general utility routines for the Audio File + Library. +*/ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + + + +/* + _af_filesetup_ok and _af_filehandle_ok are sanity check routines + which are called at the beginning of every external subroutine. +*/ +bool _af_filesetup_ok (AFfilesetup setup) +{ + if (setup == AF_NULL_FILESETUP) + { + _af_error(AF_BAD_FILESETUP, "null file setup"); + return false; + } + if (setup->valid != _AF_VALID_FILESETUP) + { + _af_error(AF_BAD_FILESETUP, "invalid file setup"); + return false; + } + return true; +} + +bool _af_filehandle_ok (AFfilehandle file) +{ + if (file == AF_NULL_FILEHANDLE) + { + _af_error(AF_BAD_FILEHANDLE, "null file handle"); + return false; + } + if (file->m_valid != _AF_VALID_FILEHANDLE) + { + _af_error(AF_BAD_FILEHANDLE, "invalid file handle"); + return false; + } + return true; +} + +void *_af_malloc (size_t size) +{ + void *p; + + if (size <= 0) + { + _af_error(AF_BAD_MALLOC, "bad memory allocation size request %zd", size); + return NULL; + } + + p = malloc(size); + +#ifdef AF_DEBUG + if (p) + memset(p, 0xff, size); +#endif + + if (p == NULL) + { + _af_error(AF_BAD_MALLOC, "allocation of %zd bytes failed", size); + return NULL; + } + + return p; +} + +char *_af_strdup (const char *s) +{ + char *p = (char *) malloc(strlen(s) + 1); + + if (p) + strcpy(p, s); + + return p; +} + +void *_af_realloc (void *p, size_t size) +{ + if (size <= 0) + { + _af_error(AF_BAD_MALLOC, "bad memory allocation size request %zd", size); + return NULL; + } + + p = realloc(p, size); + + if (p == NULL) + { + _af_error(AF_BAD_MALLOC, "allocation of %zd bytes failed", size); + return NULL; + } + + return p; +} + +void *_af_calloc (size_t nmemb, size_t size) +{ + void *p; + + if (nmemb <= 0 || size <= 0) + { + _af_error(AF_BAD_MALLOC, "bad memory allocation size request " + "%zd elements of %zd bytes each", nmemb, size); + return NULL; + } + + p = calloc(nmemb, size); + + if (p == NULL) + { + _af_error(AF_BAD_MALLOC, "allocation of %zd bytes failed", + nmemb*size); + return NULL; + } + + return p; +} + +AUpvlist _af_pv_long (long val) +{ + AUpvlist ret = AUpvnew(1); + AUpvsetparam(ret, 0, 0); + AUpvsetvaltype(ret, 0, AU_PVTYPE_LONG); + AUpvsetval(ret, 0, &val); + return ret; +} + +AUpvlist _af_pv_double (double val) +{ + AUpvlist ret = AUpvnew(1); + AUpvsetparam(ret, 0, 0); + AUpvsetvaltype(ret, 0, AU_PVTYPE_DOUBLE); + AUpvsetval(ret, 0, &val); + return ret; +} + +AUpvlist _af_pv_pointer (void *val) +{ + AUpvlist ret = AUpvnew(1); + AUpvsetparam(ret, 0, 0); + AUpvsetvaltype(ret, 0, AU_PVTYPE_PTR); + AUpvsetval(ret, 0, &val); + return ret; +} + +bool _af_pv_getlong (AUpvlist pvlist, int param, long *l) +{ + for (int i=0; i<AUpvgetmaxitems(pvlist); i++) + { + int p, t; + + AUpvgetparam(pvlist, i, &p); + + if (p != param) + continue; + + AUpvgetvaltype(pvlist, i, &t); + + /* Ensure that this parameter is of type AU_PVTYPE_LONG. */ + if (t != AU_PVTYPE_LONG) + return false; + + AUpvgetval(pvlist, i, l); + return true; + } + + return false; +} + +bool _af_pv_getdouble (AUpvlist pvlist, int param, double *d) +{ + for (int i=0; i<AUpvgetmaxitems(pvlist); i++) + { + int p, t; + + AUpvgetparam(pvlist, i, &p); + + if (p != param) + continue; + + AUpvgetvaltype(pvlist, i, &t); + + /* Ensure that this parameter is of type AU_PVTYPE_DOUBLE. */ + if (t != AU_PVTYPE_DOUBLE) + return false; + + AUpvgetval(pvlist, i, d); + return true; + } + + return false; +} + +bool _af_pv_getptr (AUpvlist pvlist, int param, void **v) +{ + for (int i=0; i<AUpvgetmaxitems(pvlist); i++) + { + int p, t; + + AUpvgetparam(pvlist, i, &p); + + if (p != param) + continue; + + AUpvgetvaltype(pvlist, i, &t); + + /* Ensure that this parameter is of type AU_PVTYPE_PTR. */ + if (t != AU_PVTYPE_PTR) + return false; + + AUpvgetval(pvlist, i, v); + return true; + } + + return false; +} + +int _af_format_sample_size_uncompressed (const AudioFormat *format, bool stretch3to4) +{ + int size = 0; + + switch (format->sampleFormat) + { + case AF_SAMPFMT_FLOAT: + size = sizeof (float); + break; + case AF_SAMPFMT_DOUBLE: + size = sizeof (double); + break; + default: + size = (int) (format->sampleWidth + 7) / 8; + if (format->compressionType == AF_COMPRESSION_NONE && + size == 3 && stretch3to4) + size = 4; + break; + } + + return size; +} + +float _af_format_sample_size (const AudioFormat *fmt, bool stretch3to4) +{ + const CompressionUnit *unit = _af_compression_unit_from_id(fmt->compressionType); + float squishFactor = unit->squishFactor; + + return _af_format_sample_size_uncompressed(fmt, stretch3to4) / + squishFactor; +} + +int _af_format_frame_size_uncompressed (const AudioFormat *fmt, bool stretch3to4) +{ + return _af_format_sample_size_uncompressed(fmt, stretch3to4) * + fmt->channelCount; +} + +float _af_format_frame_size (const AudioFormat *fmt, bool stretch3to4) +{ + const CompressionUnit *unit = _af_compression_unit_from_id(fmt->compressionType); + float squishFactor = unit->squishFactor; + + return _af_format_frame_size_uncompressed(fmt, stretch3to4) / + squishFactor; +} + +/* + Set the sampleFormat and sampleWidth fields in f, and set the + PCM info to the appropriate default values for the given sample + format and sample width. +*/ +status _af_set_sample_format (AudioFormat *f, int sampleFormat, int sampleWidth) +{ + switch (sampleFormat) + { + case AF_SAMPFMT_UNSIGNED: + case AF_SAMPFMT_TWOSCOMP: + if (sampleWidth < 1 || sampleWidth > 32) + { + _af_error(AF_BAD_SAMPFMT, + "illegal sample width %d for integer data", + sampleWidth); + return AF_FAIL; + } + else + { + int bytes; + + f->sampleFormat = sampleFormat; + f->sampleWidth = sampleWidth; + + bytes = _af_format_sample_size_uncompressed(f, false); + + if (sampleFormat == AF_SAMPFMT_TWOSCOMP) + f->pcm = _af_default_signed_integer_pcm_mappings[bytes]; + else + f->pcm = _af_default_unsigned_integer_pcm_mappings[bytes]; + } + break; + + case AF_SAMPFMT_FLOAT: + f->sampleFormat = sampleFormat; + f->sampleWidth = 32; + f->pcm = _af_default_float_pcm_mapping; + break; + case AF_SAMPFMT_DOUBLE: + f->sampleFormat = sampleFormat; + f->sampleWidth = 64; /*for convenience */ + f->pcm = _af_default_double_pcm_mapping; + break; + default: + _af_error(AF_BAD_SAMPFMT, "unknown sample format %d", + sampleFormat); + return AF_FAIL; + } + + return AF_SUCCEED; +} + +/* + Verify the uniqueness of the nids ids given. + + idname is the name of what the ids identify, as in "loop" + iderr is an error as in AF_BAD_LOOPID +*/ +bool _af_unique_ids (const int *ids, int nids, const char *idname, int iderr) +{ + for (int i = 0; i < nids; i++) + { + for (int j = 0; j < i; j++) + { + if (ids[i] == ids[j]) + { + _af_error(iderr, "nonunique %s id %d", idname, ids[i]); + return false; + } + } + } + + return true; +} diff --git a/tools/audiofile/audiofile.h b/tools/audiofile/audiofile.h @@ -0,0 +1,612 @@ +/* + Audio File Library + Copyright (C) 1998-2000, 2010-2013 Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + audiofile.h + + This file contains the public interfaces to the Audio File Library. +*/ + +#ifndef AUDIOFILE_H +#define AUDIOFILE_H + +#include <aupvlist.h> +#include <stdint.h> +#include <sys/types.h> + +#define LIBAUDIOFILE_MAJOR_VERSION 0 +#define LIBAUDIOFILE_MINOR_VERSION 3 +#define LIBAUDIOFILE_MICRO_VERSION 6 + +#ifdef __cplusplus +extern "C" { +#endif + +#if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__clang__) +#define AFAPI __attribute__((visibility("default"))) +#else +#define AFAPI +#endif + +typedef struct _AFvirtualfile AFvirtualfile; + +typedef struct _AFfilesetup *AFfilesetup; +typedef struct _AFfilehandle *AFfilehandle; +typedef void (*AFerrfunc)(long, const char *); + +// Define AFframecount and AFfileoffset as 64-bit signed integers. +#if defined(__FreeBSD__) || \ + defined(__DragonFly__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) || \ + defined(__APPLE__) || \ + defined(__sgi) || \ + (defined(__linux__) && defined(__LP64__)) +// BSD and IRIX systems define off_t as a 64-bit signed integer. +// Linux defines off_t as a 64-bit signed integer in LP64 mode. +typedef off_t AFframecount; +typedef off_t AFfileoffset; +#else +// For all other systems, use int64_t. +typedef int64_t AFframecount; +typedef int64_t AFfileoffset; +#endif + +#define AF_NULL_FILESETUP ((struct _AFfilesetup *) 0) +#define AF_NULL_FILEHANDLE ((struct _AFfilehandle *) 0) + +#define AF_ERR_BASE 3000 + +enum +{ + AF_DEFAULT_TRACK = 1001 +}; + +enum +{ + AF_DEFAULT_INST = 2001 +}; + +enum +{ + AF_NUM_UNLIMITED = 99999 +}; + +enum +{ + AF_BYTEORDER_BIGENDIAN = 501, + AF_BYTEORDER_LITTLEENDIAN = 502 +}; + +enum +{ + AF_FILE_UNKNOWN = -1, + AF_FILE_RAWDATA = 0, + AF_FILE_AIFFC = 1, + AF_FILE_AIFF = 2, + AF_FILE_NEXTSND = 3, + AF_FILE_WAVE = 4, + AF_FILE_BICSF = 5, + AF_FILE_IRCAM = AF_FILE_BICSF, + AF_FILE_MPEG1BITSTREAM = 6, /* not implemented */ + AF_FILE_SOUNDDESIGNER1 = 7, /* not implemented */ + AF_FILE_SOUNDDESIGNER2 = 8, /* not implemented */ + AF_FILE_AVR = 9, + AF_FILE_IFF_8SVX = 10, + AF_FILE_SAMPLEVISION = 11, + AF_FILE_VOC = 12, + AF_FILE_NIST_SPHERE = 13, + AF_FILE_SOUNDFONT2 = 14, /* not implemented */ + AF_FILE_CAF = 15, + AF_FILE_FLAC = 16 +}; + +enum +{ + AF_LOOP_MODE_NOLOOP = 0, + AF_LOOP_MODE_FORW = 1, + AF_LOOP_MODE_FORWBAKW = 2 +}; + +enum +{ + AF_SAMPFMT_TWOSCOMP = 401, /* linear two's complement */ + AF_SAMPFMT_UNSIGNED = 402, /* unsigned integer */ + AF_SAMPFMT_FLOAT = 403, /* 32-bit IEEE floating-point */ + AF_SAMPFMT_DOUBLE = 404 /* 64-bit IEEE double-precision floating-point */ +}; + +enum +{ + AF_INST_LOOP_OFF = 0, /* no looping */ + AF_INST_LOOP_CONTINUOUS = 1, /* loop continuously through decay */ + AF_INST_LOOP_SUSTAIN = 3 /* loop during sustain, then continue */ +}; + +enum +{ + AF_INST_MIDI_BASENOTE = 301, + AF_INST_NUMCENTS_DETUNE = 302, + AF_INST_MIDI_LONOTE = 303, + AF_INST_MIDI_HINOTE = 304, + AF_INST_MIDI_LOVELOCITY = 305, + AF_INST_MIDI_HIVELOCITY = 306, + AF_INST_NUMDBS_GAIN = 307, + AF_INST_SUSLOOPID = 308, /* loop id for AIFF sustain loop */ + AF_INST_RELLOOPID = 309, /* loop id for AIFF release loop */ + AF_INST_SAMP_STARTFRAME = 310, /* start sample for this inst */ + AF_INST_SAMP_ENDFRAME = 311, /* end sample for this inst */ + AF_INST_SAMP_MODE = 312, /* looping mode for this inst */ + AF_INST_TRACKID = 313, + AF_INST_NAME = 314, /* name of this inst */ + AF_INST_SAMP_RATE = 315, /* sample rate of this inst's sample */ + AF_INST_PRESETID = 316, /* ID of preset containing this inst */ + AF_INST_PRESET_NAME = 317 /* name of preset containing this inst */ +}; + +enum +{ + AF_MISC_UNRECOGNIZED = 0, /* unrecognized data chunk */ + AF_MISC_COPY = 201, /* copyright string */ + AF_MISC_AUTH = 202, /* author string */ + AF_MISC_NAME = 203, /* name string */ + AF_MISC_ANNO = 204, /* annotation string */ + AF_MISC_APPL = 205, /* application-specific data */ + AF_MISC_MIDI = 206, /* MIDI exclusive data */ + AF_MISC_PCMMAP = 207, /* PCM mapping information (future use) */ + AF_MISC_NeXT = 208, /* misc binary data appended to NeXT header */ + AF_MISC_IRCAM_PEAKAMP = 209, /* peak amplitude information */ + AF_MISC_IRCAM_COMMENT = 210, /* BICSF text comment */ + AF_MISC_COMMENT = 210, /* general text comment */ + + AF_MISC_ICMT = AF_MISC_COMMENT, /* comments chunk (WAVE format) */ + AF_MISC_ICRD = 211, /* creation date (WAVE format) */ + AF_MISC_ISFT = 212 /* software name (WAVE format) */ +}; + +enum +{ + /* supported compression schemes */ + AF_COMPRESSION_UNKNOWN = -1, + AF_COMPRESSION_NONE = 0, + AF_COMPRESSION_G722 = 501, + AF_COMPRESSION_G711_ULAW = 502, + AF_COMPRESSION_G711_ALAW = 503, + + /* Apple proprietary AIFF-C compression schemes (not supported) */ + AF_COMPRESSION_APPLE_ACE2 = 504, + AF_COMPRESSION_APPLE_ACE8 = 505, + AF_COMPRESSION_APPLE_MAC3 = 506, + AF_COMPRESSION_APPLE_MAC6 = 507, + + AF_COMPRESSION_G726 = 517, + AF_COMPRESSION_G728 = 518, + AF_COMPRESSION_DVI_AUDIO = 519, + AF_COMPRESSION_IMA = AF_COMPRESSION_DVI_AUDIO, + AF_COMPRESSION_GSM = 520, + AF_COMPRESSION_FS1016 = 521, + AF_COMPRESSION_DV = 522, + AF_COMPRESSION_MS_ADPCM = 523, + + AF_COMPRESSION_FLAC = 530, + AF_COMPRESSION_ALAC = 540 +}; + +/* tokens for afQuery() -- see the man page for instructions */ +/* level 1 selectors */ +enum +{ + AF_QUERYTYPE_INSTPARAM = 500, + AF_QUERYTYPE_FILEFMT = 501, + AF_QUERYTYPE_COMPRESSION = 502, + AF_QUERYTYPE_COMPRESSIONPARAM = 503, + AF_QUERYTYPE_MISC = 504, + AF_QUERYTYPE_INST = 505, + AF_QUERYTYPE_MARK = 506, + AF_QUERYTYPE_LOOP = 507 +}; + +/* level 2 selectors */ +enum +{ + AF_QUERY_NAME = 600, /* get name (1-3 words) */ + AF_QUERY_DESC = 601, /* get description */ + AF_QUERY_LABEL = 602, /* get 4- or 5-char label */ + AF_QUERY_TYPE = 603, /* get type token */ + AF_QUERY_DEFAULT = 604, /* dflt. value for param */ + AF_QUERY_ID_COUNT = 605, /* get number of ids avail. */ + AF_QUERY_IDS = 606, /* get array of id tokens */ + AF_QUERY_IMPLEMENTED = 613, /* boolean */ + AF_QUERY_TYPE_COUNT = 607, /* get number of types av. */ + AF_QUERY_TYPES = 608, /* get array of types */ + AF_QUERY_NATIVE_SAMPFMT = 609, /* for compression */ + AF_QUERY_NATIVE_SAMPWIDTH = 610, + AF_QUERY_SQUISHFAC = 611, /* 1.0 means variable */ + AF_QUERY_MAX_NUMBER = 612, /* max allowed in file */ + AF_QUERY_SUPPORTED = 613 /* insts, loops, etc., supported? */ +}; + +/* level 2 selectors which have sub-selectors */ +enum +{ + AF_QUERY_TRACKS = 620, + AF_QUERY_CHANNELS = 621, + AF_QUERY_SAMPLE_SIZES = 622, + AF_QUERY_SAMPLE_FORMATS = 623, + AF_QUERY_COMPRESSION_TYPES = 624 +}; + +/* level 3 sub-selectors */ +enum +{ + AF_QUERY_VALUE_COUNT = 650, /* number of values of the above */ + AF_QUERY_VALUES = 651 /* array of those values */ +}; + + +/* + Old Audio File Library error codes. These are still returned by the + AFerrorhandler calls, but are not used by the new digital media library + error reporting routines. See the bottom of this file for the new error + tokens. +*/ + +enum +{ + AF_BAD_NOT_IMPLEMENTED = 0, /* not implemented yet */ + AF_BAD_FILEHANDLE = 1, /* tried to use invalid filehandle */ + AF_BAD_OPEN = 3, /* unix open failed */ + AF_BAD_CLOSE = 4, /* unix close failed */ + AF_BAD_READ = 5, /* unix read failed */ + AF_BAD_WRITE = 6, /* unix write failed */ + AF_BAD_LSEEK = 7, /* unix lseek failed */ + AF_BAD_NO_FILEHANDLE = 8, /* failed to allocate a filehandle struct */ + AF_BAD_ACCMODE = 10, /* unrecognized audio file access mode */ + AF_BAD_NOWRITEACC = 11, /* file not open for writing */ + AF_BAD_NOREADACC = 12, /* file not open for reading */ + AF_BAD_FILEFMT = 13, /* unrecognized audio file format */ + AF_BAD_RATE = 14, /* invalid sample rate */ + AF_BAD_CHANNELS = 15, /* invalid number of channels*/ + AF_BAD_SAMPCNT = 16, /* invalid sample count */ + AF_BAD_WIDTH = 17, /* invalid sample width */ + AF_BAD_SEEKMODE = 18, /* invalid seek mode */ + AF_BAD_NO_LOOPDATA = 19, /* failed to allocate loop struct */ + AF_BAD_MALLOC = 20, /* malloc failed somewhere */ + AF_BAD_LOOPID = 21, + AF_BAD_SAMPFMT = 22, /* bad sample format */ + AF_BAD_FILESETUP = 23, /* bad file setup structure*/ + AF_BAD_TRACKID = 24, /* no track corresponding to id */ + AF_BAD_NUMTRACKS = 25, /* wrong number of tracks for file format */ + AF_BAD_NO_FILESETUP = 26, /* failed to allocate a filesetup struct*/ + AF_BAD_LOOPMODE = 27, /* unrecognized loop mode value */ + AF_BAD_INSTID = 28, /* invalid instrument id */ + AF_BAD_NUMLOOPS = 29, /* bad number of loops */ + AF_BAD_NUMMARKS = 30, /* bad number of markers */ + AF_BAD_MARKID = 31, /* bad marker id */ + AF_BAD_MARKPOS = 32, /* invalid marker position value */ + AF_BAD_NUMINSTS = 33, /* invalid number of instruments */ + AF_BAD_NOAESDATA = 34, + AF_BAD_MISCID = 35, + AF_BAD_NUMMISC = 36, + AF_BAD_MISCSIZE = 37, + AF_BAD_MISCTYPE = 38, + AF_BAD_MISCSEEK = 39, + AF_BAD_STRLEN = 40, /* invalid string length */ + AF_BAD_RATECONV = 45, + AF_BAD_SYNCFILE = 46, + AF_BAD_CODEC_CONFIG = 47, /* improperly configured codec */ + AF_BAD_CODEC_STATE = 48, /* invalid codec state: can't recover */ + AF_BAD_CODEC_LICENSE = 49, /* no license available for codec */ + AF_BAD_CODEC_TYPE = 50, /* unsupported codec type */ + AF_BAD_COMPRESSION = AF_BAD_CODEC_CONFIG, /* for back compat */ + AF_BAD_COMPTYPE = AF_BAD_CODEC_TYPE, /* for back compat */ + + AF_BAD_INSTPTYPE = 51, /* invalid instrument parameter type */ + AF_BAD_INSTPID = 52, /* invalid instrument parameter id */ + AF_BAD_BYTEORDER = 53, + AF_BAD_FILEFMT_PARAM = 54, /* unrecognized file format parameter */ + AF_BAD_COMP_PARAM = 55, /* unrecognized compression parameter */ + AF_BAD_DATAOFFSET = 56, /* bad data offset */ + AF_BAD_FRAMECNT = 57, /* bad frame count */ + AF_BAD_QUERYTYPE = 58, /* bad query type */ + AF_BAD_QUERY = 59, /* bad argument to afQuery() */ + AF_WARNING_CODEC_RATE = 60, /* using 8k instead of codec rate 8012 */ + AF_WARNING_RATECVT = 61, /* warning about rate conversion used */ + + AF_BAD_HEADER = 62, /* failed to parse header */ + AF_BAD_FRAME = 63, /* bad frame number */ + AF_BAD_LOOPCOUNT = 64, /* bad loop count */ + AF_BAD_DMEDIA_CALL = 65, /* error in dmedia subsystem call */ + + /* AIFF/AIFF-C specific errors when parsing file header */ + AF_BAD_AIFF_HEADER = 108, /* failed to parse chunk header */ + AF_BAD_AIFF_FORM = 109, /* failed to parse FORM chunk */ + AF_BAD_AIFF_SSND = 110, /* failed to parse SSND chunk */ + AF_BAD_AIFF_CHUNKID = 111, /* unrecognized AIFF/AIFF-C chunk id */ + AF_BAD_AIFF_COMM = 112, /* failed to parse COMM chunk */ + AF_BAD_AIFF_INST = 113, /* failed to parse INST chunk */ + AF_BAD_AIFF_MARK = 114, /* failed to parse MARK chunk */ + AF_BAD_AIFF_SKIP = 115, /* failed to skip unsupported chunk */ + AF_BAD_AIFF_LOOPMODE = 116 /* unrecognized loop mode (forw, etc)*/ +}; + +/* new error codes which may be retrieved via dmGetError() */ +/* The old error tokens continue to be retrievable via the AFerrorhandler */ +/* AF_ERR_BASE is #defined in dmedia/dmedia.h */ + +enum +{ + AF_ERR_NOT_IMPLEMENTED = 0+AF_ERR_BASE, /* not implemented yet */ + AF_ERR_BAD_FILEHANDLE = 1+AF_ERR_BASE, /* invalid filehandle */ + AF_ERR_BAD_READ = 5+AF_ERR_BASE, /* unix read failed */ + AF_ERR_BAD_WRITE = 6+AF_ERR_BASE, /* unix write failed */ + AF_ERR_BAD_LSEEK = 7+AF_ERR_BASE, /* unix lseek failed */ + AF_ERR_BAD_ACCMODE = 10+AF_ERR_BASE, /* unrecognized audio file access mode */ + AF_ERR_NO_WRITEACC = 11+AF_ERR_BASE, /* file not open for writing */ + AF_ERR_NO_READACC = 12+AF_ERR_BASE, /* file not open for reading */ + AF_ERR_BAD_FILEFMT = 13+AF_ERR_BASE, /* unrecognized audio file format */ + AF_ERR_BAD_RATE = 14+AF_ERR_BASE, /* invalid sample rate */ + AF_ERR_BAD_CHANNELS = 15+AF_ERR_BASE, /* invalid # channels*/ + AF_ERR_BAD_SAMPCNT = 16+AF_ERR_BASE, /* invalid sample count */ + AF_ERR_BAD_WIDTH = 17+AF_ERR_BASE, /* invalid sample width */ + AF_ERR_BAD_SEEKMODE = 18+AF_ERR_BASE, /* invalid seek mode */ + AF_ERR_BAD_LOOPID = 21+AF_ERR_BASE, /* invalid loop id */ + AF_ERR_BAD_SAMPFMT = 22+AF_ERR_BASE, /* bad sample format */ + AF_ERR_BAD_FILESETUP = 23+AF_ERR_BASE, /* bad file setup structure*/ + AF_ERR_BAD_TRACKID = 24+AF_ERR_BASE, /* no track corresponding to id */ + AF_ERR_BAD_NUMTRACKS = 25+AF_ERR_BASE, /* wrong number of tracks for file format */ + AF_ERR_BAD_LOOPMODE = 27+AF_ERR_BASE, /* unrecognized loop mode symbol */ + AF_ERR_BAD_INSTID = 28+AF_ERR_BASE, /* invalid instrument id */ + AF_ERR_BAD_NUMLOOPS = 29+AF_ERR_BASE, /* bad number of loops */ + AF_ERR_BAD_NUMMARKS = 30+AF_ERR_BASE, /* bad number of markers */ + AF_ERR_BAD_MARKID = 31+AF_ERR_BASE, /* bad marker id */ + AF_ERR_BAD_MARKPOS = 32+AF_ERR_BASE, /* invalid marker position value */ + AF_ERR_BAD_NUMINSTS = 33+AF_ERR_BASE, /* invalid number of instruments */ + AF_ERR_BAD_NOAESDATA = 34+AF_ERR_BASE, + AF_ERR_BAD_MISCID = 35+AF_ERR_BASE, + AF_ERR_BAD_NUMMISC = 36+AF_ERR_BASE, + AF_ERR_BAD_MISCSIZE = 37+AF_ERR_BASE, + AF_ERR_BAD_MISCTYPE = 38+AF_ERR_BASE, + AF_ERR_BAD_MISCSEEK = 39+AF_ERR_BASE, + AF_ERR_BAD_STRLEN = 40+AF_ERR_BASE, /* invalid string length */ + AF_ERR_BAD_RATECONV = 45+AF_ERR_BASE, + AF_ERR_BAD_SYNCFILE = 46+AF_ERR_BASE, + AF_ERR_BAD_CODEC_CONFIG = 47+AF_ERR_BASE, /* improperly configured codec */ + AF_ERR_BAD_CODEC_TYPE = 50+AF_ERR_BASE, /* unsupported codec type */ + AF_ERR_BAD_INSTPTYPE = 51+AF_ERR_BASE, /* invalid instrument parameter type */ + AF_ERR_BAD_INSTPID = 52+AF_ERR_BASE, /* invalid instrument parameter id */ + + AF_ERR_BAD_BYTEORDER = 53+AF_ERR_BASE, + AF_ERR_BAD_FILEFMT_PARAM = 54+AF_ERR_BASE, /* unrecognized file format parameter */ + AF_ERR_BAD_COMP_PARAM = 55+AF_ERR_BASE, /* unrecognized compression parameter */ + AF_ERR_BAD_DATAOFFSET = 56+AF_ERR_BASE, /* bad data offset */ + AF_ERR_BAD_FRAMECNT = 57+AF_ERR_BASE, /* bad frame count */ + + AF_ERR_BAD_QUERYTYPE = 58+AF_ERR_BASE, /* bad query type */ + AF_ERR_BAD_QUERY = 59+AF_ERR_BASE, /* bad argument to afQuery() */ + AF_ERR_BAD_HEADER = 62+AF_ERR_BASE, /* failed to parse header */ + AF_ERR_BAD_FRAME = 63+AF_ERR_BASE, /* bad frame number */ + AF_ERR_BAD_LOOPCOUNT = 64+AF_ERR_BASE, /* bad loop count */ + + /* AIFF/AIFF-C specific errors when parsing file header */ + + AF_ERR_BAD_AIFF_HEADER = 66+AF_ERR_BASE, /* failed to parse chunk header */ + AF_ERR_BAD_AIFF_FORM = 67+AF_ERR_BASE, /* failed to parse FORM chunk */ + AF_ERR_BAD_AIFF_SSND = 68+AF_ERR_BASE, /* failed to parse SSND chunk */ + AF_ERR_BAD_AIFF_CHUNKID = 69+AF_ERR_BASE, /* unrecognized AIFF/AIFF-C chunk id */ + AF_ERR_BAD_AIFF_COMM = 70+AF_ERR_BASE, /* failed to parse COMM chunk */ + AF_ERR_BAD_AIFF_INST = 71+AF_ERR_BASE, /* failed to parse INST chunk */ + AF_ERR_BAD_AIFF_MARK = 72+AF_ERR_BASE, /* failed to parse MARK chunk */ + AF_ERR_BAD_AIFF_SKIP = 73+AF_ERR_BASE, /* failed to skip unsupported chunk */ + AF_ERR_BAD_AIFF_LOOPMODE = 74+AF_ERR_BASE /* unrecognized loop mode (forw, etc) */ +}; + + +/* global routines */ +AFAPI AFerrfunc afSetErrorHandler (AFerrfunc efunc); + +/* query routines */ +AFAPI AUpvlist afQuery (int querytype, int arg1, int arg2, int arg3, int arg4); +AFAPI long afQueryLong (int querytype, int arg1, int arg2, int arg3, int arg4); +AFAPI double afQueryDouble (int querytype, int arg1, int arg2, int arg3, int arg4); +AFAPI void *afQueryPointer (int querytype, int arg1, int arg2, int arg3, int arg4); + +/* basic operations on file handles and file setups */ +AFAPI AFfilesetup afNewFileSetup (void); +AFAPI void afFreeFileSetup (AFfilesetup); +AFAPI int afIdentifyFD (int); +AFAPI int afIdentifyNamedFD (int, const char *filename, int *implemented); + +AFAPI AFfilehandle afOpenFile (const char *filename, const char *mode, + AFfilesetup setup); +AFAPI AFfilehandle afOpenVirtualFile (AFvirtualfile *vfile, const char *mode, + AFfilesetup setup); +AFAPI AFfilehandle afOpenFD (int fd, const char *mode, AFfilesetup setup); +AFAPI AFfilehandle afOpenNamedFD (int fd, const char *mode, AFfilesetup setup, + const char *filename); + +AFAPI void afSaveFilePosition (AFfilehandle file); +AFAPI void afRestoreFilePosition (AFfilehandle file); +AFAPI int afSyncFile (AFfilehandle file); +AFAPI int afCloseFile (AFfilehandle file); + +AFAPI void afInitFileFormat (AFfilesetup, int format); +AFAPI int afGetFileFormat (AFfilehandle, int *version); + +/* track */ +AFAPI void afInitTrackIDs (AFfilesetup, const int *trackids, int trackCount); +AFAPI int afGetTrackIDs (AFfilehandle, int *trackids); + +/* track data: reading, writng, seeking, sizing frames */ +AFAPI int afReadFrames (AFfilehandle, int track, void *buffer, int frameCount); +AFAPI int afWriteFrames (AFfilehandle, int track, const void *buffer, int frameCount); +AFAPI AFframecount afSeekFrame (AFfilehandle, int track, AFframecount frameoffset); +AFAPI AFframecount afTellFrame (AFfilehandle, int track); +AFAPI AFfileoffset afGetTrackBytes (AFfilehandle, int track); +AFAPI float afGetFrameSize (AFfilehandle, int track, int expand3to4); +AFAPI float afGetVirtualFrameSize (AFfilehandle, int track, int expand3to4); + +/* track data: AES data */ +/* afInitAESChannelData is obsolete -- use afInitAESChannelDataTo() */ +AFAPI void afInitAESChannelData (AFfilesetup, int track); /* obsolete */ +AFAPI void afInitAESChannelDataTo (AFfilesetup, int track, int willBeData); +AFAPI int afGetAESChannelData (AFfilehandle, int track, unsigned char buf[24]); +AFAPI void afSetAESChannelData (AFfilehandle, int track, unsigned char buf[24]); + +/* track data: byte order */ +AFAPI void afInitByteOrder (AFfilesetup, int track, int byteOrder); +AFAPI int afGetByteOrder (AFfilehandle, int track); +AFAPI int afSetVirtualByteOrder (AFfilehandle, int track, int byteOrder); +AFAPI int afGetVirtualByteOrder (AFfilehandle, int track); + +/* track data: number of channels */ +AFAPI void afInitChannels (AFfilesetup, int track, int nchannels); +AFAPI int afGetChannels (AFfilehandle, int track); +AFAPI int afSetVirtualChannels (AFfilehandle, int track, int channelCount); +AFAPI int afGetVirtualChannels (AFfilehandle, int track); +AFAPI void afSetChannelMatrix (AFfilehandle, int track, double *matrix); + +/* track data: sample format and sample width */ +AFAPI void afInitSampleFormat (AFfilesetup, int track, int sampleFormat, + int sampleWidth); +AFAPI void afGetSampleFormat (AFfilehandle file, int track, int *sampleFormat, + int *sampleWidth); +AFAPI int afSetVirtualSampleFormat (AFfilehandle, int track, + int sampleFormat, int sampleWidth); +AFAPI void afGetVirtualSampleFormat (AFfilehandle, int track, + int *sampleFormat, int *sampleWidth); + +/* track data: sampling rate */ +AFAPI void afInitRate (AFfilesetup, int track, double rate); +AFAPI double afGetRate (AFfilehandle, int track); + +#if 0 +int afSetVirtualRate (AFfilehandle, int track, double rate); +double afGetVirtualRate (AFfilehandle, int track); +#endif + +/* track data: compression */ +AFAPI void afInitCompression (AFfilesetup, int track, int compression); +#if 0 +void afInitCompressionParams (AFfilesetup, int track, int compression + AUpvlist params, int parameterCount); +#endif + +AFAPI int afGetCompression (AFfilehandle, int track); +#if 0 +void afGetCompressionParams (AFfilehandle, int track, int *compression, + AUpvlist params, int parameterCount); + +int afSetVirtualCompression (AFfilesetup, int track, int compression); +void afSetVirtualCompressionParams (AFfilehandle, int track, int compression, + AUpvlist params, int parameterCount); + +int afGetVirtualCompression (AFfilesetup, int track, int compression); +void afGetVirtualCompressionParams (AFfilehandle, int track, int *compression, + AUpvlist params, int parameterCount); +#endif + +/* track data: pcm mapping */ +AFAPI void afInitPCMMapping (AFfilesetup filesetup, int track, + double slope, double intercept, double minClip, double maxClip); +AFAPI void afGetPCMMapping (AFfilehandle file, int track, + double *slope, double *intercept, double *minClip, double *maxClip); +/* NOTE: afSetTrackPCMMapping() is special--it does not set the virtual */ +/* format; it changes what the AF thinks the track format is! Be careful. */ +AFAPI int afSetTrackPCMMapping (AFfilehandle file, int track, + double slope, double intercept, double minClip, double maxClip); +/* NOTE: afSetVirtualPCMMapping() is different from afSetTrackPCMMapping(): */ +/* see comment for afSetTrackPCMMapping(). */ +AFAPI int afSetVirtualPCMMapping (AFfilehandle file, int track, + double slope, double intercept, double minClip, double maxClip); +AFAPI void afGetVirtualPCMMapping (AFfilehandle file, int track, + double *slope, double *intercept, double *minClip, double *maxClip); + +/* track data: data offset within the file */ +/* initialize for raw reading only */ +AFAPI void afInitDataOffset(AFfilesetup, int track, AFfileoffset offset); +AFAPI AFfileoffset afGetDataOffset (AFfilehandle, int track); + +/* track data: count of frames in file */ +AFAPI void afInitFrameCount (AFfilesetup, int track, AFframecount frameCount); +AFAPI AFframecount afGetFrameCount (AFfilehandle file, int track); + +/* loop operations */ +AFAPI void afInitLoopIDs (AFfilesetup, int instid, const int *ids, int nids); +AFAPI int afGetLoopIDs (AFfilehandle, int instid, int loopids[]); +AFAPI void afSetLoopMode (AFfilehandle, int instid, int loop, int mode); +AFAPI int afGetLoopMode (AFfilehandle, int instid, int loopid); +AFAPI int afSetLoopCount (AFfilehandle, int instid, int loop, int count); +AFAPI int afGetLoopCount (AFfilehandle, int instid, int loopid); +AFAPI void afSetLoopStart (AFfilehandle, int instid, int loopid, int markerid); +AFAPI int afGetLoopStart (AFfilehandle, int instid, int loopid); +AFAPI void afSetLoopEnd (AFfilehandle, int instid, int loopid, int markerid); +AFAPI int afGetLoopEnd (AFfilehandle, int instid, int loopid); + +AFAPI int afSetLoopStartFrame (AFfilehandle, int instid, int loop, + AFframecount startFrame); +AFAPI AFframecount afGetLoopStartFrame (AFfilehandle, int instid, int loop); +AFAPI int afSetLoopEndFrame (AFfilehandle, int instid, int loop, + AFframecount startFrame); +AFAPI AFframecount afGetLoopEndFrame (AFfilehandle, int instid, int loop); + +AFAPI void afSetLoopTrack (AFfilehandle, int instid, int loopid, int trackid); +AFAPI int afGetLoopTrack (AFfilehandle, int instid, int loopid); + +/* marker operations */ +AFAPI void afInitMarkIDs (AFfilesetup, int trackid, const int *ids, int nids); +AFAPI int afGetMarkIDs (AFfilehandle file, int trackid, int markids[]); +AFAPI void afSetMarkPosition (AFfilehandle file, int trackid, int markid, + AFframecount markpos); +AFAPI AFframecount afGetMarkPosition (AFfilehandle file, int trackid, int markid); +AFAPI void afInitMarkName (AFfilesetup, int trackid, int marker, const char *name); +AFAPI void afInitMarkComment (AFfilesetup, int trackid, int marker, + const char *comment); +AFAPI char *afGetMarkName (AFfilehandle file, int trackid, int markid); +AFAPI char *afGetMarkComment (AFfilehandle file, int trackid, int markid); + +/* instrument operations */ +AFAPI void afInitInstIDs (AFfilesetup, const int *ids, int nids); +AFAPI int afGetInstIDs (AFfilehandle file, int *instids); +AFAPI void afGetInstParams (AFfilehandle file, int instid, AUpvlist pvlist, + int nparams); +AFAPI void afSetInstParams (AFfilehandle file, int instid, AUpvlist pvlist, + int nparams); +AFAPI long afGetInstParamLong (AFfilehandle file, int instid, int param); +AFAPI void afSetInstParamLong (AFfilehandle file, int instid, int param, long value); + +/* miscellaneous data operations */ +AFAPI void afInitMiscIDs (AFfilesetup, const int *ids, int nids); +AFAPI int afGetMiscIDs (AFfilehandle, int *ids); +AFAPI void afInitMiscType (AFfilesetup, int miscellaneousid, int type); +AFAPI int afGetMiscType (AFfilehandle, int miscellaneousid); +AFAPI void afInitMiscSize (AFfilesetup, int miscellaneousid, int size); +AFAPI int afGetMiscSize (AFfilehandle, int miscellaneousid); +AFAPI int afWriteMisc (AFfilehandle, int miscellaneousid, const void *buf, int bytes); +AFAPI int afReadMisc (AFfilehandle, int miscellaneousid, void *buf, int bytes); +AFAPI int afSeekMisc (AFfilehandle, int miscellaneousid, int offset); + +#undef AFAPI + +#ifdef __cplusplus +} +#endif + +#endif /* AUDIOFILE_H */ diff --git a/tools/audiofile/aupvlist.h b/tools/audiofile/aupvlist.h @@ -0,0 +1,68 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +/* + aupvlist.h + + This file contains the interface to the parameter value list data + structures and routines. +*/ + +#ifndef AUPVLIST_H +#define AUPVLIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__clang__) +#define AFAPI __attribute__((visibility("default"))) +#else +#define AFAPI +#endif + +enum +{ + AU_PVTYPE_LONG = 1, + AU_PVTYPE_DOUBLE = 2, + AU_PVTYPE_PTR = 3 +}; + +typedef struct _AUpvlist *AUpvlist; + +#define AU_NULL_PVLIST ((struct _AUpvlist *) 0) + +AFAPI AUpvlist AUpvnew (int maxItems); +AFAPI int AUpvgetmaxitems (AUpvlist); +AFAPI int AUpvfree (AUpvlist); +AFAPI int AUpvsetparam (AUpvlist, int item, int param); +AFAPI int AUpvsetvaltype (AUpvlist, int item, int type); +AFAPI int AUpvsetval (AUpvlist, int item, void *val); +AFAPI int AUpvgetparam (AUpvlist, int item, int *param); +AFAPI int AUpvgetvaltype (AUpvlist, int item, int *type); +AFAPI int AUpvgetval (AUpvlist, int item, void *val); + +#undef AFAPI + +#ifdef __cplusplus +} +#endif + +#endif /* AUPVLIST_H */ diff --git a/tools/n64graphics.c b/tools/n64graphics.c @@ -802,9 +802,9 @@ int main(int argc, char *argv[]) if (config.mode == MODE_IMPORT) { if (config.bin_truncate) { - bin_fp = fopen(config.bin_filename, "w"); + bin_fp = fopen(config.bin_filename, "wb"); } else { - bin_fp = fopen(config.bin_filename, "r+"); + bin_fp = fopen(config.bin_filename, "r+b"); } if (!bin_fp) { ERROR("Error opening \"%s\"\n", config.bin_filename); @@ -854,9 +854,9 @@ int main(int argc, char *argv[]) int pal_length; if (config.pal_truncate) { - pal_fp = fopen(config.pal_filename, "w"); + pal_fp = fopen(config.pal_filename, "wb"); } else { - pal_fp = fopen(config.pal_filename, "r+"); + pal_fp = fopen(config.pal_filename, "r+b"); } if (!pal_fp) { ERROR("Error opening \"%s\"\n", config.pal_filename); @@ -934,7 +934,7 @@ int main(int argc, char *argv[]) ERROR("Error: must set position width and height for export\n"); return EXIT_FAILURE; } - bin_fp = fopen(config.bin_filename, "r"); + bin_fp = fopen(config.bin_filename, "rb"); if (!bin_fp) { ERROR("Error opening \"%s\"\n", config.bin_filename); return -1; @@ -971,7 +971,7 @@ int main(int argc, char *argv[]) INFO("Extracting %s offset 0x%X, pal.offset 0x%0X, pal.format %s\n", format2str(&config.format), config.bin_offset, config.pal_offset, format2str(&config.pal_format)); - pal_fp = fopen(config.pal_filename, "r"); + pal_fp = fopen(config.pal_filename, "rb"); if (!pal_fp) { ERROR("Error opening \"%s\"\n", config.bin_filename); return EXIT_FAILURE; diff --git a/tools/util/audiofile_strip.patch b/tools/util/audiofile_strip.patch @@ -0,0 +1,318 @@ +diff --git a/libaudiofile/FileHandle.cpp b/libaudiofile/FileHandle.cpp +index 8562d4b..5d6342a 100644 +--- a/libaudiofile/FileHandle.cpp ++++ b/libaudiofile/FileHandle.cpp +@@ -74,26 +74,8 @@ _AFfilehandle *_AFfilehandle::create(int fileFormat) + case AF_FILE_AIFF: + case AF_FILE_AIFFC: + return new AIFFFile(); +- case AF_FILE_NEXTSND: +- return new NeXTFile(); + case AF_FILE_WAVE: + return new WAVEFile(); +- case AF_FILE_BICSF: +- return new IRCAMFile(); +- case AF_FILE_AVR: +- return new AVRFile(); +- case AF_FILE_IFF_8SVX: +- return new IFFFile(); +- case AF_FILE_SAMPLEVISION: +- return new SampleVisionFile(); +- case AF_FILE_VOC: +- return new VOCFile(); +- case AF_FILE_NIST_SPHERE: +- return new NISTFile(); +- case AF_FILE_CAF: +- return new CAFFile(); +- case AF_FILE_FLAC: +- return new FLACFile(); + default: + return NULL; + } +diff --git a/libaudiofile/aupv.c b/libaudiofile/aupv.c +index 64e798b..374838b 100644 +--- a/libaudiofile/aupv.c ++++ b/libaudiofile/aupv.c +@@ -47,7 +47,7 @@ AUpvlist AUpvnew (int maxitems) + if (aupvlist == NULL) + return AU_NULL_PVLIST; + +- aupvlist->items = calloc(maxitems, sizeof (struct _AUpvitem)); ++ aupvlist->items = (struct _AUpvitem *)calloc(maxitems, sizeof (struct _AUpvitem)); + + assert(aupvlist->items); + if (aupvlist->items == NULL) +diff --git a/libaudiofile/g711.c b/libaudiofile/g711.c +index 8fb2323..1b323ec 100644 +--- a/libaudiofile/g711.c ++++ b/libaudiofile/g711.c +@@ -74,8 +74,7 @@ static int search(int val, const short *table, int size) + * John Wiley & Sons, pps 98-111 and 472-476. + */ + unsigned char +-_af_linear2alaw(pcm_val) +- int pcm_val; /* 2's complement (16-bit range) */ ++_af_linear2alaw(int pcm_val) + { + int mask; + int seg; +@@ -110,8 +109,7 @@ _af_linear2alaw(pcm_val) + * + */ + int +-_af_alaw2linear(a_val) +- unsigned char a_val; ++_af_alaw2linear(unsigned char a_val) + { + int t; + int seg; +diff --git a/libaudiofile/units.cpp b/libaudiofile/units.cpp +index ffd0a63..51d2dc3 100644 +--- a/libaudiofile/units.cpp ++++ b/libaudiofile/units.cpp +@@ -32,24 +32,12 @@ + #include "units.h" + + #include "AIFF.h" +-#include "AVR.h" +-#include "CAF.h" +-#include "FLACFile.h" +-#include "IFF.h" +-#include "IRCAM.h" +-#include "NeXT.h" +-#include "NIST.h" + #include "Raw.h" +-#include "SampleVision.h" +-#include "VOC.h" + #include "WAVE.h" + + #include "compression.h" + +-#include "modules/ALAC.h" +-#include "modules/FLAC.h" + #include "modules/G711.h" +-#include "modules/IMA.h" + #include "modules/MSADPCM.h" + #include "modules/PCM.h" + +@@ -99,20 +87,6 @@ const Unit _af_units[_AF_NUM_UNITS] = + _AF_AIFF_NUM_INSTPARAMS, + _af_aiff_inst_params + }, +- { +- AF_FILE_NEXTSND, +- "NeXT .snd/Sun .au", "NeXT .snd/Sun .au Format", "next", +- true, +- NeXTFile::completeSetup, +- NeXTFile::recognize, +- AF_SAMPFMT_TWOSCOMP, 16, +- _AF_NEXT_NUM_COMPTYPES, +- _af_next_compression_types, +- 0, /* maximum marker count */ +- 0, /* maximum instrument count */ +- 0, /* maximum number of loops per instrument */ +- 0, NULL +- }, + { + AF_FILE_WAVE, + "MS RIFF WAVE", "Microsoft RIFF WAVE Format", "wave", +@@ -128,144 +102,6 @@ const Unit _af_units[_AF_NUM_UNITS] = + _AF_WAVE_NUM_INSTPARAMS, + _af_wave_inst_params + }, +- { +- AF_FILE_IRCAM, +- "BICSF", "Berkeley/IRCAM/CARL Sound Format", "bicsf", +- true, +- IRCAMFile::completeSetup, +- IRCAMFile::recognize, +- AF_SAMPFMT_TWOSCOMP, 16, +- _AF_IRCAM_NUM_COMPTYPES, +- _af_ircam_compression_types, +- 0, // maximum marker count +- 0, // maximum instrument count +- 0, // maximum number of loops per instrument +- 0, // number of instrument parameters +- NULL // instrument parameters +- }, +- { +- AF_FILE_MPEG1BITSTREAM, +- "MPEG", "MPEG Audio Bitstream", "mpeg", +- false +- }, +- { +- AF_FILE_SOUNDDESIGNER1, +- "Sound Designer 1", "Sound Designer 1 File Format", "sd1", +- false +- }, +- { +- AF_FILE_SOUNDDESIGNER2, +- "Sound Designer 2", "Sound Designer 2 File Format", "sd2", +- false +- }, +- { +- AF_FILE_AVR, +- "AVR", "Audio Visual Research File Format", "avr", +- true, +- AVRFile::completeSetup, +- AVRFile::recognize, +- AF_SAMPFMT_TWOSCOMP, 16, +- 0, /* number of compression types */ +- NULL, /* compression types */ +- 0, /* maximum marker count */ +- 0, /* maximum instrument count */ +- 0, /* maximum number of loops per instrument */ +- 0, /* number of instrument parameters */ +- }, +- { +- AF_FILE_IFF_8SVX, +- "IFF/8SVX", "Amiga IFF/8SVX Sound File Format", "iff", +- true, +- IFFFile::completeSetup, +- IFFFile::recognize, +- AF_SAMPFMT_TWOSCOMP, 8, +- 0, /* number of compression types */ +- NULL, /* compression types */ +- 0, /* maximum marker count */ +- 0, /* maximum instrument count */ +- 0, /* maximum number of loops per instrument */ +- 0, /* number of instrument parameters */ +- }, +- { +- AF_FILE_SAMPLEVISION, +- "Sample Vision", "Sample Vision File Format", "smp", +- true, +- SampleVisionFile::completeSetup, +- SampleVisionFile::recognize, +- AF_SAMPFMT_TWOSCOMP, 16, +- 0, // number of compression types +- NULL, // compression types +- 0, // maximum marker count +- 0, // maximum instrument count +- 0, // maximum number of loops per instrument +- 0, // number of instrument parameters +- NULL // instrument parameters +- }, +- { +- AF_FILE_VOC, +- "VOC", "Creative Voice File Format", "voc", +- true, +- VOCFile::completeSetup, +- VOCFile::recognize, +- AF_SAMPFMT_TWOSCOMP, 16, +- _AF_VOC_NUM_COMPTYPES, +- _af_voc_compression_types, +- 0, // maximum marker count +- 0, // maximum instrument count +- 0, // maximum number of loops per instrument +- 0, // number of instrument parameters +- NULL // instrument parameters +- }, +- { +- AF_FILE_NIST_SPHERE, +- "NIST SPHERE", "NIST SPHERE File Format", "nist", +- true, +- NISTFile::completeSetup, +- NISTFile::recognize, +- AF_SAMPFMT_TWOSCOMP, 16, +- 0, /* number of compression types */ +- NULL, /* compression types */ +- 0, /* maximum marker count */ +- 0, /* maximum instrument count */ +- 0, /* maximum number of loops per instrument */ +- 0, /* number of instrument parameters */ +- NULL /* instrument parameters */ +- }, +- { +- AF_FILE_SOUNDFONT2, +- "SoundFont 2", "SoundFont 2 File Format", "sf2", +- false +- }, +- { +- AF_FILE_CAF, +- "CAF", "Core Audio Format", "caf", +- true, +- CAFFile::completeSetup, +- CAFFile::recognize, +- AF_SAMPFMT_TWOSCOMP, 16, +- _AF_CAF_NUM_COMPTYPES, +- _af_caf_compression_types, +- 0, // maximum marker count +- 0, // maximum instrument count +- 0, // maximum number of loops per instrument +- 0, // number of instrument parameters +- NULL // instrument parameters +- }, +- { +- AF_FILE_FLAC, +- "FLAC", "Free Lossless Audio Codec", "flac", +- true, +- FLACFile::completeSetup, +- FLACFile::recognize, +- AF_SAMPFMT_TWOSCOMP, 16, +- _AF_FLAC_NUM_COMPTYPES, +- _af_flac_compression_types, +- 0, // maximum marker count +- 0, // maximum instrument count +- 0, // maximum number of loops per instrument +- 0, // number of instrument parameters +- NULL // instrument parameters +- } + }; + + const CompressionUnit _af_compression[_AF_NUM_COMPRESSION] = +@@ -309,19 +145,6 @@ const CompressionUnit _af_compression[_AF_NUM_COMPRESSION] = + _af_g711_format_ok, + _AFg711initcompress, _AFg711initdecompress + }, +- { +- AF_COMPRESSION_IMA, +- true, +- "ima4", /* label */ +- "IMA ADPCM", /* short name */ +- "IMA DVI ADPCM", +- 4.0, +- AF_SAMPFMT_TWOSCOMP, 16, +- true, /* needsRebuffer */ +- false, /* multiple_of */ +- _af_ima_adpcm_format_ok, +- _af_ima_adpcm_init_compress, _af_ima_adpcm_init_decompress +- }, + { + AF_COMPRESSION_MS_ADPCM, + true, +@@ -335,34 +158,4 @@ const CompressionUnit _af_compression[_AF_NUM_COMPRESSION] = + _af_ms_adpcm_format_ok, + _af_ms_adpcm_init_compress, _af_ms_adpcm_init_decompress + }, +- { +- AF_COMPRESSION_FLAC, +-#if ENABLE(FLAC) +- true, +-#else +- false, +-#endif +- "flac", // label +- "FLAC", // short name +- "Free Lossless Audio Codec", +- 1.0, +- AF_SAMPFMT_TWOSCOMP, 16, +- false, // needsRebuffer +- false, // multiple_of +- _af_flac_format_ok, +- _af_flac_init_compress, _af_flac_init_decompress +- }, +- { +- AF_COMPRESSION_ALAC, +- true, +- "alac", // label +- "ALAC", // short name +- "Apple Lossless Audio Codec", +- 1.0, +- AF_SAMPFMT_TWOSCOMP, 16, +- true, // needsRebuffer +- false, // multiple_of +- _af_alac_format_ok, +- _af_alac_init_compress, _af_alac_init_decompress +- } + }; diff --git a/tools/util/generate_armips_cpp.py b/tools/util/generate_armips_cpp.py @@ -63,16 +63,6 @@ file_list = [ 'Commands/CDirectiveMessage.h', 'Commands/CDirectiveMessage.cpp', 'Commands/CommandSequence.cpp', - 'Core/ELF/ElfFile.cpp', - 'Core/ELF/ElfRelocator.cpp', - 'Core/Assembler.cpp', - 'Core/Common.cpp', - 'Core/Expression.cpp', - 'Core/ExpressionFunctions.cpp', - 'Core/FileManager.cpp', - 'Core/Misc.cpp', - 'Core/SymbolData.cpp', - 'Core/SymbolTable.cpp', 'Parser/DirectivesParser.cpp', 'Parser/ExpressionParser.cpp', 'Parser/Parser.cpp', @@ -84,6 +74,16 @@ file_list = [ 'Util/Util.cpp', 'Main/CommandLineInterface.h', 'Main/CommandLineInterface.cpp', + 'Core/ELF/ElfFile.cpp', + 'Core/ELF/ElfRelocator.cpp', + 'Core/Assembler.cpp', + 'Core/Common.cpp', + 'Core/Expression.cpp', + 'Core/ExpressionFunctions.cpp', + 'Core/FileManager.cpp', + 'Core/Misc.cpp', + 'Core/SymbolData.cpp', + 'Core/SymbolTable.cpp', 'Main/main.cpp', ] diff --git a/tools/util/generate_audiofile_cpp.py b/tools/util/generate_audiofile_cpp.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python + +import os +import re +import sys + +file_list = [ + 'Features.h', + 'Compiler.h', + 'error.h', + 'extended.h', + 'compression.h', + 'aupvinternal.h', + 'aupvlist.h', + 'audiofile.h', + 'afinternal.h', + 'byteorder.h', + 'AudioFormat.h', + 'debug.h', + 'util.h', + 'units.h', + 'UUID.h', + 'Shared.h', + 'Buffer.h', + 'File.h', + 'FileHandle.h', + 'Instrument.h', + 'Track.h', + 'Marker.h', + 'Setup.h', + 'Tag.h', + 'PacketTable.h', + 'pcm.h', + 'g711.h', + 'af_vfs.h', + 'Raw.h', + 'WAVE.h', + 'SampleVision.h', + 'modules/Module.h', + 'modules/ModuleState.h', + 'modules/SimpleModule.h', + 'modules/FileModule.h', + 'modules/RebufferModule.h', + 'modules/BlockCodec.h', + 'modules/BlockCodec.cpp', + 'modules/FileModule.cpp', + 'modules/G711.h', + 'modules/G711.cpp', + 'modules/Module.cpp', + 'modules/ModuleState.cpp', + 'modules/MSADPCM.h', + 'modules/MSADPCM.cpp', + 'modules/PCM.h', + 'modules/PCM.cpp', + 'modules/SimpleModule.cpp', + 'modules/RebufferModule.cpp', + 'AIFF.h', + 'AIFF.cpp', + 'AudioFormat.cpp', + 'Buffer.cpp', + 'File.cpp', + 'FileHandle.cpp', + 'Instrument.cpp', + 'Loop.cpp', + 'Marker.cpp', + 'Miscellaneous.cpp', + 'PacketTable.cpp', + 'Raw.cpp', + 'Setup.cpp', + 'Track.cpp', + 'UUID.cpp', + 'WAVE.cpp', + 'aes.cpp', + 'af_vfs.cpp', + 'aupv.c', + 'compression.cpp', + 'data.cpp', + 'debug.cpp', + 'error.c', + 'extended.c', + 'format.cpp', + 'g711.c', + 'openclose.cpp', + 'pcm.cpp', + 'query.cpp', + 'units.cpp', + 'util.cpp', +] + +file_header = \ +"""// libaudiofile b62c902 +// https://github.com/mpruett/audiofile +// To simplify compilation, all files have been concatenated into one. +// Support for all formats except WAVE, AIFF(C) and RAW has been stripped out. +""" + +prepend_defs = \ +"""#define HAVE_UNISTD_H 1 +#if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +#endif +#include <stdlib.h> +""" + +def banned(line): + return '#pragma once' in line or '#include "' in line or '#include <config.h>' in line + +def cat_file(fout, fin_name): + with open(fin_name) as fin: + lines = fin.readlines() + lines = [l.rstrip() for l in lines if not banned(l)] + for l in lines: + fout.write(l + '\n') + fout.write('\n') + +def combine_libaudiofile(fout_name, libaudiofile_path): + with open(fout_name, 'w') as fout: + fout.write(file_header + "\n") + fout.write("/*\n") + cat_file(fout, os.path.join(libaudiofile_path, '../COPYING')) + fout.write("*/\n\n") + fout.write(prepend_defs + "\n") + for f in file_list: + fout.write(f"// file: {f}\n") + cat_file(fout, os.path.join(libaudiofile_path, f)) + +def main(): + if len(sys.argv) > 1 and sys.argv[1] in ['-h', '--help']: + print('Usage: generate_audiofile_cpp.py [output_filename] [libaudiofile_src_dir]') + print('Defaults: [output_filename = "audiofile.cpp"] [libaudiofile_src_dir = "./audiofile/libaudiofile"]') + return + fout_name = sys.argv[1] if len(sys.argv) > 1 else 'audiofile.cpp' + libaudiofile_path = sys.argv[2] if len(sys.argv) > 2 else './audiofile/libaudiofile' + combine_libaudiofile(fout_name, os.path.expanduser(libaudiofile_path)) + +main()