commit 05c7d7031c17b5208516cf0461cda6368605a4db
parent d43d9b7f204433e66f44154a1cedd26ef91fa0a5
Author: n64 <n64>
Date: Wed, 17 Jun 2020 22:14:32 -0400
Refresh 10.1
Diffstat:
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(¯oTokenizer,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(¯oTokenizer,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 = ¶meters[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>> ¶meters)
-{
- 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(¯oTokenizer,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(¯oTokenizer,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 = ¶meters[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>> ¶meters)
{
- 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(×tamp);
+ /* 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, ¶m);
+ 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, ¶m);
+
+ 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, ¶m);
+
+ 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, ¶m);
+ 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(¬eTag);
+ writeU32(¬eSize);
+ 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()