[Decompile] vector.gc (finally) (#394)

* support psubw

* do half of vector

* rest of vector

* add test

* format
This commit is contained in:
water111 2021-04-28 17:14:54 -04:00 committed by GitHub
parent 53a70d8e43
commit 14028b90bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 2166 additions and 443 deletions

View File

@ -8,10 +8,9 @@ typedef OpenGOALAsm::InstructionModifiers MOD;
const std::map<InstructionKind, OpenGOALAsm::Function> MIPS_ASM_TO_OPEN_GOAL_FUNCS = {
// ----- EE -------
// TODO - these are waiting on proper 128-bit int support in OpenGOAL
{InstructionKind::PSLLW, {".pw.sll", {}}},
{InstructionKind::PSRAW, {".pw.sra", {}}},
{InstructionKind::PSUBW, {"TODO.PSUBW", {}}},
{InstructionKind::PSUBW, {".psubw", {}}},
{InstructionKind::PEXTUW, {".pextuw", {}}},
{InstructionKind::PEXTLW, {".pextlw", {}}},

View File

@ -2694,7 +2694,7 @@
(deftype dma-packet (structure)
((dma dma-tag :offset-assert 0)
(vif0 vif-tag :offset-assert 8)
(vif1 vif-tag :offset-assert 12) ;; doesn't have to be a vif tag.
(vif1 vif-tag :offset-assert 12)
(quad uint128 :offset 0)
)
:method-count-assert 9
@ -3672,7 +3672,7 @@
(define-extern vector-v+! (function vector vector vector vector))
(define-extern vector-v*float+! (function vector vector vector float vector))
(define-extern vector-v++! (function vector vector vector))
(define-extern vector-v*float! (function vector float float vector))
(define-extern vector-v*float! (function vector vector float vector))
(define-extern vector-v*float++! (function vector vector float vector))
(define-extern vector-to-ups! (function vector vector vector))
(define-extern vector-from-ups! (function vector vector vector))
@ -3703,16 +3703,16 @@
(define-extern vector-degi (function vector vector vector))
(define-extern vector-degf (function vector vector vector))
(define-extern vector-degmod (function vector vector vector))
(define-extern vector-deg-diff (function vector vector vector vector))
(define-extern vector-deg-diff (function vector vector vector none))
(define-extern vector-deg-lerp-clamp! (function vector vector vector float vector)) ;; todo
(define-extern vector3s-copy! (function vector vector vector))
(define-extern vector3s+! (function vector vector vector vector))
(define-extern vector3s*float! (function vector vector float vector))
(define-extern vector3s-! (function vector vector vector vector))
(define-extern spheres-overlap? (function vector vector symbol))
(define-extern spheres-overlap? (function sphere sphere symbol))
(define-extern sphere<-vector! (function sphere vector sphere))
(define-extern sphere<-vector+r! (function sphere vector float sphere))
(define-extern rand-vu-sphere-point! (function vector sphere vector)) ;; todo
(define-extern rand-vu-sphere-point! (function vector float vector)) ;; todo
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -164,6 +164,9 @@
"vif1-handler", // F: weird asm for interrupt handler
"vif1-handler-debug",
// vector
"vector=", // asm branching
// texture
"adgif-shader<-texture-with-update!", // F: asm branching
"(method 9 texture-page-dir)",

View File

@ -780,7 +780,7 @@
},
"(method 0 display-frame)": {
"vars": {"gp-0": "obj"}
"vars": { "gp-0": "obj" }
},
"(method 0 draw-context)": {
@ -800,6 +800,88 @@
"vars": { "gp-0": "obj" }
},
"vector-seek-2d-xz-smooth!": {
"args": ["vec", "target", "max-step", "alpha"],
"vars": {
"f0-1": "x-diff",
"f2-1": "z-diff",
"f1-5": "x-step",
"f0-3": "z-step",
"f2-4": "step-len"
}
},
"vector-seek-2d-yz-smooth!": {
"args": ["vec", "target", "max-step", "alpha"],
"vars": {
"f0-1": "y-diff",
"f2-1": "z-diff",
"f1-5": "y-step",
"f0-3": "z-step",
"f2-4": "step-len",
"f2-6": "step-scale"
}
},
"vector-seek-3d-smooth!": {
"args": ["vec", "target", "max-step", "alpha"],
"vars": {
"f0-1": "x-diff",
"f1-2": "y-diff",
"f3-1": "z-diff",
"f2-6": "x-step",
"f1-3": "y-step",
"f0-4": "z-step",
"f3-5": "step-len",
"f3-7": "step-scale"
}
},
"seek-with-smooth": {
"args": ["value", "target", "max-step", "alpha", "deadband"],
"vars": { "f0-1": "diff", "f0-2": "step", "f1-4": "min-step" }
},
"vector-v+!": {
"args": ["result", "position", "velocity"]
},
"vector-v*float+!": {
"args": ["result", "position", "velocity", "velocity-scale"]
},
"vector-v++!": {
"args": ["position", "velocity"]
},
"vector-v*float!": {
"args": ["delta-p", "velocity", "scale"]
},
"vector-v*float++!": {
"args": ["position", "velocity", "scale"]
},
"vector-lerp!": {
"args": ["out", "a", "b", "alpha"]
},
"vector-lerp-clamp!": {
"args": ["out", "a", "b", "alpha"]
},
"vector4-lerp!": {
"args": ["out", "a", "b", "alpha"]
},
"vector4-lerp-clamp!": {
"args": ["out", "a", "b", "alpha"]
},
"vector-deg-lerp-clamp!": {
"args": ["out", "min-val", "max-val", "in"]
},
"(method 9 display)": {
"args": ["obj", "delta-seconds"],
"vars": { "gp-0": "obj", "s5-0": "delta" }

View File

@ -140,3 +140,5 @@
- `defenum` now creates real types. Boxed arrays of enums and bitfields correctly have runtime type of the parent integer.
- Added a `:copy-entries <typename>` to copy entries from a previous bitfield.
- Adding a duplicate entry to an enum now generates a compiler error.
- Added `.psubw` assembly form
- Changed `.ftoi` to `VCVTTPS2DQ` to make the rounding behavior match the PS2 (truncate).

File diff suppressed because it is too large Load Diff

View File

@ -53,3 +53,18 @@
)
)
)
(defmacro seconds (x)
"Convert number to seconds unit."
(cond
((float? x)
(* 300.0 x)
)
((integer? x)
(* 300 x)
)
(#t
`(* 300.0 ,x)
)
)
)

View File

@ -429,6 +429,7 @@ class Compiler {
Val* compile_asm_pcpyld(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_asm_pceqw(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_asm_ppach(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_asm_psubw(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_asm_xorp(const goos::Object& form, const goos::Object& rest, Env* env);
// Atoms

View File

@ -1471,6 +1471,9 @@ std::string IR_Int128Math3Asm::print() {
case Kind::PCEQW:
function = ".pceqw";
break;
case Kind::PSUBW:
function = ".psubw";
break;
default:
assert(false);
}
@ -1514,6 +1517,10 @@ void IR_Int128Math3Asm::do_codegen(emitter::ObjectGenerator* gen,
case Kind::PCEQW:
gen->add_instr(IGen::pceqw(dst, src1, src2), irec);
break;
case Kind::PSUBW:
// psubW on mips is psubD on x86...
gen->add_instr(IGen::vpsubd(dst, src1, src2), irec);
break;
default:
assert(false);
}

View File

@ -548,7 +548,8 @@ class IR_VFMath3Asm : public IR_Asm {
class IR_Int128Math3Asm : public IR_Asm {
public:
enum class Kind { PEXTLW, PEXTUW, PCPYUD, PCPYLD, PCEQW };
// these are MIPS names, not x86 names.
enum class Kind { PEXTLW, PEXTUW, PCPYUD, PCPYLD, PCEQW, PSUBW };
IR_Int128Math3Asm(bool use_color,
const RegVal* dst,
const RegVal* src1,

View File

@ -627,6 +627,10 @@ Val* Compiler::compile_asm_pceqw(const goos::Object& form, const goos::Object& r
return compile_asm_int128_math3(form, rest, IR_Int128Math3Asm::Kind::PCEQW, env);
}
Val* Compiler::compile_asm_psubw(const goos::Object& form, const goos::Object& rest, Env* env) {
return compile_asm_int128_math3(form, rest, IR_Int128Math3Asm::Kind::PSUBW, env);
}
Val* Compiler::compile_asm_ppach(const goos::Object& form, const goos::Object& rest, Env* env) {
auto args = get_va(form, rest);
va_check(form, args, {{}, {}, {}}, {});

View File

@ -102,6 +102,7 @@ const std::unordered_map<
{".pcpyud", &Compiler::compile_asm_pcpyud},
{".pceqw", &Compiler::compile_asm_pceqw},
{".ppach", &Compiler::compile_asm_ppach},
{".psubw", &Compiler::compile_asm_psubw},
// BLOCK FORMS
{"top-level", &Compiler::compile_top_level},

View File

@ -2391,9 +2391,10 @@ class IGen {
static Instruction ftoi_vf(Register dst, Register src) {
assert(dst.is_xmm());
assert(src.is_xmm());
Instruction instr(0x5b); // VCVTDQ2PS
// VEX.128.F3.0F.WIG 5B /r VCVTTPS2DQ xmm1, xmm2/m128
Instruction instr(0x5b); // VCVTTPS2DQ
instr.set_vex_modrm_and_rex(dst.hw_id(), src.hw_id(), VEX3::LeadingBytes::P_0F, 0, false,
VexPrefix::P_66);
VexPrefix::P_F3);
return instr;
}
@ -2494,6 +2495,18 @@ class IGen {
return instr;
}
static Instruction vpsubd(Register dst, Register src0, Register src1) {
assert(dst.is_xmm());
assert(src0.is_xmm());
assert(src1.is_xmm());
// VEX.128.66.0F.WIG FA /r VPSUBD xmm1, xmm2, xmm3/m128
// reg, vec, r/m
Instruction instr(0xfa);
instr.set_vex_modrm_and_rex(dst.hw_id(), src1.hw_id(), VEX3::LeadingBytes::P_0F, src0.hw_id(),
false, VexPrefix::P_66);
return instr;
}
static Instruction vpsrldq(Register dst, Register src, u8 imm) {
assert(dst.is_xmm());
assert(src.is_xmm());

View File

@ -286,5 +286,9 @@
;; display-h
(declare-type display basic)
(define-extern set-display (function display int int int int int display))
(define-extern *display* display)
;; vector
;; only because trig isn't in the reference yet.
(define-extern deg-lerp-clamp (function float float float float))

File diff suppressed because it is too large Load Diff

View File

@ -51,4 +51,24 @@
(print128 s3q) (format #t "~%")
)
)
)
(let ((s1 (new 'stack 'array 'uint32 4))
(s2 (new 'stack 'array 'uint32 4)))
(set! (-> s1 0) #x300)
(set! (-> s1 1) #x3)
(set! (-> s1 2) #x100)
(set! (-> s1 3) #x100000)
(set! (-> s2 0) #x1)
(set! (-> s2 1) #x5)
(set! (-> s2 2) #x2)
(set! (-> s2 3) #x70000)
(let ((s1q (-> (the (pointer uint128) s1)))
(s2q (-> (the (pointer uint128) s2)))
(s3q (the uint128 0)))
(.psubw s3q s1q s2q)
(print128 s3q) (format #t "~%")
;; #x00090000000000fefffffffe000002ff
)
)
)

View File

@ -0,0 +1,138 @@
(defmacro stack-vec-xyzw (x y z w)
`(let ((vec (new 'stack 'vector)))
(set! (-> vec x) ,x)
(set! (-> vec y) ,y)
(set! (-> vec z) ,z)
(set! (-> vec w) ,w)
vec
)
)
(defmacro print-vec (x)
`(begin
(format #t "[~F] [~F] [~F] [~F]~%" (-> ,x x) (-> ,x y) (-> ,x z) (-> ,x w))
)
)
(defmacro print-veci (x)
`(begin
(let ((veci (the (pointer int32) ,x)))
(format #t "~d ~d ~d ~d~%" (-> veci 0) (-> veci 1) (-> veci 2) (-> veci 3))
)
)
)
(defmacro print-vec-deg-i (x)
`(begin
(let ((veci (the (pointer int32) ,x)))
(format #t "~r ~r ~r ~r~%"
(the float (-> veci 0))
(the float (-> veci 1))
(the float (-> veci 2))
(the float (-> veci 3)))
)
)
)
(defmacro print-vec-deg-f (x)
`(format #t "~r ~r ~r ~r~%"
(-> ,x x)
(-> ,x y)
(-> ,x z)
(-> ,x w)
)
)
(defmacro set-veci! (vec x y z w)
`(begin
(let ((veci (the (pointer int32) ,vec)))
(set! (-> veci 0) ,x)
(set! (-> veci 1) ,y)
(set! (-> veci 2) ,z)
(set! (-> veci 3) ,w)
)
)
)
(defmacro set-vecd! (vec x y z w)
`(begin
(set! (-> ,vec x) (degrees ,x))
(set! (-> ,vec y) (degrees ,y))
(set! (-> ,vec z) (degrees ,z))
(set! (-> ,vec w) (degrees ,w))
)
)
(let ((v1 (stack-vec-xyzw 1. 2. 3. 4.))
(v2 (stack-vec-xyzw 5. 6. 7. 8.))
(v4 (new 'stack 'vector))
(v3 (new 'stack 'vector)))
;; part 1
;; w gets set to junk by vector-cross!
(vector-cross! v3 v1 v2) (set! (-> v3 w) 0.0) (print-vec v3)
(vector+float! v3 v1 2.0) (print-vec v3)
(vector*! v3 v1 v2) (print-vec v3)
(vector+*! v3 v1 v2 2.0) (print-vec v3)
(vector-*! v3 v1 v2 2.0) (print-vec v3)
(vector/! v3 v1 v2) (print-vec v3)
(vector-float*! v3 v1 -2.0) (print-vec v3)
(vector-average! v3 v1 v2) (print-vec v3)
(vector+float*! v3 v1 v2 2.0) (print-vec v3)
(vector--float*! v3 v1 v2 2.0) (print-vec v3)
(vector-float/! v3 v1 -2.0) (print-vec v3)
(vector-negate! v3 v1) (print-vec v3)
;; part 2
(format #t "part 2~%")
(vector-negate-in-place! v1) (print-vec v1)
(vector-negate-in-place! v1)
(format #t "~A ~A ~A ~A~%"
(vector= v1 v2) ;; no
(vector= v1 v1) ;; yes
(vector= v1 (stack-vec-xyzw 1. 2. 3. 5.)) ;; yes
(vector= v1 (stack-vec-xyzw 1. 2. 3.1 4.)) ;; no
)
(format #t "~f~%" (vector-delta (stack-vec-xyzw 1.0 -2.0 3.0 -4.0)
(stack-vec-xyzw 2.0 -3.0 1.0 -20.0)))
;; part 3
(format #t "part 3~%")
(vector-copy! v3 v1)
(vector-seek! v3 v2 0.5)
(print-vec v3)
(vector-seek! v3 v2 100.0)
(print-vec v3)
;; 2,3,6 -> 7 pythagorean quadruple (is that even a thing)
(format #t "~f~%" (vector-length (stack-vec-xyzw 2.0 3.0 6.0 99.99)))
(format #t "~f~%" (vector-length-squared (stack-vec-xyzw 2.0 3.0 6.0 99.99)))
(format #t "~f~%" (vector-vector-distance
(stack-vec-xyzw 4.0 5.0 7.0 1.0)
(stack-vec-xyzw 6.0 2.0 1.0 99.99)))
(print-vec (vector-normalize!
(stack-vec-xyzw 1.0 1.5 3. 10.0)
49.0))
(format #t "~f~%" (vector-normalize-ret-len!
(stack-vec-xyzw 1.0 1.5 3. 10.0)
49.0))
(vector-cvt.w.s! v3 (stack-vec-xyzw -1.9 2.9 -3.1 4.1))
(print-veci v3)
(vector-cvt.s.w! v3 v3)
(print-vec v3)
(vector-degi v3 (stack-vec-xyzw (/ 10.0 360.0) (/ 721.0 360.0) (/ 370.0 360.0) (/ -187.0 360.0)))
(print-vec-deg-i v3)
(set-vecd! v3 25 45 -25 (+ -360 -25))
(set-vecd! v4 45 25 (+ 360 -45) -45)
(vector-deg-diff v3 v3 v4)
(print-vec-deg-f v3)
)

View File

@ -488,6 +488,39 @@ TEST_F(WithGameTests, Trig) {
"0\n"});
}
TEST_F(WithGameTests, Vector) {
runner.run_static_test(env, testCategory, "test-vector.gc",
{"[ -4.0000] [ 8.0000] [ -4.0000] [ 0.0000]\n"
"[ 3.0000] [ 4.0000] [ 5.0000] [ 1.0000]\n"
"[ 5.0000] [ 12.0000] [ 21.0000] [ 1.0000]\n"
"[ 11.0000] [ 14.0000] [ 17.0000] [ 1.0000]\n"
"[ -9.0000] [ -10.0000] [ -11.0000] [ 1.0000]\n"
"[ 0.2000] [ 0.3333] [ 0.4285] [ 1.0000]\n"
"[ -2.0000] [ -4.0000] [ -6.0000] [ 1.0000]\n"
"[ 3.0000] [ 4.0000] [ 5.0000] [ 1.0000]\n"
"[ 11.0000] [ 14.0000] [ 17.0000] [ 1.0000]\n"
"[ -9.0000] [ -10.0000] [ -11.0000] [ 1.0000]\n"
"[ -0.5000] [ -1.0000] [ -1.5000] [ 1.0000]\n"
"[ -1.0000] [ -2.0000] [ -3.0000] [ 1.0000]\n"
"part 2\n"
"[ -1.0000] [ -2.0000] [ -3.0000] [ 4.0000]\n"
"#f #t #t #f\n"
"4.0000\n"
"part 3\n"
"[ 1.5000] [ 2.5000] [ 3.5000] [ 1.0000]\n"
"[ 5.0000] [ 6.0000] [ 7.0000] [ 1.0000]\n"
"7.0000\n"
"49.0000\n"
"7.0000\n"
"[ 14.0000] [ 21.0000] [ 42.0000] [ 10.0000]\n"
"3.5000\n"
"-1 2 -3 4\n"
"[ -1.0000] [ 2.0000] [ -3.0000] [ 4.0000]\n"
"0.0000 720.0000 360.0000 0.0000\n"
"-20.0006 20.0006 20.0006 20.0006\n"
"0\n"});
}
TEST_F(WithGameTests, InlinedPackedBasics) {
runner.run_static_test(env, testCategory, "inlined-packed-basics.gc",
{"rec stride: 48\n"
@ -607,6 +640,7 @@ TEST_F(WithGameTests, Pextlw) {
"#x1f1e1d1c1b1a19180f0e0d0c0b0a0908\n"
"#x0d0c0908050401001d1c191815141110\n"
"#xffffffff00000000ffffffff00000000\n"
"#x00090000000000fefffffffe000002ff\n"
"0\n"});
}

View File

@ -17,7 +17,7 @@ const std::unordered_set<std::string> g_object_files_to_decompile = {
"trigonometry-h", /* transformq-h */ "matrix", "transform", "quaternion",
"euler", /* geometry, trigonometry, */
"gsound-h", "timer-h", "timer", "vif-h", "dma-h", "video-h", "vu1-user-h", "dma", "dma-buffer",
"dma-bucket", "dma-disasm", "pad", "gs", "display-h",
"dma-bucket", "dma-disasm", "pad", "gs", "display-h", "vector",
/* gap */
"bounding-box",
/* gap */
@ -32,7 +32,7 @@ const std::vector<std::string> g_object_files_to_check_against_reference = {
/* transformq-h, */
"matrix", "transform", "quaternion", "euler", /* geometry, trigonometry */
"gsound-h", "timer-h", /* timer, */ "vif-h", "dma-h", "video-h", "vu1-user-h", "dma",
"dma-buffer", "dma-bucket", "dma-disasm", "pad", "gs", "display-h",
"dma-buffer", "dma-bucket", "dma-disasm", "pad", "gs", "display-h", "vector",
/* gap */ "bounding-box",
/* gap */
"sync-info-h", "sync-info"};
@ -71,6 +71,7 @@ const std::unordered_set<std::string> expected_skip_in_decompiler = {
"dma-sync-with-count", "dma-send-no-scratch", "dma-sync-fast",
// dma
"symlink2", "symlink3", "dma-sync-hang", // handwritten asm
"vector=", // asm branching
// sync-info
"(method 15 sync-info)", // needs *res-static-buf*
"(method 15 sync-info-eased)", // needs *res-static-buf*
@ -126,6 +127,10 @@ const std::unordered_set<std::string> skip_in_compiling = {
// display-h
"put-draw-env",
// vector
// bad decisions on float vs int128
"vector-degf", "vector-degmod", "vector-deg-diff", "vector-degi",
// sync-info
"(method 15 sync-info)", // needs display stuff first
"(method 15 sync-info-eased)", // needs display stuff first

View File

@ -340,7 +340,7 @@ TEST(EmitterAVX, FTOI) {
tester.emit(IGen::ftoi_vf(XMM0 + 3, XMM0 + 14));
tester.emit(IGen::ftoi_vf(XMM0 + 13, XMM0 + 4));
tester.emit(IGen::ftoi_vf(XMM0 + 13, XMM0 + 14));
EXPECT_EQ(tester.dump_to_hex_string(true), "C5F95BDCC4C1795BDEC5795BECC441795BEE");
EXPECT_EQ(tester.dump_to_hex_string(true), "C5FA5BDCC4C17A5BDEC57A5BECC4417A5BEE");
}
TEST(EmitterAVX, VPSRAD) {
@ -488,7 +488,7 @@ TEST(EmitterAVX, VPSHUFHW) {
EXPECT_EQ(tester.dump_to_hex_string(true), "C5FA70DC03C4C17A70DE04C57A70EC05C4417A70EE06");
}
TEST(EmitrerAVX, movq_to_gpr_from_xmm) {
TEST(EmitterAVX, movq_to_gpr_from_xmm) {
CodeTester tester;
tester.init_code_buffer(1024);
tester.emit(IGen::movq_gpr64_xmm64(RSP, XMM0 + 3));
@ -498,7 +498,7 @@ TEST(EmitrerAVX, movq_to_gpr_from_xmm) {
EXPECT_EQ(tester.dump_to_hex_string(true), "66480F7EDC664C0F7EEC66490F7EDC664D0F7EEC");
}
TEST(EmitrerAVX, movq_to_xmm_from_gpr) {
TEST(EmitterAVX, movq_to_xmm_from_gpr) {
CodeTester tester;
tester.init_code_buffer(1024);
tester.emit(IGen::movq_xmm64_gpr64(XMM0 + 3, RSP));
@ -506,4 +506,19 @@ TEST(EmitrerAVX, movq_to_xmm_from_gpr) {
tester.emit(IGen::movq_xmm64_gpr64(XMM0 + 3, R12));
tester.emit(IGen::movq_xmm64_gpr64(XMM0 + 13, R12));
EXPECT_EQ(tester.dump_to_hex_string(true), "66480F6EDC664C0F6EEC66490F6EDC664D0F6EEC");
}
TEST(EmitterAVX, VPSUBD) {
CodeTester tester;
tester.init_code_buffer(1024);
tester.emit(IGen::vpsubd(XMM0 + 3, XMM0 + 3, XMM0 + 3));
tester.emit(IGen::vpsubd(XMM0 + 3, XMM0 + 3, XMM0 + 13));
tester.emit(IGen::vpsubd(XMM0 + 3, XMM0 + 13, XMM0 + 3));
tester.emit(IGen::vpsubd(XMM0 + 3, XMM0 + 13, XMM0 + 13));
tester.emit(IGen::vpsubd(XMM0 + 13, XMM0 + 3, XMM0 + 3));
tester.emit(IGen::vpsubd(XMM0 + 13, XMM0 + 3, XMM0 + 13));
tester.emit(IGen::vpsubd(XMM0 + 13, XMM0 + 13, XMM0 + 3));
tester.emit(IGen::vpsubd(XMM0 + 13, XMM0 + 13, XMM0 + 13));
EXPECT_EQ(tester.dump_to_hex_string(true),
"C5E1FADBC4C161FADDC591FADBC4C111FADDC561FAEBC44161FAEDC511FAEBC44111FAED");
}